From 51385723bd3eb6e43788455fd31a0e8eb0e3fbff Mon Sep 17 00:00:00 2001 From: Vlad Ionescu Date: Tue, 7 Jul 2026 10:36:34 +0300 Subject: [PATCH 1/9] Validate protocol values in Zed Settings This both adds nicer error messages and avoid issues like https://github.com/zed-industries/zed/issues/56869#issuecomment-4550154554 For backwards compatibility and for a better UX, this was left optional and not made a required field. For better UX, multiple formatting options are supported (both openai_chat and open_ai_chat, for example) --- crates/language_models/src/provider/opencode.rs | 13 +++++++------ crates/settings_content/src/language_model.rs | 16 ++++++++++++++-- docs/src/ai/use-api-access.md | 2 +- 3 files changed, 22 insertions(+), 9 deletions(-) diff --git a/crates/language_models/src/provider/opencode.rs b/crates/language_models/src/provider/opencode.rs index 94689cc0d66b84..a46b5c1d1c0484 100644 --- a/crates/language_models/src/provider/opencode.rs +++ b/crates/language_models/src/provider/opencode.rs @@ -14,6 +14,7 @@ use language_model::{ SubPageProviderSettings, env_var, }; use opencode::{ApiProtocol, OPENCODE_API_URL, OpenCodeSubscription}; +pub use settings::OpenCodeApiProtocol; pub use settings::OpenCodeAvailableModel as AvailableModel; use settings::{Settings, SettingsStore, update_settings_file}; use std::sync::{Arc, LazyLock}; @@ -263,12 +264,12 @@ impl LanguageModelProvider for OpenCodeLanguageModelProvider { } for model in &settings.available_models { - let protocol = match model.protocol.as_str() { - "anthropic" => ApiProtocol::Anthropic, - "openai_responses" => ApiProtocol::OpenAiResponses, - "openai_chat" => ApiProtocol::OpenAiChat, - "google" => ApiProtocol::Google, - _ => ApiProtocol::OpenAiChat, // default fallback + let protocol = match model.protocol { + Some(OpenCodeApiProtocol::Anthropic) => ApiProtocol::Anthropic, + Some(OpenCodeApiProtocol::OpenAiResponses) => ApiProtocol::OpenAiResponses, + Some(OpenCodeApiProtocol::OpenAiChat) => ApiProtocol::OpenAiChat, + Some(OpenCodeApiProtocol::Google) => ApiProtocol::Google, + None => ApiProtocol::OpenAiChat, // default fallback }; let subscription = match model.subscription { Some(settings::OpenCodeModelSubscription::Go) => OpenCodeSubscription::Go, diff --git a/crates/settings_content/src/language_model.rs b/crates/settings_content/src/language_model.rs index e58f3dcc02b511..f61d80ead75efb 100644 --- a/crates/settings_content/src/language_model.rs +++ b/crates/settings_content/src/language_model.rs @@ -217,6 +217,18 @@ pub struct OpenCodeSettingsContent { pub show_free_models: Option, } +#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema, MergeFrom)] +pub enum OpenCodeApiProtocol { + #[serde(rename = "anthropic")] + Anthropic, + #[serde(rename = "openai_responses", alias = "open_ai_responses")] + OpenAiResponses, + #[serde(rename = "openai_chat", alias = "open_ai_chat")] + OpenAiChat, + #[serde(rename = "google")] + Google, +} + #[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema, MergeFrom)] #[serde(rename_all = "snake_case")] pub enum OpenCodeModelSubscription { @@ -232,8 +244,8 @@ pub struct OpenCodeAvailableModel { pub display_name: Option, pub max_tokens: u64, pub max_output_tokens: Option, - /// The API protocol to use for this model: "anthropic", "openai_responses", "openai_chat", or "google". - pub protocol: String, + /// The API protocol to use for this model: "anthropic", "openai_responses", "openai_chat", or "google". Defaults to "openai_chat". + pub protocol: Option, /// The subscription for this model: "zen", "go", or "free". Defaults to Zen. pub subscription: Option, /// Custom Model API URL to use for this model. diff --git a/docs/src/ai/use-api-access.md b/docs/src/ai/use-api-access.md index 0ddf85ba33b77c..b641f7292238b1 100644 --- a/docs/src/ai/use-api-access.md +++ b/docs/src/ai/use-api-access.md @@ -406,7 +406,7 @@ The available configuration options for custom OpenCode models are: - `display_name` (optional): human-readable model name shown in the UI, such as `Custom GLM 9000` - `max_tokens` (required): maximum model context window size, such as `1000000` - `max_output_tokens` (optional): maximum tokens the model can generate, such as `64000` -- `protocol` (required): model API protocol, one of `"anthropic"`, `"openai_responses"`, `"openai_chat"`, or `"google"` +- `protocol` (optional, default `"openai_chat"`): model API protocol, one of `"anthropic"`, `"openai_responses"`, `"openai_chat"`, or `"google"` - `reasoning_effort_levels` (optional): list of supported reasoning effort levels, such as `["low", "medium", "high", "max"]`. The last value in the list is used as the default - `interleaved_reasoning` (optional, default `false`): whether thinking tokens are sent as a dedicated `reasoning_content` field. Applies only when using the `openai_chat` protocol - `subscription` (optional): `"zen"`, `"go"`, or `"free"`; defaults to `"zen"` From 42eefa0c05eb3a40e1b9531cf09cbe00c81c78b9 Mon Sep 17 00:00:00 2001 From: Vlad Ionescu Date: Tue, 7 Jul 2026 10:39:57 +0300 Subject: [PATCH 2/9] Support parallel tool calls 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 for all but 1 model) --- crates/language_models/src/provider/opencode.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/language_models/src/provider/opencode.rs b/crates/language_models/src/provider/opencode.rs index a46b5c1d1c0484..0b4f480065e6a6 100644 --- a/crates/language_models/src/provider/opencode.rs +++ b/crates/language_models/src/provider/opencode.rs @@ -693,7 +693,7 @@ impl LanguageModel for OpenCodeLanguageModel { let openai_request = into_open_ai( request, self.model.id(), - false, + true, false, self.model.max_output_tokens(self.subscription), ChatCompletionMaxTokensParameter::MaxCompletionTokens, @@ -716,7 +716,7 @@ impl LanguageModel for OpenCodeLanguageModel { let response_request = into_open_ai_response( request, self.model.id(), - false, + true, false, self.model.max_output_tokens(self.subscription), None, From 02149b87758b77620950dfffbe259b3be1a60642 Mon Sep 17 00:00:00 2001 From: Vlad Ionescu Date: Tue, 7 Jul 2026 10:47:15 +0300 Subject: [PATCH 3/9] Fix display for thinking-only models Add support for https://github.com/zed-industries/zed/pull/58980 and https://github.com/zed-industries/zed/pull/59111 which disable the "Disable Thinking" button for models that don't support that (forces the button to always be "On" for models that are thinking-only) --- crates/language_models/src/provider/opencode.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/crates/language_models/src/provider/opencode.rs b/crates/language_models/src/provider/opencode.rs index 0b4f480065e6a6..f92f34d74517d8 100644 --- a/crates/language_models/src/provider/opencode.rs +++ b/crates/language_models/src/provider/opencode.rs @@ -575,6 +575,12 @@ impl LanguageModel for OpenCodeLanguageModel { .is_some_and(|levels| levels.iter().any(|effort| *effort != ReasoningEffort::None)) } + fn supports_disabling_thinking(&self) -> bool { + self.model + .supported_reasoning_effort_levels() + .is_some_and(|levels| levels.contains(&ReasoningEffort::None)) + } + fn supported_effort_levels(&self) -> Vec { self.model .supported_reasoning_effort_levels() From 3f2045d5274211c14b5c6c86a6638b322090ed3e Mon Sep 17 00:00:00 2001 From: Vlad Ionescu Date: Tue, 7 Jul 2026 11:43:29 +0300 Subject: [PATCH 4/9] Zen: Sonnet 5 and Fable 5 --- crates/opencode/src/opencode.rs | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/crates/opencode/src/opencode.rs b/crates/opencode/src/opencode.rs index ab030721e35ab7..b87bd468306f4c 100644 --- a/crates/opencode/src/opencode.rs +++ b/crates/opencode/src/opencode.rs @@ -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")] @@ -243,6 +247,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 +308,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", @@ -364,11 +372,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 @@ -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, @@ -514,6 +526,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 @@ -587,7 +601,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 From 80296a4e5c198d4751fd3e2d4151bf0001006b27 Mon Sep 17 00:00:00 2001 From: Vlad Ionescu Date: Tue, 7 Jul 2026 11:54:30 +0300 Subject: [PATCH 5/9] Zen: GLM 5.2, K2.7 Code, M3 + Qwen changes --- crates/opencode/src/opencode.rs | 44 +++++++++++++++++++++------------ 1 file changed, 28 insertions(+), 16 deletions(-) diff --git a/crates/opencode/src/opencode.rs b/crates/opencode/src/opencode.rs index b87bd468306f4c..f68e67a2c26ac2 100644 --- a/crates/opencode/src/opencode.rs +++ b/crates/opencode/src/opencode.rs @@ -207,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], @@ -364,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 { @@ -404,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 @@ -417,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 @@ -442,6 +441,7 @@ impl Model { | Self::Glm5_2 | Self::MiniMaxM2_5 | Self::MiniMaxM2_7 + | Self::MiniMaxM3 | Self::Nemotron3UltraFree | Self::BigPickle => true, @@ -485,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 { @@ -553,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) From 54b2b9f4bd2b52e8946c4fe95033cbd4b58c2f85 Mon Sep 17 00:00:00 2001 From: Vlad Ionescu Date: Tue, 7 Jul 2026 12:11:42 +0300 Subject: [PATCH 6/9] Add reasoning levels, as per Models.dev + OpenCode CLI + manual validation --- crates/opencode/src/opencode.rs | 98 ++++++++++++++++++++++++++++++++- 1 file changed, 95 insertions(+), 3 deletions(-) diff --git a/crates/opencode/src/opencode.rs b/crates/opencode/src/opencode.rs index f68e67a2c26ac2..af81deeac9ec53 100644 --- a/crates/opencode/src/opencode.rs +++ b/crates/opencode/src/opencode.rs @@ -677,27 +677,119 @@ impl Model { pub fn supported_reasoning_effort_levels(&self) -> Option> { match self { - Self::ClaudeOpus4_8 => Some(vec![ + // Anthropic models + Self::ClaudeFable5 + | Self::ClaudeOpus4_8 + | Self::ClaudeOpus4_7 + | Self::ClaudeSonnet5 => Some(vec![ ReasoningEffort::Low, ReasoningEffort::Medium, ReasoningEffort::High, ReasoningEffort::XHigh, + ReasoningEffort::Max, ]), - Self::Nemotron3UltraFree | Self::MimoV2_5Pro | Self::MimoV2_5 => Some(vec![ + Self::ClaudeOpus4_6 | Self::ClaudeSonnet4_6 => Some(vec![ ReasoningEffort::Low, ReasoningEffort::Medium, ReasoningEffort::High, + ReasoningEffort::Max, ]), - Self::DeepSeekV4Pro | Self::DeepSeekV4Flash => Some(vec![ + 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, .. From 27de0f2d745c6aea5d4509253c588f9d11c65a2c Mon Sep 17 00:00:00 2001 From: Vlad Ionescu Date: Tue, 7 Jul 2026 12:12:13 +0300 Subject: [PATCH 7/9] NOT TESTED: Support Google thinking with auto budget? NOT TESTED but matches the google_ai behavior --- crates/language_models/src/provider/opencode.rs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/crates/language_models/src/provider/opencode.rs b/crates/language_models/src/provider/opencode.rs index f92f34d74517d8..c3e5c0f8dab4bb 100644 --- a/crates/language_models/src/provider/opencode.rs +++ b/crates/language_models/src/provider/opencode.rs @@ -737,11 +737,14 @@ impl LanguageModel for OpenCodeLanguageModel { .boxed() } ApiProtocol::Google => { - let google_request = into_google( - request, - self.model.id().to_string(), - google_ai::GoogleModelMode::Default, - ); + let mode = if self.supports_thinking() && request.thinking_allowed { + google_ai::GoogleModelMode::Thinking { + budget_tokens: None, + } + } else { + google_ai::GoogleModelMode::Default + }; + let google_request = into_google(request, self.model.id().to_string(), mode); let stream = self.stream_google(google_request, http_client, extra_headers, cx); async move { let mapper = GoogleEventMapper::new(); From e5af72774c686453fcc9b78af487b35d5041ce70 Mon Sep 17 00:00:00 2001 From: Vlad Ionescu Date: Tue, 7 Jul 2026 12:28:57 +0300 Subject: [PATCH 8/9] Formatting --- crates/opencode/src/opencode.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/opencode/src/opencode.rs b/crates/opencode/src/opencode.rs index af81deeac9ec53..79d95b9cd25a62 100644 --- a/crates/opencode/src/opencode.rs +++ b/crates/opencode/src/opencode.rs @@ -765,7 +765,7 @@ impl Model { // DeepSeek models Self::DeepSeekV4Pro | Self::DeepSeekV4Flash => Some(vec![ - // OpenCode also supports Low&Medium but as per deepSeek those are mapped to High + // OpenCode also supports Low&Medium but as per DeepSeek those are mapped to High ReasoningEffort::High, ReasoningEffort::Max, ]), From 8074e091addd528e43f573a0d431d3c3ee0a334a Mon Sep 17 00:00:00 2001 From: Vlad Ionescu Date: Tue, 7 Jul 2026 12:33:49 +0300 Subject: [PATCH 9/9] Expand doc examples I didn't know if `none` was supported or not and, after testing, I can confirm it is supported. For clarity, I decided to also update the example list --- docs/src/ai/use-api-access.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/ai/use-api-access.md b/docs/src/ai/use-api-access.md index b641f7292238b1..7af0c0e68a3948 100644 --- a/docs/src/ai/use-api-access.md +++ b/docs/src/ai/use-api-access.md @@ -407,7 +407,7 @@ The available configuration options for custom OpenCode models are: - `max_tokens` (required): maximum model context window size, such as `1000000` - `max_output_tokens` (optional): maximum tokens the model can generate, such as `64000` - `protocol` (optional, default `"openai_chat"`): model API protocol, one of `"anthropic"`, `"openai_responses"`, `"openai_chat"`, or `"google"` -- `reasoning_effort_levels` (optional): list of supported reasoning effort levels, such as `["low", "medium", "high", "max"]`. The last value in the list is used as the default +- `reasoning_effort_levels` (optional): list of supported reasoning effort levels, such as `["none", "low", "medium", "high", "xhigh", "max"]`. The last value in the list is used as the default - `interleaved_reasoning` (optional, default `false`): whether thinking tokens are sent as a dedicated `reasoning_content` field. Applies only when using the `openai_chat` protocol - `subscription` (optional): `"zen"`, `"go"`, or `"free"`; defaults to `"zen"` - `custom_model_api_url` (optional): custom API base URL to use instead of the default OpenCode API