opencode: Model updates + fixes (#60526)

**TL;DR**: model updates + reasoning levels + fixes discovered when
working on https://github.com/zed-industries/zed/pull/60373

# Objective

Since the model auto-discovery PR was
[cancelled](https://github.com/zed-industries/zed/pull/60373#issuecomment-4886521448),
here is a manual model list update! I also copied the stand-alone
bugfixes/enhancements from that PR.

## Solution

A lot of manual work 😅 

**OpenCode Zen**: 
- added Fable 5 and Sonnet 5
- added models that were previously only available on OpenCode Go: GLM
5.2, Kimi K2.7 Code, and Minimax M3
- added reasoning levels for all models. I started from the data on
[`Models.dev`](https://models.dev) (the `/api.json` raw data), and then
I matched that with what is shown in the OpenCode CLI and what I know to
be true

**OpenCode Go**:
- added reasoning levels for GLM 5.2

**OpenCode in general**:
- added `protocol` validation for the settings, by moving from a random
`String` to an `enum`, for both nicer error messages (random strings or
typos will get an error instead of using `openai_chat` by default) and
to avoid issues like [folks saying non-existent protocols are a
thing](https://github.com/zed-industries/zed/issues/56869#issuecomment-4550154554)
- enabled parallel tool calls by default. As per [OpenCode developer on
Discord](https://discord.com/channels/1391832426048651334/1471233160993050918/1472020924881702912),
_"almost all models worth using support parallel tool calling
natively"_. Manual tests confirmed all OpenCode Go models support this
correctly (and was enabled by default on the OpenCode side for all but 1
model). I initially wanted to skip this from the release notes, but I
added it so folks are aware of it in case any issues are caused by this
being enabled for all models
- allegedly fixed Google thinking since reasoning levels / thinking
effort levels were added for Google models and an auto-checker LLM
highlighted that was not properly configured
- added support for the new-ish `supports_disabling_thinking` so
thinking-only models don't get a no-impact toggle to disable thinking

I have no idea if any of the Free models will disappear in 2 days or
not, so I did not update those 🤷 (as per decision in
https://github.com/zed-industries/zed/issues/56869#issuecomment-4466637648)

## Testing

The Zen and Google changes were not tested as I don't have a Zen
subscription and I stubbornly refuse to get one.

The Free&Go changes were tested by running a "_rename this variable for
me. add a function. delete the function_" test with a few different
models.

## Self-Review Checklist:

- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content adheres to Zed's UI standards
([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
and
[icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md)
guidelines)
- [ ] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable

---

Release Notes:
- Agent: OpenCode settings now validate `protocol` values
- Agent: OpenCode only shows the "Disable thinking" toggle if thinking
can indeed be disabled/enabled
- Agent: OpenCode models now enable parallel tool calls by default
- Agent: Updated OpenCode Zen models (added Fable 5, Sonnet 5, GLM 5.2,
Kimi K2.7 Code, and Minimax M3)
- Agent: Added OpenCode Go GLM 5.2 reasoning effort levels
- Agent: Added reasoning effort levels for all OpenCode Zen models
- Agent: Fixed thinking for OpenCode Zen Google models
This commit is contained in:
Vlad Ionescu authored and GitHub committed 2026-07-07 10:16:42 +00:00
1 parent 2b47bfd6b2
commit 114307768e
4 files changed
+191 -49

No files matched your search

+152 -32
View File
@@ -77,6 +77,10 @@ pub enum Model {
ClaudeSonnet4,
#[serde(rename = "claude-haiku-4-5")]
ClaudeHaiku4_5,
#[serde(rename = "claude-sonnet-5")]
ClaudeSonnet5,
#[serde(rename = "claude-fable-5")]
ClaudeFable5,
// -- OpenAI Responses API models --
#[serde(rename = "gpt-5.5")]
@@ -203,23 +207,24 @@ impl Model {
match self {
// Models available in both Zen and Go
Self::Glm5_1
| Self::Glm5_2
| Self::KimiK2_6
| Self::KimiK2_7Code
| Self::MiniMaxM2_7
| Self::MiniMaxM3
| Self::DeepSeekV4Pro
| Self::DeepSeekV4Flash
| Self::Qwen3_6Plus => &[OpenCodeSubscription::Zen, OpenCodeSubscription::Go],
// Go-only models
Self::MimoV2_5Pro
| Self::MimoV2_5
| Self::Qwen3_7Plus
| Self::Qwen3_7Max
| Self::KimiK2_7Code
| Self::Glm5_2
| Self::MiniMaxM3 => &[OpenCodeSubscription::Go],
Self::MimoV2_5Pro | Self::MimoV2_5 | Self::Qwen3_7Plus | Self::Qwen3_7Max => {
&[OpenCodeSubscription::Go]
}
// Deprecated on Go (per models.dev); still offered on Zen
Self::Glm5 | Self::KimiK2_5 | Self::MiniMaxM2_5 => &[OpenCodeSubscription::Zen],
Self::Glm5 | Self::KimiK2_5 | Self::MiniMaxM2_5 | Self::Qwen3_5Plus => {
&[OpenCodeSubscription::Zen]
}
// Free models
Self::Nemotron3UltraFree | Self::BigPickle => &[OpenCodeSubscription::Free],
@@ -243,6 +248,8 @@ impl Model {
Self::ClaudeSonnet4_5 => "claude-sonnet-4-5",
Self::ClaudeSonnet4 => "claude-sonnet-4",
Self::ClaudeHaiku4_5 => "claude-haiku-4-5",
Self::ClaudeSonnet5 => "claude-sonnet-5",
Self::ClaudeFable5 => "claude-fable-5",
Self::Gpt5_5 => "gpt-5.5",
Self::Gpt5_5Pro => "gpt-5.5-pro",
@@ -302,6 +309,8 @@ impl Model {
Self::ClaudeSonnet4_5 => "Claude Sonnet 4.5",
Self::ClaudeSonnet4 => "Claude Sonnet 4",
Self::ClaudeHaiku4_5 => "Claude Haiku 4.5",
Self::ClaudeSonnet5 => "Claude Sonnet 5",
Self::ClaudeFable5 => "Claude Fable 5",
Self::Gpt5_5 => "GPT 5.5",
Self::Gpt5_5Pro => "GPT 5.5 Pro",
@@ -356,7 +365,7 @@ impl Model {
match self {
// Models offered by OpenCode have the same configuration across subscriptions
// with one outlier: non-free MiniMax models
Self::MiniMaxM2_7 | Self::MiniMaxM2_5 => {
Self::MiniMaxM3 | Self::MiniMaxM2_7 | Self::MiniMaxM2_5 => {
if subscription == OpenCodeSubscription::Zen {
ApiProtocol::OpenAiChat
} else {
@@ -364,11 +373,13 @@ impl Model {
}
}
Self::ClaudeOpus4_8
Self::ClaudeFable5
| Self::ClaudeOpus4_8
| Self::ClaudeOpus4_7
| Self::ClaudeOpus4_6
| Self::ClaudeOpus4_5
| Self::ClaudeOpus4_1
| Self::ClaudeSonnet5
| Self::ClaudeSonnet4_6
| Self::ClaudeSonnet4_5
| Self::ClaudeSonnet4
@@ -394,9 +405,9 @@ impl Model {
Self::Gemini3_1Pro | Self::Gemini3Flash | Self::Gemini3_5Flash => ApiProtocol::Google,
Self::Qwen3_7Max | Self::Qwen3_7Plus => ApiProtocol::Anthropic,
Self::MiniMaxM3 => ApiProtocol::Anthropic,
Self::Qwen3_7Max | Self::Qwen3_7Plus | Self::Qwen3_6Plus | Self::Qwen3_5Plus => {
ApiProtocol::Anthropic
}
Self::Glm5
| Self::Glm5_1
@@ -407,8 +418,6 @@ impl Model {
| Self::KimiK2_7Code
| Self::MimoV2_5Pro
| Self::MimoV2_5
| Self::Qwen3_5Plus
| Self::Qwen3_6Plus
| Self::DeepSeekV4Pro
| Self::DeepSeekV4Flash
| Self::BigPickle
@@ -432,6 +441,7 @@ impl Model {
| Self::Glm5_2
| Self::MiniMaxM2_5
| Self::MiniMaxM2_7
| Self::MiniMaxM3
| Self::Nemotron3UltraFree
| Self::BigPickle => true,
@@ -453,6 +463,8 @@ impl Model {
Self::ClaudeOpus4_5 | Self::ClaudeHaiku4_5 => 200_000,
Self::ClaudeOpus4_1 => 200_000,
Self::ClaudeSonnet4 => 1_000_000,
Self::ClaudeSonnet5 => 1_000_000,
Self::ClaudeFable5 => 1_000_000,
// OpenAI models
Self::Gpt5_5 | Self::Gpt5_5Pro => 1_050_000,
@@ -473,7 +485,13 @@ impl Model {
// OpenAI-compatible models
Self::MiniMaxM2_7 => 204_800,
Self::MiniMaxM3 => 512_000,
Self::MiniMaxM3 => {
if subscription == OpenCodeSubscription::Go {
1_000_000
} else {
512_000
}
}
Self::MiniMaxM2_5 => 204_800,
Self::Glm5 | Self::Glm5_1 => {
if subscription == OpenCodeSubscription::Go {
@@ -514,6 +532,8 @@ impl Model {
| Self::ClaudeHaiku4_5
| Self::ClaudeSonnet4 => Some(64_000),
Self::ClaudeOpus4_1 => Some(32_000),
Self::ClaudeSonnet5 => Some(128_000),
Self::ClaudeFable5 => Some(128_000),
// OpenAI models
Self::Gpt5_5
@@ -539,7 +559,13 @@ impl Model {
// OpenAI-compatible models
Self::MiniMaxM2_7 => Some(131_072),
Self::MiniMaxM3 => Some(131_072),
Self::MiniMaxM3 => {
if subscription == OpenCodeSubscription::Go {
Some(131_072)
} else {
Some(128_000)
}
}
Self::MiniMaxM2_5 => {
if subscription == OpenCodeSubscription::Go {
Some(65_536)
@@ -587,7 +613,9 @@ impl Model {
| Self::ClaudeSonnet4_6
| Self::ClaudeSonnet4_5
| Self::ClaudeSonnet4
| Self::ClaudeHaiku4_5 => true,
| Self::ClaudeHaiku4_5
| Self::ClaudeSonnet5
| Self::ClaudeFable5 => true,
// OpenAI models support images
Self::Gpt5_5
@@ -649,20 +677,11 @@ impl Model {
pub fn supported_reasoning_effort_levels(&self) -> Option<Vec<ReasoningEffort>> {
match self {
Self::ClaudeOpus4_8 => Some(vec![
ReasoningEffort::Low,
ReasoningEffort::Medium,
ReasoningEffort::High,
ReasoningEffort::XHigh,
]),
Self::Nemotron3UltraFree | Self::MimoV2_5Pro | Self::MimoV2_5 => Some(vec![
ReasoningEffort::Low,
ReasoningEffort::Medium,
ReasoningEffort::High,
]),
Self::DeepSeekV4Pro | Self::DeepSeekV4Flash => Some(vec![
// Anthropic models
Self::ClaudeFable5
| Self::ClaudeOpus4_8
| Self::ClaudeOpus4_7
| Self::ClaudeSonnet5 => Some(vec![
ReasoningEffort::Low,
ReasoningEffort::Medium,
ReasoningEffort::High,
@@ -670,6 +689,107 @@ impl Model {
ReasoningEffort::Max,
]),
Self::ClaudeOpus4_6 | Self::ClaudeSonnet4_6 => Some(vec![
ReasoningEffort::Low,
ReasoningEffort::Medium,
ReasoningEffort::High,
ReasoningEffort::Max,
]),
Self::ClaudeOpus4_5 => Some(vec![
ReasoningEffort::Low,
ReasoningEffort::Medium,
ReasoningEffort::High,
]),
// OpenAI models
Self::Gpt5_5
| Self::Gpt5_4
| Self::Gpt5_4Mini
| Self::Gpt5_4Nano
| Self::Gpt5_3Codex
| Self::Gpt5_2 => Some(vec![
ReasoningEffort::None,
ReasoningEffort::Low,
ReasoningEffort::Medium,
ReasoningEffort::High,
ReasoningEffort::XHigh,
]),
Self::Gpt5_5Pro | Self::Gpt5_4Pro => Some(vec![
ReasoningEffort::Medium,
ReasoningEffort::High,
ReasoningEffort::XHigh,
]),
Self::Gpt5_2Codex | Self::Gpt5_3Spark | Self::Gpt5_1CodexMax => Some(vec![
ReasoningEffort::Low,
ReasoningEffort::Medium,
ReasoningEffort::High,
ReasoningEffort::XHigh,
]),
Self::Gpt5_1 => Some(vec![
ReasoningEffort::None,
ReasoningEffort::Low,
ReasoningEffort::Medium,
ReasoningEffort::High,
]),
Self::Gpt5Codex | Self::Gpt5_1Codex | Self::Gpt5_1CodexMini => Some(vec![
ReasoningEffort::Low,
ReasoningEffort::Medium,
ReasoningEffort::High,
]),
Self::Gpt5 | Self::Gpt5Nano => Some(vec![
ReasoningEffort::Minimal,
ReasoningEffort::Low,
ReasoningEffort::Medium,
ReasoningEffort::High,
]),
// Google models
Self::Gemini3Flash | Self::Gemini3_5Flash => Some(vec![
ReasoningEffort::Minimal,
ReasoningEffort::Low,
ReasoningEffort::Medium,
ReasoningEffort::High,
]),
Self::Gemini3_1Pro => Some(vec![
ReasoningEffort::Low,
ReasoningEffort::Medium,
ReasoningEffort::High,
]),
// DeepSeek models
Self::DeepSeekV4Pro | Self::DeepSeekV4Flash => Some(vec![
// OpenCode also supports Low&Medium but as per DeepSeek those are mapped to High
ReasoningEffort::High,
ReasoningEffort::Max,
]),
// MiniMax models
Self::MiniMaxM3 => Some(vec![ReasoningEffort::None]),
// NVIDIA models
Self::Nemotron3UltraFree => Some(vec![
ReasoningEffort::Low,
ReasoningEffort::Medium,
ReasoningEffort::High,
]),
// Xiaomi MiMo models
Self::MimoV2_5Pro | Self::MimoV2_5 => Some(vec![
ReasoningEffort::Low,
ReasoningEffort::Medium,
ReasoningEffort::High,
]),
// Z AI models
Self::Glm5_2 => Some(vec![ReasoningEffort::High, ReasoningEffort::Max]),
Self::Custom {
reasoning_effort_levels,
..