Skip to content

llm

OpenAI-compatible LLM transport: chat completions + embeddings.

All config resolves at construction/call time (never at import): constructor arg > env var > default. No retries here — reliability policy (retry-then-skip, cache fallback) belongs to the callers.

ChatClient

A schema-constrained chat completion, against any OpenAI-compatible server -- see the module docstring: config resolves per call/construction, never at import, and reliability policy (retry, degrade) is the caller's.

chat_json(messages, schema)

One chat call, requesting a JSON object matching schema.

Sends reasoning_effort="none" first (see the comment below: chain- of-thought buys nothing for a small schema-bound reply and roughly doubled latency when measured), retrying once without it for a server that rejects the field with a 400 rather than ignoring it.

EmbeddingClient

One embedding vector per call, against any OpenAI-compatible server -- same construction-time config resolution as ChatClient.

embed(text)

The raw embedding for text, untruncated and unnormalized -- embed.truncate_normalize is the caller's job, not this client's.

base_url()

The configured LLM server root, resolved at call time: env var, else DEFAULT_BASE_URL -- never cached, so a .env change takes effect without restarting anything that only imports this module.

chat_model()

The configured chat model name, resolved at call time -- see base_url.

default_chat_fn(messages, schema)

Module-level lazy ChatClient; the default chat_fn for explain/tagging.

embed_model()

The configured embedding model name, resolved at call time -- see base_url.

load_env()

Load .env (repo root first, then cwd-upward search); real env always wins.

Called only from process entry points (cli.main, mcp_server.main) — never from library imports, so tests stay dotenv-free.

Any OTHER entry point must call this itself. A standalone script that imports attestation and skips it gets DEFAULT_CHAT_MODEL rather than the model in .env, silently and with no error — a one-off re-tagging script did exactly that on 2026-08-11 and ran against the wrong model until the banner it printed gave it away.