Skip to content

feat(search): add pluggable lexical backends#713

Open
pavel-voronin wants to merge 1 commit into
tobi:mainfrom
pavel-voronin:feat/pluggable-lexical-backends-upstream
Open

feat(search): add pluggable lexical backends#713
pavel-voronin wants to merge 1 commit into
tobi:mainfrom
pavel-voronin:feat/pluggable-lexical-backends-upstream

Conversation

@pavel-voronin

Copy link
Copy Markdown

Summary

Adds a pluggable lexical search backend layer while keeping SQLite FTS5 as the default behavior.

This lets QMD delegate lexical retrieval to an external backend without replacing QMD itself. QMD remains responsible for the SQLite store, collections, document hydration, snippets, context lookup, vector search, RRF, and reranking.

What Changed

  • Added a lexical backend contract:
    • LexicalSearchBackend
    • LexicalSearchRequest
    • LexicalSearchHit
    • LexicalBackendConfig
  • Added command backend support:
    • QMD writes a JSON search request to backend stdin.
    • Backend returns JSON hits on stdout.
    • QMD hydrates returned document references from its SQLite database.
  • Added SDK support:
    • createStore({ lexicalBackend })
    • store.searchLex(...) uses the configured lexical backend.
  • Added config support:
    search:
      lexicalBackend:
        type: command
        name: custom-lexical
        command: /path/to/lexical-backend
        args: ["search", "--index-dir", "/path/to/lexical-index"]
        timeoutMs: 5000
  • Updated hybrid/structured search so lex queries use the configured lexical backend.
  • Preserved vector search behavior. Vector search does not use lexical backends.
  • Added tests for custom lexical backend hydration and hybrid search integration.
  • Documented the custom lexical backend contract.

Compatibility

SQLite FTS5 remains the default lexical backend. Existing users who do not configure search.lexicalBackend should keep the same behavior.

Search results still use source: "fts" for lexical results to preserve pipeline compatibility. A new lexicalBackend field exposes the concrete backend name for observability.

Testing

Verified locally:

  • npm run test:types
  • npm run build
  • vitest test/sdk.test.ts -t "custom lexical backend"
  • vitest test/bin-wrapper.test.ts
  • End-to-end smoke test with an external command backend:
    • qmd update
    • external backend index rebuild
    • qmd search ... --format json

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