Skip to content

Models

An agent doesn't name an API. It says what its model must support, and Kindgi picks a model from the providers registered for your tenant. A provider is one connection, to a vendor's API or to an endpoint you serve, with the models it offers: each model's features, context window and price.

How a turn picks among them is on Choose the model an agent uses.

A model call that fails with a temporary error (a timeout, 408; a rate limit, 429; an overloaded or failing server, 500, 502, 503 or 504) is tried again, waiting longer each time, three attempts in all, before the turn fails. Every adapter does this: the Gemini adapter sets it, and the Anthropic and OpenAI-compatible ones keep their SDKs' defaults. The waits count against the turn's time budget (maxWallMs).

A new pack's tenant has dev-echo, a stand-in that answers without a model (it calls the agent's first tool and replies with what the tool returned, after a first line saying it isn't a real model). It's a fallback: it answers only while no other provider fits the agent, so a provider you register takes over at the next turn, with nothing to switch off and no restart.

Terminal window
kindgi providers presets

It prints each preset as JSON: its adapter, models, key and when its prices were checked. They are:

Preset What Key
anthropic Claude on the Anthropic API ANTHROPIC_API_KEY
gemini-api Gemini on the Gemini Developer API, with an API key from Google AI Studio GEMINI_API_KEY
gemini Gemini on Vertex AI, with Google Application Default Credentials (--project=<gcp-project>; in kindgi dev, set KINDGI_DEV_GOOGLE_CREDENTIALS=adc) none
groq Open-weight models on Groq's fast inference GROQ_API_KEY
openai GPT on the OpenAI API OPENAI_API_KEY
openrouter A hosted service in front of several vendors: your prompts pass through OpenRouter OPENROUTER_API_KEY

A preset carries its models' features, context windows, output limits and prices. pricesCheckedAt is when the prices were last compared with the vendor's. --max-output-tokens=<n> registers the models with another output limit.

Anything that isn't a preset registers from a spec: a JSON file you pass with kindgi providers register --spec=@<file>.

{
"adapter_id": "@kindgi/adapter-model-openai-compat",
"adapter_config": { "baseURL": "http://localhost:11434/v1" },
"metadata": {
"id": "ollama",
"region": "local",
"models": [
{ "name": "llama3.1", "contextWindow": 8192, "features": ["tool-use"],
"cost": { "promptUsdPer1kTokens": 0, "completionUsdPer1kTokens": 0 } }
]
}
}
  • adapter_id is the adapter's full package name.
  • adapter_config holds the connection's settings, such as the endpoint's URL: flat keys with string, number or boolean values. Never a key.
  • secret_ref names the secret that holds the key: { "envName": "local", "name": "GROQ_API_KEY" }. Leave it out for an endpoint that takes none.
  • send_traceparent (optional, true or false) sends each model call's W3C traceparent to the provider as a request header, so its request logs can be matched to the run that made the call (Follow a request or a run). Only the ids go: the trace id and a span id, never a prompt or an answer. It's off by default: nothing about a run's trace leaves your deployment unless you turn it on (see below).
  • metadata.id names the connection; agents prefer it by this id. metadata.models lists the models, by the name the endpoint knows them by, each with its contextWindow, its features (what agents can require) and its cost; optionally its maxOutputTokens, sampling and thinking (see Temperature and thinking).
  • metadata.defaultModel (optional) is the model an agent gets when nothing else decides: no preference, and equal weights. Without it, the first model by name. Another name is refused: provider "ollama" defaultModel must be one of its models (llama3.1), got "llama3.2".
  • metadata.attributes (optional) are labels agents can rank by, such as "local"; metadata.fallback: true makes the provider a fallback, like dev-echo.
  • metadata.labels (optional) are your own key-value notes, such as who manages the registration: { "kindgi.com/managed-by": "terraform" }. Routing ignores them; kindgi providers get shows them. Up to 32, each key 1 to 63 lowercase letters and digits (with ., -, _ or / inside), each value up to 256 characters. Another key is refused: provider "ollama" labels key "Team Name" must be 1-63 lowercase letters and digits, with ".", "-", "_" or "/" inside.

With send_traceparent: true, a model call's request to the provider carries the run's trace id and a span of its own:

traceparent: 00-57fb198d8afd340a2b81454221085849-6fef3833e512d758-01

kindgi runs get <run-id> shows the same trace id as the run's traceId. A value other than true or false is refused, and nothing is registered:

Error [invalid-request]: Provider "acme-vendor": send_traceparent must be true or false.
✗ /send_traceparent: send_traceparent must be true or false.

The runtime checks a spec against its adapter when you register it, without calling the endpoint or reading the key. A setting the adapter can't use is refused, with each problem on a line of its own, and nothing is registered. Here, the spec above without its baseURL, and with an api it doesn't take:

Error [invalid-request]: Provider "ollama" doesn't fit adapter @kindgi/adapter-model-openai-compat: needs adapter_config.baseURL, an http(s) URL (e.g. https://api.openai.com/v1) (and 1 more).
✗ /adapter_config/baseURL: needs adapter_config.baseURL, an http(s) URL (e.g. https://api.openai.com/v1).
✗ /adapter_config/api: adapter_config.api must be one of responses, chat-completions.
  • A key it needs: an adapter that takes a key refuses a spec without secret_ref: ✗ /secret_ref: needs secret_ref: its Anthropic API key.
  • An adapter the runtime doesn't have: This runtime has no adapter "@kindgi/adapter-model-nope", so it can't build provider "ollama".
  • What isn't checked: whether the key works, the endpoint answers, or it serves the models you list. The first turn finds that out.
  • From the API: POST /v1/providers answers 422 provider-config-invalid, with each problem in details.issues: its path, a JSON pointer into the spec, and its message. The clients read it as an invalid request with those issues (TypeScript code: 'invalid-request', Python InvalidRequestError).

Some models take no temperature: the Claude 5.5 models and OpenAI's GPT-6 models reject one. Their registrations say "sampling": false, and Kindgi sends their calls without it. The turn then carries a sampling-unsupported warning instead of failing, and kindgi runs start prints it. Here from a local llama3.1 registered with "sampling": false and a model-settings block asking for 0.2:

⚠ llama3.1 doesn't take a temperature, so the call went without one (it asked for 0.2).

Some models think before they answer. A model's thinking says how: "mode": "adaptive" (on unless turned down) or "always", and lowest, the vendor's own setting for the least thinking. Thinking counts against the model's output limit (maxOutputTokens) and bills as output, so it's in a turn's tokens and cost. A guardrail's model judge asks for the least thinking, and allows 2,048 tokens for it on top of the 256 its verdict gets.

Preset Model Takes a temperature Thinks Least thinking
anthropic claude-opus-5-5 no always low
claude-sonnet-5-5 no adaptive between_tools
claude-haiku-5-5 no adaptive disabled
claude-haiku-4-5 yes not set
openai gpt-6.1-sol no adaptive low
gpt-6-astra no adaptive low
gpt-6-luna no adaptive none
gemini, gemini-api gemini-3.8-flash yes adaptive low
gemini-3.5-flash-lite yes adaptive minimal
gemini-api gemini-3.1-pro-preview yes not set

groq's models take a temperature and set no thinking. On openrouter, anthropic/claude-sonnet-5.5, openai/gpt-6.1-sol and openai/gpt-6-luna take no temperature. A model without thinking gets nothing sent for it: it thinks, or not, as its vendor's default.

A preset registered before 0.1.4 has none of these marks: register it again to pick them up.

Under kindgi dev, each project and each git worktree has its own database, and a provider you register by hand is in that one only. Declare it in the pack's config instead, and kindgi dev registers it on every boot: in a new worktree, after --reset, and on a teammate's machine.

// in kindgi.config.ts
providers: [
{ preset: 'anthropic', models: ['claude-sonnet-5-5'] }, // its key, ANTHROPIC_API_KEY, from the env files
{ preset: 'gemini', project: 'acme-gcp', models: ['gemini-3.8-flash'] },
{ spec: { /* what --spec takes */ } },
],

A preset entry takes models, project, maxOutputTokens, and secret: the key's name, in place of the preset's own. The keys are spelled the same in pyproject.toml. A spec entry is what --spec takes. A key is always a secret's name, read from the env files; a spec with a credential in adapter_config is refused.

At each boot, kindgi dev says what it did:

Providers from kindgi.config.ts:
✓ anthropic: registered
✓ qwen-local: registered
⚠ qwen-keyed: not registered: QWEN_API_KEY is not in .env, .env.local, .kindgi/secrets.env. Set it (pnpm exec kindgi secrets set QWEN_API_KEY --env=local --scope=tenant), then restart kindgi dev
  • A provider whose key isn't in the env files is skipped. The config isn't watched: restart kindgi dev after setting the key.

  • A key in .env, which your app loads too, gets a line of its own:

    ⚠ ANTHROPIC_API_KEY is in .env, which your app loads too. To give Kindgi its own copy: pnpm exec kindgi secrets copy (it never edits your app's files).

    See A key your app's env files already hold.

  • One kindgi dev registered and whose declaration changed is registered again (qwen-local: registered again (changed in kindgi.config.ts)); one the config no longer declares is unregistered.

  • If the runtime already has a provider with that id that kindgi dev didn't register, kindgi dev leaves it as it is. When its region or models (names, limits, prices) differ from the config's, a ⚠ line names the kindgi providers unregister that lets the config's version apply. The runtime doesn't list a provider's adapter, its settings or its key's name, so one that differs only there is left with a plain line.

kindgi dev keeps track of the ones it registered in .kindgi/dev/providers.json. A runtime that kindgi dev doesn't run, such as your staging or production one, gets its providers with kindgi providers register.

With kindgi dev, a provider's key is read from your app's env files, .env and then .env.local, and then Kindgi's own .kindgi/secrets.env, which wins on a clash, when a turn calls the model. To store one without echoing it, with kindgi dev running:

Terminal window
kindgi secrets set ANTHROPIC_API_KEY --env=local --scope=tenant

It prompts for the value and writes it to .kindgi/secrets.env, which your app doesn't load. In a script, pipe it in with --from-stdin. To replace a key that's already set, add --write-mode=add-version.

A provider's key is used by that provider only. No tool, MCP endpoint or webhook endpoint gets a secret that a provider registration names, in any environment: a tool that calls a model itself declares a key of its own. Registering a provider whose key a tool or an endpoint already uses is refused (provider-key-in-use), and the error names each one: store the provider's key under a name of its own, then register.

Terminal window
kindgi providers list # every provider, with its models
kindgi providers list --feature=structured-output
kindgi providers get ollama
kindgi providers unregister ollama

list and get show the metadata only, never adapter_config or the key. A registration can't be edited: registering an id that exists is refused, so unregister it first, then register the new spec.

Unregistering takes a provider out of list, get and routing at once, and its id is free to register again; Kindgi keeps the record until a retention policy on provider ("Removed model providers" in the console) purges it:

Error [not-found]: No provider registered with id "ollama"
Error [conflict]: Provider "ollama" is already registered

A registration stored before 0.1.5 wasn't checked when it was registered. GET /v1/providers/{providerId}/check (providers.check(id) in the clients) runs the same check over a stored one. Here, an Ollama registration whose api doesn't fit:

{"providerId":"ollama","adapterId":"@kindgi/adapter-model-openai-compat","checked":true,"issues":[{"path":"/adapter_config/api","message":"adapter_config.api must be one of responses, chat-completions."}]}

An empty issues means the runtime can build the provider. checked is false for an adapter this runtime has no check for, such as dev-echo's. kindgi doctor asks this for every provider and names each problem (Check this machine). To fix one, unregister it and register it again with a spec that fits.