Commit Graph

41 Commits

Author SHA1 Message Date
Tobias Lütke
5b48bcb6c1
Merge pull request #389 from sonwr/fix-issue-380-cleanup-no-sqlite-vec
fix: skip cleanup when sqlite-vec is unavailable
2026-03-14 08:07:11 -04:00
programcaicai
809aa36172 fix: bound memory usage during embed 2026-03-13 17:39:17 +08:00
sonwr
7df09e8235 fix: skip vector cleanup when sqlite-vec is unavailable 2026-03-12 13:51:20 +00:00
nkkko
b16d77146a feat(skill): install packaged qmd skill 2026-03-10 23:18:15 +01:00
Tobi Lutke
55f16460d0
fix(ci): guard LLM calls in CI and increase test timeouts
Add _ciMode flag to LlamaCpp that throws immediately on embedBatch,
generate, expandQuery, and rerank when CI=true — prevents silent 30s
timeouts. Skip MCP HTTP Transport tests in CI (they instantiate a real
LlamaCpp). Bump vitest/bun test timeouts to 60s for slower CI runners.
2026-03-10 13:28:37 -04:00
Tobi Lutke
ed0249fd6b
fix(test): increase timeout for SDK search tests that trigger LLM expansion
These tests load the query expansion model on first call, which
consistently exceeds the 30s timeout on CI runners.
2026-03-10 12:59:46 -04:00
Tobi Lutke
c68904fe08
refactor: move CLI and MCP to subdirectories, MCP consumes SDK
Move frontends into src/cli/ and src/mcp/ to separate them from the
core library. The MCP server is fully rewritten to import only from
the SDK (src/index.ts) — zero direct store.ts/collections.ts/llm.ts
access.

- src/qmd.ts → src/cli/qmd.ts
- src/formatter.ts → src/cli/formatter.ts
- src/mcp.ts → src/mcp/server.ts (rewritten to use QMDStore SDK)
- New src/maintenance.ts: Maintenance class for CLI housekeeping
- SDK gains: getDocumentBody(), getDefaultCollectionNames(),
  extractSnippet/addLineNumbers/DEFAULT_MULTI_GET_MAX_BYTES exports,
  getDefaultDbPath re-export, InternalStore type export
- package.json bin/scripts updated for new paths
- All 692 tests pass
2026-03-10 11:39:55 -04:00
Tobi Lutke
839d774a06
feat: redesign SDK search API with unified search() and ExpandedQuery type
Replace three separate search methods (query, search, structuredSearch)
with a single search(options) that accepts either a query string
(auto-expanded) or pre-expanded queries. Add searchLex/searchVector
convenience methods and expandQuery for manual control.

Unify StructuredSubSearch and ExpandedQuery into a single ExpandedQuery
type with { type, query } used throughout the pipeline. Add skipRerank
option to hybridQuery and structuredSearch for fast no-LLM searches.

New SDK surface:
- search({ query, intent, rerank, limit, ... })
- search({ queries: expanded })
- searchLex(query, opts)
- searchVector(query, opts)
- expandQuery(query, { intent })
2026-03-10 11:04:45 -04:00
Tobi Lutke
040c6fa904
feat: add SDK/library mode for programmatic access
Allow QMD to be used as a library (`import { createStore } from '@tobilu/qmd'`)
in addition to CLI and MCP modes. The constructor requires explicit dbPath and
either a configPath (YAML file) or inline config object — no defaults assumed,
making it safe to embed in any application.

- Add src/index.ts entry point with QMDStore interface exposing search,
  retrieval, collection/context management, and index health
- Add setConfigSource() to collections.ts for inline config support
  (in-memory config with no file I/O)
- Add main/types/exports fields to package.json
- Add SDK documentation section to README
- Add 56 unit tests covering constructor, collections, contexts, search,
  document retrieval, config isolation, YAML persistence, and lifecycle
2026-03-08 15:59:22 -04:00
Tobi Lutke
ad38c1f698
feat: add intent parameter for query disambiguation
Add optional `intent` parameter that steers query expansion, reranking,
chunk selection, and snippet extraction without searching on its own.

When a query like "performance" is ambiguous (web-perf vs team health vs
fitness), intent provides background context that disambiguates results
across all pipeline stages:

- expandQuery: includes intent in LLM prompt ("Query intent: {intent}")
- rerank: prepends intent to rerank query for Qwen3-Reranker
- chunk selection: intent terms scored at 0.5x weight vs query terms
- snippet extraction: intent terms scored at 0.3x weight
- strong-signal bypass: disabled when intent provided

Available via CLI (--intent flag or intent: line in query documents),
MCP (intent field on query tool), and programmatic API.

Adapted from PR #180 (thanks @vyalamar).
2026-03-07 19:27:29 -04:00
Tobi Lutke
e3549dab1a
perf(rerank): cap parallelism, deduplicate chunks, cache by content
- Cap rerank contexts at 4 to avoid VRAM exhaustion on high-core machines
- Deduplicate identical chunk texts before sending to reranker
- Cache rerank scores by chunk content instead of file path — same text
  from different files now shares a single reranker call
- Add truncation cache to avoid re-tokenizing duplicate documents
2026-03-07 15:57:36 -04:00
vyalamar
b068ad0dd6
feat(query): add --explain score traces for hybrid search 2026-03-07 14:35:10 -04:00
Tobias Lütke
8bd93366ad
Merge pull request #228 from amsminn/fix-empty-results-format
fix(cli): prevent parser breakage on empty results across output formats
2026-03-07 14:25:16 -04:00
Tobias Lütke
ee08997f23
Merge pull request #313 from 0xble/fix/expand-context-size-config
fix(llm): make query expansion context size configurable
2026-03-07 14:25:04 -04:00
Tobias Lütke
a28163fb2c
Merge pull request #304 from sebkouba/feature/collection-ignore
feat: add ignore patterns for collections
2026-03-07 14:25:02 -04:00
Tobias Lütke
e6b50cfca9
Merge pull request #308 from debugerman/fix/handelize-emoji-crash
fix(store): handle emoji-only filenames in handelize (#302)
2026-03-07 14:24:59 -04:00
Brian Le
0dec1df047
fix(llm): make expansion context size configurable 2026-03-06 16:35:33 -05:00
Brian Le
49d5b4f450
fix(index): deactivate stale docs on empty collection updates 2026-03-06 16:29:52 -05:00
Ning
dc777e3be0
fix(store): handle emoji-only filenames in handelize (#302)
Convert emoji codepoints to hex representation (e.g. 🐘 → 1f418) instead
of crashing, so files like 🐘.md can be indexed without halting the
entire update process.

Fixes #302
2026-03-06 14:24:24 +08:00
Sebastian Kouba
fde542cd0d feat: add ignore patterns for collections
Add an optional 'ignore' field to collection config that accepts an array
of glob patterns to exclude from indexing. This allows collections to skip
specific subdirectories without needing separate collections.

Example YAML config:
  personal:
    path: ~/personal_synced
    pattern: '**/*.md'
    ignore:
      - 'Sessions/**'
      - 'archive/**'

The ignore patterns are passed to fast-glob's ignore option alongside the
existing hardcoded excludes (node_modules, .git, etc). Already-indexed
files matching new ignore patterns are deactivated on the next update.

Changes:
- Add ignore?: string[] to Collection interface
- Pass ignore patterns through to fast-glob in indexFiles()
- Show ignore patterns in collection list/status output
- 5 new CLI integration tests covering the feature
2026-03-05 19:17:44 +01:00
CHAEWAN KIM
b024693f5d
Merge branch 'main' into fix-empty-results-format 2026-02-23 22:36:21 -08:00
Tobi Lütke
5233e676d9
fix(rerank): truncate documents exceeding 2048-token context size
node-llama-cpp throws a hard error when any document + query + template
overhead exceeds the ranking context size. Truncate oversized documents
using the rerank model's tokenizer before passing them to rankAll().

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 12:41:59 -05:00
Tobi Lutke
64ef25e1f6
Document query grammar and add skill helpers 2026-02-22 13:36:08 -04:00
Tobi Lutke
c7e8ea02a5
test: restructure container smoke tests for interactive use
Replaces the inner test script with an outer driver that runs individual
podman/docker commands against a pre-built image. Tests sqlite-vec
loading and store unit tests under both node and bun runtimes.

Supports --build (image only), --shell (interactive), and -- CMD
(arbitrary command) for debugging install issues in isolation.
2026-02-22 11:09:36 -04:00
Tobi Lutke
0b57711d32
refactor: replace bash wrapper with standard #!/usr/bin/env node shebang
The qmd bin was a custom bash script that discovered node via hardcoded
fallback paths (mise, asdf, nvm, homebrew). This was nonstandard and
caused ABI mismatches when installed via bun (native modules compiled
for bun but executed with node).

Now uses the standard npm bin convention: dist/qmd.js with a node
shebang, added by the build script. The isMain guard resolves symlinks
so it works when npm/bun create symlinked bin entries.

Also converts all dynamic require() calls in tests to ESM imports, and
adds container-based smoke tests (test/smoke-install.sh) that verify
install + run under both node and bun via mise in a Debian container.
2026-02-22 11:09:36 -04:00
CHAEWAN KIM
7579a2c017 fix(cli): prevent parser breakage on empty results across output formats
- --json: []
- --csv: header only
- --xml: <results></results>
- --md/--files: empty output
- default CLI keeps human-readable message
2026-02-19 15:38:42 -08:00
Tobi Lütke
3b87e3e224
feat: query document format, lex phrase/negation syntax, training data
The 'query document' is now a first-class concept in QMD: a structured
document with typed sub-queries that combine for best recall.

## Query types
- lex:    BM25 keyword search with phrase and negation syntax
- vec:    Semantic vector search (natural language questions)
- hyde:   Hypothetical document (write the expected answer)
- expand: Auto-expand via local LLM (max 1, default for plain queries)

## Lex syntax
Full BM25 operator support:
  "exact phrase"     verbatim match, no prefix
  -term              exclude documents containing term
  -"exact phrase"   exclude documents containing phrase

Examples:
  "C++ performance" optimization -sports -athlete
  "connection pool" timeout -redis
  "machine learning" -sports -athlete

## MCP tool description rewritten
The 'query' tool description now fully teaches AI agents the query
document format, lex syntax, and strategy for combining types.
Includes worked examples including intent-aware lex (C++ performance,
not sports) which is critical for disambiguation in dense corpora.

## Unit tests
11 new lex parser tests covering:
- plain terms, quoted phrases, negation, combined
- intent-aware disambiguation (performance -sports -athlete)
- only-negation returns null (FTS5 constraint)
- empty/whitespace handling

## Training data
12 new intent-aware examples for next model training round:
- Real technical topics with lex phrase+negation combinations
- Covers: C++ perf, Python memory, DB connections, rate limiting,
  SQL optimization, ML overfitting, Docker, JWT, async/await,
  git conflicts, Kubernetes, React state
- Each shows how context/intent shapes lex query construction
  (e.g. performance with C++ context → -sports -athlete exclusions)
2026-02-19 06:52:58 -05:00
Tobi Lütke
4649069e62
feat: add expand: type, rename to query, document syntax
BREAKING CHANGES:
- MCP tool renamed: structured_search → query
- HTTP endpoint renamed: /search → /query

New features:
- expand: type auto-expands via local LLM (max 1 per query)
- docs/SYNTAX.md formal grammar for query documents
- lex syntax: "phrase", -negation documented

Query types: lex, vec, hyde, expand
Default (no prefix) = expand (backwards compatible)
2026-02-18 22:22:50 -05:00
Tobi Lütke
de3a83a553
refactor: remove OR operator from lex queries
Simplify to just: terms, "phrases", and -negation
2026-02-18 22:17:52 -05:00
Tobi Lütke
efb39616e6
feat(lex): add query syntax for exact phrases, negation, and OR
Lex queries now support:
- "exact phrase" - quoted exact matching (no prefix)
- -term or -"phrase" - exclude from results
- term1 OR term2 - match either term

Semantic queries (vec/hyde) validate and reject these operators
with helpful error messages.

Examples:
  performance -sports     → matches "performance" excluding "sports"
  "machine learning"      → exact phrase match
  auth OR authentication  → matches either term
2026-02-18 22:14:09 -05:00
Tobi Lütke
19284ddb80
refactor(mcp): remove deprecated search tools, keep only structured_search
BREAKING CHANGE: MCP tools search, vector_search, deep_search removed.
Use structured_search with lex/vec/hyde queries instead.

- Remove search, vector_search, deep_search MCP tool registrations
- Update MCP instructions to focus on structured_search
- Update skill docs to reflect simplified API
- Rename test describes to reflect they test store functions
- CLI commands (qmd search, vsearch, query) unchanged for backwards compat
2026-02-18 21:50:25 -05:00
Tobi Lütke
db44e1a5bc
test: add comprehensive tests for structured search
32 tests covering:
- parseStructuredQuery parser (24 tests)
  - plain queries returning null
  - single/multiple prefixed queries
  - mixed plain + prefixed lines
  - error on multiple plain lines
  - whitespace handling
  - edge cases (colons in text, etc.)
- StructuredSubSearch type validation (3 tests)
- structuredSearch function basics (5 tests)
  - empty searches
  - no matches
  - limit/minScore options
2026-02-18 21:39:40 -05:00
Tobi Lutke
648779a04d
fix(test): reset currentIndexName between test files
collections-config.test.ts set currentIndexName to "myindex" in its
last test but only restored env vars in afterEach — not the module
variable. Under bun test (single process), this leaked into mcp.test.ts,
causing it to look for myindex.yml instead of index.yml.

Fix: reset setConfigIndexName("index") in afterEach, and add defensive
reset in mcp.test.ts beforeAll.
2026-02-18 15:53:58 -04:00
Tobi Lutke
640ac13cd0
fix: support multiple -c collection filters in search commands
Closes #191 (thanks @openclaw)
2026-02-16 14:03:53 -04:00
Tobi Lutke
8c2282c979
fix: respect XDG_CONFIG_HOME in collection config path
Closes #190 (thanks @openclaw)
2026-02-16 14:03:49 -04:00
Tobi Lutke
93f277c5e3
fix: MCP session support and cross-runtime test compat
- mcp.ts: add sessionIdGenerator to HTTP transport (fixes "stateless
  transport cannot be reused" error in CI)
- test-preload.ts: set 30s default timeout for bun test runner (matches
  vitest config, prevents CLI subprocess test timeouts)
- mcp.test.ts: use == null check instead of toBeUndefined for SQLite
  get() result (bun:sqlite returns null, better-sqlite3 returns undefined)
2026-02-15 21:54:25 -04:00
Tobi Lutke
edc9a87234
fix: correct test paths after moving to test/ directory
- cli.test.ts: fix qmdScript path from <root>/qmd.ts to <root>/src/qmd.ts
  (broke when tests moved from src/integration/ to test/)
- mcp.test.ts: forward Mcp-Session-Id header per MCP Streamable HTTP spec
2026-02-15 21:46:45 -04:00
Tobi Lutke
870d3aed3b
test: move all tests to flat test/ directory
No more src/models/ and src/integration/ subfolders to forget about.
All 9 test files live in test/, one command runs everything:

  npx vitest run test/
  bun test test/
2026-02-15 21:37:47 -04:00
Tobi Lutke
431f6e505b
Fix qmd embed crash and resolve all TypeScript errors
- Fix ReferenceError in vectorIndex(): firstResult was used but never
  defined. Added code to embed first chunk to get embedding dimensions.

- Fix 87 TypeScript errors across codebase:
  - formatter.ts: Define MultiGetFile type locally (was missing from store.ts)
  - collections.ts: Add non-null assertion for array access
  - mcp.ts: Fix StatusResult type to match store.ts CollectionInfo,
    add list parameter to ResourceTemplate, fix undefined checks
  - qmd.ts: Fix boolean/string type coercions, undefined array access
  - llm.test.ts: Update expandQuery tests for Queryable[] return type,
    fix array access assertions
  - store.test.ts: Add non-null assertions for array access in tests
  - eval-harness.ts: Fix array access assertion
2025-12-31 13:32:30 -04:00
Tobi Lutke
945d4b4572
Add 6 synthetic evaluation documents
Topics covered:
- API design principles
- Startup fundraising memo
- Distributed systems overview
- Product launch retrospective
- Machine learning primer
- Remote work policy

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-21 13:10:35 -04:00
Tobi Lutke
7828566333
Add evaluation harness with synthetic test documents
- 6 public-style documents covering diverse topics
- 18 test queries: 6 easy, 6 medium, 6 hard
- Easy: exact keyword matches
- Medium: semantic/conceptual queries
- Hard: partial recall, indirect references
- Measures Hit@1, Hit@3, Hit@5 by difficulty
- Tests both search (BM25) and query (hybrid) modes

Run: bun test/eval-harness.ts

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-21 13:10:24 -04:00