fix(provider): disable native tool calling for MiniMax

MiniMax API does not support OpenAI-style native tool definitions
(`tools` parameter in chat completions). Sending them causes a 500
Internal Server Error with "unknown error (1000)" on every request.

Add a `native_tool_calling` field to `OpenAiCompatibleProvider` so each
constructor can declare its tool-calling capability independently.
MiniMax (via `new_merge_system_into_user`) now sets this to `false`,
causing the agent loop to inject tool instructions into the system
prompt as text instead of sending native JSON tool definitions.

Closes #1387

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
keiten arch 2026-02-22 19:43:22 +09:00
parent 74581a3aa5
commit 2b92a774fb

View File

@ -33,6 +33,9 @@ pub struct OpenAiCompatibleProvider {
/// to the first `user` message, then drop the system messages.
/// Required for providers that reject `role: system` (e.g. MiniMax).
merge_system_into_user: bool,
/// Whether this provider supports OpenAI-style native tool calling.
/// When false, tools are injected into the system prompt as text.
native_tool_calling: bool,
}
/// How the provider expects the API key to be sent.
@ -54,7 +57,7 @@ impl OpenAiCompatibleProvider {
auth_style: AuthStyle,
) -> Self {
Self::new_with_options(
name, base_url, credential, auth_style, false, true, None, false,
name, base_url, credential, auth_style, false, true, None, false, true,
)
}
@ -74,6 +77,7 @@ impl OpenAiCompatibleProvider {
true,
None,
false,
true,
)
}
@ -86,7 +90,7 @@ impl OpenAiCompatibleProvider {
auth_style: AuthStyle,
) -> Self {
Self::new_with_options(
name, base_url, credential, auth_style, false, false, None, false,
name, base_url, credential, auth_style, false, false, None, false, true,
)
}
@ -110,6 +114,7 @@ impl OpenAiCompatibleProvider {
true,
Some(user_agent),
false,
true,
)
}
@ -130,6 +135,7 @@ impl OpenAiCompatibleProvider {
true,
Some(user_agent),
false,
true,
)
}
@ -142,7 +148,7 @@ impl OpenAiCompatibleProvider {
auth_style: AuthStyle,
) -> Self {
Self::new_with_options(
name, base_url, credential, auth_style, false, false, None, true,
name, base_url, credential, auth_style, false, false, None, true, false,
)
}
@ -155,6 +161,7 @@ impl OpenAiCompatibleProvider {
supports_responses_fallback: bool,
user_agent: Option<&str>,
merge_system_into_user: bool,
native_tool_calling: bool,
) -> Self {
Self {
name: name.to_string(),
@ -165,6 +172,7 @@ impl OpenAiCompatibleProvider {
supports_responses_fallback,
user_agent: user_agent.map(ToString::to_string),
merge_system_into_user,
native_tool_calling,
}
}
@ -1124,7 +1132,7 @@ impl OpenAiCompatibleProvider {
impl Provider for OpenAiCompatibleProvider {
fn capabilities(&self) -> crate::providers::traits::ProviderCapabilities {
crate::providers::traits::ProviderCapabilities {
native_tool_calling: true,
native_tool_calling: self.native_tool_calling,
vision: self.supports_vision,
}
}
@ -2385,6 +2393,22 @@ mod tests {
assert!(caps.vision);
}
#[test]
fn minimax_provider_disables_native_tool_calling() {
let p = OpenAiCompatibleProvider::new_merge_system_into_user(
"MiniMax",
"https://api.minimax.chat/v1",
Some("k"),
AuthStyle::Bearer,
);
let caps = <OpenAiCompatibleProvider as Provider>::capabilities(&p);
assert!(
!caps.native_tool_calling,
"MiniMax should use prompt-guided tool calling, not native"
);
assert!(!caps.vision);
}
#[test]
fn to_message_content_converts_image_markers_to_openai_parts() {
let content = "Describe this\n\n[IMAGE:data:image/png;base64,abcd]";