Skip to content

LLM providers

The interpretation stage (deutung) supports several backends, selected with --provider (or llm_provider in a config file). Without a valid key the pipeline still runs deterministically and exports the prompts for the chat workflow.

Provider Notes Key
anthropic default; needs the anthropic extra (--extras anthropic) ANTHROPIC_API_KEY or a .api_key file
openai and OpenAI-compatible (azure, google, mistral, groq, openrouter, …) via the standard library, no extra package the provider's API key env var
ollama local server, runs without a key
chat no API: prompts are exported for copy-paste into a chat UI; answers are injected back

Setting the key

export ANTHROPIC_API_KEY=sk-...        # or the provider's variable
# or put the key in a file ".api_key" in the working directory

Deterministic / chat workflow

# deterministic only (no AI):
normpare compare --old OLD --new NEW --out OUT --no-llm

# with AI interpretation:
export ANTHROPIC_API_KEY=sk-...
normpare compare --old OLD --new NEW --out OUT

Answers are cached by a hash over (model, prompt), so repeated runs are cheap, and every interpretation carries an evidence quote that is checked against the source text (hallucination guard). Failed checks land in a review queue.

OpenAI

export OPENAI_API_KEY=sk-...
normpare compare --old OLD --new NEW --out OUT --provider openai --model gpt-4.1

Any OpenAI-compatible endpoint works the same way — --provider google|mistral|groq|together|openrouter, or --provider openai_compatible --base-url https://your-endpoint/v1 for anything else. Azure needs --provider azure_openai --base-url <deployment-url> (the API version comes from llm_api_version in the config).

Ollama (local, free)

Run a model locally and point normpare at it — no key, no API cost:

ollama serve
ollama pull llama3.1:70b
normpare compare --old OLD --new NEW --out OUT --provider ollama --model llama3.1:70b
# custom host: --base-url http://my-host:11434/v1

Choosing a model: measure, do not guess

The interpretation is only worth as much as its evidence. Every entry carries a quote that is checked against the standard, and deutung.json reports evidence_ok per entry — so the quality of a model is directly measurable:

python -c "import json;d=json.load(open('OUT/deutung.json'));x=[e for c in d['chapters'] for e in (c.get('interpretations') or [])];print(f\"evidence_ok: {100*sum(bool(e.get('evidence_ok')) for e in x)/len(x):.0f}%\")"

In practice this separates models sharply. On a real standard revision, a frontier model reached 99 % verified evidence, while a small, cheap model reached only 21 % — it paraphrased instead of quoting, was more confident while being less grounded, and misread editorial rewording as substantive change. For regulatory work, prefer a strong model and cut cost with --batch (~50 % cheaper) and the answer cache instead of with a weaker model.

Note that --batch currently uses the Anthropic message-batch API; other providers fall back to sequential calls.