fix(ai-formats): preserve OpenAI tool call item ids

This commit is contained in:
zhefox
2026-06-17 04:03:40 +08:00
parent 6ab08f4014
commit 18d8ea2052
3 changed files with 17 additions and 3 deletions
@@ -1431,6 +1431,7 @@ mod tests {
assert_eq!(body["input"].as_array().expect("input").len(), 2);
assert_eq!(body["input"][0]["type"], "function_call");
assert_eq!(body["input"][0]["id"], "fc_call_auto_0");
assert_eq!(body["input"][0]["call_id"], "call_auto_0");
assert_eq!(body["input"][0]["name"], "unknown");
assert_eq!(body["input"][0]["arguments"], "{\"q\":\"rust\"}");
@@ -2580,6 +2580,7 @@ mod tests {
.value;
assert_eq!(converted["input"][0]["type"], "function_call");
assert_eq!(converted["input"][0]["id"], "fc_call_lookup_1");
assert_eq!(converted["input"][0]["call_id"], "call_lookup_1");
assert_eq!(converted["input"][1]["type"], "function_call_output");
assert_eq!(converted["input"][1]["call_id"], "call_lookup_1");
@@ -766,10 +766,11 @@ pub(crate) fn canonical_tool_use_to_openai_responses_item(
input: &Value,
extensions: &BTreeMap<String, Value>,
) -> Value {
let item_id = openai_responses_tool_call_item_id(id);
if let Some(item_type) = openai_responses_hosted_tool_call_item_type(extensions) {
let mut item = Map::new();
item.insert("type".to_string(), Value::String(item_type.to_string()));
item.insert("id".to_string(), Value::String(id.to_string()));
item.insert("id".to_string(), Value::String(item_id));
item.insert("call_id".to_string(), Value::String(id.to_string()));
item.insert("status".to_string(), Value::String("completed".to_string()));
if let Some(input_object) = input.as_object() {
@@ -788,7 +789,7 @@ pub(crate) fn canonical_tool_use_to_openai_responses_item(
if is_openai_custom_tool_call(extensions) {
return json!({
"type": "custom_tool_call",
"id": id,
"id": item_id,
"call_id": id,
"status": "completed",
"name": name,
@@ -797,13 +798,24 @@ pub(crate) fn canonical_tool_use_to_openai_responses_item(
}
json!({
"type": "function_call",
"id": id,
"id": item_id,
"call_id": id,
"name": name,
"arguments": canonicalize_tool_arguments(input),
})
}
fn openai_responses_tool_call_item_id(call_id: &str) -> String {
let trimmed = call_id.trim();
if trimmed.starts_with("fc") {
trimmed.to_string()
} else if trimmed.is_empty() {
"fc_auto".to_string()
} else {
format!("fc_{trimmed}")
}
}
fn openai_responses_hosted_tool_call_item_type(
extensions: &BTreeMap<String, Value>,
) -> Option<&str> {