mirror of
https://github.com/farion1231/cc-switch.git
synced 2026-07-24 12:44:18 +08:00
fix: normalize function parameters type to "object" for strict OpenAI-compatible providers (#4706)
* fix: normalize function parameters type to "object" for strict OpenAI-compatible providers
Some Responses tools carry parameters with `type: null` (e.g.
codex_app__automation_update), causing HTTP 400 from strict
OpenAI-compatible providers like DeepSeek that require
`{"type": "object", "properties": {...}}`.
This adds normalize_function_parameters() to ensure the type
field is always "object" in both branches of
responses_function_tool_to_chat_tool.
Closes #4705
* style: fix cargo fmt issues
* fix: handle null function parameters
This commit is contained in:
@@ -1096,6 +1096,26 @@ fn serialize_tool_definition_for_description(tool: &Value) -> String {
|
||||
canonical_json_string(tool)
|
||||
}
|
||||
|
||||
/// Normalize a function's `parameters` JSON Schema so `type` is always `"object"`.
|
||||
///
|
||||
/// Some Responses tools carry `parameters: null` or `parameters: {"type": null}`,
|
||||
/// but OpenAI Chat Completions strictly requires `{"type": "object", "properties": {...}}`.
|
||||
fn normalize_function_parameters(params: Option<&Value>) -> Value {
|
||||
let mut params = match params {
|
||||
Some(Value::Object(obj)) => Value::Object(obj.clone()),
|
||||
_ => json!({"type": "object", "properties": {}}),
|
||||
};
|
||||
if let Some(obj) = params.as_object_mut() {
|
||||
match obj.get("type").and_then(|v| v.as_str()) {
|
||||
Some("object") => {}
|
||||
_ => {
|
||||
obj.insert("type".to_string(), json!("object"));
|
||||
}
|
||||
}
|
||||
}
|
||||
params
|
||||
}
|
||||
|
||||
fn responses_function_tool_to_chat_tool(tool: &Value, chat_name: &str) -> Option<Value> {
|
||||
if tool.get("type").and_then(|v| v.as_str()) != Some("function") {
|
||||
return None;
|
||||
@@ -1110,6 +1130,14 @@ fn responses_function_tool_to_chat_tool(tool: &Value, chat_name: &str) -> Option
|
||||
.get_mut("function")
|
||||
.and_then(|value| value.as_object_mut())
|
||||
{
|
||||
// Ensure parameters.type is "object" for strict OpenAI-compatible providers
|
||||
if let Some(params) = obj.get("parameters") {
|
||||
let normalized = normalize_function_parameters(Some(params));
|
||||
if normalized != *params {
|
||||
obj.insert("parameters".to_string(), normalized);
|
||||
}
|
||||
}
|
||||
|
||||
obj.insert("name".to_string(), json!(chat_name));
|
||||
if let Some(strict) = tool.get("strict").cloned() {
|
||||
obj.entry("strict".to_string()).or_insert(strict);
|
||||
@@ -1121,7 +1149,7 @@ fn responses_function_tool_to_chat_tool(tool: &Value, chat_name: &str) -> Option
|
||||
let mut function = json!({
|
||||
"name": chat_name,
|
||||
"description": tool.get("description").cloned().unwrap_or(Value::Null),
|
||||
"parameters": tool.get("parameters").cloned().unwrap_or_else(|| json!({}))
|
||||
"parameters": normalize_function_parameters(tool.get("parameters"))
|
||||
});
|
||||
if let Some(strict) = tool.get("strict") {
|
||||
function["strict"] = strict.clone();
|
||||
|
||||
Reference in New Issue
Block a user