diff --git a/crates/goose-providers/src/openai.rs b/crates/goose-providers/src/openai.rs index 0951375c6..f3303c1bd 100644 --- a/crates/goose-providers/src/openai.rs +++ b/crates/goose-providers/src/openai.rs @@ -439,6 +439,7 @@ impl OpenAiProvider { const PROVIDERS_NEEDING_MAX_TOKENS_REMAP: &[&str] = &[ "cerebras", "custom_deepseek", + "custom_deepseek-cp", "groq", "inception", "kimi", @@ -449,6 +450,23 @@ impl OpenAiProvider { "ovhcloud", ]; + /// DashScope / Memind relay rejects max_tokens above 131072 for DeepSeek V4. + const DASHSCOPE_COMPAT_MAX_TOKENS: i32 = 131_072; + + const PROVIDERS_DASHSCOPE_MAX_TOKENS_CAP: &[&str] = &["custom_deepseek", "custom_deepseek-cp"]; + + fn cap_token_limits_for_dashscope_compat(obj: &mut serde_json::Map) { + for key in ["max_tokens", "max_completion_tokens"] { + if let Some(value) = obj.get_mut(key) { + if let Some(n) = value.as_i64() { + if n > i64::from(Self::DASHSCOPE_COMPAT_MAX_TOKENS) { + *value = json!(Self::DASHSCOPE_COMPAT_MAX_TOKENS); + } + } + } + } + } + const PROVIDERS_NEEDING_STANDARD_CHAT_PARAMS: &[&str] = &["nearai", "pleumrouter"]; /// Providers whose reasoning models accept an OpenAI-style @@ -524,6 +542,10 @@ impl OpenAiProvider { } } } + + if Self::PROVIDERS_DASHSCOPE_MAX_TOKENS_CAP.contains(&self.name.as_str()) { + Self::cap_token_limits_for_dashscope_compat(obj); + } } payload @@ -1039,6 +1061,39 @@ mod tests { } } + #[test] + fn sanitize_caps_deepseek_cp_max_tokens_for_dashscope() { + let provider = make_provider("custom_deepseek-cp"); + let payload = json!({ + "model": "deepseek-v4-flash", + "messages": [], + "max_tokens": 384000 + }); + + let result = + provider.sanitize_request_for_compat(payload, &ModelConfig::new("deepseek-v4-flash")); + let obj = result.as_object().unwrap(); + + assert_eq!(obj.get("max_tokens").unwrap(), &json!(131_072)); + } + + #[test] + fn sanitize_caps_deepseek_cp_max_completion_tokens_for_dashscope() { + let provider = make_provider("custom_deepseek-cp"); + let payload = json!({ + "model": "deepseek-v4-flash", + "messages": [], + "max_completion_tokens": 384000 + }); + + let result = + provider.sanitize_request_for_compat(payload, &ModelConfig::new("deepseek-v4-flash")); + let obj = result.as_object().unwrap(); + + assert_eq!(obj.get("max_tokens").unwrap(), &json!(131_072)); + assert!(!obj.contains_key("max_completion_tokens")); + } + #[test] fn sanitize_remaps_max_completion_tokens_for_compat_provider() { let provider = make_provider("mistral");