A runnable Retrieval-Augmented Generation use-case over a folder of technical documentation, built with LangChain.js (LCEL), Chroma, OpenRouter embeddings (with a local Transformers.js option) and an OpenRouter chat model.
Ask questions about a corpus/ of Markdown docs and get grounded answers with
source citations — switchable between hosted and fully local embeddings.
| Concern | Choice |
|---|---|
| Language / runtime | TypeScript (strict, ESM), Node ≥ 22 |
| Framework | LangChain.js LCEL |
| Embeddings | OpenRouter (liquid/lfm-2.5-embedding-350m:free, 1024-dim) by default; local Transformers.js (Xenova/all-MiniLM-L6-v2) via EMBEDDINGS_PROVIDER=local |
| Vector store | Chroma (@langchain/community), persisted to .chroma/ |
| Chat model | OpenRouter via @langchain/openrouter |
Architecture lives in docs/design.md; the PNGs below are rendered from
docs/diagrams/*.js via pnpm diagrams.
Components
Ingest pipeline
Query pipeline
Data model
pnpm install
cp .env.example .env # add OPENROUTER_API_KEY
# 1. start the Chroma server (separate terminal)
pnpm engine:up
# 2. index the corpus
pnpm ingest --reset
# 3. ask a question
pnpm ask "What is the rate limit on the Free plan?"ask prints the answer followed by a Sources: list (path#chunkIndex).
| Command | Purpose |
|---|---|
pnpm engine:up |
Start the local Chroma server (persists to ./.chroma) |
pnpm ingest [--reset] |
Load corpus/ → split → embed → store |
pnpm ask "<question>" |
Retrieve + generate a cited answer |
pnpm diagrams |
Render docs/diagrams/*.js → PNG |
pnpm typecheck |
tsc --noEmit |
pnpm test |
Unit tests (no network) |
pnpm test:integration |
Chroma round-trip (needs pnpm engine:up) |
pnpm test:model |
Local embedding model check (downloads model) |
pnpm verify |
typecheck && test |
See .env.example. Key variables: OPENROUTER_API_KEY, CHAT_MODEL,
EMBEDDING_MODEL, CHROMA_URL, CHROMA_COLLECTION, CORPUS_DIR, RETRIEVER_K,
CHUNK_SIZE, CHUNK_OVERLAP.
Free models: if your OpenRouter key has no credits, set
CHAT_MODELto a:freevariant (e.g.liquid/lfm-2.5-2.6b:free). The repo's local.envdoes this by default, along with the:freeembeddings model.
Note:
EMBEDDING_MODEL(andEMBEDDINGS_PROVIDER) must match between ingest and query. Changing either requires re-runningpnpm ingest --reset(the vector dimension changes).
Drop Markdown/.txt files under corpus/ (recursively) and re-run
pnpm ingest --reset. Chunk ids are sha256(sourcePath#chunkIndex), so re-ingesting is safe.
Chroma unreachable at ...— the Chroma server isn't running:pnpm engine:up.collection "docs" is empty— runpnpm ingest --resetfirst.- OpenRouter key limit —
askrequires a key that can call the chosen model. Paid models need credits; setCHAT_MODEL/EMBEDDING_MODELto:freevariants otherwise. Free models are per-minute rate limited (the smoke eval paces calls; tune withEVAL_DELAY_MS). Model ... does not exist— provider/model mismatch:EMBEDDINGS_PROVIDER=openrouterneeds an OpenRouter model id,localneeds a Transformers.js model id.The 'path' argument is deprecated— a deprecation notice emitted by LangChain's Chroma integration; harmless.
docs/intent.md— problem, scope, constraintsdocs/design.md— architecture, interfaces, ADRsdocs/plan.md— implementation plan and traceabilitydocs/testing.md— test strategy and metrics



