mirror of
https://github.com/farion1231/cc-switch.git
synced 2026-07-30 02:14:43 +08:00
fix: 修复 Copilot 作为 Claude 时 OpenAI 模型的 Responses 分流 (#1735)
* fix: route copilot claude openai models to responses * fix(i18n): add copilotProxyHint translation key for all locales The copilotProxyHint message was using inline defaultValue with Chinese text, which would show Chinese to English and Japanese users. Added proper translation keys in zh/en/ja locale files and removed the hardcoded defaultValue fallback. --------- Co-authored-by: Jason <farion1231@gmail.com>
This commit is contained in:
@@ -88,6 +88,7 @@ impl StreamCheckService {
|
||||
provider: &Provider,
|
||||
config: &StreamCheckConfig,
|
||||
auth_override: Option<AuthInfo>,
|
||||
claude_api_format_override: Option<String>,
|
||||
) -> Result<StreamCheckResult, AppError> {
|
||||
// 合并供应商单独配置和全局配置
|
||||
let effective_config = Self::merge_provider_config(provider, config);
|
||||
@@ -95,8 +96,14 @@ impl StreamCheckService {
|
||||
|
||||
for attempt in 0..=effective_config.max_retries {
|
||||
let result =
|
||||
Self::check_once(app_type, provider, &effective_config, auth_override.clone())
|
||||
.await;
|
||||
Self::check_once(
|
||||
app_type,
|
||||
provider,
|
||||
&effective_config,
|
||||
auth_override.clone(),
|
||||
claude_api_format_override.clone(),
|
||||
)
|
||||
.await;
|
||||
|
||||
match &result {
|
||||
Ok(r) if r.success => {
|
||||
@@ -185,6 +192,7 @@ impl StreamCheckService {
|
||||
provider: &Provider,
|
||||
config: &StreamCheckConfig,
|
||||
auth_override: Option<AuthInfo>,
|
||||
claude_api_format_override: Option<String>,
|
||||
) -> Result<StreamCheckResult, AppError> {
|
||||
let start = Instant::now();
|
||||
let adapter = get_adapter(app_type);
|
||||
@@ -215,6 +223,7 @@ impl StreamCheckService {
|
||||
test_prompt,
|
||||
request_timeout,
|
||||
provider,
|
||||
claude_api_format_override.as_deref(),
|
||||
)
|
||||
.await
|
||||
}
|
||||
@@ -303,6 +312,7 @@ impl StreamCheckService {
|
||||
test_prompt: &str,
|
||||
timeout: std::time::Duration,
|
||||
provider: &Provider,
|
||||
claude_api_format_override: Option<&str>,
|
||||
) -> Result<(u16, String), AppError> {
|
||||
let base = base_url.trim_end_matches('/');
|
||||
let is_github_copilot = auth.strategy == AuthStrategy::GitHubCopilot;
|
||||
@@ -320,19 +330,28 @@ impl StreamCheckService {
|
||||
})
|
||||
.unwrap_or("anthropic");
|
||||
|
||||
let effective_api_format = claude_api_format_override.unwrap_or(api_format);
|
||||
|
||||
let is_full_url = provider
|
||||
.meta
|
||||
.as_ref()
|
||||
.and_then(|meta| meta.is_full_url)
|
||||
.unwrap_or(false);
|
||||
let is_openai_chat = is_github_copilot || api_format == "openai_chat";
|
||||
let is_openai_responses = !is_github_copilot && api_format == "openai_responses";
|
||||
let url = Self::resolve_claude_stream_url(base, auth.strategy, api_format, is_full_url);
|
||||
let is_openai_chat = effective_api_format == "openai_chat";
|
||||
let is_openai_responses = effective_api_format == "openai_responses";
|
||||
let url = Self::resolve_claude_stream_url(
|
||||
base,
|
||||
auth.strategy,
|
||||
effective_api_format,
|
||||
is_full_url,
|
||||
);
|
||||
|
||||
let max_tokens = if is_openai_responses { 16 } else { 1 };
|
||||
|
||||
// Build from Anthropic-native shape first, then convert for configured targets.
|
||||
let anthropic_body = json!({
|
||||
"model": model,
|
||||
"max_tokens": 1,
|
||||
"max_tokens": max_tokens,
|
||||
"messages": [{ "role": "user", "content": test_prompt }],
|
||||
"stream": true
|
||||
});
|
||||
@@ -724,7 +743,9 @@ impl StreamCheckService {
|
||||
let base = base_url.trim_end_matches('/');
|
||||
let is_github_copilot = auth_strategy == AuthStrategy::GitHubCopilot;
|
||||
|
||||
if is_github_copilot {
|
||||
if is_github_copilot && api_format == "openai_responses" {
|
||||
format!("{base}/v1/responses")
|
||||
} else if is_github_copilot {
|
||||
format!("{base}/chat/completions")
|
||||
} else if api_format == "openai_responses" {
|
||||
if base.ends_with("/v1") {
|
||||
@@ -758,6 +779,15 @@ impl StreamCheckService {
|
||||
vec![format!("{base}/responses"), format!("{base}/v1/responses")]
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn resolve_effective_test_model(
|
||||
app_type: &AppType,
|
||||
provider: &Provider,
|
||||
config: &StreamCheckConfig,
|
||||
) -> String {
|
||||
let effective_config = Self::merge_provider_config(provider, config);
|
||||
Self::resolve_test_model(app_type, provider, &effective_config)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -878,13 +908,25 @@ mod tests {
|
||||
let url = StreamCheckService::resolve_claude_stream_url(
|
||||
"https://api.githubcopilot.com",
|
||||
AuthStrategy::GitHubCopilot,
|
||||
"anthropic",
|
||||
"openai_chat",
|
||||
false,
|
||||
);
|
||||
|
||||
assert_eq!(url, "https://api.githubcopilot.com/chat/completions");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_resolve_claude_stream_url_for_github_copilot_responses() {
|
||||
let url = StreamCheckService::resolve_claude_stream_url(
|
||||
"https://api.githubcopilot.com",
|
||||
AuthStrategy::GitHubCopilot,
|
||||
"openai_responses",
|
||||
false,
|
||||
);
|
||||
|
||||
assert_eq!(url, "https://api.githubcopilot.com/v1/responses");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_resolve_claude_stream_url_for_openai_chat() {
|
||||
let url = StreamCheckService::resolve_claude_stream_url(
|
||||
|
||||
Reference in New Issue
Block a user