InferRequest. The infer: verb takes a
model: <provider>/<name> string, looks up capability rules
( rules), and dispatches
through the right API dialect.
One InferRequest for all providers
nika-provider-anthropic, nika-provider-openai,
nika-provider-gemini, …) implements the single Provider trait from
nika-kernel:
API dialects (7)
Providers speak 7 distinct dialects. One provider crate per dialect + an OpenAI-compatible family that covers 23 providers.In-process runner:
nika-infer-local is the sovereign local
sidecar (candle-based, per ADR-091): quantized generation without an
external server. Most users take the simpler path first: a local server
(Ollama, LM Studio, llama.cpp, vLLM) per the local models
guide.Capability-aware routing
EveryInferRequest is routed through capability rules before
dispatch. The rules decide:
- What modalities the model accepts (text / image / audio / pdf / …).
- Whether tool calling + parallel tool calls are supported.
- Whether JSON mode is
unavailable/object/schema. - Whether streaming is native or emulated.
- Whether prompt caching / context caching reduces cost.
- Which supported parameters are legal (reasoning-effort, thinking- budget, computer-use, citations, …).
If you send
tools: [...] to a model whose capability rule says
tool_calling = false, Nika fails fast with NIKA-13x before hitting
the network: no silent provider-side error.Structured output · the engine picks the wire (ADR-098)
A task’sschema: is the single authored contract — there is no json: true sugar and no per-provider knob. The engine decides, per run, how to
honor it on the wire the model speaks:
- Fully-specified schema (every
objecthasproperties, everyarrayhasitems): forwarded verbatim to the provider’s strict structured mode where the wire has one. - Underspecified schema (any node typed
objectwithoutproperties, orarraywithoutitems— the honest shape for “return the same free-form JSON, transformed”): strict mode would reject it with an HTTP 400, so the engine requests the provider’s native JSON mode instead, steers the shape through the prompt instruction, and validates the reply locally against your schema, with bounded retry. - Wire with no native JSON mode (the anthropic dialect): the prompt instruction carries the shape; the same local validation applies.
- The mock wire receives the schema verbatim and synthesizes a
conformant instance — this is what lets every schema workflow run
offline under
nika test.
Fallback chains (target design)
When a provider fails, Nika can route to a fallback:target design · a future minor
Why and not 9
Earlier drafts of this page listed 9 providers. That was a snapshot from
mid-2025. Today’s catalog is -deep, covering frontier
cloud labs, fast-inference shops (Groq, Cerebras, SambaNova), Chinese
labs (Moonshot, Qwen, MiniMax, Zhipu), enterprise platforms (Bedrock,
Azure, Vertex), and niche (Voyage embeddings, Writer enterprise).
Source of truth lives in the TOML and is build-time validated.
Adding a new provider
1
Add a `[[providers]]` block to `llm-providers.toml`
Declare
id, name, aliases, env_var, key_prefixes,
default_model, cheap_model, api_dialect, tags, and at least
one [[providers.models]] sub-block.2
Add capability rules if the provider has unique features
Put rules in
model-capabilities.toml scoped via scope.providers.
Inherit defaults if the provider is standard (most openai-chat
providers need no rules).3
Implement the L1 provider crate
If the
api_dialect already exists, wire a new provider instance
with the correct base URL + auth. If new dialect, new L1 crate
nika-provider-<name>.4
Pass the engine's admission checklist
Property test against the provider’s API contract. Criterion bench for
token throughput. Capability parity test. How admission works →.
See also
Providers catalog
All providers with env vars, default / cheap models, dialects.
Capability rules
Resolution algorithm and -rule contract.
Concepts · Verbs
The
infer: verb and its three siblings (exec / invoke / agent) · fetch is the nika:fetch builtin under invoke:.L0 decisions
Why capability rules use prefix matching, not regex (Q1).