Skip to content

Commit ec5e6d9

Browse files
Vlaaaaaaadebaah46
authored andcommitted
opencode: Support interleaved_reasoning and fix DeepSeek (zed-industries#55574)
OpenCode API endpoints for DeepSeek were [moved from Anthropic-compatible to OpenAI-compatible](anomalyco/opencode#24500) and DeepSeek requires interleaved reasoning enabled to work. I ran a _"rename this variable to potato"_ test and I can confirm DeepSeek V4 Flash and Pro both work now 🎉 Some other OpenCode Go models were marked [on models.dev](https://github.com/anomalyco/models.dev/tree/dev/providers/opencode-go/models) as supporting `interleaved_reasoning` so they too got that enabled. Kimi K2.5 and Kimi K2.6 continue to fail with zed-industries#51743 (zed-industries#55085 seems to hint at this being [an OpenCode issue](zed-industries#51743 (comment))?), but all other models seem to work fine both with `interleaved_reasoning` and without it 🤷 I assume it's better to have that turned on? Again, the intersection of OpenAI Chat Completions API, different models, different inference providers, how they all work together is something I know nothing about! 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 is consistent with the [UI/UX checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) - [ ] Tests cover the new/changed behavior - [X] Performance impact has been considered and is acceptable Release Notes: - OpenCode Go: use correct DeepSeek endpoints - OpenCode: add support for interleaved_reasoning
1 parent b2168bc commit ec5e6d9

4 files changed

Lines changed: 34 additions & 9 deletions

File tree

crates/language_models/src/provider/opencode.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,7 @@ impl LanguageModelProvider for OpenCodeLanguageModelProvider {
278278
protocol,
279279
reasoning_effort_levels: model.reasoning_effort_levels.clone(),
280280
custom_model_api_url: model.custom_model_api_url.clone(),
281+
interleaved_reasoning: model.interleaved_reasoning,
281282
};
282283
let key = format!("{}/{}", subscription.id_prefix(), model.name);
283284
models.insert(key, (custom_model, subscription));
@@ -664,7 +665,7 @@ impl LanguageModel for OpenCodeLanguageModel {
664665
false,
665666
self.model.max_output_tokens(),
666667
reasoning_effort,
667-
false,
668+
self.model.interleaved_reasoning(),
668669
);
669670
let stream = self.stream_openai_chat(openai_request, http_client, cx);
670671
async move {

crates/opencode/src/opencode.rs

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,7 @@ pub enum Model {
168168
protocol: ApiProtocol,
169169
reasoning_effort_levels: Option<Vec<ReasoningEffort>>,
170170
custom_model_api_url: Option<String>,
171+
interleaved_reasoning: bool,
171172
},
172173
}
173174

@@ -385,8 +386,6 @@ impl Model {
385386

386387
Self::Gemini3_1Pro | Self::Gemini3Flash => ApiProtocol::Google,
387388

388-
Self::DeepSeekV4Pro | Self::DeepSeekV4Flash => ApiProtocol::Anthropic,
389-
390389
Self::MiniMaxM2_5Free
391390
| Self::Glm5
392391
| Self::Glm5_1
@@ -398,6 +397,8 @@ impl Model {
398397
| Self::MimoV2_5
399398
| Self::Qwen3_5Plus
400399
| Self::Qwen3_6Plus
400+
| Self::DeepSeekV4Pro
401+
| Self::DeepSeekV4Flash
401402
| Self::BigPickle
402403
| Self::Nemotron3SuperFree
403404
| Self::Ling2_6FlashFree
@@ -407,6 +408,27 @@ impl Model {
407408
}
408409
}
409410

411+
pub fn interleaved_reasoning(&self) -> bool {
412+
match self {
413+
Self::DeepSeekV4Pro
414+
| Self::DeepSeekV4Flash
415+
| Self::KimiK2_5
416+
| Self::KimiK2_6
417+
| Self::MimoV2Omni
418+
| Self::MimoV2_5
419+
| Self::MimoV2_5Pro
420+
| Self::Glm5
421+
| Self::Glm5_1 => true,
422+
423+
Self::Custom {
424+
interleaved_reasoning,
425+
..
426+
} => *interleaved_reasoning,
427+
428+
_ => false,
429+
}
430+
}
431+
410432
pub fn max_token_count(&self) -> u64 {
411433
match self {
412434
// Anthropic models
@@ -487,16 +509,14 @@ impl Model {
487509
// Google models
488510
Self::Gemini3_1Pro | Self::Gemini3Flash => Some(65_536),
489511

490-
// Anthropic-compatible models
491-
Self::DeepSeekV4Pro | Self::DeepSeekV4Flash => Some(384_000),
492-
493512
// OpenAI-compatible models
494513
Self::MiniMaxM2_7 => Some(131_072),
495514
Self::MiniMaxM2_5 | Self::MiniMaxM2_5Free => Some(131_072),
496515
Self::Glm5 | Self::Glm5_1 => Some(32_768),
497516
Self::BigPickle => Some(128_000),
498517
Self::KimiK2_6 | Self::KimiK2_5 => Some(65_536),
499518
Self::Qwen3_5Plus | Self::Qwen3_6Plus => Some(65_536),
519+
Self::DeepSeekV4Pro | Self::DeepSeekV4Flash => Some(384_000),
500520
Self::Nemotron3SuperFree => Some(128_000),
501521
Self::MimoV2_5Pro | Self::MimoV2_5 | Self::MimoV2Pro | Self::MimoV2Omni => {
502522
Some(128_000)
@@ -565,14 +585,13 @@ impl Model {
565585
| Self::MiniMaxM2_7
566586
| Self::MimoV2Pro
567587
| Self::MimoV2_5Pro
588+
| Self::DeepSeekV4Pro
589+
| Self::DeepSeekV4Flash
568590
| Self::BigPickle
569591
| Self::Nemotron3SuperFree
570592
| Self::Ling2_6FlashFree
571593
| Self::Hy3PreviewFree => false,
572594

573-
// DeepSeek models (Anthropic protocol) don't support images
574-
Self::DeepSeekV4Pro | Self::DeepSeekV4Flash => false,
575-
576595
Self::Custom { protocol, .. } => matches!(
577596
protocol,
578597
ApiProtocol::Anthropic

crates/settings_content/src/language_model.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,9 @@ pub struct OpenCodeAvailableModel {
181181
pub custom_model_api_url: Option<String>,
182182
/// Supported reasoning effort levels, for example `["low", "medium", "high"].
183183
pub reasoning_effort_levels: Option<Vec<ReasoningEffort>>,
184+
/// When using OpenAiChat protocol, whether thinking tokens are sent as a dedicated `reasoning_content` field or inline in message text.
185+
#[serde(default)]
186+
pub interleaved_reasoning: bool,
184187
}
185188

186189
#[with_fallible_options]

docs/src/ai/llm-providers.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -663,6 +663,7 @@ The Zed agent comes pre-configured with OpenCode models. If you wish to use newe
663663
"max_output_tokens": 98765,
664664
"protocol": "openai_chat",
665665
"reasoning_effort_levels": ["low", "medium", "high"],
666+
"interleaved_reasoning": false,
666667
"subscription": "go",
667668
"custom_model_api_url": "https://example.com/zen"
668669
}
@@ -680,6 +681,7 @@ The available configuration options for custom models are:
680681
- `max_output_tokens` (optional): maximum tokens the model can generate, for example `64000`
681682
- `protocol` (required): model API protocol, one of `"anthropic"`, `"openai_responses"`, `"openai_chat"`, or `"google"`
682683
- `reasoning_effort_levels` (optional): list of supported reasoning effort levels, for example `["low", "medium", "high"]`. The latest value in the list is used as the default
684+
- `interleaved_reasoning` (optional, default `false`): if thinking tokens are sent as a dedicated `reasoning_content` field (`true`) or inline in message text (`false`). Applies only when using the `openai_chat` protocol
683685
- `subscription` (optional): `"zen"`, `"go"`, or `"free"` (defaults to `"zen"`)
684686
- `custom_model_api_url` (optional): custom API base URL to use instead of the default OpenCode API
685687

0 commit comments

Comments
 (0)