mirror of
https://github.com/farion1231/cc-switch.git
synced 2026-07-27 16:26:16 +08:00
refactor(proxy): remove Codex Chat Completions API format support
Codex CLI only uses OpenAI Responses API. Remove unused Chat Completions routes, handler, parser config, URL builder branches, frontend API format selector, and stale i18n keys. Claude openai_chat support is preserved.
This commit is contained in:
@@ -42,22 +42,6 @@ fn claude_model_extractor(events: &[Value], request_model: &str) -> String {
|
||||
request_model.to_string()
|
||||
}
|
||||
|
||||
/// OpenAI Chat Completions 流式响应模型提取(优先使用 usage.model)
|
||||
fn openai_model_extractor(events: &[Value], request_model: &str) -> String {
|
||||
// 首先尝试从解析的 usage 中获取模型
|
||||
if let Some(usage) = TokenUsage::from_openai_stream_events(events) {
|
||||
if let Some(model) = usage.model {
|
||||
return model;
|
||||
}
|
||||
}
|
||||
// 回退:从事件中直接提取
|
||||
events
|
||||
.iter()
|
||||
.find_map(|e| e.get("model")?.as_str())
|
||||
.unwrap_or(request_model)
|
||||
.to_string()
|
||||
}
|
||||
|
||||
/// Codex 智能流式响应模型提取(自动检测格式)
|
||||
fn codex_auto_model_extractor(events: &[Value], request_model: &str) -> String {
|
||||
// 首先尝试从解析的 usage 中获取模型
|
||||
@@ -107,14 +91,6 @@ pub const CLAUDE_PARSER_CONFIG: UsageParserConfig = UsageParserConfig {
|
||||
app_type_str: "claude",
|
||||
};
|
||||
|
||||
/// OpenAI Chat Completions API 解析配置(用于 Codex /v1/chat/completions)
|
||||
pub const OPENAI_PARSER_CONFIG: UsageParserConfig = UsageParserConfig {
|
||||
stream_parser: TokenUsage::from_openai_stream_events,
|
||||
response_parser: TokenUsage::from_openai_response,
|
||||
model_extractor: openai_model_extractor,
|
||||
app_type_str: "codex",
|
||||
};
|
||||
|
||||
/// Codex 智能解析配置(自动检测 OpenAI 或 Codex 格式)
|
||||
pub const CODEX_PARSER_CONFIG: UsageParserConfig = UsageParserConfig {
|
||||
stream_parser: TokenUsage::from_codex_stream_events_auto,
|
||||
@@ -160,15 +136,6 @@ pub const CLAUDE_HANDLER_CONFIG: HandlerConfig = HandlerConfig {
|
||||
parser_config: &CLAUDE_PARSER_CONFIG,
|
||||
};
|
||||
|
||||
/// Codex Chat Completions Handler 配置
|
||||
#[allow(dead_code)]
|
||||
pub const CODEX_CHAT_HANDLER_CONFIG: HandlerConfig = HandlerConfig {
|
||||
app_type: AppType::Codex,
|
||||
tag: "Codex",
|
||||
app_type_str: "codex",
|
||||
parser_config: &OPENAI_PARSER_CONFIG,
|
||||
};
|
||||
|
||||
/// Codex Responses Handler 配置
|
||||
#[allow(dead_code)]
|
||||
pub const CODEX_RESPONSES_HANDLER_CONFIG: HandlerConfig = HandlerConfig {
|
||||
|
||||
@@ -9,9 +9,7 @@
|
||||
|
||||
use super::{
|
||||
error_mapper::{get_error_message, map_proxy_error_to_status},
|
||||
handler_config::{
|
||||
CLAUDE_PARSER_CONFIG, CODEX_PARSER_CONFIG, GEMINI_PARSER_CONFIG, OPENAI_PARSER_CONFIG,
|
||||
},
|
||||
handler_config::{CLAUDE_PARSER_CONFIG, CODEX_PARSER_CONFIG, GEMINI_PARSER_CONFIG},
|
||||
handler_context::RequestContext,
|
||||
providers::{get_adapter, streaming::create_anthropic_sse_stream, transform},
|
||||
response_processor::{create_logged_passthrough_stream, process_response, SseUsageCollector},
|
||||
@@ -273,47 +271,6 @@ async fn handle_claude_transform(
|
||||
// Codex API 处理器
|
||||
// ============================================================================
|
||||
|
||||
/// 处理 /v1/chat/completions 请求(OpenAI Chat Completions API - Codex CLI)
|
||||
pub async fn handle_chat_completions(
|
||||
State(state): State<ProxyState>,
|
||||
headers: axum::http::HeaderMap,
|
||||
Json(body): Json<Value>,
|
||||
) -> Result<axum::response::Response, ProxyError> {
|
||||
let mut ctx =
|
||||
RequestContext::new(&state, &body, &headers, AppType::Codex, "Codex", "codex").await?;
|
||||
|
||||
let is_stream = body
|
||||
.get("stream")
|
||||
.and_then(|v| v.as_bool())
|
||||
.unwrap_or(false);
|
||||
|
||||
let forwarder = ctx.create_forwarder(&state);
|
||||
let result = match forwarder
|
||||
.forward_with_retry(
|
||||
&AppType::Codex,
|
||||
"/chat/completions",
|
||||
body,
|
||||
headers,
|
||||
ctx.get_providers(),
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(result) => result,
|
||||
Err(mut err) => {
|
||||
if let Some(provider) = err.provider.take() {
|
||||
ctx.provider = provider;
|
||||
}
|
||||
log_forward_error(&state, &ctx, is_stream, &err.error);
|
||||
return Err(err.error);
|
||||
}
|
||||
};
|
||||
|
||||
ctx.provider = result.provider;
|
||||
let response = result.response;
|
||||
|
||||
process_response(response, &ctx, &state, &OPENAI_PARSER_CONFIG).await
|
||||
}
|
||||
|
||||
/// 处理 /v1/responses 请求(OpenAI Responses API - Codex CLI 透传)
|
||||
pub async fn handle_responses(
|
||||
State(state): State<ProxyState>,
|
||||
|
||||
@@ -144,13 +144,8 @@ impl ProviderAdapter for CodexAdapter {
|
||||
let endpoint_trimmed = endpoint.trim_start_matches('/');
|
||||
|
||||
// 检测 base_url 是否已经以 API 路径结尾(用户填写了完整路径)
|
||||
// 支持的 API 路径模式:/v1/responses, /responses, /v1/chat/completions, /chat/completions
|
||||
let api_path_patterns = [
|
||||
"/v1/responses",
|
||||
"/responses",
|
||||
"/v1/chat/completions",
|
||||
"/chat/completions",
|
||||
];
|
||||
// 仅支持 Responses API 路径模式:/v1/responses, /responses
|
||||
let api_path_patterns = ["/v1/responses", "/responses"];
|
||||
|
||||
let base_ends_with_api_path = api_path_patterns
|
||||
.iter()
|
||||
@@ -293,17 +288,6 @@ mod tests {
|
||||
assert_eq!(url, "https://example.com/v1/responses");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_build_url_full_path_chat_completions() {
|
||||
let adapter = CodexAdapter::new();
|
||||
// base_url 已包含完整路径 /v1/chat/completions,不再追加
|
||||
let url = adapter.build_url(
|
||||
"https://example.com/v1/chat/completions",
|
||||
"/chat/completions",
|
||||
);
|
||||
assert_eq!(url, "https://example.com/v1/chat/completions");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_build_url_full_path_short_suffix() {
|
||||
let adapter = CodexAdapter::new();
|
||||
@@ -319,13 +303,6 @@ mod tests {
|
||||
// 场景:https://integrate.api.nvidia.com/v1 + /v1/responses
|
||||
let url = adapter.build_url("https://integrate.api.nvidia.com/v1", "/v1/responses");
|
||||
assert_eq!(url, "https://integrate.api.nvidia.com/v1/responses");
|
||||
|
||||
// 另一个场景:/v1 + /v1/chat/completions
|
||||
let url2 = adapter.build_url(
|
||||
"https://integrate.api.nvidia.com/v1",
|
||||
"/v1/chat/completions",
|
||||
);
|
||||
assert_eq!(url2, "https://integrate.api.nvidia.com/v1/chat/completions");
|
||||
}
|
||||
|
||||
// 官方客户端检测测试
|
||||
|
||||
@@ -218,20 +218,6 @@ impl ProxyServer {
|
||||
// Claude API (支持带前缀和不带前缀两种格式)
|
||||
.route("/v1/messages", post(handlers::handle_messages))
|
||||
.route("/claude/v1/messages", post(handlers::handle_messages))
|
||||
// OpenAI Chat Completions API (Codex CLI,支持带前缀和不带前缀)
|
||||
.route("/chat/completions", post(handlers::handle_chat_completions))
|
||||
.route(
|
||||
"/v1/chat/completions",
|
||||
post(handlers::handle_chat_completions),
|
||||
)
|
||||
.route(
|
||||
"/v1/v1/chat/completions",
|
||||
post(handlers::handle_chat_completions),
|
||||
)
|
||||
.route(
|
||||
"/codex/v1/chat/completions",
|
||||
post(handlers::handle_chat_completions),
|
||||
)
|
||||
// OpenAI Responses API (Codex CLI,支持带前缀和不带前缀)
|
||||
.route("/responses", post(handlers::handle_responses))
|
||||
.route("/v1/responses", post(handlers::handle_responses))
|
||||
|
||||
@@ -59,32 +59,11 @@ impl ApiPathPatterns {
|
||||
}
|
||||
|
||||
fn for_codex(api_format: Option<&str>) -> Self {
|
||||
// Codex:
|
||||
// - Direct mode (Codex CLI) hard-codes Responses API: `/responses`
|
||||
// - Proxy mode might target different upstream formats (e.g. Chat Completions)
|
||||
let is_chat = matches!(api_format, Some("chat"));
|
||||
if is_chat {
|
||||
Self {
|
||||
direct_endpoint: "/responses",
|
||||
proxy_endpoint: "/v1/chat/completions",
|
||||
full_url_patterns: &[
|
||||
"/v1/responses",
|
||||
"/responses",
|
||||
"/v1/chat/completions",
|
||||
"/chat/completions",
|
||||
],
|
||||
}
|
||||
} else {
|
||||
Self {
|
||||
direct_endpoint: "/responses",
|
||||
proxy_endpoint: "/responses",
|
||||
full_url_patterns: &[
|
||||
"/v1/responses",
|
||||
"/responses",
|
||||
"/v1/chat/completions",
|
||||
"/chat/completions",
|
||||
],
|
||||
}
|
||||
let _ = api_format;
|
||||
Self {
|
||||
direct_endpoint: "/responses",
|
||||
proxy_endpoint: "/responses",
|
||||
full_url_patterns: &["/v1/responses", "/responses"],
|
||||
}
|
||||
}
|
||||
|
||||
@@ -304,17 +283,6 @@ mod tests {
|
||||
assert!(!preview.is_full_url);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_build_url_preview_codex_chat_proxy_endpoint() {
|
||||
let preview = build_url_preview(&AppType::Codex, "https://api.openai.com", Some("chat"));
|
||||
assert_eq!(preview.direct_url, "https://api.openai.com/v1/responses");
|
||||
assert_eq!(
|
||||
preview.proxy_url,
|
||||
"https://api.openai.com/v1/chat/completions"
|
||||
);
|
||||
assert!(!preview.is_full_url);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_build_url_preview_codex_full_url() {
|
||||
// 全链接时:直连/代理均保持原地址(运行时适配器规则)
|
||||
|
||||
@@ -814,7 +814,7 @@ impl ProxyService {
|
||||
///
|
||||
/// 代理服务器的路由已经根据 API 端点自动区分应用类型:
|
||||
/// - `/v1/messages` → Claude
|
||||
/// - `/v1/chat/completions`, `/v1/responses` → Codex
|
||||
/// - `/v1/responses` → Codex
|
||||
/// - `/v1beta/*` → Gemini
|
||||
///
|
||||
/// 因此不需要在 URL 中添加应用前缀。
|
||||
|
||||
Reference in New Issue
Block a user