Skip to content

Course · OpenCode · Providers & models

Switch models without leaving the session

Say you’re working feedmill’s suspected date bug - feedmill is the course’s illustrative Go feed aggregator, and one feed type parses its published dates wrong - and the session started on a cheap, fast model. For the easy parts that was the right call: it read the parsers, mapped the dedupe path, and proposed sane edits without burning much. Then you hit the part of feedmill that always bites: timezone normalization. Feeds publish timestamps in a dozen shapes - Z, +05:30, naive local, an Atom feed that lies about its offset - and the cheap model is on its third wrong proposal, each one fixing one source and quietly breaking another. This isn’t a prompting problem. It’s a reasoning problem, and the wrong model is on it.

The move is not to quit, re-launch with a bigger model, and re-explain everything. It’s to swap the model under the live session and keep going.

OpenCode’s defining trait is that the session isn’t welded to one model or to one vendor. Every provider you’ve connected is selectable at any prompt, and switching doesn’t reset the thread - the model that picks up your next turn inherits the entire conversation so far: the files already read, the failed proposals, the constraints you’ve stated. Type the slash command at the prompt:

> /models

That opens the in-session model switcher - a filterable list of every model across every connected provider (/models as of October 2026; models documentation). You’re not picking from one vendor’s menu; you’re picking from the whole set you connected (if only one is wired so far, the multi-provider setup page shows how to add another). Type to filter - an illustrative picker, since catalogs move and yours should be read live:

Select model
> type to filter ◂ filter
anthropic/claude-opus-4-... reasoning · vision
anthropic/claude-sonnet-4-... reasoning · vision
openai/gpt-5... reasoning
google/gemini-2.5-pro reasoning · vision
openrouter/deepseek-... reasoning
↑↓ move enter select esc cancel

Models are addressed in provider/model form - anthropic/claude-opus-4-..., openai/gpt-5..., google/gemini-2.5-pro - so the same identifier you’d put in a config file is what you pick here. Hit enter, and the next thing the strong model sees is everything the cheap one already gathered - an illustrative exchange:

✓ model → anthropic/claude-opus-4-...
> the previous attempts each fixed one feed's timezone and broke another.
step back and find the rule that holds for all of them at once before
you touch any code.
⏵ Read internal/parse/date.go, internal/parse/atom.go (2 files)
The breakage is that each fix special-cased one input shape. The
invariant that actually holds: every feed timestamp must be resolved to
a single UTC instant at the parse boundary, and a feed that declares no
offset is untrusted - it gets the feed's configured fallback zone, never
the server's local zone. I'll centralize that in one resolver and route
every parser through it.

Notice what you didn’t pay for: re-exploring the repo, re-stating the bug, re-listing every feed that broke. The expensive model spent its first turn reasoning about the accumulated context, not rebuilding it. That’s the whole point of switching in place - you’re buying smarter thinking exactly where the task got hard, and only there. The decision is capability against cost, and cost includes the cheap model’s failed attempts: three wrong proposals already spent tokens and your review time.

Switching in place isn’t free, though. Prompt caching is per model and billed by the provider, so the new model starts with a cold cache and reads the whole thread as uncached input on its first turn. You skip the work of re-gathering context, but you pay to re-ingest the transcript. That price grows with the length of the thread, and you pay it again on every switch, including the switch back down. A long thread may also not fit a model with a smaller context window. Early in a session the trade is cheap. Deep into a long one, a fresh session that starts from a short summary can cost less.

A stronger base model is one lever; how hard it thinks is another. For reasoning-capable models, OpenCode exposes thinking variants - preset reasoning-effort levels you can change at runtime without picking a different model. The keybind is variant_cycle, bound to ctrl+t by default, and it steps through the variants the current model supports (keybind defaults as of October 2026; keybinds documentation):

⌃t variant → max (anthropic: high → max)

The variants are provider-shaped, so what ctrl+t cycles through depends on who you’re talking to. As of October 2026 the models documentation lists the built-in ones: Anthropic models toggle between high and max; OpenAI reasoning models step across a wider band - none, minimal, low, medium, high, up to xhigh; Google models offer low and high. Which lever first: when the current model supports variants and is nearly right, bump its variant and keep the model; when it repeats the same wrong reasoning, switch models. The cheap model here had failed three times, so the switch came first, and the variant bump goes on the strong model. For this timezone resolver, a notch up buys the model room to actually hold every feed shape in mind at once instead of patching them one at a time.

Spend it deliberately. The reason you started cheap and only switched here is that the expensive combination - strong model, high variant - is exactly that, expensive, and most of feedmill’s work doesn’t need it. Reading parsers, wiring a new source, writing a test: a cheap model on a low variant clears all of it. Reserve the heavy reasoner for the stretch that defeated the cheap one, then switch back down.

Once the resolver lands and the tests go green, you’re back to ordinary edits - and you don’t want to keep paying reasoner rates to wire up the next feed. Open /models again and drop to the cheap model for the rest of the session, same thread intact:

> /models # back down to the fast model for the cleanup
✓ model → anthropic/claude-haiku-...

If you’re bouncing between two models repeatedly, the faster path is the recent-model cycle on f2 (model_cycle_recent), which flips between your recently used models without opening the picker (OpenCode keeps that list across sessions, not only this one; local.tsx in source); shift+f2 (model_cycle_recent_reverse) cycles the other way (the same defaults as of October 2026; keybinds documentation). Either way, the session never restarted: one continuous thread, the model dialed up for the hard stretch and back down for the rest. Because the picker spans every provider you connected, the question stops being “which model did I launch with” and becomes “which model, from any vendor, should answer this turn.”

You’ve now switched models by hand, mid-thread, for one stretch of work. The next question is what to do when a part of the work is permanently better suited to a different model - not a one-off switch you drive, but a standing assignment. Next: give each agent its own model.