Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 21 additions & 2 deletions lat.md/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,9 @@ Core search logic in [[src/cli/search.ts#runSearch]] (returns matched sections),

### Provider Detection

Requires an LLM key resolved by [[src/config.ts#getLlmKey]] in priority order:
Resolves an embedding provider from the LLM key (or lack thereof). When no key is configured, falls back to a local embedding model so search works out of the box without any API key.

Key resolution order (via [[src/config.ts#getLlmKey]]):

1. `LAT_LLM_KEY` env var — direct value
2. `LAT_LLM_KEY_FILE` env var — path to a file containing the key (read and trimmed)
Expand All @@ -312,14 +314,29 @@ Requires an LLM key resolved by [[src/config.ts#getLlmKey]] in priority order:

Provider is auto-detected from the resolved key prefix:

- *(no key)* — local embeddings via `@huggingface/transformers` (see [[cli#search#Local Embeddings]])
- `sk-...` — OpenAI (uses `text-embedding-3-small`, 1536 dims)
- `vck_...` — Vercel AI Gateway (uses `openai/text-embedding-3-small`, 1536 dims)
- `sk-ant-...` — Anthropic (not supported, errors with guidance)
- `REPLAY_LAT_LLM_KEY::<url>` — test-only replay server for offline testing

Implementation: [[src/search/provider.ts]], [[src/config.ts]]

### Embeddings
### Local Embeddings

Falls back to a local model when no API key is configured. Uses `@huggingface/transformers` (optional dep).

Three pre-defined model sizes, selected via `LAT_LOCAL_MODEL_SIZE` env var (default `small`):

- `small` — `Xenova/all-MiniLM-L6-v2`, 384 dims, ~45 MB download
- `medium` — `Xenova/bge-base-en-v1.5`, 768 dims, ~130 MB download
- `large` — `Xenova/bge-large-en-v1.5`, 1024 dims, ~330 MB download

Dimensions are known statically from the model table — no need to load the model to discover them. The pipeline promise is cached so concurrent callers share a single load. Errors (missing package, invalid size) surface before any indexing work begins.

Implementation: [[src/search/local.ts#embedLocal]]

### API Embeddings

Direct `fetch()` calls to the provider's OpenAI-compatible `/v1/embeddings` endpoint. No LangChain or other framework — keeps the dependency tree minimal. Batches up to 2048 texts per request.

Expand All @@ -331,6 +348,8 @@ Uses `@libsql/client` (Turso's libsql) in local file mode — pure JS/WASM, no n

Single `sections` table holds metadata, content, content hash, and the embedding vector. No separate vector table needed.

A `meta` table tracks the current embedding dimensions. On dimension mismatch (e.g. switching from API at 1536 to local at 384), the table is dropped and rebuilt in a single `db.batch()` call.

The database is stored at `lat.md/.cache/vectors.db` and should not be committed (included in `.gitignore` template).

Implementation: [[src/search/db.ts]]
Expand Down
4 changes: 2 additions & 2 deletions lat.md/tests/mcp.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,5 +40,5 @@ Semantic search via `lat_search` for a login/security query returns results cont
## lat_search finds performance section
Semantic search for a latency/response-times query returns results containing the Performance section.

## lat_search returns no results message
When `LAT_LLM_KEY` is not set, `lat_search` returns an error with `isError: true` explaining the missing key.
## lat_search works without an API key
When `LAT_LLM_KEY` is not set, `lat_search` handles the no-key case gracefully. If `@huggingface/transformers` is installed, returns search results via local embeddings. If not, returns a clean error with install guidance. Either way, no crash.
14 changes: 13 additions & 1 deletion lat.md/tests/search.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,19 @@ Tests in `tests/search.test.ts`.

## Provider Detection

Unit tests (always run). Verify `detectProvider` correctly identifies OpenAI (`sk-`), Vercel (`vck_`), rejects Anthropic (`sk-ant-`) with a helpful message, and rejects unknown prefixes.
Unit tests (always run). Verify `detectProvider` identifies providers by key prefix and returns local with correct dimensions when no key is given.

Covers OpenAI (`sk-`), Vercel (`vck_`), local (no key, 384 dims), Anthropic rejection (`sk-ant-`), and unknown prefix rejection.

## Schema Dimension Mismatch

Verify that `ensureSchema` rebuilds the sections table when stored dimensions differ from the provider's.

Creates a DB at 1536 dimensions with a row, re-inits at 384, asserts the table is empty and the new dimension is stored in meta.

## Local Embedding

Gated on `@huggingface/transformers` availability. Verifies that `embedLocal` produces normalized vectors with correct dimensions (384 for `Xenova/all-MiniLM-L6-v2`) and that semantically similar texts rank closer than unrelated ones.

## RAG Replay Tests

Expand Down
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@
"typescript": "^5.7.0",
"vitest": "^3.0.0"
},
"optionalDependencies": {
"@huggingface/transformers": "^3.8.1"
},
"dependencies": {
"@folder/xdg": "^4.0.1",
"@libsql/client": "^0.17.0",
Expand Down
Loading