Skip to main content
models.think picks the model that decides what to say and when to call a tool.
SLNG serves this role as a proxy with a cache in front. The Context Router answers the turns it judges repeatable and calls your model for the rest. The model stays yours to choose, and so does the provider that serves it, so one SLNG_API_KEY can sit in front of any host you already use.
On this page:

Quickstart

agent.yaml
Model ids are forwarded to the vendor exactly as you write them. Unmute keeps no allowlist, so a typo surfaces as a provider error at run time. The LiveKit Responses mode below is the narrow exception: api selects the generated client and reasoning_effort becomes the API’s nested reasoning setting.

Every key a think binding takes

provider: and model: carry a plain binding. The rest shape the request, or put the Context Router in front.
string
A provider supported for this role and target, listed on the model pages. Required for an API binding; there is no inferred API provider. local selects local placement.
string
Provider model id, passed through as written. Required where the selected integration requires a model; otherwise its default applies. No model id is checked against a provider catalog.
object
Provider parameter names and values. Omit to add no extra parameters. Provider limits apply; Unmute does not define a universal accepted set. SLNG params.world_part is checked against its supported regions.
number
Sampling temperature on a think entry. Provider-defined values and limits; omitted leaves the provider default.
number
Nucleus sampling value on a think entry. Provider-defined values and limits; omitted leaves the provider default.
integer
Sampling count on a think entry. Provider-defined values and limits; omitted leaves the provider default.
an environment variable name
Points at a variable holding an OpenAI-compatible endpoint URL, for a binding that talks to your gateway directly. On Pipecat this is what lets an unlisted provider through.
api | local
Whether the model is called over the network or runs in the agent’s own process. Left out, it is api whenever the entry names a provider or a model. local is refused on the slng target.
list of strings
Names from the same think or listen section, in retry order. Omit for no fallback chain. Cycles and other roles are refused; Pipecat refuses generated fallback.
string, up to 512 characters
Literal text appended to every system prompt this binding sends: each agent’s, each task’s, and the summarizer’s. No {{variables}}. It exists for models that take instructions no parameter can carry. A per-target override cannot name a different value.
string
Scopes the Context Router’s cache. One stable value per package, written by you, with a version suffix you bump after a prompt change you judge meaningful. Router bindings only, and refused on any other.
block: provider, url, key_env
Where the router actually calls the model and whose credentials pay for it. Required on a router think binding, because the configuration travels inline on every request. Router bindings only.
string
An author note. It reaches no generated artifact, so it is for the next person reading agent.yaml.

Why reasoning_effort is there

gpt-5.6-terra is a reasoning model, and OpenAI rejects a chat completions request that carries function tools unless the request also sets reasoning_effort. Leaving the line out is not the same as leaving the value alone: the server applies its own default, and every turn comes back as HTTP 400. This model takes none, low, medium, high, and xhigh. It rejects minimal. Every shared example profile writes none, and so does unmute init. Keep the line whenever the agent has tools; a target-specific Responses override is the exception below.
On LiveKit the line matters for a second reason. livekit-plugins-openai 1.8.1 injects reasoning_effort="minimal" by itself for several older ids in the same GPT-5 family, which is the same 400 once the agent has tools. Setting the param yourself is what avoids it.
One shared authoring line lowers correctly to both targets. LiveKit passes it as a constructor argument, openai.LLM(..., reasoning_effort="none"). Pipecat’s settings class has no field for it, so it rides the service’s extra field, OpenAILLMService.Settings(..., extra={"reasoning_effort": "none"}), which Pipecat merges into the request body as written.

LiveKit Responses API

Ask for the Responses API on the shared think binding when a package needs it:
agent.yaml
Unmute emits openai.responses.LLM and turns reasoning_effort into the API’s nested reasoning setting when it is present. Use that field instead of a raw reasoning map. api and use_websocket are the two params only LiveKit can act on: one picks the class, the other is a kwarg that class has and the chat completions one does not. Write them here rather than in a target override. A per-target models: entry replaces the base entry instead of merging into it. An override would have to repeat provider, model and reasoning_effort to keep them, and two copies of one binding is a binding somebody edits on one side only. Pipecat drops both and builds OpenAILLMService either way, and unmute validate prints a warning per param naming the target, so you are told what did not apply. salon-concierge-single-prompt shows this binding; salon-concierge uses native Gemini through Google’s EU Vertex endpoint. use_websocket: true keeps a WebSocket connection for Responses requests. HTTP clients can also reuse connections, so compare latency on the package’s own prompts and tools before choosing a transport.

Native Gemini locations

provider: google uses the native Google plugin on both targets. gemini is also accepted. On LiveKit this uses livekit-plugins-google, with no LiveKit Inference request.
Set GOOGLE_API_KEY in the package’s .env. params.location selects the inference endpoint, independently of the worker’s deployment region. These are Google’s endpoint rules. Unmute checks the location’s format without keeping a region allowlist. Google determines whether the requested model and API key work at that location. An unsupported location or unavailable model fails at the requested endpoint; there is no fallback to global or another region. The pinned plugins handle streaming and tools; a small adapter supplies their Google GenAI client because their Vertex constructors expect OAuth credentials. Other provider parameters, including thinking_config, still reach the native plugin. Omit vertexai and location to keep using the Gemini Developer API with GOOGLE_API_KEY.
boolean
Set to true for the Vertex API-key path. Omit it for the Gemini Developer API.
string
Required with vertexai: true. Use us, eu, an individual Google region, or explicitly choose global. A location without Vertex is refused.
object
Google thinking options. Pipecat receives these as its native ThinkingConfig.
string
This example uses minimal. Allowed levels depend on the selected model.

Pipecat

LiveKit

The differences that matter

This is the role where the two lists diverge most. LiveKit adds aws, azure, and sarvam. Pipecat adds deepseek and qwen. LiveKit spells the Mistral integration mistralai.

When your provider is not listed

The two targets answer this differently, and the difference is worth knowing before you pick one. On Pipecat, an unlisted provider is legal for any role on one condition: a genuinely OpenAI-compatible endpoint, named with endpoint_env.

Behind the Context Router: openai-compat

endpoint_env above is for a think binding that talks to your gateway directly. When the Context Router is in front, the upstream travels inline in the request body instead, and an OpenAI-compatible host is the openai-compat kind. OpenRouter is the worked example:
agent.yaml
url and key_env are both required here: there is no default to fall back on, and your key travels to the router on every think request, so a package has to say whose it is. Two things this shape buys, both worth knowing before you copy it:
  • A host pin. OpenRouter serves one model from several providers, and they do not behave the same. params.provider is forwarded verbatim, so you can accept one. With only set, an unavailable host is a 404 with the message intact rather than a quiet fall-back to a provider that breaks your tools.
  • The context ceiling is the host’s, not the model’s. A model card advertises the largest window any provider offers. On qwen/qwen3-32b that is 131,072 on Groq and 40,960 on Nebius, for the same model. Check the one you pinned.
Measure the host you pin, and measure two things separately. On qwen/qwen3-32b the four providers spanned a wide range at both the median and the tail. Latency did not predict correctness: the second-fastest spoke a fragment of its own tool-call template into every reply. A provider’s published figures are aggregated over everybody’s traffic, mostly short prompts with no tools, so they will not match a voice agent’s request. Send your own prompt and your own tool schemas, and count tool calls as well as milliseconds. A model whose behaviour no parameter reaches may still need prompt_suffix.
Without endpoint_env it is refused, and the error lists what the role does take:
On LiveKit, an unlisted reasoning provider routes through LiveKit Inference: managed models billed through LiveKit Cloud, with no provider key of their own. That path needs LIVEKIT_API_KEY and LIVEKIT_API_SECRET, in local development as well as in production. Writing provider: livekit passes your model string through verbatim.

Fallback

fallback: on a think entry names other think entries, tried in order when the primary model’s call fails:
agent.yaml
fallback is legal on think and listen models only. Naming it on a speak or turn entry is refused, by name. A name the section does not have, or a chain that loops back on itself, is refused too, naming the loop. What that one block compiles to depends on the target:
Pipecat does not emit a generated fallback yet, so validating the same package against a Pipecat target refuses it. The SLNG list uses the same vendor/model shape as the primary binding. Speech to text documents the same field on the listen role.

Troubleshooting

Every turn comes back as HTTP 400

A reasoning model was sent function tools without reasoning_effort. Fix: set it in params:, as the Quickstart does. On LiveKit it also stops the plugin injecting a value of its own.

agent_id or upstream is refused

Both belong to a think binding routed through the Context Router, and the message names the provider the binding actually has. Fix: set provider: slng on that binding, or remove the key.

prompt_suffix is refused on a listen or speak binding

It appends to a system prompt, and only a think model sends one. Fix: move it to the think binding those prompts run on.

The package compiles for LiveKit and is refused for Pipecat

Pipecat does not emit generated fallback yet. Fix: remove fallback: from the think section, or keep that package on LiveKit or slng.

A provider is refused and the message lists other names

The vendor is not on your target’s list for this role. Fix: see When your provider is not listed; the two targets answer it differently.

Where to go next

Turn detection

Knowing when the caller has finished.

Live model

One model that listens, thinks and speaks, on Pipecat.