Skip to content

feat: @byok-relay/llamaindex — LlamaIndex.TS custom LLM, embeddings, tool calling for BYOK AI (Growth Day 44) - #80

Open
alokit-bot wants to merge 2 commits into
mainfrom
feat/growth-2026-07-23-d44
Open

feat: @byok-relay/llamaindex — LlamaIndex.TS custom LLM, embeddings, tool calling for BYOK AI (Growth Day 44)#80
alokit-bot wants to merge 2 commits into
mainfrom
feat/growth-2026-07-23-d44

Conversation

@alokit-bot

@alokit-bot alokit-bot commented Jul 23, 2026

Copy link
Copy Markdown
Collaborator

Growth Day 44 — @byok-relay/llamaindex

LlamaIndex.TS custom LLM and embedding adapter for BYOK relay. Natural companion to @byok-relay/langchain (Day 43), covering the other major JS AI framework.

What ships

packages/llamaindex/src/index.js

  • ByokRelayLLM (extends BaseLLM): chat() non-streaming + stream() async-generator SSE streaming; complete(); withTools(tools) bound-tool pattern; multi-modal message support; tool_call delta streaming
  • ByokRelayEmbedding (extends BaseEmbedding): getQueryEmbedding(), getTextEmbedding(), getTextEmbeddings() with configurable batchSize (default 512); routes to /relay/:provider/embeddings
  • ByokRelayClient: plain-JS key management; localStorage default in browser; in-memory fallback on Node.js/edge; custom storage adapter

packages/llamaindex/README.md: quick-start (chat, streaming, embeddings+VectorStoreIndex, tool calling, ReActAgent, withTools), full API tables, provider table, comparison vs @byok-relay/langchain.

llms.txt: LlamaIndex.TS Integration section + npm link.

README.md: @byok-relay/llamaindex section after LangChain.js integration.

metrics/daily.jsonl: 2026-07-23 snapshot (stars=52).

Tests

52 smoke tests passing (node test/llamaindex.test.js) — no llamaindex peer dep required.

Next for Avi

cd packages/llamaindex && npm publish --access public

Summary by CodeRabbit

  • New Features

    • Added LangChain.js integration with chat, streaming, tool calling, embeddings, vector stores, and agent workflows.
    • Added LlamaIndex.TS integration with chat, streaming, tool calling, embeddings, vector indexes, and ReAct agents.
    • Added key management and relay client capabilities for both integrations.
    • Added publishable package configurations for both integrations.
  • Documentation

    • Added installation instructions, usage examples, API references, supported providers, and self-hosting guidance.
  • Tests

    • Added comprehensive smoke tests covering chat, streaming, embeddings, tools, key management, and error handling.

@coderabbitai

coderabbitai Bot commented Jul 23, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Added new LangChain.js and LlamaIndex.TS packages with relay-backed chat, streaming, tool-calling, embedding, token, and key-management APIs. Added package smoke tests, integration documentation, package metadata, and daily metrics records.

Changes

Framework integrations

Layer / File(s) Summary
LangChain runtime and package wiring
packages/langchain/package.json, packages/langchain/src/index.js
Adds ByokRelayChatModel, ByokRelayEmbeddings, and ByokRelayClient with relay requests, streaming, tool conversion, embeddings batching, and token/key management.
LangChain behavior validation
packages/langchain/test/langchain.test.js
Adds mocked-fetch coverage for client lifecycle, chat generation, message conversion, tool calling, embeddings, and batching.
LlamaIndex runtime and package wiring
packages/llamaindex/package.json, packages/llamaindex/src/index.js
Adds ByokRelayLLM, ByokRelayEmbedding, and ByokRelayClient with chat, completion, streaming, tools, embeddings, and account APIs.
LlamaIndex behavior validation
packages/llamaindex/test/llamaindex.test.js
Adds mocked-fetch coverage for client operations, chat/completion, streaming, tools, parameter forwarding, embeddings, ordering, and batching.
Integration documentation
README.md, llms.txt, packages/langchain/README.md, packages/llamaindex/README.md
Documents installation, examples, supported capabilities, APIs, providers, self-hosting, vector-store usage, and agent integrations.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant LangChainApplication
  participant ByokRelayChatModel
  participant BYOKRelay
  LangChainApplication->>ByokRelayChatModel: invoke or stream messages
  ByokRelayChatModel->>BYOKRelay: send relay request
  BYOKRelay-->>ByokRelayChatModel: completion response or stream
  ByokRelayChatModel-->>LangChainApplication: return LangChain result
Loading
sequenceDiagram
  participant LlamaIndexApplication
  participant ByokRelayLLM
  participant BYOKRelay
  LlamaIndexApplication->>ByokRelayLLM: chat or stream messages
  ByokRelayLLM->>BYOKRelay: send converted relay request
  BYOKRelay-->>ByokRelayLLM: completion response or stream
  ByokRelayLLM-->>LlamaIndexApplication: return ChatResponse or deltas
Loading

Possibly related PRs

  • avikalpg/byok-relay#79: Directly overlaps the LangChain integration package, documentation, and smoke-test changes.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 46.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: a new @byok-relay/llamaindex integration with LLM, embeddings, and tool-calling support.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/growth-2026-07-23-d44

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 Checkov (3.3.8)
packages/langchain/package.json

Traceback (most recent call last):
File "/usr/local/bin/checkov", line 2, in
from checkov.main import Checkov
ModuleNotFoundError: No module named 'checkov'

packages/llamaindex/package.json

Traceback (most recent call last):
File "/usr/local/bin/checkov", line 2, in
from checkov.main import Checkov
ModuleNotFoundError: No module named 'checkov'


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

🧹 Nitpick comments (3)
packages/langchain/package.json (1)

13-15: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider declaring an engines constraint.

The runtime relies on native fetch/ReadableStream and the JSDoc states "Node.js 18+". Adding an engines field surfaces that requirement to consumers at install time.

♻️ Suggested addition
   "scripts": {
     "test": "node test/langchain.test.js"
   },
+  "engines": {
+    "node": ">=18"
+  },
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/langchain/package.json` around lines 13 - 15, Add an engines
constraint to packages/langchain/package.json declaring Node.js 18 or newer,
alongside the existing scripts configuration, to match the package’s native
fetch/ReadableStream runtime requirement.
packages/langchain/src/index.js (2)

432-477: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Harden the shared SSE reader loop in both streaming paths. Both _stream and streamChat duplicate the same manual SSE parser whose reader/buffer handling has minor robustness gaps: no guard when res.body is null, the final non-newline-terminated event in buffer is discarded after done, and decoder.decode() is never flushed. Consider extracting a shared async-generator helper so both paths get the same fix.

  • packages/langchain/src/index.js#L432-L477: guard res.body before getReader(), and after the loop process any residual buffer (and flush the decoder) instead of relying solely on the [DONE] sentinel.
  • packages/langchain/src/index.js#L675-L696: apply the same reader/buffer hardening and add the missing delta existence guard (as in _stream) before reading delta.content on Line 691.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/langchain/src/index.js` around lines 432 - 477, Harden the
duplicated SSE parsing in packages/langchain/src/index.js lines 432-477 and
675-696, preferably via a shared async-generator helper: validate res.body
before calling getReader(), flush decoder.decode() after reading completes, and
process any residual non-newline-terminated buffer event. In the streamChat site
at lines 675-696, also guard the parsed choice delta before accessing
delta.content, matching _stream’s existing protection.

204-223: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Convert LangChain StructuredTool schemas before using them as function parameters.

_convertTools assigns tool.schema directly to function.parameters, but LangChain StructuredTool instances expose a Zod schema. Passing that object as the relay schema will not yield a valid OpenAI-compatible JSON Schema; use zodToJsonSchema or LangChain’s convertToOpenAITool path for Zod schemas, while still accepting raw JSON-Schema objects where already provided.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/langchain/src/index.js` around lines 204 - 223, Update _convertTools
to convert LangChain StructuredTool Zod schemas into OpenAI-compatible JSON
Schema before assigning function.parameters, using zodToJsonSchema or
LangChain’s convertToOpenAITool path. Preserve raw JSON-Schema objects when
tool.schema, tool.parameters, or tool.inputSchema is already JSON Schema, and
retain the existing fallback for tools without schemas.

Source: Linters/SAST tools

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/langchain/README.md`:
- Around line 3-5: Update the introductory README description to state that
users can use provider keys without their application server storing them, while
preserving the existing clarification that the relay stores keys encrypted at
rest.
- Around line 212-215: Update the ByokRelayChatModel API table entries for
_generate and _stream to describe them as internal hooks used by LangChain’s
chat-model/runnable invocation machinery, replacing the inaccurate BaseChain
reference while leaving invoke and stream descriptions unchanged.
- Around line 74-77: Register the provider key before each standalone relay
invocation: in packages/langchain/README.md at lines 74-77, 141-150, and
172-187, call model.storeKey('openai', 'sk-...') before the tool-calling, LCEL,
and ReAct examples; in packages/llamaindex/README.md at lines 55-63, call await
llm.storeKey('openai', 'sk-...') before engine.query(...), or explicitly
configure llm with shared storage.

In `@packages/llamaindex/README.md`:
- Around line 133-140: Update the README quick-start withTools example to
declare calendarTool before it is passed to llm.withTools, using the existing
tool-definition pattern, or remove calendarTool from the tools array and keep
the example valid when copied.
- Around line 22-23: Update the README example comment near llm.storeKey to
avoid stating that the API key is persisted in localStorage. Explain that
storeKey sends the key to the relay, while localStorage caches the relay token,
and qualify the storage behavior for Node/edge environments where storage is in
memory.
- Around line 303-305: Update the license section in the package README and the
package metadata to remove the inconsistent MIT claim, aligning
`@byok-relay/llamaindex` with the repository’s Apache-2.0 licensing and existing
root documentation.

In `@packages/llamaindex/src/index.js`:
- Around line 242-260: Scope the persisted relay token by app ID across the
ByokRelayClient lifecycle. Add or reuse a _tokenKey derived from this.appId,
then replace the fixed 'byok_relay_token' key in ensureToken, register, logout,
and deleteAccount with this._tokenKey, preserving existing token loading,
saving, and deletion behavior.
- Around line 142-149: Update the image_url branch in the content mapping to
normalize both LlamaIndex image URL shapes: use the string directly when
image_url is already a string, and extract its url property when it is an
object. Ensure the returned image_url contains a single string url rather than a
nested object.

---

Nitpick comments:
In `@packages/langchain/package.json`:
- Around line 13-15: Add an engines constraint to
packages/langchain/package.json declaring Node.js 18 or newer, alongside the
existing scripts configuration, to match the package’s native
fetch/ReadableStream runtime requirement.

In `@packages/langchain/src/index.js`:
- Around line 432-477: Harden the duplicated SSE parsing in
packages/langchain/src/index.js lines 432-477 and 675-696, preferably via a
shared async-generator helper: validate res.body before calling getReader(),
flush decoder.decode() after reading completes, and process any residual
non-newline-terminated buffer event. In the streamChat site at lines 675-696,
also guard the parsed choice delta before accessing delta.content, matching
_stream’s existing protection.
- Around line 204-223: Update _convertTools to convert LangChain StructuredTool
Zod schemas into OpenAI-compatible JSON Schema before assigning
function.parameters, using zodToJsonSchema or LangChain’s convertToOpenAITool
path. Preserve raw JSON-Schema objects when tool.schema, tool.parameters, or
tool.inputSchema is already JSON Schema, and retain the existing fallback for
tools without schemas.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 93b61e02-5201-4ed8-aef5-884ffe9bbbd6

📥 Commits

Reviewing files that changed from the base of the PR and between f0e1a43 and 53e3775.

📒 Files selected for processing (11)
  • README.md
  • llms.txt
  • metrics/daily.jsonl
  • packages/langchain/README.md
  • packages/langchain/package.json
  • packages/langchain/src/index.js
  • packages/langchain/test/langchain.test.js
  • packages/llamaindex/README.md
  • packages/llamaindex/package.json
  • packages/llamaindex/src/index.js
  • packages/llamaindex/test/llamaindex.test.js

Comment on lines +3 to +5
LangChain.js custom chat model and embeddings adapter for [byok-relay](https://github.com/avikalpg/byok-relay).

Use **user-supplied AI provider keys** in your LangChain chains, agents, and RAG pipelines — without storing keys server-side. Keys are encrypted at rest in the relay; your server only sees a relay token.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Clarify the key-storage threat model.

“Without storing keys server-side” is incorrect as written: the relay stores provider keys server-side, encrypted at rest. Change this to “without your application server storing keys” so users understand the relay’s role.

🧰 Tools
🪛 LanguageTool

[grammar] ~3-~3: Ensure spelling is correct
Context: ...l and embeddings adapter for byok-relay. Use user-supplied AI provider keys i...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/langchain/README.md` around lines 3 - 5, Update the introductory
README description to state that users can use provider keys without their
application server storing them, while preserving the existing clarification
that the relay stores keys encrypted at rest.

Comment on lines +74 to +77
const model = new ByokRelayChatModel({ modelName: 'openai/gpt-4o' });
const modelWithTools = model.bindTools([weatherTool]);

const result = await modelWithTools.invoke([new HumanMessage('Weather in Tokyo?')]);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== files =="
git ls-files | rg '(^|/)(README\.md|.*\.ts|.*\.tsx|.*\.js)$' | sed -n '1,220p'

echo "== README relevant snippets =="
for f in packages/langchain/README.md packages/llamaindex/README.md; do
  if [ -f "$f" ]; then
    echo "--- $f line count ---"
    wc -l "$f"
    echo "--- $f ranges ---"
    sed -n '1,230p' "$f" | nl -ba | sed -n '45,210p'
  else
    echo "missing $f"
  fi
done

echo "== search adapter key APIs/usages =="
rg -n "storeKey|registerKey|set.*Key|key" packages -g '*.ts' -g '*.tsx' -g '*.md' -g '*.js' | sed -n '1,240p'

echo "== relevant symbols =="
rg -n "ByokRelay(Chat|Embedding|LLM)|class ByokRelay|storeKey|registerKey" packages -g '*.ts' -g '*.tsx' -g '*.js' | sed -n '1,240p'

Repository: avikalpg/byok-relay

Length of output: 724


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== package README snippets =="
for f in packages/langchain/README.md packages/llamaindex/README.md; do
  if [ -f "$f" ]; then
    echo "--- $f line count ---"
    wc -l "$f"
    echo "--- $f relevant ranges ---"
    awk '{printf "%4d  %s\n", NR, $0}' "$f" | sed -n '55,205p'
  else
    echo "missing $f"
  fi
done

echo "== package source snippets =="
for f in packages/langchain/src/index.js packages/llamaindex/src/index.js; do
  if [ -f "$f" ]; then
    echo "--- $f line count ---"
    wc -l "$f"
    echo "--- $f outline-ish summary ---"
    awk '{printf "%4d  %s\n", NR, $0}' "$f" | sed -n '1,260p'
  else
    echo "missing $f"
  fi
done

echo "== key-related searches =="
rg -n "storeKey|registerKey|set.*Key|key|env\.process|process\.env|secret|apiKey|model:" packages src -g '*.ts' -g '*.tsx' -g '*.md' -g '*.js' | sed -n '1,300p'

Repository: avikalpg/byok-relay

Length of output: 50377


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== langchain storeKey/client implementation around token/key forwarding =="
sed -n '255,380p' packages/langchain/src/index.js | awk '{printf "%4d  %s\n", 254+NR, $0}'
echo "== llamaindex storeKey/client implementation around token/key forwarding =="
sed -n '240,320p' packages/llamaindex/src/index.js | awk '{printf "%4d  %s\n", 239+NR, $0}'
sed -n '330,420p' packages/llamaindex/src/index.js | awk '{printf "%4d  %s\n", 329+NR, $0}'

echo "== deterministic verifier for example key availability in README snippets =="
python3 - <<'PY'
from pathlib import Path
import re

files = {
  'packages/langchain/README.md': list(range(48,94)) + list(range(136,153)) + list(range(172,189)),
  'packages/llamaindex/README.md': list(range(50,66)),
}
for path, ranges in files.items():
    text = Path(path).read_text()
    lines = text.splitlines()
    snippet = '\n'.join(lines[i-1] for i in ranges if i-1 < len(lines))
    model_names = re.findall(r'new ByokRelay(Link|Embedding|LLM|ChatModel)\([^)]*?', snippet)
    print(f'--- {path} --- {list(snippet.count("storeKey") for _ in [1])} storeKey calls -> {snippet.count("storeKey(")}')
    before_store = [i for i, line in enumerate(range(1, len(lines)+1), 1) if i in ranges and 'storeKey' not in line]\
                   if ranges else []
    print('stores_in_ranges:', snippet.count('storeKey('))
PY

Repository: avikalpg/byok-relay

Length of output: 13522


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== packages/llamaindex/src/index.js line 520-565 =="
sed -n '520,565p' packages/llamaindex/src/index.js | awk '{printf "%4d  %s\n", 519+NR, $0}'

echo "== packages/langchain/src/index.js storage/client forwarding =="
grep -n "_ensureToken|_k.get|_storage.*get|_relayUrl|relayUrl|api_key|provider.*model" packages/langchain/src/index.js packages/llamaindex/src/index.js | sed -n '1,140p'

Repository: avikalpg/byok-relay

Length of output: 1922


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== packages/langchain/src/index.js _Client and forwarding =="
rg -n "class ByokRelayClient|_Client|_ensureToken|_k\\.|_storage|relay/" packages/langchain/src/index.js -C 3 | sed -n '1,220p'

echo "== packages/llamaindex/src/index.js _Client and forwarding =="
rg -n "class ByokRelayClient|ByokRelayClient|_client|relay/" packages/llamaindex/src/index.js -C 2 | sed -n '1,220p'

echo "== deterministic snippet check =="
python3 - <<'PY'
from pathlib import Path
for path, ranges in [
 ("packages/langchain/README.md", (list(range(58,87)) + list(range(136,151)) + list(range(172,188)))),
 ("packages/llamaindex/README.md", list(range(55,64))),
]:
    text = Path(path).read_text().splitlines()
    snippet = "\n".join(text[max(0,i-1):i] for i in ranges)
    print(f"{path}: storeKey count in snippet={snippet.count('storeKey')}; ranges={ranges}")
PY

Repository: avikalpg/byok-relay

Length of output: 10504


Ensure the standalone provider calls include a registered key.

These snippets create adapters that read their own stored keys, so each standalone example needs storeKey(...) before invoking the relay request:

  • packages/langchain/README.md around lines 74, 141, and 172: call model.storeKey('openai', 'sk-...') before the tool-calling, LCEL, and ReAct examples.
  • packages/llamaindex/README.md around lines 61: this llm is not a copy of embed, so add await llm.storeKey('openai', 'sk-...'); before engine.query(...) or explicitly pass shared storage to llm.
📍 Affects 2 files
  • packages/langchain/README.md#L74-L77 (this comment)
  • packages/langchain/README.md#L141-L150
  • packages/langchain/README.md#L172-L187
  • packages/llamaindex/README.md#L55-L63
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/langchain/README.md` around lines 74 - 77, Register the provider key
before each standalone relay invocation: in packages/langchain/README.md at
lines 74-77, 141-150, and 172-187, call model.storeKey('openai', 'sk-...')
before the tool-calling, LCEL, and ReAct examples; in
packages/llamaindex/README.md at lines 55-63, call await llm.storeKey('openai',
'sk-...') before engine.query(...), or explicitly configure llm with shared
storage.

Comment on lines +212 to +215
| `invoke(messages, opts?)` | Non-streaming completion (LangChain standard) |
| `stream(messages, opts?)` | Streaming completion (LangChain standard) |
| `_generate(messages, opts?)` | Internal; called by LangChain BaseChain |
| `_stream(messages, opts?)` | Internal; called by LangChain streaming |

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "## Files"
git ls-files | rg '(^|/)packages/langchain/README\.md$|(^|/)packages/langchain/' | sed -n '1,120p'

echo
echo "## README context"
if [ -f packages/langchain/README.md ]; then
  nl -ba packages/langchain/README.md | sed -n '160,230p'
fi

echo
echo "## Search for ByokRelayChatModel and BaseChatModel/BaseChain"
rg -n "ByokRelayChatModel|BaseChatModel|BaseChain|extends BaseChain|extends BaseChatModel|_generate|_stream" packages/langchain -S

Repository: avikalpg/byok-relay

Length of output: 361


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "## README context"
if [ -f packages/langchain/README.md ]; then
  sed -n '160,235p' packages/langchain/README.md | cat -n | sed 's/^/README: /'
fi

echo
echo "## Search for ByokRelayChatModel and relevant symbols"
rg -n "ByokRelayChatModel|BaseChatModel|BaseChain|extends BaseChain|extends BaseChatModel|_generate|_stream|invoke\\(|stream\\(" packages/langchain -S

echo
echo "## Source outline/implementation excerpt"
if [ -f packages/langchain/src/index.js ]; then
  wc -l packages/langchain/src/index.js
  sed -n '1,260p' packages/langchain/src/index.js | cat -n | sed 's/^/src: /'
fi

Repository: avikalpg/byok-relay

Length of output: 25569


Correct the internal API reference.

ByokRelayChatModel extends BaseChatModel; update _generate and _stream from “called by LangChain BaseChain” to hooks used by LangChain’s chat-model/runnable invocation machinery.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/langchain/README.md` around lines 212 - 215, Update the
ByokRelayChatModel API table entries for _generate and _stream to describe them
as internal hooks used by LangChain’s chat-model/runnable invocation machinery,
replacing the inaccurate BaseChain reference while leaving invoke and stream
descriptions unchanged.

Comment on lines +22 to +23
// Store the user's API key (once — persisted in localStorage)
await llm.storeKey('openai', 'sk-...');

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Do not claim the API key is persisted in localStorage.

storeKey sends the API key to the relay; localStorage caches the relay token. Also qualify this for Node/edge in-memory storage.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/llamaindex/README.md` around lines 22 - 23, Update the README
example comment near llm.storeKey to avoid stating that the API key is persisted
in localStorage. Explain that storeKey sends the key to the relay, while
localStorage caches the relay token, and qualify the storage behavior for
Node/edge environments where storage is in memory.

Comment on lines +133 to +140
## Quick start — withTools (bound tools)

```js
const llmWithTools = llm.withTools([weatherTool, calendarTool]);

// Tools are forwarded automatically on every call
const response = await llmWithTools.chat({
messages: [{ role: 'user', content: 'Am I free tomorrow?' }],

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Define calendarTool or remove it from the example.

calendarTool is never declared in this README, so copying this snippet causes a ReferenceError.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/llamaindex/README.md` around lines 133 - 140, Update the README
quick-start withTools example to declare calendarTool before it is passed to
llm.withTools, using the existing tool-definition pattern, or remove
calendarTool from the tools array and keep the example valid when copied.

Comment on lines +303 to +305
## License

MIT — [byokrelay.com](https://byokrelay.com)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Tracked README/license/metadata candidates:"
git ls-files | rg '(^|/)(README\.md|LICENSE|LICEN[CS]E|package\.json|pyproject.toml|setup.py|setup.cfg|Cargo.toml)$' || true

echo
echo "Root README relevant lines:"
if [ -f README.md ]; then
  rg -n -i -C 3 'license|apache|mit' README.md || true
fi

if [ -f packages/llamaindex/README.md ]; then
  echo
  echo "packages/llamaindex/README.md relevant lines:"
  sed -n '285,315p' packages/llamaindex/README.md
fi

echo
echo "All occurrences in tracked files:"
rg -n -i 'license|apache|mit|byokrelay.com' . --glob '!node_modules' --glob '!dist' --glob '!build' || true

Repository: avikalpg/byok-relay

Length of output: 17835


Keep @byok-relay/llamaindex license metadata in-sync with the Apache-2.0 package.

This README says MIT, while packages/llamaindex/package.json is also MIT, but root metadata/license docs are Apache 2.0 (LICENSE, root README, root package.json, llms.txt). Remove the per-package MIT claim unless this package intentionally needs different licensing, and align package metadata accordingly.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/llamaindex/README.md` around lines 303 - 305, Update the license
section in the package README and the package metadata to remove the
inconsistent MIT claim, aligning `@byok-relay/llamaindex` with the repository’s
Apache-2.0 licensing and existing root documentation.

Comment on lines +142 to +149
if (Array.isArray(content)) {
const parts = content.map(part => {
if (part.type === 'text') return { type: 'text', text: part.text };
if (part.type === 'image_url') return { type: 'image_url', image_url: { url: part.imageUrl || part.image_url } };
return { type: 'text', text: String(part) };
});
return { role, content: parts };
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

LlamaIndex.TS MessageContentDetail image_url type ChatMessage content format

💡 Result:

In LlamaIndex.TS, a ChatMessage follows a structure where the content field can be either a simple string or an array of MessageContentDetail objects [1][2]. For multi-modal interactions, the content is typically an array that includes objects representing different media types [2][3]. The MessageContentImageDetail type is used to provide image inputs and is structured as follows [1][4]: { "type": "image_url", "image_url": { "url": "string" }, "detail"?: "high" | "low" | "auto" } The image_url property must contain an object with a url field, which accepts either a standard HTTP/HTTPS URL or a base64-encoded data URL (e.g., data:image/jpeg;base64,...) [2][3]. Note regarding the detail property: While LlamaIndex.TS defines the detail field as a sibling of the image_url object [1][4], some underlying LLM providers (like OpenAI) expect the detail parameter to be nested inside the image_url object [5]. Users have reported that mismatches between this LlamaIndex.TS structure and specific provider requirements can lead to validation errors [5]. Example of a multi-modal ChatMessage content: { "role": "user", "content": [ { "type": "text", "text": "What is in this image?" }, { "type": "image_url", "image_url": { "url": "data:image/jpeg;base64,..." } } ] }

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== package references =="
rg -n '"llamaindex"|`@llamaindex/core`|llamaindex' package.json packages/llamaindex/package.json pnpm-lock.yaml yarn.lock package-lock.json 2>/dev/null || true

echo
echo "== target file excerpt =="
nl -ba packages/llamaindex/src/index.js | sed -n '120,160p'

echo
echo "== package declaration excerpt =="
if [ -f packages/llamaindex/package.json ]; then
  nl -ba packages/llamaindex/package.json | sed -n '1,120p'
fi

echo
echo "== lockfile llamaindex exact entries =="
rg -n '"llamaindex"|llamaindex@|`@llamaindex/core`' pnpm-lock.yaml package-lock.json yarn.lock 2>/dev/null | head -80 || true

Repository: avikalpg/byok-relay

Length of output: 704


🏁 Script executed:

#!/bin/bash
set -euo pipefail
export LC_ALL=C

echo "== target file excerpt =="
awk '{printf "%6d\t%s\n", NR, $0}' packages/llamaindex/src/index.js | sed -n '120,160p'

echo
echo "== package declaration excerpt =="
awk '{printf "%6d\t%s\n", NR, $0}' packages/llamaindex/package.json | sed -n '1,120p'

echo
echo "== lockfile llamaindex exact entries (if present) =="
for f in pnpm-lock.yaml package-lock.json yarn.lock; do
  if [ -f "$f" ]; then
    echo "-- $f --"
    rg -n '"llamaindex"|llamaindex@|`@llamaindex/core`' "$f" | head -120 || true
  fi
done

echo
echo "== dependency shape probe =="
python3 - <<'PY'
from typing import Any

def current_normalize(part: dict[str, Any]) -> dict[str, Any]:
    if part.get("type") == "image_url":
        return {"type": "image_url", "image_url": {"url": part.get("imageUrl") or part.get("image_url")}}
    return {"type": "text", "text": str(part)}

samples = [
    {"type": "image_url", "imageUrl": "value"},
    {"type": "image_url", "image_url": "value"},
    {"type": "image_url", "image_url": {"url": "value"}},
]
for s in samples:
    out = current_normalize(s)
    print(s, "=>", out)
PY

Repository: avikalpg/byok-relay

Length of output: 3520


Normalize LlamaIndex image URL input formats. LlamaIndex.TS image content uses type: 'image_url', image_url: { url }, so the current fallback produces image_url: { url: { url } } for real image messages.

Proposed fix
-      if (part.type === 'image_url') return { type: 'image_url', image_url: { url: part.imageUrl || part.image_url } };
+      if (part.type === 'image_url') {
+        const url = part.imageUrl || (typeof part.image_url === 'object' ? part.image_url.url : part.image_url);
+        return { type: 'image_url', image_url: { url } };
+      }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (Array.isArray(content)) {
const parts = content.map(part => {
if (part.type === 'text') return { type: 'text', text: part.text };
if (part.type === 'image_url') return { type: 'image_url', image_url: { url: part.imageUrl || part.image_url } };
return { type: 'text', text: String(part) };
});
return { role, content: parts };
}
if (Array.isArray(content)) {
const parts = content.map(part => {
if (part.type === 'text') return { type: 'text', text: part.text };
if (part.type === 'image_url') {
const url = part.imageUrl || (typeof part.image_url === 'object' ? part.image_url.url : part.image_url);
return { type: 'image_url', image_url: { url } };
}
return { type: 'text', text: String(part) };
});
return { role, content: parts };
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/llamaindex/src/index.js` around lines 142 - 149, Update the
image_url branch in the content mapping to normalize both LlamaIndex image URL
shapes: use the string directly when image_url is already a string, and extract
its url property when it is an object. Ensure the returned image_url contains a
single string url rather than a nested object.

Comment on lines +242 to +260
async ensureToken () {
if (this._token) return this._token;
const stored = this._storage.getItem('byok_relay_token');
if (stored) { this._token = stored; return stored; }
return this.register();
}

async register () {
const res = await fetch(`${this.relayUrl}/users`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ app_id: this.appId }),
});
const data = await res.json();
if (!res.ok) throw new Error(`Registration failed: ${data.error || res.status}`);
this._token = data.token;
this._storage.setItem('byok_relay_token', data.token);
return data.token;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Token storage key isn't scoped to appId. ensureToken/register (and logout/deleteAccount) use a fixed 'byok_relay_token' key, so two ByokRelayClient instances with different appIds in the same origin overwrite each other's token, causing wrong-token auth failures. The sibling packages/langchain scopes this per app (byok_relay_token_${appId}); consider matching that here.

🔧 Proposed approach
   constructor (opts = {}) {
     this.relayUrl = (opts.relayUrl || DEFAULT_RELAY_URL).replace(/\/$/, '');
     this.appId    = opts.appId || 'llamaindex-app';
     this._token   = null;
+    this._tokenKey = `byok_relay_token_${this.appId}`;

Then replace the literal 'byok_relay_token' uses with this._tokenKey.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/llamaindex/src/index.js` around lines 242 - 260, Scope the persisted
relay token by app ID across the ByokRelayClient lifecycle. Add or reuse a
_tokenKey derived from this.appId, then replace the fixed 'byok_relay_token' key
in ensureToken, register, logout, and deleteAccount with this._tokenKey,
preserving existing token loading, saving, and deletion behavior.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant