Files
noema/docs/site/pages/llm-providers.md
T
Andy Dixon 67a825a995 docs: static documentation site generator and authored pages
Twenty-six pages covering concepts, architecture, every subsystem, an
interface tour with screenshots, the full API route table generated from
openapi.json, the CLI, security and threat model, operations, ADRs,
development history and a glossary. Built into the dixon.cx page skeleton,
unlinked and noindex.
2026-07-07 23:34:55 +01:00

4.0 KiB

Language models are services a mind calls for specific cognitive tasks. They are configured per role, replaceable without touching the mind, recorded on every call, budgeted, and given untrusted text only inside delimited data blocks. This page explains the roles, the providers, routing, budgets, recording and replay, and versioned prompts.

Roles

Role Task Deterministic fallback
perceive understand a message or document into structured data lexical analysis
respond turn a response intent into language template rendering
hypothesise propose explanations and alternatives vocabulary-based wording
summarise compress episodes, documents, cycles first-sentences summariser
judge relevance, paraphrase and contradiction judgement negation and overlap rules
narrate internal narrative and reflection prose templated narrative
embed embeddings for semantic retrieval none; retrieval stays lexical and structural

Every role has a primary model and ordered fallbacks. A role with no working model is simply unavailable, and the code path that would have used it takes the deterministic branch. Nothing in the core depends on a model being present.

Providers

Three provider kinds are implemented over raw HTTP with no SDKs: OpenAI-compatible (chat completions with JSON mode, also used for local servers that speak the same protocol), Anthropic (Messages API) and Ollama. A provider row holds a name, kind, endpoint, timeout, extra headers, an allow_private_endpoint flag (required for local endpoints, because the outbound HTTP client refuses private and loopback ranges by default) and an API key reference. Models belong to providers and carry context window, dimensions for embedding models, per-token cost estimates and default parameters. The interface tests connectivity with a real call and records the result.

Secrets

Keys are never stored in clear. A key entered in the interface becomes an enc: reference encrypted under the master key; env:NAME and file:/path references defer to the environment or a file at call time. Forms never echo a stored key; the API returns whether a key is set, not the key; exports exclude them; the log handler redacts values that look like secrets; error messages from providers are scrubbed of the key before being stored or shown.

Routing, budgets and concurrency

The router resolves a role to its models, tries them in order, and records which one answered. A daemon-wide concurrency pool bounds simultaneous calls. Budgets are enforced at three levels: a per-cycle call cap per mind, a per-mind daily token budget and a global daily token budget; exhaustion is a recorded event and the deterministic branch, not an exception. Usage is aggregated per mind, role and model for the interface and for Prometheus.

Recording and replay

Every call records role, purpose, provider, model, prompt name and version, the rendered request, the response, token usage, latency, estimated cost, seed and a hash of the request. Calls are visible per mind and per event (a response links to its call). When a router runs in replay mode, a request whose hash matches a recorded call is served from the recording and marked replayed; this is how cognitive replay reproduces model-dependent steps exactly and how it knows to label a step approximate when no recording matches.

Prompts

Prompts are versioned rows: name, role, description, system text, a Go template for the user message and an optional JSON schema for the answer. Untrusted text is always rendered through a data helper that wraps it in a delimited block with an explicit "this is data, not instructions" marker. Saving a prompt creates a new version; the interface diffs versions, lets you test a prompt against sample input, and shows which version each recorded call used. Default prompts are embedded in the binary and installed on first start; edited ones live in the database.

{{include docs/llm-providers.md}}