From 7ddf7ffd668f56b7b7d86d66a3ad8098bd2a7640 Mon Sep 17 00:00:00 2001 From: xpfo <47739366+xpfo-go@users.noreply.github.com> Date: Thu, 23 Apr 2026 08:54:31 +0800 Subject: [PATCH] fix(proxy): handle Codex OAuth responses as streaming (#2235) --- src-tauri/src/proxy/handlers.rs | 58 ++++++++++++++++++++++++++++++++- 1 file changed, 57 insertions(+), 1 deletion(-) diff --git a/src-tauri/src/proxy/handlers.rs b/src-tauri/src/proxy/handlers.rs index 086f944e6..39c7243bc 100644 --- a/src-tauri/src/proxy/handlers.rs +++ b/src-tauri/src/proxy/handlers.rs @@ -151,10 +151,20 @@ async fn handle_claude_transform( api_format: &str, ) -> Result { let status = response.status(); + let use_streaming = should_use_claude_transform_streaming( + is_stream, + response.is_sse(), + api_format, + ctx.provider + .meta + .as_ref() + .and_then(|meta| meta.provider_type.as_deref()) + == Some("codex_oauth"), + ); let tool_schema_hints = transform_gemini::extract_anthropic_tool_schema_hints(original_body); let tool_schema_hints = (!tool_schema_hints.is_empty()).then_some(tool_schema_hints); - if is_stream { + if use_streaming { // 根据 api_format 选择流式转换器 let stream = response.bytes_stream(); let sse_stream: Box< @@ -561,6 +571,17 @@ pub async fn handle_gemini( process_response(response, &ctx, &state, &GEMINI_PARSER_CONFIG).await } +fn should_use_claude_transform_streaming( + requested_streaming: bool, + upstream_is_sse: bool, + api_format: &str, + is_codex_oauth: bool, +) -> bool { + requested_streaming + || upstream_is_sse + || (is_codex_oauth && api_format == "openai_responses") +} + // ============================================================================ // 使用量记录(保留用于 Claude 转换逻辑) // ============================================================================ @@ -641,3 +662,38 @@ async fn log_usage( log::warn!("[USG-001] 记录使用量失败: {e}"); } } + +#[cfg(test)] +mod tests { + use super::should_use_claude_transform_streaming; + + #[test] + fn codex_oauth_responses_force_streaming_even_if_client_sent_false() { + assert!(should_use_claude_transform_streaming( + false, + false, + "openai_responses", + true, + )); + } + + #[test] + fn upstream_sse_response_always_uses_streaming_path() { + assert!(should_use_claude_transform_streaming( + false, + true, + "openai_chat", + false, + )); + } + + #[test] + fn non_streaming_response_stays_non_streaming_for_regular_openai_responses() { + assert!(!should_use_claude_transform_streaming( + false, + false, + "openai_responses", + false, + )); + } +}