-
-
Notifications
You must be signed in to change notification settings - Fork 3k
修复 GitHub Copilot 认证与登录流程问题 #1894
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 9 commits
f58ac9a
0cecc7f
b9f1b86
6db4abe
a9e3f47
09d0ccd
85d25fb
bac42e5
1d92ae7
a273b27
1ba6f0d
bc89116
5923958
486c5d3
10057eb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change | ||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -37,6 +37,20 @@ pub struct ForwardError { | |||||||||||||||||
| pub provider: Option<Provider>, | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| pub struct ForwardRequestInput { | ||||||||||||||||||
| pub endpoint: String, | ||||||||||||||||||
| pub body: Value, | ||||||||||||||||||
| pub headers: axum::http::HeaderMap, | ||||||||||||||||||
| pub extensions: Extensions, | ||||||||||||||||||
| pub client_session_id: Option<String>, | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| struct ForwardMetadata<'a> { | ||||||||||||||||||
| headers: &'a axum::http::HeaderMap, | ||||||||||||||||||
| extensions: &'a Extensions, | ||||||||||||||||||
| client_session_id: Option<&'a str>, | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| pub struct RequestForwarder { | ||||||||||||||||||
| /// 共享的 ProviderRouter(持有熔断器状态) | ||||||||||||||||||
| router: Arc<ProviderRouter>, | ||||||||||||||||||
|
|
@@ -99,12 +113,16 @@ impl RequestForwarder { | |||||||||||||||||
| pub async fn forward_with_retry( | ||||||||||||||||||
| &self, | ||||||||||||||||||
| app_type: &AppType, | ||||||||||||||||||
| endpoint: &str, | ||||||||||||||||||
| body: Value, | ||||||||||||||||||
| headers: axum::http::HeaderMap, | ||||||||||||||||||
| extensions: Extensions, | ||||||||||||||||||
| request: ForwardRequestInput, | ||||||||||||||||||
| providers: Vec<Provider>, | ||||||||||||||||||
| ) -> Result<ForwardResult, ForwardError> { | ||||||||||||||||||
| let ForwardRequestInput { | ||||||||||||||||||
| endpoint, | ||||||||||||||||||
| body, | ||||||||||||||||||
| headers, | ||||||||||||||||||
| extensions, | ||||||||||||||||||
| client_session_id, | ||||||||||||||||||
| } = request; | ||||||||||||||||||
| // 获取适配器 | ||||||||||||||||||
| let adapter = get_adapter(app_type); | ||||||||||||||||||
| let app_type_str = app_type.as_str(); | ||||||||||||||||||
|
|
@@ -176,10 +194,13 @@ impl RequestForwarder { | |||||||||||||||||
| match self | ||||||||||||||||||
| .forward( | ||||||||||||||||||
| provider, | ||||||||||||||||||
| endpoint, | ||||||||||||||||||
| &endpoint, | ||||||||||||||||||
| &provider_body, | ||||||||||||||||||
| &headers, | ||||||||||||||||||
| &extensions, | ||||||||||||||||||
| ForwardMetadata { | ||||||||||||||||||
| headers: &headers, | ||||||||||||||||||
| extensions: &extensions, | ||||||||||||||||||
| client_session_id: client_session_id.as_deref(), | ||||||||||||||||||
| }, | ||||||||||||||||||
| adapter.as_ref(), | ||||||||||||||||||
| ) | ||||||||||||||||||
| .await | ||||||||||||||||||
|
|
@@ -306,10 +327,13 @@ impl RequestForwarder { | |||||||||||||||||
| match self | ||||||||||||||||||
| .forward( | ||||||||||||||||||
| provider, | ||||||||||||||||||
| endpoint, | ||||||||||||||||||
| &endpoint, | ||||||||||||||||||
| &provider_body, | ||||||||||||||||||
| &headers, | ||||||||||||||||||
| &extensions, | ||||||||||||||||||
| ForwardMetadata { | ||||||||||||||||||
| headers: &headers, | ||||||||||||||||||
| extensions: &extensions, | ||||||||||||||||||
| client_session_id: client_session_id.as_deref(), | ||||||||||||||||||
| }, | ||||||||||||||||||
| adapter.as_ref(), | ||||||||||||||||||
| ) | ||||||||||||||||||
| .await | ||||||||||||||||||
|
|
@@ -505,10 +529,13 @@ impl RequestForwarder { | |||||||||||||||||
| match self | ||||||||||||||||||
| .forward( | ||||||||||||||||||
| provider, | ||||||||||||||||||
| endpoint, | ||||||||||||||||||
| &endpoint, | ||||||||||||||||||
| &provider_body, | ||||||||||||||||||
| &headers, | ||||||||||||||||||
| &extensions, | ||||||||||||||||||
| ForwardMetadata { | ||||||||||||||||||
| headers: &headers, | ||||||||||||||||||
| extensions: &extensions, | ||||||||||||||||||
| client_session_id: client_session_id.as_deref(), | ||||||||||||||||||
| }, | ||||||||||||||||||
| adapter.as_ref(), | ||||||||||||||||||
| ) | ||||||||||||||||||
| .await | ||||||||||||||||||
|
|
@@ -746,14 +773,16 @@ impl RequestForwarder { | |||||||||||||||||
| provider: &Provider, | ||||||||||||||||||
| endpoint: &str, | ||||||||||||||||||
| body: &Value, | ||||||||||||||||||
| headers: &axum::http::HeaderMap, | ||||||||||||||||||
| extensions: &Extensions, | ||||||||||||||||||
| metadata: ForwardMetadata<'_>, | ||||||||||||||||||
| adapter: &dyn ProviderAdapter, | ||||||||||||||||||
| ) -> Result<(ProxyResponse, Option<String>), ProxyError> { | ||||||||||||||||||
| let headers = metadata.headers; | ||||||||||||||||||
| let extensions = metadata.extensions; | ||||||||||||||||||
| let client_session_id = metadata.client_session_id; | ||||||||||||||||||
| // 使用适配器提取 base_url | ||||||||||||||||||
| let mut base_url = adapter.extract_base_url(provider)?; | ||||||||||||||||||
|
|
||||||||||||||||||
| let is_full_url = provider | ||||||||||||||||||
| let has_explicit_full_url = provider | ||||||||||||||||||
| .meta | ||||||||||||||||||
| .as_ref() | ||||||||||||||||||
| .and_then(|meta| meta.is_full_url) | ||||||||||||||||||
|
|
@@ -830,7 +859,35 @@ impl RequestForwarder { | |||||||||||||||||
| } else { | ||||||||||||||||||
| None | ||||||||||||||||||
| }; | ||||||||||||||||||
| let resolved_claude_api_format = if adapter.name() == "Claude" { | ||||||||||||||||||
| Some( | ||||||||||||||||||
| self.resolve_claude_api_format(provider, &mapped_body, is_copilot) | ||||||||||||||||||
| .await, | ||||||||||||||||||
| ) | ||||||||||||||||||
| } else { | ||||||||||||||||||
| None | ||||||||||||||||||
| }; | ||||||||||||||||||
| let needs_transform = match resolved_claude_api_format.as_deref() { | ||||||||||||||||||
| Some(api_format) => super::providers::claude_api_format_needs_transform(api_format), | ||||||||||||||||||
| None => adapter.needs_transform(provider), | ||||||||||||||||||
| }; | ||||||||||||||||||
| let (effective_endpoint, passthrough_query) = | ||||||||||||||||||
| if needs_transform && adapter.name() == "Claude" { | ||||||||||||||||||
| let api_format = resolved_claude_api_format | ||||||||||||||||||
| .as_deref() | ||||||||||||||||||
| .unwrap_or_else(|| super::providers::get_claude_api_format(provider)); | ||||||||||||||||||
| rewrite_claude_transform_endpoint(endpoint, api_format, is_copilot) | ||||||||||||||||||
| } else { | ||||||||||||||||||
| ( | ||||||||||||||||||
| endpoint.to_string(), | ||||||||||||||||||
| split_endpoint_and_query(endpoint) | ||||||||||||||||||
| .1 | ||||||||||||||||||
| .map(ToString::to_string), | ||||||||||||||||||
| ) | ||||||||||||||||||
| }; | ||||||||||||||||||
|
|
||||||||||||||||||
| let is_full_url = has_explicit_full_url | ||||||||||||||||||
| || is_legacy_full_url_for_endpoint(&base_url, &effective_endpoint); | ||||||||||||||||||
| // GitHub Copilot 动态 endpoint 路由 | ||||||||||||||||||
| // 从 CopilotAuthManager 获取缓存的 API endpoint(支持企业版等非默认 endpoint) | ||||||||||||||||||
| if is_copilot && !is_full_url { | ||||||||||||||||||
|
|
@@ -860,32 +917,6 @@ impl RequestForwarder { | |||||||||||||||||
| } | ||||||||||||||||||
| } | ||||||||||||||||||
| } | ||||||||||||||||||
| let resolved_claude_api_format = if adapter.name() == "Claude" { | ||||||||||||||||||
| Some( | ||||||||||||||||||
| self.resolve_claude_api_format(provider, &mapped_body, is_copilot) | ||||||||||||||||||
| .await, | ||||||||||||||||||
| ) | ||||||||||||||||||
| } else { | ||||||||||||||||||
| None | ||||||||||||||||||
| }; | ||||||||||||||||||
| let needs_transform = match resolved_claude_api_format.as_deref() { | ||||||||||||||||||
| Some(api_format) => super::providers::claude_api_format_needs_transform(api_format), | ||||||||||||||||||
| None => adapter.needs_transform(provider), | ||||||||||||||||||
| }; | ||||||||||||||||||
| let (effective_endpoint, passthrough_query) = | ||||||||||||||||||
| if needs_transform && adapter.name() == "Claude" { | ||||||||||||||||||
| let api_format = resolved_claude_api_format | ||||||||||||||||||
| .as_deref() | ||||||||||||||||||
| .unwrap_or_else(|| super::providers::get_claude_api_format(provider)); | ||||||||||||||||||
| rewrite_claude_transform_endpoint(endpoint, api_format, is_copilot) | ||||||||||||||||||
| } else { | ||||||||||||||||||
| ( | ||||||||||||||||||
| endpoint.to_string(), | ||||||||||||||||||
| split_endpoint_and_query(endpoint) | ||||||||||||||||||
| .1 | ||||||||||||||||||
| .map(ToString::to_string), | ||||||||||||||||||
| ) | ||||||||||||||||||
| }; | ||||||||||||||||||
|
|
||||||||||||||||||
| let url = if is_full_url { | ||||||||||||||||||
| append_query_to_full_url(&base_url, passthrough_query.as_deref()) | ||||||||||||||||||
|
|
@@ -946,10 +977,27 @@ impl RequestForwarder { | |||||||||||||||||
|
|
||||||||||||||||||
| match token_result { | ||||||||||||||||||
| Ok(token) => { | ||||||||||||||||||
| auth = AuthInfo::new(token, AuthStrategy::GitHubCopilot); | ||||||||||||||||||
| // 获取 machine ID 和 session ID | ||||||||||||||||||
| let (machine_id, session_id) = copilot_auth | ||||||||||||||||||
| .get_account_ids_for_conversation( | ||||||||||||||||||
| account_id.as_deref(), | ||||||||||||||||||
| client_session_id, | ||||||||||||||||||
| ) | ||||||||||||||||||
| .await; | ||||||||||||||||||
|
|
||||||||||||||||||
| // 使用新的构造函数 | ||||||||||||||||||
| auth = AuthInfo::new_with_ids( | ||||||||||||||||||
| token, | ||||||||||||||||||
| AuthStrategy::GitHubCopilot, | ||||||||||||||||||
| machine_id.clone(), | ||||||||||||||||||
| session_id.clone(), | ||||||||||||||||||
| ); | ||||||||||||||||||
|
|
||||||||||||||||||
| log::debug!( | ||||||||||||||||||
| "[Copilot] 成功获取 Copilot token (account={})", | ||||||||||||||||||
| account_id.as_deref().unwrap_or("default") | ||||||||||||||||||
| "[Copilot] 成功获取 Copilot token (account={}, machine_id={}, session_id={})", | ||||||||||||||||||
| account_id.as_deref().unwrap_or("default"), | ||||||||||||||||||
| machine_id.as_ref().map(|s| &s[..16]).unwrap_or("none"), | ||||||||||||||||||
| session_id.as_ref().map(|s| &s[..16]).unwrap_or("none"), | ||||||||||||||||||
|
||||||||||||||||||
| "[Copilot] 成功获取 Copilot token (account={}, machine_id={}, session_id={})", | |
| account_id.as_deref().unwrap_or("default"), | |
| machine_id.as_ref().map(|s| &s[..16]).unwrap_or("none"), | |
| session_id.as_ref().map(|s| &s[..16]).unwrap_or("none"), | |
| "[Copilot] 成功获取 Copilot token (account={}, has_machine_id={}, has_session_id={})", | |
| account_id.as_deref().unwrap_or("default"), | |
| machine_id.is_some(), | |
| session_id.is_some(), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
新增了
rand依赖,但当前仓库内未找到rand::的引用(PR 中的 jitter 也已改用SystemTime生成)。如果没有其它未展示的用途,建议移除该依赖,避免增加构建体积和依赖面。