fix(provider): omit null tool-call fields in compatible payloads

This commit is contained in:
argenis de la rosa 2026-03-04 05:57:13 -05:00
parent 32a2cf370d
commit 0aa4f94c86

View File

@ -526,20 +526,23 @@ impl ResponseMessage {
struct ToolCall {
#[serde(skip_serializing_if = "Option::is_none")]
id: Option<String>,
#[serde(rename = "type")]
#[serde(default)]
#[serde(rename = "type", default, skip_serializing_if = "Option::is_none")]
kind: Option<String>,
#[serde(default)]
#[serde(default, skip_serializing_if = "Option::is_none")]
function: Option<Function>,
// Compatibility: Some providers (e.g., older GLM) may use 'name' directly
#[serde(default)]
#[serde(default, skip_serializing_if = "Option::is_none")]
name: Option<String>,
#[serde(default)]
#[serde(default, skip_serializing_if = "Option::is_none")]
arguments: Option<String>,
// Compatibility: DeepSeek sometimes wraps arguments differently
#[serde(rename = "parameters", default)]
#[serde(
rename = "parameters",
default,
skip_serializing_if = "Option::is_none"
)]
parameters: Option<serde_json::Value>,
}
@ -2919,6 +2922,26 @@ mod tests {
));
}
#[test]
fn convert_messages_for_native_omits_null_compatibility_tool_call_fields() {
let input = vec![ChatMessage::assistant(
r#"{"content":"Need tool","tool_calls":[{"id":"call_abc","name":"shell","arguments":"{\"command\":\"pwd\"}"}]}"#,
)];
let converted = OpenAiCompatibleProvider::convert_messages_for_native(&input, true);
assert_eq!(converted.len(), 1);
let serialized = serde_json::to_value(&converted[0]).unwrap();
let tool_call = &serialized["tool_calls"][0];
assert_eq!(tool_call["id"], "call_abc");
assert_eq!(tool_call["type"], "function");
assert_eq!(tool_call["function"]["name"], "shell");
assert_eq!(tool_call["function"]["arguments"], "{\"command\":\"pwd\"}");
assert!(tool_call.get("name").is_none());
assert!(tool_call.get("arguments").is_none());
assert!(tool_call.get("parameters").is_none());
}
#[test]
fn flatten_system_messages_merges_into_first_user() {
let input = vec![