fix(gateway): OpenAI 图像 finalize 统一返回 b64_json 并对齐 codex CLI 候选流式上游

- finalize 移除 response_format=url 的 data URL 分支,统一输出 b64_json
- image planner decision 依据请求体中的 stream 字段决定 upstream_is_stream
- openai chat sync plan 对 codex+openai:cli 候选强制上游流式
- 扩充 conversion registry 与 standard matrix 的全量 surface 对端测试
This commit is contained in:
fawney19
2026-04-24 19:01:11 +08:00
parent 4618184516
commit d0d71aa51a
6 changed files with 363 additions and 41 deletions

View File

@@ -396,6 +396,25 @@ mod tests {
GatewayProviderTransportProvider, GatewayProviderTransportSnapshot,
};
const STANDARD_SURFACES: &[&str] = &[
"openai:chat",
"openai:cli",
"claude:chat",
"claude:cli",
"gemini:chat",
"gemini:cli",
];
fn expected_request_conversion_kind(provider_api_format: &str) -> RequestConversionKind {
match provider_api_format {
"openai:chat" => RequestConversionKind::ToOpenAIChat,
"openai:cli" => RequestConversionKind::ToOpenAIFamilyCli,
"claude:chat" | "claude:cli" => RequestConversionKind::ToClaudeStandard,
"gemini:chat" | "gemini:cli" => RequestConversionKind::ToGeminiStandard,
other => panic!("unexpected provider api format: {other}"),
}
}
#[test]
fn request_conversion_registry_supports_bidirectional_standard_matrix() {
assert_eq!(
@@ -433,6 +452,27 @@ mod tests {
assert_eq!(request_conversion_kind("claude:chat", "claude:chat"), None);
}
#[test]
fn request_conversion_registry_covers_all_standard_surface_pairs() {
for client_api_format in STANDARD_SURFACES {
for provider_api_format in STANDARD_SURFACES {
let actual = request_conversion_kind(client_api_format, provider_api_format);
if client_api_format == provider_api_format {
assert_eq!(
actual, None,
"{client_api_format} -> {provider_api_format} should be same-format"
);
} else {
assert_eq!(
actual,
Some(expected_request_conversion_kind(provider_api_format)),
"{client_api_format} -> {provider_api_format} should be routable"
);
}
}
}
}
#[test]
fn sync_response_conversion_registry_supports_bidirectional_standard_matrix() {
assert_eq!(
@@ -469,6 +509,57 @@ mod tests {
);
}
#[test]
fn sync_response_conversion_registry_covers_all_standard_surface_pairs() {
for provider_api_format in STANDARD_SURFACES {
for client_api_format in ["openai:chat", "claude:chat", "gemini:chat"] {
let actual =
sync_chat_response_conversion_kind(provider_api_format, client_api_format);
if *provider_api_format == client_api_format {
assert_eq!(
actual, None,
"{provider_api_format} -> {client_api_format} should be same-format"
);
} else {
let expected = match client_api_format {
"openai:chat" => SyncChatResponseConversionKind::ToOpenAIChat,
"claude:chat" => SyncChatResponseConversionKind::ToClaudeChat,
"gemini:chat" => SyncChatResponseConversionKind::ToGeminiChat,
other => panic!("unexpected chat client api format: {other}"),
};
assert_eq!(
actual,
Some(expected),
"{provider_api_format} -> {client_api_format} should finalize to chat"
);
}
}
for client_api_format in ["openai:cli", "claude:cli", "gemini:cli"] {
let actual =
sync_cli_response_conversion_kind(provider_api_format, client_api_format);
if *provider_api_format == client_api_format {
assert_eq!(
actual, None,
"{provider_api_format} -> {client_api_format} should be same-format"
);
} else {
let expected = match client_api_format {
"openai:cli" => SyncCliResponseConversionKind::ToOpenAIFamilyCli,
"claude:cli" => SyncCliResponseConversionKind::ToClaudeCli,
"gemini:cli" => SyncCliResponseConversionKind::ToGeminiCli,
other => panic!("unexpected cli client api format: {other}"),
};
assert_eq!(
actual,
Some(expected),
"{provider_api_format} -> {client_api_format} should finalize to cli"
);
}
}
}
}
#[test]
fn request_candidate_registry_excludes_compact_as_cross_format_target() {
assert_eq!(