Providers & Models¶
Nova supports four LLM provider types: ollama for local models,
anthropic for the Anthropic Messages API (POST /v1/messages),
openai-compatible for cloud APIs and locally-hosted OpenAI-compatible
servers, and openai-response for the OpenAI Responses API.
Configuration File¶
The config file lives at ~/.nova/config.json.
Provider Types¶
Ollama (Local)¶
Ollama runs models on your own machine. No data leaves your computer.
{
"providers": {
"ollama": {
"type": "ollama",
"name": "Ollama (local)",
"options": {
"base_url": "http://localhost:11434"
},
"models": {
"gemma4:26b": {
"name": "gemma4:26b",
"tools": true
}
}
}
}
}
base_url-- default ishttp://localhost:11434- Models with
tools: truecan use Nova's built-in tools
OpenAI-compatible (Cloud)¶
Works with OpenAI, DeepSeek, OpenRouter, Azure OpenAI, or any server exposing an OpenAI-compatible chat completions API.
{
"providers": {
"openai": {
"type": "openai-compatible",
"name": "OpenAI",
"options": {
"base_url": "https://api.openai.com/v1",
"api_key": "sk-your-key-here"
},
"models": {
"gpt-4o": {
"name": "gpt-4o",
"tools": true
}
}
}
}
}
Anthropic¶
Talks to the Anthropic Messages API (POST /v1/messages) directly over HTTP.
No vendor SDK and no OpenAI compatibility shim.
{
"providers": {
"anthropic": {
"type": "anthropic",
"name": "Anthropic",
"options": {
"base_url": "https://api.anthropic.com",
"api_key": "sk-ant-...",
"anthropic_version": "2023-06-01",
"betas": []
},
"models": {
"claude-sonnet-4-5": {
"name": "claude-sonnet-4-5",
"tools": true
}
}
}
}
}
base_url-- optional, defaults tohttps://api.anthropic.com. A URL that already ends in/v1is accepted and not doubled, so an Anthropic-compatible gateway can be pointed at directly.api_key-- sent as thex-api-keyheader. Anthropic does not useAuthorization: Bearer.anthropic_version-- optional, defaults to2023-06-01. Sent as theanthropic-versionheader.betas-- optional list of strings, joined into theanthropic-betaheader.extra_body-- same semantics as the other providers: flattened into the outgoing request body, model-level entries overriding provider-level ones. See Config rules in the project README for deep-merge precedence.
The Anthropic API requires max_tokens on every request. Nova picks a per-model
default and you can override it with extra_body.max_tokens at either the
provider or model level.
Extended thinking is enabled through extra_body:
{
"providers": {
"anthropic": {
"type": "anthropic",
"options": {
"extra_body": { "thinking": { "type": "enabled", "budget_tokens": 4000 } }
}
}
}
}
Thinking text surfaces as Nova's reasoning stream, the same as
reasoning_content on OpenAI-compatible providers.
Extended thinking with tool calling relies on Anthropic thinking-block
signatures. Each thinking block comes with a cryptographic signature
and the API requires the latest assistant turn's block back unmodified
whenever tools are in play. Nova stores the thinking text in
reasoning_content and the signature in the internal provider_meta
field on the message, recombining them on the next request. The signature
covers the text so the text is stored once. Existing databases pick up
the storage on next launch with no manual migration or config change.
Nova skips replaying the block when it would be rejected and degrades gracefully:
- extended thinking is not enabled for the request
- the message was produced by a different model -- signatures are model-scoped, so switching models mid-session drops reasoning continuity for that turn instead of erroring
- only the latest assistant turn that carries a thinking block is replayed -- Anthropic requires that one, and each extra block would be billed as input tokens and consume context window on models that retain prior turns. Whether prior-turn thinking counts against the window is model-dependent -- some models strip it automatically, others retain and bill it -- so replaying only the last turn bounds the cost either way.
OpenAI Response¶
Uses the OpenAI Responses API via nova/llm/openai_response.py. Provider
options are base_url, api_key, and user_agent.
{
"providers": {
"openai-responses": {
"type": "openai-response",
"name": "OpenAI Responses",
"options": {
"base_url": "https://api.openai.com/v1",
"api_key": "sk-..."
},
"models": {
"gpt-4o": {
"name": "gpt-4o",
"tools": true
}
}
}
}
}
Custom Headers and Request Hook¶
openai-compatible, openai-response, and anthropic providers accept two
extra options for controlling HTTP headers:
headers-- an object of static header name/value pairs sent with every provider request.request_hook-- path to a user Python script run before every request to supply dynamic headers. Use it when a header value must be computed per request (e.g. a fresh request id).request_session_hook-- same protocol, but runs once per session (result cached); use it for session-stable values (e.g. a session attestation id). When both hooks set the same header,request_hookwins.
Hook protocol: nova writes {"session_id": ...} as JSON to the script's
stdin; the script must print {"headers": {...}} as JSON to stdout and exit
0. Missing file, timeout (5s), nonzero exit, or bad JSON fails the request
with a clear error -- hooks are fail-closed and header values are never
logged. Relative paths resolve against NOVA_HOME. Scripts run with
sys.executable (the frozen desktop app re-executes itself), so no system
Python is required. Hook output is merged over static headers.
Example ~/.nova/hooks/request_headers.py (stdlib only):
import json
import sys
import uuid
def main():
try:
ctx = json.load(sys.stdin)
except Exception:
ctx = {}
sys.stdout.write(json.dumps({"headers": {
"x-request-id": uuid.uuid4().hex,
"x-session-id": str(ctx.get("session_id") or ""),
}}))
if __name__ == "__main__":
main()
Note: the older session_header option has been removed; move its use to a
hook script as above.
These exist for gateways that require client identification or per-session affinity. A gateway that asks every client to send a stable session id and to identify itself:
{
"providers": {
"example-gateway": {
"type": "openai-compatible",
"name": "Example Gateway",
"options": {
"base_url": "https://gateway.example.com/v1",
"api_key": "sk-...",
"user_agent": "nova/1.0",
"headers": { "x-client-name": "nova/1.0" },
"request_hook": "~/.nova/hooks/request_headers.py",
"request_session_hook": "~/.nova/hooks/request_headers.py"
},
"models": {
"example-model": { "name": "example-model", "tools": true }
}
}
}
}
user_agent is a separate option that sets the User-Agent header directly.
Identify Nova with its own value rather than impersonating another client.
Both hook options take the same script shape; the difference is execution
frequency. request_hook runs before every request, so its output may vary
per call (e.g. the x-request-id above). request_session_hook runs once
per session and reuses the result, so its output must be session-stable
(e.g. the x-session-id above). Configure either or both; when both set the
same header, request_hook wins.
A provider's type fixes its endpoint: openai-compatible posts to
/chat/completions, openai-response to /responses, and anthropic to
/messages. A gateway that exposes models across several endpoints needs
one provider per endpoint, each carrying the same headers and hook options.
Multiple Providers¶
You can define several providers under providers. There is no top-level
default-model key. Nova resolves the provider and model for each request in
this order:
- Explicit
providerandmodelfields onPOST /api/chat - The session's agent config -- what
/modelsin the TUI or the model selector in the web frontend writes - The first configured provider and its first model
{
"providers": {
"ollama": {
"type": "ollama",
"options": { "base_url": "http://localhost:11434" },
"models": { "gemma4:26b": { "name": "gemma4:26b", "tools": true } }
},
"deepseek": {
"type": "openai-compatible",
"name": "DeepSeek",
"options": {
"base_url": "https://api.deepseek.com/v1",
"api_key": "sk-..."
},
"models": {
"deepseek-chat": {
"name": "deepseek-chat",
"tools": true
}
}
}
}
}
Switch at runtime with /models in the TUI, the model selector in the web
frontend, or by starting the backend with
nova serve --provider ollama --model gemma4:26b.
Environment Variables¶
| Variable | Description |
|---|---|
NOVA_HOME |
Override ~/.nova/ home directory |
NOVA_OLLAMA_BASE_URL |
Override Ollama base URL |
NOVA_OPENAI_BASE_URL |
Override OpenAI-compatible base URL |
NOVA_OPENAI_API_KEY |
Override API key |
OPENAI_API_KEY |
Fallback API key |
NOVA_FRONTEND_DIST |
Override the directory of the built frontend the server serves |
Server bind host, port, log level, and LAN auth live in the config file
server block, not in the environment. See Settings.