Prima Get an API key

PrimaChat

Stone, search

Keyword search always, embeddings when the repository has been indexed, and one citation format that points at a section a person can open.

Live Built and working today on the surface this card describes.

What it does

BM25 keyword matching runs on every search. Embeddings are added on top when a semantic index exists for that repository, and the two score sets are fused rather than chosen between.

Keyword search is the floor, not the fallback. A knowledge base is full of proper nouns, client names and endpoint paths, and lexical match is the correct behaviour for those. Embeddings are for the questions phrased in words the document never uses.

Every hit says which signals fired, as via: ["lexical"], ["semantic"] or both, so a bad hit is explainable.

The semantic index is built by an explicit call, not on connect. Indexing bills one embedding request per chunk, and a product that spends that silently when a repository is connected is a product that earns an angry invoice email.

Inputs and outputs

GET /api/stone/repos/:id/search?q=&limit= {hits, semantic}. Each hit carries path, heading, trail, line, label, score, via and a snippet.
Label path § heading, or the path alone for text before the first heading. This is what a citation says out loud.
POST /api/stone/repos/:id/index {cap?} in. {progress, repo} back. Builds or rebuilds the semantic index.
Empty query {hits: []}. Not an error.

Configuration

limit 1 to 20 on the HTTP route, default 6.
Index cap A per-call cap on chunks to embed, itself capped by the deployment's own ceiling. It exists so a first index of a large repository can be bounded.
Chunk size Sections are the unit. A section longer than about 2,400 characters is split on paragraph boundaries, and every piece keeps the same heading, so a citation still resolves to something openable.

Limits

  • Chunking is by heading, not by a fixed token window, because the thing a citation has to point at is a section a person can open, not a character range. A window-based splitter makes the chunk and the citation two objects that drift apart.
  • Search over a repository with no semantic index still works, and the response says semantic: false. It is not degraded, it is lexical.
  • Indexing costs real money and is not free to repeat. Rebuild it when the repository has changed enough to be worth it, not on a schedule.
  • Content read out of a repository is treated as untrusted before it reaches the model. See the tools card.

Example

a citation, as it renders

handbook/holidays.md § Carry-over  (Line 42)

Source of truth

  • prima-chat/server/search.ts (BM25 plus optional embeddings, score fusion, the `via` field, why the index is opt-in)
  • prima-chat/server/wiki.ts (`chunkMarkdown`, the 2,400-character split, `citationLabel`)
  • prima-chat/server/routes/stone.ts (the search and index routes, and the limit ceiling)