Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Providers and models

A provider is a model back-end: Anthropic, any API speaking the OpenAI Chat Completions dialect (OpenAI, Groq, Mistral, xAI, OpenRouter, Ollama, most gateways), or the built-in mock. You declare providers in .vibe/config.toml, give each a name, and refer to models as <provider name>/<model id>.

Defaults

Without any configuration, Vibe Factory knows three providers and uses anthropic/claude-sonnet-5 for every phase with the medium thinking level:

default_provider = "anthropic"
default_model = "anthropic/claude-sonnet-5"

[providers.anthropic]
kind = "anthropic"
api_key_env = "ANTHROPIC_API_KEY"

[providers.openai]
kind = "openai"
api_key_env = "OPENAI_API_KEY"

[providers.ollama]
kind = "openai"
base_url = "http://localhost:11434/v1"
default_model = "qwen2.5-coder"

These built-in values apply when .vibe/config.toml does not exist and when the file contains no [providers.*] table at all. As soon as you declare one provider, the providers are exactly the tables you wrote: declare every provider you use (a file written from the defaults, as above, already contains the three).

A provider whose key is missing still loads. It fails with an authentication error on its first call only, so listing providers you do not use is harmless.

Provider keys

Every [providers.<name>] table accepts:

KeyTypeMeaning
kindstring, requiredanthropic, openai, ollama or mock (plugins may add kinds)
api_keystringthe key itself; avoid committing it, prefer api_key_env
api_key_envstringenvironment variable holding the key (an empty value counts as missing)
base_urlstringendpoint override (gateway, proxy, self-hosted server); trailing / is ignored
default_modelstringmodel used when a reference names no model, or names default
streamboolstream answers as they are generated (default true); set false for a gateway that mishandles server-sent events
extratablekind-specific settings, below

api_key wins over api_key_env when both are set.

An Anthropic API key that is not scoped to a workspace is refused with HTTP 400 (“must include the anthropic-workspace-id header”). Set ANTHROPIC_WORKSPACE_ID to the workspace id (wrkspc_…, from the console’s workspace settings) and every anthropic provider sends it as anthropic-workspace-id, or use a key created in a workspace. In the macOS app, add the variable in Settings like the key.

extra keys

KeyKindsTypeEffect
headersanthropic, openai, ollamatable of stringsextra HTTP headers sent with every request
thinkinganthropic"adaptive" or "budget"force the thinking form instead of choosing it from the model id (see Thinking levels)
authanthropic"api_key" (default) or "bearer"bearer sends the key as Authorization: Bearer with the OAuth beta header instead of x-api-key
require_api_keyopenai, ollamabooldefault false when base_url is set, true on the official OpenAI endpoint; with false, no Authorization header is sent when the key is missing
max_tokens_paramopenai, ollamastringname of the output-budget field; default max_completion_tokens on the official endpoint, max_tokens elsewhere
reasoningopenai, ollamaboolsend reasoning_effort; default on for the official endpoint only, since many compatible servers reject unknown fields
replymockstringfixed answer of the mock provider (default "Mock response.")

The ollama kind is the openai kind with base_url defaulting to http://localhost:11434/v1.

[providers.gateway]
kind = "openai"
base_url = "https://llm.internal.example.com/v1"
api_key_env = "GATEWAY_TOKEN"

[providers.gateway.extra]
max_tokens_param = "max_tokens"

[providers.gateway.extra.headers]
"X-Team" = "platform"

Referring to models

A model reference is provider/model, where provider is the name of a [providers.<name>] table, not its kind. A reference without / is attributed to default_provider.

ReferenceResolves to
anthropic/claude-opus-5provider anthropic, model claude-opus-5
claude-opus-5provider default_provider, model claude-opus-5
ollama/defaultprovider ollama, its default_model
openrouter/meta-llama/llama-4-maverickprovider openrouter, model meta-llama/llama-4-maverick (split at the first /)

On providers of kind anthropic these shorthands are expanded:

ShorthandModel id
opusclaude-opus-5
sonnetclaude-sonnet-5
haikuclaude-haiku-4-5-20251001
fableclaude-fable-5-1

An unknown provider name fails with unknown provider `x` for model `x/y` (configured: …), and an unknown kind with provider `x` has unknown kind `y` (known kinds: anthropic, mock, ollama, openai).

Models per phase

Each pipeline phase can use its own model and, optionally, its own thinking level. Phases without an entry use default_model, and every agent keeps its own thinking level.

default_model = "anthropic/sonnet"

[phases.assess]
model = "anthropic/haiku"
thinking = "low"

[phases.plan]
model = "anthropic/opus"
thinking = "high"

[phases.build]
model = "ollama/qwen2.5-coder"
thinking = "off"

The phase names are assess, spec, plan, build, qa, fix and merge. An agent can also pin a model regardless of the phase; see Customising agents.

Thinking levels

LevelBudgetAnthropic, current modelsAnthropic, older modelsOpenAI-compatible
offnoneno thinkingno thinkingno reasoning_effort
low1 024 tokensadaptive, effort lowbudget_tokens: 1024reasoning_effort = "low"
medium4 096adaptive, effort medium4 096"medium"
high16 384adaptive, effort high16 384"high"
max32 768adaptive, effort max32 768"high"

On OpenAI-compatible servers the level is only sent when reasoning is enabled (see above); otherwise it is ignored.

With Anthropic, current models (Claude 4.6 and later: Sonnet 5, Opus 5 and 5.5, Fable, Opus 4.6 to 4.8, Sonnet 4.6) take thinking: {type: "adaptive"} with output_config.effort and reject budget_tokens and sampling parameters, so vibe never sends a temperature to them. Older models (Haiku 4.5, Claude 4.5 and before) and unknown model ids keep budget_tokens, and thinking removes the temperature as the API requires. The form is chosen from the model id; set extra.thinking = "adaptive" or "budget" on the provider to force it, for example behind a gateway that renames models. Model ids are sent as is, so the choice follows claude-... names.

Recipes

Ollama (local, no key):

[providers.ollama]
kind = "ollama"
default_model = "qwen2.5-coder:32b"

Groq:

[providers.groq]
kind = "openai"
base_url = "https://api.groq.com/openai/v1"
api_key_env = "GROQ_API_KEY"
extra = { require_api_key = true }

OpenRouter:

[providers.openrouter]
kind = "openai"
base_url = "https://openrouter.ai/api/v1"
api_key_env = "OPENROUTER_API_KEY"
default_model = "anthropic/claude-sonnet-5"

[providers.openrouter.extra]
require_api_key = true

[providers.openrouter.extra.headers]
"X-Title" = "vibe-factory"

Reference it as openrouter/anthropic/claude-sonnet-5.

xAI:

[providers.xai]
kind = "openai"
base_url = "https://api.x.ai/v1"
api_key_env = "XAI_API_KEY"
extra = { require_api_key = true }

Mistral:

[providers.mistral]
kind = "openai"
base_url = "https://api.mistral.ai/v1"
api_key_env = "MISTRAL_API_KEY"
extra = { require_api_key = true }

Setting require_api_key = true on hosted services turns a forgotten environment variable into a clear no API key configured error instead of an anonymous request rejected by the server.

Mock (dry runs and tests, no network):

default_model = "fake/any"

[providers.fake]
kind = "mock"
extra = { reply = "Nothing to do." }

Streaming

Anthropic and OpenAI-compatible providers stream their answers: the text and, when the model exposes it, the reasoning arrive as agent_delta events while a step is being generated (vibe --json run prints them; the terminal output shows the complete text at the end of each step, as before). The final answer is identical to a non-streamed one.

A request that fails before any text arrived is retried as usual. A stream that breaks after text was sent is not retried by the provider (the text would be repeated): the step fails and the agent runtime’s own retry policy applies. An OpenAI-compatible server that refuses stream or stream_options with a 4xx error gets the same request without streaming. agent_delta events are not written to events.jsonl.

Retries and errors

HTTP providers retry transient failures on their own: four attempts in total, starting at 1 second and doubling up to 60 seconds, with ±25 % jitter. A Retry-After header from the server replaces the computed delay, still capped at 60 seconds. On top of that, the agent runtime retries a still-failing call twice more and publishes a retrying event you will see in the live output.

SituationClassified asRetried
HTTP 429 or “rate limit” wordingrate limitedyes
HTTP 5xx, including 529 overloadedserver erroryes
DNS, TLS, connection or timeout failure, HTTP 408networkyes
HTTP 401 / 403authentication failedno
billing, credit or quota wording, or HTTP 402 (even on a 429)invalid requestno
HTTP 400 / 413 mentioning the context lengthcontext too longno; the agent session stops and may continue from a summary
other 4xxinvalid requestno

Messages you may see:

AuthFailed: no API key configured for provider `anthropic` (set `api_key` or `api_key_env`)
AuthFailed: HTTP 401: invalid x-api-key sk-***
InvalidRequest: HTTP 429: You exceeded your current quota
RateLimited: HTTP 429: rate limit reached for requests
ServerError: malformed provider response: no choices
Network: request timed out: …

Keys, bearer tokens and token= / api_key= values are masked before anything is logged or shown. Requests time out after 30 seconds to connect and 300 seconds overall.

Prompt caching

With Anthropic, the system prompt is sent as a cached block, so the many turns of one agent session reuse it; cache reads and writes are reported in the token usage. OpenAI-compatible servers that cache automatically report cached prompt tokens the same way. Nothing needs to be configured.