mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-02 01:10:23 +08:00
Merge remote-tracking branch 'origin/pr/516'
This commit is contained in:
@@ -44,16 +44,27 @@ fn is_openai_image_request(provider_api_format: &str) -> bool {
|
|||||||
.eq_ignore_ascii_case("openai:image")
|
.eq_ignore_ascii_case("openai:image")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn codex_openai_responses_body_uses_image_generation_tool(
|
/// Returns true only when `tool_choice` *explicitly* targets image_generation,
|
||||||
|
/// matching either the `"image_generation"` string form or the
|
||||||
|
/// `{"type":"image_generation"}` object form.
|
||||||
|
///
|
||||||
|
/// Intentionally does NOT inspect the `tools` array: tools merely advertise
|
||||||
|
/// what is available, while only `tool_choice` expresses the caller's
|
||||||
|
/// selection. Treating "image_generation present in tools" as a trigger
|
||||||
|
/// caused codex CLI requests (which list image_generation alongside ~20
|
||||||
|
/// other tools under `tool_choice: "auto"`) to be incorrectly rewritten
|
||||||
|
/// into image-generation-only requests, leading to upstream 400s.
|
||||||
|
fn codex_openai_responses_tool_choice_references_image_generation(
|
||||||
body_object: &serde_json::Map<String, Value>,
|
body_object: &serde_json::Map<String, Value>,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
body_object
|
match body_object.get("tool_choice") {
|
||||||
.get("tools")
|
Some(Value::String(name)) => name.trim().eq_ignore_ascii_case("image_generation"),
|
||||||
.and_then(Value::as_array)
|
Some(Value::Object(choice)) => choice
|
||||||
.into_iter()
|
.get("type")
|
||||||
.flatten()
|
.and_then(Value::as_str)
|
||||||
.filter_map(Value::as_object)
|
.is_some_and(|kind| kind.trim().eq_ignore_ascii_case("image_generation")),
|
||||||
.any(|tool| tool.get("type").and_then(Value::as_str) == Some("image_generation"))
|
_ => false,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_codex_openai_image_tool_overrides(body_object: &mut serde_json::Map<String, Value>) {
|
fn apply_codex_openai_image_tool_overrides(body_object: &mut serde_json::Map<String, Value>) {
|
||||||
@@ -410,7 +421,7 @@ pub fn apply_codex_openai_responses_special_body_edits(
|
|||||||
body_object.insert("instructions".to_string(), json!(""));
|
body_object.insert("instructions".to_string(), json!(""));
|
||||||
}
|
}
|
||||||
if is_openai_image_request(provider_api_format)
|
if is_openai_image_request(provider_api_format)
|
||||||
|| codex_openai_responses_body_uses_image_generation_tool(body_object)
|
|| codex_openai_responses_tool_choice_references_image_generation(body_object)
|
||||||
{
|
{
|
||||||
body_object.insert(
|
body_object.insert(
|
||||||
"model".to_string(),
|
"model".to_string(),
|
||||||
@@ -733,7 +744,8 @@ mod tests {
|
|||||||
"input": "generate image",
|
"input": "generate image",
|
||||||
"tools": [{
|
"tools": [{
|
||||||
"type": "image_generation"
|
"type": "image_generation"
|
||||||
}]
|
}],
|
||||||
|
"tool_choice": {"type": "image_generation"}
|
||||||
});
|
});
|
||||||
|
|
||||||
apply_codex_openai_responses_special_body_edits(
|
apply_codex_openai_responses_special_body_edits(
|
||||||
@@ -759,6 +771,131 @@ mod tests {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn codex_responses_image_tool_edits_triggered_by_string_tool_choice() {
|
||||||
|
let mut provider_request_body = json!({
|
||||||
|
"model": "gpt-image-2",
|
||||||
|
"input": "generate image",
|
||||||
|
"tools": [{"type": "image_generation"}],
|
||||||
|
"tool_choice": "image_generation"
|
||||||
|
});
|
||||||
|
|
||||||
|
apply_codex_openai_responses_special_body_edits(
|
||||||
|
&mut provider_request_body,
|
||||||
|
"codex",
|
||||||
|
"openai:responses",
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
provider_request_body["model"],
|
||||||
|
json!(CODEX_OPENAI_IMAGE_INTERNAL_MODEL)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
provider_request_body["tool_choice"]["type"],
|
||||||
|
json!("image_generation")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn codex_responses_image_tool_edits_skipped_when_tool_choice_is_auto() {
|
||||||
|
let original_model = "gpt-5.5";
|
||||||
|
let mut provider_request_body = json!({
|
||||||
|
"model": original_model,
|
||||||
|
"input": [{"role": "user", "content": "hi"}],
|
||||||
|
"tools": [
|
||||||
|
{"type": "function", "name": "shell"},
|
||||||
|
{"type": "image_generation"},
|
||||||
|
{"type": "web_search"}
|
||||||
|
],
|
||||||
|
"tool_choice": "auto"
|
||||||
|
});
|
||||||
|
|
||||||
|
apply_codex_openai_responses_special_body_edits(
|
||||||
|
&mut provider_request_body,
|
||||||
|
"codex",
|
||||||
|
"openai:responses",
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(provider_request_body["model"], json!(original_model));
|
||||||
|
assert_eq!(provider_request_body["tool_choice"], json!("auto"));
|
||||||
|
assert_eq!(
|
||||||
|
provider_request_body["tools"]
|
||||||
|
.as_array()
|
||||||
|
.map(Vec::len)
|
||||||
|
.unwrap_or_default(),
|
||||||
|
3,
|
||||||
|
"tools array should be preserved when tool_choice is auto"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn codex_responses_image_tool_edits_skipped_when_tool_choice_absent() {
|
||||||
|
let original_model = "gpt-5.5";
|
||||||
|
let mut provider_request_body = json!({
|
||||||
|
"model": original_model,
|
||||||
|
"input": [{"role": "user", "content": "hi"}],
|
||||||
|
"tools": [{"type": "image_generation"}]
|
||||||
|
});
|
||||||
|
|
||||||
|
apply_codex_openai_responses_special_body_edits(
|
||||||
|
&mut provider_request_body,
|
||||||
|
"codex",
|
||||||
|
"openai:responses",
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(provider_request_body["model"], json!(original_model));
|
||||||
|
assert!(
|
||||||
|
provider_request_body.get("tool_choice").is_none(),
|
||||||
|
"tool_choice should not be injected when caller did not set it"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
provider_request_body["tools"][0]["type"],
|
||||||
|
json!("image_generation")
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
provider_request_body["tools"]
|
||||||
|
.as_array()
|
||||||
|
.map(Vec::len)
|
||||||
|
.unwrap_or_default(),
|
||||||
|
1,
|
||||||
|
"tools array should be preserved verbatim when tool_choice is absent"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn codex_responses_image_tool_edits_skipped_when_tool_choice_targets_other_tool() {
|
||||||
|
let original_model = "gpt-5.5";
|
||||||
|
let mut provider_request_body = json!({
|
||||||
|
"model": original_model,
|
||||||
|
"input": [{"role": "user", "content": "hi"}],
|
||||||
|
"tools": [
|
||||||
|
{"type": "function", "name": "shell"},
|
||||||
|
{"type": "image_generation"}
|
||||||
|
],
|
||||||
|
"tool_choice": {"type": "function", "name": "shell"}
|
||||||
|
});
|
||||||
|
|
||||||
|
apply_codex_openai_responses_special_body_edits(
|
||||||
|
&mut provider_request_body,
|
||||||
|
"codex",
|
||||||
|
"openai:responses",
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(provider_request_body["model"], json!(original_model));
|
||||||
|
assert_eq!(
|
||||||
|
provider_request_body["tool_choice"],
|
||||||
|
json!({"type": "function", "name": "shell"})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn codex_image_body_edits_preserve_edit_action_without_generate_defaults() {
|
fn codex_image_body_edits_preserve_edit_action_without_generate_defaults() {
|
||||||
let mut provider_request_body = json!({
|
let mut provider_request_body = json!({
|
||||||
|
|||||||
Reference in New Issue
Block a user