-
-
Notifications
You must be signed in to change notification settings - Fork 6.5k
feat(oauth): allow upstream base URL overrides #4086
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: dev
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -158,7 +158,7 @@ func (e *ClaudeExecutor) PrepareRequest(req *http.Request, auth *cliproxyauth.Au | |
| if req == nil { | ||
| return nil | ||
| } | ||
| apiKey, _ := claudeCreds(auth) | ||
| apiKey, _ := claudeCredsWithConfig(e.cfg, auth) | ||
| if strings.TrimSpace(apiKey) == "" { | ||
| return nil | ||
| } | ||
|
|
@@ -201,7 +201,7 @@ func (e *ClaudeExecutor) Execute(ctx context.Context, auth *cliproxyauth.Auth, r | |
| } | ||
| baseModel := thinking.ParseSuffix(req.Model).ModelName | ||
|
|
||
| apiKey, baseURL := claudeCreds(auth) | ||
| apiKey, baseURL := claudeCredsWithConfig(e.cfg, auth) | ||
| if baseURL == "" { | ||
| baseURL = "https://api.anthropic.com" | ||
| } | ||
|
|
@@ -393,7 +393,7 @@ func (e *ClaudeExecutor) ExecuteStream(ctx context.Context, auth *cliproxyauth.A | |
| } | ||
| baseModel := thinking.ParseSuffix(req.Model).ModelName | ||
|
|
||
| apiKey, baseURL := claudeCreds(auth) | ||
| apiKey, baseURL := claudeCredsWithConfig(e.cfg, auth) | ||
| if baseURL == "" { | ||
| baseURL = "https://api.anthropic.com" | ||
| } | ||
|
|
@@ -675,7 +675,7 @@ func validateClaudeStreamingResponse(data []byte) error { | |
| func (e *ClaudeExecutor) CountTokens(ctx context.Context, auth *cliproxyauth.Auth, req cliproxyexecutor.Request, opts cliproxyexecutor.Options) (cliproxyexecutor.Response, error) { | ||
| baseModel := thinking.ParseSuffix(req.Model).ModelName | ||
|
|
||
| apiKey, baseURL := claudeCreds(auth) | ||
| apiKey, baseURL := claudeCredsWithConfig(e.cfg, auth) | ||
| if baseURL == "" { | ||
| baseURL = "https://api.anthropic.com" | ||
| } | ||
|
|
@@ -1135,12 +1135,25 @@ func applyClaudeHeaders(r *http.Request, auth *cliproxyauth.Auth, apiKey string, | |
| } | ||
|
|
||
| func claudeCreds(a *cliproxyauth.Auth) (apiKey, baseURL string) { | ||
| return claudeCredsWithConfig(nil, a) | ||
| } | ||
|
|
||
| func claudeCredsWithConfig(cfg *config.Config, a *cliproxyauth.Auth) (apiKey, baseURL string) { | ||
| if a == nil { | ||
| return "", "" | ||
| } | ||
| if a.Attributes != nil { | ||
| apiKey = a.Attributes["api_key"] | ||
| baseURL = a.Attributes["base_url"] | ||
| baseURL = strings.TrimSpace(a.Attributes["base_url"]) | ||
| } | ||
| if baseURL == "" && a.AuthKind() == cliproxyauth.AuthKindOAuth && a.Metadata != nil { | ||
| baseURL = cliproxyauth.BaseURLFromMetadata(a.Metadata) | ||
| } | ||
| if baseURL == "" && a.AuthKind() == cliproxyauth.AuthKindOAuth && cfg != nil { | ||
| baseURL = strings.TrimSpace(cfg.ClaudeBaseURL) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When an OAuth Useful? React with 👍 / 👎.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed in 2357dd5. @chatgpt-codex-connector please review again. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Codex Review: Didn't find any major issues. 🎉 Reviewed commit: ℹ️ About Codex in GitHubCodex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed in 2357dd5. @chatgpt-codex-connector please review again. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Codex Review: Didn't find any major issues. More of your lovely PRs please. Reviewed commit: ℹ️ About Codex in GitHubCodex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback". |
||
| } | ||
| if baseURL != "" { | ||
| baseURL = normalizeClaudeBaseURL(baseURL) | ||
| } | ||
| if apiKey == "" && a.Metadata != nil { | ||
| if v, ok := a.Metadata["access_token"].(string); ok { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -710,7 +710,7 @@ func (e *CodexExecutor) PrepareRequest(req *http.Request, auth *cliproxyauth.Aut | |
| if req == nil { | ||
| return nil | ||
| } | ||
| apiKey, _ := codexCreds(auth) | ||
| apiKey, _ := codexCredsWithConfig(e.cfg, auth) | ||
| if strings.TrimSpace(apiKey) != "" { | ||
| req.Header.Set("Authorization", "Bearer "+apiKey) | ||
| } | ||
|
|
@@ -747,7 +747,7 @@ func (e *CodexExecutor) Execute(ctx context.Context, auth *cliproxyauth.Auth, re | |
| } | ||
| baseModel := thinking.ParseSuffix(req.Model).ModelName | ||
|
|
||
| apiKey, baseURL := codexCreds(auth) | ||
| apiKey, baseURL := codexCredsWithConfig(e.cfg, auth) | ||
| if baseURL == "" { | ||
| baseURL = "https://chatgpt.com/backend-api/codex" | ||
| } | ||
|
|
@@ -927,7 +927,7 @@ func (e *CodexExecutor) Execute(ctx context.Context, auth *cliproxyauth.Auth, re | |
| func (e *CodexExecutor) executeCompact(ctx context.Context, auth *cliproxyauth.Auth, req cliproxyexecutor.Request, opts cliproxyexecutor.Options) (resp cliproxyexecutor.Response, err error) { | ||
| baseModel := thinking.ParseSuffix(req.Model).ModelName | ||
|
|
||
| apiKey, baseURL := codexCreds(auth) | ||
| apiKey, baseURL := codexCredsWithConfig(e.cfg, auth) | ||
| if baseURL == "" { | ||
| baseURL = "https://chatgpt.com/backend-api/codex" | ||
| } | ||
|
|
@@ -1034,7 +1034,7 @@ func (e *CodexExecutor) ExecuteStream(ctx context.Context, auth *cliproxyauth.Au | |
| } | ||
| baseModel := thinking.ParseSuffix(req.Model).ModelName | ||
|
|
||
| apiKey, baseURL := codexCreds(auth) | ||
| apiKey, baseURL := codexCredsWithConfig(e.cfg, auth) | ||
| if baseURL == "" { | ||
| baseURL = "https://chatgpt.com/backend-api/codex" | ||
| } | ||
|
|
@@ -1853,12 +1853,22 @@ func parseCodexRetryAfter(statusCode int, errorBody []byte, now time.Time) *time | |
| } | ||
|
|
||
| func codexCreds(a *cliproxyauth.Auth) (apiKey, baseURL string) { | ||
| return codexCredsWithConfig(nil, a) | ||
| } | ||
|
Comment on lines
1855
to
+1857
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
This legacy helper is still used by Codex paths that were not updated, so they cannot see Useful? React with 👍 / 👎.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed in 0f0f640. @chatgpt-codex-connector please review again.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @codex revicew |
||
|
|
||
| func codexCredsWithConfig(cfg *config.Config, a *cliproxyauth.Auth) (apiKey, baseURL string) { | ||
| if a == nil { | ||
| return "", "" | ||
| } | ||
| if a.Attributes != nil { | ||
| apiKey = a.Attributes["api_key"] | ||
| baseURL = a.Attributes["base_url"] | ||
| baseURL = strings.TrimSpace(a.Attributes["base_url"]) | ||
| } | ||
| if baseURL == "" && a.AuthKind() == cliproxyauth.AuthKindOAuth && a.Metadata != nil { | ||
| baseURL = cliproxyauth.BaseURLFromMetadata(a.Metadata) | ||
| } | ||
| if baseURL == "" && a.AuthKind() == cliproxyauth.AuthKindOAuth && cfg != nil { | ||
| baseURL = strings.TrimSpace(cfg.CodexBaseURL) | ||
| } | ||
| if apiKey == "" && a.Metadata != nil { | ||
| if v, ok := a.Metadata["access_token"].(string); ok { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,48 @@ | ||
| package executor | ||
|
|
||
| import ( | ||
| "testing" | ||
|
|
||
| "github.com/router-for-me/CLIProxyAPI/v7/internal/config" | ||
| cliproxyauth "github.com/router-for-me/CLIProxyAPI/v7/sdk/cliproxy/auth" | ||
| ) | ||
|
|
||
| func TestCodexCredsUsesGlobalBaseURLWhenAuthHasNoBaseURL(t *testing.T) { | ||
| apiKey, baseURL := codexCredsWithConfig(&config.Config{CodexBaseURL: "http://127.0.0.1:18082"}, &cliproxyauth.Auth{ | ||
| Metadata: map[string]any{"access_token": "oauth-token"}, | ||
| }) | ||
| if apiKey != "oauth-token" { | ||
| t.Fatalf("apiKey = %q, want oauth-token", apiKey) | ||
| } | ||
| if baseURL != "http://127.0.0.1:18082" { | ||
| t.Fatalf("baseURL = %q, want global override", baseURL) | ||
| } | ||
| } | ||
|
|
||
| func TestCodexCredsAuthBaseURLPrecedesGlobalBaseURL(t *testing.T) { | ||
| _, baseURL := codexCredsWithConfig(&config.Config{CodexBaseURL: "http://127.0.0.1:18082"}, &cliproxyauth.Auth{ | ||
| Attributes: map[string]string{"base_url": "http://127.0.0.1:18102"}, | ||
| Metadata: map[string]any{"access_token": "oauth-token"}, | ||
| }) | ||
| if baseURL != "http://127.0.0.1:18102" { | ||
| t.Fatalf("baseURL = %q, want auth override", baseURL) | ||
| } | ||
| } | ||
|
|
||
| func TestCodexCredsMetadataBaseURLPrecedesGlobalBaseURL(t *testing.T) { | ||
| _, baseURL := codexCredsWithConfig(&config.Config{CodexBaseURL: "http://127.0.0.1:18082"}, &cliproxyauth.Auth{ | ||
| Metadata: map[string]any{"access_token": "oauth-token", "base_url": "http://127.0.0.1:18104"}, | ||
| }) | ||
| if baseURL != "http://127.0.0.1:18104" { | ||
| t.Fatalf("baseURL = %q, want metadata override", baseURL) | ||
| } | ||
| } | ||
|
|
||
| func TestCodexCredsDoesNotApplyGlobalBaseURLToAPIKeyAuth(t *testing.T) { | ||
| _, baseURL := codexCredsWithConfig(&config.Config{CodexBaseURL: "http://127.0.0.1:18082"}, &cliproxyauth.Auth{ | ||
| Attributes: map[string]string{"api_key": "sk-codex-test"}, | ||
| }) | ||
| if baseURL != "" { | ||
| t.Fatalf("baseURL = %q, want empty for API-key auth without per-key base_url", baseURL) | ||
| } | ||
| } |
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.
When a newly-supported OAuth override is configured with a trailing slash, e.g.
claude-base-url: http://proxy/or an auth-filebase_url, this returns that slash unchanged; the Claude request builders later concatenatefmt.Sprintf("%s/v1/messages?beta=true", baseURL)and.../count_tokens, producing//v1/.... Gateways with exact routing on/v1/messageswill 404, while the Codex paths already usestrings.TrimSuffix, so normalize the Claude override before returning it.Useful? React with 👍 / 👎.
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.
@codex review