Skip to content

Choose the model an agent uses

View as Markdown

This guide says what an agent's model must support, prefers or requires a provider or a model, and shows which one answered.

An agent doesn't name a model. Its capabilities say what the model must support. When a turn starts, Kindgi picks one from the providers registered for your tenant. You can prefer a provider or a model, or require one.

  • An agent, in a pack on a runtime such as kindgi dev, with Kindgi 0.1's CLI. This guide uses acme.order-desk.
  • Providers registered for your tenant, as Models shows. Without one, dev-echo answers.

Ask your coding agent: "My agent needs a model that can call tools." Kindgi's skills in your pack tell it how (How it knows Kindgi). For reference, it adds capabilities to the agent's definition. This one needs a model that calls tools:

// in agents/order-desk/index.ts
capabilities: [{ needs: [{ feature: 'tool-use' }] }],
Excerpt

Each entry in needs is a hard requirement. A model qualifies only if it meets all of them:

  • { feature: … }: a feature the model lists in its registration: tool-use, parallel-tool-use, structured-output, long-context, thinking, vision and others. An agent that calls tools needs tool-use. structured-output says the model can follow a JSON schema itself; Kindgi doesn't use that. On every model, an agent's typed answer comes from its instructions and the schema at the end of its system message, then a check against the schema and repairs. So needing structured-output only narrows which models qualify.
  • { contextWindow: { op: '>=', value: 500000 } }: a context window.
  • { region: 'on-prem' }: a provider registered with this region.
  • { models: { allow: […] } }, { providers: { allow: […] } }: only these models or providers. deny excludes them instead.

needs: [] takes any model. Only the first entry of capabilities picks the turn's model.

kindgi capabilities list --table lists the features an agent can require, what each one means, and which of your providers' models have it. kindgi capabilities get feature:tool-use gives one.

The agent's turns now run only on a model that meets its needs.

  1. It keeps every registered model that meets all the needs.
  2. A fallback provider, such as dev-echo, is considered only when no other model qualifies.
  3. It ranks what's left: the agent's preferred provider and model first, then the weights in its capability's prefer, then by provider id, with each provider's default model before its others, then by model name.

The last step decides more often than you might expect. Each preset names a mid-priced default, such as claude-sonnet-5-5 for anthropic. So with one preset registered, an agent with no preference gets its default. A provider registered without a defaultModel falls back to its first model by name, which can be its dearest. Prefer or require the model you mean.

A turn routed to a model its provider has retired fails, so an agent that prefers or requires one needs a new version naming another model. Anthropic lists each Claude model's status, and any retirement date, on its model deprecations page. An Anthropic provider registered without a default falls back to its first model by name too. Register it again, as Connect Anthropic shows.

A preference ranks a provider or a model first, without excluding the others. Ask your coding agent: "Prefer Claude Sonnet 5.5 for my agent." For reference, it adds the preference to the agent's definition:

// in agents/order-desk/index.ts
capabilities: [{ needs: [{ feature: 'tool-use' }] }],
preferredProvider: 'anthropic',
preferredModel: 'claude-sonnet-5-5',
Excerpt

A dry run shows the pick without calling the model:

Terminal window
pnpm exec kindgi runs start --agent=acme.order-desk --input='{"userMessage":"Where is my order A-1001?"}' --dry-run

--dry-run runs the turn without its model call. Its output names the provider and model the turn would use:

{
…
"status": "completed",
"dryRun": true,
…
"output": {
…
"provider": { "id": "anthropic", "model": "claude-sonnet-5-5" },
…
}
}
Excerpt
  • Both set: that model of that provider is ranked first.
  • Only preferredModel: that model is ranked first, from whichever provider serves it.
  • Only preferredProvider: its models are ranked first.

preferredProvider is a provider id, such as anthropic, and preferredModel a model name, such as claude-sonnet-5-5. A combined anthropic/claude-sonnet-5-5 matches nothing. A preference never excludes: if the provider isn't registered, or its models don't meet the needs, the turn takes the next model in line.

To make sure a turn runs on one model, ask your coding agent: "My agent must only run on Claude Sonnet 5.5." For reference, it puts the model in the needs:

// in agents/order-desk/index.ts
capabilities: [{ needs: [{ feature: 'tool-use' }, { models: { allow: ['claude-sonnet-5-5'] } }] }],
Excerpt

When no registered model qualifies, the turn fails instead of falling back to another one:

Error [capability-routing-failed]: No registered provider satisfies the capability declaration

The run's journal says why, model by model, in the failed setup step. Read it with kindgi runs journal <run-id>, where <run-id> is the id in the JSON that kindgi runs start prints. For models: { allow: ['gpt-5'] }:

{
"code": "capability-unsatisfiable",
"message": "No registered provider satisfies the capability declaration",
"reasons": [
{
"requirement": "models{allow:[gpt-5]deny:[]}",
"satisfyingProviders": [],
"rejectingProviders": [
{ "id": "anthropic/claude-opus-5-5", "reason": { "code": "model-not-in-allowlist", "message": "model not on allow list", … } },
…
{ "id": "ollama/llama3.1", "reason": { "code": "model-not-in-allowlist", "message": "model not on allow list", … } }
]
}
]
}
Excerpt

A provider registered for the tenant that the runtime couldn't set up, such as one whose settings the deployment no longer allows, is never a candidate. The reasons name it under provider set up, by its provider id alone, with the code provider-setup-failed. The error's message gives the first one's why, and counts the rest. A registration's settings can be checked at any time with GET /v1/providers/{providerId}/check.

prefer adds a weight for each match, against a model's features or a provider's attributes, and ranks the higher total first. A weight can be negative. Here a local model is registered with "attributes": ["local"], as Connect an OpenAI-compatible endpoint shows. Ask your coding agent: "Rank my local models above the others for my agent." For reference, it adds a weight for local:

// in agents/order-desk/index.ts
capabilities: [{ needs: [{ feature: 'tool-use' }], prefer: [{ feature: 'local', weight: 1 }] }],
Excerpt

With it, the dry run picks { "id": "ollama", "model": "llama3.1" } over the Anthropic models.

kindgi dev gives your tenant dev-echo, a stand-in that needs no key. It calls the agent's first tool with {"message": <the user message>}, and answers Tool responded: <the result>. An agent with no tools gets its user message back. Its text answers start with a line that says what it is:

⚠ dev-echo isn't a real model: it only repeats what it's given. Add an LLM provider key (Anthropic, OpenAI, Gemini, Groq, OpenRouter…) to get real answers.
Tool responded: {"echo":"Hi there","echoedAt":"2026-10-07T13:16:58.923Z","characterCount":8}
Excerpt

It's a fallback: once a registered model qualifies, it never answers.

When it does answer, the turn says so, in its result and on the CLI's stderr:

"warnings": [
{
"code": "fallback-provider",
"message": "Answered by \"dev-echo\", a fallback provider: no other registered provider satisfies agent \"acme.echo-agent\"."
},
{
"code": "dev-echo-not-a-model",
"message": "dev-echo answered, and it isn't a real model: it only repeats what it's given. Add an LLM provider key: kindgi secrets set <KEY> --env=local --scope=tenant (ANTHROPIC_API_KEY, OPENAI_API_KEY, GEMINI_API_KEY, GROQ_API_KEY, OPENROUTER_API_KEY…), then kindgi providers register --preset=<anthropic|openai|gemini-api|groq|openrouter>. kindgi providers presets lists them."
}
]
Excerpt
⚠ dev-echo answered, and it isn't a real model: it only repeats what it's given. Add an LLM provider key: kindgi secrets set <KEY> --env=local --scope=tenant (ANTHROPIC_API_KEY, OPENAI_API_KEY, GEMINI_API_KEY, GROQ_API_KEY, OPENROUTER_API_KEY…), then kindgi providers register --preset=<anthropic|openai|gemini-api|groq|openrouter>. kindgi providers presets lists them.
Excerpt

If you registered a model and still see this, the agent's needs don't match it. Compare its capabilities with the models' features, which kindgi providers list shows. dev-echo has only tool-use, so an agent that needs anything else fails with no model registered.

The turn's result names it in output.provider, such as { "id": "anthropic", "model": "claude-sonnet-5-5" }. Each model call in the turn's provenance record names its model and cost, as Trace an answer and its cost shows.