Skip to content

feat(ai): add local RAG vector store and interactive RAGChat widget#779

Open
Tejas9406 wants to merge 3 commits into
Karanjot786:mainfrom
Tejas9406:feat/rag-integration
Open

feat(ai): add local RAG vector store and interactive RAGChat widget#779
Tejas9406 wants to merge 3 commits into
Karanjot786:mainfrom
Tejas9406:feat/rag-integration

Conversation

@Tejas9406
Copy link
Copy Markdown
Contributor

@Tejas9406 Tejas9406 commented Jun 5, 2026

Description

This PR implements a lightweight, local, zero-dependency RAG (Retrieval-Augmented Generation) system. It introduces a pure-TypeScript vector database (LocalVectorStore) utilizing in-memory cosine similarity search, an automated directory indexer (indexDirectory) that chunks markdown and text files, and an interactive TUI <RAGChat> assistant widget supporting scrollable histories, multiline query input, and real-time streaming answers.

Related Issue

Closes #777

Which package(s)?

@termuijs/adapters, @termuijs/ui

Type of Change

  • 🐛 Bug fix (type:bug)
  • ✨ Feature (type:feature)
  • 📝 Docs (type:docs)
  • 🧪 Tests (type:testing)
  • ♻️ Refactor (type:refactor)
  • 🎨 Design / UX (type:design)
  • ♿ Accessibility (type:accessibility)
  • ⚡ Performance (type:performance)
  • 🔧 DevOps / CI (type:devops)
  • 🔒 Security (type:security)

Checklist

  • ⭐ You starred the repo. The needs-star check blocks your merge otherwise.
  • Tests pass locally: bun vitest run
  • Build passes: bun run build
  • Typecheck passes: bun run typecheck
  • You read CONTRIBUTING.md.
  • Your PR title follows type: short description.
  • Widget state mutators call markDirty() (if your change affects rendering).
  • No new any types without an inline comment explaining why.
  • No unrelated refactors bundled into this PR.

GSSoC 2026 Participation

  • You are a GSSoC 2026 contributor.
  • Your GSSoC profile: https://gssoc.girlscript.org/profile/666f63ca-ed6a-4d0f-8ac8-1518b06ec839

Screenshots / Recordings (UI changes)

(Terminal screenshots / recordings can be dropped here if needed)

Notes for the Reviewer

  • All tests in @termuijs/adapters and @termuijs/ui pass.
  • OpenAI embeddings are used for vectorization. A mock AI adapter was configured in unit tests to bypass external API network requests.
  • Fixed a pre-existing type mismatch warning in packages/adapters/src/commander/index.ts so the workspace TypeScript declarations compile cleanly.

Summary by CodeRabbit

  • New Features

    • Added text-embedding support (OpenAI provider) to the AI adapter.
    • Introduced RAGChat: terminal UI for retrieval-augmented chat with document indexing, context retrieval, streaming AI responses, keyboard input/navigation, and error callbacks/events.
    • Added a local vector store with chunking, cosine-similarity querying, directory indexing, and JSON persistence.
  • Tests

    • Added tests for the vector store and RAGChat covering chunking, querying, persistence, streaming, and error propagation.

@Tejas9406 Tejas9406 requested a review from Karanjot786 as a code owner June 5, 2026 14:26
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jun 5, 2026

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 80a1ad87-5f6d-4244-a8f9-06b0da50d2f8

📥 Commits

Reviewing files that changed from the base of the PR and between d243551 and b127531.

📒 Files selected for processing (2)
  • packages/ui/src/RAGChat.test.ts
  • packages/ui/src/RAGChat.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/ui/src/RAGChat.test.ts
  • packages/ui/src/RAGChat.ts

📝 Walkthrough

Walkthrough

Adds AIAdapter.embed, a LocalVectorStore with chunking, cosine-similarity search, directory indexer and JSON persistence, a RAGChat terminal widget that streams AI responses, accompanying tests, and barrel exports plus UI package dependency updates.

Changes

RAG Vector Store and Chat Feature

Layer / File(s) Summary
AI Embedding Capability
packages/adapters/src/ai/index.ts
AIAdapter gains embed(text: string): Promise<number[]>; useAI() implements OpenAI embeddings path and errors for unsupported providers.
Vector Store Implementation and Testing
packages/adapters/src/ai/vectorStore.ts, packages/adapters/src/ai/vectorStore.test.ts
Adds DocumentChunk/VectorStoreOptions, cosineSimilarity, chunkText, indexDirectory, and LocalVectorStore with add/query/load/save; tests cover chunking, similarity queries, persistence, and directory indexing.
RAGChat Interactive Widget and Tests
packages/ui/src/RAGChat.ts, packages/ui/src/RAGChat.test.ts
Adds RAGChatOptions and RAGChat widget: indexes docs on init, supports keyboard multi-line input, queries vector store for context, streams AI.chat tokens into assistant messages, handles errors, and renders chat; tests validate rendering, input, retrieval, streaming, and error propagation.
Package Exports and Wiring
packages/adapters/src/index.ts, packages/ui/src/index.ts, packages/ui/package.json, packages/adapters/src/commander/index.ts
Barrel exports LocalVectorStore, indexDirectory, chunkText, and types; UI package adds workspace dependency on @termuijs/adapters; commander opts cast change to program.opts() as T.

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant LocalVectorStore
  participant AIAdapter
  Client->>LocalVectorStore: query(queryText)
  LocalVectorStore->>AIAdapter: embed(queryText)
  AIAdapter-->>LocalVectorStore: embedding (number[])
  LocalVectorStore->>LocalVectorStore: compute cosineSimilarity
  LocalVectorStore->>LocalVectorStore: sort & limit
  LocalVectorStore-->>Client: DocumentChunk[]
Loading
sequenceDiagram
  participant User
  participant RAGChat
  participant LocalVectorStore
  participant AIAdapter
  User->>RAGChat: submit query
  RAGChat->>LocalVectorStore: query(userText, limit=3)
  LocalVectorStore->>AIAdapter: embed(userText)
  AIAdapter-->>LocalVectorStore: embedding
  LocalVectorStore-->>RAGChat: DocumentChunk[]
  RAGChat->>AIAdapter: chat(messages with context)
  AIAdapter-->>RAGChat: AsyncIterable<token>
  RAGChat->>RAGChat: stream tokens into display
  RAGChat-->>User: rendered response
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • Karanjot786/TermUI#562: Related prior work modifying packages/adapters/src/ai/index.ts and provider integrations for OpenAI/Anthropic.

Suggested labels

gssoc:approved, level:advanced, quality:clean

Suggested reviewers

  • Karanjot786

Poem

🐰 I hopped through docs and built a little nest,
Chunks and vectors snug, cosine scores do their best,
RAGChat listens closely, streams answers in play,
Token by token, the night turns to day — hooray!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% 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
Title check ✅ Passed The PR title accurately summarizes the main change: adding a local RAG vector store and interactive RAGChat widget to the codebase.
Description check ✅ Passed The PR description is comprehensive, covering all required template sections including description, related issue, packages affected, type of change, and completed checklists.
Linked Issues check ✅ Passed All objectives from issue #777 are met: AIAdapter.embed() added, LocalVectorStore implemented with cosine similarity and persistence, indexer supports chunking with overlap, RAGChat widget renders and streams responses, all tests pass.
Out of Scope Changes check ✅ Passed All changes remain within scope: @termuijs/adapters and @termuijs/ui packages only, no external databases, only plaintext/Markdown files, no core/store/motion/router modifications.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

@github-actions github-actions Bot added type:feature +10 pts. New feature. area:ui @termuijs/ui type:testing +10 pts. Tests. labels Jun 5, 2026
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Nitpick comments (3)
packages/adapters/src/commander/index.ts (1)

14-14: ⚡ Quick win

Add an inline justification for the type assertion on Line 14.

as T currently violates the repo rule requiring an inline explanation for type assertions.

Suggested patch
-    options: program.opts() as T,
+    options: program.opts() as T, // Commander returns an untyped options bag; caller-provided generic `T` defines the expected shape.

As per coding guidelines, "No type assertions without an inline comment explaining why."

🤖 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/adapters/src/commander/index.ts` at line 14, The type assertion on
the options assignment (options: program.opts() as T) needs an inline comment
explaining why the assertion is safe; update the line to keep the assertion but
add a brief justification comment after it (e.g., stating that commander
validates/returns the expected shape or that upstream type inference cannot
express the generic T but runtime checks cover it), referencing the exact
expression "options: program.opts() as T" so future readers know why the
assertion is allowed.
packages/ui/src/RAGChat.ts (1)

98-133: ⚡ Quick win

Remove console.error from library source code.

Line 124 logs errors directly to the console. Library code should propagate errors to the caller (e.g., via a callback or event) so consumers control logging behavior.

As per coding guidelines: packages/**/src/**/*.{ts,tsx}: No console.log in source files.

🤖 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/ui/src/RAGChat.ts` around lines 98 - 133, The catch block in
_submitQuery currently calls console.error('RAG query failed:', error) which
violates the no-console rule; remove that console call and instead propagate the
error to the consumer by invoking a dedicated error-handling hook (e.g., call
this._handleError(error) or emit an 'error' event via this.emit('error', error))
and keep the existing push of an assistant error message; update or add the
handler (e.g., _handleError or an EventEmitter hookup) so callers can subscribe
and control logging behavior and ensure _submitQuery still sets _loading = false
and markDirty() in finally.
packages/adapters/src/ai/vectorStore.ts (1)

98-150: ⚡ Quick win

Remove console.error calls from library source code.

Lines 90 and 124 use console.error to log initialization and query failures. Library code should avoid writing directly to console; instead, propagate errors to the caller or emit events/callbacks so consumers can handle logging.

As per coding guidelines: packages/**/src/**/*.{ts,tsx}: No console.log in source files.

🤖 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/adapters/src/ai/vectorStore.ts` around lines 98 - 150, Remove any
console.error calls in LocalVectorStore (e.g., in the constructor, load(),
query() or any other methods) and stop swallowing errors there; instead either
rethrow the caught error or let promise rejections propagate to the caller so
callers can handle logging. Concretely, in load() keep the ENOENT branch that
sets this._documents = [], but for other errors remove console.error and throw
the error (or do not catch it). In query() and addDocuments() remove any
console.error around AI calls and allow embed() failures to surface (or emit an
error via an EventEmitter you add to LocalVectorStore) so consumers control
logging. Ensure no console.* remains in LocalVectorStore, and preserve existing
behavior for ENOENT only.
🤖 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/adapters/src/ai/index.ts`:
- Around line 118-129: The embed method currently returns an empty array when
OpenAI's response lacks an embedding, which yields a zero-magnitude vector and
corrupts cosineSimilarity; modify the embed function (the async embed(text:
string) implementation for provider === 'openai') to detect missing
response.data[0]?.embedding and throw a descriptive Error (or rethrow the
provider error) instead of returning [] so callers can surface the failure
rather than treat it as a valid zero vector.

In `@packages/adapters/src/ai/vectorStore.ts`:
- Around line 74-96: The for-loop inside indexDirectory is shadowing the
chunkText function by using the loop variable name `chunkText`; rename that loop
variable (e.g., to `chunk`, `chunkStr`, or `chunkTextSlice`) and update its
usage in the docs.push call so it no longer conflicts with the `chunkText`
function name defined earlier, leaving the rest of indexDirectory and the call
to store.addDocuments(ai) unchanged.

In `@packages/ui/src/RAGChat.ts`:
- Around line 45-96: The constructor uses a type coercion for borderClr when
assigning this._borderColor (see RAGChat constructor and the borderClr ->
this._borderColor code) — replace the bare "as any" with a short inline comment
explaining why the runtime value cannot be typed more precisely (e.g., it can be
a named color string or a Style['fg'] object from upstream libs) and keep the
cast only with that justification; and in _initIndex replace the silent
console.error('Failed to initialize RAG index:', error) with error propagation
(either rethrow the caught error or rethrow after logging) so callers can handle
initialization failures instead of swallowing them (modify _initIndex's catch to
rethrow or remove console.error and throw the original error).
- Around line 187-293: In _renderSelf add short inline comments next to every
"as any" explaining why the cast is required and why it is safe: annotate the ba
assignment (ba = { ... } as any) to state the border color union mismatch and
why runtime value is valid; annotate fgColor (fgColor = ... as any) to explain
the intended CellAttr shape for user messages; annotate the two places in the
indexing/loading screen.writeString calls where fg is cast as any to say the
color literal is intentionally used despite TypeScript mismatch; keep each
comment one short sentence referencing the variable (ba, fgColor,
indexing/loading writeString fg) and the reason (third‑party type mismatch or
known runtime shape).

---

Nitpick comments:
In `@packages/adapters/src/ai/vectorStore.ts`:
- Around line 98-150: Remove any console.error calls in LocalVectorStore (e.g.,
in the constructor, load(), query() or any other methods) and stop swallowing
errors there; instead either rethrow the caught error or let promise rejections
propagate to the caller so callers can handle logging. Concretely, in load()
keep the ENOENT branch that sets this._documents = [], but for other errors
remove console.error and throw the error (or do not catch it). In query() and
addDocuments() remove any console.error around AI calls and allow embed()
failures to surface (or emit an error via an EventEmitter you add to
LocalVectorStore) so consumers control logging. Ensure no console.* remains in
LocalVectorStore, and preserve existing behavior for ENOENT only.

In `@packages/adapters/src/commander/index.ts`:
- Line 14: The type assertion on the options assignment (options: program.opts()
as T) needs an inline comment explaining why the assertion is safe; update the
line to keep the assertion but add a brief justification comment after it (e.g.,
stating that commander validates/returns the expected shape or that upstream
type inference cannot express the generic T but runtime checks cover it),
referencing the exact expression "options: program.opts() as T" so future
readers know why the assertion is allowed.

In `@packages/ui/src/RAGChat.ts`:
- Around line 98-133: The catch block in _submitQuery currently calls
console.error('RAG query failed:', error) which violates the no-console rule;
remove that console call and instead propagate the error to the consumer by
invoking a dedicated error-handling hook (e.g., call this._handleError(error) or
emit an 'error' event via this.emit('error', error)) and keep the existing push
of an assistant error message; update or add the handler (e.g., _handleError or
an EventEmitter hookup) so callers can subscribe and control logging behavior
and ensure _submitQuery still sets _loading = false and markDirty() in finally.
🪄 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: 1f3111de-6329-41bf-ab60-dfae7340e8a1

📥 Commits

Reviewing files that changed from the base of the PR and between f098137 and 161ff20.

📒 Files selected for processing (9)
  • packages/adapters/src/ai/index.ts
  • packages/adapters/src/ai/vectorStore.test.ts
  • packages/adapters/src/ai/vectorStore.ts
  • packages/adapters/src/commander/index.ts
  • packages/adapters/src/index.ts
  • packages/ui/package.json
  • packages/ui/src/RAGChat.test.ts
  • packages/ui/src/RAGChat.ts
  • packages/ui/src/index.ts

Comment thread packages/adapters/src/ai/index.ts
Comment thread packages/adapters/src/ai/vectorStore.ts
Comment thread packages/ui/src/RAGChat.ts
Comment thread packages/ui/src/RAGChat.ts
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (1)
packages/ui/src/RAGChat.ts (1)

87-93: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Add an inline justification for the event emitter as any cast.

Line 92 uses as any without an inline rationale. Add a short inline comment (or a typed wrapper) to justify why this cast is required.

Suggested minimal fix
-        (this.events as any).emit('error', err);
+        // Cast needed: Widget event typings do not include custom 'error', but runtime emitter supports it.
+        (this.events as any).emit('error', err);

As per coding guidelines: **/*.{ts,tsx}: Use TypeScript strict mode. No any without an inline comment explaining why. No type assertions without an inline comment explaining why.

🤖 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/ui/src/RAGChat.ts` around lines 87 - 93, In _handleError, the cast
(this.events as any) is used to access emit but lacks justification; either add
a short inline comment explaining why a typed cast is necessary (e.g., events
uses a dynamic/emitter API not expressible in current Events type) or introduce
a small typed wrapper/override that exposes emit (e.g., a local variable typed
as EventEmitter-like) and use that instead; update references in _handleError to
use the justified cast or wrapper and mention the matching reason and link to
the underlying type limitation (keep comment concise and adjacent to the
cast/wrapper).
🤖 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/ui/src/RAGChat.test.ts`:
- Line 145: Add short inline comments next to each use of `any`/`as any` in
RAGChat.test.ts to justify the cast: explain that the test is wiring a
lightweight mock that doesn't implement the full EventEmitter/interface so `} as
any` (around the mocked props) is used to bypass strict typing, that `store:
any` is a minimal partial store used only for test setup, and that `('error' as
any, ...)`/similar casts are to satisfy the event API signature on a mocked
emitter rather than the real typed EventEmitter; place each comment directly
after the cast (near the symbols in the file) so future readers know these are
deliberate test-only loosenings.

---

Duplicate comments:
In `@packages/ui/src/RAGChat.ts`:
- Around line 87-93: In _handleError, the cast (this.events as any) is used to
access emit but lacks justification; either add a short inline comment
explaining why a typed cast is necessary (e.g., events uses a dynamic/emitter
API not expressible in current Events type) or introduce a small typed
wrapper/override that exposes emit (e.g., a local variable typed as
EventEmitter-like) and use that instead; update references in _handleError to
use the justified cast or wrapper and mention the matching reason and link to
the underlying type limitation (keep comment concise and adjacent to the
cast/wrapper).
🪄 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: 0de60ec6-6a7c-4e25-a249-97b81f1891a2

📥 Commits

Reviewing files that changed from the base of the PR and between 161ff20 and d243551.

📒 Files selected for processing (5)
  • packages/adapters/src/ai/index.ts
  • packages/adapters/src/ai/vectorStore.ts
  • packages/adapters/src/commander/index.ts
  • packages/ui/src/RAGChat.test.ts
  • packages/ui/src/RAGChat.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/adapters/src/commander/index.ts
  • packages/adapters/src/ai/vectorStore.ts

Comment thread packages/ui/src/RAGChat.test.ts Outdated
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:ui @termuijs/ui type:feature +10 pts. New feature. type:testing +10 pts. Tests.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(ai): add local RAG vector store and interactive RAGChat widget

1 participant