Models and reasoning
Choosing the model that runs an agent's turns: the catalog, why ids must be verbatim, the single reasoning-effort knob, and why every model call goes through the platform relay.
Each agent declares the model that runs its turns. Which one you pick is the largest lever you have on both quality and cost.
The catalog
sf models # what your team can call; verified models listed first
sf model <id> # one model: tier, task fit, price, verified flag
"Verified" means the platform has proven full agent turns on it — tool calls, multi-step reasoning, the whole loop. Prefer those. At the time of writing the verified set includes:
@cf/meta/llama-4-scout-17b-16e-instruct
@cf/moonshotai/kimi-k2.7-code
@cf/openai/gpt-oss-120b
@cf/zai-org/glm-5.2
anthropic/claude-haiku-4.5
anthropic/claude-sonnet-5
moonshotai/kimi-k3
openai/gpt-5.6-sol
Many more are available beyond that list. Two to avoid: anything marked unpriced (it falls back to a punitive rate), and anything you haven't checked with sf model <id>.
Run sf models rather than trusting a list — including this one. The catalog moves.
Ids are verbatim
override model = "anthropic/claude-sonnet-5"; // ✅ exactly as the catalog prints it
override model = "claude-sonnet-5"; // ❌ fails at runtime
override model = "anthropic:claude-sonnet-5"; // ❌ legacy prefix, rejected
Model ids are not validated at deploy time, so a typo doesn't fail the build — it fails the first turn, in production, with a runtime error. Two habits close that gap: copy the id from sf models rather than typing it, and run sf agents lint, which does check ids against the catalog.
If you're converting an older agent, don't guess the "nearest" id. Legacy definitions hold platform aliases rather than catalog ids, and the mapping is mechanical but not obvious — version dashes become dots, and some vendors are renamed (moonshot/kimi-k3 is really moonshotai/kimi-k3). Check every id with sf model <id>.
Reasoning effort
One property, one vocabulary:
override reasoningEffort = "low"; // "none" | "low" | "medium" | "high"
The platform translates that into whatever the vendor actually wants — one provider's effort setting, another's thinking budget. Never hand-write provider-specific reasoning parameters. Leaving it unset uses the model's own default, which is usually right.
Non-reasoning models ignore it. Any other string fails when the model resolves.
Rough guidance: low for classification, extraction, and routing; unset or medium for ordinary conversation; high where the agent has to weigh a genuinely hard decision. Higher effort costs more and takes longer, so spend it where a wrong answer is expensive.
Everything goes through the relay
All inference is proxied by the platform relay. This isn't a detour — it's what makes the rest of the system work:
- Metering. Every turn's tokens and cost are attributed to your team and visible per turn.
- Budgets. Caps can refuse a call before it happens, rather than after the bill.
- Wire-shape handling. The platform speaks each vendor's protocol, so switching model ids doesn't change your code.
Consequences: there is no env.AI to reach for, no place to put a provider key, and no supported way to call a vendor directly. Don't hand-roll a provider client.
When a budget is exhausted, the model call fails with a typed 402 — tree_budget_exhausted or team_cap_exhausted. Surface it to the user; never retry-loop it. A per-tree cap covers the whole delegation tree, so children inherit the root's budget.
Choosing per agent, and per conversation
Different nodes deserve different models. The usual shape: an expensive model at the root where judgment happens, cheap models on narrow leaves that classify or extract. A three-node tree running one strong model everywhere is the most common way to overpay.
You can also move one conversation:
await this.setConversationModel("anthropic/claude-haiku-4.5");
That persists in the conversation's state and applies from the next turn — useful for "switch to the fast model for this thread" behavior.
Watching the cost
sf api get "/apps/<appId>/agent-turns?limit=100"
Per-turn calls, tokens, and cost. A delegating tree bills as one root-labeled turn, so the number you see is the true cost of the whole thing rather than a fragment. The same data renders in the console and the desktop app.
If a turn costs more than you expected, read its trace before changing the model — usually the cause is a tool loop or a bloated prompt, not the model choice. See Observability and costs.
Next
- Tools and data — what the model is allowed to do.
- Delegation — putting cheap models where they belong.
- Observability and costs — traces and spend.
Frequently asked questions
How do I know which models I can use?
Run sf models. It lists what your team can call, with the platform-verified ones first, and sf model <id> shows one model's tier, task fit, and price.
Do I bring my own API key?
No. Every model call goes through the platform relay, which is what makes usage metered and budget caps enforceable. There is nowhere to put a provider key, and calling a vendor directly is rejected.
What happens if I mistype a model id?
Model ids are not validated at deploy, so a typo fails at runtime instead. Run sf agents lint, which does check ids against the catalog.
How do I control how hard a model thinks?
One property: reasoningEffort, set to none, low, medium, or high. The platform translates it to each vendor's own parameter — never hand-write provider reasoning settings.
Can different agents use different models?
Yes, and they should. Put an expensive model where judgment happens and cheap models on narrow leaf tasks. You can also switch a single conversation's model at runtime.