Skip to content

feat(store): add zero-dependency local fallback stores and make native deps optional#92

Merged
amondnet merged 8 commits intomainfrom
feat/local-vector-store
Feb 23, 2026
Merged

feat(store): add zero-dependency local fallback stores and make native deps optional#92
amondnet merged 8 commits intomainfrom
feat/local-vector-store

Conversation

@amondnet
Copy link
Contributor

@amondnet amondnet commented Feb 20, 2026

Summary

  • Add LocalVectorStore: a JSON file-based vector store with cosine similarity search and zero native dependencies, replacing NullVectorStore as the default in DefaultContextStore
  • Move @lancedb/lancedb to optionalDependencies in both @pleaseai/rpg-store and @pleaseai/rpg-utils, making LanceDB an opt-in dependency
  • Remove NullVectorStore (packages/store/src/null-vector-store.ts) and the vectorEnabled field from ContextStoreConfig

Changes

  • packages/store/src/local/vector-store.ts — new LocalVectorStore implementation (JSON persistence, cosine similarity)
  • packages/store/src/local/index.ts — barrel export for ./local sub-path
  • packages/store/src/default-context-store.ts — switch default vector store from LanceDB to LocalVectorStore
  • packages/store/src/types.ts — remove vectorEnabled field from ContextStoreConfig
  • packages/store/package.json@lancedb/lancedb moved to optionalDependencies; added ./null-vector-store and ./local sub-path exports
  • packages/utils/package.json@lancedb/lancedb moved to optionalDependencies
  • packages/graph/src/rpg.ts — remove vectorEnabled: false from store.open() call
  • tests/store/local-vector-store.test.ts — 10 unit tests for LocalVectorStore

Test Plan

  • Run bun run test tests/store/local-vector-store.test.ts — all 10 unit tests pass
  • Run bun run build — build succeeds without LanceDB native bindings required
  • Run bun run typecheck — no type errors
  • Verify DefaultContextStore works without @lancedb/lancedb installed

Summary by cubic

Adds LocalVectorStore and local graph/text stores so the app runs without native deps. DefaultContextStore uses the local vector store and falls back to local graph/text when SQLite isn’t available; SemanticSearch injects a VectorStore and LanceDB is optional.

  • New Features

    • LocalVectorStore with JSON persistence at {path}/vectors.json and cosine search; supports a 'memory' path, cleans up its temp dir on close, and throws on embedding dimension mismatch.
    • Added LocalGraphStore (JSON-backed) and LocalTextSearchStore (in-memory). DefaultContextStore falls back to these when better-sqlite3 isn’t available.
    • SemanticSearch now takes a VectorStore via constructor; MCP server and tests updated to use LocalVectorStore. Hybrid/FTS paths log and fall back to vector-only.
    • rpg-store: added ./local export; moved @lancedb/lancedb and better-sqlite3 to optionalDependencies. Removed the LanceDB-based VectorStore from rpg-utils.
    • Tests: 10 unit tests for LocalVectorStore plus 38 tests for LocalGraphStore/TextSearchStore; removed obsolete hybrid test.
    • Robustness: validate LocalVectorStore.open() config.path; error on corrupt vectors.json; optional clear() guarded; DefaultContextStore.close() aggregates errors; MCP checks {path}/vectors.json before reindexing; LocalGraphStore.close() cleans up its temp dir; DefaultContextStore falls back to local stores only on MODULE_NOT_FOUND errors (other SQLite errors are rethrown).
  • Migration

    • Update SemanticSearch usage to pass a VectorStore instance; remove dbPath/tableName.
    • Remove vectorEnabled from ContextStoreConfig.
    • LanceDB and better-sqlite3 are opt-in; install them only if you prefer their engines over the local stores.

Written for commit 1f20881. Summary will update on new commits.

- Add LocalVectorStore: JSON file-based vector store with cosine similarity
  and no native dependencies as the new default in DefaultContextStore
- Move @lancedb/lancedb to optionalDependencies in both rpg-store and rpg-utils
- Remove NullVectorStore (replaced by LocalVectorStore)
- Remove vectorEnabled field from ContextStoreConfig
- Add ./null-vector-store and ./local sub-path exports to rpg-store
- Add 10 unit tests for LocalVectorStore
@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @amondnet, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces a new, lightweight LocalVectorStore that utilizes JSON files for vector persistence and cosine similarity for search, effectively removing the need for native dependencies in the default vector store setup. By making @lancedb/lancedb an optional dependency, the system gains flexibility, allowing users to choose whether to install LanceDB based on their specific needs. The DefaultContextStore has been updated to use this new local store by default, simplifying the initial setup and reducing the overall dependency footprint.

Highlights

  • New LocalVectorStore: Introduced LocalVectorStore, a new JSON file-based vector store with cosine similarity search and zero native dependencies, replacing NullVectorStore as the default in DefaultContextStore.
  • Optional LanceDB Dependency: Moved @lancedb/lancedb to optionalDependencies in both @pleaseai/rpg-store and @pleaseai/rpg-utils, making LanceDB an opt-in dependency.
  • Refactored ContextStore: Removed NullVectorStore and the vectorEnabled field from ContextStoreConfig, simplifying the context store configuration and implementation.
Changelog
  • bun.lock
    • Added better-sqlite3 to trustedDependencies.
  • package.json
    • Removed better-sqlite3 from the build script's external dependencies.
    • Removed the postinstall script for better-sqlite3.
    • Added better-sqlite3 to trustedDependencies.
  • packages/store/package.json
    • Moved @lancedb/lancedb from dependencies to optionalDependencies.
    • Added new exports for ./null-vector-store and ./local.
  • packages/store/src/default-context-store.ts
    • Updated the class description to reflect the use of LocalVectorStore.
    • Removed imports for mkdtempSync, tmpdir, join, and rmSync.
    • Removed the _tempVectorPath private field.
    • Switched the import of LanceDBVectorStore to LocalVectorStore.
    • Modified the vectorPath logic to simplify memory handling.
    • Simplified the close method by removing temporary directory cleanup logic.
  • packages/store/src/local/index.ts
    • Added an export for LocalVectorStore.
  • packages/store/src/local/vector-store.ts
    • Implemented the LocalVectorStore class, providing JSON file-based vector storage with cosine similarity.
    • Included methods for open, close, upsert, remove, search, upsertBatch, and count.
    • Added a cosineSimilarity utility function.
  • packages/utils/package.json
    • Moved @lancedb/lancedb from dependencies to optionalDependencies.
  • packages/utils/src/index.ts
    • Removed the export of VectorStore.
  • tests/store/local-vector-store.test.ts
    • Added comprehensive unit tests for LocalVectorStore, covering initialization, search, upsert, remove, batch operations, and persistence.
Activity
  • The pull request includes a test plan to verify the new LocalVectorStore functionality and the optional nature of LanceDB.
  • Unit tests for LocalVectorStore are expected to pass by running bun run test tests/store/local-vector-store.test.ts.
  • The build process should succeed without requiring LanceDB native bindings by running bun run build.
  • Type checking should report no errors by running bun run typecheck.
  • Verification is planned to ensure DefaultContextStore works correctly without @lancedb/lancedb installed.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

The introduction of LocalVectorStore is a great architectural improvement to make LanceDB an optional dependency, simplifying the setup for environments where native binaries are difficult to manage. However, the LocalVectorStore implementation has several issues regarding resource management and performance that should be addressed. Specifically, it leaks temporary directories in memory mode and uses blocking synchronous I/O operations in asynchronous methods. There is also a broken export in packages/store/package.json for a file that was reportedly removed.

Copy link

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

7 issues found across 9 files

Prompt for AI agents (all issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="packages/utils/package.json">

<violation number="1" location="packages/utils/package.json:34">
P2: @lancedb/lancedb is imported directly by @pleaseai/rpg-utils, so it must remain a required dependency. Keeping it optional can break runtime module resolution when vector.ts is used. Move it back to dependencies or remove/guard the import.

(Based on your team's feedback about keeping directly imported native DB modules in dependencies.) [FEEDBACK_USED]</violation>
</file>

<file name="packages/store/src/local/vector-store.ts">

<violation number="1" location="packages/store/src/local/vector-store.ts:31">
P2: Temporary directories created for memory mode are never cleaned up in close(), which can leak disk space over time. Track the auto-created path and remove it during close().

(Based on your team's feedback about cleaning up mkdtempSync-created directories.) [FEEDBACK_USED]</violation>

<violation number="2" location="packages/store/src/local/vector-store.ts:107">
P2: `Math.min(a.length, b.length)` silently truncates the longer vector when dimensions differ, producing incorrect cosine similarity scores. Vectors with mismatched dimensions should either throw an error (to surface embedding bugs early) or at minimum treat missing dimensions as 0 by using `Math.max` instead of `Math.min`.</violation>
</file>

<file name="packages/store/package.json">

<violation number="1" location="packages/store/package.json:19">
P2: The new export "./null-vector-store" points to ./src/null-vector-store.ts, but that file no longer exists. This will break package exports resolution for consumers trying to import the sub-path. Remove the export or restore the file.</violation>
</file>

<file name="package.json">

<violation number="1" location="package.json:18">
P2: Native module better-sqlite3 is still imported in the codebase, but the build script no longer marks it as external. Bun build will try to bundle the native bindings, which typically breaks native modules at build or runtime. Keep better-sqlite3 external to avoid bundling native binaries.</violation>
</file>

<file name="packages/store/src/default-context-store.ts">

<violation number="1" location="packages/store/src/default-context-store.ts:52">
P2: If _vector.close() throws, _text and _graph are never closed, which can leak resources. Ensure all stores attempt to close even if one fails (e.g., use Promise.allSettled and rethrow).</violation>
</file>

<file name="tests/store/local-vector-store.test.ts">

<violation number="1" location="tests/store/local-vector-store.test.ts:65">
P2: Add a non-empty assertion before using `.every()` so the test doesn't pass vacuously when `results` is empty.

(Based on your team's feedback about asserting non-empty collections before using Array.prototype.every() in tests.) [FEEDBACK_USED]</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

… SemanticSearch

- Delete packages/utils/src/vector.ts (LanceDB-based VectorStore implementation)
- Remove @lancedb/lancedb dependency and ./vector export from packages/utils/package.json
- Remove vector type re-exports from packages/utils/src/index.ts
- Rewrite SemanticSearch to accept VectorStore interface via constructor injection (removed dbPath)
- Update MCP server to create LocalVectorStore and inject it into SemanticSearch
- Update all encoder and tools tests to use LocalVectorStore from @pleaseai/rpg-store
Copy link

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

1 issue found across 10 files (changes from recent commits).

Prompt for AI agents (all issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="packages/encoder/src/semantic-search.ts">

<violation number="1" location="packages/encoder/src/semantic-search.ts:88">
P2: `upsertBatch` always falls through to the per-document upsert because `await ...` resolves to `undefined`. This duplicates work and can create duplicate writes when `upsertBatch` exists.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

…ee-sitter deps

- Add --external better-sqlite3 to build script
- Move @lancedb/lancedb back to optionalDependencies in rpg-store
- Remove stale ./null-vector-store export from rpg-store package.json
- Add tree-sitter-python and tree-sitter-typescript to root devDependencies
  so Node.js can resolve them when running the built CLI
- Delete obsolete tests/utils/vector-hybrid.test.ts (imports deleted module)
- Add direct clear() unit test (count and search return empty)
- Add clear() persistence test (data absent after reopen)
- Add SemanticSearch.clear() no-op test when VectorStore lacks clear
- Strengthen searchFts fallback assertion: exact count instead of >=0
- Validate config.path in LocalVectorStore.open() with clear TypeError
- Narrow catch to ENOENT only; corrupt vectors.json now throws instead of silently resetting
- Wrap flush() in close() with try/finally so index/filePath always reset
- Add clear() to VectorStore interface (optional) and LocalVectorStore
- Fix upsertBatch dispatch: use explicit if/else instead of ?. + ?? to avoid void/undefined confusion
- Add SemanticSearch.clear() guarded by VectorStore.clear?
- Add log.debug() in searchHybrid and searchFts to surface degraded-mode fallback
- Aggregate errors in DefaultContextStore.close() so all stores close even if one fails
- Fix existsSync check in MCP server to target vectors.json, not the directory
…-sqlite3 optional

- Add LocalGraphStore: zero-dependency in-memory+JSON-file GraphStore fallback
- Add LocalTextSearchStore: in-memory term-frequency TextSearchStore fallback
- DefaultContextStore falls back to local stores when better-sqlite3 is unavailable
- Move better-sqlite3 to optionalDependencies in @pleaseai/rpg-store
- Add 38 unit tests covering all store operations and persistence
@amondnet amondnet changed the title feat(store): add LocalVectorStore and make @lancedb/lancedb optional feat(store): add zero-dependency local fallback stores and make native deps optional Feb 20, 2026
Copy link

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

3 issues found across 7 files (changes from recent commits).

Prompt for AI agents (all issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="packages/store/src/local/graph-store.ts">

<violation number="1" location="packages/store/src/local/graph-store.ts:55">
P2: Temp directories created for memory mode are never cleaned up, so each open/close leaves a directory behind. Track the auto-created path and remove it in close() to avoid accumulating temp files.

(Based on your team's feedback about cleaning up mkdtempSync directories.) [FEEDBACK_USED]</violation>
</file>

<file name="packages/store/src/default-context-store.ts">

<violation number="1" location="packages/store/src/default-context-store.ts:43">
P2: This bare `catch` swallows any SQLite initialization/open error and silently falls back to the local stores, which can mask real DB failures and lead to writing data to the wrong backend. Only fall back when the optional SQLite module is missing; otherwise rethrow.</violation>
</file>

<file name="packages/store/package.json">

<violation number="1" location="packages/store/package.json:26">
P2: better-sqlite3 is imported directly by SQLiteGraphStore/SQLiteTextSearchStore, so making it optional can break runtime when those modules load. Keep it in dependencies (or make the imports truly optional/lazy).

(Based on your team's feedback about declaring native database modules as dependencies when imported directly.) [FEEDBACK_USED]</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

…ismatch

- Track auto-created temp directory in LocalVectorStore and remove it
  in close() to prevent orphaned directories from accumulating
- Replace Math.min dimension truncation with a strict dimension check
  that throws on mismatch to surface embedding bugs early
- Add non-empty assertion before .every() in remove test to prevent
  vacuous truth pass when results array is empty
…catch

- LocalGraphStore.close() now removes auto-created mkdtemp dirs (same
  pattern as LocalVectorStore._tempDir)
- DefaultContextStore.open() catch block now only falls back to local
  stores on MODULE_NOT_FOUND errors; other errors are rethrown to
  surface real SQLite failures
@sonarqubecloud
Copy link

Quality Gate Failed Quality Gate failed

Failed conditions
4.4% Duplication on New Code (required ≤ 3%)

See analysis details on SonarQube Cloud

@amondnet amondnet merged commit edf43fe into main Feb 23, 2026
6 of 7 checks passed
@amondnet amondnet deleted the feat/local-vector-store branch February 23, 2026 12:24
This was referenced Feb 23, 2026
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