mirror of
https://github.com/fawney19/Aether.git
synced 2026-10-12 20:19:51 +08:00
fix(ai-formats): preserve OpenAI Responses tool call item ids
This commit is contained in:
@@ -1430,7 +1430,7 @@ mod tests {
|
|||||||
|
|
||||||
assert_eq!(body["input"].as_array().expect("input").len(), 2);
|
assert_eq!(body["input"].as_array().expect("input").len(), 2);
|
||||||
assert_eq!(body["input"][0]["type"], "function_call");
|
assert_eq!(body["input"][0]["type"], "function_call");
|
||||||
assert_eq!(body["input"][0]["id"], "fc_call_auto_0");
|
assert_eq!(body["input"][0]["id"], "call_auto_0");
|
||||||
assert_eq!(body["input"][0]["call_id"], "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]["name"], "unknown");
|
||||||
assert_eq!(body["input"][0]["arguments"], "{\"q\":\"rust\"}");
|
assert_eq!(body["input"][0]["arguments"], "{\"q\":\"rust\"}");
|
||||||
|
|||||||
@@ -2609,7 +2609,7 @@ mod tests {
|
|||||||
.value;
|
.value;
|
||||||
|
|
||||||
assert_eq!(converted["input"][0]["type"], "function_call");
|
assert_eq!(converted["input"][0]["type"], "function_call");
|
||||||
assert_eq!(converted["input"][0]["id"], "fc_call_lookup_1");
|
assert_eq!(converted["input"][0]["id"], "call_lookup_1");
|
||||||
assert_eq!(converted["input"][0]["call_id"], "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]["type"], "function_call_output");
|
||||||
assert_eq!(converted["input"][1]["call_id"], "call_lookup_1");
|
assert_eq!(converted["input"][1]["call_id"], "call_lookup_1");
|
||||||
|
|||||||
@@ -788,16 +788,17 @@ pub(crate) fn canonical_tool_use_to_openai_responses_item(
|
|||||||
return Value::Object(item);
|
return Value::Object(item);
|
||||||
}
|
}
|
||||||
if is_openai_custom_tool_call(extensions) {
|
if is_openai_custom_tool_call(extensions) {
|
||||||
|
let item_id = openai_responses_tool_call_item_id(id, extensions);
|
||||||
return json!({
|
return json!({
|
||||||
"type": "custom_tool_call",
|
"type": "custom_tool_call",
|
||||||
"id": id,
|
"id": item_id,
|
||||||
"call_id": id,
|
"call_id": id,
|
||||||
"status": "completed",
|
"status": "completed",
|
||||||
"name": name,
|
"name": name,
|
||||||
"input": openai_custom_tool_input_text(input),
|
"input": openai_custom_tool_input_text(input),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
let item_id = openai_responses_function_call_item_id(id);
|
let item_id = openai_responses_tool_call_item_id(id, extensions);
|
||||||
json!({
|
json!({
|
||||||
"type": "function_call",
|
"type": "function_call",
|
||||||
"id": item_id,
|
"id": item_id,
|
||||||
@@ -807,14 +808,23 @@ pub(crate) fn canonical_tool_use_to_openai_responses_item(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn openai_responses_function_call_item_id(call_id: &str) -> String {
|
fn openai_responses_tool_call_item_id(
|
||||||
|
call_id: &str,
|
||||||
|
extensions: &BTreeMap<String, Value>,
|
||||||
|
) -> String {
|
||||||
|
if let Some(item_id) = openai_responses_extension(extensions)
|
||||||
|
.and_then(|value| value.get("item_id").or_else(|| value.get("id")))
|
||||||
|
.and_then(Value::as_str)
|
||||||
|
.map(str::trim)
|
||||||
|
.filter(|value| !value.is_empty())
|
||||||
|
{
|
||||||
|
return item_id.to_string();
|
||||||
|
}
|
||||||
let trimmed = call_id.trim();
|
let trimmed = call_id.trim();
|
||||||
if trimmed.starts_with("fc") {
|
if trimmed.is_empty() {
|
||||||
trimmed.to_string()
|
"call_auto".to_string()
|
||||||
} else if trimmed.is_empty() {
|
|
||||||
"fc_auto".to_string()
|
|
||||||
} else {
|
} else {
|
||||||
format!("fc_{trimmed}")
|
trimmed.to_string()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1938,14 +1948,16 @@ pub(crate) fn openai_responses_input_to_canonical_messages(
|
|||||||
next_generated_tool_call_index += 1;
|
next_generated_tool_call_index += 1;
|
||||||
generated
|
generated
|
||||||
});
|
});
|
||||||
|
let mut extensions = openai_responses_extensions(
|
||||||
|
item_object,
|
||||||
|
&["type", "call_id", "id", "name", "arguments"],
|
||||||
|
);
|
||||||
|
remember_openai_responses_tool_call_item_id(&mut extensions, item_object);
|
||||||
let tool_use = CanonicalContentBlock::ToolUse {
|
let tool_use = CanonicalContentBlock::ToolUse {
|
||||||
id,
|
id,
|
||||||
name,
|
name,
|
||||||
input: parse_jsonish_value(item_object.get("arguments")),
|
input: parse_jsonish_value(item_object.get("arguments")),
|
||||||
extensions: openai_responses_extensions(
|
extensions,
|
||||||
item_object,
|
|
||||||
&["type", "call_id", "id", "name", "arguments"],
|
|
||||||
),
|
|
||||||
};
|
};
|
||||||
append_openai_responses_tool_use(
|
append_openai_responses_tool_use(
|
||||||
&mut messages,
|
&mut messages,
|
||||||
@@ -1986,6 +1998,7 @@ pub(crate) fn openai_responses_input_to_canonical_messages(
|
|||||||
"status",
|
"status",
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
remember_openai_responses_tool_call_item_id(&mut extensions, item_object);
|
||||||
mark_openai_custom_tool_call(&mut extensions);
|
mark_openai_custom_tool_call(&mut extensions);
|
||||||
let tool_use = CanonicalContentBlock::ToolUse {
|
let tool_use = CanonicalContentBlock::ToolUse {
|
||||||
id,
|
id,
|
||||||
@@ -2328,14 +2341,16 @@ pub(crate) fn openai_responses_output_to_canonical_blocks(
|
|||||||
.filter(|value| !value.is_empty())
|
.filter(|value| !value.is_empty())
|
||||||
.map(ToOwned::to_owned)
|
.map(ToOwned::to_owned)
|
||||||
.unwrap_or_else(|| format!("call_auto_{index}"));
|
.unwrap_or_else(|| format!("call_auto_{index}"));
|
||||||
|
let mut extensions = openai_responses_extensions(
|
||||||
|
item_object,
|
||||||
|
&["type", "id", "call_id", "name", "arguments", "status"],
|
||||||
|
);
|
||||||
|
remember_openai_responses_tool_call_item_id(&mut extensions, item_object);
|
||||||
blocks.push(CanonicalContentBlock::ToolUse {
|
blocks.push(CanonicalContentBlock::ToolUse {
|
||||||
id,
|
id,
|
||||||
name: name.to_string(),
|
name: name.to_string(),
|
||||||
input: parse_jsonish_value(item_object.get("arguments")),
|
input: parse_jsonish_value(item_object.get("arguments")),
|
||||||
extensions: openai_responses_extensions(
|
extensions,
|
||||||
item_object,
|
|
||||||
&["type", "id", "call_id", "name", "arguments", "status"],
|
|
||||||
),
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
"custom_tool_call" => {
|
"custom_tool_call" => {
|
||||||
@@ -2365,6 +2380,7 @@ pub(crate) fn openai_responses_output_to_canonical_blocks(
|
|||||||
"status",
|
"status",
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
remember_openai_responses_tool_call_item_id(&mut extensions, item_object);
|
||||||
mark_openai_custom_tool_call(&mut extensions);
|
mark_openai_custom_tool_call(&mut extensions);
|
||||||
blocks.push(CanonicalContentBlock::ToolUse {
|
blocks.push(CanonicalContentBlock::ToolUse {
|
||||||
id,
|
id,
|
||||||
@@ -3236,6 +3252,21 @@ fn mark_openai_custom_tool_call(extensions: &mut BTreeMap<String, Value>) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn remember_openai_responses_tool_call_item_id(
|
||||||
|
extensions: &mut BTreeMap<String, Value>,
|
||||||
|
item_object: &Map<String, Value>,
|
||||||
|
) {
|
||||||
|
if let Some(item_id) = item_object
|
||||||
|
.get("id")
|
||||||
|
.and_then(Value::as_str)
|
||||||
|
.map(str::trim)
|
||||||
|
.filter(|value| !value.is_empty())
|
||||||
|
{
|
||||||
|
canonical_extension_object_mut(extensions, OPENAI_RESPONSES_EXTENSION_NAMESPACE)
|
||||||
|
.insert("item_id".to_string(), Value::String(item_id.to_string()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn mark_openai_output_audio(extensions: &mut BTreeMap<String, Value>) {
|
fn mark_openai_output_audio(extensions: &mut BTreeMap<String, Value>) {
|
||||||
canonical_extension_object_mut(extensions, AETHER_EXTENSION_NAMESPACE).insert(
|
canonical_extension_object_mut(extensions, AETHER_EXTENSION_NAMESPACE).insert(
|
||||||
"source".to_string(),
|
"source".to_string(),
|
||||||
@@ -7420,6 +7451,8 @@ mod tests {
|
|||||||
let rebuilt = canonical_to_openai_responses_request(&canonical, "gpt-5-upstream", false)
|
let rebuilt = canonical_to_openai_responses_request(&canonical, "gpt-5-upstream", false)
|
||||||
.expect("openai responses request");
|
.expect("openai responses request");
|
||||||
assert_eq!(rebuilt["input"][0]["type"], "custom_tool_call");
|
assert_eq!(rebuilt["input"][0]["type"], "custom_tool_call");
|
||||||
|
assert_eq!(rebuilt["input"][0]["id"], "ctc_1");
|
||||||
|
assert_eq!(rebuilt["input"][0]["call_id"], "call_custom_1");
|
||||||
assert_eq!(rebuilt["input"][0]["name"], "shell_command");
|
assert_eq!(rebuilt["input"][0]["name"], "shell_command");
|
||||||
assert_eq!(rebuilt["input"][0]["input"], "ls -la");
|
assert_eq!(rebuilt["input"][0]["input"], "ls -la");
|
||||||
assert_eq!(rebuilt["input"][1]["type"], "custom_tool_call_output");
|
assert_eq!(rebuilt["input"][1]["type"], "custom_tool_call_output");
|
||||||
|
|||||||
Reference in New Issue
Block a user