mirror of
https://github.com/aiclientproxy/proxycast.git
synced 2026-09-24 23:10:56 +08:00
feat(codex): 支持 Yunyi 等代理的 API Key 认证和 responses API
## 主要改进 ### 1. 增强 API Key 字段兼容性 - 支持多种字段名:api_key, apiKey, OPENAI_API_KEY - 兼容 Codex CLI 和 Yunyi 等代理的凭证格式 ### 2. 修复健康检查逻辑 - 自定义 base_url 时使用 Codex responses API 格式 - 官方 OpenAI 时使用 chat/completions API - 正确处理 Yunyi 等代理的 500 错误响应 ### 3. 改进错误提示 - 提供更友好的配置错误提示信息 ## 技术细节 - codex.rs: 添加 OPENAI_API_KEY 别名支持 - provider_pool_service.rs: 根据 base_url 选择正确的 API 格式 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.5
parent
fe542ccbf3
commit
4b34a8f57f
@@ -51,7 +51,13 @@ pub struct CodexCredentials {
|
||||
)]
|
||||
pub refresh_token: Option<String>,
|
||||
/// API Key(Codex CLI 支持通过 API Key 登录)
|
||||
#[serde(default, skip_serializing_if = "Option::is_none", alias = "apiKey")]
|
||||
/// 支持字段名: api_key, apiKey, OPENAI_API_KEY
|
||||
#[serde(
|
||||
default,
|
||||
skip_serializing_if = "Option::is_none",
|
||||
alias = "apiKey",
|
||||
alias = "OPENAI_API_KEY"
|
||||
)]
|
||||
pub api_key: Option<String>,
|
||||
/// API Base URL(可选)
|
||||
#[serde(default, skip_serializing_if = "Option::is_none", alias = "apiBaseUrl")]
|
||||
|
||||
@@ -837,6 +837,7 @@ impl ProviderPoolService {
|
||||
}
|
||||
|
||||
// Codex 健康检查
|
||||
// 支持 Yunyi 等代理使用 responses API 格式
|
||||
async fn check_codex_health(&self, creds_path: &str, model: &str) -> Result<(), String> {
|
||||
use crate::providers::codex::CodexProvider;
|
||||
|
||||
@@ -846,13 +847,14 @@ impl ProviderPoolService {
|
||||
.await
|
||||
.map_err(|e| format!("加载 Codex 凭证失败: {}", e))?;
|
||||
|
||||
let token = provider
|
||||
.ensure_valid_token()
|
||||
.await
|
||||
.map_err(|e| format!("获取 Codex Token 失败: {}", e))?;
|
||||
let token = provider.ensure_valid_token().await.map_err(|e| {
|
||||
format!(
|
||||
"获取 Codex Token 失败: 配置错误,请检查凭证设置。详情:{}",
|
||||
e
|
||||
)
|
||||
})?;
|
||||
|
||||
// 使用 OpenAI 兼容 API 进行健康检查
|
||||
// 兼容 Codex CLI API Key 模式:如果 auth.json 提供 api_base_url,则优先使用
|
||||
// 获取 base_url,如果设置了则使用 responses API,否则使用 OpenAI chat/completions
|
||||
let base_url = provider
|
||||
.credentials
|
||||
.api_base_url
|
||||
@@ -860,7 +862,59 @@ impl ProviderPoolService {
|
||||
.map(|s| s.trim())
|
||||
.filter(|s| !s.is_empty());
|
||||
|
||||
self.check_openai_health(&token, base_url, model).await
|
||||
match base_url {
|
||||
Some(base) => {
|
||||
// 使用自定义 base_url (如 Yunyi),使用 responses API 格式
|
||||
let base = base.trim_end_matches('/');
|
||||
let url = if base.ends_with("/v1") {
|
||||
format!("{}/responses", base)
|
||||
} else {
|
||||
format!("{}/v1/responses", base)
|
||||
};
|
||||
|
||||
// Codex/Yunyi 使用 responses API 格式
|
||||
let request_body = serde_json::json!({
|
||||
"model": model,
|
||||
"input": "Say OK",
|
||||
"max_output_tokens": 10
|
||||
});
|
||||
|
||||
tracing::debug!(
|
||||
"[HEALTH_CHECK] Codex responses API URL: {}, model: {}",
|
||||
url,
|
||||
model
|
||||
);
|
||||
|
||||
let response = self
|
||||
.client
|
||||
.post(&url)
|
||||
.bearer_auth(&token)
|
||||
.header("Content-Type", "application/json")
|
||||
.header("Accept", "text/event-stream")
|
||||
.header("Openai-Beta", "responses=experimental")
|
||||
.json(&request_body)
|
||||
.timeout(self.health_check_timeout)
|
||||
.send()
|
||||
.await
|
||||
.map_err(|e| format!("请求失败: {}", e))?;
|
||||
|
||||
if response.status().is_success() {
|
||||
Ok(())
|
||||
} else {
|
||||
let status = response.status();
|
||||
let body = response.text().await.unwrap_or_default();
|
||||
Err(format!(
|
||||
"HTTP {} - {}",
|
||||
status,
|
||||
body.chars().take(200).collect::<String>()
|
||||
))
|
||||
}
|
||||
}
|
||||
None => {
|
||||
// 没有自定义 base_url,使用 OpenAI 官方 chat/completions API
|
||||
self.check_openai_health(&token, None, model).await
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Claude OAuth 健康检查
|
||||
|
||||
Reference in New Issue
Block a user