diff --git a/.gitignore b/.gitignore index 90ff3a94..80f4b2eb 100644 --- a/.gitignore +++ b/.gitignore @@ -49,3 +49,4 @@ _bmad-output/* # macOS .DS_Store ._* +.gocache/ diff --git a/internal/api/handlers/management/auth_files.go b/internal/api/handlers/management/auth_files.go index 2e1f02bf..a7916e79 100644 --- a/internal/api/handlers/management/auth_files.go +++ b/internal/api/handlers/management/auth_files.go @@ -2566,20 +2566,9 @@ func performGeminiCLISetup(ctx context.Context, httpClient *http.Client, storage finalProjectID := projectID if responseProjectID != "" { if explicitProject && !strings.EqualFold(responseProjectID, projectID) { - // Check if this is a free user (gen-lang-client projects or free/legacy tier) - isFreeUser := strings.HasPrefix(projectID, "gen-lang-client-") || - strings.EqualFold(tierID, "FREE") || - strings.EqualFold(tierID, "LEGACY") - - if isFreeUser { - // For free users, use backend project ID for preview model access - log.Infof("Gemini onboarding: frontend project %s maps to backend project %s", projectID, responseProjectID) - log.Infof("Using backend project ID: %s (recommended for preview model access)", responseProjectID) - finalProjectID = responseProjectID - } else { - // Pro users: keep requested project ID (original behavior) - log.Warnf("Gemini onboarding returned project %s instead of requested %s; keeping requested project ID.", responseProjectID, projectID) - } + log.Infof("Gemini onboarding: requested project %s maps to backend project %s", projectID, responseProjectID) + log.Infof("Using backend project ID: %s", responseProjectID) + finalProjectID = responseProjectID } else { finalProjectID = responseProjectID } diff --git a/internal/cmd/login.go b/internal/cmd/login.go index 16af718e..298e9546 100644 --- a/internal/cmd/login.go +++ b/internal/cmd/login.go @@ -333,39 +333,9 @@ func performGeminiCLISetup(ctx context.Context, httpClient *http.Client, storage finalProjectID := projectID if responseProjectID != "" { if explicitProject && !strings.EqualFold(responseProjectID, projectID) { - // Check if this is a free user (gen-lang-client projects or free/legacy tier) - isFreeUser := strings.HasPrefix(projectID, "gen-lang-client-") || - strings.EqualFold(tierID, "FREE") || - strings.EqualFold(tierID, "LEGACY") - - if isFreeUser { - // Interactive prompt for free users - fmt.Printf("\nGoogle returned a different project ID:\n") - fmt.Printf(" Requested (frontend): %s\n", projectID) - fmt.Printf(" Returned (backend): %s\n\n", responseProjectID) - fmt.Printf(" Backend project IDs have access to preview models (gemini-3-*).\n") - fmt.Printf(" This is normal for free tier users.\n\n") - fmt.Printf("Which project ID would you like to use?\n") - fmt.Printf(" [1] Backend (recommended): %s\n", responseProjectID) - fmt.Printf(" [2] Frontend: %s\n\n", projectID) - fmt.Printf("Enter choice [1]: ") - - reader := bufio.NewReader(os.Stdin) - choice, _ := reader.ReadString('\n') - choice = strings.TrimSpace(choice) - - if choice == "2" { - log.Infof("Using frontend project ID: %s", projectID) - fmt.Println(". Warning: Frontend project IDs may not have access to preview models.") - finalProjectID = projectID - } else { - log.Infof("Using backend project ID: %s (recommended)", responseProjectID) - finalProjectID = responseProjectID - } - } else { - // Pro users: keep requested project ID (original behavior) - log.Warnf("Gemini onboarding returned project %s instead of requested %s; keeping requested project ID.", responseProjectID, projectID) - } + log.Infof("Gemini onboarding: requested project %s maps to backend project %s", projectID, responseProjectID) + log.Infof("Using backend project ID: %s", responseProjectID) + finalProjectID = responseProjectID } else { finalProjectID = responseProjectID } diff --git a/internal/misc/header_utils.go b/internal/misc/header_utils.go index 5752a269..ac022a96 100644 --- a/internal/misc/header_utils.go +++ b/internal/misc/header_utils.go @@ -12,7 +12,7 @@ import ( const ( // GeminiCLIVersion is the version string reported in the User-Agent for upstream requests. - GeminiCLIVersion = "0.31.0" + GeminiCLIVersion = "0.34.0" // GeminiCLIApiClientHeader is the value for the X-Goog-Api-Client header sent to the Gemini CLI upstream. GeminiCLIApiClientHeader = "google-genai-sdk/1.41.0 gl-node/v22.19.0" @@ -46,7 +46,7 @@ func GeminiCLIUserAgent(model string) string { if model == "" { model = "unknown" } - return fmt.Sprintf("GeminiCLI/%s/%s (%s; %s)", GeminiCLIVersion, model, geminiCLIOS(), geminiCLIArch()) + return fmt.Sprintf("GeminiCLI/%s/%s (%s; %s; terminal)", GeminiCLIVersion, model, geminiCLIOS(), geminiCLIArch()) } // ScrubProxyAndFingerprintHeaders removes all headers that could reveal -- 2.51.2 From 91387ca2472aac07440b542b80fb1f070f63a624 Mon Sep 17 00:00:00 2001 From: daniel <15257433+kslamph@users.noreply.github.com> Date: Mon, 30 Mar 2026 20:07:02 +0800 Subject: [PATCH 02/51] refactor(gemini-cli): simplify redundant if/else in project ID assignment Both branches assign finalProjectID = responseProjectID, so move the assignment outside the conditional and keep only the logging inside. --- internal/api/handlers/management/auth_files.go | 4 +--- internal/cmd/login.go | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/internal/api/handlers/management/auth_files.go b/internal/api/handlers/management/auth_files.go index a7916e79..63b1d62d 100644 --- a/internal/api/handlers/management/auth_files.go +++ b/internal/api/handlers/management/auth_files.go @@ -2568,10 +2568,8 @@ func performGeminiCLISetup(ctx context.Context, httpClient *http.Client, storage if explicitProject && !strings.EqualFold(responseProjectID, projectID) { log.Infof("Gemini onboarding: requested project %s maps to backend project %s", projectID, responseProjectID) log.Infof("Using backend project ID: %s", responseProjectID) - finalProjectID = responseProjectID - } else { - finalProjectID = responseProjectID } + finalProjectID = responseProjectID } storage.ProjectID = strings.TrimSpace(finalProjectID) diff --git a/internal/cmd/login.go b/internal/cmd/login.go index 298e9546..22404dac 100644 --- a/internal/cmd/login.go +++ b/internal/cmd/login.go @@ -335,10 +335,8 @@ func performGeminiCLISetup(ctx context.Context, httpClient *http.Client, storage if explicitProject && !strings.EqualFold(responseProjectID, projectID) { log.Infof("Gemini onboarding: requested project %s maps to backend project %s", projectID, responseProjectID) log.Infof("Using backend project ID: %s", responseProjectID) - finalProjectID = responseProjectID - } else { - finalProjectID = responseProjectID } + finalProjectID = responseProjectID } storage.ProjectID = strings.TrimSpace(finalProjectID) -- 2.51.2 From 6431cec7d3c12d14a5eac272a8555d82753b4dad Mon Sep 17 00:00:00 2001 From: Code_G <12405078+codeg-dev@users.noreply.github.com> Date: Mon, 6 Apr 2026 17:16:15 +0900 Subject: [PATCH 03/51] fix(claude-auth): dedupe OAuth refresh and honor 429 backoff Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus --- internal/auth/claude/anthropic_auth.go | 135 +++++++++++++++++++- internal/auth/claude/anthropic_auth_test.go | 123 ++++++++++++++++++ 2 files changed, 257 insertions(+), 1 deletion(-) create mode 100644 internal/auth/claude/anthropic_auth_test.go diff --git a/internal/auth/claude/anthropic_auth.go b/internal/auth/claude/anthropic_auth.go index 12bb53ac..b7f997ef 100644 --- a/internal/auth/claude/anthropic_auth.go +++ b/internal/auth/claude/anthropic_auth.go @@ -6,15 +6,18 @@ package claude import ( "context" "encoding/json" + "errors" "fmt" "io" "net/http" "net/url" "strings" + "sync" "time" "github.com/router-for-me/CLIProxyAPI/v6/internal/config" log "github.com/sirupsen/logrus" + "golang.org/x/sync/singleflight" ) // OAuth configuration constants for Claude/Anthropic @@ -23,8 +26,94 @@ const ( TokenURL = "https://api.anthropic.com/v1/oauth/token" ClientID = "9d1c250a-e61b-44d9-88ed-5944d1962f5e" RedirectURI = "http://localhost:54545/callback" + + claudeRefreshMinBackoff = 5 * time.Second + claudeRefreshMaxBackoff = 5 * time.Minute +) + +var ( + claudeRefreshGroup singleflight.Group + claudeRefreshMu sync.Mutex + claudeRefreshBlock = make(map[string]time.Time) ) +type refreshHTTPError struct { + status int + message string + retryable bool +} + +func (e *refreshHTTPError) Error() string { + return fmt.Sprintf("token refresh failed with status %d: %s", e.status, e.message) +} + +func (e *refreshHTTPError) Retryable() bool { + return e != nil && e.retryable +} + +func resetClaudeRefreshState() { + claudeRefreshMu.Lock() + defer claudeRefreshMu.Unlock() + claudeRefreshBlock = make(map[string]time.Time) + claudeRefreshGroup = singleflight.Group{} +} + +func claudeRefreshBlockedUntil(refreshToken string) time.Time { + claudeRefreshMu.Lock() + defer claudeRefreshMu.Unlock() + return claudeRefreshBlock[refreshToken] +} + +func setClaudeRefreshBlockedUntil(refreshToken string, until time.Time) { + claudeRefreshMu.Lock() + defer claudeRefreshMu.Unlock() + claudeRefreshBlock[refreshToken] = until +} + +func clearClaudeRefreshBlockedUntil(refreshToken string) { + claudeRefreshMu.Lock() + defer claudeRefreshMu.Unlock() + delete(claudeRefreshBlock, refreshToken) +} + +func clampClaudeRefreshBackoff(d time.Duration) time.Duration { + if d < claudeRefreshMinBackoff { + return claudeRefreshMinBackoff + } + if d > claudeRefreshMaxBackoff { + return claudeRefreshMaxBackoff + } + return d +} + +func parseClaudeRetryAfter(resp *http.Response) time.Duration { + if resp == nil { + return claudeRefreshMinBackoff + } + if raw := strings.TrimSpace(resp.Header.Get("Retry-After")); raw != "" { + if seconds, err := time.ParseDuration(raw + "s"); err == nil { + return clampClaudeRefreshBackoff(seconds) + } + if when, err := http.ParseTime(raw); err == nil { + return clampClaudeRefreshBackoff(time.Until(when)) + } + } + if raw := strings.TrimSpace(resp.Header.Get("Retry-After-Ms")); raw != "" { + if ms, err := time.ParseDuration(raw + "ms"); err == nil { + return clampClaudeRefreshBackoff(ms) + } + } + return claudeRefreshMinBackoff +} + +func isClaudeRefreshRetryable(err error) bool { + var httpErr *refreshHTTPError + if errors.As(err, &httpErr) { + return httpErr.Retryable() + } + return true +} + // tokenResponse represents the response structure from Anthropic's OAuth token endpoint. // It contains access token, refresh token, and associated user/organization information. type tokenResponse struct { @@ -222,6 +311,35 @@ func (o *ClaudeAuth) RefreshTokens(ctx context.Context, refreshToken string) (*C if refreshToken == "" { return nil, fmt.Errorf("refresh token is required") } + if blockedUntil := claudeRefreshBlockedUntil(refreshToken); blockedUntil.After(time.Now()) { + return nil, &refreshHTTPError{ + status: http.StatusTooManyRequests, + message: fmt.Sprintf("refresh temporarily blocked until %s", blockedUntil.Format(time.RFC3339)), + retryable: false, + } + } + + result, err, _ := claudeRefreshGroup.Do(refreshToken, func() (interface{}, error) { + return o.refreshTokensSingleFlight(context.WithoutCancel(ctx), refreshToken) + }) + if err != nil { + return nil, err + } + tokenData, ok := result.(*ClaudeTokenData) + if !ok || tokenData == nil { + return nil, fmt.Errorf("token refresh failed: invalid single-flight result") + } + return tokenData, nil +} + +func (o *ClaudeAuth) refreshTokensSingleFlight(ctx context.Context, refreshToken string) (*ClaudeTokenData, error) { + if blockedUntil := claudeRefreshBlockedUntil(refreshToken); blockedUntil.After(time.Now()) { + return nil, &refreshHTTPError{ + status: http.StatusTooManyRequests, + message: fmt.Sprintf("refresh temporarily blocked until %s", blockedUntil.Format(time.RFC3339)), + retryable: false, + } + } reqBody := map[string]interface{}{ "client_id": ClientID, @@ -256,7 +374,17 @@ func (o *ClaudeAuth) RefreshTokens(ctx context.Context, refreshToken string) (*C } if resp.StatusCode != http.StatusOK { - return nil, fmt.Errorf("token refresh failed with status %d: %s", resp.StatusCode, string(body)) + message := string(body) + if resp.StatusCode == http.StatusTooManyRequests { + retryAfter := parseClaudeRetryAfter(resp) + setClaudeRefreshBlockedUntil(refreshToken, time.Now().Add(retryAfter)) + return nil, &refreshHTTPError{status: resp.StatusCode, message: message, retryable: false} + } + return nil, &refreshHTTPError{ + status: resp.StatusCode, + message: message, + retryable: resp.StatusCode >= http.StatusInternalServerError, + } } // log.Debugf("Token response: %s", string(body)) @@ -267,6 +395,8 @@ func (o *ClaudeAuth) RefreshTokens(ctx context.Context, refreshToken string) (*C } // Create token data + clearClaudeRefreshBlockedUntil(refreshToken) + return &ClaudeTokenData{ AccessToken: tokenResp.AccessToken, RefreshToken: tokenResp.RefreshToken, @@ -328,6 +458,9 @@ func (o *ClaudeAuth) RefreshTokensWithRetry(ctx context.Context, refreshToken st lastErr = err log.Warnf("Token refresh attempt %d failed: %v", attempt+1, err) + if !isClaudeRefreshRetryable(err) { + break + } } return nil, fmt.Errorf("token refresh failed after %d attempts: %w", maxRetries, lastErr) diff --git a/internal/auth/claude/anthropic_auth_test.go b/internal/auth/claude/anthropic_auth_test.go new file mode 100644 index 00000000..0b14d083 --- /dev/null +++ b/internal/auth/claude/anthropic_auth_test.go @@ -0,0 +1,123 @@ +package claude + +import ( + "context" + "io" + "net/http" + "strings" + "sync" + "sync/atomic" + "testing" + "time" +) + +type roundTripFunc func(*http.Request) (*http.Response, error) + +func (f roundTripFunc) RoundTrip(req *http.Request) (*http.Response, error) { + return f(req) +} + +func TestRefreshTokensWithRetry_429BlocksImmediateReplay(t *testing.T) { + resetClaudeRefreshState() + defer resetClaudeRefreshState() + + var calls int32 + auth := &ClaudeAuth{ + httpClient: &http.Client{ + Transport: roundTripFunc(func(req *http.Request) (*http.Response, error) { + atomic.AddInt32(&calls, 1) + return &http.Response{ + StatusCode: http.StatusTooManyRequests, + Body: io.NopCloser(strings.NewReader(`{"error":"rate_limited"}`)), + Header: http.Header{"Retry-After": []string{"60"}}, + Request: req, + }, nil + }), + }, + } + + _, err := auth.RefreshTokensWithRetry(context.Background(), "dummy_refresh_token", 3) + if err == nil { + t.Fatalf("expected 429 refresh error") + } + if !strings.Contains(err.Error(), "status 429") { + t.Fatalf("expected status 429 in error, got %v", err) + } + if got := atomic.LoadInt32(&calls); got != 1 { + t.Fatalf("expected 1 refresh attempt after 429, got %d", got) + } + + _, err = auth.RefreshTokensWithRetry(context.Background(), "dummy_refresh_token", 3) + if err == nil { + t.Fatalf("expected immediate blocked refresh error") + } + if got := atomic.LoadInt32(&calls); got != 1 { + t.Fatalf("expected blocked retry to avoid a second refresh call, got %d attempts", got) + } + if blockedUntil := claudeRefreshBlockedUntil("dummy_refresh_token"); !blockedUntil.After(time.Now()) { + t.Fatalf("expected blocked-until timestamp to be set, got %v", blockedUntil) + } +} + +func TestRefreshTokens_DeduplicatesConcurrentRefresh(t *testing.T) { + resetClaudeRefreshState() + defer resetClaudeRefreshState() + + var calls int32 + started := make(chan struct{}) + release := make(chan struct{}) + var once sync.Once + + auth := &ClaudeAuth{ + httpClient: &http.Client{ + Transport: roundTripFunc(func(req *http.Request) (*http.Response, error) { + atomic.AddInt32(&calls, 1) + once.Do(func() { close(started) }) + <-release + return &http.Response{ + StatusCode: http.StatusOK, + Body: io.NopCloser(strings.NewReader(`{ + "access_token":"new-access", + "refresh_token":"new-refresh", + "token_type":"Bearer", + "expires_in":3600, + "account":{"email_address":"shared@example.com"} + }`)), + Header: make(http.Header), + Request: req, + }, nil + }), + }, + } + + results := make(chan *ClaudeTokenData, 2) + errs := make(chan error, 2) + runRefresh := func() { + td, err := auth.RefreshTokens(context.Background(), "shared-refresh-token") + results <- td + errs <- err + } + + go runRefresh() + go runRefresh() + + <-started + time.Sleep(20 * time.Millisecond) + if got := atomic.LoadInt32(&calls); got != 1 { + t.Fatalf("expected concurrent refresh to share a single upstream call, got %d", got) + } + close(release) + + for i := 0; i < 2; i++ { + if err := <-errs; err != nil { + t.Fatalf("expected refresh to succeed, got %v", err) + } + td := <-results + if td == nil || td.AccessToken != "new-access" { + t.Fatalf("expected refreshed access token, got %#v", td) + } + } + if got := atomic.LoadInt32(&calls); got != 1 { + t.Fatalf("expected exactly 1 upstream refresh call, got %d", got) + } +} -- 2.51.2 From 29e32aaab940f0681b9f9a9b2da94b81d2e5d098 Mon Sep 17 00:00:00 2001 From: Code_G <12405078+codeg-dev@users.noreply.github.com> Date: Mon, 6 Apr 2026 17:16:42 +0900 Subject: [PATCH 04/51] fix(executor): route Claude refresh through retry-aware auth Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus --- internal/runtime/executor/claude_executor.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/runtime/executor/claude_executor.go b/internal/runtime/executor/claude_executor.go index 7b2e5d8d..93487311 100644 --- a/internal/runtime/executor/claude_executor.go +++ b/internal/runtime/executor/claude_executor.go @@ -594,7 +594,7 @@ func (e *ClaudeExecutor) Refresh(ctx context.Context, auth *cliproxyauth.Auth) ( return auth, nil } svc := claudeauth.NewClaudeAuth(e.cfg) - td, err := svc.RefreshTokens(ctx, refreshToken) + td, err := svc.RefreshTokensWithRetry(ctx, refreshToken, 3) if err != nil { return nil, err } -- 2.51.2 From a0fe273081eaa3a4b89ec21fd718a4585b84fda2 Mon Sep 17 00:00:00 2001 From: DragonFSKY Date: Sun, 22 Mar 2026 09:49:34 +0800 Subject: [PATCH 05/51] fix(websocket): skip stale state merge after client-side compact After a Codex CLI compact, the client sends a full conversation transcript (with compaction items or assistant messages) as input. Previously, normalizeResponseSubsequentRequest() unconditionally merged this with stale lastRequest/lastResponseOutput, breaking function_call/function_call_output pairings and causing 400 errors ("No tool output found for function call"). Add inputContainsFullTranscript() heuristic that detects compaction items (type=compaction/compaction_summary) or assistant messages in the input array, and bypasses the merge when a full transcript is present. Fixes #2207 --- .../openai/openai_responses_websocket.go | 66 +++++++++--- .../openai/openai_responses_websocket_test.go | 101 ++++++++++++++++++ 2 files changed, 155 insertions(+), 12 deletions(-) diff --git a/sdk/api/handlers/openai/openai_responses_websocket.go b/sdk/api/handlers/openai/openai_responses_websocket.go index 2f6b14a7..6457cd3e 100644 --- a/sdk/api/handlers/openai/openai_responses_websocket.go +++ b/sdk/api/handlers/openai/openai_responses_websocket.go @@ -315,20 +315,32 @@ func normalizeResponseSubsequentRequest(rawJSON []byte, lastRequest []byte, last } } - existingInput := gjson.GetBytes(lastRequest, "input") - mergedInput, errMerge := mergeJSONArrayRaw(existingInput.Raw, normalizeJSONArrayRaw(lastResponseOutput)) - if errMerge != nil { - return nil, lastRequest, &interfaces.ErrorMessage{ - StatusCode: http.StatusBadRequest, - Error: fmt.Errorf("invalid previous response output: %w", errMerge), + // When the client sends a full conversation transcript (e.g. after compact), + // the input already contains the complete history including assistant messages. + // In that case, skip merging with stale lastRequest/lastResponseOutput to avoid + // breaking function_call / function_call_output pairings. + // See: https://github.com/router-for-me/CLIProxyAPI/issues/2207 + var mergedInput string + if inputContainsFullTranscript(nextInput) { + log.Infof("responses websocket: full transcript detected, skipping stale merge (input items=%d)", len(nextInput.Array())) + mergedInput = nextInput.Raw + } else { + existingInput := gjson.GetBytes(lastRequest, "input") + var errMerge error + mergedInput, errMerge = mergeJSONArrayRaw(existingInput.Raw, normalizeJSONArrayRaw(lastResponseOutput)) + if errMerge != nil { + return nil, lastRequest, &interfaces.ErrorMessage{ + StatusCode: http.StatusBadRequest, + Error: fmt.Errorf("invalid previous response output: %w", errMerge), + } } - } - mergedInput, errMerge = mergeJSONArrayRaw(mergedInput, nextInput.Raw) - if errMerge != nil { - return nil, lastRequest, &interfaces.ErrorMessage{ - StatusCode: http.StatusBadRequest, - Error: fmt.Errorf("invalid request input: %w", errMerge), + mergedInput, errMerge = mergeJSONArrayRaw(mergedInput, nextInput.Raw) + if errMerge != nil { + return nil, lastRequest, &interfaces.ErrorMessage{ + StatusCode: http.StatusBadRequest, + Error: fmt.Errorf("invalid request input: %w", errMerge), + } } } dedupedInput, errDedupeFunctionCalls := dedupeFunctionCallsByCallID(mergedInput) @@ -691,6 +703,36 @@ func mergeJSONArrayRaw(existingRaw, appendRaw string) (string, error) { return string(out), nil } +// inputContainsFullTranscript returns true when the input array looks like a +// complete conversation history rather than an incremental append. After a +// client-side compact the input already carries the full (compacted) transcript +// which may include assistant messages or compaction items. Merging that with +// the stale lastRequest / lastResponseOutput would duplicate or break +// function_call / function_call_output pairings, so the caller should use the +// input as-is. +// +// Heuristic: the array is a full transcript when it contains either +// - a message with role="assistant", or +// - a compaction item (type="compaction" or "compaction_summary"). +// +// Normal incremental turns only contain user messages or function_call_output +// items and never carry either of these signals. +func inputContainsFullTranscript(input gjson.Result) bool { + if !input.IsArray() { + return false + } + for _, item := range input.Array() { + t := item.Get("type").String() + if t == "message" && item.Get("role").String() == "assistant" { + return true + } + if t == "compaction" || t == "compaction_summary" { + return true + } + } + return false +} + func normalizeJSONArrayRaw(raw []byte) string { trimmed := strings.TrimSpace(string(raw)) if trimmed == "" { diff --git a/sdk/api/handlers/openai/openai_responses_websocket_test.go b/sdk/api/handlers/openai/openai_responses_websocket_test.go index ecfc90b3..2c5ef579 100644 --- a/sdk/api/handlers/openai/openai_responses_websocket_test.go +++ b/sdk/api/handlers/openai/openai_responses_websocket_test.go @@ -1400,3 +1400,104 @@ func TestResponsesWebsocketCompactionResetsTurnStateOnTranscriptReplacement(t *t t.Fatalf("post-compact function call id = %s, want call-1", items[0].Get("call_id").String()) } } + +func TestInputContainsFullTranscriptDetectsAssistantMessage(t *testing.T) { + input := gjson.Parse(`[ + {"type":"message","role":"user","content":"hello"}, + {"type":"message","role":"assistant","content":"hi there"} + ]`) + if !inputContainsFullTranscript(input) { + t.Fatal("expected full transcript when assistant message is present") + } +} + +func TestInputContainsFullTranscriptDetectsCompactionItem(t *testing.T) { + for _, typ := range []string{"compaction", "compaction_summary"} { + input := gjson.Parse(`[{"type":"message","role":"user","content":"hello"},{"type":"` + typ + `","encrypted_content":"summary"}]`) + if !inputContainsFullTranscript(input) { + t.Fatalf("expected full transcript for type=%s", typ) + } + } +} + +func TestInputContainsFullTranscriptFalseForIncremental(t *testing.T) { + // Normal incremental turns: user messages or function_call_output only. + for _, raw := range []string{ + `[{"type":"function_call_output","call_id":"call-1","output":"result"}]`, + `[{"type":"message","role":"user","content":"next question"}]`, + `[]`, + } { + if inputContainsFullTranscript(gjson.Parse(raw)) { + t.Fatalf("incremental input must not be detected as full transcript: %s", raw) + } + } +} + +func TestNormalizeSubsequentRequestCompactSkipsMerge(t *testing.T) { + lastRequest := []byte(`{"model":"gpt-5.4","stream":true,"input":[ + {"type":"message","role":"user","id":"msg-1","content":"original long prompt"}, + {"type":"message","role":"assistant","id":"msg-2","content":"original long response"}, + {"type":"function_call","id":"fc-1","call_id":"call-old","name":"bash","arguments":"{}"}, + {"type":"function_call_output","id":"fco-1","call_id":"call-old","output":"old result"} + ]}`) + lastResponseOutput := []byte(`[ + {"type":"message","role":"assistant","id":"msg-3","content":"another assistant reply"}, + {"type":"function_call","id":"fc-2","call_id":"call-stale","name":"read","arguments":"{}"} + ]`) + + // Remote compact response: user messages + compaction item, NO assistant message. + // This is the primary compact scenario from Codex CLI. + raw := []byte(`{"type":"response.create","input":[ + {"type":"message","role":"user","id":"msg-1c","content":"compacted user msg"}, + {"type":"compaction","encrypted_content":"conversation summary"} + ]}`) + + normalized, _, errMsg := normalizeResponsesWebsocketRequest(raw, lastRequest, lastResponseOutput) + if errMsg != nil { + t.Fatalf("unexpected error: %v", errMsg.Error) + } + + input := gjson.GetBytes(normalized, "input").Array() + if len(input) != 2 { + t.Fatalf("input len = %d, want 2 (compacted only); stale state was not skipped", len(input)) + } + if input[0].Get("id").String() != "msg-1c" { + t.Fatalf("input[0].id = %q, want %q", input[0].Get("id").String(), "msg-1c") + } + if input[1].Get("type").String() != "compaction" { + t.Fatalf("input[1].type = %q, want %q", input[1].Get("type").String(), "compaction") + } +} + +func TestNormalizeSubsequentRequestIncrementalInputStillMerges(t *testing.T) { + // Normal incremental flow: user sends function_call_output (no assistant message). + lastRequest := []byte(`{"model":"gpt-5.4","stream":true,"input":[ + {"type":"message","role":"user","id":"msg-1","content":"hello"} + ]}`) + lastResponseOutput := []byte(`[ + {"type":"message","role":"assistant","id":"msg-2","content":"let me check"}, + {"type":"function_call","id":"fc-1","call_id":"call-1","name":"bash","arguments":"{}"} + ]`) + raw := []byte(`{"type":"response.create","input":[ + {"type":"function_call_output","call_id":"call-1","id":"fco-1","output":"done"} + ]}`) + + normalized, _, errMsg := normalizeResponsesWebsocketRequest(raw, lastRequest, lastResponseOutput) + if errMsg != nil { + t.Fatalf("unexpected error: %v", errMsg.Error) + } + + input := gjson.GetBytes(normalized, "input").Array() + + // Should be merged: msg-1 + msg-2 + fc-1 + fco-1 = 4 items + if len(input) != 4 { + t.Fatalf("input len = %d, want 4 (merged)", len(input)) + } + wantIDs := []string{"msg-1", "msg-2", "fc-1", "fco-1"} + for i, want := range wantIDs { + got := input[i].Get("id").String() + if got != want { + t.Fatalf("input[%d].id = %q, want %q", i, got, want) + } + } +} -- 2.51.2 From d2d0e6f6a1c2e4126151cd1ad78e7809598620ad Mon Sep 17 00:00:00 2001 From: DragonFSKY Date: Mon, 23 Mar 2026 23:27:20 +0800 Subject: [PATCH 06/51] fix(websocket): narrow compact replay detection --- .../openai/openai_responses_websocket.go | 23 ++++-------- .../openai/openai_responses_websocket_test.go | 36 +++++++++++++++++-- 2 files changed, 40 insertions(+), 19 deletions(-) diff --git a/sdk/api/handlers/openai/openai_responses_websocket.go b/sdk/api/handlers/openai/openai_responses_websocket.go index 6457cd3e..273547d8 100644 --- a/sdk/api/handlers/openai/openai_responses_websocket.go +++ b/sdk/api/handlers/openai/openai_responses_websocket.go @@ -703,29 +703,20 @@ func mergeJSONArrayRaw(existingRaw, appendRaw string) (string, error) { return string(out), nil } -// inputContainsFullTranscript returns true when the input array looks like a -// complete conversation history rather than an incremental append. After a -// client-side compact the input already carries the full (compacted) transcript -// which may include assistant messages or compaction items. Merging that with -// the stale lastRequest / lastResponseOutput would duplicate or break -// function_call / function_call_output pairings, so the caller should use the -// input as-is. +// inputContainsFullTranscript returns true when the input array carries compact +// replay markers that indicate the client already sent the full conversation +// transcript. Merging that input with stale lastRequest/lastResponseOutput +// would duplicate or break function_call/function_call_output pairings, so the +// caller should use the input as-is. // -// Heuristic: the array is a full transcript when it contains either -// - a message with role="assistant", or -// - a compaction item (type="compaction" or "compaction_summary"). -// -// Normal incremental turns only contain user messages or function_call_output -// items and never carry either of these signals. +// Assistant messages alone are not enough to classify the payload as a replay: +// incremental websocket requests may legitimately append assistant items. func inputContainsFullTranscript(input gjson.Result) bool { if !input.IsArray() { return false } for _, item := range input.Array() { t := item.Get("type").String() - if t == "message" && item.Get("role").String() == "assistant" { - return true - } if t == "compaction" || t == "compaction_summary" { return true } diff --git a/sdk/api/handlers/openai/openai_responses_websocket_test.go b/sdk/api/handlers/openai/openai_responses_websocket_test.go index 2c5ef579..82b96f14 100644 --- a/sdk/api/handlers/openai/openai_responses_websocket_test.go +++ b/sdk/api/handlers/openai/openai_responses_websocket_test.go @@ -1401,13 +1401,13 @@ func TestResponsesWebsocketCompactionResetsTurnStateOnTranscriptReplacement(t *t } } -func TestInputContainsFullTranscriptDetectsAssistantMessage(t *testing.T) { +func TestInputContainsFullTranscriptFalseForAssistantMessageOnly(t *testing.T) { input := gjson.Parse(`[ {"type":"message","role":"user","content":"hello"}, {"type":"message","role":"assistant","content":"hi there"} ]`) - if !inputContainsFullTranscript(input) { - t.Fatal("expected full transcript when assistant message is present") + if inputContainsFullTranscript(input) { + t.Fatal("assistant message alone must not be treated as full transcript") } } @@ -1501,3 +1501,33 @@ func TestNormalizeSubsequentRequestIncrementalInputStillMerges(t *testing.T) { } } } + +func TestNormalizeSubsequentRequestAssistantIncrementalInputStillMerges(t *testing.T) { + lastRequest := []byte(`{"model":"gpt-5.4","stream":true,"input":[ + {"type":"message","role":"user","id":"msg-1","content":"hello"} + ]}`) + lastResponseOutput := []byte(`[ + {"type":"message","role":"assistant","id":"msg-2","content":"prior assistant"}, + {"type":"function_call","id":"fc-1","call_id":"call-1","name":"bash","arguments":"{}"} + ]`) + raw := []byte(`{"type":"response.append","input":[ + {"type":"message","role":"assistant","id":"msg-3","content":"patched assistant turn"} + ]}`) + + normalized, _, errMsg := normalizeResponsesWebsocketRequest(raw, lastRequest, lastResponseOutput) + if errMsg != nil { + t.Fatalf("unexpected error: %v", errMsg.Error) + } + + input := gjson.GetBytes(normalized, "input").Array() + if len(input) != 4 { + t.Fatalf("input len = %d, want 4 (merged)", len(input)) + } + wantIDs := []string{"msg-1", "msg-2", "fc-1", "msg-3"} + for i, want := range wantIDs { + got := input[i].Get("id").String() + if got != want { + t.Fatalf("input[%d].id = %q, want %q", i, got, want) + } + } +} -- 2.51.2 From 4ca00f79832a2111d23d1d80b490e5a9c3026aab Mon Sep 17 00:00:00 2001 From: DragonFSKY Date: Tue, 24 Mar 2026 19:48:32 +0800 Subject: [PATCH 07/51] fix(websocket): gate compact replay by downstream support --- .../openai/openai_responses_websocket.go | 166 ++++++++++++------ .../openai/openai_responses_websocket_test.go | 106 +++++++++-- 2 files changed, 211 insertions(+), 61 deletions(-) diff --git a/sdk/api/handlers/openai/openai_responses_websocket.go b/sdk/api/handlers/openai/openai_responses_websocket.go index 273547d8..caf26f13 100644 --- a/sdk/api/handlers/openai/openai_responses_websocket.go +++ b/sdk/api/handlers/openai/openai_responses_websocket.go @@ -116,6 +116,19 @@ func (h *OpenAIResponsesAPIHandler) ResponsesWebsocket(c *gin.Context) { allowIncrementalInputWithPreviousResponseID = h.websocketUpstreamSupportsIncrementalInputForModel(requestModelName) } + allowCompactionReplayBypass := false + if pinnedAuthID != "" && h != nil && h.AuthManager != nil { + if pinnedAuth, ok := h.AuthManager.GetByID(pinnedAuthID); ok && pinnedAuth != nil { + allowCompactionReplayBypass = responsesWebsocketAuthSupportsCompactionReplay(pinnedAuth) + } + } else { + requestModelName := strings.TrimSpace(gjson.GetBytes(payload, "model").String()) + if requestModelName == "" { + requestModelName = strings.TrimSpace(gjson.GetBytes(lastRequest, "model").String()) + } + allowCompactionReplayBypass = h.websocketUpstreamSupportsCompactionReplayForModel(requestModelName) + } + var requestJSON []byte var updatedLastRequest []byte var errMsg *interfaces.ErrorMessage @@ -124,6 +137,7 @@ func (h *OpenAIResponsesAPIHandler) ResponsesWebsocket(c *gin.Context) { lastRequest, lastResponseOutput, allowIncrementalInputWithPreviousResponseID, + allowCompactionReplayBypass, ) if errMsg != nil { h.LoggingAPIResponseError(context.WithValue(context.Background(), "gin", c), errMsg) @@ -222,10 +236,10 @@ func websocketUpgradeHeaders(req *http.Request) http.Header { } func normalizeResponsesWebsocketRequest(rawJSON []byte, lastRequest []byte, lastResponseOutput []byte) ([]byte, []byte, *interfaces.ErrorMessage) { - return normalizeResponsesWebsocketRequestWithMode(rawJSON, lastRequest, lastResponseOutput, true) + return normalizeResponsesWebsocketRequestWithMode(rawJSON, lastRequest, lastResponseOutput, true, true) } -func normalizeResponsesWebsocketRequestWithMode(rawJSON []byte, lastRequest []byte, lastResponseOutput []byte, allowIncrementalInputWithPreviousResponseID bool) ([]byte, []byte, *interfaces.ErrorMessage) { +func normalizeResponsesWebsocketRequestWithMode(rawJSON []byte, lastRequest []byte, lastResponseOutput []byte, allowIncrementalInputWithPreviousResponseID bool, allowCompactionReplayBypass bool) ([]byte, []byte, *interfaces.ErrorMessage) { requestType := strings.TrimSpace(gjson.GetBytes(rawJSON, "type").String()) switch requestType { case wsRequestTypeCreate: @@ -233,10 +247,10 @@ func normalizeResponsesWebsocketRequestWithMode(rawJSON []byte, lastRequest []by if len(lastRequest) == 0 { return normalizeResponseCreateRequest(rawJSON) } - return normalizeResponseSubsequentRequest(rawJSON, lastRequest, lastResponseOutput, allowIncrementalInputWithPreviousResponseID) + return normalizeResponseSubsequentRequest(rawJSON, lastRequest, lastResponseOutput, allowIncrementalInputWithPreviousResponseID, allowCompactionReplayBypass) case wsRequestTypeAppend: // log.Infof("responses websocket: response.append request") - return normalizeResponseSubsequentRequest(rawJSON, lastRequest, lastResponseOutput, allowIncrementalInputWithPreviousResponseID) + return normalizeResponseSubsequentRequest(rawJSON, lastRequest, lastResponseOutput, allowIncrementalInputWithPreviousResponseID, allowCompactionReplayBypass) default: return nil, lastRequest, &interfaces.ErrorMessage{ StatusCode: http.StatusBadRequest, @@ -265,7 +279,7 @@ func normalizeResponseCreateRequest(rawJSON []byte) ([]byte, []byte, *interfaces return normalized, bytes.Clone(normalized), nil } -func normalizeResponseSubsequentRequest(rawJSON []byte, lastRequest []byte, lastResponseOutput []byte, allowIncrementalInputWithPreviousResponseID bool) ([]byte, []byte, *interfaces.ErrorMessage) { +func normalizeResponseSubsequentRequest(rawJSON []byte, lastRequest []byte, lastResponseOutput []byte, allowIncrementalInputWithPreviousResponseID bool, allowCompactionReplayBypass bool) ([]byte, []byte, *interfaces.ErrorMessage) { if len(lastRequest) == 0 { return nil, lastRequest, &interfaces.ErrorMessage{ StatusCode: http.StatusBadRequest, @@ -315,16 +329,21 @@ func normalizeResponseSubsequentRequest(rawJSON []byte, lastRequest []byte, last } } - // When the client sends a full conversation transcript (e.g. after compact), - // the input already contains the complete history including assistant messages. - // In that case, skip merging with stale lastRequest/lastResponseOutput to avoid - // breaking function_call / function_call_output pairings. + // When the client sends a compact replay for a downstream that can consume it + // directly, the input already carries the canonical history. In that case, + // skip merging with stale lastRequest/lastResponseOutput to avoid breaking + // function_call / function_call_output pairings. // See: https://github.com/router-for-me/CLIProxyAPI/issues/2207 var mergedInput string - if inputContainsFullTranscript(nextInput) { + if allowCompactionReplayBypass && inputContainsFullTranscript(nextInput) { log.Infof("responses websocket: full transcript detected, skipping stale merge (input items=%d)", len(nextInput.Array())) mergedInput = nextInput.Raw } else { + appendInputRaw := nextInput.Raw + if inputContainsFullTranscript(nextInput) { + appendInputRaw = inputWithoutCompactionItems(nextInput) + } + existingInput := gjson.GetBytes(lastRequest, "input") var errMerge error mergedInput, errMerge = mergeJSONArrayRaw(existingInput.Raw, normalizeJSONArrayRaw(lastResponseOutput)) @@ -335,7 +354,7 @@ func normalizeResponseSubsequentRequest(rawJSON []byte, lastRequest []byte, last } } - mergedInput, errMerge = mergeJSONArrayRaw(mergedInput, nextInput.Raw) + mergedInput, errMerge = mergeJSONArrayRaw(mergedInput, appendInputRaw) if errMerge != nil { return nil, lastRequest, &interfaces.ErrorMessage{ StatusCode: http.StatusBadRequest, @@ -492,72 +511,104 @@ func websocketUpstreamSupportsIncrementalInput(attributes map[string]string, met } func (h *OpenAIResponsesAPIHandler) websocketUpstreamSupportsIncrementalInputForModel(modelName string) bool { - if h == nil || h.AuthManager == nil { + auths, _ := h.responsesWebsocketAvailableAuthsForModel(modelName) + for _, auth := range auths { + if websocketUpstreamSupportsIncrementalInput(auth.Attributes, auth.Metadata) { + return true + } + } + return false +} + +func (h *OpenAIResponsesAPIHandler) websocketUpstreamSupportsCompactionReplayForModel(modelName string) bool { + auths, _ := h.responsesWebsocketAvailableAuthsForModel(modelName) + if len(auths) == 0 { return false } + for _, auth := range auths { + if !responsesWebsocketAuthSupportsCompactionReplay(auth) { + return false + } + } + return true +} - resolvedModelName := modelName +func (h *OpenAIResponsesAPIHandler) responsesWebsocketAvailableAuthsForModel(modelName string) ([]*coreauth.Auth, string) { + if h == nil || h.AuthManager == nil { + return nil, "" + } + resolvedModelName := responsesWebsocketResolvedModelName(modelName) + providerSet, modelKey := responsesWebsocketProviderSetForModel(resolvedModelName) + if len(providerSet) == 0 { + return nil, modelKey + } + + registryRef := registry.GetGlobalRegistry() + now := time.Now() + auths := h.AuthManager.List() + available := make([]*coreauth.Auth, 0, len(auths)) + for _, auth := range auths { + if !responsesWebsocketAuthMatchesModel(auth, providerSet, modelKey, registryRef, now) { + continue + } + available = append(available, auth) + } + return available, modelKey +} + +func responsesWebsocketResolvedModelName(modelName string) string { initialSuffix := thinking.ParseSuffix(modelName) if initialSuffix.ModelName == "auto" { resolvedBase := util.ResolveAutoModel(initialSuffix.ModelName) if initialSuffix.HasSuffix { - resolvedModelName = fmt.Sprintf("%s(%s)", resolvedBase, initialSuffix.RawSuffix) - } else { - resolvedModelName = resolvedBase + return fmt.Sprintf("%s(%s)", resolvedBase, initialSuffix.RawSuffix) } - } else { - resolvedModelName = util.ResolveAutoModel(modelName) + return resolvedBase } + return util.ResolveAutoModel(modelName) +} +func responsesWebsocketProviderSetForModel(resolvedModelName string) (map[string]struct{}, string) { parsed := thinking.ParseSuffix(resolvedModelName) baseModel := strings.TrimSpace(parsed.ModelName) providers := util.GetProviderName(baseModel) if len(providers) == 0 && baseModel != resolvedModelName { providers = util.GetProviderName(resolvedModelName) } - if len(providers) == 0 { - return false - } - providerSet := make(map[string]struct{}, len(providers)) - for i := 0; i < len(providers); i++ { - providerKey := strings.TrimSpace(strings.ToLower(providers[i])) + for _, provider := range providers { + providerKey := strings.TrimSpace(strings.ToLower(provider)) if providerKey == "" { continue } providerSet[providerKey] = struct{}{} } - if len(providerSet) == 0 { - return false - } - modelKey := baseModel if modelKey == "" { modelKey = strings.TrimSpace(resolvedModelName) } - registryRef := registry.GetGlobalRegistry() - now := time.Now() - auths := h.AuthManager.List() - for i := 0; i < len(auths); i++ { - auth := auths[i] - if auth == nil { - continue - } - providerKey := strings.TrimSpace(strings.ToLower(auth.Provider)) - if _, ok := providerSet[providerKey]; !ok { - continue - } - if modelKey != "" && registryRef != nil && !registryRef.ClientSupportsModel(auth.ID, modelKey) { - continue - } - if !responsesWebsocketAuthAvailableForModel(auth, modelKey, now) { - continue - } - if websocketUpstreamSupportsIncrementalInput(auth.Attributes, auth.Metadata) { - return true - } + return providerSet, modelKey +} + +func responsesWebsocketAuthMatchesModel(auth *coreauth.Auth, providerSet map[string]struct{}, modelKey string, registryRef *registry.ModelRegistry, now time.Time) bool { + if auth == nil { + return false } - return false + providerKey := strings.TrimSpace(strings.ToLower(auth.Provider)) + if _, ok := providerSet[providerKey]; !ok { + return false + } + if modelKey != "" && registryRef != nil && !registryRef.ClientSupportsModel(auth.ID, modelKey) { + return false + } + return responsesWebsocketAuthAvailableForModel(auth, modelKey, now) +} + +func responsesWebsocketAuthSupportsCompactionReplay(auth *coreauth.Auth) bool { + if auth == nil { + return false + } + return strings.EqualFold(strings.TrimSpace(auth.Provider), "codex") } func responsesWebsocketAuthAvailableForModel(auth *coreauth.Auth, modelName string, now time.Time) bool { @@ -724,6 +775,21 @@ func inputContainsFullTranscript(input gjson.Result) bool { return false } +func inputWithoutCompactionItems(input gjson.Result) string { + if !input.IsArray() { + return normalizeJSONArrayRaw([]byte(input.Raw)) + } + filtered := make([]string, 0, len(input.Array())) + for _, item := range input.Array() { + t := item.Get("type").String() + if t == "compaction" || t == "compaction_summary" { + continue + } + filtered = append(filtered, item.Raw) + } + return "[" + strings.Join(filtered, ",") + "]" +} + func normalizeJSONArrayRaw(raw []byte) string { trimmed := strings.TrimSpace(string(raw)) if trimmed == "" { diff --git a/sdk/api/handlers/openai/openai_responses_websocket_test.go b/sdk/api/handlers/openai/openai_responses_websocket_test.go index 82b96f14..f2c4319e 100644 --- a/sdk/api/handlers/openai/openai_responses_websocket_test.go +++ b/sdk/api/handlers/openai/openai_responses_websocket_test.go @@ -242,7 +242,7 @@ func TestNormalizeResponsesWebsocketRequestWithPreviousResponseIDIncremental(t * ]`) raw := []byte(`{"type":"response.create","previous_response_id":"resp-1","input":[{"type":"function_call_output","call_id":"call-1","id":"tool-out-1"}]}`) - normalized, next, errMsg := normalizeResponsesWebsocketRequestWithMode(raw, lastRequest, lastResponseOutput, true) + normalized, next, errMsg := normalizeResponsesWebsocketRequestWithMode(raw, lastRequest, lastResponseOutput, true, false) if errMsg != nil { t.Fatalf("unexpected error: %v", errMsg.Error) } @@ -278,7 +278,7 @@ func TestNormalizeResponsesWebsocketRequestWithPreviousResponseIDMergedWhenIncre ]`) raw := []byte(`{"type":"response.create","previous_response_id":"resp-1","input":[{"type":"function_call_output","call_id":"call-1","id":"tool-out-1"}]}`) - normalized, next, errMsg := normalizeResponsesWebsocketRequestWithMode(raw, lastRequest, lastResponseOutput, false) + normalized, next, errMsg := normalizeResponsesWebsocketRequestWithMode(raw, lastRequest, lastResponseOutput, false, false) if errMsg != nil { t.Fatalf("unexpected error: %v", errMsg.Error) } @@ -867,6 +867,53 @@ func TestWebsocketUpstreamSupportsIncrementalInputForModel(t *testing.T) { } } +func TestWebsocketUpstreamSupportsCompactionReplayForModel(t *testing.T) { + manager := coreauth.NewManager(nil, nil, nil) + auth := &coreauth.Auth{ + ID: "auth-codex", + Provider: "codex", + Status: coreauth.StatusActive, + } + if _, err := manager.Register(context.Background(), auth); err != nil { + t.Fatalf("Register auth: %v", err) + } + registry.GetGlobalRegistry().RegisterClient(auth.ID, auth.Provider, []*registry.ModelInfo{{ID: "test-model"}}) + t.Cleanup(func() { + registry.GetGlobalRegistry().UnregisterClient(auth.ID) + }) + + base := handlers.NewBaseAPIHandlers(&sdkconfig.SDKConfig{}, manager) + h := NewOpenAIResponsesAPIHandler(base) + if !h.websocketUpstreamSupportsCompactionReplayForModel("test-model") { + t.Fatalf("expected codex upstream to support compaction replay") + } +} + +func TestWebsocketUpstreamSupportsCompactionReplayForModelFalseWhenMixedBackends(t *testing.T) { + manager := coreauth.NewManager(nil, nil, nil) + auths := []*coreauth.Auth{ + {ID: "auth-codex", Provider: "codex", Status: coreauth.StatusActive}, + {ID: "auth-claude", Provider: "claude", Status: coreauth.StatusActive}, + } + for _, auth := range auths { + if _, err := manager.Register(context.Background(), auth); err != nil { + t.Fatalf("Register auth %s: %v", auth.ID, err) + } + registry.GetGlobalRegistry().RegisterClient(auth.ID, auth.Provider, []*registry.ModelInfo{{ID: "test-model"}}) + } + t.Cleanup(func() { + for _, auth := range auths { + registry.GetGlobalRegistry().UnregisterClient(auth.ID) + } + }) + + base := handlers.NewBaseAPIHandlers(&sdkconfig.SDKConfig{}, manager) + h := NewOpenAIResponsesAPIHandler(base) + if h.websocketUpstreamSupportsCompactionReplayForModel("test-model") { + t.Fatalf("expected mixed backend model to disable compaction replay bypass") + } +} + func TestResponsesWebsocketPrewarmHandledLocallyForSSEUpstream(t *testing.T) { gin.SetMode(gin.TestMode) @@ -1469,6 +1516,45 @@ func TestNormalizeSubsequentRequestCompactSkipsMerge(t *testing.T) { } } +func TestNormalizeSubsequentRequestCompactMergesWhenCompactionReplayUnsupported(t *testing.T) { + lastRequest := []byte(`{"model":"gpt-5.4","stream":true,"input":[ + {"type":"message","role":"user","id":"msg-1","content":"original long prompt"}, + {"type":"message","role":"assistant","id":"msg-2","content":"original long response"}, + {"type":"function_call","id":"fc-1","call_id":"call-old","name":"bash","arguments":"{}"}, + {"type":"function_call_output","id":"fco-1","call_id":"call-old","output":"old result"} + ]}`) + lastResponseOutput := []byte(`[ + {"type":"message","role":"assistant","id":"msg-3","content":"another assistant reply"}, + {"type":"function_call","id":"fc-2","call_id":"call-stale","name":"read","arguments":"{}"} + ]`) + raw := []byte(`{"type":"response.create","input":[ + {"type":"message","role":"user","id":"msg-1c","content":"compacted user msg"}, + {"type":"compaction","encrypted_content":"conversation summary"} + ]}`) + + normalized, _, errMsg := normalizeResponsesWebsocketRequestWithMode(raw, lastRequest, lastResponseOutput, false, false) + if errMsg != nil { + t.Fatalf("unexpected error: %v", errMsg.Error) + } + + input := gjson.GetBytes(normalized, "input").Array() + if len(input) != 7 { + t.Fatalf("input len = %d, want 7 (merged fallback without compaction items)", len(input)) + } + wantIDs := []string{"msg-1", "msg-2", "fc-1", "fco-1", "msg-3", "fc-2", "msg-1c"} + for i, want := range wantIDs { + got := input[i].Get("id").String() + if got != want { + t.Fatalf("input[%d].id = %q, want %q", i, got, want) + } + } + for _, item := range input { + if item.Get("type").String() == "compaction" || item.Get("type").String() == "compaction_summary" { + t.Fatalf("compaction items must be stripped for unsupported downstream fallback: %s", item.Raw) + } + } +} + func TestNormalizeSubsequentRequestIncrementalInputStillMerges(t *testing.T) { // Normal incremental flow: user sends function_call_output (no assistant message). lastRequest := []byte(`{"model":"gpt-5.4","stream":true,"input":[ @@ -1502,7 +1588,9 @@ func TestNormalizeSubsequentRequestIncrementalInputStillMerges(t *testing.T) { } } -func TestNormalizeSubsequentRequestAssistantIncrementalInputStillMerges(t *testing.T) { +func TestNormalizeSubsequentRequestAssistantInputTriggersTranscriptReplacement(t *testing.T) { + // After dev's shouldReplaceWebsocketTranscript, assistant messages in input + // trigger transcript replacement (no merge with prior state). lastRequest := []byte(`{"model":"gpt-5.4","stream":true,"input":[ {"type":"message","role":"user","id":"msg-1","content":"hello"} ]}`) @@ -1520,14 +1608,10 @@ func TestNormalizeSubsequentRequestAssistantIncrementalInputStillMerges(t *testi } input := gjson.GetBytes(normalized, "input").Array() - if len(input) != 4 { - t.Fatalf("input len = %d, want 4 (merged)", len(input)) + if len(input) != 1 { + t.Fatalf("input len = %d, want 1 (transcript replacement, not merge)", len(input)) } - wantIDs := []string{"msg-1", "msg-2", "fc-1", "msg-3"} - for i, want := range wantIDs { - got := input[i].Get("id").String() - if got != want { - t.Fatalf("input[%d].id = %q, want %q", i, got, want) - } + if input[0].Get("id").String() != "msg-3" { + t.Fatalf("input[0].id = %q, want %q", input[0].Get("id").String(), "msg-3") } } -- 2.51.2 From e707cf7d462f0895f3eb3901aec8f337e68a980e Mon Sep 17 00:00:00 2001 From: Enzo Lucchesi Date: Sat, 18 Apr 2026 10:34:02 -0400 Subject: [PATCH 08/51] fix(claude): only reverse-remap OAuth tool names that were forward-renamed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit remapOAuthToolNames renames lowercase client-sent tools (e.g. `glob` → `Glob`) to Claude Code equivalents on OAuth requests to avoid tool-name fingerprinting. The reverse pass previously ran against a *global* reverse map and rewrote every tool_use block whose name matched any value in oauthToolRenameMap — regardless of what the client actually sent. For clients that send mixed casing (notably Amp CLI — `Bash`, `Read`, `Grep`, `Task` alongside `glob`, `skill`, etc.) this corrupted the response. Any forward rename in the request set the "renamed" flag, which then unconditionally lowercased every `Bash` in the response to `bash`. Amp's tool registry has `Bash`, not `bash`, so it rejected the tool_use with `tool "bash" is not allowed for smart mode` and tool execution failed. Fix: `remapOAuthToolNames` now returns a per-request map keyed on the upstream (TitleCase) name valued with the original client-sent name. The reverse functions take this map and only touch entries in it. Names the client sent in TitleCase pass through untouched in both directions. - Change remapOAuthToolNames signature from `([]byte, bool)` to `([]byte, map[string]string)`; populate at every rename site (tools[], tool_choice.name, message tool_use, tool_reference, nested tool_reference inside tool_result). - Change reverseRemapOAuthToolNames and reverseRemapOAuthToolNamesFromStreamLine to accept and consume the per-request map; remove the global oauthToolRenameReverseMap. - Update all three executor call sites (Execute, ExecuteStream direct passthrough, ExecuteStream translated) + count_tokens. - Add regression tests for the mixed-case scenario in both the non-streaming and SSE code paths. --- internal/runtime/executor/claude_executor.go | 95 ++++++++++++------- .../runtime/executor/claude_executor_test.go | 91 +++++++++++++++--- 2 files changed, 136 insertions(+), 50 deletions(-) diff --git a/internal/runtime/executor/claude_executor.go b/internal/runtime/executor/claude_executor.go index 235db1f3..7f00ac08 100644 --- a/internal/runtime/executor/claude_executor.go +++ b/internal/runtime/executor/claude_executor.go @@ -65,14 +65,13 @@ var oauthToolRenameMap = map[string]string{ "notebookedit": "NotebookEdit", } -// oauthToolRenameReverseMap is the inverse of oauthToolRenameMap for response decoding. -var oauthToolRenameReverseMap = func() map[string]string { - m := make(map[string]string, len(oauthToolRenameMap)) - for k, v := range oauthToolRenameMap { - m[v] = k - } - return m -}() +// The reverse map is now computed per-request in remapOAuthToolNames so that +// only names the client actually caused us to rewrite are restored on the +// response. A global reverse map — as used previously — corrupted responses +// for clients that sent mixed casing (e.g. Amp CLI sends `Bash` TitleCase +// alongside `glob` lowercase; the request flagged renames via `glob→Glob`, +// then the global reverse map incorrectly rewrote every `Bash` in the +// response to `bash`, causing Amp to reject the tool_use as unknown). // oauthToolsToRemove lists tool names that must be stripped from OAuth requests // even after remapping. Currently empty — all tools are mapped instead of removed. @@ -191,7 +190,7 @@ func (e *ClaudeExecutor) Execute(ctx context.Context, auth *cliproxyauth.Auth, r bodyForTranslation := body bodyForUpstream := body oauthToken := isClaudeOAuthToken(apiKey) - oauthToolNamesRemapped := false + var oauthToolNamesReverseMap map[string]string if oauthToken && !auth.ToolPrefixDisabled() { bodyForUpstream = applyClaudeToolPrefix(body, claudeToolPrefix) } @@ -199,7 +198,7 @@ func (e *ClaudeExecutor) Execute(ctx context.Context, auth *cliproxyauth.Auth, r // tools without official counterparts. This prevents Anthropic from // fingerprinting the request as third-party via tool naming patterns. if oauthToken { - bodyForUpstream, oauthToolNamesRemapped = remapOAuthToolNames(bodyForUpstream) + bodyForUpstream, oauthToolNamesReverseMap = remapOAuthToolNames(bodyForUpstream) } // Enable cch signing by default for OAuth tokens (not just experimental flag). // Claude Code always computes cch; missing or invalid cch is a detectable fingerprint. @@ -297,8 +296,8 @@ func (e *ClaudeExecutor) Execute(ctx context.Context, auth *cliproxyauth.Auth, r data = stripClaudeToolPrefixFromResponse(data, claudeToolPrefix) } // Reverse the OAuth tool name remap so the downstream client sees original names. - if isClaudeOAuthToken(apiKey) && oauthToolNamesRemapped { - data = reverseRemapOAuthToolNames(data) + if isClaudeOAuthToken(apiKey) && len(oauthToolNamesReverseMap) > 0 { + data = reverseRemapOAuthToolNames(data, oauthToolNamesReverseMap) } var param any out := sdktranslator.TranslateNonStream( @@ -373,7 +372,7 @@ func (e *ClaudeExecutor) ExecuteStream(ctx context.Context, auth *cliproxyauth.A bodyForTranslation := body bodyForUpstream := body oauthToken := isClaudeOAuthToken(apiKey) - oauthToolNamesRemapped := false + var oauthToolNamesReverseMap map[string]string if oauthToken && !auth.ToolPrefixDisabled() { bodyForUpstream = applyClaudeToolPrefix(body, claudeToolPrefix) } @@ -381,7 +380,7 @@ func (e *ClaudeExecutor) ExecuteStream(ctx context.Context, auth *cliproxyauth.A // tools without official counterparts. This prevents Anthropic from // fingerprinting the request as third-party via tool naming patterns. if oauthToken { - bodyForUpstream, oauthToolNamesRemapped = remapOAuthToolNames(bodyForUpstream) + bodyForUpstream, oauthToolNamesReverseMap = remapOAuthToolNames(bodyForUpstream) } // Enable cch signing by default for OAuth tokens (not just experimental flag). if oauthToken || experimentalCCHSigningEnabled(e.cfg, auth) { @@ -475,8 +474,8 @@ func (e *ClaudeExecutor) ExecuteStream(ctx context.Context, auth *cliproxyauth.A if isClaudeOAuthToken(apiKey) && !auth.ToolPrefixDisabled() { line = stripClaudeToolPrefixFromStreamLine(line, claudeToolPrefix) } - if isClaudeOAuthToken(apiKey) && oauthToolNamesRemapped { - line = reverseRemapOAuthToolNamesFromStreamLine(line) + if isClaudeOAuthToken(apiKey) && len(oauthToolNamesReverseMap) > 0 { + line = reverseRemapOAuthToolNamesFromStreamLine(line, oauthToolNamesReverseMap) } // Forward the line as-is to preserve SSE format cloned := make([]byte, len(line)+1) @@ -505,8 +504,8 @@ func (e *ClaudeExecutor) ExecuteStream(ctx context.Context, auth *cliproxyauth.A if isClaudeOAuthToken(apiKey) && !auth.ToolPrefixDisabled() { line = stripClaudeToolPrefixFromStreamLine(line, claudeToolPrefix) } - if isClaudeOAuthToken(apiKey) && oauthToolNamesRemapped { - line = reverseRemapOAuthToolNamesFromStreamLine(line) + if isClaudeOAuthToken(apiKey) && len(oauthToolNamesReverseMap) > 0 { + line = reverseRemapOAuthToolNamesFromStreamLine(line, oauthToolNamesReverseMap) } chunks := sdktranslator.TranslateStream( ctx, @@ -1009,8 +1008,25 @@ func isClaudeOAuthToken(apiKey string) bool { // It operates on: tools[].name, tool_choice.name, and all tool_use/tool_reference // references in messages. Removed tools' corresponding tool_result blocks are preserved // (they just become orphaned, which is safe for Claude). -func remapOAuthToolNames(body []byte) ([]byte, bool) { - renamed := false +// +// The returned map is keyed on the upstream (TitleCase) name and maps to the +// client-supplied original name. Callers MUST pass this map to the reverse +// functions so only names the client actually caused us to rewrite are restored +// on the response. A global reverse map (the previous implementation) incorrectly +// rewrote names the client originally sent in TitleCase (e.g. Amp CLI's `Bash`) +// when any OTHER tool in the same request triggered a forward rename (e.g. +// Amp's `glob`→`Glob`), because the global reverse map contained `Bash`→`bash` +// regardless of what the client originally sent. +func remapOAuthToolNames(body []byte) ([]byte, map[string]string) { + reverseMap := make(map[string]string) + recordRename := func(original, renamed string) { + // Preserve the first-seen original name if the same upstream name is + // produced from multiple call sites; they all map back identically. + if _, exists := reverseMap[renamed]; !exists { + reverseMap[renamed] = original + } + } + // 1. Rewrite tools array in a single pass (if present). // IMPORTANT: do not mutate names first and then rebuild from an older gjson // snapshot. gjson results are snapshots of the original bytes; rebuilding from a @@ -1043,7 +1059,7 @@ func remapOAuthToolNames(body []byte) ([]byte, bool) { updatedTool, err := sjson.Set(toolJSON, "name", newName) if err == nil { toolJSON = updatedTool - renamed = true + recordRename(name, newName) } } @@ -1068,7 +1084,7 @@ func remapOAuthToolNames(body []byte) ([]byte, bool) { body, _ = sjson.DeleteBytes(body, "tool_choice") } else if newName, ok := oauthToolRenameMap[tcName]; ok && newName != tcName { body, _ = sjson.SetBytes(body, "tool_choice.name", newName) - renamed = true + recordRename(tcName, newName) } } @@ -1088,14 +1104,14 @@ func remapOAuthToolNames(body []byte) ([]byte, bool) { if newName, ok := oauthToolRenameMap[name]; ok && newName != name { path := fmt.Sprintf("messages.%d.content.%d.name", msgIndex.Int(), contentIndex.Int()) body, _ = sjson.SetBytes(body, path, newName) - renamed = true + recordRename(name, newName) } case "tool_reference": toolName := part.Get("tool_name").String() if newName, ok := oauthToolRenameMap[toolName]; ok && newName != toolName { path := fmt.Sprintf("messages.%d.content.%d.tool_name", msgIndex.Int(), contentIndex.Int()) body, _ = sjson.SetBytes(body, path, newName) - renamed = true + recordRename(toolName, newName) } case "tool_result": // Handle nested tool_reference blocks inside tool_result.content[] @@ -1109,7 +1125,7 @@ func remapOAuthToolNames(body []byte) ([]byte, bool) { if newName, ok := oauthToolRenameMap[nestedToolName]; ok && newName != nestedToolName { nestedPath := fmt.Sprintf("messages.%d.content.%d.content.%d.tool_name", msgIndex.Int(), contentIndex.Int(), nestedIndex.Int()) body, _ = sjson.SetBytes(body, nestedPath, newName) - renamed = true + recordRename(nestedToolName, newName) } } return true @@ -1122,13 +1138,16 @@ func remapOAuthToolNames(body []byte) ([]byte, bool) { }) } - return body, renamed + return body, reverseMap } -// reverseRemapOAuthToolNames reverses the tool name mapping for non-stream responses. -// It maps Claude Code TitleCase names back to the original lowercase names so the -// downstream client receives tool names it recognizes. -func reverseRemapOAuthToolNames(body []byte) []byte { +// reverseRemapOAuthToolNames reverses the tool name mapping for non-stream responses +// using the per-request map produced by remapOAuthToolNames. Names the client sent +// that were NOT forward-renamed are passed through unchanged. +func reverseRemapOAuthToolNames(body []byte, reverseMap map[string]string) []byte { + if len(reverseMap) == 0 { + return body + } content := gjson.GetBytes(body, "content") if !content.Exists() || !content.IsArray() { return body @@ -1138,13 +1157,13 @@ func reverseRemapOAuthToolNames(body []byte) []byte { switch partType { case "tool_use": name := part.Get("name").String() - if origName, ok := oauthToolRenameReverseMap[name]; ok { + if origName, ok := reverseMap[name]; ok { path := fmt.Sprintf("content.%d.name", index.Int()) body, _ = sjson.SetBytes(body, path, origName) } case "tool_reference": toolName := part.Get("tool_name").String() - if origName, ok := oauthToolRenameReverseMap[toolName]; ok { + if origName, ok := reverseMap[toolName]; ok { path := fmt.Sprintf("content.%d.tool_name", index.Int()) body, _ = sjson.SetBytes(body, path, origName) } @@ -1154,8 +1173,12 @@ func reverseRemapOAuthToolNames(body []byte) []byte { return body } -// reverseRemapOAuthToolNamesFromStreamLine reverses the tool name mapping for SSE stream lines. -func reverseRemapOAuthToolNamesFromStreamLine(line []byte) []byte { +// reverseRemapOAuthToolNamesFromStreamLine reverses the tool name mapping for SSE +// stream lines, using the per-request reverseMap produced by remapOAuthToolNames. +func reverseRemapOAuthToolNamesFromStreamLine(line []byte, reverseMap map[string]string) []byte { + if len(reverseMap) == 0 { + return line + } payload := helps.JSONPayload(line) if len(payload) == 0 || !gjson.ValidBytes(payload) { return line @@ -1173,7 +1196,7 @@ func reverseRemapOAuthToolNamesFromStreamLine(line []byte) []byte { switch blockType { case "tool_use": name := contentBlock.Get("name").String() - if origName, ok := oauthToolRenameReverseMap[name]; ok { + if origName, ok := reverseMap[name]; ok { updated, err = sjson.SetBytes(payload, "content_block.name", origName) if err != nil { return line @@ -1183,7 +1206,7 @@ func reverseRemapOAuthToolNamesFromStreamLine(line []byte) []byte { } case "tool_reference": toolName := contentBlock.Get("tool_name").String() - if origName, ok := oauthToolRenameReverseMap[toolName]; ok { + if origName, ok := reverseMap[toolName]; ok { updated, err = sjson.SetBytes(payload, "content_block.tool_name", origName) if err != nil { return line diff --git a/internal/runtime/executor/claude_executor_test.go b/internal/runtime/executor/claude_executor_test.go index c1ce8fc0..0176340b 100644 --- a/internal/runtime/executor/claude_executor_test.go +++ b/internal/runtime/executor/claude_executor_test.go @@ -1989,19 +1989,16 @@ func TestNormalizeClaudeTemperatureForThinking_AfterForcedToolChoiceKeepsOrigina func TestRemapOAuthToolNames_TitleCase_NoReverseNeeded(t *testing.T) { body := []byte(`{"tools":[{"name":"Bash","description":"Run shell commands","input_schema":{"type":"object","properties":{"cmd":{"type":"string"}}}}],"messages":[{"role":"user","content":[{"type":"text","text":"hi"}]}]}`) - out, renamed := remapOAuthToolNames(body) - if renamed { - t.Fatalf("renamed = true, want false") + out, reverseMap := remapOAuthToolNames(body) + if len(reverseMap) != 0 { + t.Fatalf("reverseMap = %v, want empty", reverseMap) } if got := gjson.GetBytes(out, "tools.0.name").String(); got != "Bash" { t.Fatalf("tools.0.name = %q, want %q", got, "Bash") } resp := []byte(`{"content":[{"type":"tool_use","id":"toolu_01","name":"Bash","input":{"cmd":"ls"}}]}`) - reversed := resp - if renamed { - reversed = reverseRemapOAuthToolNames(resp) - } + reversed := reverseRemapOAuthToolNames(resp, reverseMap) if got := gjson.GetBytes(reversed, "content.0.name").String(); got != "Bash" { t.Fatalf("content.0.name = %q, want %q", got, "Bash") } @@ -2010,20 +2007,86 @@ func TestRemapOAuthToolNames_TitleCase_NoReverseNeeded(t *testing.T) { func TestRemapOAuthToolNames_Lowercase_ReverseApplied(t *testing.T) { body := []byte(`{"tools":[{"name":"bash","description":"Run shell commands","input_schema":{"type":"object","properties":{"cmd":{"type":"string"}}}}],"messages":[{"role":"user","content":[{"type":"text","text":"hi"}]}]}`) - out, renamed := remapOAuthToolNames(body) - if !renamed { - t.Fatalf("renamed = false, want true") + out, reverseMap := remapOAuthToolNames(body) + if reverseMap["Bash"] != "bash" { + t.Fatalf("reverseMap = %v, want entry Bash->bash", reverseMap) } if got := gjson.GetBytes(out, "tools.0.name").String(); got != "Bash" { t.Fatalf("tools.0.name = %q, want %q", got, "Bash") } resp := []byte(`{"content":[{"type":"tool_use","id":"toolu_01","name":"Bash","input":{"cmd":"ls"}}]}`) - reversed := resp - if renamed { - reversed = reverseRemapOAuthToolNames(resp) - } + reversed := reverseRemapOAuthToolNames(resp, reverseMap) if got := gjson.GetBytes(reversed, "content.0.name").String(); got != "bash" { t.Fatalf("content.0.name = %q, want %q", got, "bash") } } + +// TestRemapOAuthToolNames_MixedCase_OnlyRenamedToolsReversed is the regression +// test for a case where a single request contains both a TitleCase tool (which +// must pass through unchanged) and a lowercase tool that we forward-rename. +// Before the fix, triggering ANY forward rename caused the reverse pass to +// lowercase every TitleCase tool in the response using a global reverse map, +// corrupting tool names the client originally sent in TitleCase (notably Amp +// CLI's `Bash`, which its registry lookup cannot find as `bash`). +func TestRemapOAuthToolNames_MixedCase_OnlyRenamedToolsReversed(t *testing.T) { + body := []byte(`{"tools":[` + + `{"name":"Bash","input_schema":{"type":"object","properties":{"cmd":{"type":"string"}}}},` + + `{"name":"glob","input_schema":{"type":"object","properties":{"filePattern":{"type":"string"}}}}` + + `]}`) + + out, reverseMap := remapOAuthToolNames(body) + + // Forward: TitleCase `Bash` is not a forward-map key, must pass through. + if got := gjson.GetBytes(out, "tools.0.name").String(); got != "Bash" { + t.Fatalf("tools.0.name = %q, want %q (TitleCase tool must not be renamed)", got, "Bash") + } + // Forward: `glob` is a forward-map key, upstream sees `Glob`. + if got := gjson.GetBytes(out, "tools.1.name").String(); got != "Glob" { + t.Fatalf("tools.1.name = %q, want %q", got, "Glob") + } + + // Reverse map records ONLY the rename that happened. + if len(reverseMap) != 1 || reverseMap["Glob"] != "glob" { + t.Fatalf("reverseMap = %v, want {Glob:glob}", reverseMap) + } + + // Upstream responds with a `Bash` tool_use. Since we never renamed `Bash`, + // reverseRemap MUST leave it alone. + bashResp := []byte(`{"content":[{"type":"tool_use","id":"toolu_01","name":"Bash","input":{"cmd":"ls"}}]}`) + reversed := reverseRemapOAuthToolNames(bashResp, reverseMap) + if got := gjson.GetBytes(reversed, "content.0.name").String(); got != "Bash" { + t.Fatalf("content.0.name = %q, want %q (Bash must be preserved; was never forward-renamed)", got, "Bash") + } + + // Upstream responds with a `Glob` tool_use. Since we renamed `glob`→`Glob`, + // reverseRemap MUST restore the original `glob`. + globResp := []byte(`{"content":[{"type":"tool_use","id":"toolu_02","name":"Glob","input":{"filePattern":"**/*.go"}}]}`) + reversed = reverseRemapOAuthToolNames(globResp, reverseMap) + if got := gjson.GetBytes(reversed, "content.0.name").String(); got != "glob" { + t.Fatalf("content.0.name = %q, want %q (Glob must be restored to client's original `glob`)", got, "glob") + } +} + +// TestReverseRemapOAuthToolNamesFromStreamLine_HonorsPerRequestMap guards the +// SSE streaming code path against the same mixed-case bug. +func TestReverseRemapOAuthToolNamesFromStreamLine_HonorsPerRequestMap(t *testing.T) { + reverseMap := map[string]string{"Glob": "glob"} + + // Bash block was never renamed, must pass through as-is. + bashLine := []byte(`data: {"type":"content_block_start","index":0,"content_block":{"type":"tool_use","id":"toolu_01","name":"Bash","input":{}}}`) + out := reverseRemapOAuthToolNamesFromStreamLine(bashLine, reverseMap) + if !bytes.Contains(out, []byte(`"name":"Bash"`)) { + t.Fatalf("Bash should be preserved, got: %s", string(out)) + } + if bytes.Contains(out, []byte(`"name":"bash"`)) { + t.Fatalf("Bash must not be lowercased, got: %s", string(out)) + } + + // Glob block IS in the reverseMap, must be restored to `glob`. + globLine := []byte(`data: {"type":"content_block_start","index":0,"content_block":{"type":"tool_use","id":"toolu_02","name":"Glob","input":{}}}`) + out = reverseRemapOAuthToolNamesFromStreamLine(globLine, reverseMap) + if !bytes.Contains(out, []byte(`"name":"glob"`)) { + t.Fatalf("Glob should be restored to glob, got: %s", string(out)) + } +} -- 2.51.2 From 03ea4e569fb1df9237d7032469a744737cd30d72 Mon Sep 17 00:00:00 2001 From: edlsh Date: Sat, 18 Apr 2026 12:49:02 -0400 Subject: [PATCH 09/51] perf(claude): pre-allocate reverseMap capacity Address Gemini code review suggestion: the reverseMap can contain at most len(oauthToolRenameMap) entries, so pre-allocating avoids reallocations as entries are added. --- internal/runtime/executor/claude_executor.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/runtime/executor/claude_executor.go b/internal/runtime/executor/claude_executor.go index 7f00ac08..78fa3cd6 100644 --- a/internal/runtime/executor/claude_executor.go +++ b/internal/runtime/executor/claude_executor.go @@ -1018,7 +1018,7 @@ func isClaudeOAuthToken(apiKey string) bool { // Amp's `glob`→`Glob`), because the global reverse map contained `Bash`→`bash` // regardless of what the client originally sent. func remapOAuthToolNames(body []byte) ([]byte, map[string]string) { - reverseMap := make(map[string]string) + reverseMap := make(map[string]string, len(oauthToolRenameMap)) recordRename := func(original, renamed string) { // Preserve the first-seen original name if the same upstream name is // produced from multiple call sites; they all map back identically. -- 2.51.2 From fc1ddf365f489ca465e5fd85334d01303e635f11 Mon Sep 17 00:00:00 2001 From: Enzo Lucchesi Date: Sun, 19 Apr 2026 14:36:25 +0000 Subject: [PATCH 10/51] fix(claude): centralize oauth tool-name transform flow --- internal/runtime/executor/claude_executor.go | 74 +++++++++---------- .../runtime/executor/claude_executor_test.go | 64 ++++++++++++++++ 2 files changed, 100 insertions(+), 38 deletions(-) diff --git a/internal/runtime/executor/claude_executor.go b/internal/runtime/executor/claude_executor.go index 78fa3cd6..2dbff1d3 100644 --- a/internal/runtime/executor/claude_executor.go +++ b/internal/runtime/executor/claude_executor.go @@ -191,14 +191,8 @@ func (e *ClaudeExecutor) Execute(ctx context.Context, auth *cliproxyauth.Auth, r bodyForUpstream := body oauthToken := isClaudeOAuthToken(apiKey) var oauthToolNamesReverseMap map[string]string - if oauthToken && !auth.ToolPrefixDisabled() { - bodyForUpstream = applyClaudeToolPrefix(body, claudeToolPrefix) - } - // Remap third-party tool names to Claude Code equivalents and remove - // tools without official counterparts. This prevents Anthropic from - // fingerprinting the request as third-party via tool naming patterns. if oauthToken { - bodyForUpstream, oauthToolNamesReverseMap = remapOAuthToolNames(bodyForUpstream) + bodyForUpstream, oauthToolNamesReverseMap = prepareClaudeOAuthToolNamesForUpstream(bodyForUpstream, claudeToolPrefix, auth.ToolPrefixDisabled()) } // Enable cch signing by default for OAuth tokens (not just experimental flag). // Claude Code always computes cch; missing or invalid cch is a detectable fingerprint. @@ -292,13 +286,7 @@ func (e *ClaudeExecutor) Execute(ctx context.Context, auth *cliproxyauth.Auth, r } else { reporter.Publish(ctx, helps.ParseClaudeUsage(data)) } - if isClaudeOAuthToken(apiKey) && !auth.ToolPrefixDisabled() { - data = stripClaudeToolPrefixFromResponse(data, claudeToolPrefix) - } - // Reverse the OAuth tool name remap so the downstream client sees original names. - if isClaudeOAuthToken(apiKey) && len(oauthToolNamesReverseMap) > 0 { - data = reverseRemapOAuthToolNames(data, oauthToolNamesReverseMap) - } + data = restoreClaudeOAuthToolNamesFromResponse(data, claudeToolPrefix, auth.ToolPrefixDisabled(), oauthToolNamesReverseMap) var param any out := sdktranslator.TranslateNonStream( ctx, @@ -373,14 +361,8 @@ func (e *ClaudeExecutor) ExecuteStream(ctx context.Context, auth *cliproxyauth.A bodyForUpstream := body oauthToken := isClaudeOAuthToken(apiKey) var oauthToolNamesReverseMap map[string]string - if oauthToken && !auth.ToolPrefixDisabled() { - bodyForUpstream = applyClaudeToolPrefix(body, claudeToolPrefix) - } - // Remap third-party tool names to Claude Code equivalents and remove - // tools without official counterparts. This prevents Anthropic from - // fingerprinting the request as third-party via tool naming patterns. if oauthToken { - bodyForUpstream, oauthToolNamesReverseMap = remapOAuthToolNames(bodyForUpstream) + bodyForUpstream, oauthToolNamesReverseMap = prepareClaudeOAuthToolNamesForUpstream(bodyForUpstream, claudeToolPrefix, auth.ToolPrefixDisabled()) } // Enable cch signing by default for OAuth tokens (not just experimental flag). if oauthToken || experimentalCCHSigningEnabled(e.cfg, auth) { @@ -471,12 +453,7 @@ func (e *ClaudeExecutor) ExecuteStream(ctx context.Context, auth *cliproxyauth.A if detail, ok := helps.ParseClaudeStreamUsage(line); ok { reporter.Publish(ctx, detail) } - if isClaudeOAuthToken(apiKey) && !auth.ToolPrefixDisabled() { - line = stripClaudeToolPrefixFromStreamLine(line, claudeToolPrefix) - } - if isClaudeOAuthToken(apiKey) && len(oauthToolNamesReverseMap) > 0 { - line = reverseRemapOAuthToolNamesFromStreamLine(line, oauthToolNamesReverseMap) - } + line = restoreClaudeOAuthToolNamesFromStreamLine(line, claudeToolPrefix, auth.ToolPrefixDisabled(), oauthToolNamesReverseMap) // Forward the line as-is to preserve SSE format cloned := make([]byte, len(line)+1) copy(cloned, line) @@ -501,12 +478,7 @@ func (e *ClaudeExecutor) ExecuteStream(ctx context.Context, auth *cliproxyauth.A if detail, ok := helps.ParseClaudeStreamUsage(line); ok { reporter.Publish(ctx, detail) } - if isClaudeOAuthToken(apiKey) && !auth.ToolPrefixDisabled() { - line = stripClaudeToolPrefixFromStreamLine(line, claudeToolPrefix) - } - if isClaudeOAuthToken(apiKey) && len(oauthToolNamesReverseMap) > 0 { - line = reverseRemapOAuthToolNamesFromStreamLine(line, oauthToolNamesReverseMap) - } + line = restoreClaudeOAuthToolNamesFromStreamLine(line, claudeToolPrefix, auth.ToolPrefixDisabled(), oauthToolNamesReverseMap) chunks := sdktranslator.TranslateStream( ctx, to, @@ -556,12 +528,8 @@ func (e *ClaudeExecutor) CountTokens(ctx context.Context, auth *cliproxyauth.Aut // Extract betas from body and convert to header (for count_tokens too) var extraBetas []string extraBetas, body = extractAndRemoveBetas(body) - if isClaudeOAuthToken(apiKey) && !auth.ToolPrefixDisabled() { - body = applyClaudeToolPrefix(body, claudeToolPrefix) - } - // Remap tool names for OAuth token requests to avoid third-party fingerprinting. if isClaudeOAuthToken(apiKey) { - body, _ = remapOAuthToolNames(body) + body, _ = prepareClaudeOAuthToolNamesForUpstream(body, claudeToolPrefix, auth.ToolPrefixDisabled()) } url := fmt.Sprintf("%s/v1/messages/count_tokens?beta=true", baseURL) @@ -1001,6 +969,36 @@ func isClaudeOAuthToken(apiKey string) bool { return strings.Contains(apiKey, "sk-ant-oat") } +// prepareClaudeOAuthToolNamesForUpstream applies the Claude OAuth tool-name +// transforms in the same order across request paths. Remap runs before prefixing +// so any future non-empty prefix still composes correctly with the per-request +// reverse map. +func prepareClaudeOAuthToolNamesForUpstream(body []byte, prefix string, prefixDisabled bool) ([]byte, map[string]string) { + body, reverseMap := remapOAuthToolNames(body) + if !prefixDisabled { + body = applyClaudeToolPrefix(body, prefix) + } + return body, reverseMap +} + +// restoreClaudeOAuthToolNamesFromResponse undoes the Claude OAuth tool-name +// transforms for non-stream responses in reverse order. +func restoreClaudeOAuthToolNamesFromResponse(body []byte, prefix string, prefixDisabled bool, reverseMap map[string]string) []byte { + if !prefixDisabled { + body = stripClaudeToolPrefixFromResponse(body, prefix) + } + return reverseRemapOAuthToolNames(body, reverseMap) +} + +// restoreClaudeOAuthToolNamesFromStreamLine undoes the Claude OAuth tool-name +// transforms for SSE lines in reverse order. +func restoreClaudeOAuthToolNamesFromStreamLine(line []byte, prefix string, prefixDisabled bool, reverseMap map[string]string) []byte { + if !prefixDisabled { + line = stripClaudeToolPrefixFromStreamLine(line, prefix) + } + return reverseRemapOAuthToolNamesFromStreamLine(line, reverseMap) +} + // remapOAuthToolNames renames third-party tool names to Claude Code equivalents // and removes tools without an official counterpart. This prevents Anthropic from // fingerprinting the request as a third-party client via tool naming patterns. diff --git a/internal/runtime/executor/claude_executor_test.go b/internal/runtime/executor/claude_executor_test.go index 0176340b..9011be04 100644 --- a/internal/runtime/executor/claude_executor_test.go +++ b/internal/runtime/executor/claude_executor_test.go @@ -2090,3 +2090,67 @@ func TestReverseRemapOAuthToolNamesFromStreamLine_HonorsPerRequestMap(t *testing t.Fatalf("Glob should be restored to glob, got: %s", string(out)) } } + +func TestPrepareClaudeOAuthToolNamesForUpstream_MixedCaseWithPrefix(t *testing.T) { + body := []byte(`{"tools":[` + + `{"name":"Bash","input_schema":{"type":"object","properties":{"cmd":{"type":"string"}}}},` + + `{"name":"glob","input_schema":{"type":"object","properties":{"filePattern":{"type":"string"}}}}` + + `],"messages":[{"role":"assistant","content":[` + + `{"type":"tool_use","id":"toolu_01","name":"Bash","input":{}},` + + `{"type":"tool_use","id":"toolu_02","name":"glob","input":{}}` + + `]}]}`) + + out, reverseMap := prepareClaudeOAuthToolNamesForUpstream(body, "proxy_", false) + + if got := gjson.GetBytes(out, "tools.0.name").String(); got != "proxy_Bash" { + t.Fatalf("tools.0.name = %q, want %q", got, "proxy_Bash") + } + if got := gjson.GetBytes(out, "tools.1.name").String(); got != "proxy_Glob" { + t.Fatalf("tools.1.name = %q, want %q", got, "proxy_Glob") + } + if got := gjson.GetBytes(out, "messages.0.content.0.name").String(); got != "proxy_Bash" { + t.Fatalf("messages.0.content.0.name = %q, want %q", got, "proxy_Bash") + } + if got := gjson.GetBytes(out, "messages.0.content.1.name").String(); got != "proxy_Glob" { + t.Fatalf("messages.0.content.1.name = %q, want %q", got, "proxy_Glob") + } + if len(reverseMap) != 1 || reverseMap["Glob"] != "glob" { + t.Fatalf("reverseMap = %v, want {Glob:glob}", reverseMap) + } +} + +func TestRestoreClaudeOAuthToolNamesFromResponse_MixedCaseWithPrefix(t *testing.T) { + reverseMap := map[string]string{"Glob": "glob"} + resp := []byte(`{"content":[` + + `{"type":"tool_use","id":"toolu_01","name":"proxy_Bash","input":{}},` + + `{"type":"tool_use","id":"toolu_02","name":"proxy_Glob","input":{}}` + + `]}`) + + out := restoreClaudeOAuthToolNamesFromResponse(resp, "proxy_", false, reverseMap) + + if got := gjson.GetBytes(out, "content.0.name").String(); got != "Bash" { + t.Fatalf("content.0.name = %q, want %q", got, "Bash") + } + if got := gjson.GetBytes(out, "content.1.name").String(); got != "glob" { + t.Fatalf("content.1.name = %q, want %q", got, "glob") + } +} + +func TestRestoreClaudeOAuthToolNamesFromStreamLine_MixedCaseWithPrefix(t *testing.T) { + reverseMap := map[string]string{"Glob": "glob"} + + bashLine := []byte(`data: {"type":"content_block_start","index":0,"content_block":{"type":"tool_use","id":"toolu_01","name":"proxy_Bash","input":{}}}`) + out := restoreClaudeOAuthToolNamesFromStreamLine(bashLine, "proxy_", false, reverseMap) + if !bytes.Contains(out, []byte(`"name":"Bash"`)) { + t.Fatalf("Bash should be preserved, got: %s", string(out)) + } + if bytes.Contains(out, []byte(`"name":"bash"`)) { + t.Fatalf("Bash must not be lowercased, got: %s", string(out)) + } + + globLine := []byte(`data: {"type":"content_block_start","index":0,"content_block":{"type":"tool_use","id":"toolu_02","name":"proxy_Glob","input":{}}}`) + out = restoreClaudeOAuthToolNamesFromStreamLine(globLine, "proxy_", false, reverseMap) + if !bytes.Contains(out, []byte(`"name":"glob"`)) { + t.Fatalf("Glob should be restored to glob, got: %s", string(out)) + } +} -- 2.51.2 From 95318ad46dce78e19a74e06872b4901b83985bc9 Mon Sep 17 00:00:00 2001 From: edlsh Date: Mon, 13 Apr 2026 09:39:01 -0400 Subject: [PATCH 11/51] fix(amp): preserve lowercase glob tool name --- internal/api/modules/amp/response_rewriter.go | 50 ++++++++++++++++++ .../api/modules/amp/response_rewriter_test.go | 51 +++++++++++++++++++ 2 files changed, 101 insertions(+) diff --git a/internal/api/modules/amp/response_rewriter.go b/internal/api/modules/amp/response_rewriter.go index 707fe576..895c494e 100644 --- a/internal/api/modules/amp/response_rewriter.go +++ b/internal/api/modules/amp/response_rewriter.go @@ -123,6 +123,52 @@ func (rw *ResponseRewriter) Flush() { var modelFieldPaths = []string{"message.model", "model", "modelVersion", "response.model", "response.modelVersion"} +// ampCanonicalToolNames maps tool names to the exact casing expected by the +// Amp mode tool whitelist (case-sensitive match). +var ampCanonicalToolNames = map[string]string{ + "bash": "Bash", + "read": "Read", + "grep": "Grep", + "glob": "glob", + "task": "Task", + "check": "Check", +} + +// normalizeAmpToolNames fixes tool_use block names to match Amp's canonical casing. +// Some upstream models return lowercase tool names (e.g. "bash" instead of "Bash") +// which causes Amp's case-sensitive mode whitelist to reject them. +func normalizeAmpToolNames(data []byte) []byte { + // Non-streaming: content[].name in tool_use blocks + for index, block := range gjson.GetBytes(data, "content").Array() { + if block.Get("type").String() != "tool_use" { + continue + } + name := block.Get("name").String() + if canonical, ok := ampCanonicalToolNames[strings.ToLower(name)]; ok && name != canonical { + path := fmt.Sprintf("content.%d.name", index) + var err error + data, err = sjson.SetBytes(data, path, canonical) + if err != nil { + log.Warnf("Amp ResponseRewriter: failed to normalize tool name %q to %q: %v", name, canonical, err) + } + } + } + + // Streaming: content_block.name in content_block_start events + if gjson.GetBytes(data, "content_block.type").String() == "tool_use" { + name := gjson.GetBytes(data, "content_block.name").String() + if canonical, ok := ampCanonicalToolNames[strings.ToLower(name)]; ok && name != canonical { + var err error + data, err = sjson.SetBytes(data, "content_block.name", canonical) + if err != nil { + log.Warnf("Amp ResponseRewriter: failed to normalize streaming tool name %q to %q: %v", name, canonical, err) + } + } + } + + return data +} + // ensureAmpSignature injects empty signature fields into tool_use/thinking blocks // in API responses so that the Amp TUI does not crash on P.signature.length. func ensureAmpSignature(data []byte) []byte { @@ -179,6 +225,7 @@ func (rw *ResponseRewriter) suppressAmpThinking(data []byte) []byte { func (rw *ResponseRewriter) rewriteModelInResponse(data []byte) []byte { data = ensureAmpSignature(data) + data = normalizeAmpToolNames(data) data = rw.suppressAmpThinking(data) if len(data) == 0 { return data @@ -278,6 +325,9 @@ func (rw *ResponseRewriter) rewriteStreamEvent(data []byte) []byte { // Inject empty signature where needed data = ensureAmpSignature(data) + // Normalize tool names to canonical casing + data = normalizeAmpToolNames(data) + // Rewrite model name if rw.originalModel != "" { for _, path := range modelFieldPaths { diff --git a/internal/api/modules/amp/response_rewriter_test.go b/internal/api/modules/amp/response_rewriter_test.go index ac95dfc6..a3a350cb 100644 --- a/internal/api/modules/amp/response_rewriter_test.go +++ b/internal/api/modules/amp/response_rewriter_test.go @@ -175,6 +175,57 @@ func TestSanitizeAmpRequestBody_MixedInvalidThinkingAndToolUseSignature(t *testi } } +func TestNormalizeAmpToolNames_NonStreaming(t *testing.T) { + input := []byte(`{"content":[{"type":"tool_use","id":"toolu_01","name":"bash","input":{"cmd":"ls"}},{"type":"tool_use","id":"toolu_02","name":"read","input":{"path":"/tmp"}},{"type":"text","text":"hello"}]}`) + result := normalizeAmpToolNames(input) + + if !contains(result, []byte(`"name":"Bash"`)) { + t.Errorf("expected bash->Bash, got %s", string(result)) + } + if !contains(result, []byte(`"name":"Read"`)) { + t.Errorf("expected read->Read, got %s", string(result)) + } + if contains(result, []byte(`"name":"bash"`)) { + t.Errorf("expected lowercase bash to be replaced, got %s", string(result)) + } +} + +func TestNormalizeAmpToolNames_Streaming(t *testing.T) { + input := []byte(`{"type":"content_block_start","index":1,"content_block":{"type":"tool_use","name":"grep","id":"toolu_01","input":{}}}`) + result := normalizeAmpToolNames(input) + + if !contains(result, []byte(`"name":"Grep"`)) { + t.Errorf("expected grep->Grep in streaming, got %s", string(result)) + } +} + +func TestNormalizeAmpToolNames_AlreadyCorrect(t *testing.T) { + input := []byte(`{"content":[{"type":"tool_use","id":"toolu_01","name":"Bash","input":{"cmd":"ls"}}]}`) + result := normalizeAmpToolNames(input) + + if string(result) != string(input) { + t.Errorf("expected no modification for correctly-cased tool, got %s", string(result)) + } +} + +func TestNormalizeAmpToolNames_GlobPreserved(t *testing.T) { + input := []byte(`{"content":[{"type":"tool_use","id":"toolu_01","name":"glob","input":{"pattern":"*.go"}}]}`) + result := normalizeAmpToolNames(input) + + if string(result) != string(input) { + t.Errorf("expected glob to remain lowercase, got %s", string(result)) + } +} + +func TestNormalizeAmpToolNames_UnknownToolUntouched(t *testing.T) { + input := []byte(`{"content":[{"type":"tool_use","id":"toolu_01","name":"edit_file","input":{"path":"/tmp/x"}}]}`) + result := normalizeAmpToolNames(input) + + if string(result) != string(input) { + t.Errorf("expected no modification for unknown tool, got %s", string(result)) + } +} + func contains(data, substr []byte) bool { for i := 0; i <= len(data)-len(substr); i++ { if string(data[i:i+len(substr)]) == string(substr) { -- 2.51.2 From fd45dece7f027ef198f00cad8c6455a333a36ca4 Mon Sep 17 00:00:00 2001 From: edlsh Date: Fri, 24 Apr 2026 15:15:01 -0400 Subject: [PATCH 12/51] fix(openai): repair empty responses stream output --- .../openai/openai_responses_handlers.go | 122 +++++++++++++++++- .../openai_responses_handlers_stream_test.go | 34 ++++- 2 files changed, 151 insertions(+), 5 deletions(-) diff --git a/sdk/api/handlers/openai/openai_responses_handlers.go b/sdk/api/handlers/openai/openai_responses_handlers.go index 8969ce2f..67c648dc 100644 --- a/sdk/api/handlers/openai/openai_responses_handlers.go +++ b/sdk/api/handlers/openai/openai_responses_handlers.go @@ -13,6 +13,7 @@ import ( "fmt" "io" "net/http" + "sort" "github.com/gin-gonic/gin" . "github.com/router-for-me/CLIProxyAPI/v6/internal/constant" @@ -45,7 +46,9 @@ func writeResponsesSSEChunk(w io.Writer, chunk []byte) { } type responsesSSEFramer struct { - pending []byte + pending []byte + outputItems map[int][]byte + outputOrder []int } func (f *responsesSSEFramer) WriteChunk(w io.Writer, chunk []byte) { @@ -61,7 +64,7 @@ func (f *responsesSSEFramer) WriteChunk(w io.Writer, chunk []byte) { if frameLen == 0 { break } - writeResponsesSSEChunk(w, f.pending[:frameLen]) + f.writeFrame(w, f.pending[:frameLen]) copy(f.pending, f.pending[frameLen:]) f.pending = f.pending[:len(f.pending)-frameLen] } @@ -72,7 +75,7 @@ func (f *responsesSSEFramer) WriteChunk(w io.Writer, chunk []byte) { if len(f.pending) == 0 || !responsesSSECanEmitWithoutDelimiter(f.pending) { return } - writeResponsesSSEChunk(w, f.pending) + f.writeFrame(w, f.pending) f.pending = f.pending[:0] } @@ -88,10 +91,121 @@ func (f *responsesSSEFramer) Flush(w io.Writer) { f.pending = f.pending[:0] return } - writeResponsesSSEChunk(w, f.pending) + f.writeFrame(w, f.pending) f.pending = f.pending[:0] } +func (f *responsesSSEFramer) writeFrame(w io.Writer, frame []byte) { + writeResponsesSSEChunk(w, f.repairFrame(frame)) +} + +func (f *responsesSSEFramer) repairFrame(frame []byte) []byte { + payload, ok := responsesSSEDataPayload(frame) + if !ok || len(payload) == 0 || bytes.Equal(payload, []byte("[DONE]")) || !json.Valid(payload) { + return frame + } + + switch gjson.GetBytes(payload, "type").String() { + case "response.output_item.done": + f.recordOutputItem(payload) + case "response.completed": + repaired := f.repairCompletedPayload(payload) + if !bytes.Equal(repaired, payload) { + return responsesSSEFrameWithData(frame, repaired) + } + } + return frame +} + +func responsesSSEDataPayload(frame []byte) ([]byte, bool) { + var payload []byte + found := false + for _, line := range bytes.Split(frame, []byte("\n")) { + line = bytes.TrimRight(line, "\r") + trimmed := bytes.TrimSpace(line) + if !bytes.HasPrefix(trimmed, []byte("data:")) { + continue + } + data := bytes.TrimSpace(trimmed[len("data:"):]) + if found { + payload = append(payload, '\n') + } + payload = append(payload, data...) + found = true + } + return payload, found +} + +func responsesSSEFrameWithData(frame, payload []byte) []byte { + var out bytes.Buffer + for _, line := range bytes.Split(frame, []byte("\n")) { + line = bytes.TrimRight(line, "\r") + trimmed := bytes.TrimSpace(line) + if len(trimmed) == 0 || bytes.HasPrefix(trimmed, []byte("data:")) { + continue + } + out.Write(line) + out.WriteByte('\n') + } + out.WriteString("data: ") + out.Write(payload) + out.WriteString("\n\n") + return out.Bytes() +} + +func (f *responsesSSEFramer) recordOutputItem(payload []byte) { + item := gjson.GetBytes(payload, "item") + if !item.Exists() || !item.IsObject() || item.Get("type").String() == "" { + return + } + + index := len(f.outputOrder) + if outputIndex := gjson.GetBytes(payload, "output_index"); outputIndex.Exists() { + index = int(outputIndex.Int()) + } + if f.outputItems == nil { + f.outputItems = make(map[int][]byte) + } + if _, exists := f.outputItems[index]; !exists { + f.outputOrder = append(f.outputOrder, index) + } + f.outputItems[index] = append([]byte(nil), item.Raw...) +} + +func (f *responsesSSEFramer) repairCompletedPayload(payload []byte) []byte { + if len(f.outputOrder) == 0 { + return payload + } + output := gjson.GetBytes(payload, "response.output") + if output.Exists() && (!output.IsArray() || len(output.Array()) > 0) { + return payload + } + + var outputJSON bytes.Buffer + outputJSON.WriteByte('[') + indexes := append([]int(nil), f.outputOrder...) + sort.Ints(indexes) + written := 0 + for _, index := range indexes { + item, ok := f.outputItems[index] + if !ok { + continue + } + if written > 0 { + outputJSON.WriteByte(',') + } + outputJSON.Write(item) + written++ + } + outputJSON.WriteByte(']') + + repaired, err := sjson.SetRawBytes(payload, "response.output", outputJSON.Bytes()) + if err != nil { + return payload + } + return repaired +} + func responsesSSEFrameLen(chunk []byte) int { if len(chunk) == 0 { return 0 diff --git a/sdk/api/handlers/openai/openai_responses_handlers_stream_test.go b/sdk/api/handlers/openai/openai_responses_handlers_stream_test.go index ef16fe80..8b3f79e3 100644 --- a/sdk/api/handlers/openai/openai_responses_handlers_stream_test.go +++ b/sdk/api/handlers/openai/openai_responses_handlers_stream_test.go @@ -10,6 +10,7 @@ import ( "github.com/router-for-me/CLIProxyAPI/v6/internal/interfaces" "github.com/router-for-me/CLIProxyAPI/v6/sdk/api/handlers" sdkconfig "github.com/router-for-me/CLIProxyAPI/v6/sdk/config" + "github.com/tidwall/gjson" ) func newResponsesStreamTestHandler(t *testing.T) (*OpenAIResponsesAPIHandler, *httptest.ResponseRecorder, *gin.Context, http.Flusher) { @@ -53,12 +54,43 @@ func TestForwardResponsesStreamSeparatesDataOnlySSEChunks(t *testing.T) { t.Errorf("unexpected first event.\nGot: %q\nWant: %q", parts[0], expectedPart1) } - expectedPart2 := "data: {\"type\":\"response.completed\",\"response\":{\"id\":\"resp-1\",\"output\":[]}}" + expectedPart2 := "data: {\"type\":\"response.completed\",\"response\":{\"id\":\"resp-1\",\"output\":[{\"type\":\"function_call\",\"arguments\":\"{}\"}]}}" if parts[1] != expectedPart2 { t.Errorf("unexpected second event.\nGot: %q\nWant: %q", parts[1], expectedPart2) } } +func TestForwardResponsesStreamRepairsEmptyCompletedOutputFromDoneItems(t *testing.T) { + h, recorder, c, flusher := newResponsesStreamTestHandler(t) + + data := make(chan []byte, 3) + errs := make(chan *interfaces.ErrorMessage) + data <- []byte(`data: {"type":"response.output_item.done","output_index":0,"item":{"type":"reasoning","id":"rs-1","summary":[]}}`) + data <- []byte(`data: {"type":"response.output_item.done","output_index":1,"item":{"type":"function_call","id":"fc-1","call_id":"call-1","name":"shell","arguments":"{\"cmd\":\"pwd\"}","status":"completed"}}`) + data <- []byte(`data: {"type":"response.completed","response":{"id":"resp-1","output":[]}}`) + close(data) + close(errs) + + h.forwardResponsesStream(c, flusher, func(error) {}, data, errs, nil) + + parts := strings.Split(strings.TrimSpace(recorder.Body.String()), "\n\n") + if len(parts) != 3 { + t.Fatalf("expected 3 SSE events, got %d. Body: %q", len(parts), recorder.Body.String()) + } + + payload := strings.TrimPrefix(parts[2], "data: ") + output := gjson.Get(payload, "response.output") + if !output.IsArray() || len(output.Array()) != 2 { + t.Fatalf("expected repaired completed output with 2 items, got %s", output.Raw) + } + if got := gjson.Get(payload, "response.output.1.name").String(); got != "shell" { + t.Fatalf("expected function_call name to be preserved, got %q in %s", got, payload) + } + if got := gjson.Get(payload, "response.output.1.arguments").String(); got != `{"cmd":"pwd"}` { + t.Fatalf("expected function_call arguments to be preserved, got %q in %s", got, payload) + } +} + func TestForwardResponsesStreamReassemblesSplitSSEEventChunks(t *testing.T) { h, recorder, c, flusher := newResponsesStreamTestHandler(t) -- 2.51.2 From d36e70e9dcfd5e4a79f2165a582e76e385423895 Mon Sep 17 00:00:00 2001 From: edlsh Date: Sat, 25 Apr 2026 18:06:00 -0400 Subject: [PATCH 13/51] fix(openai): preserve unindexed response output items --- .../openai/openai_responses_handlers.go | 36 ++++++++++++------- .../openai_responses_handlers_stream_test.go | 31 ++++++++++++++++ 2 files changed, 54 insertions(+), 13 deletions(-) diff --git a/sdk/api/handlers/openai/openai_responses_handlers.go b/sdk/api/handlers/openai/openai_responses_handlers.go index 67c648dc..578977d6 100644 --- a/sdk/api/handlers/openai/openai_responses_handlers.go +++ b/sdk/api/handlers/openai/openai_responses_handlers.go @@ -46,9 +46,10 @@ func writeResponsesSSEChunk(w io.Writer, chunk []byte) { } type responsesSSEFramer struct { - pending []byte - outputItems map[int][]byte - outputOrder []int + pending []byte + outputItems map[int][]byte + outputOrder []int + unindexedOutputItems [][]byte } func (f *responsesSSEFramer) WriteChunk(w io.Writer, chunk []byte) { @@ -159,21 +160,23 @@ func (f *responsesSSEFramer) recordOutputItem(payload []byte) { return } - index := len(f.outputOrder) if outputIndex := gjson.GetBytes(payload, "output_index"); outputIndex.Exists() { - index = int(outputIndex.Int()) - } - if f.outputItems == nil { - f.outputItems = make(map[int][]byte) - } - if _, exists := f.outputItems[index]; !exists { - f.outputOrder = append(f.outputOrder, index) + index := int(outputIndex.Int()) + if f.outputItems == nil { + f.outputItems = make(map[int][]byte) + } + if _, exists := f.outputItems[index]; !exists { + f.outputOrder = append(f.outputOrder, index) + } + f.outputItems[index] = append([]byte(nil), item.Raw...) + return } - f.outputItems[index] = append([]byte(nil), item.Raw...) + + f.unindexedOutputItems = append(f.unindexedOutputItems, append([]byte(nil), item.Raw...)) } func (f *responsesSSEFramer) repairCompletedPayload(payload []byte) []byte { - if len(f.outputOrder) == 0 { + if len(f.outputOrder) == 0 && len(f.unindexedOutputItems) == 0 { return payload } output := gjson.GetBytes(payload, "response.output") @@ -197,6 +200,13 @@ func (f *responsesSSEFramer) repairCompletedPayload(payload []byte) []byte { outputJSON.Write(item) written++ } + for _, item := range f.unindexedOutputItems { + if written > 0 { + outputJSON.WriteByte(',') + } + outputJSON.Write(item) + written++ + } outputJSON.WriteByte(']') repaired, err := sjson.SetRawBytes(payload, "response.output", outputJSON.Bytes()) diff --git a/sdk/api/handlers/openai/openai_responses_handlers_stream_test.go b/sdk/api/handlers/openai/openai_responses_handlers_stream_test.go index 8b3f79e3..3851278f 100644 --- a/sdk/api/handlers/openai/openai_responses_handlers_stream_test.go +++ b/sdk/api/handlers/openai/openai_responses_handlers_stream_test.go @@ -91,6 +91,37 @@ func TestForwardResponsesStreamRepairsEmptyCompletedOutputFromDoneItems(t *testi } } +func TestForwardResponsesStreamRepairsMixedIndexedAndUnindexedDoneItems(t *testing.T) { + h, recorder, c, flusher := newResponsesStreamTestHandler(t) + + data := make(chan []byte, 3) + errs := make(chan *interfaces.ErrorMessage) + data <- []byte(`data: {"type":"response.output_item.done","output_index":1,"item":{"type":"function_call","id":"fc-1","call_id":"call-1","name":"shell","arguments":"{}","status":"completed"}}`) + data <- []byte(`data: {"type":"response.output_item.done","item":{"type":"message","id":"msg-1","role":"assistant","content":[{"type":"output_text","text":"done"}]}}`) + data <- []byte(`data: {"type":"response.completed","response":{"id":"resp-1","output":[]}}`) + close(data) + close(errs) + + h.forwardResponsesStream(c, flusher, func(error) {}, data, errs, nil) + + parts := strings.Split(strings.TrimSpace(recorder.Body.String()), "\n\n") + if len(parts) != 3 { + t.Fatalf("expected 3 SSE events, got %d. Body: %q", len(parts), recorder.Body.String()) + } + + payload := strings.TrimPrefix(parts[2], "data: ") + output := gjson.Get(payload, "response.output") + if !output.IsArray() || len(output.Array()) != 2 { + t.Fatalf("expected repaired completed output with 2 items, got %s", output.Raw) + } + if got := gjson.Get(payload, "response.output.0.name").String(); got != "shell" { + t.Fatalf("expected indexed function_call to be preserved first, got %q in %s", got, payload) + } + if got := gjson.Get(payload, "response.output.1.id").String(); got != "msg-1" { + t.Fatalf("expected unindexed message to be appended, got %q in %s", got, payload) + } +} + func TestForwardResponsesStreamReassemblesSplitSSEEventChunks(t *testing.T) { h, recorder, c, flusher := newResponsesStreamTestHandler(t) -- 2.51.2 From 80eb03709a569a7620b6bba4f1e4f30f8170d3bd Mon Sep 17 00:00:00 2001 From: edlsh Date: Sat, 25 Apr 2026 18:12:27 -0400 Subject: [PATCH 14/51] fix(openai): preserve multiline repaired SSE data --- .../openai/openai_responses_handlers.go | 9 +++-- .../openai_responses_handlers_stream_test.go | 34 +++++++++++++++++++ 2 files changed, 40 insertions(+), 3 deletions(-) diff --git a/sdk/api/handlers/openai/openai_responses_handlers.go b/sdk/api/handlers/openai/openai_responses_handlers.go index 578977d6..8dd1a0a7 100644 --- a/sdk/api/handlers/openai/openai_responses_handlers.go +++ b/sdk/api/handlers/openai/openai_responses_handlers.go @@ -148,9 +148,12 @@ func responsesSSEFrameWithData(frame, payload []byte) []byte { out.Write(line) out.WriteByte('\n') } - out.WriteString("data: ") - out.Write(payload) - out.WriteString("\n\n") + for _, line := range bytes.Split(payload, []byte("\n")) { + out.WriteString("data: ") + out.Write(line) + out.WriteByte('\n') + } + out.WriteByte('\n') return out.Bytes() } diff --git a/sdk/api/handlers/openai/openai_responses_handlers_stream_test.go b/sdk/api/handlers/openai/openai_responses_handlers_stream_test.go index 3851278f..151da9a7 100644 --- a/sdk/api/handlers/openai/openai_responses_handlers_stream_test.go +++ b/sdk/api/handlers/openai/openai_responses_handlers_stream_test.go @@ -122,6 +122,40 @@ func TestForwardResponsesStreamRepairsMixedIndexedAndUnindexedDoneItems(t *testi } } +func TestForwardResponsesStreamRepairsMultilineCompletedOutputAsSSEDataLines(t *testing.T) { + h, recorder, c, flusher := newResponsesStreamTestHandler(t) + + data := make(chan []byte, 2) + errs := make(chan *interfaces.ErrorMessage) + data <- []byte(`data: {"type":"response.output_item.done","item":{"type":"function_call","arguments":"{}"}}`) + data <- []byte("data: {\"type\":\"response.completed\",\ndata: \"response\":{\"id\":\"resp-1\",\"output\":[]}}\n\n") + close(data) + close(errs) + + h.forwardResponsesStream(c, flusher, func(error) {}, data, errs, nil) + + parts := strings.Split(strings.TrimSpace(recorder.Body.String()), "\n\n") + if len(parts) != 2 { + t.Fatalf("expected 2 SSE events, got %d. Body: %q", len(parts), recorder.Body.String()) + } + + completedFrame := []byte(parts[1]) + for _, line := range strings.Split(parts[1], "\n") { + if line != "" && !strings.HasPrefix(line, "data: ") { + t.Fatalf("expected every completed payload line to be an SSE data line, got %q in %q", line, parts[1]) + } + } + + payload, ok := responsesSSEDataPayload(completedFrame) + if !ok { + t.Fatalf("expected completed frame to contain data payload: %q", parts[1]) + } + output := gjson.GetBytes(payload, "response.output") + if !output.IsArray() || len(output.Array()) != 1 { + t.Fatalf("expected repaired completed output with 1 item, got %s from %q", output.Raw, payload) + } +} + func TestForwardResponsesStreamReassemblesSplitSSEEventChunks(t *testing.T) { h, recorder, c, flusher := newResponsesStreamTestHandler(t) -- 2.51.2 From aa70d13f606e96d570c65e4eeb1792913f0a51ce Mon Sep 17 00:00:00 2001 From: C4AL <104809382+C4AL@users.noreply.github.com> Date: Thu, 30 Apr 2026 20:36:37 +0800 Subject: [PATCH 15/51] docs: add CodexCliPlus to README ecosystem list --- README.md | 4 ++++ README_CN.md | 4 ++++ README_JA.md | 4 ++++ 3 files changed, 12 insertions(+) diff --git a/README.md b/README.md index 70f5a044..93ef6f71 100644 --- a/README.md +++ b/README.md @@ -187,6 +187,10 @@ Ready-to-use cross-platform quota inspector for CLIProxyAPI, supporting per-acco Standalone persistence and visualization service for CLIProxyAPI, with periodic data sync, SQLite storage, aggregate APIs, and a built-in dashboard for usage and statistics. +### [CodexCliPlus](https://github.com/C4AL/CodexCliPlus) + +Windows-focused, local-first desktop management platform for Codex CLI built on CLIProxyAPI, focused on simplifying local setup, account and runtime management, and providing a more complete Codex CLI experience for local users. + > [!NOTE] > If you developed a project based on CLIProxyAPI, please open a PR to add it to this list. diff --git a/README_CN.md b/README_CN.md index e08e4ed1..6199095c 100644 --- a/README_CN.md +++ b/README_CN.md @@ -183,6 +183,10 @@ Shadow AI 是一款专为受限环境设计的 AI 辅助工具。提供无窗口 独立的 CLIProxyAPI 使用量持久化与可视化服务,定期同步 CPA 数据,存储到 SQLite,提供聚合 API,并内置使用量分析与统计仪表盘。 +### [CodexCliPlus](https://github.com/C4AL/CodexCliPlus) + +基于 CLIProxyAPI 的 Windows Codex CLI 本地优先桌面管理平台,聚焦简化本机配置、账号与运行状态管理,并为本地用户提供更完整的 Codex CLI 使用体验。 + > [!NOTE] > 如果你开发了基于 CLIProxyAPI 的项目,请提交一个 PR(拉取请求)将其添加到此列表中。 diff --git a/README_JA.md b/README_JA.md index 6360320c..1bb30d48 100644 --- a/README_JA.md +++ b/README_JA.md @@ -182,6 +182,10 @@ CLIProxyAPI向けのすぐに使えるクロスプラットフォームのクォ CLIProxyAPI向けの独立した使用量永続化・可視化サービス。CPAデータを定期同期してSQLiteに保存し、集計APIと、使用量や各種統計を確認できる組み込みダッシュボードを提供します。 +### [CodexCliPlus](https://github.com/C4AL/CodexCliPlus) + +CLIProxyAPIを基盤にしたWindows向けのローカル優先Codex CLIデスクトップ管理プラットフォーム。ローカル設定、アカウント、実行状態の管理を簡素化し、ローカルユーザーにより包括的なCodex CLI体験を提供します。 + > [!NOTE] > CLIProxyAPIをベースにプロジェクトを開発した場合は、PRを送ってこのリストに追加してください。 -- 2.51.2 From 4035abc0cd6b7dabdff49b695256d6d6ceb03245 Mon Sep 17 00:00:00 2001 From: Luis Pater Date: Thu, 30 Apr 2026 23:36:07 +0800 Subject: [PATCH 16/51] refactor(logging): replace gin-specific context handling with generic context-based request metadata utilities - Introduced reusable utilities in `requestmeta` to manage endpoint and response status in request contexts. - Refactored plugins and handlers to use context-based metadata, removing direct dependency on `gin`. - Updated tests to validate new context utilities and replaced `gin`-based context handling. Fixed: #3166 --- internal/logging/requestmeta.go | 62 ++++++++++++++++++++++ internal/redisqueue/plugin.go | 42 ++------------- internal/redisqueue/plugin_test.go | 84 +++++++++++++++++++++++++++--- internal/usage/logger_plugin.go | 28 ++-------- sdk/api/handlers/handlers.go | 26 ++++++++- 5 files changed, 174 insertions(+), 68 deletions(-) create mode 100644 internal/logging/requestmeta.go diff --git a/internal/logging/requestmeta.go b/internal/logging/requestmeta.go new file mode 100644 index 00000000..a28d7c62 --- /dev/null +++ b/internal/logging/requestmeta.go @@ -0,0 +1,62 @@ +package logging + +import ( + "context" + "sync/atomic" +) + +type endpointKey struct{} +type responseStatusKey struct{} + +type responseStatusHolder struct { + status atomic.Int32 +} + +func WithEndpoint(ctx context.Context, endpoint string) context.Context { + if ctx == nil { + ctx = context.Background() + } + return context.WithValue(ctx, endpointKey{}, endpoint) +} + +func GetEndpoint(ctx context.Context) string { + if ctx == nil { + return "" + } + if endpoint, ok := ctx.Value(endpointKey{}).(string); ok { + return endpoint + } + return "" +} + +func WithResponseStatusHolder(ctx context.Context) context.Context { + if ctx == nil { + ctx = context.Background() + } + if holder, ok := ctx.Value(responseStatusKey{}).(*responseStatusHolder); ok && holder != nil { + return ctx + } + return context.WithValue(ctx, responseStatusKey{}, &responseStatusHolder{}) +} + +func SetResponseStatus(ctx context.Context, status int) { + if ctx == nil || status <= 0 { + return + } + holder, ok := ctx.Value(responseStatusKey{}).(*responseStatusHolder) + if !ok || holder == nil { + return + } + holder.status.Store(int32(status)) +} + +func GetResponseStatus(ctx context.Context) int { + if ctx == nil { + return 0 + } + holder, ok := ctx.Value(responseStatusKey{}).(*responseStatusHolder) + if !ok || holder == nil { + return 0 + } + return int(holder.status.Load()) +} diff --git a/internal/redisqueue/plugin.go b/internal/redisqueue/plugin.go index a805e5da..39739dbe 100644 --- a/internal/redisqueue/plugin.go +++ b/internal/redisqueue/plugin.go @@ -3,11 +3,9 @@ package redisqueue import ( "context" "encoding/json" - "net/http" "strings" "time" - "github.com/gin-gonic/gin" internallogging "github.com/router-for-me/CLIProxyAPI/v6/internal/logging" internalusage "github.com/router-for-me/CLIProxyAPI/v6/internal/usage" coreusage "github.com/router-for-me/CLIProxyAPI/v6/sdk/cliproxy/usage" @@ -46,11 +44,6 @@ func (p *usageQueuePlugin) HandleUsage(ctx context.Context, record coreusage.Rec } apiKey := strings.TrimSpace(record.APIKey) requestID := strings.TrimSpace(internallogging.GetRequestID(ctx)) - if requestID == "" { - if ginCtx, ok := ctx.Value("gin").(*gin.Context); ok && ginCtx != nil { - requestID = strings.TrimSpace(internallogging.GetGinRequestID(ginCtx)) - } - } tokens := internalusage.TokenStats{ InputTokens: record.Detail.InputTokens, @@ -106,40 +99,15 @@ type queuedUsageDetail struct { } func resolveSuccess(ctx context.Context) bool { - if ctx == nil { - return true - } - ginCtx, ok := ctx.Value("gin").(*gin.Context) - if !ok || ginCtx == nil { - return true - } - status := ginCtx.Writer.Status() + status := internallogging.GetResponseStatus(ctx) if status == 0 { return true } - return status < http.StatusBadRequest + return status < httpStatusBadRequest } func resolveEndpoint(ctx context.Context) string { - if ctx == nil { - return "" - } - ginCtx, ok := ctx.Value("gin").(*gin.Context) - if !ok || ginCtx == nil || ginCtx.Request == nil { - return "" - } - - path := strings.TrimSpace(ginCtx.FullPath()) - if path == "" && ginCtx.Request.URL != nil { - path = strings.TrimSpace(ginCtx.Request.URL.Path) - } - if path == "" { - return "" - } - - method := strings.TrimSpace(ginCtx.Request.Method) - if method == "" { - return path - } - return method + " " + path + return strings.TrimSpace(internallogging.GetEndpoint(ctx)) } + +const httpStatusBadRequest = 400 diff --git a/internal/redisqueue/plugin_test.go b/internal/redisqueue/plugin_test.go index 907b8aee..1e8bda48 100644 --- a/internal/redisqueue/plugin_test.go +++ b/internal/redisqueue/plugin_test.go @@ -16,9 +16,10 @@ import ( func TestUsageQueuePluginPayloadIncludesStableFieldsAndSuccess(t *testing.T) { withEnabledQueue(t, func() { - ginCtx := newTestGinContext(t, http.MethodPost, "/v1/chat/completions", http.StatusOK) - internallogging.SetGinRequestID(ginCtx, "gin-request-id-ignored") - ctx := context.WithValue(internallogging.WithRequestID(context.Background(), "ctx-request-id"), "gin", ginCtx) + ctx := internallogging.WithRequestID(context.Background(), "ctx-request-id") + ctx = internallogging.WithEndpoint(ctx, "POST /v1/chat/completions") + ctx = internallogging.WithResponseStatusHolder(ctx) + internallogging.SetResponseStatus(ctx, http.StatusOK) plugin := &usageQueuePlugin{} plugin.HandleUsage(ctx, coreusage.Record{ @@ -49,9 +50,10 @@ func TestUsageQueuePluginPayloadIncludesStableFieldsAndSuccess(t *testing.T) { func TestUsageQueuePluginPayloadIncludesStableFieldsAndFailureAndGinRequestID(t *testing.T) { withEnabledQueue(t, func() { - ginCtx := newTestGinContext(t, http.MethodGet, "/v1/responses", http.StatusInternalServerError) - internallogging.SetGinRequestID(ginCtx, "gin-request-id") - ctx := context.WithValue(context.Background(), "gin", ginCtx) + ctx := internallogging.WithRequestID(context.Background(), "gin-request-id") + ctx = internallogging.WithEndpoint(ctx, "GET /v1/responses") + ctx = internallogging.WithResponseStatusHolder(ctx) + internallogging.SetResponseStatus(ctx, http.StatusInternalServerError) plugin := &usageQueuePlugin{} plugin.HandleUsage(ctx, coreusage.Record{ @@ -80,6 +82,47 @@ func TestUsageQueuePluginPayloadIncludesStableFieldsAndFailureAndGinRequestID(t }) } +func TestUsageQueuePluginAsyncIgnoresRecycledGinContext(t *testing.T) { + withEnabledQueue(t, func() { + ginCtx := newTestGinContext(t, http.MethodPost, "/v1/chat/completions", http.StatusOK) + ctx := context.WithValue(context.Background(), "gin", ginCtx) + ctx = internallogging.WithRequestID(ctx, "ctx-request-id") + ctx = internallogging.WithEndpoint(ctx, "POST /v1/chat/completions") + ctx = internallogging.WithResponseStatusHolder(ctx) + internallogging.SetResponseStatus(ctx, http.StatusInternalServerError) + + mgr := coreusage.NewManager(16) + defer mgr.Stop() + + mgr.Register(pluginFunc(func(_ context.Context, _ coreusage.Record) { + ginCtx.Request = httptest.NewRequest(http.MethodGet, "http://example.com/v1/responses", nil) + ginCtx.Status(http.StatusOK) + })) + mgr.Register(&usageQueuePlugin{}) + + mgr.Publish(ctx, coreusage.Record{ + Provider: "openai", + Model: "gpt-5.4", + APIKey: "test-key", + AuthIndex: "0", + AuthType: "apikey", + Source: "user@example.com", + RequestedAt: time.Date(2026, 4, 25, 0, 0, 0, 0, time.UTC), + Latency: 1500 * time.Millisecond, + Detail: coreusage.Detail{ + InputTokens: 10, + OutputTokens: 20, + TotalTokens: 30, + }, + }) + + payload := waitForSinglePayload(t, 2*time.Second) + requireStringField(t, payload, "endpoint", "POST /v1/chat/completions") + requireStringField(t, payload, "request_id", "ctx-request-id") + requireBoolField(t, payload, "failed", true) + }) +} + func withEnabledQueue(t *testing.T, fn func()) { t.Helper() @@ -127,6 +170,29 @@ func popSinglePayload(t *testing.T) map[string]json.RawMessage { return payload } +func waitForSinglePayload(t *testing.T, timeout time.Duration) map[string]json.RawMessage { + t.Helper() + + deadline := time.Now().Add(timeout) + for time.Now().Before(deadline) { + items := PopOldest(10) + if len(items) == 0 { + time.Sleep(10 * time.Millisecond) + continue + } + if len(items) != 1 { + t.Fatalf("PopOldest() items = %d, want 1", len(items)) + } + var payload map[string]json.RawMessage + if err := json.Unmarshal(items[0], &payload); err != nil { + t.Fatalf("unmarshal payload: %v", err) + } + return payload + } + t.Fatalf("timeout waiting for queued payload") + return nil +} + func requireStringField(t *testing.T, payload map[string]json.RawMessage, key, want string) { t.Helper() @@ -143,6 +209,12 @@ func requireStringField(t *testing.T, payload map[string]json.RawMessage, key, w } } +type pluginFunc func(context.Context, coreusage.Record) + +func (fn pluginFunc) HandleUsage(ctx context.Context, record coreusage.Record) { + fn(ctx, record) +} + func requireBoolField(t *testing.T, payload map[string]json.RawMessage, key string, want bool) { t.Helper() diff --git a/internal/usage/logger_plugin.go b/internal/usage/logger_plugin.go index 803d005e..9d59de4f 100644 --- a/internal/usage/logger_plugin.go +++ b/internal/usage/logger_plugin.go @@ -11,7 +11,7 @@ import ( "sync/atomic" "time" - "github.com/gin-gonic/gin" + internallogging "github.com/router-for-me/CLIProxyAPI/v6/internal/logging" coreusage "github.com/router-for-me/CLIProxyAPI/v6/sdk/cliproxy/usage" ) @@ -401,21 +401,8 @@ func dedupKey(apiName, modelName string, detail RequestDetail) string { func resolveAPIIdentifier(ctx context.Context, record coreusage.Record) string { if ctx != nil { - if ginCtx, ok := ctx.Value("gin").(*gin.Context); ok && ginCtx != nil { - path := ginCtx.FullPath() - if path == "" && ginCtx.Request != nil { - path = ginCtx.Request.URL.Path - } - method := "" - if ginCtx.Request != nil { - method = ginCtx.Request.Method - } - if path != "" { - if method != "" { - return method + " " + path - } - return path - } + if endpoint := strings.TrimSpace(internallogging.GetEndpoint(ctx)); endpoint != "" { + return endpoint } } if record.Provider != "" { @@ -425,14 +412,7 @@ func resolveAPIIdentifier(ctx context.Context, record coreusage.Record) string { } func resolveSuccess(ctx context.Context) bool { - if ctx == nil { - return true - } - ginCtx, ok := ctx.Value("gin").(*gin.Context) - if !ok || ginCtx == nil { - return true - } - status := ginCtx.Writer.Status() + status := internallogging.GetResponseStatus(ctx) if status == 0 { return true } diff --git a/sdk/api/handlers/handlers.go b/sdk/api/handlers/handlers.go index 22f7c41a..52b2a4fd 100644 --- a/sdk/api/handlers/handlers.go +++ b/sdk/api/handlers/handlers.go @@ -375,11 +375,32 @@ func (h *BaseAPIHandler) GetContextWithCancel(handler interfaces.APIHandler, c * if requestCtx != nil && logging.GetRequestID(parentCtx) == "" { if requestID := logging.GetRequestID(requestCtx); requestID != "" { parentCtx = logging.WithRequestID(parentCtx, requestID) - } else if requestID := logging.GetGinRequestID(c); requestID != "" { + } else if requestID = logging.GetGinRequestID(c); requestID != "" { parentCtx = logging.WithRequestID(parentCtx, requestID) } } newCtx, cancel := context.WithCancel(parentCtx) + + endpoint := "" + if c != nil && c.Request != nil { + path := strings.TrimSpace(c.FullPath()) + if path == "" && c.Request.URL != nil { + path = strings.TrimSpace(c.Request.URL.Path) + } + if path != "" { + method := strings.TrimSpace(c.Request.Method) + if method != "" { + endpoint = method + " " + path + } else { + endpoint = path + } + } + } + if endpoint != "" { + newCtx = logging.WithEndpoint(newCtx, endpoint) + } + newCtx = logging.WithResponseStatusHolder(newCtx) + cancelCtx := newCtx if requestCtx != nil && requestCtx != parentCtx { go func() { @@ -393,6 +414,9 @@ func (h *BaseAPIHandler) GetContextWithCancel(handler interfaces.APIHandler, c * newCtx = context.WithValue(newCtx, "gin", c) newCtx = context.WithValue(newCtx, "handler", handler) return newCtx, func(params ...interface{}) { + if c != nil { + logging.SetResponseStatus(cancelCtx, c.Writer.Status()) + } if h.Cfg.RequestLog && len(params) == 1 { if existing, exists := c.Get("API_RESPONSE"); exists { if existingBytes, ok := existing.([]byte); ok && len(bytes.TrimSpace(existingBytes)) > 0 { -- 2.51.2 From 61879190002c267d70ad0dd3992c817ad0014b23 Mon Sep 17 00:00:00 2001 From: Luis Pater Date: Fri, 1 May 2026 22:55:22 +0800 Subject: [PATCH 17/51] feat: add support for recent request tracking in auth records - Implemented `RecentRequestsSnapshot` in `Auth` to capture bucketed recent request data. - Added new fields and methods to `Auth` for tracking request success and failure counts over time. - Updated `/v0/management/auth-files` response to include recent request data for each auth record. - Introduced unit tests to validate request tracking and snapshot generation logic. --- .../api/handlers/management/auth_files.go | 1 + .../auth_files_recent_requests_test.go | 87 ++++++++++++++++++ sdk/cliproxy/auth/conductor.go | 1 + .../auth/conductor_recent_requests_test.go | 44 ++++++++++ sdk/cliproxy/auth/types.go | 88 ++++++++++++++++++- sdk/cliproxy/auth/types_test.go | 75 +++++++++++++++- 6 files changed, 294 insertions(+), 2 deletions(-) create mode 100644 internal/api/handlers/management/auth_files_recent_requests_test.go create mode 100644 sdk/cliproxy/auth/conductor_recent_requests_test.go diff --git a/internal/api/handlers/management/auth_files.go b/internal/api/handlers/management/auth_files.go index 8f7b8c5e..2bcfaac4 100644 --- a/internal/api/handlers/management/auth_files.go +++ b/internal/api/handlers/management/auth_files.go @@ -388,6 +388,7 @@ func (h *Handler) buildAuthFileEntry(auth *coreauth.Auth) gin.H { "source": "memory", "size": int64(0), } + entry["recent_requests"] = auth.RecentRequestsSnapshot(time.Now()) if email := authEmail(auth); email != "" { entry["email"] = email } diff --git a/internal/api/handlers/management/auth_files_recent_requests_test.go b/internal/api/handlers/management/auth_files_recent_requests_test.go new file mode 100644 index 00000000..fd28ca1d --- /dev/null +++ b/internal/api/handlers/management/auth_files_recent_requests_test.go @@ -0,0 +1,87 @@ +package management + +import ( + "context" + "encoding/json" + "net/http" + "net/http/httptest" + "testing" + + "github.com/gin-gonic/gin" + "github.com/router-for-me/CLIProxyAPI/v6/internal/config" + coreauth "github.com/router-for-me/CLIProxyAPI/v6/sdk/cliproxy/auth" +) + +func TestListAuthFiles_IncludesRecentRequestsBuckets(t *testing.T) { + t.Setenv("MANAGEMENT_PASSWORD", "") + gin.SetMode(gin.TestMode) + + manager := coreauth.NewManager(nil, nil, nil) + record := &coreauth.Auth{ + ID: "runtime-only-auth-1", + Provider: "codex", + Attributes: map[string]string{ + "runtime_only": "true", + }, + Metadata: map[string]any{ + "type": "codex", + }, + } + if _, errRegister := manager.Register(context.Background(), record); errRegister != nil { + t.Fatalf("failed to register auth record: %v", errRegister) + } + + h := NewHandlerWithoutConfigFilePath(&config.Config{AuthDir: t.TempDir()}, manager) + h.tokenStore = &memoryAuthStore{} + + rec := httptest.NewRecorder() + ginCtx, _ := gin.CreateTestContext(rec) + req := httptest.NewRequest(http.MethodGet, "/v0/management/auth-files", nil) + ginCtx.Request = req + + h.ListAuthFiles(ginCtx) + + if rec.Code != http.StatusOK { + t.Fatalf("expected list status %d, got %d with body %s", http.StatusOK, rec.Code, rec.Body.String()) + } + + var payload map[string]any + if errUnmarshal := json.Unmarshal(rec.Body.Bytes(), &payload); errUnmarshal != nil { + t.Fatalf("failed to decode list payload: %v", errUnmarshal) + } + filesRaw, ok := payload["files"].([]any) + if !ok { + t.Fatalf("expected files array, payload: %#v", payload) + } + if len(filesRaw) != 1 { + t.Fatalf("expected 1 auth entry, got %d", len(filesRaw)) + } + + fileEntry, ok := filesRaw[0].(map[string]any) + if !ok { + t.Fatalf("expected file entry object, got %#v", filesRaw[0]) + } + + recentRaw, ok := fileEntry["recent_requests"].([]any) + if !ok { + t.Fatalf("expected recent_requests array, got %#v", fileEntry["recent_requests"]) + } + if len(recentRaw) != 20 { + t.Fatalf("expected 20 recent_requests buckets, got %d", len(recentRaw)) + } + for idx, item := range recentRaw { + bucket, ok := item.(map[string]any) + if !ok { + t.Fatalf("expected bucket object at %d, got %#v", idx, item) + } + if _, ok := bucket["time"].(string); !ok { + t.Fatalf("expected bucket time string at %d, got %#v", idx, bucket["time"]) + } + if _, ok := bucket["success"].(float64); !ok { + t.Fatalf("expected bucket success number at %d, got %#v", idx, bucket["success"]) + } + if _, ok := bucket["failed"].(float64); !ok { + t.Fatalf("expected bucket failed number at %d, got %#v", idx, bucket["failed"]) + } + } +} diff --git a/sdk/cliproxy/auth/conductor.go b/sdk/cliproxy/auth/conductor.go index 6571518d..61a0e413 100644 --- a/sdk/cliproxy/auth/conductor.go +++ b/sdk/cliproxy/auth/conductor.go @@ -2021,6 +2021,7 @@ func (m *Manager) MarkResult(ctx context.Context, result Result) { m.mu.Lock() if auth, ok := m.auths[result.AuthID]; ok && auth != nil { now := time.Now() + auth.recordRecentRequest(now, result.Success) if result.Success { if result.Model != "" { diff --git a/sdk/cliproxy/auth/conductor_recent_requests_test.go b/sdk/cliproxy/auth/conductor_recent_requests_test.go new file mode 100644 index 00000000..3f5a7212 --- /dev/null +++ b/sdk/cliproxy/auth/conductor_recent_requests_test.go @@ -0,0 +1,44 @@ +package auth + +import ( + "context" + "testing" + "time" +) + +func TestManagerMarkResultRecordsRecentRequests(t *testing.T) { + mgr := NewManager(nil, nil, nil) + auth := &Auth{ + ID: "auth-1", + Provider: "antigravity", + Attributes: map[string]string{ + "runtime_only": "true", + }, + Metadata: map[string]any{ + "type": "antigravity", + }, + } + + if _, err := mgr.Register(WithSkipPersist(context.Background()), auth); err != nil { + t.Fatalf("Register returned error: %v", err) + } + + mgr.MarkResult(context.Background(), Result{AuthID: "auth-1", Provider: "antigravity", Model: "gpt-5", Success: true}) + mgr.MarkResult(context.Background(), Result{AuthID: "auth-1", Provider: "antigravity", Model: "gpt-5", Success: false}) + + gotAuth, ok := mgr.GetByID("auth-1") + if !ok || gotAuth == nil { + t.Fatalf("GetByID returned ok=%v auth=%v", ok, gotAuth) + } + + snapshot := gotAuth.RecentRequestsSnapshot(time.Now()) + var successTotal int64 + var failedTotal int64 + for _, bucket := range snapshot { + successTotal += bucket.Success + failedTotal += bucket.Failed + } + if successTotal != 1 || failedTotal != 1 { + t.Fatalf("totals = success=%d failed=%d, want 1/1", successTotal, failedTotal) + } +} diff --git a/sdk/cliproxy/auth/types.go b/sdk/cliproxy/auth/types.go index f30f4dc0..93dd3881 100644 --- a/sdk/cliproxy/auth/types.go +++ b/sdk/cliproxy/auth/types.go @@ -92,7 +92,29 @@ type Auth struct { // Runtime carries non-serialisable data used during execution (in-memory only). Runtime any `json:"-"` - indexAssigned bool `json:"-"` + recentRequests recentRequestRing `json:"-"` + indexAssigned bool `json:"-"` +} + +const ( + recentRequestBucketSeconds int64 = 10 * 60 + recentRequestBucketCount = 20 +) + +type recentRequestBucket struct { + bucketID int64 + success int64 + failed int64 +} + +type recentRequestRing struct { + buckets [recentRequestBucketCount]recentRequestBucket +} + +type RecentRequestBucket struct { + Time string `json:"time"` + Success int64 `json:"success"` + Failed int64 `json:"failed"` } // QuotaState contains limiter tracking data for a credential. @@ -125,6 +147,70 @@ type ModelState struct { UpdatedAt time.Time `json:"updated_at"` } +func recentRequestBucketID(now time.Time) int64 { + if now.IsZero() { + return 0 + } + return now.Unix() / recentRequestBucketSeconds +} + +func recentRequestBucketIndex(bucketID int64) int { + mod := bucketID % int64(recentRequestBucketCount) + if mod < 0 { + mod += int64(recentRequestBucketCount) + } + return int(mod) +} + +func formatRecentRequestBucketLabel(bucketID int64) string { + start := time.Unix(bucketID*recentRequestBucketSeconds, 0).In(time.Local) + end := start.Add(10 * time.Minute) + return start.Format("15:04") + "-" + end.Format("15:04") +} + +func (a *Auth) recordRecentRequest(now time.Time, success bool) { + if a == nil { + return + } + bucketID := recentRequestBucketID(now) + idx := recentRequestBucketIndex(bucketID) + bucket := &a.recentRequests.buckets[idx] + if bucket.bucketID != bucketID { + bucket.bucketID = bucketID + bucket.success = 0 + bucket.failed = 0 + } + if success { + bucket.success++ + return + } + bucket.failed++ +} + +func (a *Auth) RecentRequestsSnapshot(now time.Time) []RecentRequestBucket { + out := make([]RecentRequestBucket, 0, recentRequestBucketCount) + if a == nil { + return out + } + + currentBucketID := recentRequestBucketID(now) + for i := recentRequestBucketCount - 1; i >= 0; i-- { + bucketID := currentBucketID - int64(i) + idx := recentRequestBucketIndex(bucketID) + bucket := a.recentRequests.buckets[idx] + entry := RecentRequestBucket{ + Time: formatRecentRequestBucketLabel(bucketID), + } + if bucket.bucketID == bucketID { + entry.Success = bucket.success + entry.Failed = bucket.failed + } + out = append(out, entry) + } + + return out +} + // Clone shallow copies the Auth structure, duplicating maps to avoid accidental mutation. func (a *Auth) Clone() *Auth { if a == nil { diff --git a/sdk/cliproxy/auth/types_test.go b/sdk/cliproxy/auth/types_test.go index e7029385..06836da1 100644 --- a/sdk/cliproxy/auth/types_test.go +++ b/sdk/cliproxy/auth/types_test.go @@ -1,6 +1,10 @@ package auth -import "testing" +import ( + "strings" + "testing" + "time" +) func TestToolPrefixDisabled(t *testing.T) { var a *Auth @@ -96,3 +100,72 @@ func TestEnsureIndexUsesCredentialIdentity(t *testing.T) { t.Fatalf("duplicate config entries should be separated by source-derived seed, got %q", geminiIndex) } } + +func TestRecentRequestsSnapshotEmptyReturnsTwentyBuckets(t *testing.T) { + now := time.Unix(1_700_000_000, 0).In(time.Local) + a := &Auth{} + + got := a.RecentRequestsSnapshot(now) + if len(got) != recentRequestBucketCount { + t.Fatalf("len = %d, want %d", len(got), recentRequestBucketCount) + } + + currentBucketID := now.Unix() / recentRequestBucketSeconds + baseBucketID := currentBucketID - int64(recentRequestBucketCount-1) + for i, bucket := range got { + if bucket.Success != 0 || bucket.Failed != 0 { + t.Fatalf("bucket[%d] counts = %d/%d, want 0/0", i, bucket.Success, bucket.Failed) + } + if strings.TrimSpace(bucket.Time) == "" { + t.Fatalf("bucket[%d] time label is empty", i) + } + expectedBucketID := baseBucketID + int64(i) + start := time.Unix(expectedBucketID*recentRequestBucketSeconds, 0).In(time.Local) + end := start.Add(10 * time.Minute) + expected := start.Format("15:04") + "-" + end.Format("15:04") + if bucket.Time != expected { + t.Fatalf("bucket[%d] time = %q, want %q", i, bucket.Time, expected) + } + } +} + +func TestRecentRequestsSnapshotIncludesCounts(t *testing.T) { + now := time.Unix(1_700_000_000, 0).In(time.Local) + a := &Auth{} + + a.recordRecentRequest(now, true) + a.recordRecentRequest(now, false) + + got := a.RecentRequestsSnapshot(now) + if len(got) != recentRequestBucketCount { + t.Fatalf("len = %d, want %d", len(got), recentRequestBucketCount) + } + + newest := got[len(got)-1] + if newest.Success != 1 || newest.Failed != 1 { + t.Fatalf("newest bucket = success=%d failed=%d, want 1/1", newest.Success, newest.Failed) + } +} + +func TestRecentRequestsSnapshotBucketAdvanceMovesCounts(t *testing.T) { + now := time.Unix(1_700_000_000, 0).In(time.Local) + next := now.Add(10 * time.Minute) + a := &Auth{} + + a.recordRecentRequest(now, true) + a.recordRecentRequest(next, false) + + got := a.RecentRequestsSnapshot(next) + if len(got) != recentRequestBucketCount { + t.Fatalf("len = %d, want %d", len(got), recentRequestBucketCount) + } + + secondNewest := got[len(got)-2] + newest := got[len(got)-1] + if secondNewest.Success != 1 || secondNewest.Failed != 0 { + t.Fatalf("second newest bucket = success=%d failed=%d, want 1/0", secondNewest.Success, secondNewest.Failed) + } + if newest.Success != 0 || newest.Failed != 1 { + t.Fatalf("newest bucket = success=%d failed=%d, want 0/1", newest.Success, newest.Failed) + } +} -- 2.51.2 From b0dc9df887ef9f8fd9fad5bd9e4ebd639d6de8f3 Mon Sep 17 00:00:00 2001 From: Luis Pater Date: Fri, 1 May 2026 23:34:18 +0800 Subject: [PATCH 18/51] feat: add API key usage endpoint with provider and key grouping - Implemented `GetAPIKeyUsage` to expose recent request data grouped by provider and API key. - Added supporting function `mergeRecentRequestBuckets` for bucket aggregation. - Registered new endpoint `/v0/management/api-key-usage` in the management API. - Included extensive unit tests for provider and key-based grouping validation. - Updated `formatRecentRequestBucketLabel` to support configurable bucket duration. --- .../api/handlers/management/api_key_usage.go | 86 ++++++++++++++++++ .../handlers/management/api_key_usage_test.go | 87 +++++++++++++++++++ internal/api/server.go | 1 + sdk/cliproxy/auth/types.go | 2 +- 4 files changed, 175 insertions(+), 1 deletion(-) create mode 100644 internal/api/handlers/management/api_key_usage.go create mode 100644 internal/api/handlers/management/api_key_usage_test.go diff --git a/internal/api/handlers/management/api_key_usage.go b/internal/api/handlers/management/api_key_usage.go new file mode 100644 index 00000000..599fbad9 --- /dev/null +++ b/internal/api/handlers/management/api_key_usage.go @@ -0,0 +1,86 @@ +package management + +import ( + "net/http" + "strings" + "time" + + "github.com/gin-gonic/gin" + coreauth "github.com/router-for-me/CLIProxyAPI/v6/sdk/cliproxy/auth" +) + +func mergeRecentRequestBuckets(dst, src []coreauth.RecentRequestBucket) []coreauth.RecentRequestBucket { + if len(dst) == 0 { + return src + } + if len(src) == 0 { + return dst + } + if len(dst) != len(src) { + n := len(dst) + if len(src) < n { + n = len(src) + } + for i := 0; i < n; i++ { + dst[i].Success += src[i].Success + dst[i].Failed += src[i].Failed + } + return dst + } + for i := range dst { + dst[i].Success += src[i].Success + dst[i].Failed += src[i].Failed + } + return dst +} + +// GetAPIKeyUsage returns recent request buckets for all in-memory api_key auths, +// grouped by provider and keyed by the raw api-key value. +func (h *Handler) GetAPIKeyUsage(c *gin.Context) { + if h == nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": "handler not initialized"}) + return + } + + h.mu.Lock() + manager := h.authManager + h.mu.Unlock() + if manager == nil { + c.JSON(http.StatusServiceUnavailable, gin.H{"error": "core auth manager unavailable"}) + return + } + + now := time.Now() + out := make(map[string]map[string][]coreauth.RecentRequestBucket) + for _, auth := range manager.List() { + if auth == nil { + continue + } + kind, apiKey := auth.AccountInfo() + if !strings.EqualFold(strings.TrimSpace(kind), "api_key") { + continue + } + apiKey = strings.TrimSpace(apiKey) + if apiKey == "" { + continue + } + provider := strings.ToLower(strings.TrimSpace(auth.Provider)) + if provider == "" { + provider = "unknown" + } + + recent := auth.RecentRequestsSnapshot(now) + providerBucket, ok := out[provider] + if !ok { + providerBucket = make(map[string][]coreauth.RecentRequestBucket) + out[provider] = providerBucket + } + if existing, exists := providerBucket[apiKey]; exists { + providerBucket[apiKey] = mergeRecentRequestBuckets(existing, recent) + continue + } + providerBucket[apiKey] = recent + } + + c.JSON(http.StatusOK, out) +} diff --git a/internal/api/handlers/management/api_key_usage_test.go b/internal/api/handlers/management/api_key_usage_test.go new file mode 100644 index 00000000..230dca4a --- /dev/null +++ b/internal/api/handlers/management/api_key_usage_test.go @@ -0,0 +1,87 @@ +package management + +import ( + "context" + "encoding/json" + "net/http" + "net/http/httptest" + "testing" + + "github.com/gin-gonic/gin" + "github.com/router-for-me/CLIProxyAPI/v6/internal/config" + coreauth "github.com/router-for-me/CLIProxyAPI/v6/sdk/cliproxy/auth" +) + +func sumRecentRequestBuckets(buckets []coreauth.RecentRequestBucket) (int64, int64) { + var success int64 + var failed int64 + for _, bucket := range buckets { + success += bucket.Success + failed += bucket.Failed + } + return success, failed +} + +func TestGetAPIKeyUsage_GroupsByProviderAndAPIKey(t *testing.T) { + t.Setenv("MANAGEMENT_PASSWORD", "") + gin.SetMode(gin.TestMode) + + manager := coreauth.NewManager(nil, nil, nil) + if _, err := manager.Register(context.Background(), &coreauth.Auth{ + ID: "codex-auth", + Provider: "codex", + Attributes: map[string]string{ + "api_key": "codex-key", + }, + }); err != nil { + t.Fatalf("register codex auth: %v", err) + } + if _, err := manager.Register(context.Background(), &coreauth.Auth{ + ID: "claude-auth", + Provider: "claude", + Attributes: map[string]string{ + "api_key": "claude-key", + }, + }); err != nil { + t.Fatalf("register claude auth: %v", err) + } + + manager.MarkResult(context.Background(), coreauth.Result{AuthID: "codex-auth", Provider: "codex", Model: "gpt-5", Success: true}) + manager.MarkResult(context.Background(), coreauth.Result{AuthID: "codex-auth", Provider: "codex", Model: "gpt-5", Success: false}) + manager.MarkResult(context.Background(), coreauth.Result{AuthID: "claude-auth", Provider: "claude", Model: "claude-4", Success: true}) + + h := NewHandlerWithoutConfigFilePath(&config.Config{AuthDir: t.TempDir()}, manager) + + rec := httptest.NewRecorder() + ginCtx, _ := gin.CreateTestContext(rec) + req := httptest.NewRequest(http.MethodGet, "/v0/management/api-key-usage", nil) + ginCtx.Request = req + h.GetAPIKeyUsage(ginCtx) + + if rec.Code != http.StatusOK { + t.Fatalf("status = %d, want %d body=%s", rec.Code, http.StatusOK, rec.Body.String()) + } + + var payload map[string]map[string][]coreauth.RecentRequestBucket + if err := json.Unmarshal(rec.Body.Bytes(), &payload); err != nil { + t.Fatalf("decode payload: %v", err) + } + + codexBuckets := payload["codex"]["codex-key"] + if len(codexBuckets) != 20 { + t.Fatalf("codex buckets len = %d, want 20", len(codexBuckets)) + } + codexSuccess, codexFailed := sumRecentRequestBuckets(codexBuckets) + if codexSuccess != 1 || codexFailed != 1 { + t.Fatalf("codex totals = %d/%d, want 1/1", codexSuccess, codexFailed) + } + + claudeBuckets := payload["claude"]["claude-key"] + if len(claudeBuckets) != 20 { + t.Fatalf("claude buckets len = %d, want 20", len(claudeBuckets)) + } + claudeSuccess, claudeFailed := sumRecentRequestBuckets(claudeBuckets) + if claudeSuccess != 1 || claudeFailed != 0 { + t.Fatalf("claude totals = %d/%d, want 1/0", claudeSuccess, claudeFailed) + } +} diff --git a/internal/api/server.go b/internal/api/server.go index 8421357b..4d51460d 100644 --- a/internal/api/server.go +++ b/internal/api/server.go @@ -554,6 +554,7 @@ func (s *Server) registerManagementRoutes() { mgmt.PUT("/api-keys", s.mgmt.PutAPIKeys) mgmt.PATCH("/api-keys", s.mgmt.PatchAPIKeys) mgmt.DELETE("/api-keys", s.mgmt.DeleteAPIKeys) + mgmt.GET("/api-key-usage", s.mgmt.GetAPIKeyUsage) mgmt.GET("/gemini-api-key", s.mgmt.GetGeminiKeys) mgmt.PUT("/gemini-api-key", s.mgmt.PutGeminiKeys) diff --git a/sdk/cliproxy/auth/types.go b/sdk/cliproxy/auth/types.go index 93dd3881..4a394ad4 100644 --- a/sdk/cliproxy/auth/types.go +++ b/sdk/cliproxy/auth/types.go @@ -164,7 +164,7 @@ func recentRequestBucketIndex(bucketID int64) int { func formatRecentRequestBucketLabel(bucketID int64) string { start := time.Unix(bucketID*recentRequestBucketSeconds, 0).In(time.Local) - end := start.Add(10 * time.Minute) + end := start.Add(time.Duration(recentRequestBucketSeconds) * time.Second) return start.Format("15:04") + "-" + end.Format("15:04") } -- 2.51.2 From e37f3be0bfc482934d9669b58df1562e59f1196e Mon Sep 17 00:00:00 2001 From: Luis Pater Date: Sat, 2 May 2026 00:09:08 +0800 Subject: [PATCH 19/51] chore: update .goreleaser.yml to include custom archive naming with arch override logic --- .goreleaser.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.goreleaser.yml b/.goreleaser.yml index f8bebfc1..c479255e 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -19,6 +19,8 @@ builds: archives: - id: "cli-proxy-api" format: tar.gz + name_template: >- + {{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{- if eq .Arch "arm64" -}}aarch64{{- else -}}{{ .Arch }}{{- end -}} format_overrides: - goos: windows format: zip -- 2.51.2 From 8c2f1a80d39e542d3d85d569d035d7df8d5c39f6 Mon Sep 17 00:00:00 2001 From: Luis Pater Date: Sat, 2 May 2026 02:20:49 +0800 Subject: [PATCH 20/51] feat: enhance API key usage grouping with base URL inclusion - Updated `GetAPIKeyUsage` to group API key usage by "base_url|api_key" composite keys. - Adjusted logic to handle `base_url` extraction from auth attributes. - Revised unit tests to validate "base_url|api_key" grouping behavior. --- .../api/handlers/management/api_key_usage.go | 16 ++++++++++++---- .../handlers/management/api_key_usage_test.go | 10 ++++++---- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/internal/api/handlers/management/api_key_usage.go b/internal/api/handlers/management/api_key_usage.go index 599fbad9..76b32bbb 100644 --- a/internal/api/handlers/management/api_key_usage.go +++ b/internal/api/handlers/management/api_key_usage.go @@ -35,7 +35,7 @@ func mergeRecentRequestBuckets(dst, src []coreauth.RecentRequestBucket) []coreau } // GetAPIKeyUsage returns recent request buckets for all in-memory api_key auths, -// grouped by provider and keyed by the raw api-key value. +// grouped by provider and keyed by "base_url|api_key". func (h *Handler) GetAPIKeyUsage(c *gin.Context) { if h == nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "handler not initialized"}) @@ -64,6 +64,14 @@ func (h *Handler) GetAPIKeyUsage(c *gin.Context) { if apiKey == "" { continue } + baseURL := "" + if auth.Attributes != nil { + baseURL = strings.TrimSpace(auth.Attributes["base_url"]) + if baseURL == "" { + baseURL = strings.TrimSpace(auth.Attributes["base-url"]) + } + } + compositeKey := baseURL + "|" + apiKey provider := strings.ToLower(strings.TrimSpace(auth.Provider)) if provider == "" { provider = "unknown" @@ -75,11 +83,11 @@ func (h *Handler) GetAPIKeyUsage(c *gin.Context) { providerBucket = make(map[string][]coreauth.RecentRequestBucket) out[provider] = providerBucket } - if existing, exists := providerBucket[apiKey]; exists { - providerBucket[apiKey] = mergeRecentRequestBuckets(existing, recent) + if existing, exists := providerBucket[compositeKey]; exists { + providerBucket[compositeKey] = mergeRecentRequestBuckets(existing, recent) continue } - providerBucket[apiKey] = recent + providerBucket[compositeKey] = recent } c.JSON(http.StatusOK, out) diff --git a/internal/api/handlers/management/api_key_usage_test.go b/internal/api/handlers/management/api_key_usage_test.go index 230dca4a..56617161 100644 --- a/internal/api/handlers/management/api_key_usage_test.go +++ b/internal/api/handlers/management/api_key_usage_test.go @@ -31,7 +31,8 @@ func TestGetAPIKeyUsage_GroupsByProviderAndAPIKey(t *testing.T) { ID: "codex-auth", Provider: "codex", Attributes: map[string]string{ - "api_key": "codex-key", + "api_key": "codex-key", + "base_url": "https://codex.example.com", }, }); err != nil { t.Fatalf("register codex auth: %v", err) @@ -40,7 +41,8 @@ func TestGetAPIKeyUsage_GroupsByProviderAndAPIKey(t *testing.T) { ID: "claude-auth", Provider: "claude", Attributes: map[string]string{ - "api_key": "claude-key", + "api_key": "claude-key", + "base_url": "https://claude.example.com", }, }); err != nil { t.Fatalf("register claude auth: %v", err) @@ -67,7 +69,7 @@ func TestGetAPIKeyUsage_GroupsByProviderAndAPIKey(t *testing.T) { t.Fatalf("decode payload: %v", err) } - codexBuckets := payload["codex"]["codex-key"] + codexBuckets := payload["codex"]["https://codex.example.com|codex-key"] if len(codexBuckets) != 20 { t.Fatalf("codex buckets len = %d, want 20", len(codexBuckets)) } @@ -76,7 +78,7 @@ func TestGetAPIKeyUsage_GroupsByProviderAndAPIKey(t *testing.T) { t.Fatalf("codex totals = %d/%d, want 1/1", codexSuccess, codexFailed) } - claudeBuckets := payload["claude"]["claude-key"] + claudeBuckets := payload["claude"]["https://claude.example.com|claude-key"] if len(claudeBuckets) != 20 { t.Fatalf("claude buckets len = %d, want 20", len(claudeBuckets)) } -- 2.51.2 From b8bba053fcdafd80abc2152c88c78f4e7713c05a Mon Sep 17 00:00:00 2001 From: Luis Pater Date: Sat, 2 May 2026 03:40:00 +0800 Subject: [PATCH 21/51] feat: add tracking for auth request success and failure counts - Introduced `Success` and `Failed` fields in auth records to track request outcomes. - Updated `/v0/management/auth-files` and `/v0/management/api-key-usage` responses to include success and failure counts. - Enhanced tests to validate tracking logic and API responses. --- .../api/handlers/management/api_key_usage.go | 21 ++++++-- .../handlers/management/api_key_usage_test.go | 24 +++++---- .../api/handlers/management/auth_files.go | 2 + .../auth_files_recent_requests_test.go | 7 +++ sdk/cliproxy/auth/conductor.go | 8 +++ .../auth/conductor_recent_requests_test.go | 51 +++++++++++++++++++ sdk/cliproxy/auth/types.go | 3 ++ 7 files changed, 103 insertions(+), 13 deletions(-) diff --git a/internal/api/handlers/management/api_key_usage.go b/internal/api/handlers/management/api_key_usage.go index 76b32bbb..3361da5d 100644 --- a/internal/api/handlers/management/api_key_usage.go +++ b/internal/api/handlers/management/api_key_usage.go @@ -9,6 +9,12 @@ import ( coreauth "github.com/router-for-me/CLIProxyAPI/v6/sdk/cliproxy/auth" ) +type apiKeyUsageEntry struct { + Success int64 `json:"success"` + Failed int64 `json:"failed"` + RecentRequests []coreauth.RecentRequestBucket `json:"recent_requests"` +} + func mergeRecentRequestBuckets(dst, src []coreauth.RecentRequestBucket) []coreauth.RecentRequestBucket { if len(dst) == 0 { return src @@ -51,7 +57,7 @@ func (h *Handler) GetAPIKeyUsage(c *gin.Context) { } now := time.Now() - out := make(map[string]map[string][]coreauth.RecentRequestBucket) + out := make(map[string]map[string]apiKeyUsageEntry) for _, auth := range manager.List() { if auth == nil { continue @@ -80,14 +86,21 @@ func (h *Handler) GetAPIKeyUsage(c *gin.Context) { recent := auth.RecentRequestsSnapshot(now) providerBucket, ok := out[provider] if !ok { - providerBucket = make(map[string][]coreauth.RecentRequestBucket) + providerBucket = make(map[string]apiKeyUsageEntry) out[provider] = providerBucket } if existing, exists := providerBucket[compositeKey]; exists { - providerBucket[compositeKey] = mergeRecentRequestBuckets(existing, recent) + existing.Success += auth.Success + existing.Failed += auth.Failed + existing.RecentRequests = mergeRecentRequestBuckets(existing.RecentRequests, recent) + providerBucket[compositeKey] = existing continue } - providerBucket[compositeKey] = recent + providerBucket[compositeKey] = apiKeyUsageEntry{ + Success: auth.Success, + Failed: auth.Failed, + RecentRequests: recent, + } } c.JSON(http.StatusOK, out) diff --git a/internal/api/handlers/management/api_key_usage_test.go b/internal/api/handlers/management/api_key_usage_test.go index 56617161..2880567f 100644 --- a/internal/api/handlers/management/api_key_usage_test.go +++ b/internal/api/handlers/management/api_key_usage_test.go @@ -64,25 +64,31 @@ func TestGetAPIKeyUsage_GroupsByProviderAndAPIKey(t *testing.T) { t.Fatalf("status = %d, want %d body=%s", rec.Code, http.StatusOK, rec.Body.String()) } - var payload map[string]map[string][]coreauth.RecentRequestBucket + var payload map[string]map[string]apiKeyUsageEntry if err := json.Unmarshal(rec.Body.Bytes(), &payload); err != nil { t.Fatalf("decode payload: %v", err) } - codexBuckets := payload["codex"]["https://codex.example.com|codex-key"] - if len(codexBuckets) != 20 { - t.Fatalf("codex buckets len = %d, want 20", len(codexBuckets)) + codexEntry := payload["codex"]["https://codex.example.com|codex-key"] + if codexEntry.Success != 1 || codexEntry.Failed != 1 { + t.Fatalf("codex totals = %d/%d, want 1/1", codexEntry.Success, codexEntry.Failed) } - codexSuccess, codexFailed := sumRecentRequestBuckets(codexBuckets) + if len(codexEntry.RecentRequests) != 20 { + t.Fatalf("codex buckets len = %d, want 20", len(codexEntry.RecentRequests)) + } + codexSuccess, codexFailed := sumRecentRequestBuckets(codexEntry.RecentRequests) if codexSuccess != 1 || codexFailed != 1 { t.Fatalf("codex totals = %d/%d, want 1/1", codexSuccess, codexFailed) } - claudeBuckets := payload["claude"]["https://claude.example.com|claude-key"] - if len(claudeBuckets) != 20 { - t.Fatalf("claude buckets len = %d, want 20", len(claudeBuckets)) + claudeEntry := payload["claude"]["https://claude.example.com|claude-key"] + if claudeEntry.Success != 1 || claudeEntry.Failed != 0 { + t.Fatalf("claude totals = %d/%d, want 1/0", claudeEntry.Success, claudeEntry.Failed) + } + if len(claudeEntry.RecentRequests) != 20 { + t.Fatalf("claude buckets len = %d, want 20", len(claudeEntry.RecentRequests)) } - claudeSuccess, claudeFailed := sumRecentRequestBuckets(claudeBuckets) + claudeSuccess, claudeFailed := sumRecentRequestBuckets(claudeEntry.RecentRequests) if claudeSuccess != 1 || claudeFailed != 0 { t.Fatalf("claude totals = %d/%d, want 1/0", claudeSuccess, claudeFailed) } diff --git a/internal/api/handlers/management/auth_files.go b/internal/api/handlers/management/auth_files.go index 2bcfaac4..bb94daa9 100644 --- a/internal/api/handlers/management/auth_files.go +++ b/internal/api/handlers/management/auth_files.go @@ -388,6 +388,8 @@ func (h *Handler) buildAuthFileEntry(auth *coreauth.Auth) gin.H { "source": "memory", "size": int64(0), } + entry["success"] = auth.Success + entry["failed"] = auth.Failed entry["recent_requests"] = auth.RecentRequestsSnapshot(time.Now()) if email := authEmail(auth); email != "" { entry["email"] = email diff --git a/internal/api/handlers/management/auth_files_recent_requests_test.go b/internal/api/handlers/management/auth_files_recent_requests_test.go index fd28ca1d..979040f5 100644 --- a/internal/api/handlers/management/auth_files_recent_requests_test.go +++ b/internal/api/handlers/management/auth_files_recent_requests_test.go @@ -62,6 +62,13 @@ func TestListAuthFiles_IncludesRecentRequestsBuckets(t *testing.T) { t.Fatalf("expected file entry object, got %#v", filesRaw[0]) } + if _, ok := fileEntry["success"].(float64); !ok { + t.Fatalf("expected success number, got %#v", fileEntry["success"]) + } + if _, ok := fileEntry["failed"].(float64); !ok { + t.Fatalf("expected failed number, got %#v", fileEntry["failed"]) + } + recentRaw, ok := fileEntry["recent_requests"].([]any) if !ok { t.Fatalf("expected recent_requests array, got %#v", fileEntry["recent_requests"]) diff --git a/sdk/cliproxy/auth/conductor.go b/sdk/cliproxy/auth/conductor.go index 61a0e413..d2a3db18 100644 --- a/sdk/cliproxy/auth/conductor.go +++ b/sdk/cliproxy/auth/conductor.go @@ -1126,6 +1126,9 @@ func (m *Manager) Update(ctx context.Context, auth *Auth) (*Auth, error) { auth.Index = existing.Index auth.indexAssigned = existing.indexAssigned } + auth.Success = existing.Success + auth.Failed = existing.Failed + auth.recentRequests = existing.recentRequests if !existing.Disabled && existing.Status != StatusDisabled && !auth.Disabled && auth.Status != StatusDisabled { if len(auth.ModelStates) == 0 && len(existing.ModelStates) > 0 { auth.ModelStates = existing.ModelStates @@ -2022,6 +2025,11 @@ func (m *Manager) MarkResult(ctx context.Context, result Result) { if auth, ok := m.auths[result.AuthID]; ok && auth != nil { now := time.Now() auth.recordRecentRequest(now, result.Success) + if result.Success { + auth.Success++ + } else { + auth.Failed++ + } if result.Success { if result.Model != "" { diff --git a/sdk/cliproxy/auth/conductor_recent_requests_test.go b/sdk/cliproxy/auth/conductor_recent_requests_test.go index 3f5a7212..d2003b7c 100644 --- a/sdk/cliproxy/auth/conductor_recent_requests_test.go +++ b/sdk/cliproxy/auth/conductor_recent_requests_test.go @@ -31,6 +31,10 @@ func TestManagerMarkResultRecordsRecentRequests(t *testing.T) { t.Fatalf("GetByID returned ok=%v auth=%v", ok, gotAuth) } + if gotAuth.Success != 1 || gotAuth.Failed != 1 { + t.Fatalf("auth totals = success=%d failed=%d, want 1/1", gotAuth.Success, gotAuth.Failed) + } + snapshot := gotAuth.RecentRequestsSnapshot(time.Now()) var successTotal int64 var failedTotal int64 @@ -42,3 +46,50 @@ func TestManagerMarkResultRecordsRecentRequests(t *testing.T) { t.Fatalf("totals = success=%d failed=%d, want 1/1", successTotal, failedTotal) } } + +func TestManagerUpdatePreservesRecentRequestsAndTotals(t *testing.T) { + mgr := NewManager(nil, nil, nil) + auth := &Auth{ + ID: "auth-1", + Provider: "antigravity", + Metadata: map[string]any{ + "type": "antigravity", + }, + } + if _, err := mgr.Register(WithSkipPersist(context.Background()), auth); err != nil { + t.Fatalf("Register returned error: %v", err) + } + + mgr.MarkResult(context.Background(), Result{AuthID: "auth-1", Provider: "antigravity", Model: "gpt-5", Success: true}) + + updated := &Auth{ + ID: "auth-1", + Provider: "antigravity", + Metadata: map[string]any{ + "type": "antigravity", + "note": "updated", + }, + } + if _, err := mgr.Update(WithSkipPersist(context.Background()), updated); err != nil { + t.Fatalf("Update returned error: %v", err) + } + + gotAuth, ok := mgr.GetByID("auth-1") + if !ok || gotAuth == nil { + t.Fatalf("GetByID returned ok=%v auth=%v", ok, gotAuth) + } + if gotAuth.Success != 1 || gotAuth.Failed != 0 { + t.Fatalf("auth totals = success=%d failed=%d, want 1/0", gotAuth.Success, gotAuth.Failed) + } + + snapshot := gotAuth.RecentRequestsSnapshot(time.Now()) + var successTotal int64 + var failedTotal int64 + for _, bucket := range snapshot { + successTotal += bucket.Success + failedTotal += bucket.Failed + } + if successTotal != 1 || failedTotal != 0 { + t.Fatalf("bucket totals = success=%d failed=%d, want 1/0", successTotal, failedTotal) + } +} diff --git a/sdk/cliproxy/auth/types.go b/sdk/cliproxy/auth/types.go index 4a394ad4..76f4c396 100644 --- a/sdk/cliproxy/auth/types.go +++ b/sdk/cliproxy/auth/types.go @@ -92,6 +92,9 @@ type Auth struct { // Runtime carries non-serialisable data used during execution (in-memory only). Runtime any `json:"-"` + Success int64 `json:"-"` + Failed int64 `json:"-"` + recentRequests recentRequestRing `json:"-"` indexAssigned bool `json:"-"` } -- 2.51.2 From 18bb9c315fced2c428f57b4a0e66b06183c46c06 Mon Sep 17 00:00:00 2001 From: Luis Pater Date: Sat, 2 May 2026 04:50:58 +0800 Subject: [PATCH 22/51] chore: remove usage tracking and logging functionality - Deleted the `LoggerPlugin` along with associated usage tracking and in-memory statistics logic. - Removed all related tests (`logger_plugin_test.go`, `usage_tab_test.go`) and external-facing handler (`usage.go`) for usage statistics export/import. - Cleaned up TUI integration by deleting `usage_tab.go`. --- cmd/server/main.go | 4 +- docker-build.sh | 136 +----- internal/api/handlers/management/handler.go | 6 - internal/api/handlers/management/usage.go | 79 ---- internal/api/server.go | 6 +- internal/redisqueue/plugin.go | 28 +- internal/redisqueue/plugin_test.go | 7 +- internal/redisqueue/usage_toggle.go | 16 + internal/tui/app.go | 22 +- internal/tui/client.go | 5 - internal/tui/dashboard.go | 77 +--- internal/tui/i18n.go | 4 +- internal/tui/usage_tab.go | 418 ------------------ internal/tui/usage_tab_test.go | 134 ------ internal/usage/logger_plugin.go | 464 -------------------- internal/usage/logger_plugin_test.go | 96 ---- sdk/cliproxy/service.go | 1 - test/usage_logging_test.go | 83 ++-- 18 files changed, 116 insertions(+), 1470 deletions(-) delete mode 100644 internal/api/handlers/management/usage.go create mode 100644 internal/redisqueue/usage_toggle.go delete mode 100644 internal/tui/usage_tab.go delete mode 100644 internal/tui/usage_tab_test.go delete mode 100644 internal/usage/logger_plugin.go delete mode 100644 internal/usage/logger_plugin_test.go diff --git a/cmd/server/main.go b/cmd/server/main.go index b8707f0a..e735b144 100644 --- a/cmd/server/main.go +++ b/cmd/server/main.go @@ -24,11 +24,11 @@ import ( "github.com/router-for-me/CLIProxyAPI/v6/internal/logging" "github.com/router-for-me/CLIProxyAPI/v6/internal/managementasset" "github.com/router-for-me/CLIProxyAPI/v6/internal/misc" + "github.com/router-for-me/CLIProxyAPI/v6/internal/redisqueue" "github.com/router-for-me/CLIProxyAPI/v6/internal/registry" "github.com/router-for-me/CLIProxyAPI/v6/internal/store" _ "github.com/router-for-me/CLIProxyAPI/v6/internal/translator" "github.com/router-for-me/CLIProxyAPI/v6/internal/tui" - "github.com/router-for-me/CLIProxyAPI/v6/internal/usage" "github.com/router-for-me/CLIProxyAPI/v6/internal/util" sdkAuth "github.com/router-for-me/CLIProxyAPI/v6/sdk/auth" coreauth "github.com/router-for-me/CLIProxyAPI/v6/sdk/cliproxy/auth" @@ -417,7 +417,7 @@ func main() { configFileExists = true } } - usage.SetStatisticsEnabled(cfg.UsageStatisticsEnabled) + redisqueue.SetUsageStatisticsEnabled(cfg.UsageStatisticsEnabled) coreauth.SetQuotaCooldownDisabled(cfg.DisableCooling) if err = logging.ConfigureLogOutput(cfg); err != nil { diff --git a/docker-build.sh b/docker-build.sh index 4538b807..ebe7d923 100644 --- a/docker-build.sh +++ b/docker-build.sh @@ -5,123 +5,13 @@ # This script automates the process of building and running the Docker container # with version information dynamically injected at build time. -# Hidden feature: Preserve usage statistics across rebuilds -# Usage: ./docker-build.sh --with-usage -# First run prompts for management API key, saved to temp/stats/.api_secret - set -euo pipefail -STATS_DIR="temp/stats" -STATS_FILE="${STATS_DIR}/.usage_backup.json" -SECRET_FILE="${STATS_DIR}/.api_secret" -WITH_USAGE=false - -get_port() { - if [[ -f "config.yaml" ]]; then - grep -E "^port:" config.yaml | sed -E 's/^port: *["'"'"']?([0-9]+)["'"'"']?.*$/\1/' - else - echo "8317" - fi -} - -export_stats_api_secret() { - if [[ -f "${SECRET_FILE}" ]]; then - API_SECRET=$(cat "${SECRET_FILE}") - else - if [[ ! -d "${STATS_DIR}" ]]; then - mkdir -p "${STATS_DIR}" - fi - echo "First time using --with-usage. Management API key required." - read -r -p "Enter management key: " -s API_SECRET - echo - echo "${API_SECRET}" > "${SECRET_FILE}" - chmod 600 "${SECRET_FILE}" - fi -} - -check_container_running() { - local port - port=$(get_port) - - if ! curl -s -o /dev/null -w "%{http_code}" "http://localhost:${port}/" | grep -q "200"; then - echo "Error: cli-proxy-api service is not responding at localhost:${port}" - echo "Please start the container first or use without --with-usage flag." - exit 1 - fi -} - -export_stats() { - local port - port=$(get_port) - - if [[ ! -d "${STATS_DIR}" ]]; then - mkdir -p "${STATS_DIR}" - fi - check_container_running - echo "Exporting usage statistics..." - EXPORT_RESPONSE=$(curl -s -w "\n%{http_code}" -H "X-Management-Key: ${API_SECRET}" \ - "http://localhost:${port}/v0/management/usage/export") - HTTP_CODE=$(echo "${EXPORT_RESPONSE}" | tail -n1) - RESPONSE_BODY=$(echo "${EXPORT_RESPONSE}" | sed '$d') - - if [[ "${HTTP_CODE}" != "200" ]]; then - echo "Export failed (HTTP ${HTTP_CODE}): ${RESPONSE_BODY}" - exit 1 - fi - - echo "${RESPONSE_BODY}" > "${STATS_FILE}" - echo "Statistics exported to ${STATS_FILE}" -} - -import_stats() { - local port - port=$(get_port) - - echo "Importing usage statistics..." - IMPORT_RESPONSE=$(curl -s -w "\n%{http_code}" -X POST \ - -H "X-Management-Key: ${API_SECRET}" \ - -H "Content-Type: application/json" \ - -d @"${STATS_FILE}" \ - "http://localhost:${port}/v0/management/usage/import") - IMPORT_CODE=$(echo "${IMPORT_RESPONSE}" | tail -n1) - IMPORT_BODY=$(echo "${IMPORT_RESPONSE}" | sed '$d') - - if [[ "${IMPORT_CODE}" == "200" ]]; then - echo "Statistics imported successfully" - else - echo "Import failed (HTTP ${IMPORT_CODE}): ${IMPORT_BODY}" - fi - - rm -f "${STATS_FILE}" -} - -wait_for_service() { - local port - port=$(get_port) - - echo "Waiting for service to be ready..." - for i in {1..30}; do - if curl -s -o /dev/null -w "%{http_code}" "http://localhost:${port}/" | grep -q "200"; then - break - fi - sleep 1 - done - sleep 2 -} - -case "${1:-}" in - "") - ;; - "--with-usage") - WITH_USAGE=true - export_stats_api_secret - ;; - *) - echo "Error: unknown option '${1}'. Did you mean '--with-usage'?" - echo "Usage: ./docker-build.sh [--with-usage]" - exit 1 - ;; -esac +if [[ "${1:-}" != "" ]]; then + echo "Error: unknown option '${1}'." + echo "Usage: ./docker-build.sh" + exit 1 +fi # --- Step 1: Choose Environment --- echo "Please select an option:" @@ -133,14 +23,7 @@ read -r -p "Enter choice [1-2]: " choice case "$choice" in 1) echo "--- Running with Pre-built Image ---" - if [[ "${WITH_USAGE}" == "true" ]]; then - export_stats - fi docker compose up -d --remove-orphans --no-build - if [[ "${WITH_USAGE}" == "true" ]]; then - wait_for_service - import_stats - fi echo "Services are starting from remote image." echo "Run 'docker compose logs -f' to see the logs." ;; @@ -167,18 +50,9 @@ case "$choice" in --build-arg COMMIT="${COMMIT}" \ --build-arg BUILD_DATE="${BUILD_DATE}" - if [[ "${WITH_USAGE}" == "true" ]]; then - export_stats - fi - echo "Starting the services..." docker compose up -d --remove-orphans --pull never - if [[ "${WITH_USAGE}" == "true" ]]; then - wait_for_service - import_stats - fi - echo "Build complete. Services are starting." echo "Run 'docker compose logs -f' to see the logs." ;; diff --git a/internal/api/handlers/management/handler.go b/internal/api/handlers/management/handler.go index af11366c..9abc8a5c 100644 --- a/internal/api/handlers/management/handler.go +++ b/internal/api/handlers/management/handler.go @@ -15,7 +15,6 @@ import ( "github.com/gin-gonic/gin" "github.com/router-for-me/CLIProxyAPI/v6/internal/buildinfo" "github.com/router-for-me/CLIProxyAPI/v6/internal/config" - "github.com/router-for-me/CLIProxyAPI/v6/internal/usage" sdkAuth "github.com/router-for-me/CLIProxyAPI/v6/sdk/auth" coreauth "github.com/router-for-me/CLIProxyAPI/v6/sdk/cliproxy/auth" "golang.org/x/crypto/bcrypt" @@ -41,7 +40,6 @@ type Handler struct { attemptsMu sync.Mutex failedAttempts map[string]*attemptInfo // keyed by client IP authManager *coreauth.Manager - usageStats *usage.RequestStatistics tokenStore coreauth.Store localPassword string allowRemoteOverride bool @@ -60,7 +58,6 @@ func NewHandler(cfg *config.Config, configFilePath string, manager *coreauth.Man configFilePath: configFilePath, failedAttempts: make(map[string]*attemptInfo), authManager: manager, - usageStats: usage.GetRequestStatistics(), tokenStore: sdkAuth.GetTokenStore(), allowRemoteOverride: envSecret != "", envSecret: envSecret, @@ -124,9 +121,6 @@ func (h *Handler) SetAuthManager(manager *coreauth.Manager) { h.mu.Unlock() } -// SetUsageStatistics allows replacing the usage statistics reference. -func (h *Handler) SetUsageStatistics(stats *usage.RequestStatistics) { h.usageStats = stats } - // SetLocalPassword configures the runtime-local password accepted for localhost requests. func (h *Handler) SetLocalPassword(password string) { h.localPassword = password } diff --git a/internal/api/handlers/management/usage.go b/internal/api/handlers/management/usage.go deleted file mode 100644 index 5f794089..00000000 --- a/internal/api/handlers/management/usage.go +++ /dev/null @@ -1,79 +0,0 @@ -package management - -import ( - "encoding/json" - "net/http" - "time" - - "github.com/gin-gonic/gin" - "github.com/router-for-me/CLIProxyAPI/v6/internal/usage" -) - -type usageExportPayload struct { - Version int `json:"version"` - ExportedAt time.Time `json:"exported_at"` - Usage usage.StatisticsSnapshot `json:"usage"` -} - -type usageImportPayload struct { - Version int `json:"version"` - Usage usage.StatisticsSnapshot `json:"usage"` -} - -// GetUsageStatistics returns the in-memory request statistics snapshot. -func (h *Handler) GetUsageStatistics(c *gin.Context) { - var snapshot usage.StatisticsSnapshot - if h != nil && h.usageStats != nil { - snapshot = h.usageStats.Snapshot() - } - c.JSON(http.StatusOK, gin.H{ - "usage": snapshot, - "failed_requests": snapshot.FailureCount, - }) -} - -// ExportUsageStatistics returns a complete usage snapshot for backup/migration. -func (h *Handler) ExportUsageStatistics(c *gin.Context) { - var snapshot usage.StatisticsSnapshot - if h != nil && h.usageStats != nil { - snapshot = h.usageStats.Snapshot() - } - c.JSON(http.StatusOK, usageExportPayload{ - Version: 1, - ExportedAt: time.Now().UTC(), - Usage: snapshot, - }) -} - -// ImportUsageStatistics merges a previously exported usage snapshot into memory. -func (h *Handler) ImportUsageStatistics(c *gin.Context) { - if h == nil || h.usageStats == nil { - c.JSON(http.StatusBadRequest, gin.H{"error": "usage statistics unavailable"}) - return - } - - data, err := c.GetRawData() - if err != nil { - c.JSON(http.StatusBadRequest, gin.H{"error": "failed to read request body"}) - return - } - - var payload usageImportPayload - if err := json.Unmarshal(data, &payload); err != nil { - c.JSON(http.StatusBadRequest, gin.H{"error": "invalid json"}) - return - } - if payload.Version != 0 && payload.Version != 1 { - c.JSON(http.StatusBadRequest, gin.H{"error": "unsupported version"}) - return - } - - result := h.usageStats.MergeSnapshot(payload.Usage) - snapshot := h.usageStats.Snapshot() - c.JSON(http.StatusOK, gin.H{ - "added": result.Added, - "skipped": result.Skipped, - "total_requests": snapshot.TotalRequests, - "failed_requests": snapshot.FailureCount, - }) -} diff --git a/internal/api/server.go b/internal/api/server.go index 4d51460d..176bc2a3 100644 --- a/internal/api/server.go +++ b/internal/api/server.go @@ -31,7 +31,6 @@ import ( "github.com/router-for-me/CLIProxyAPI/v6/internal/logging" "github.com/router-for-me/CLIProxyAPI/v6/internal/managementasset" "github.com/router-for-me/CLIProxyAPI/v6/internal/redisqueue" - "github.com/router-for-me/CLIProxyAPI/v6/internal/usage" "github.com/router-for-me/CLIProxyAPI/v6/internal/util" sdkaccess "github.com/router-for-me/CLIProxyAPI/v6/sdk/access" "github.com/router-for-me/CLIProxyAPI/v6/sdk/api/handlers" @@ -507,9 +506,6 @@ func (s *Server) registerManagementRoutes() { mgmt := s.engine.Group("/v0/management") mgmt.Use(s.managementAvailabilityMiddleware(), s.mgmt.Middleware()) { - mgmt.GET("/usage", s.mgmt.GetUsageStatistics) - mgmt.GET("/usage/export", s.mgmt.ExportUsageStatistics) - mgmt.POST("/usage/import", s.mgmt.ImportUsageStatistics) mgmt.GET("/config", s.mgmt.GetConfig) mgmt.GET("/config.yaml", s.mgmt.GetConfigYAML) mgmt.PUT("/config.yaml", s.mgmt.PutConfigYAML) @@ -1001,7 +997,7 @@ func (s *Server) UpdateClients(cfg *config.Config) { } if oldCfg == nil || oldCfg.UsageStatisticsEnabled != cfg.UsageStatisticsEnabled { - usage.SetStatisticsEnabled(cfg.UsageStatisticsEnabled) + redisqueue.SetUsageStatisticsEnabled(cfg.UsageStatisticsEnabled) } if s.requestLogger != nil && (oldCfg == nil || oldCfg.ErrorLogsMaxFiles != cfg.ErrorLogsMaxFiles) { diff --git a/internal/redisqueue/plugin.go b/internal/redisqueue/plugin.go index 39739dbe..97168419 100644 --- a/internal/redisqueue/plugin.go +++ b/internal/redisqueue/plugin.go @@ -7,7 +7,6 @@ import ( "time" internallogging "github.com/router-for-me/CLIProxyAPI/v6/internal/logging" - internalusage "github.com/router-for-me/CLIProxyAPI/v6/internal/usage" coreusage "github.com/router-for-me/CLIProxyAPI/v6/sdk/cliproxy/usage" ) @@ -21,7 +20,7 @@ func (p *usageQueuePlugin) HandleUsage(ctx context.Context, record coreusage.Rec if p == nil { return } - if !Enabled() || !internalusage.StatisticsEnabled() { + if !Enabled() || !UsageStatisticsEnabled() { return } @@ -45,7 +44,7 @@ func (p *usageQueuePlugin) HandleUsage(ctx context.Context, record coreusage.Rec apiKey := strings.TrimSpace(record.APIKey) requestID := strings.TrimSpace(internallogging.GetRequestID(ctx)) - tokens := internalusage.TokenStats{ + tokens := tokenStats{ InputTokens: record.Detail.InputTokens, OutputTokens: record.Detail.OutputTokens, ReasoningTokens: record.Detail.ReasoningTokens, @@ -64,7 +63,7 @@ func (p *usageQueuePlugin) HandleUsage(ctx context.Context, record coreusage.Rec failed = !resolveSuccess(ctx) } - detail := internalusage.RequestDetail{ + detail := requestDetail{ Timestamp: timestamp, LatencyMs: record.Latency.Milliseconds(), Source: record.Source, @@ -74,7 +73,7 @@ func (p *usageQueuePlugin) HandleUsage(ctx context.Context, record coreusage.Rec } payload, err := json.Marshal(queuedUsageDetail{ - RequestDetail: detail, + requestDetail: detail, Provider: provider, Model: modelName, Endpoint: resolveEndpoint(ctx), @@ -89,7 +88,7 @@ func (p *usageQueuePlugin) HandleUsage(ctx context.Context, record coreusage.Rec } type queuedUsageDetail struct { - internalusage.RequestDetail + requestDetail Provider string `json:"provider"` Model string `json:"model"` Endpoint string `json:"endpoint"` @@ -98,6 +97,23 @@ type queuedUsageDetail struct { RequestID string `json:"request_id"` } +type requestDetail struct { + Timestamp time.Time `json:"timestamp"` + LatencyMs int64 `json:"latency_ms"` + Source string `json:"source"` + AuthIndex string `json:"auth_index"` + Tokens tokenStats `json:"tokens"` + Failed bool `json:"failed"` +} + +type tokenStats struct { + InputTokens int64 `json:"input_tokens"` + OutputTokens int64 `json:"output_tokens"` + ReasoningTokens int64 `json:"reasoning_tokens"` + CachedTokens int64 `json:"cached_tokens"` + TotalTokens int64 `json:"total_tokens"` +} + func resolveSuccess(ctx context.Context) bool { status := internallogging.GetResponseStatus(ctx) if status == 0 { diff --git a/internal/redisqueue/plugin_test.go b/internal/redisqueue/plugin_test.go index 1e8bda48..0cc8b9b9 100644 --- a/internal/redisqueue/plugin_test.go +++ b/internal/redisqueue/plugin_test.go @@ -10,7 +10,6 @@ import ( "github.com/gin-gonic/gin" internallogging "github.com/router-for-me/CLIProxyAPI/v6/internal/logging" - internalusage "github.com/router-for-me/CLIProxyAPI/v6/internal/usage" coreusage "github.com/router-for-me/CLIProxyAPI/v6/sdk/cliproxy/usage" ) @@ -127,16 +126,16 @@ func withEnabledQueue(t *testing.T, fn func()) { t.Helper() prevQueueEnabled := Enabled() - prevStatsEnabled := internalusage.StatisticsEnabled() + prevUsageEnabled := UsageStatisticsEnabled() SetEnabled(false) SetEnabled(true) - internalusage.SetStatisticsEnabled(true) + SetUsageStatisticsEnabled(true) defer func() { SetEnabled(false) SetEnabled(prevQueueEnabled) - internalusage.SetStatisticsEnabled(prevStatsEnabled) + SetUsageStatisticsEnabled(prevUsageEnabled) }() fn() diff --git a/internal/redisqueue/usage_toggle.go b/internal/redisqueue/usage_toggle.go new file mode 100644 index 00000000..dddbeca6 --- /dev/null +++ b/internal/redisqueue/usage_toggle.go @@ -0,0 +1,16 @@ +package redisqueue + +import "sync/atomic" + +var usageStatisticsEnabled atomic.Bool + +func init() { + usageStatisticsEnabled.Store(true) +} + +// SetUsageStatisticsEnabled toggles whether usage records are enqueued into the redisqueue payload buffer. +// This is controlled by the config field `usage-statistics-enabled` and the corresponding management API. +func SetUsageStatisticsEnabled(enabled bool) { usageStatisticsEnabled.Store(enabled) } + +// UsageStatisticsEnabled reports whether the usage queue plugin should publish records. +func UsageStatisticsEnabled() bool { return usageStatisticsEnabled.Load() } diff --git a/internal/tui/app.go b/internal/tui/app.go index b9ee9e1a..c0a7c3a8 100644 --- a/internal/tui/app.go +++ b/internal/tui/app.go @@ -18,7 +18,6 @@ const ( tabAuthFiles tabAPIKeys tabOAuth - tabUsage tabLogs ) @@ -40,7 +39,6 @@ type App struct { auth authTabModel keys keysTabModel oauth oauthTabModel - usage usageTabModel logs logsTabModel client *Client @@ -50,7 +48,7 @@ type App struct { ready bool // Track which tabs have been initialized (fetched data) - initialized [7]bool + initialized [6]bool } type authConnectMsg struct { @@ -81,10 +79,9 @@ func NewApp(port int, secretKey string, hook *LogHook) App { auth: newAuthTabModel(client), keys: newKeysTabModel(client), oauth: newOAuthTabModel(client), - usage: newUsageTabModel(client), logs: newLogsTabModel(client, hook), client: client, - initialized: [7]bool{ + initialized: [6]bool{ tabDashboard: true, tabLogs: true, }, @@ -92,7 +89,7 @@ func NewApp(port int, secretKey string, hook *LogHook) App { app.refreshTabs() if authRequired { - app.initialized = [7]bool{} + app.initialized = [6]bool{} } app.setAuthInputPrompt() return app @@ -128,7 +125,6 @@ func (a App) Update(msg tea.Msg) (tea.Model, tea.Cmd) { a.auth.SetSize(contentW, contentH) a.keys.SetSize(contentW, contentH) a.oauth.SetSize(contentW, contentH) - a.usage.SetSize(contentW, contentH) a.logs.SetSize(contentW, contentH) return a, nil @@ -142,7 +138,7 @@ func (a App) Update(msg tea.Msg) (tea.Model, tea.Cmd) { a.authenticated = true a.logsEnabled = a.standalone || isLogsEnabledFromConfig(msg.cfg) a.refreshTabs() - a.initialized = [7]bool{} + a.initialized = [6]bool{} a.initialized[tabDashboard] = true cmds := []tea.Cmd{a.dashboard.Init()} if a.logsEnabled { @@ -258,8 +254,6 @@ func (a App) Update(msg tea.Msg) (tea.Model, tea.Cmd) { a.keys, cmd = a.keys.Update(msg) case tabOAuth: a.oauth, cmd = a.oauth.Update(msg) - case tabUsage: - a.usage, cmd = a.usage.Update(msg) case tabLogs: a.logs, cmd = a.logs.Update(msg) } @@ -322,8 +316,6 @@ func (a *App) initTabIfNeeded(_ int) tea.Cmd { return a.keys.Init() case tabOAuth: return a.oauth.Init() - case tabUsage: - return a.usage.Init() case tabLogs: if !a.logsEnabled { return nil @@ -360,8 +352,6 @@ func (a App) View() string { sb.WriteString(a.keys.View()) case tabOAuth: sb.WriteString(a.oauth.View()) - case tabUsage: - sb.WriteString(a.usage.View()) case tabLogs: if a.logsEnabled { sb.WriteString(a.logs.View()) @@ -529,10 +519,6 @@ func (a App) broadcastToAllTabs(msg tea.Msg) (tea.Model, tea.Cmd) { if cmd != nil { cmds = append(cmds, cmd) } - a.usage, cmd = a.usage.Update(msg) - if cmd != nil { - cmds = append(cmds, cmd) - } a.logs, cmd = a.logs.Update(msg) if cmd != nil { cmds = append(cmds, cmd) diff --git a/internal/tui/client.go b/internal/tui/client.go index 6f75d6be..747f30b9 100644 --- a/internal/tui/client.go +++ b/internal/tui/client.go @@ -140,11 +140,6 @@ func (c *Client) PutConfigYAML(yamlContent string) error { return err } -// GetUsage fetches usage statistics. -func (c *Client) GetUsage() (map[string]any, error) { - return c.getJSON("/v0/management/usage") -} - // GetAuthFiles lists auth credential files. // API returns {"files": [...]}. func (c *Client) GetAuthFiles() ([]map[string]any, error) { diff --git a/internal/tui/dashboard.go b/internal/tui/dashboard.go index 8561fe9c..99b5409c 100644 --- a/internal/tui/dashboard.go +++ b/internal/tui/dashboard.go @@ -22,14 +22,12 @@ type dashboardModel struct { // Cached data for re-rendering on locale change lastConfig map[string]any - lastUsage map[string]any lastAuthFiles []map[string]any lastAPIKeys []string } type dashboardDataMsg struct { config map[string]any - usage map[string]any authFiles []map[string]any apiKeys []string err error @@ -47,25 +45,24 @@ func (m dashboardModel) Init() tea.Cmd { func (m dashboardModel) fetchData() tea.Msg { cfg, cfgErr := m.client.GetConfig() - usage, usageErr := m.client.GetUsage() authFiles, authErr := m.client.GetAuthFiles() apiKeys, keysErr := m.client.GetAPIKeys() var err error - for _, e := range []error{cfgErr, usageErr, authErr, keysErr} { + for _, e := range []error{cfgErr, authErr, keysErr} { if e != nil { err = e break } } - return dashboardDataMsg{config: cfg, usage: usage, authFiles: authFiles, apiKeys: apiKeys, err: err} + return dashboardDataMsg{config: cfg, authFiles: authFiles, apiKeys: apiKeys, err: err} } func (m dashboardModel) Update(msg tea.Msg) (dashboardModel, tea.Cmd) { switch msg := msg.(type) { case localeChangedMsg: // Re-render immediately with cached data using new locale - m.content = m.renderDashboard(m.lastConfig, m.lastUsage, m.lastAuthFiles, m.lastAPIKeys) + m.content = m.renderDashboard(m.lastConfig, m.lastAuthFiles, m.lastAPIKeys) m.viewport.SetContent(m.content) // Also fetch fresh data in background return m, m.fetchData @@ -78,11 +75,10 @@ func (m dashboardModel) Update(msg tea.Msg) (dashboardModel, tea.Cmd) { m.err = nil // Cache data for locale switching m.lastConfig = msg.config - m.lastUsage = msg.usage m.lastAuthFiles = msg.authFiles m.lastAPIKeys = msg.apiKeys - m.content = m.renderDashboard(msg.config, msg.usage, msg.authFiles, msg.apiKeys) + m.content = m.renderDashboard(msg.config, msg.authFiles, msg.apiKeys) } m.viewport.SetContent(m.content) return m, nil @@ -121,7 +117,7 @@ func (m dashboardModel) View() string { return m.viewport.View() } -func (m dashboardModel) renderDashboard(cfg, usage map[string]any, authFiles []map[string]any, apiKeys []string) string { +func (m dashboardModel) renderDashboard(cfg map[string]any, authFiles []map[string]any, apiKeys []string) string { var sb strings.Builder sb.WriteString(titleStyle.Render(T("dashboard_title"))) @@ -138,7 +134,7 @@ func (m dashboardModel) renderDashboard(cfg, usage map[string]any, authFiles []m // ━━━ Stats Cards ━━━ cardWidth := 25 if m.width > 0 { - cardWidth = (m.width - 6) / 4 + cardWidth = (m.width - 2) / 2 if cardWidth < 18 { cardWidth = 18 } @@ -173,34 +169,7 @@ func (m dashboardModel) renderDashboard(cfg, usage map[string]any, authFiles []m lipgloss.NewStyle().Foreground(colorMuted).Render(fmt.Sprintf("%s (%d %s)", T("auth_files_label"), activeAuth, T("active_suffix"))), )) - // Card 3: Total Requests - totalReqs := int64(0) - successReqs := int64(0) - failedReqs := int64(0) - totalTokens := int64(0) - if usage != nil { - if usageMap, ok := usage["usage"].(map[string]any); ok { - totalReqs = int64(getFloat(usageMap, "total_requests")) - successReqs = int64(getFloat(usageMap, "success_count")) - failedReqs = int64(getFloat(usageMap, "failure_count")) - totalTokens = int64(getFloat(usageMap, "total_tokens")) - } - } - card3 := cardStyle.Render(fmt.Sprintf( - "%s\n%s", - lipgloss.NewStyle().Bold(true).Foreground(lipgloss.Color("214")).Render(fmt.Sprintf("📈 %d", totalReqs)), - lipgloss.NewStyle().Foreground(colorMuted).Render(fmt.Sprintf("%s (✓%d ✗%d)", T("total_requests"), successReqs, failedReqs)), - )) - - // Card 4: Total Tokens - tokenStr := formatLargeNumber(totalTokens) - card4 := cardStyle.Render(fmt.Sprintf( - "%s\n%s", - lipgloss.NewStyle().Bold(true).Foreground(lipgloss.Color("170")).Render(fmt.Sprintf("🔤 %s", tokenStr)), - lipgloss.NewStyle().Foreground(colorMuted).Render(T("total_tokens")), - )) - - sb.WriteString(lipgloss.JoinHorizontal(lipgloss.Top, card1, " ", card2, " ", card3, " ", card4)) + sb.WriteString(lipgloss.JoinHorizontal(lipgloss.Top, card1, " ", card2)) sb.WriteString("\n\n") // ━━━ Current Config ━━━ @@ -258,38 +227,6 @@ func (m dashboardModel) renderDashboard(cfg, usage map[string]any, authFiles []m sb.WriteString("\n") - // ━━━ Per-Model Usage ━━━ - if usage != nil { - if usageMap, ok := usage["usage"].(map[string]any); ok { - if apis, ok := usageMap["apis"].(map[string]any); ok && len(apis) > 0 { - sb.WriteString(lipgloss.NewStyle().Bold(true).Foreground(colorHighlight).Render(T("model_stats"))) - sb.WriteString("\n") - sb.WriteString(strings.Repeat("─", minInt(m.width, 60))) - sb.WriteString("\n") - - header := fmt.Sprintf(" %-40s %10s %12s", T("model"), T("requests"), T("tokens")) - sb.WriteString(tableHeaderStyle.Render(header)) - sb.WriteString("\n") - - for _, apiSnap := range apis { - if apiMap, ok := apiSnap.(map[string]any); ok { - if models, ok := apiMap["models"].(map[string]any); ok { - for model, v := range models { - if stats, ok := v.(map[string]any); ok { - reqs := int64(getFloat(stats, "total_requests")) - toks := int64(getFloat(stats, "total_tokens")) - row := fmt.Sprintf(" %-40s %10d %12s", truncate(model, 40), reqs, formatLargeNumber(toks)) - sb.WriteString(tableCellStyle.Render(row)) - sb.WriteString("\n") - } - } - } - } - } - } - } - } - return sb.String() } diff --git a/internal/tui/i18n.go b/internal/tui/i18n.go index f6a33ca4..a4c0ac16 100644 --- a/internal/tui/i18n.go +++ b/internal/tui/i18n.go @@ -50,8 +50,8 @@ var locales = map[string]map[string]string{ // ────────────────────────────────────────── // Tab names // ────────────────────────────────────────── -var zhTabNames = []string{"仪表盘", "配置", "认证文件", "API 密钥", "OAuth", "使用统计", "日志"} -var enTabNames = []string{"Dashboard", "Config", "Auth Files", "API Keys", "OAuth", "Usage", "Logs"} +var zhTabNames = []string{"仪表盘", "配置", "认证文件", "API 密钥", "OAuth", "日志"} +var enTabNames = []string{"Dashboard", "Config", "Auth Files", "API Keys", "OAuth", "Logs"} // TabNames returns tab names in the current locale. func TabNames() []string { diff --git a/internal/tui/usage_tab.go b/internal/tui/usage_tab.go deleted file mode 100644 index 6b9fef5e..00000000 --- a/internal/tui/usage_tab.go +++ /dev/null @@ -1,418 +0,0 @@ -package tui - -import ( - "fmt" - "sort" - "strings" - - "github.com/charmbracelet/bubbles/viewport" - tea "github.com/charmbracelet/bubbletea" - "github.com/charmbracelet/lipgloss" -) - -// usageTabModel displays usage statistics with charts and breakdowns. -type usageTabModel struct { - client *Client - viewport viewport.Model - usage map[string]any - err error - width int - height int - ready bool -} - -type usageDataMsg struct { - usage map[string]any - err error -} - -func newUsageTabModel(client *Client) usageTabModel { - return usageTabModel{ - client: client, - } -} - -func (m usageTabModel) Init() tea.Cmd { - return m.fetchData -} - -func (m usageTabModel) fetchData() tea.Msg { - usage, err := m.client.GetUsage() - return usageDataMsg{usage: usage, err: err} -} - -func (m usageTabModel) Update(msg tea.Msg) (usageTabModel, tea.Cmd) { - switch msg := msg.(type) { - case localeChangedMsg: - m.viewport.SetContent(m.renderContent()) - return m, nil - case usageDataMsg: - if msg.err != nil { - m.err = msg.err - } else { - m.err = nil - m.usage = msg.usage - } - m.viewport.SetContent(m.renderContent()) - return m, nil - - case tea.KeyMsg: - if msg.String() == "r" { - return m, m.fetchData - } - var cmd tea.Cmd - m.viewport, cmd = m.viewport.Update(msg) - return m, cmd - } - - var cmd tea.Cmd - m.viewport, cmd = m.viewport.Update(msg) - return m, cmd -} - -func (m *usageTabModel) SetSize(w, h int) { - m.width = w - m.height = h - if !m.ready { - m.viewport = viewport.New(w, h) - m.viewport.SetContent(m.renderContent()) - m.ready = true - } else { - m.viewport.Width = w - m.viewport.Height = h - } -} - -func (m usageTabModel) View() string { - if !m.ready { - return T("loading") - } - return m.viewport.View() -} - -func (m usageTabModel) renderContent() string { - var sb strings.Builder - - sb.WriteString(titleStyle.Render(T("usage_title"))) - sb.WriteString("\n") - sb.WriteString(helpStyle.Render(T("usage_help"))) - sb.WriteString("\n\n") - - if m.err != nil { - sb.WriteString(errorStyle.Render("⚠ Error: " + m.err.Error())) - sb.WriteString("\n") - return sb.String() - } - - if m.usage == nil { - sb.WriteString(subtitleStyle.Render(T("usage_no_data"))) - sb.WriteString("\n") - return sb.String() - } - - usageMap, _ := m.usage["usage"].(map[string]any) - if usageMap == nil { - sb.WriteString(subtitleStyle.Render(T("usage_no_data"))) - sb.WriteString("\n") - return sb.String() - } - - totalReqs := int64(getFloat(usageMap, "total_requests")) - successCnt := int64(getFloat(usageMap, "success_count")) - failureCnt := int64(getFloat(usageMap, "failure_count")) - totalTokens := int64(getFloat(usageMap, "total_tokens")) - - // ━━━ Overview Cards ━━━ - cardWidth := 20 - if m.width > 0 { - cardWidth = (m.width - 6) / 4 - if cardWidth < 16 { - cardWidth = 16 - } - } - cardStyle := lipgloss.NewStyle(). - Border(lipgloss.RoundedBorder()). - BorderForeground(lipgloss.Color("240")). - Padding(0, 1). - Width(cardWidth). - Height(3) - - // Total Requests - card1 := cardStyle.Copy().BorderForeground(lipgloss.Color("111")).Render(fmt.Sprintf( - "%s\n%s\n%s", - lipgloss.NewStyle().Foreground(colorMuted).Render(T("usage_total_reqs")), - lipgloss.NewStyle().Bold(true).Foreground(lipgloss.Color("111")).Render(fmt.Sprintf("%d", totalReqs)), - lipgloss.NewStyle().Foreground(colorMuted).Render(fmt.Sprintf("● %s: %d ● %s: %d", T("usage_success"), successCnt, T("usage_failure"), failureCnt)), - )) - - // Total Tokens - card2 := cardStyle.Copy().BorderForeground(lipgloss.Color("214")).Render(fmt.Sprintf( - "%s\n%s\n%s", - lipgloss.NewStyle().Foreground(colorMuted).Render(T("usage_total_tokens")), - lipgloss.NewStyle().Bold(true).Foreground(lipgloss.Color("214")).Render(formatLargeNumber(totalTokens)), - lipgloss.NewStyle().Foreground(colorMuted).Render(fmt.Sprintf("%s: %s", T("usage_total_token_l"), formatLargeNumber(totalTokens))), - )) - - // RPM - rpm := float64(0) - if totalReqs > 0 { - if rByH, ok := usageMap["requests_by_hour"].(map[string]any); ok && len(rByH) > 0 { - rpm = float64(totalReqs) / float64(len(rByH)) / 60.0 - } - } - card3 := cardStyle.Copy().BorderForeground(lipgloss.Color("76")).Render(fmt.Sprintf( - "%s\n%s\n%s", - lipgloss.NewStyle().Foreground(colorMuted).Render(T("usage_rpm")), - lipgloss.NewStyle().Bold(true).Foreground(lipgloss.Color("76")).Render(fmt.Sprintf("%.2f", rpm)), - lipgloss.NewStyle().Foreground(colorMuted).Render(fmt.Sprintf("%s: %d", T("usage_total_reqs"), totalReqs)), - )) - - // TPM - tpm := float64(0) - if totalTokens > 0 { - if tByH, ok := usageMap["tokens_by_hour"].(map[string]any); ok && len(tByH) > 0 { - tpm = float64(totalTokens) / float64(len(tByH)) / 60.0 - } - } - card4 := cardStyle.Copy().BorderForeground(lipgloss.Color("170")).Render(fmt.Sprintf( - "%s\n%s\n%s", - lipgloss.NewStyle().Foreground(colorMuted).Render(T("usage_tpm")), - lipgloss.NewStyle().Bold(true).Foreground(lipgloss.Color("170")).Render(fmt.Sprintf("%.2f", tpm)), - lipgloss.NewStyle().Foreground(colorMuted).Render(fmt.Sprintf("%s: %s", T("usage_total_tokens"), formatLargeNumber(totalTokens))), - )) - - sb.WriteString(lipgloss.JoinHorizontal(lipgloss.Top, card1, " ", card2, " ", card3, " ", card4)) - sb.WriteString("\n\n") - - // ━━━ Requests by Hour (ASCII bar chart) ━━━ - if rByH, ok := usageMap["requests_by_hour"].(map[string]any); ok && len(rByH) > 0 { - sb.WriteString(lipgloss.NewStyle().Bold(true).Foreground(colorHighlight).Render(T("usage_req_by_hour"))) - sb.WriteString("\n") - sb.WriteString(strings.Repeat("─", minInt(m.width, 60))) - sb.WriteString("\n") - sb.WriteString(renderBarChart(rByH, m.width-6, lipgloss.Color("111"))) - sb.WriteString("\n") - } - - // ━━━ Tokens by Hour ━━━ - if tByH, ok := usageMap["tokens_by_hour"].(map[string]any); ok && len(tByH) > 0 { - sb.WriteString(lipgloss.NewStyle().Bold(true).Foreground(colorHighlight).Render(T("usage_tok_by_hour"))) - sb.WriteString("\n") - sb.WriteString(strings.Repeat("─", minInt(m.width, 60))) - sb.WriteString("\n") - sb.WriteString(renderBarChart(tByH, m.width-6, lipgloss.Color("214"))) - sb.WriteString("\n") - } - - // ━━━ Requests by Day ━━━ - if rByD, ok := usageMap["requests_by_day"].(map[string]any); ok && len(rByD) > 0 { - sb.WriteString(lipgloss.NewStyle().Bold(true).Foreground(colorHighlight).Render(T("usage_req_by_day"))) - sb.WriteString("\n") - sb.WriteString(strings.Repeat("─", minInt(m.width, 60))) - sb.WriteString("\n") - sb.WriteString(renderBarChart(rByD, m.width-6, lipgloss.Color("76"))) - sb.WriteString("\n") - } - - // ━━━ API Detail Stats ━━━ - if apis, ok := usageMap["apis"].(map[string]any); ok && len(apis) > 0 { - sb.WriteString(lipgloss.NewStyle().Bold(true).Foreground(colorHighlight).Render(T("usage_api_detail"))) - sb.WriteString("\n") - sb.WriteString(strings.Repeat("─", minInt(m.width, 80))) - sb.WriteString("\n") - - header := fmt.Sprintf(" %-30s %10s %12s", "API", T("requests"), T("tokens")) - sb.WriteString(tableHeaderStyle.Render(header)) - sb.WriteString("\n") - - for apiName, apiSnap := range apis { - if apiMap, ok := apiSnap.(map[string]any); ok { - apiReqs := int64(getFloat(apiMap, "total_requests")) - apiToks := int64(getFloat(apiMap, "total_tokens")) - - row := fmt.Sprintf(" %-30s %10d %12s", - truncate(maskKey(apiName), 30), apiReqs, formatLargeNumber(apiToks)) - sb.WriteString(lipgloss.NewStyle().Bold(true).Render(row)) - sb.WriteString("\n") - - // Per-model breakdown - if models, ok := apiMap["models"].(map[string]any); ok { - for model, v := range models { - if stats, ok := v.(map[string]any); ok { - mReqs := int64(getFloat(stats, "total_requests")) - mToks := int64(getFloat(stats, "total_tokens")) - mRow := fmt.Sprintf(" ├─ %-28s %10d %12s", - truncate(model, 28), mReqs, formatLargeNumber(mToks)) - sb.WriteString(tableCellStyle.Render(mRow)) - sb.WriteString("\n") - - // Token type breakdown from details - sb.WriteString(m.renderTokenBreakdown(stats)) - - // Latency breakdown from details - sb.WriteString(m.renderLatencyBreakdown(stats)) - } - } - } - } - } - } - - sb.WriteString("\n") - return sb.String() -} - -// renderTokenBreakdown aggregates input/output/cached/reasoning tokens from model details. -func (m usageTabModel) renderTokenBreakdown(modelStats map[string]any) string { - details, ok := modelStats["details"] - if !ok { - return "" - } - detailList, ok := details.([]any) - if !ok || len(detailList) == 0 { - return "" - } - - var inputTotal, outputTotal, cachedTotal, reasoningTotal int64 - for _, d := range detailList { - dm, ok := d.(map[string]any) - if !ok { - continue - } - tokens, ok := dm["tokens"].(map[string]any) - if !ok { - continue - } - inputTotal += int64(getFloat(tokens, "input_tokens")) - outputTotal += int64(getFloat(tokens, "output_tokens")) - cachedTotal += int64(getFloat(tokens, "cached_tokens")) - reasoningTotal += int64(getFloat(tokens, "reasoning_tokens")) - } - - if inputTotal == 0 && outputTotal == 0 && cachedTotal == 0 && reasoningTotal == 0 { - return "" - } - - parts := []string{} - if inputTotal > 0 { - parts = append(parts, fmt.Sprintf("%s:%s", T("usage_input"), formatLargeNumber(inputTotal))) - } - if outputTotal > 0 { - parts = append(parts, fmt.Sprintf("%s:%s", T("usage_output"), formatLargeNumber(outputTotal))) - } - if cachedTotal > 0 { - parts = append(parts, fmt.Sprintf("%s:%s", T("usage_cached"), formatLargeNumber(cachedTotal))) - } - if reasoningTotal > 0 { - parts = append(parts, fmt.Sprintf("%s:%s", T("usage_reasoning"), formatLargeNumber(reasoningTotal))) - } - - return fmt.Sprintf(" │ %s\n", - lipgloss.NewStyle().Foreground(colorMuted).Render(strings.Join(parts, " "))) -} - -// renderLatencyBreakdown aggregates latency_ms from model details and displays avg/min/max. -func (m usageTabModel) renderLatencyBreakdown(modelStats map[string]any) string { - details, ok := modelStats["details"] - if !ok { - return "" - } - detailList, ok := details.([]any) - if !ok || len(detailList) == 0 { - return "" - } - - var totalLatency int64 - var count int - var minLatency, maxLatency int64 - first := true - - for _, d := range detailList { - dm, ok := d.(map[string]any) - if !ok { - continue - } - latencyMs := int64(getFloat(dm, "latency_ms")) - if latencyMs <= 0 { - continue - } - totalLatency += latencyMs - count++ - if first { - minLatency = latencyMs - maxLatency = latencyMs - first = false - } else { - if latencyMs < minLatency { - minLatency = latencyMs - } - if latencyMs > maxLatency { - maxLatency = latencyMs - } - } - } - - if count == 0 { - return "" - } - - avgLatency := totalLatency / int64(count) - return fmt.Sprintf(" │ %s: avg %dms min %dms max %dms\n", - lipgloss.NewStyle().Foreground(colorMuted).Render(T("usage_time")), - avgLatency, minLatency, maxLatency) -} - -// renderBarChart renders a simple ASCII horizontal bar chart. -func renderBarChart(data map[string]any, maxBarWidth int, barColor lipgloss.Color) string { - if maxBarWidth < 10 { - maxBarWidth = 10 - } - - // Sort keys - keys := make([]string, 0, len(data)) - for k := range data { - keys = append(keys, k) - } - sort.Strings(keys) - - // Find max value - maxVal := float64(0) - for _, k := range keys { - v := getFloat(data, k) - if v > maxVal { - maxVal = v - } - } - if maxVal == 0 { - return "" - } - - barStyle := lipgloss.NewStyle().Foreground(barColor) - var sb strings.Builder - - labelWidth := 12 - barAvail := maxBarWidth - labelWidth - 12 - if barAvail < 5 { - barAvail = 5 - } - - for _, k := range keys { - v := getFloat(data, k) - barLen := int(v / maxVal * float64(barAvail)) - if barLen < 1 && v > 0 { - barLen = 1 - } - bar := strings.Repeat("█", barLen) - label := k - if len(label) > labelWidth { - label = label[:labelWidth] - } - sb.WriteString(fmt.Sprintf(" %-*s %s %s\n", - labelWidth, label, - barStyle.Render(bar), - lipgloss.NewStyle().Foreground(colorMuted).Render(fmt.Sprintf("%.0f", v)), - )) - } - - return sb.String() -} diff --git a/internal/tui/usage_tab_test.go b/internal/tui/usage_tab_test.go deleted file mode 100644 index 4fffcd98..00000000 --- a/internal/tui/usage_tab_test.go +++ /dev/null @@ -1,134 +0,0 @@ -package tui - -import ( - "strings" - "testing" -) - -func TestRenderLatencyBreakdown(t *testing.T) { - tests := []struct { - name string - modelStats map[string]any - wantEmpty bool - wantContains string - }{ - { - name: "no details", - modelStats: map[string]any{}, - wantEmpty: true, - }, - { - name: "empty details", - modelStats: map[string]any{ - "details": []any{}, - }, - wantEmpty: true, - }, - { - name: "details with zero latency", - modelStats: map[string]any{ - "details": []any{ - map[string]any{ - "latency_ms": float64(0), - }, - }, - }, - wantEmpty: true, - }, - { - name: "single request with latency", - modelStats: map[string]any{ - "details": []any{ - map[string]any{ - "latency_ms": float64(1500), - }, - }, - }, - wantEmpty: false, - wantContains: "avg 1500ms min 1500ms max 1500ms", - }, - { - name: "multiple requests with varying latency", - modelStats: map[string]any{ - "details": []any{ - map[string]any{ - "latency_ms": float64(100), - }, - map[string]any{ - "latency_ms": float64(200), - }, - map[string]any{ - "latency_ms": float64(300), - }, - }, - }, - wantEmpty: false, - wantContains: "avg 200ms min 100ms max 300ms", - }, - { - name: "mixed valid and invalid latency values", - modelStats: map[string]any{ - "details": []any{ - map[string]any{ - "latency_ms": float64(500), - }, - map[string]any{ - "latency_ms": float64(0), - }, - map[string]any{ - "latency_ms": float64(1500), - }, - }, - }, - wantEmpty: false, - wantContains: "avg 1000ms min 500ms max 1500ms", - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - m := usageTabModel{} - result := m.renderLatencyBreakdown(tt.modelStats) - - if tt.wantEmpty { - if result != "" { - t.Errorf("renderLatencyBreakdown() = %q, want empty string", result) - } - return - } - - if result == "" { - t.Errorf("renderLatencyBreakdown() = empty, want non-empty string") - return - } - - if tt.wantContains != "" && !strings.Contains(result, tt.wantContains) { - t.Errorf("renderLatencyBreakdown() = %q, want to contain %q", result, tt.wantContains) - } - }) - } -} - -func TestUsageTimeTranslations(t *testing.T) { - prevLocale := CurrentLocale() - t.Cleanup(func() { - SetLocale(prevLocale) - }) - - tests := []struct { - locale string - want string - }{ - {locale: "en", want: "Time"}, - {locale: "zh", want: "时间"}, - } - - for _, tt := range tests { - t.Run(tt.locale, func(t *testing.T) { - SetLocale(tt.locale) - if got := T("usage_time"); got != tt.want { - t.Fatalf("T(usage_time) = %q, want %q", got, tt.want) - } - }) - } -} diff --git a/internal/usage/logger_plugin.go b/internal/usage/logger_plugin.go deleted file mode 100644 index 9d59de4f..00000000 --- a/internal/usage/logger_plugin.go +++ /dev/null @@ -1,464 +0,0 @@ -// Package usage provides usage tracking and logging functionality for the CLI Proxy API server. -// It includes plugins for monitoring API usage, token consumption, and other metrics -// to help with observability and billing purposes. -package usage - -import ( - "context" - "fmt" - "strings" - "sync" - "sync/atomic" - "time" - - internallogging "github.com/router-for-me/CLIProxyAPI/v6/internal/logging" - coreusage "github.com/router-for-me/CLIProxyAPI/v6/sdk/cliproxy/usage" -) - -var statisticsEnabled atomic.Bool - -func init() { - statisticsEnabled.Store(true) - coreusage.RegisterPlugin(NewLoggerPlugin()) -} - -// LoggerPlugin collects in-memory request statistics for usage analysis. -// It implements coreusage.Plugin to receive usage records emitted by the runtime. -type LoggerPlugin struct { - stats *RequestStatistics -} - -// NewLoggerPlugin constructs a new logger plugin instance. -// -// Returns: -// - *LoggerPlugin: A new logger plugin instance wired to the shared statistics store. -func NewLoggerPlugin() *LoggerPlugin { return &LoggerPlugin{stats: defaultRequestStatistics} } - -// HandleUsage implements coreusage.Plugin. -// It updates the in-memory statistics store whenever a usage record is received. -// -// Parameters: -// - ctx: The context for the usage record -// - record: The usage record to aggregate -func (p *LoggerPlugin) HandleUsage(ctx context.Context, record coreusage.Record) { - if !statisticsEnabled.Load() { - return - } - if p == nil || p.stats == nil { - return - } - p.stats.Record(ctx, record) -} - -// SetStatisticsEnabled toggles whether in-memory statistics are recorded. -func SetStatisticsEnabled(enabled bool) { statisticsEnabled.Store(enabled) } - -// StatisticsEnabled reports the current recording state. -func StatisticsEnabled() bool { return statisticsEnabled.Load() } - -// RequestStatistics maintains aggregated request metrics in memory. -type RequestStatistics struct { - mu sync.RWMutex - - totalRequests int64 - successCount int64 - failureCount int64 - totalTokens int64 - - apis map[string]*apiStats - - requestsByDay map[string]int64 - requestsByHour map[int]int64 - tokensByDay map[string]int64 - tokensByHour map[int]int64 -} - -// apiStats holds aggregated metrics for a single API key. -type apiStats struct { - TotalRequests int64 - TotalTokens int64 - Models map[string]*modelStats -} - -// modelStats holds aggregated metrics for a specific model within an API. -type modelStats struct { - TotalRequests int64 - TotalTokens int64 - Details []RequestDetail -} - -// RequestDetail stores the timestamp, latency, and token usage for a single request. -type RequestDetail struct { - Timestamp time.Time `json:"timestamp"` - LatencyMs int64 `json:"latency_ms"` - Source string `json:"source"` - AuthIndex string `json:"auth_index"` - Tokens TokenStats `json:"tokens"` - Failed bool `json:"failed"` -} - -// TokenStats captures the token usage breakdown for a request. -type TokenStats struct { - InputTokens int64 `json:"input_tokens"` - OutputTokens int64 `json:"output_tokens"` - ReasoningTokens int64 `json:"reasoning_tokens"` - CachedTokens int64 `json:"cached_tokens"` - TotalTokens int64 `json:"total_tokens"` -} - -// StatisticsSnapshot represents an immutable view of the aggregated metrics. -type StatisticsSnapshot struct { - TotalRequests int64 `json:"total_requests"` - SuccessCount int64 `json:"success_count"` - FailureCount int64 `json:"failure_count"` - TotalTokens int64 `json:"total_tokens"` - - APIs map[string]APISnapshot `json:"apis"` - - RequestsByDay map[string]int64 `json:"requests_by_day"` - RequestsByHour map[string]int64 `json:"requests_by_hour"` - TokensByDay map[string]int64 `json:"tokens_by_day"` - TokensByHour map[string]int64 `json:"tokens_by_hour"` -} - -// APISnapshot summarises metrics for a single API key. -type APISnapshot struct { - TotalRequests int64 `json:"total_requests"` - TotalTokens int64 `json:"total_tokens"` - Models map[string]ModelSnapshot `json:"models"` -} - -// ModelSnapshot summarises metrics for a specific model. -type ModelSnapshot struct { - TotalRequests int64 `json:"total_requests"` - TotalTokens int64 `json:"total_tokens"` - Details []RequestDetail `json:"details"` -} - -var defaultRequestStatistics = NewRequestStatistics() - -// GetRequestStatistics returns the shared statistics store. -func GetRequestStatistics() *RequestStatistics { return defaultRequestStatistics } - -// NewRequestStatistics constructs an empty statistics store. -func NewRequestStatistics() *RequestStatistics { - return &RequestStatistics{ - apis: make(map[string]*apiStats), - requestsByDay: make(map[string]int64), - requestsByHour: make(map[int]int64), - tokensByDay: make(map[string]int64), - tokensByHour: make(map[int]int64), - } -} - -// Record ingests a new usage record and updates the aggregates. -func (s *RequestStatistics) Record(ctx context.Context, record coreusage.Record) { - if s == nil { - return - } - if !statisticsEnabled.Load() { - return - } - timestamp := record.RequestedAt - if timestamp.IsZero() { - timestamp = time.Now() - } - detail := normaliseDetail(record.Detail) - totalTokens := detail.TotalTokens - statsKey := record.APIKey - if statsKey == "" { - statsKey = resolveAPIIdentifier(ctx, record) - } - failed := record.Failed - if !failed { - failed = !resolveSuccess(ctx) - } - success := !failed - modelName := record.Model - if modelName == "" { - modelName = "unknown" - } - dayKey := timestamp.Format("2006-01-02") - hourKey := timestamp.Hour() - - s.mu.Lock() - defer s.mu.Unlock() - - s.totalRequests++ - if success { - s.successCount++ - } else { - s.failureCount++ - } - s.totalTokens += totalTokens - - stats, ok := s.apis[statsKey] - if !ok { - stats = &apiStats{Models: make(map[string]*modelStats)} - s.apis[statsKey] = stats - } - s.updateAPIStats(stats, modelName, RequestDetail{ - Timestamp: timestamp, - LatencyMs: normaliseLatency(record.Latency), - Source: record.Source, - AuthIndex: record.AuthIndex, - Tokens: detail, - Failed: failed, - }) - - s.requestsByDay[dayKey]++ - s.requestsByHour[hourKey]++ - s.tokensByDay[dayKey] += totalTokens - s.tokensByHour[hourKey] += totalTokens -} - -func (s *RequestStatistics) updateAPIStats(stats *apiStats, model string, detail RequestDetail) { - stats.TotalRequests++ - stats.TotalTokens += detail.Tokens.TotalTokens - modelStatsValue, ok := stats.Models[model] - if !ok { - modelStatsValue = &modelStats{} - stats.Models[model] = modelStatsValue - } - modelStatsValue.TotalRequests++ - modelStatsValue.TotalTokens += detail.Tokens.TotalTokens - modelStatsValue.Details = append(modelStatsValue.Details, detail) -} - -// Snapshot returns a copy of the aggregated metrics for external consumption. -func (s *RequestStatistics) Snapshot() StatisticsSnapshot { - result := StatisticsSnapshot{} - if s == nil { - return result - } - - s.mu.RLock() - defer s.mu.RUnlock() - - result.TotalRequests = s.totalRequests - result.SuccessCount = s.successCount - result.FailureCount = s.failureCount - result.TotalTokens = s.totalTokens - - result.APIs = make(map[string]APISnapshot, len(s.apis)) - for apiName, stats := range s.apis { - apiSnapshot := APISnapshot{ - TotalRequests: stats.TotalRequests, - TotalTokens: stats.TotalTokens, - Models: make(map[string]ModelSnapshot, len(stats.Models)), - } - for modelName, modelStatsValue := range stats.Models { - requestDetails := make([]RequestDetail, len(modelStatsValue.Details)) - copy(requestDetails, modelStatsValue.Details) - apiSnapshot.Models[modelName] = ModelSnapshot{ - TotalRequests: modelStatsValue.TotalRequests, - TotalTokens: modelStatsValue.TotalTokens, - Details: requestDetails, - } - } - result.APIs[apiName] = apiSnapshot - } - - result.RequestsByDay = make(map[string]int64, len(s.requestsByDay)) - for k, v := range s.requestsByDay { - result.RequestsByDay[k] = v - } - - result.RequestsByHour = make(map[string]int64, len(s.requestsByHour)) - for hour, v := range s.requestsByHour { - key := formatHour(hour) - result.RequestsByHour[key] = v - } - - result.TokensByDay = make(map[string]int64, len(s.tokensByDay)) - for k, v := range s.tokensByDay { - result.TokensByDay[k] = v - } - - result.TokensByHour = make(map[string]int64, len(s.tokensByHour)) - for hour, v := range s.tokensByHour { - key := formatHour(hour) - result.TokensByHour[key] = v - } - - return result -} - -type MergeResult struct { - Added int64 `json:"added"` - Skipped int64 `json:"skipped"` -} - -// MergeSnapshot merges an exported statistics snapshot into the current store. -// Existing data is preserved and duplicate request details are skipped. -func (s *RequestStatistics) MergeSnapshot(snapshot StatisticsSnapshot) MergeResult { - result := MergeResult{} - if s == nil { - return result - } - - s.mu.Lock() - defer s.mu.Unlock() - - seen := make(map[string]struct{}) - for apiName, stats := range s.apis { - if stats == nil { - continue - } - for modelName, modelStatsValue := range stats.Models { - if modelStatsValue == nil { - continue - } - for _, detail := range modelStatsValue.Details { - seen[dedupKey(apiName, modelName, detail)] = struct{}{} - } - } - } - - for apiName, apiSnapshot := range snapshot.APIs { - apiName = strings.TrimSpace(apiName) - if apiName == "" { - continue - } - stats, ok := s.apis[apiName] - if !ok || stats == nil { - stats = &apiStats{Models: make(map[string]*modelStats)} - s.apis[apiName] = stats - } else if stats.Models == nil { - stats.Models = make(map[string]*modelStats) - } - for modelName, modelSnapshot := range apiSnapshot.Models { - modelName = strings.TrimSpace(modelName) - if modelName == "" { - modelName = "unknown" - } - for _, detail := range modelSnapshot.Details { - detail.Tokens = normaliseTokenStats(detail.Tokens) - if detail.LatencyMs < 0 { - detail.LatencyMs = 0 - } - if detail.Timestamp.IsZero() { - detail.Timestamp = time.Now() - } - key := dedupKey(apiName, modelName, detail) - if _, exists := seen[key]; exists { - result.Skipped++ - continue - } - seen[key] = struct{}{} - s.recordImported(apiName, modelName, stats, detail) - result.Added++ - } - } - } - - return result -} - -func (s *RequestStatistics) recordImported(apiName, modelName string, stats *apiStats, detail RequestDetail) { - totalTokens := detail.Tokens.TotalTokens - if totalTokens < 0 { - totalTokens = 0 - } - - s.totalRequests++ - if detail.Failed { - s.failureCount++ - } else { - s.successCount++ - } - s.totalTokens += totalTokens - - s.updateAPIStats(stats, modelName, detail) - - dayKey := detail.Timestamp.Format("2006-01-02") - hourKey := detail.Timestamp.Hour() - - s.requestsByDay[dayKey]++ - s.requestsByHour[hourKey]++ - s.tokensByDay[dayKey] += totalTokens - s.tokensByHour[hourKey] += totalTokens -} - -func dedupKey(apiName, modelName string, detail RequestDetail) string { - timestamp := detail.Timestamp.UTC().Format(time.RFC3339Nano) - tokens := normaliseTokenStats(detail.Tokens) - return fmt.Sprintf( - "%s|%s|%s|%s|%s|%t|%d|%d|%d|%d|%d", - apiName, - modelName, - timestamp, - detail.Source, - detail.AuthIndex, - detail.Failed, - tokens.InputTokens, - tokens.OutputTokens, - tokens.ReasoningTokens, - tokens.CachedTokens, - tokens.TotalTokens, - ) -} - -func resolveAPIIdentifier(ctx context.Context, record coreusage.Record) string { - if ctx != nil { - if endpoint := strings.TrimSpace(internallogging.GetEndpoint(ctx)); endpoint != "" { - return endpoint - } - } - if record.Provider != "" { - return record.Provider - } - return "unknown" -} - -func resolveSuccess(ctx context.Context) bool { - status := internallogging.GetResponseStatus(ctx) - if status == 0 { - return true - } - return status < httpStatusBadRequest -} - -const httpStatusBadRequest = 400 - -func normaliseDetail(detail coreusage.Detail) TokenStats { - tokens := TokenStats{ - InputTokens: detail.InputTokens, - OutputTokens: detail.OutputTokens, - ReasoningTokens: detail.ReasoningTokens, - CachedTokens: detail.CachedTokens, - TotalTokens: detail.TotalTokens, - } - if tokens.TotalTokens == 0 { - tokens.TotalTokens = detail.InputTokens + detail.OutputTokens + detail.ReasoningTokens - } - if tokens.TotalTokens == 0 { - tokens.TotalTokens = detail.InputTokens + detail.OutputTokens + detail.ReasoningTokens + detail.CachedTokens - } - return tokens -} - -func normaliseTokenStats(tokens TokenStats) TokenStats { - if tokens.TotalTokens == 0 { - tokens.TotalTokens = tokens.InputTokens + tokens.OutputTokens + tokens.ReasoningTokens - } - if tokens.TotalTokens == 0 { - tokens.TotalTokens = tokens.InputTokens + tokens.OutputTokens + tokens.ReasoningTokens + tokens.CachedTokens - } - return tokens -} - -func normaliseLatency(latency time.Duration) int64 { - if latency <= 0 { - return 0 - } - return latency.Milliseconds() -} - -func formatHour(hour int) string { - if hour < 0 { - hour = 0 - } - hour = hour % 24 - return fmt.Sprintf("%02d", hour) -} diff --git a/internal/usage/logger_plugin_test.go b/internal/usage/logger_plugin_test.go deleted file mode 100644 index 842b3f0c..00000000 --- a/internal/usage/logger_plugin_test.go +++ /dev/null @@ -1,96 +0,0 @@ -package usage - -import ( - "context" - "testing" - "time" - - coreusage "github.com/router-for-me/CLIProxyAPI/v6/sdk/cliproxy/usage" -) - -func TestRequestStatisticsRecordIncludesLatency(t *testing.T) { - stats := NewRequestStatistics() - stats.Record(context.Background(), coreusage.Record{ - APIKey: "test-key", - Model: "gpt-5.4", - RequestedAt: time.Date(2026, 3, 20, 12, 0, 0, 0, time.UTC), - Latency: 1500 * time.Millisecond, - Detail: coreusage.Detail{ - InputTokens: 10, - OutputTokens: 20, - TotalTokens: 30, - }, - }) - - snapshot := stats.Snapshot() - details := snapshot.APIs["test-key"].Models["gpt-5.4"].Details - if len(details) != 1 { - t.Fatalf("details len = %d, want 1", len(details)) - } - if details[0].LatencyMs != 1500 { - t.Fatalf("latency_ms = %d, want 1500", details[0].LatencyMs) - } -} - -func TestRequestStatisticsMergeSnapshotDedupIgnoresLatency(t *testing.T) { - stats := NewRequestStatistics() - timestamp := time.Date(2026, 3, 20, 12, 0, 0, 0, time.UTC) - first := StatisticsSnapshot{ - APIs: map[string]APISnapshot{ - "test-key": { - Models: map[string]ModelSnapshot{ - "gpt-5.4": { - Details: []RequestDetail{{ - Timestamp: timestamp, - LatencyMs: 0, - Source: "user@example.com", - AuthIndex: "0", - Tokens: TokenStats{ - InputTokens: 10, - OutputTokens: 20, - TotalTokens: 30, - }, - }}, - }, - }, - }, - }, - } - second := StatisticsSnapshot{ - APIs: map[string]APISnapshot{ - "test-key": { - Models: map[string]ModelSnapshot{ - "gpt-5.4": { - Details: []RequestDetail{{ - Timestamp: timestamp, - LatencyMs: 2500, - Source: "user@example.com", - AuthIndex: "0", - Tokens: TokenStats{ - InputTokens: 10, - OutputTokens: 20, - TotalTokens: 30, - }, - }}, - }, - }, - }, - }, - } - - result := stats.MergeSnapshot(first) - if result.Added != 1 || result.Skipped != 0 { - t.Fatalf("first merge = %+v, want added=1 skipped=0", result) - } - - result = stats.MergeSnapshot(second) - if result.Added != 0 || result.Skipped != 1 { - t.Fatalf("second merge = %+v, want added=0 skipped=1", result) - } - - snapshot := stats.Snapshot() - details := snapshot.APIs["test-key"].Models["gpt-5.4"].Details - if len(details) != 1 { - t.Fatalf("details len = %d, want 1", len(details)) - } -} diff --git a/sdk/cliproxy/service.go b/sdk/cliproxy/service.go index d9613150..9f195f56 100644 --- a/sdk/cliproxy/service.go +++ b/sdk/cliproxy/service.go @@ -16,7 +16,6 @@ import ( _ "github.com/router-for-me/CLIProxyAPI/v6/internal/redisqueue" "github.com/router-for-me/CLIProxyAPI/v6/internal/registry" "github.com/router-for-me/CLIProxyAPI/v6/internal/runtime/executor" - _ "github.com/router-for-me/CLIProxyAPI/v6/internal/usage" "github.com/router-for-me/CLIProxyAPI/v6/internal/watcher" "github.com/router-for-me/CLIProxyAPI/v6/internal/wsrelay" sdkaccess "github.com/router-for-me/CLIProxyAPI/v6/sdk/access" diff --git a/test/usage_logging_test.go b/test/usage_logging_test.go index 41c2ee34..ee03c4d7 100644 --- a/test/usage_logging_test.go +++ b/test/usage_logging_test.go @@ -2,6 +2,7 @@ package test import ( "context" + "encoding/json" "fmt" "net/http" "net/http/httptest" @@ -9,14 +10,14 @@ import ( "time" "github.com/router-for-me/CLIProxyAPI/v6/internal/config" + "github.com/router-for-me/CLIProxyAPI/v6/internal/redisqueue" runtimeexecutor "github.com/router-for-me/CLIProxyAPI/v6/internal/runtime/executor" - internalusage "github.com/router-for-me/CLIProxyAPI/v6/internal/usage" cliproxyauth "github.com/router-for-me/CLIProxyAPI/v6/sdk/cliproxy/auth" cliproxyexecutor "github.com/router-for-me/CLIProxyAPI/v6/sdk/cliproxy/executor" sdktranslator "github.com/router-for-me/CLIProxyAPI/v6/sdk/translator" ) -func TestGeminiExecutorRecordsSuccessfulZeroUsageInStatistics(t *testing.T) { +func TestGeminiExecutorRecordsSuccessfulZeroUsageInQueue(t *testing.T) { model := fmt.Sprintf("gemini-2.5-flash-zero-usage-%d", time.Now().UnixNano()) source := fmt.Sprintf("zero-usage-%d@example.com", time.Now().UnixNano()) @@ -42,10 +43,15 @@ func TestGeminiExecutorRecordsSuccessfulZeroUsageInStatistics(t *testing.T) { }, } - prevStatsEnabled := internalusage.StatisticsEnabled() - internalusage.SetStatisticsEnabled(true) + prevQueueEnabled := redisqueue.Enabled() + prevUsageEnabled := redisqueue.UsageStatisticsEnabled() + redisqueue.SetEnabled(false) + redisqueue.SetEnabled(true) + redisqueue.SetUsageStatisticsEnabled(true) t.Cleanup(func() { - internalusage.SetStatisticsEnabled(prevStatsEnabled) + redisqueue.SetEnabled(false) + redisqueue.SetEnabled(prevQueueEnabled) + redisqueue.SetUsageStatisticsEnabled(prevUsageEnabled) }) _, err := executor.Execute(context.Background(), auth, cliproxyexecutor.Request{ @@ -59,39 +65,58 @@ func TestGeminiExecutorRecordsSuccessfulZeroUsageInStatistics(t *testing.T) { t.Fatalf("Execute error: %v", err) } - detail := waitForStatisticsDetail(t, "gemini", model, source) - if detail.Failed { - t.Fatalf("detail failed = true, want false") - } - if detail.Tokens.TotalTokens != 0 { - t.Fatalf("total tokens = %d, want 0", detail.Tokens.TotalTokens) - } + waitForQueuedUsageModelTotalTokens(t, "gemini", model, 0) } -func waitForStatisticsDetail(t *testing.T, apiName, model, source string) internalusage.RequestDetail { +func waitForQueuedUsageModelTotalTokens(t *testing.T, wantProvider, wantModel string, wantTokens int64) { t.Helper() deadline := time.Now().Add(2 * time.Second) for time.Now().Before(deadline) { - snapshot := internalusage.GetRequestStatistics().Snapshot() - apiSnapshot, ok := snapshot.APIs[apiName] - if !ok { - time.Sleep(10 * time.Millisecond) - continue - } - modelSnapshot, ok := apiSnapshot.Models[model] - if !ok { - time.Sleep(10 * time.Millisecond) - continue - } - for _, detail := range modelSnapshot.Details { - if detail.Source == source { - return detail + items := redisqueue.PopOldest(10) + for _, item := range items { + got, ok := parseQueuedUsagePayload(t, item) + if !ok { + continue } + if got.Provider != wantProvider || got.Model != wantModel { + continue + } + if got.Failed { + t.Fatalf("payload failed = true, want false") + } + if got.Tokens.TotalTokens != wantTokens { + t.Fatalf("payload total tokens = %d, want %d", got.Tokens.TotalTokens, wantTokens) + } + return } time.Sleep(10 * time.Millisecond) } - t.Fatalf("timed out waiting for statistics detail for api=%q model=%q source=%q", apiName, model, source) - return internalusage.RequestDetail{} + t.Fatalf("timed out waiting for queued usage payload for provider=%q model=%q", wantProvider, wantModel) +} + +type queuedUsagePayload struct { + Provider string `json:"provider"` + Model string `json:"model"` + Failed bool `json:"failed"` + Tokens struct { + TotalTokens int64 `json:"total_tokens"` + } `json:"tokens"` +} + +func parseQueuedUsagePayload(t *testing.T, payload []byte) (queuedUsagePayload, bool) { + t.Helper() + + var parsed queuedUsagePayload + if len(payload) == 0 { + return parsed, false + } + if err := json.Unmarshal(payload, &parsed); err != nil { + return parsed, false + } + if parsed.Provider == "" || parsed.Model == "" { + return parsed, false + } + return parsed, true } -- 2.51.2 From 79579c34bf9ea72f51ccaea53908741d84d05829 Mon Sep 17 00:00:00 2001 From: Luis Pater Date: Sat, 2 May 2026 13:35:19 +0800 Subject: [PATCH 23/51] docs: update README to consolidate and clarify CPA Usage Keeper details - Moved CPA Usage Keeper from "CLI tools" to a dedicated "Usage Statistics" section. - Added details on its functionality, periodic data sync, SQLite storage, and built-in dashboard. - Applied updates across English, Chinese, and Japanese README files for consistency. --- README.md | 12 ++++++++---- README_CN.md | 12 ++++++++---- README_JA.md | 12 ++++++++---- 3 files changed, 24 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 93ef6f71..3958668e 100644 --- a/README.md +++ b/README.md @@ -74,6 +74,14 @@ CLIProxyAPI Guides: [https://help.router-for.me/](https://help.router-for.me/) see [MANAGEMENT_API.md](https://help.router-for.me/management/api) +## Usage Statistics + +Since v6.10.0, CLIProxyAPI and [CPAMC](https://github.com/router-for-me/Cli-Proxy-API-Management-Center) no longer ship built-in usage statistics. If you need usage statistics, use: + +### [CPA Usage Keeper](https://github.com/Willxup/cpa-usage-keeper) + +Standalone persistence and visualization service for CLIProxyAPI, with periodic data sync, SQLite storage, aggregate APIs, and a built-in dashboard for usage and statistics. + ## Amp CLI Support CLIProxyAPI includes integrated support for [Amp CLI](https://ampcode.com) and Amp IDE extensions, enabling you to use your Google/ChatGPT/Claude OAuth subscriptions with Amp's coding tools: @@ -183,10 +191,6 @@ Cross-platform desktop app (macOS, Windows, Linux) wrapping CLIProxyAPI with a n Ready-to-use cross-platform quota inspector for CLIProxyAPI, supporting per-account codex 5h/7d quota windows, plan-based sorting, status coloring, and multi-account summary analytics. -### [CPA Usage Keeper](https://github.com/Willxup/cpa-usage-keeper) - -Standalone persistence and visualization service for CLIProxyAPI, with periodic data sync, SQLite storage, aggregate APIs, and a built-in dashboard for usage and statistics. - ### [CodexCliPlus](https://github.com/C4AL/CodexCliPlus) Windows-focused, local-first desktop management platform for Codex CLI built on CLIProxyAPI, focused on simplifying local setup, account and runtime management, and providing a more complete Codex CLI experience for local users. diff --git a/README_CN.md b/README_CN.md index 6199095c..5c341d27 100644 --- a/README_CN.md +++ b/README_CN.md @@ -74,6 +74,14 @@ CLIProxyAPI 用户手册: [https://help.router-for.me/](https://help.router-fo 请参见 [MANAGEMENT_API_CN.md](https://help.router-for.me/cn/management/api) +## 使用量统计 + +自v6.10.0版本以后,CLIProxyAPI及 [CPAMC](https://github.com/router-for-me/Cli-Proxy-API-Management-Center) 项目不再预置数据统计功能,如果有数据统计需求的请使用以下项目: + +### [CPA Usage Keeper](https://github.com/Willxup/cpa-usage-keeper) + +独立的 CLIProxyAPI 使用量持久化与可视化服务,定期同步 CLIProxyAPI 数据,存储到 SQLite,提供聚合 API,并内置使用量分析与统计仪表盘。 + ## Amp CLI 支持 CLIProxyAPI 已内置对 [Amp CLI](https://ampcode.com) 和 Amp IDE 扩展的支持,可让你使用自己的 Google/ChatGPT/Claude OAuth 订阅来配合 Amp 编码工具: @@ -179,10 +187,6 @@ Shadow AI 是一款专为受限环境设计的 AI 辅助工具。提供无窗口 上手即用的面向 CLIProxyAPI 跨平台配额查询工具,支持按账号展示 codex 5h/7d 配额窗口、按计划排序、状态着色及多账号汇总分析。 -### [CPA Usage Keeper](https://github.com/Willxup/cpa-usage-keeper) - -独立的 CLIProxyAPI 使用量持久化与可视化服务,定期同步 CPA 数据,存储到 SQLite,提供聚合 API,并内置使用量分析与统计仪表盘。 - ### [CodexCliPlus](https://github.com/C4AL/CodexCliPlus) 基于 CLIProxyAPI 的 Windows Codex CLI 本地优先桌面管理平台,聚焦简化本机配置、账号与运行状态管理,并为本地用户提供更完整的 Codex CLI 使用体验。 diff --git a/README_JA.md b/README_JA.md index 1bb30d48..cbb37767 100644 --- a/README_JA.md +++ b/README_JA.md @@ -72,6 +72,14 @@ CLIProxyAPIガイド:[https://help.router-for.me/](https://help.router-for.me/ [MANAGEMENT_API.md](https://help.router-for.me/management/api)を参照 +## 使用量統計 + +v6.10.0以降、CLIProxyAPIおよび [CPAMC](https://github.com/router-for-me/Cli-Proxy-API-Management-Center) プロジェクトには使用量統計機能がプリセットされなくなりました。使用量統計が必要な場合は、次のプロジェクトをご利用ください: + +### [CPA Usage Keeper](https://github.com/Willxup/cpa-usage-keeper) + +CLIProxyAPI向けの独立した使用量永続化・可視化サービス。CLIProxyAPIデータを定期同期してSQLiteに保存し、集計APIと、使用量や各種統計を確認できる組み込みダッシュボードを提供します。 + ## Amp CLIサポート CLIProxyAPIは[Amp CLI](https://ampcode.com)およびAmp IDE拡張機能の統合サポートを含んでおり、Google/ChatGPT/ClaudeのOAuthサブスクリプションをAmpのコーディングツールで使用できます: @@ -178,10 +186,6 @@ CLIProxyAPIをネイティブGUIでラップしたクロスプラットフォー CLIProxyAPI向けのすぐに使えるクロスプラットフォームのクォータ確認ツール。アカウントごとの codex 5h/7d クォータ表示、プラン別ソート、ステータス色分け、複数アカウントの集計分析に対応。 -### [CPA Usage Keeper](https://github.com/Willxup/cpa-usage-keeper) - -CLIProxyAPI向けの独立した使用量永続化・可視化サービス。CPAデータを定期同期してSQLiteに保存し、集計APIと、使用量や各種統計を確認できる組み込みダッシュボードを提供します。 - ### [CodexCliPlus](https://github.com/C4AL/CodexCliPlus) CLIProxyAPIを基盤にしたWindows向けのローカル優先Codex CLIデスクトップ管理プラットフォーム。ローカル設定、アカウント、実行状態の管理を簡素化し、ローカルユーザーにより包括的なCodex CLI体験を提供します。 -- 2.51.2 From 2efa56dbb8191f02a0c43aee0075fa04cb899775 Mon Sep 17 00:00:00 2001 From: daishuge Date: Sat, 2 May 2026 15:34:57 +0800 Subject: [PATCH 24/51] docs: add Playful Proxy API Panel --- README.md | 4 ++++ README_CN.md | 4 ++++ README_JA.md | 4 ++++ 3 files changed, 12 insertions(+) diff --git a/README.md b/README.md index 3958668e..47ea6909 100644 --- a/README.md +++ b/README.md @@ -212,6 +212,10 @@ Never stop coding. Smart routing to FREE & low-cost AI models with automatic fal OmniRoute is an AI gateway for multi-provider LLMs: an OpenAI-compatible endpoint with smart routing, load balancing, retries, and fallbacks. Add policies, rate limits, caching, and observability for reliable, cost-aware inference. +### [Playful Proxy API Panel (PPAP)](https://github.com/daishuge/playful-proxy-api-panel) + +A public CLIProxyAPI-compatible fork and bundled management panel. It keeps upstream-style usage while restoring built-in usage statistics, adding cache hit rate, first-byte latency, TPS tracking, and Docker-oriented self-hosted installation docs. + > [!NOTE] > If you have developed a port of CLIProxyAPI or a project inspired by it, please open a PR to add it to this list. diff --git a/README_CN.md b/README_CN.md index 5c341d27..e9b9c2a4 100644 --- a/README_CN.md +++ b/README_CN.md @@ -208,6 +208,10 @@ Shadow AI 是一款专为受限环境设计的 AI 辅助工具。提供无窗口 OmniRoute 是一个面向多供应商大语言模型的 AI 网关:它提供兼容 OpenAI 的端点,具备智能路由、负载均衡、重试及回退机制。通过添加策略、速率限制、缓存和可观测性,确保推理过程既可靠又具备成本意识。 +### [Playful Proxy API Panel (PPAP)](https://github.com/daishuge/playful-proxy-api-panel) + +一个公开的 CLIProxyAPI 兼容二开版本和配套管理面板,尽量保持与上游一致的使用方式,同时恢复内置使用量统计,并补充缓存命中率、首字响应时间、TPS 记录和面向 Docker 自托管的安装说明。 + > [!NOTE] > 如果你开发了 CLIProxyAPI 的移植或衍生项目,请提交 PR 将其添加到此列表中。 diff --git a/README_JA.md b/README_JA.md index cbb37767..58ad22cf 100644 --- a/README_JA.md +++ b/README_JA.md @@ -207,6 +207,10 @@ CLIProxyAPIに触発されたNext.js実装。インストールと使用が簡 OmniRouteはマルチプロバイダーLLM向けのAIゲートウェイです:スマートルーティング、負荷分散、リトライ、フォールバックを備えたOpenAI互換エンドポイント。ポリシー、レート制限、キャッシュ、可観測性を追加して、信頼性が高くコストを意識した推論を実現します。 +### [Playful Proxy API Panel (PPAP)](https://github.com/daishuge/playful-proxy-api-panel) + +上流に近い使い方を維持する公開CLIProxyAPI互換フォーク兼管理パネルです。内蔵の使用量統計を復元し、キャッシュヒット率、初回バイト待ち時間、TPSの記録、Docker向けのセルフホスト手順を追加しています。 + > [!NOTE] > CLIProxyAPIの移植版またはそれに触発されたプロジェクトを開発した場合は、PRを送ってこのリストに追加してください。 -- 2.51.2 From 56df36895a0ed21720a3aa315f5b394f8b20b1b3 Mon Sep 17 00:00:00 2001 From: Luis Pater Date: Sat, 2 May 2026 20:43:16 +0800 Subject: [PATCH 25/51] feat: add configurable retention period for Redis usage queue - Introduced `redis-usage-queue-retention-seconds` config parameter with a default of 60 seconds and a max of 3600 seconds. - Updated logic in `redisqueue` to honor configurable retention periods for enqueued usage data. - Modified config validation and initialization to support and enforce retention limits. - Enhanced change tracking in `config_diff` to detect updates to this parameter. --- cmd/server/main.go | 1 + config.example.yaml | 4 ++++ internal/api/server.go | 4 ++++ internal/config/config.go | 13 ++++++++++++ internal/redisqueue/queue.go | 30 ++++++++++++++++++++++++---- internal/watcher/diff/config_diff.go | 3 +++ 6 files changed, 51 insertions(+), 4 deletions(-) diff --git a/cmd/server/main.go b/cmd/server/main.go index e735b144..b10bc9c8 100644 --- a/cmd/server/main.go +++ b/cmd/server/main.go @@ -418,6 +418,7 @@ func main() { } } redisqueue.SetUsageStatisticsEnabled(cfg.UsageStatisticsEnabled) + redisqueue.SetRetentionSeconds(cfg.RedisUsageQueueRetentionSeconds) coreauth.SetQuotaCooldownDisabled(cfg.DisableCooling) if err = logging.ConfigureLogOutput(cfg); err != nil { diff --git a/config.example.yaml b/config.example.yaml index 172e961f..d7d5a9f5 100644 --- a/config.example.yaml +++ b/config.example.yaml @@ -66,6 +66,10 @@ error-logs-max-files: 10 # When false, disable in-memory usage statistics aggregation usage-statistics-enabled: false +# How long (in seconds) Redis usage queue items are retained in memory for the RESP interface (LPOP/RPOP). +# Default: 60. Max: 3600. +redis-usage-queue-retention-seconds: 60 + # Proxy URL. Supports socks5/http/https protocols. Example: socks5://user:pass@192.168.1.1:1080/ # Per-entry proxy-url also supports "direct" or "none" to bypass both the global proxy-url and environment proxies explicitly. proxy-url: "" diff --git a/internal/api/server.go b/internal/api/server.go index 176bc2a3..2e89ac5a 100644 --- a/internal/api/server.go +++ b/internal/api/server.go @@ -1000,6 +1000,10 @@ func (s *Server) UpdateClients(cfg *config.Config) { redisqueue.SetUsageStatisticsEnabled(cfg.UsageStatisticsEnabled) } + if oldCfg == nil || oldCfg.RedisUsageQueueRetentionSeconds != cfg.RedisUsageQueueRetentionSeconds { + redisqueue.SetRetentionSeconds(cfg.RedisUsageQueueRetentionSeconds) + } + if s.requestLogger != nil && (oldCfg == nil || oldCfg.ErrorLogsMaxFiles != cfg.ErrorLogsMaxFiles) { if setter, ok := s.requestLogger.(interface{ SetErrorLogsMaxFiles(int) }); ok { setter.SetErrorLogsMaxFiles(cfg.ErrorLogsMaxFiles) diff --git a/internal/config/config.go b/internal/config/config.go index 39c91127..46ce4f50 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -65,6 +65,11 @@ type Config struct { // UsageStatisticsEnabled toggles in-memory usage aggregation; when false, usage data is discarded. UsageStatisticsEnabled bool `yaml:"usage-statistics-enabled" json:"usage-statistics-enabled"` + // RedisUsageQueueRetentionSeconds controls how long (in seconds) usage queue items + // are retained in memory for the Redis RESP interface (LPOP/RPOP). + // Default: 60. Max: 3600. + RedisUsageQueueRetentionSeconds int `yaml:"redis-usage-queue-retention-seconds" json:"redis-usage-queue-retention-seconds"` + // DisableCooling disables quota cooldown scheduling when true. DisableCooling bool `yaml:"disable-cooling" json:"disable-cooling"` @@ -609,6 +614,7 @@ func LoadConfigOptional(configFile string, optional bool) (*Config, error) { cfg.LogsMaxTotalSizeMB = 0 cfg.ErrorLogsMaxFiles = 10 cfg.UsageStatisticsEnabled = false + cfg.RedisUsageQueueRetentionSeconds = 60 cfg.DisableCooling = false cfg.DisableImageGeneration = DisableImageGenerationOff cfg.Pprof.Enable = false @@ -671,6 +677,13 @@ func LoadConfigOptional(configFile string, optional bool) (*Config, error) { cfg.ErrorLogsMaxFiles = 10 } + if cfg.RedisUsageQueueRetentionSeconds <= 0 { + cfg.RedisUsageQueueRetentionSeconds = 60 + } else if cfg.RedisUsageQueueRetentionSeconds > 3600 { + log.WithField("value", cfg.RedisUsageQueueRetentionSeconds).Warn("redis-usage-queue-retention-seconds too large; clamping to 3600") + cfg.RedisUsageQueueRetentionSeconds = 3600 + } + if cfg.MaxRetryCredentials < 0 { cfg.MaxRetryCredentials = 0 } diff --git a/internal/redisqueue/queue.go b/internal/redisqueue/queue.go index 8a4b6742..2fea5839 100644 --- a/internal/redisqueue/queue.go +++ b/internal/redisqueue/queue.go @@ -6,7 +6,10 @@ import ( "time" ) -const retentionWindow = time.Minute +const ( + defaultRetentionSeconds int64 = 60 + maxRetentionSeconds int64 = 3600 +) type queueItem struct { enqueuedAt time.Time @@ -20,10 +23,15 @@ type queue struct { } var ( - enabled atomic.Bool - global queue + enabled atomic.Bool + retentionSeconds atomic.Int64 + global queue ) +func init() { + retentionSeconds.Store(defaultRetentionSeconds) +} + func SetEnabled(value bool) { enabled.Store(value) if !value { @@ -35,6 +43,16 @@ func Enabled() bool { return enabled.Load() } +func SetRetentionSeconds(value int) { + normalized := int64(value) + if normalized <= 0 { + normalized = defaultRetentionSeconds + } else if normalized > maxRetentionSeconds { + normalized = maxRetentionSeconds + } + retentionSeconds.Store(normalized) +} + func Enqueue(payload []byte) { if !Enabled() { return @@ -110,7 +128,11 @@ func (q *queue) pruneLocked(now time.Time) { return } - cutoff := now.Add(-retentionWindow) + windowSeconds := retentionSeconds.Load() + if windowSeconds <= 0 { + windowSeconds = defaultRetentionSeconds + } + cutoff := now.Add(-time.Duration(windowSeconds) * time.Second) for q.head < len(q.items) && q.items[q.head].enqueuedAt.Before(cutoff) { q.head++ } diff --git a/internal/watcher/diff/config_diff.go b/internal/watcher/diff/config_diff.go index 2be9aa90..b414ed5a 100644 --- a/internal/watcher/diff/config_diff.go +++ b/internal/watcher/diff/config_diff.go @@ -39,6 +39,9 @@ func BuildConfigChangeDetails(oldCfg, newCfg *config.Config) []string { if oldCfg.UsageStatisticsEnabled != newCfg.UsageStatisticsEnabled { changes = append(changes, fmt.Sprintf("usage-statistics-enabled: %t -> %t", oldCfg.UsageStatisticsEnabled, newCfg.UsageStatisticsEnabled)) } + if oldCfg.RedisUsageQueueRetentionSeconds != newCfg.RedisUsageQueueRetentionSeconds { + changes = append(changes, fmt.Sprintf("redis-usage-queue-retention-seconds: %d -> %d", oldCfg.RedisUsageQueueRetentionSeconds, newCfg.RedisUsageQueueRetentionSeconds)) + } if oldCfg.DisableCooling != newCfg.DisableCooling { changes = append(changes, fmt.Sprintf("disable-cooling: %t -> %t", oldCfg.DisableCooling, newCfg.DisableCooling)) } -- 2.51.2 From 101b59cfe872778f5915b0eef0ccac0eec79cae4 Mon Sep 17 00:00:00 2001 From: Vijay Chimmi Date: Sat, 2 May 2026 17:37:38 -0700 Subject: [PATCH 26/51] docs: update Subtitle Translator project description --- README.md | 2 +- README_CN.md | 2 +- README_JA.md | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 47ea6909..91f38239 100644 --- a/README.md +++ b/README.md @@ -130,7 +130,7 @@ Native macOS menu bar app to use your Claude Code & ChatGPT subscriptions with A ### [Subtitle Translator](https://github.com/VjayC/SRT-Subtitle-Translator-Validator) -Browser-based tool to translate SRT subtitles using your Gemini subscription via CLIProxyAPI with automatic validation/error correction - no API keys needed +A cross-platform desktop and web app to translate and validate SRT subtitles using your existing LLM subscriptions (Gemini, ChatGPT, Claude, etc.) via CLIProxyAPI - no API keys needed. ### [CCS (Claude Code Switch)](https://github.com/kaitranntt/ccs) diff --git a/README_CN.md b/README_CN.md index e9b9c2a4..a307dc95 100644 --- a/README_CN.md +++ b/README_CN.md @@ -129,7 +129,7 @@ CLIProxyAPI 已内置对 [Amp CLI](https://ampcode.com) 和 Amp IDE 扩展的支 ### [Subtitle Translator](https://github.com/VjayC/SRT-Subtitle-Translator-Validator) -一款基于浏览器的 SRT 字幕翻译工具,可通过 CLI 代理 API 使用您的 Gemini 订阅。内置自动验证与错误修正功能,无需 API 密钥。 +一款跨平台的桌面和 Web 应用程序,可通过 CLIProxyAPI 使用您现有的 LLM 订阅(Gemini、ChatGPT、Claude, etc.)来翻译和验证 SRT 字幕 - 无需 API 密钥。 ### [CCS (Claude Code Switch)](https://github.com/kaitranntt/ccs) diff --git a/README_JA.md b/README_JA.md index 58ad22cf..266b612a 100644 --- a/README_JA.md +++ b/README_JA.md @@ -128,7 +128,7 @@ macOSネイティブのメニューバーアプリで、Claude CodeとChatGPTの ### [Subtitle Translator](https://github.com/VjayC/SRT-Subtitle-Translator-Validator) -CLIProxyAPI経由でGeminiサブスクリプションを使用してSRT字幕を翻訳するブラウザベースのツール。自動検証/エラー修正機能付き - APIキー不要 +CLIProxyAPI経由で既存のLLMサブスクリプション(Gemini、ChatGPT、Claude, etc.)を使用してSRT字幕を翻訳および検証する、クロスプラットフォームのデスクトップおよびWebアプリ - APIキー不要。 ### [CCS (Claude Code Switch)](https://github.com/kaitranntt/ccs) -- 2.51.2 From 5fc6f662e14a30be75d3994b3c64270d04358593 Mon Sep 17 00:00:00 2001 From: ziwu Date: Sun, 3 May 2026 18:25:11 +0800 Subject: [PATCH 27/51] docs: add CLIProxy Pool Watch project --- README.md | 4 ++++ README_CN.md | 4 ++++ README_JA.md | 4 ++++ 3 files changed, 12 insertions(+) diff --git a/README.md b/README.md index 91f38239..e404e894 100644 --- a/README.md +++ b/README.md @@ -195,6 +195,10 @@ Ready-to-use cross-platform quota inspector for CLIProxyAPI, supporting per-acco Windows-focused, local-first desktop management platform for Codex CLI built on CLIProxyAPI, focused on simplifying local setup, account and runtime management, and providing a more complete Codex CLI experience for local users. +### [CLIProxy Pool Watch](https://github.com/murasame612/CLIProxyPoolWidget) + +Native macOS SwiftUI app for monitoring ChatGPT/Codex account quotas in CLIProxyAPI pools. Displays account availability, Plus-base capacity, 5-hour and weekly quota bars, plan weights, and restore forecasts through the Management API. + > [!NOTE] > If you developed a project based on CLIProxyAPI, please open a PR to add it to this list. diff --git a/README_CN.md b/README_CN.md index a307dc95..e5d9db1e 100644 --- a/README_CN.md +++ b/README_CN.md @@ -191,6 +191,10 @@ Shadow AI 是一款专为受限环境设计的 AI 辅助工具。提供无窗口 基于 CLIProxyAPI 的 Windows Codex CLI 本地优先桌面管理平台,聚焦简化本机配置、账号与运行状态管理,并为本地用户提供更完整的 Codex CLI 使用体验。 +### [CLIProxy Pool Watch](https://github.com/murasame612/CLIProxyPoolWidget) + +原生 macOS SwiftUI 应用,用于监控 CLIProxyAPI 池中的 ChatGPT/Codex 账号额度。通过 Management API 展示账号可用状态、Plus 基准容量、5 小时与周额度进度条、套餐权重和恢复预测。 + > [!NOTE] > 如果你开发了基于 CLIProxyAPI 的项目,请提交一个 PR(拉取请求)将其添加到此列表中。 diff --git a/README_JA.md b/README_JA.md index 266b612a..84816641 100644 --- a/README_JA.md +++ b/README_JA.md @@ -190,6 +190,10 @@ CLIProxyAPI向けのすぐに使えるクロスプラットフォームのクォ CLIProxyAPIを基盤にしたWindows向けのローカル優先Codex CLIデスクトップ管理プラットフォーム。ローカル設定、アカウント、実行状態の管理を簡素化し、ローカルユーザーにより包括的なCodex CLI体験を提供します。 +### [CLIProxy Pool Watch](https://github.com/murasame612/CLIProxyPoolWidget) + +CLIProxyAPIプール内のChatGPT/Codexアカウントクォータを監視するmacOSネイティブSwiftUIアプリ。Management APIを通じて、アカウントの可用性、Plus基準の容量、5時間/週次クォータバー、プラン重み、復元予測を表示します。 + > [!NOTE] > CLIProxyAPIをベースにプロジェクトを開発した場合は、PRを送ってこのリストに追加してください。 -- 2.51.2 From 81db7fdc1e06b88c6fe9d14e9f1dfb57d5c642d1 Mon Sep 17 00:00:00 2001 From: John Date: Sun, 3 May 2026 20:23:23 +0800 Subject: [PATCH 28/51] Add CLIProxyAPI Usage Dashboard to statistics docs --- README.md | 4 ++++ README_CN.md | 4 ++++ README_JA.md | 4 ++++ 3 files changed, 12 insertions(+) diff --git a/README.md b/README.md index 91f38239..f5bcc4ee 100644 --- a/README.md +++ b/README.md @@ -82,6 +82,10 @@ Since v6.10.0, CLIProxyAPI and [CPAMC](https://github.com/router-for-me/Cli-Prox Standalone persistence and visualization service for CLIProxyAPI, with periodic data sync, SQLite storage, aggregate APIs, and a built-in dashboard for usage and statistics. +### [CLIProxyAPI Usage Dashboard](https://github.com/zhanglunet/cliproxyapi-usage-dashboard) + +Local-first usage and quota dashboard for CLIProxyAPI. It collects per-request token usage from the Redis-compatible usage queue into SQLite, visualizes daily and recent-window usage by account and model, and shows Codex 5h/7d quota remaining in a local web UI. + ## Amp CLI Support CLIProxyAPI includes integrated support for [Amp CLI](https://ampcode.com) and Amp IDE extensions, enabling you to use your Google/ChatGPT/Claude OAuth subscriptions with Amp's coding tools: diff --git a/README_CN.md b/README_CN.md index a307dc95..10bba6e3 100644 --- a/README_CN.md +++ b/README_CN.md @@ -82,6 +82,10 @@ CLIProxyAPI 用户手册: [https://help.router-for.me/](https://help.router-fo 独立的 CLIProxyAPI 使用量持久化与可视化服务,定期同步 CLIProxyAPI 数据,存储到 SQLite,提供聚合 API,并内置使用量分析与统计仪表盘。 +### [CLIProxyAPI Usage Dashboard](https://github.com/zhanglunet/cliproxyapi-usage-dashboard) + +面向 CLIProxyAPI 的本地优先使用量与配额看板。它从 Redis 兼容使用量队列采集每次请求的 token 消耗并写入 SQLite,按账号和模型展示当天及最近时间窗口的用量,并在本地网页中显示 Codex 5h/7d 配额余量。 + ## Amp CLI 支持 CLIProxyAPI 已内置对 [Amp CLI](https://ampcode.com) 和 Amp IDE 扩展的支持,可让你使用自己的 Google/ChatGPT/Claude OAuth 订阅来配合 Amp 编码工具: diff --git a/README_JA.md b/README_JA.md index 266b612a..d5638173 100644 --- a/README_JA.md +++ b/README_JA.md @@ -80,6 +80,10 @@ v6.10.0以降、CLIProxyAPIおよび [CPAMC](https://github.com/router-for-me/Cl CLIProxyAPI向けの独立した使用量永続化・可視化サービス。CLIProxyAPIデータを定期同期してSQLiteに保存し、集計APIと、使用量や各種統計を確認できる組み込みダッシュボードを提供します。 +### [CLIProxyAPI Usage Dashboard](https://github.com/zhanglunet/cliproxyapi-usage-dashboard) + +CLIProxyAPI向けのローカル優先の使用量・クォータダッシュボード。Redis互換の使用量キューからリクエストごとのtoken使用量を収集してSQLiteに保存し、アカウント別・モデル別の日次および直近時間枠の使用量と、Codex 5h/7dクォータ残量をローカルWeb UIで表示します。 + ## Amp CLIサポート CLIProxyAPIは[Amp CLI](https://ampcode.com)およびAmp IDE拡張機能の統合サポートを含んでおり、Google/ChatGPT/ClaudeのOAuthサブスクリプションをAmpのコーディングツールで使用できます: -- 2.51.2 From 7972130513c2f234be29dd733a33ec7c48f6a54c Mon Sep 17 00:00:00 2001 From: zhanglu <1160377+zhanglunet@users.noreply.github.com> Date: Sun, 3 May 2026 20:38:25 +0800 Subject: [PATCH 29/51] Update README_CN.md Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> --- README_CN.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README_CN.md b/README_CN.md index 10bba6e3..6989cf3f 100644 --- a/README_CN.md +++ b/README_CN.md @@ -84,7 +84,7 @@ CLIProxyAPI 用户手册: [https://help.router-for.me/](https://help.router-fo ### [CLIProxyAPI Usage Dashboard](https://github.com/zhanglunet/cliproxyapi-usage-dashboard) -面向 CLIProxyAPI 的本地优先使用量与配额看板。它从 Redis 兼容使用量队列采集每次请求的 token 消耗并写入 SQLite,按账号和模型展示当天及最近时间窗口的用量,并在本地网页中显示 Codex 5h/7d 配额余量。 +面向 CLIProxyAPI 的本地优先使用量与配额看板。它从 Redis 兼容使用量队列采集每次请求的 Token 消耗并写入 SQLite,按账号和模型可视化每日及最近时间窗口的用量,并在本地网页中显示 Codex 5h/7d 配额余量。 ## Amp CLI 支持 -- 2.51.2 From d2386a31144530c0f6aadd5330f90d8067c0a796 Mon Sep 17 00:00:00 2001 From: zhanglu <1160377+zhanglunet@users.noreply.github.com> Date: Sun, 3 May 2026 20:38:51 +0800 Subject: [PATCH 30/51] Update README_JA.md Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> --- README_JA.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README_JA.md b/README_JA.md index d5638173..b07ca492 100644 --- a/README_JA.md +++ b/README_JA.md @@ -82,7 +82,7 @@ CLIProxyAPI向けの独立した使用量永続化・可視化サービス。CLI ### [CLIProxyAPI Usage Dashboard](https://github.com/zhanglunet/cliproxyapi-usage-dashboard) -CLIProxyAPI向けのローカル優先の使用量・クォータダッシュボード。Redis互換の使用量キューからリクエストごとのtoken使用量を収集してSQLiteに保存し、アカウント別・モデル別の日次および直近時間枠の使用量と、Codex 5h/7dクォータ残量をローカルWeb UIで表示します。 +CLIProxyAPI向けのローカル優先の使用量・クォータダッシュボード。Redis互換の使用量キューからリクエストごとのToken使用量を収集してSQLiteに保存し、アカウント別・モデル別の日次および直近時間枠の使用量を可視化し、Codex 5h/7dクォータ残量をローカルWeb UIで表示します。 ## Amp CLIサポート -- 2.51.2 From af65908cb0e172c91f467cfd6b18b0b596ed47c4 Mon Sep 17 00:00:00 2001 From: Luis Pater Date: Sun, 3 May 2026 22:26:23 +0800 Subject: [PATCH 31/51] feat: enhance tool mapping with namespace and web search support - Added functions to handle tool conversion, including namespace-based tools and web search tools. - Improved parameter normalization and tool input schema standardization. - Integrated logic to handle qualified tool names and map override functionality. - Refactored existing tool processing for better extensibility and maintainability. Fixed: #3199 --- .../claude_openai-responses_request.go | 206 ++++++++++++++++-- .../claude_openai-responses_response.go | 10 +- 2 files changed, 197 insertions(+), 19 deletions(-) diff --git a/internal/translator/claude/openai/responses/claude_openai-responses_request.go b/internal/translator/claude/openai/responses/claude_openai-responses_request.go index 514129ca..c0479b87 100644 --- a/internal/translator/claude/openai/responses/claude_openai-responses_request.go +++ b/internal/translator/claude/openai/responses/claude_openai-responses_request.go @@ -339,25 +339,21 @@ func ConvertOpenAIResponsesRequestToClaude(modelName string, inputRawJSON []byte }) } + includedToolNames := map[string]struct{}{} + toolNameMap := map[string]string{} + // tools mapping: parameters -> input_schema if tools := root.Get("tools"); tools.Exists() && tools.IsArray() { toolsJSON := []byte("[]") tools.ForEach(func(_, tool gjson.Result) bool { - tJSON := []byte(`{"name":"","description":"","input_schema":{}}`) - if n := tool.Get("name"); n.Exists() { - tJSON, _ = sjson.SetBytes(tJSON, "name", n.String()) - } - if d := tool.Get("description"); d.Exists() { - tJSON, _ = sjson.SetBytes(tJSON, "description", d.String()) - } - - if params := tool.Get("parameters"); params.Exists() { - tJSON, _ = sjson.SetRawBytes(tJSON, "input_schema", []byte(params.Raw)) - } else if params = tool.Get("parametersJsonSchema"); params.Exists() { - tJSON, _ = sjson.SetRawBytes(tJSON, "input_schema", []byte(params.Raw)) + convertedTools := convertResponsesToolToClaudeTools(tool, toolNameMap) + for _, tJSON := range convertedTools { + toolName := gjson.GetBytes(tJSON, "name").String() + if toolName != "" { + includedToolNames[toolName] = struct{}{} + } + toolsJSON, _ = sjson.SetRawBytes(toolsJSON, "-1", tJSON) } - - toolsJSON, _ = sjson.SetRawBytes(toolsJSON, "-1", tJSON) return true }) if parsedTools := gjson.ParseBytes(toolsJSON); parsedTools.IsArray() && len(parsedTools.Array()) > 0 { @@ -375,14 +371,24 @@ func ConvertOpenAIResponsesRequestToClaude(modelName string, inputRawJSON []byte case "none": // Leave unset; implies no tools case "required": - out, _ = sjson.SetRawBytes(out, "tool_choice", []byte(`{"type":"any"}`)) + if len(includedToolNames) > 0 { + out, _ = sjson.SetRawBytes(out, "tool_choice", []byte(`{"type":"any"}`)) + } } case gjson.JSON: if toolChoice.Get("type").String() == "function" { fn := toolChoice.Get("function.name").String() - toolChoiceJSON := []byte(`{"name":"","type":"tool"}`) - toolChoiceJSON, _ = sjson.SetBytes(toolChoiceJSON, "name", fn) - out, _ = sjson.SetRawBytes(out, "tool_choice", toolChoiceJSON) + if fn == "" { + fn = toolChoice.Get("name").String() + } + if mappedName := toolNameMap[fn]; mappedName != "" { + fn = mappedName + } + if _, ok := includedToolNames[fn]; ok { + toolChoiceJSON := []byte(`{"name":"","type":"tool"}`) + toolChoiceJSON, _ = sjson.SetBytes(toolChoiceJSON, "name", fn) + out, _ = sjson.SetRawBytes(out, "tool_choice", toolChoiceJSON) + } } default: @@ -391,3 +397,167 @@ func ConvertOpenAIResponsesRequestToClaude(modelName string, inputRawJSON []byte return out } + +func convertResponsesToolToClaudeTools(tool gjson.Result, toolNameMap map[string]string) [][]byte { + toolType := strings.TrimSpace(tool.Get("type").String()) + switch toolType { + case "", "function": + if tJSON, ok := convertResponsesFunctionToolToClaude(tool, ""); ok { + return [][]byte{tJSON} + } + case "namespace": + return convertResponsesNamespaceToolToClaude(tool, toolNameMap) + case "web_search": + if tJSON, ok := convertResponsesWebSearchToolToClaude(tool); ok { + if name := gjson.GetBytes(tJSON, "name").String(); name != "" { + toolNameMap[name] = name + } + return [][]byte{tJSON} + } + default: + if isUnsupportedOpenAIBuiltinToolType(toolType) { + return nil + } + if tool.Get("name").String() != "" { + return [][]byte{[]byte(tool.Raw)} + } + } + return nil +} + +func convertResponsesNamespaceToolToClaude(tool gjson.Result, toolNameMap map[string]string) [][]byte { + namespaceName := strings.TrimSpace(tool.Get("name").String()) + children := tool.Get("tools") + if !children.Exists() || !children.IsArray() { + return nil + } + + var out [][]byte + children.ForEach(func(_, child gjson.Result) bool { + childName := responsesToolName(child) + qualifiedName := qualifyResponsesNamespaceToolName(namespaceName, childName) + if tJSON, ok := convertResponsesFunctionToolToClaude(child, qualifiedName); ok { + out = append(out, tJSON) + toolNameMap[qualifiedName] = qualifiedName + if childName != "" { + toolNameMap[childName] = qualifiedName + } + } + return true + }) + return out +} + +func convertResponsesFunctionToolToClaude(tool gjson.Result, overrideName string) ([]byte, bool) { + name := strings.TrimSpace(overrideName) + if name == "" { + name = responsesToolName(tool) + } + if name == "" { + return nil, false + } + + tJSON := []byte(`{"name":"","description":"","input_schema":{}}`) + tJSON, _ = sjson.SetBytes(tJSON, "name", name) + if d := responsesToolDescription(tool); d != "" { + tJSON, _ = sjson.SetBytes(tJSON, "description", d) + } + tJSON, _ = sjson.SetRawBytes(tJSON, "input_schema", normalizeClaudeToolInputSchema(responsesToolParameters(tool))) + return tJSON, true +} + +func convertResponsesWebSearchToolToClaude(tool gjson.Result) ([]byte, bool) { + if externalWebAccess := tool.Get("external_web_access"); externalWebAccess.Exists() && !externalWebAccess.Bool() { + return nil, false + } + + name := strings.TrimSpace(tool.Get("name").String()) + if name == "" { + name = "web_search" + } + tJSON := []byte(`{"type":"web_search_20250305","name":""}`) + tJSON, _ = sjson.SetBytes(tJSON, "name", name) + if maxUses := tool.Get("max_uses"); maxUses.Exists() { + tJSON, _ = sjson.SetBytes(tJSON, "max_uses", maxUses.Int()) + } + if allowedDomains := tool.Get("filters.allowed_domains"); allowedDomains.Exists() && allowedDomains.IsArray() { + tJSON, _ = sjson.SetRawBytes(tJSON, "allowed_domains", []byte(allowedDomains.Raw)) + } + if userLocation := tool.Get("user_location"); userLocation.Exists() && userLocation.IsObject() { + tJSON, _ = sjson.SetRawBytes(tJSON, "user_location", []byte(userLocation.Raw)) + } + return tJSON, true +} + +func responsesToolName(tool gjson.Result) string { + if name := strings.TrimSpace(tool.Get("name").String()); name != "" { + return name + } + return strings.TrimSpace(tool.Get("function.name").String()) +} + +func responsesToolDescription(tool gjson.Result) string { + if description := tool.Get("description").String(); description != "" { + return description + } + return tool.Get("function.description").String() +} + +func responsesToolParameters(tool gjson.Result) gjson.Result { + for _, path := range []string{ + "parameters", + "parametersJsonSchema", + "input_schema", + "function.parameters", + "function.parametersJsonSchema", + } { + if parameters := tool.Get(path); parameters.Exists() { + return parameters + } + } + return gjson.Result{} +} + +func normalizeClaudeToolInputSchema(parameters gjson.Result) []byte { + raw := strings.TrimSpace(parameters.Raw) + if raw == "" || raw == "null" || !gjson.Valid(raw) { + return []byte(`{"type":"object","properties":{}}`) + } + result := gjson.Parse(raw) + if !result.IsObject() { + return []byte(`{"type":"object","properties":{}}`) + } + schema := []byte(raw) + schemaType := result.Get("type").String() + if schemaType == "" { + schema, _ = sjson.SetBytes(schema, "type", "object") + schemaType = "object" + } + if schemaType == "object" && !result.Get("properties").Exists() { + schema, _ = sjson.SetRawBytes(schema, "properties", []byte(`{}`)) + } + return schema +} + +func qualifyResponsesNamespaceToolName(namespaceName, childName string) string { + childName = strings.TrimSpace(childName) + if childName == "" || namespaceName == "" || strings.HasPrefix(childName, "mcp__") { + return childName + } + if strings.HasPrefix(childName, namespaceName) { + return childName + } + if strings.HasSuffix(namespaceName, "__") { + return namespaceName + childName + } + return namespaceName + "__" + childName +} + +func isUnsupportedOpenAIBuiltinToolType(toolType string) bool { + switch toolType { + case "image_generation", "file_search", "code_interpreter", "computer_use_preview": + return true + default: + return false + } +} diff --git a/internal/translator/claude/openai/responses/claude_openai-responses_response.go b/internal/translator/claude/openai/responses/claude_openai-responses_response.go index ef2cc1f8..10d12c99 100644 --- a/internal/translator/claude/openai/responses/claude_openai-responses_response.go +++ b/internal/translator/claude/openai/responses/claude_openai-responses_response.go @@ -26,7 +26,8 @@ type claudeToResponsesState struct { FuncNames map[int]string // index -> function name FuncCallIDs map[int]string // index -> call id // message text aggregation - TextBuf strings.Builder + TextBuf strings.Builder + CurrentTextBuf strings.Builder // reasoning state ReasoningActive bool ReasoningItemID string @@ -80,6 +81,7 @@ func ConvertClaudeResponseToOpenAIResponses(ctx context.Context, modelName strin st.CreatedAt = time.Now().Unix() // Reset per-message aggregation state st.TextBuf.Reset() + st.CurrentTextBuf.Reset() st.ReasoningBuf.Reset() st.ReasoningActive = false st.InTextBlock = false @@ -128,6 +130,7 @@ func ConvertClaudeResponseToOpenAIResponses(ctx context.Context, modelName strin if typ == "text" { // open message item + content part st.InTextBlock = true + st.CurrentTextBuf.Reset() st.CurrentMsgID = fmt.Sprintf("msg_%s_0", st.ResponseID) item := []byte(`{"type":"response.output_item.added","sequence_number":0,"output_index":0,"item":{"id":"","type":"message","status":"in_progress","content":[],"role":"assistant"}}`) item, _ = sjson.SetBytes(item, "sequence_number", nextSeq()) @@ -189,6 +192,7 @@ func ConvertClaudeResponseToOpenAIResponses(ctx context.Context, modelName strin out = append(out, emitEvent("response.output_text.delta", msg)) // aggregate text for response.output st.TextBuf.WriteString(t.String()) + st.CurrentTextBuf.WriteString(t.String()) } } else if dt == "input_json_delta" { idx := int(root.Get("index").Int()) @@ -220,17 +224,21 @@ func ConvertClaudeResponseToOpenAIResponses(ctx context.Context, modelName strin case "content_block_stop": idx := int(root.Get("index").Int()) if st.InTextBlock { + fullText := st.CurrentTextBuf.String() done := []byte(`{"type":"response.output_text.done","sequence_number":0,"item_id":"","output_index":0,"content_index":0,"text":"","logprobs":[]}`) done, _ = sjson.SetBytes(done, "sequence_number", nextSeq()) done, _ = sjson.SetBytes(done, "item_id", st.CurrentMsgID) + done, _ = sjson.SetBytes(done, "text", fullText) out = append(out, emitEvent("response.output_text.done", done)) partDone := []byte(`{"type":"response.content_part.done","sequence_number":0,"item_id":"","output_index":0,"content_index":0,"part":{"type":"output_text","annotations":[],"logprobs":[],"text":""}}`) partDone, _ = sjson.SetBytes(partDone, "sequence_number", nextSeq()) partDone, _ = sjson.SetBytes(partDone, "item_id", st.CurrentMsgID) + partDone, _ = sjson.SetBytes(partDone, "part.text", fullText) out = append(out, emitEvent("response.content_part.done", partDone)) final := []byte(`{"type":"response.output_item.done","sequence_number":0,"output_index":0,"item":{"id":"","type":"message","status":"completed","content":[{"type":"output_text","text":""}],"role":"assistant"}}`) final, _ = sjson.SetBytes(final, "sequence_number", nextSeq()) final, _ = sjson.SetBytes(final, "item.id", st.CurrentMsgID) + final, _ = sjson.SetBytes(final, "item.content.0.text", fullText) out = append(out, emitEvent("response.output_item.done", final)) st.InTextBlock = false } else if st.InFuncBlock { -- 2.51.2 From 672fdd14ed0a5d1c0db9e8cd9140023545fa30e8 Mon Sep 17 00:00:00 2001 From: Luis Pater Date: Sun, 3 May 2026 22:40:42 +0800 Subject: [PATCH 32/51] feat: filter and drop empty assistant messages in Kimi executor - Added `filterKimiEmptyAssistantMessages` to identify and remove empty assistant messages with no content, tool links, or reasoning. - Integrated logging to track the number of dropped messages. - Updated tests to validate the filtering logic for both empty and valid assistant messages. Fixed: #1730 --- internal/runtime/executor/kimi_executor.go | 103 +++++++++++++++++- .../runtime/executor/kimi_executor_test.go | 67 ++++++++++++ 2 files changed, 168 insertions(+), 2 deletions(-) diff --git a/internal/runtime/executor/kimi_executor.go b/internal/runtime/executor/kimi_executor.go index 3588c962..12c8239f 100644 --- a/internal/runtime/executor/kimi_executor.go +++ b/internal/runtime/executor/kimi_executor.go @@ -322,7 +322,17 @@ func normalizeKimiToolMessageLinks(body []byte) ([]byte, error) { return body, nil } - out := body + msgs := messages.Array() + out, dropped, err := filterKimiEmptyAssistantMessages(body, msgs) + if err != nil { + return body, err + } + if dropped > 0 { + log.WithField("dropped_assistant_messages", dropped).Debug("kimi executor: dropped empty assistant messages") + } + + messages = gjson.GetBytes(out, "messages") + msgs = messages.Array() pending := make([]string, 0) patched := 0 patchedReasoning := 0 @@ -340,7 +350,6 @@ func normalizeKimiToolMessageLinks(body []byte) ([]byte, error) { } } - msgs := messages.Array() for msgIdx := range msgs { msg := msgs[msgIdx] role := strings.TrimSpace(msg.Get("role").String()) @@ -428,6 +437,96 @@ func normalizeKimiToolMessageLinks(body []byte) ([]byte, error) { return out, nil } +func filterKimiEmptyAssistantMessages(body []byte, msgs []gjson.Result) ([]byte, int, error) { + kept := make([]string, 0, len(msgs)) + dropped := 0 + for _, msg := range msgs { + if shouldDropKimiAssistantMessage(msg) { + dropped++ + continue + } + kept = append(kept, msg.Raw) + } + if dropped == 0 { + return body, 0, nil + } + + rawMessages := []byte("[" + strings.Join(kept, ",") + "]") + out, err := sjson.SetRawBytes(body, "messages", rawMessages) + if err != nil { + return body, 0, fmt.Errorf("kimi executor: failed to drop empty assistant messages: %w", err) + } + return out, dropped, nil +} + +func shouldDropKimiAssistantMessage(msg gjson.Result) bool { + if strings.TrimSpace(msg.Get("role").String()) != "assistant" { + return false + } + if hasKimiToolCalls(msg) || hasKimiLegacyFunctionCall(msg) || hasKimiAssistantReasoning(msg) { + return false + } + return isKimiAssistantContentEmpty(msg.Get("content")) +} + +func hasKimiToolCalls(msg gjson.Result) bool { + toolCalls := msg.Get("tool_calls") + return toolCalls.Exists() && toolCalls.IsArray() && len(toolCalls.Array()) > 0 +} + +func hasKimiLegacyFunctionCall(msg gjson.Result) bool { + functionCall := msg.Get("function_call") + if !functionCall.Exists() || functionCall.Type == gjson.Null { + return false + } + if functionCall.IsObject() && strings.TrimSpace(functionCall.Raw) == "{}" { + return false + } + return strings.TrimSpace(functionCall.Raw) != "" +} + +func hasKimiAssistantReasoning(msg gjson.Result) bool { + reasoning := msg.Get("reasoning_content") + return reasoning.Exists() && strings.TrimSpace(reasoning.String()) != "" +} + +func isKimiAssistantContentEmpty(content gjson.Result) bool { + if !content.Exists() || content.Type == gjson.Null { + return true + } + if content.Type == gjson.String { + return strings.TrimSpace(content.String()) == "" + } + if !content.IsArray() { + return false + } + for _, part := range content.Array() { + if !isKimiAssistantContentPartEmpty(part) { + return false + } + } + return true +} + +func isKimiAssistantContentPartEmpty(part gjson.Result) bool { + if !part.Exists() || part.Type == gjson.Null { + return true + } + if part.Type == gjson.String { + return strings.TrimSpace(part.String()) == "" + } + if !part.IsObject() { + return false + } + if text := part.Get("text"); text.Exists() { + return strings.TrimSpace(text.String()) == "" + } + if strings.TrimSpace(part.Get("type").String()) == "text" { + return true + } + return strings.TrimSpace(part.Raw) == "{}" +} + func fallbackAssistantReasoning(msg gjson.Result, hasLatest bool, latest string) string { if hasLatest && strings.TrimSpace(latest) != "" { return latest diff --git a/internal/runtime/executor/kimi_executor_test.go b/internal/runtime/executor/kimi_executor_test.go index 210ddb0e..f3de70f1 100644 --- a/internal/runtime/executor/kimi_executor_test.go +++ b/internal/runtime/executor/kimi_executor_test.go @@ -203,3 +203,70 @@ func TestNormalizeKimiToolMessageLinks_RepairsIDsAndReasoningTogether(t *testing t.Fatalf("messages.2.reasoning_content = %q, want %q", got, "r1") } } + +func TestNormalizeKimiToolMessageLinks_DropsEmptyAssistantWithoutToolLink(t *testing.T) { + body := []byte(`{ + "messages":[ + {"role":"user","content":"start"}, + {"role":"assistant","content":""}, + {"role":"assistant","content":" "}, + {"role":"assistant","content":"","tool_calls":null}, + {"role":"assistant","content":[{"type":"text","text":" "}]}, + {"role":"assistant"}, + {"role":"assistant","content":"keep"}, + {"role":"user","content":"next"} + ] + }`) + + out, err := normalizeKimiToolMessageLinks(body) + if err != nil { + t.Fatalf("normalizeKimiToolMessageLinks() error = %v", err) + } + + messages := gjson.GetBytes(out, "messages").Array() + if len(messages) != 3 { + t.Fatalf("messages length = %d, want 3, raw = %s", len(messages), gjson.GetBytes(out, "messages").Raw) + } + if got := messages[0].Get("content").String(); got != "start" { + t.Fatalf("messages.0.content = %q, want %q", got, "start") + } + if got := messages[1].Get("content").String(); got != "keep" { + t.Fatalf("messages.1.content = %q, want %q", got, "keep") + } + if got := messages[2].Get("content").String(); got != "next" { + t.Fatalf("messages.2.content = %q, want %q", got, "next") + } +} + +func TestNormalizeKimiToolMessageLinks_PreservesAssistantWithToolLinkOrReasoning(t *testing.T) { + body := []byte(`{ + "messages":[ + {"role":"assistant","content":"","tool_calls":[{"id":"call_1","type":"function","function":{"name":"list_directory","arguments":"{}"}}]}, + {"role":"assistant","content":"","function_call":{"name":"legacy_call","arguments":"{}"}}, + {"role":"assistant","content":"","reasoning_content":"thought"}, + {"role":"assistant","content":[{"type":"text","text":" visible "}]} + ] + }`) + + out, err := normalizeKimiToolMessageLinks(body) + if err != nil { + t.Fatalf("normalizeKimiToolMessageLinks() error = %v", err) + } + + messages := gjson.GetBytes(out, "messages").Array() + if len(messages) != 4 { + t.Fatalf("messages length = %d, want 4, raw = %s", len(messages), gjson.GetBytes(out, "messages").Raw) + } + if !messages[0].Get("tool_calls").Exists() { + t.Fatalf("messages.0.tool_calls should exist") + } + if !messages[1].Get("function_call").Exists() { + t.Fatalf("messages.1.function_call should exist") + } + if got := messages[2].Get("reasoning_content").String(); got != "thought" { + t.Fatalf("messages.2.reasoning_content = %q, want %q", got, "thought") + } + if got := messages[3].Get("content.0.text").String(); got != " visible " { + t.Fatalf("messages.3.content.0.text = %q, want %q", got, " visible ") + } +} -- 2.51.2 From bf0e5c23f731e6d80457679e721c535823e5e60e Mon Sep 17 00:00:00 2001 From: 1137043480 <1137043480@users.noreply.github.com> Date: Sun, 3 May 2026 11:25:04 -0400 Subject: [PATCH 33/51] fix: prevent goroutine leaks in streaming executors via context-aware channel sends All streaming executors use bare channel sends (out <- chunk) inside goroutines that process upstream SSE responses. When the downstream consumer disconnects (client timeout, network drop, etc.), these sends block indefinitely, causing the goroutine and all associated resources (HTTP response body, scanner buffers, translation state) to leak permanently. Over time, leaked goroutines accumulate monotonically, leading to RSS growth from ~30MB to 3.7GB+ and eventual OOM kills on resource-constrained VPS hosts. Fix: Replace all bare 'out <- ...' sends with: select { case out <- ...: case <-ctx.Done(): return } This ensures goroutines terminate promptly when the request context is canceled, allowing GC to reclaim all associated resources. Affected executors (9 files, 36+ send sites): - antigravity_executor.go (5 sites) - gemini_cli_executor.go (6 sites) - gemini_vertex_executor.go (6 sites) - aistudio_executor.go (4 sites) - gemini_executor.go (3 sites) - openai_compat_executor.go (3 sites) - claude_executor.go (4 sites) - codex_executor.go (2 sites) - kimi_executor.go (3 sites) --- .../runtime/executor/aistudio_executor.go | 22 +++++++++--- .../runtime/executor/antigravity_executor.go | 28 ++++++++++++--- internal/runtime/executor/claude_executor.go | 22 +++++++++--- internal/runtime/executor/codex_executor.go | 11 ++++-- .../runtime/executor/gemini_cli_executor.go | 34 +++++++++++++++---- internal/runtime/executor/gemini_executor.go | 17 ++++++++-- .../executor/gemini_vertex_executor.go | 34 +++++++++++++++---- internal/runtime/executor/kimi_executor.go | 17 ++++++++-- .../executor/openai_compat_executor.go | 17 ++++++++-- 9 files changed, 166 insertions(+), 36 deletions(-) diff --git a/internal/runtime/executor/aistudio_executor.go b/internal/runtime/executor/aistudio_executor.go index 73491d82..37e85377 100644 --- a/internal/runtime/executor/aistudio_executor.go +++ b/internal/runtime/executor/aistudio_executor.go @@ -285,7 +285,10 @@ func (e *AIStudioExecutor) ExecuteStream(ctx context.Context, auth *cliproxyauth if event.Err != nil { helps.RecordAPIResponseError(ctx, e.cfg, event.Err) reporter.PublishFailure(ctx) - out <- cliproxyexecutor.StreamChunk{Err: fmt.Errorf("wsrelay: %v", event.Err)} + select { + case out <- cliproxyexecutor.StreamChunk{Err: fmt.Errorf("wsrelay: %v", event.Err)}: + case <-ctx.Done(): + } return false } switch event.Type { @@ -303,7 +306,11 @@ func (e *AIStudioExecutor) ExecuteStream(ctx context.Context, auth *cliproxyauth } lines := sdktranslator.TranslateStream(ctx, body.toFormat, opts.SourceFormat, req.Model, opts.OriginalRequest, translatedReq, filtered, ¶m) for i := range lines { - out <- cliproxyexecutor.StreamChunk{Payload: ensureColonSpacedJSON(lines[i])} + select { + case out <- cliproxyexecutor.StreamChunk{Payload: ensureColonSpacedJSON(lines[i])}: + case <-ctx.Done(): + return false + } } break } @@ -319,14 +326,21 @@ func (e *AIStudioExecutor) ExecuteStream(ctx context.Context, auth *cliproxyauth } lines := sdktranslator.TranslateStream(ctx, body.toFormat, opts.SourceFormat, req.Model, opts.OriginalRequest, translatedReq, event.Payload, ¶m) for i := range lines { - out <- cliproxyexecutor.StreamChunk{Payload: ensureColonSpacedJSON(lines[i])} + select { + case out <- cliproxyexecutor.StreamChunk{Payload: ensureColonSpacedJSON(lines[i])}: + case <-ctx.Done(): + return false + } } reporter.Publish(ctx, helps.ParseGeminiUsage(event.Payload)) return false case wsrelay.MessageTypeError: helps.RecordAPIResponseError(ctx, e.cfg, event.Err) reporter.PublishFailure(ctx) - out <- cliproxyexecutor.StreamChunk{Err: fmt.Errorf("wsrelay: %v", event.Err)} + select { + case out <- cliproxyexecutor.StreamChunk{Err: fmt.Errorf("wsrelay: %v", event.Err)}: + case <-ctx.Done(): + } return false } return true diff --git a/internal/runtime/executor/antigravity_executor.go b/internal/runtime/executor/antigravity_executor.go index 280c799a..c07680e8 100644 --- a/internal/runtime/executor/antigravity_executor.go +++ b/internal/runtime/executor/antigravity_executor.go @@ -894,12 +894,19 @@ attemptLoop: reporter.Publish(ctx, detail) } - out <- cliproxyexecutor.StreamChunk{Payload: payload} + select { + case out <- cliproxyexecutor.StreamChunk{Payload: payload}: + case <-ctx.Done(): + return + } } if errScan := scanner.Err(); errScan != nil { helps.RecordAPIResponseError(ctx, e.cfg, errScan) reporter.PublishFailure(ctx) - out <- cliproxyexecutor.StreamChunk{Err: errScan} + select { + case out <- cliproxyexecutor.StreamChunk{Err: errScan}: + case <-ctx.Done(): + } } else { reporter.EnsurePublished(ctx) } @@ -1357,17 +1364,28 @@ attemptLoop: chunks := sdktranslator.TranslateStream(ctx, to, from, req.Model, opts.OriginalRequest, translated, bytes.Clone(payload), ¶m) for i := range chunks { - out <- cliproxyexecutor.StreamChunk{Payload: chunks[i]} + select { + case out <- cliproxyexecutor.StreamChunk{Payload: chunks[i]}: + case <-ctx.Done(): + return + } } } tail := sdktranslator.TranslateStream(ctx, to, from, req.Model, opts.OriginalRequest, translated, []byte("[DONE]"), ¶m) for i := range tail { - out <- cliproxyexecutor.StreamChunk{Payload: tail[i]} + select { + case out <- cliproxyexecutor.StreamChunk{Payload: tail[i]}: + case <-ctx.Done(): + return + } } if errScan := scanner.Err(); errScan != nil { helps.RecordAPIResponseError(ctx, e.cfg, errScan) reporter.PublishFailure(ctx) - out <- cliproxyexecutor.StreamChunk{Err: errScan} + select { + case out <- cliproxyexecutor.StreamChunk{Err: errScan}: + case <-ctx.Done(): + } } else { reporter.EnsurePublished(ctx) } diff --git a/internal/runtime/executor/claude_executor.go b/internal/runtime/executor/claude_executor.go index 66432ac4..ea94526e 100644 --- a/internal/runtime/executor/claude_executor.go +++ b/internal/runtime/executor/claude_executor.go @@ -484,12 +484,19 @@ func (e *ClaudeExecutor) ExecuteStream(ctx context.Context, auth *cliproxyauth.A cloned := make([]byte, len(line)+1) copy(cloned, line) cloned[len(line)] = '\n' - out <- cliproxyexecutor.StreamChunk{Payload: cloned} + select { + case out <- cliproxyexecutor.StreamChunk{Payload: cloned}: + case <-ctx.Done(): + return + } } if errScan := scanner.Err(); errScan != nil { helps.RecordAPIResponseError(ctx, e.cfg, errScan) reporter.PublishFailure(ctx) - out <- cliproxyexecutor.StreamChunk{Err: errScan} + select { + case out <- cliproxyexecutor.StreamChunk{Err: errScan}: + case <-ctx.Done(): + } } return } @@ -521,13 +528,20 @@ func (e *ClaudeExecutor) ExecuteStream(ctx context.Context, auth *cliproxyauth.A ¶m, ) for i := range chunks { - out <- cliproxyexecutor.StreamChunk{Payload: chunks[i]} + select { + case out <- cliproxyexecutor.StreamChunk{Payload: chunks[i]}: + case <-ctx.Done(): + return + } } } if errScan := scanner.Err(); errScan != nil { helps.RecordAPIResponseError(ctx, e.cfg, errScan) reporter.PublishFailure(ctx) - out <- cliproxyexecutor.StreamChunk{Err: errScan} + select { + case out <- cliproxyexecutor.StreamChunk{Err: errScan}: + case <-ctx.Done(): + } } }() return &cliproxyexecutor.StreamResult{Headers: httpResp.Header.Clone(), Chunks: out}, nil diff --git a/internal/runtime/executor/codex_executor.go b/internal/runtime/executor/codex_executor.go index aa8223f4..6efc25b0 100644 --- a/internal/runtime/executor/codex_executor.go +++ b/internal/runtime/executor/codex_executor.go @@ -515,13 +515,20 @@ func (e *CodexExecutor) ExecuteStream(ctx context.Context, auth *cliproxyauth.Au chunks := sdktranslator.TranslateStream(ctx, to, from, req.Model, originalPayload, body, translatedLine, ¶m) for i := range chunks { - out <- cliproxyexecutor.StreamChunk{Payload: chunks[i]} + select { + case out <- cliproxyexecutor.StreamChunk{Payload: chunks[i]}: + case <-ctx.Done(): + return + } } } if errScan := scanner.Err(); errScan != nil { helps.RecordAPIResponseError(ctx, e.cfg, errScan) reporter.PublishFailure(ctx) - out <- cliproxyexecutor.StreamChunk{Err: errScan} + select { + case out <- cliproxyexecutor.StreamChunk{Err: errScan}: + case <-ctx.Done(): + } } }() return &cliproxyexecutor.StreamResult{Headers: httpResp.Header.Clone(), Chunks: out}, nil diff --git a/internal/runtime/executor/gemini_cli_executor.go b/internal/runtime/executor/gemini_cli_executor.go index 15e84572..b6210e6a 100644 --- a/internal/runtime/executor/gemini_cli_executor.go +++ b/internal/runtime/executor/gemini_cli_executor.go @@ -411,19 +411,30 @@ func (e *GeminiCLIExecutor) ExecuteStream(ctx context.Context, auth *cliproxyaut if bytes.HasPrefix(line, dataTag) { segments := sdktranslator.TranslateStream(respCtx, to, from, attemptModel, opts.OriginalRequest, reqBody, bytes.Clone(line), ¶m) for i := range segments { - out <- cliproxyexecutor.StreamChunk{Payload: segments[i]} + select { + case out <- cliproxyexecutor.StreamChunk{Payload: segments[i]}: + case <-ctx.Done(): + return + } } } } segments := sdktranslator.TranslateStream(respCtx, to, from, attemptModel, opts.OriginalRequest, reqBody, []byte("[DONE]"), ¶m) for i := range segments { - out <- cliproxyexecutor.StreamChunk{Payload: segments[i]} + select { + case out <- cliproxyexecutor.StreamChunk{Payload: segments[i]}: + case <-ctx.Done(): + return + } } if errScan := scanner.Err(); errScan != nil { helps.RecordAPIResponseError(ctx, e.cfg, errScan) reporter.PublishFailure(ctx) - out <- cliproxyexecutor.StreamChunk{Err: errScan} + select { + case out <- cliproxyexecutor.StreamChunk{Err: errScan}: + case <-ctx.Done(): + } return } reporter.EnsurePublished(ctx) @@ -434,7 +445,10 @@ func (e *GeminiCLIExecutor) ExecuteStream(ctx context.Context, auth *cliproxyaut if errRead != nil { helps.RecordAPIResponseError(ctx, e.cfg, errRead) reporter.PublishFailure(ctx) - out <- cliproxyexecutor.StreamChunk{Err: errRead} + select { + case out <- cliproxyexecutor.StreamChunk{Err: errRead}: + case <-ctx.Done(): + } return } helps.AppendAPIResponseChunk(ctx, e.cfg, data) @@ -442,12 +456,20 @@ func (e *GeminiCLIExecutor) ExecuteStream(ctx context.Context, auth *cliproxyaut var param any segments := sdktranslator.TranslateStream(respCtx, to, from, attemptModel, opts.OriginalRequest, reqBody, data, ¶m) for i := range segments { - out <- cliproxyexecutor.StreamChunk{Payload: segments[i]} + select { + case out <- cliproxyexecutor.StreamChunk{Payload: segments[i]}: + case <-ctx.Done(): + return + } } segments = sdktranslator.TranslateStream(respCtx, to, from, attemptModel, opts.OriginalRequest, reqBody, []byte("[DONE]"), ¶m) for i := range segments { - out <- cliproxyexecutor.StreamChunk{Payload: segments[i]} + select { + case out <- cliproxyexecutor.StreamChunk{Payload: segments[i]}: + case <-ctx.Done(): + return + } } }(httpResp, append([]byte(nil), payload...), attemptModel) diff --git a/internal/runtime/executor/gemini_executor.go b/internal/runtime/executor/gemini_executor.go index 0e3c3ec6..2a6e9a6e 100644 --- a/internal/runtime/executor/gemini_executor.go +++ b/internal/runtime/executor/gemini_executor.go @@ -324,17 +324,28 @@ func (e *GeminiExecutor) ExecuteStream(ctx context.Context, auth *cliproxyauth.A } lines := sdktranslator.TranslateStream(ctx, to, from, req.Model, opts.OriginalRequest, body, bytes.Clone(payload), ¶m) for i := range lines { - out <- cliproxyexecutor.StreamChunk{Payload: lines[i]} + select { + case out <- cliproxyexecutor.StreamChunk{Payload: lines[i]}: + case <-ctx.Done(): + return + } } } lines := sdktranslator.TranslateStream(ctx, to, from, req.Model, opts.OriginalRequest, body, []byte("[DONE]"), ¶m) for i := range lines { - out <- cliproxyexecutor.StreamChunk{Payload: lines[i]} + select { + case out <- cliproxyexecutor.StreamChunk{Payload: lines[i]}: + case <-ctx.Done(): + return + } } if errScan := scanner.Err(); errScan != nil { helps.RecordAPIResponseError(ctx, e.cfg, errScan) reporter.PublishFailure(ctx) - out <- cliproxyexecutor.StreamChunk{Err: errScan} + select { + case out <- cliproxyexecutor.StreamChunk{Err: errScan}: + case <-ctx.Done(): + } } }() return &cliproxyexecutor.StreamResult{Headers: httpResp.Header.Clone(), Chunks: out}, nil diff --git a/internal/runtime/executor/gemini_vertex_executor.go b/internal/runtime/executor/gemini_vertex_executor.go index b147fde9..20f5aec1 100644 --- a/internal/runtime/executor/gemini_vertex_executor.go +++ b/internal/runtime/executor/gemini_vertex_executor.go @@ -656,17 +656,28 @@ func (e *GeminiVertexExecutor) executeStreamWithServiceAccount(ctx context.Conte } lines := sdktranslator.TranslateStream(ctx, to, from, req.Model, opts.OriginalRequest, body, bytes.Clone(line), ¶m) for i := range lines { - out <- cliproxyexecutor.StreamChunk{Payload: lines[i]} + select { + case out <- cliproxyexecutor.StreamChunk{Payload: lines[i]}: + case <-ctx.Done(): + return + } } } lines := sdktranslator.TranslateStream(ctx, to, from, req.Model, opts.OriginalRequest, body, []byte("[DONE]"), ¶m) for i := range lines { - out <- cliproxyexecutor.StreamChunk{Payload: lines[i]} + select { + case out <- cliproxyexecutor.StreamChunk{Payload: lines[i]}: + case <-ctx.Done(): + return + } } if errScan := scanner.Err(); errScan != nil { helps.RecordAPIResponseError(ctx, e.cfg, errScan) reporter.PublishFailure(ctx) - out <- cliproxyexecutor.StreamChunk{Err: errScan} + select { + case out <- cliproxyexecutor.StreamChunk{Err: errScan}: + case <-ctx.Done(): + } } }() return &cliproxyexecutor.StreamResult{Headers: httpResp.Header.Clone(), Chunks: out}, nil @@ -786,17 +797,28 @@ func (e *GeminiVertexExecutor) executeStreamWithAPIKey(ctx context.Context, auth } lines := sdktranslator.TranslateStream(ctx, to, from, req.Model, opts.OriginalRequest, body, bytes.Clone(line), ¶m) for i := range lines { - out <- cliproxyexecutor.StreamChunk{Payload: lines[i]} + select { + case out <- cliproxyexecutor.StreamChunk{Payload: lines[i]}: + case <-ctx.Done(): + return + } } } lines := sdktranslator.TranslateStream(ctx, to, from, req.Model, opts.OriginalRequest, body, []byte("[DONE]"), ¶m) for i := range lines { - out <- cliproxyexecutor.StreamChunk{Payload: lines[i]} + select { + case out <- cliproxyexecutor.StreamChunk{Payload: lines[i]}: + case <-ctx.Done(): + return + } } if errScan := scanner.Err(); errScan != nil { helps.RecordAPIResponseError(ctx, e.cfg, errScan) reporter.PublishFailure(ctx) - out <- cliproxyexecutor.StreamChunk{Err: errScan} + select { + case out <- cliproxyexecutor.StreamChunk{Err: errScan}: + case <-ctx.Done(): + } } }() return &cliproxyexecutor.StreamResult{Headers: httpResp.Header.Clone(), Chunks: out}, nil diff --git a/internal/runtime/executor/kimi_executor.go b/internal/runtime/executor/kimi_executor.go index 3588c962..2bb0c7fd 100644 --- a/internal/runtime/executor/kimi_executor.go +++ b/internal/runtime/executor/kimi_executor.go @@ -290,17 +290,28 @@ func (e *KimiExecutor) ExecuteStream(ctx context.Context, auth *cliproxyauth.Aut } chunks := sdktranslator.TranslateStream(ctx, to, from, req.Model, opts.OriginalRequest, body, bytes.Clone(line), ¶m) for i := range chunks { - out <- cliproxyexecutor.StreamChunk{Payload: chunks[i]} + select { + case out <- cliproxyexecutor.StreamChunk{Payload: chunks[i]}: + case <-ctx.Done(): + return + } } } doneChunks := sdktranslator.TranslateStream(ctx, to, from, req.Model, opts.OriginalRequest, body, []byte("[DONE]"), ¶m) for i := range doneChunks { - out <- cliproxyexecutor.StreamChunk{Payload: doneChunks[i]} + select { + case out <- cliproxyexecutor.StreamChunk{Payload: doneChunks[i]}: + case <-ctx.Done(): + return + } } if errScan := scanner.Err(); errScan != nil { helps.RecordAPIResponseError(ctx, e.cfg, errScan) reporter.PublishFailure(ctx) - out <- cliproxyexecutor.StreamChunk{Err: errScan} + select { + case out <- cliproxyexecutor.StreamChunk{Err: errScan}: + case <-ctx.Done(): + } } }() return &cliproxyexecutor.StreamResult{Headers: httpResp.Header.Clone(), Chunks: out}, nil diff --git a/internal/runtime/executor/openai_compat_executor.go b/internal/runtime/executor/openai_compat_executor.go index 4e44a7ae..ebddfddb 100644 --- a/internal/runtime/executor/openai_compat_executor.go +++ b/internal/runtime/executor/openai_compat_executor.go @@ -293,20 +293,31 @@ func (e *OpenAICompatExecutor) ExecuteStream(ctx context.Context, auth *cliproxy // Pass through translator; it yields one or more chunks for the target schema. chunks := sdktranslator.TranslateStream(ctx, to, from, req.Model, opts.OriginalRequest, translated, bytes.Clone(line), ¶m) for i := range chunks { - out <- cliproxyexecutor.StreamChunk{Payload: chunks[i]} + select { + case out <- cliproxyexecutor.StreamChunk{Payload: chunks[i]}: + case <-ctx.Done(): + return + } } } if errScan := scanner.Err(); errScan != nil { helps.RecordAPIResponseError(ctx, e.cfg, errScan) reporter.PublishFailure(ctx) - out <- cliproxyexecutor.StreamChunk{Err: errScan} + select { + case out <- cliproxyexecutor.StreamChunk{Err: errScan}: + case <-ctx.Done(): + } } else { // In case the upstream close the stream without a terminal [DONE] marker. // Feed a synthetic done marker through the translator so pending // response.completed events are still emitted exactly once. chunks := sdktranslator.TranslateStream(ctx, to, from, req.Model, opts.OriginalRequest, translated, []byte("data: [DONE]"), ¶m) for i := range chunks { - out <- cliproxyexecutor.StreamChunk{Payload: chunks[i]} + select { + case out <- cliproxyexecutor.StreamChunk{Payload: chunks[i]}: + case <-ctx.Done(): + return + } } } // Ensure we record the request if no usage chunk was ever seen -- 2.51.2 From 2753d9fb711bb967e5310f35cde43135b04d84e9 Mon Sep 17 00:00:00 2001 From: Luis Pater Date: Mon, 4 May 2026 03:37:31 +0800 Subject: [PATCH 34/51] feat: add validation for Claude streaming responses - Implemented `validateClaudeStreamingResponse` to ensure upstream streaming data integrity. - Added new tests to verify response validation, including empty streams, error events, incomplete streams, and valid streams. - Integrated validation logic into the Claude executor's streaming handler, returning detailed errors for malformed upstream data. Fixed: #2193 --- internal/runtime/executor/claude_executor.go | 62 ++++++++++ .../runtime/executor/claude_executor_test.go | 107 ++++++++++++++++++ 2 files changed, 169 insertions(+) diff --git a/internal/runtime/executor/claude_executor.go b/internal/runtime/executor/claude_executor.go index 66432ac4..3734f262 100644 --- a/internal/runtime/executor/claude_executor.go +++ b/internal/runtime/executor/claude_executor.go @@ -285,6 +285,10 @@ func (e *ClaudeExecutor) Execute(ctx context.Context, auth *cliproxyauth.Auth, r } helps.AppendAPIResponseChunk(ctx, e.cfg, data) if stream { + if errValidate := validateClaudeStreamingResponse(data); errValidate != nil { + helps.RecordAPIResponseError(ctx, e.cfg, errValidate) + return resp, errValidate + } lines := bytes.Split(data, []byte("\n")) for _, line := range lines { if detail, ok := helps.ParseClaudeStreamUsage(line); ok { @@ -533,6 +537,64 @@ func (e *ClaudeExecutor) ExecuteStream(ctx context.Context, auth *cliproxyauth.A return &cliproxyexecutor.StreamResult{Headers: httpResp.Header.Clone(), Chunks: out}, nil } +func validateClaudeStreamingResponse(data []byte) error { + scanner := bufio.NewScanner(bytes.NewReader(data)) + scanner.Buffer(nil, 52_428_800) + + hasData := false + hasMessageStart := false + hasMessageDelta := false + + for scanner.Scan() { + line := bytes.TrimSpace(scanner.Bytes()) + if len(line) == 0 || !bytes.HasPrefix(line, []byte("data:")) { + continue + } + payload := bytes.TrimSpace(line[len("data:"):]) + if len(payload) == 0 || bytes.Equal(payload, []byte("[DONE]")) { + continue + } + hasData = true + if !gjson.ValidBytes(payload) { + return statusErr{code: http.StatusBadGateway, msg: "claude executor: upstream returned malformed stream data"} + } + + root := gjson.ParseBytes(payload) + switch root.Get("type").String() { + case "error": + message := strings.TrimSpace(root.Get("error.message").String()) + if message == "" { + message = strings.TrimSpace(root.Get("error.type").String()) + } + if message == "" { + message = "unknown upstream error" + } + return statusErr{code: http.StatusBadGateway, msg: "claude executor: upstream returned error event: " + message} + case "message_start": + message := root.Get("message") + if strings.TrimSpace(message.Get("id").String()) == "" || strings.TrimSpace(message.Get("model").String()) == "" { + return statusErr{code: http.StatusBadGateway, msg: "claude executor: upstream stream message_start is missing id or model"} + } + hasMessageStart = true + case "message_delta": + hasMessageDelta = true + } + } + if errScan := scanner.Err(); errScan != nil { + return errScan + } + if !hasData { + return statusErr{code: http.StatusBadGateway, msg: "claude executor: upstream returned empty stream response"} + } + if !hasMessageStart { + return statusErr{code: http.StatusBadGateway, msg: "claude executor: upstream stream response is missing message_start"} + } + if !hasMessageDelta { + return statusErr{code: http.StatusBadGateway, msg: "claude executor: upstream stream response ended before message completion"} + } + return nil +} + 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 diff --git a/internal/runtime/executor/claude_executor_test.go b/internal/runtime/executor/claude_executor_test.go index c1ce8fc0..6793adda 100644 --- a/internal/runtime/executor/claude_executor_test.go +++ b/internal/runtime/executor/claude_executor_test.go @@ -936,6 +936,113 @@ func TestClaudeExecutor_GeneratesNewUserIDByDefault(t *testing.T) { } } +func TestClaudeExecutor_ExecuteOpenAINonStreamRejectsEmptyClaudeStream(t *testing.T) { + _, err := executeOpenAIChatCompletionThroughClaude(t, "") + if err == nil { + t.Fatal("Execute error = nil, want empty stream error") + } + assertStatusErr(t, err, http.StatusBadGateway) + if !strings.Contains(err.Error(), "empty stream response") { + t.Fatalf("Execute error = %q, want empty stream response", err.Error()) + } +} + +func TestClaudeExecutor_ExecuteOpenAINonStreamRejectsClaudeErrorEvent(t *testing.T) { + body := `data: {"type":"error","error":{"type":"overloaded_error","message":"upstream overloaded"}}` + "\n" + _, err := executeOpenAIChatCompletionThroughClaude(t, body) + if err == nil { + t.Fatal("Execute error = nil, want upstream error event") + } + assertStatusErr(t, err, http.StatusBadGateway) + if !strings.Contains(err.Error(), "upstream overloaded") { + t.Fatalf("Execute error = %q, want upstream overloaded", err.Error()) + } +} + +func TestClaudeExecutor_ExecuteOpenAINonStreamRejectsIncompleteClaudeStream(t *testing.T) { + body := strings.Join([]string{ + `data: {"type":"message_start","message":{"id":"msg_123","model":"claude-3-5-sonnet-20241022"}}`, + `data: {"type":"message_stop"}`, + ``, + }, "\n") + + _, err := executeOpenAIChatCompletionThroughClaude(t, body) + if err == nil { + t.Fatal("Execute error = nil, want incomplete stream error") + } + assertStatusErr(t, err, http.StatusBadGateway) + if !strings.Contains(err.Error(), "ended before message completion") { + t.Fatalf("Execute error = %q, want incomplete stream error", err.Error()) + } +} + +func TestClaudeExecutor_ExecuteOpenAINonStreamConvertsValidClaudeStream(t *testing.T) { + body := strings.Join([]string{ + `event: message_start`, + `data: {"type":"message_start","message":{"id":"msg_123","model":"claude-3-5-sonnet-20241022"}}`, + `event: content_block_delta`, + `data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"ok"}}`, + `event: message_delta`, + `data: {"type":"message_delta","delta":{"stop_reason":"end_turn"},"usage":{"input_tokens":2,"output_tokens":1}}`, + `event: message_stop`, + `data: {"type":"message_stop"}`, + ``, + }, "\n") + + resp, err := executeOpenAIChatCompletionThroughClaude(t, body) + if err != nil { + t.Fatalf("Execute error: %v", err) + } + if got := gjson.GetBytes(resp.Payload, "id").String(); got != "msg_123" { + t.Fatalf("response id = %q, want msg_123; payload=%s", got, string(resp.Payload)) + } + if got := gjson.GetBytes(resp.Payload, "model").String(); got != "claude-3-5-sonnet-20241022" { + t.Fatalf("response model = %q, want claude-3-5-sonnet-20241022", got) + } + if got := gjson.GetBytes(resp.Payload, "choices.0.message.content").String(); got != "ok" { + t.Fatalf("response content = %q, want ok", got) + } + if got := gjson.GetBytes(resp.Payload, "usage.total_tokens").Int(); got != 3 { + t.Fatalf("usage.total_tokens = %d, want 3", got) + } +} + +func executeOpenAIChatCompletionThroughClaude(t *testing.T, upstreamBody string) (cliproxyexecutor.Response, error) { + t.Helper() + + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "text/event-stream") + _, _ = w.Write([]byte(upstreamBody)) + })) + defer server.Close() + + executor := NewClaudeExecutor(&config.Config{}) + auth := &cliproxyauth.Auth{Attributes: map[string]string{ + "api_key": "key-123", + "base_url": server.URL, + }} + payload := []byte(`{"model":"claude-3-5-sonnet-20241022","messages":[{"role":"user","content":"hi"}]}`) + + return executor.Execute(context.Background(), auth, cliproxyexecutor.Request{ + Model: "claude-3-5-sonnet-20241022", + Payload: payload, + }, cliproxyexecutor.Options{ + SourceFormat: sdktranslator.FromString("openai"), + }) +} + +func assertStatusErr(t *testing.T, err error, want int) { + t.Helper() + + status, ok := err.(interface{ StatusCode() int }) + if !ok { + t.Fatalf("error %T does not expose StatusCode", err) + } + if got := status.StatusCode(); got != want { + t.Fatalf("StatusCode() = %d, want %d", got, want) + } +} + func TestStripClaudeToolPrefixFromResponse_NestedToolReference(t *testing.T) { input := []byte(`{"content":[{"type":"tool_result","tool_use_id":"toolu_123","content":[{"type":"tool_reference","tool_name":"proxy_mcp__nia__manage_resource"}]}]}`) out := stripClaudeToolPrefixFromResponse(input, "proxy_") -- 2.51.2 From a1487b095855d37998e4c9edbf94aad1670e8e9f Mon Sep 17 00:00:00 2001 From: Luis Pater Date: Mon, 4 May 2026 05:08:31 +0800 Subject: [PATCH 35/51] fix(translator): handle non-string types in tools result processing - Skip setting values for non-string `type` fields to prevent runtime errors. Closes: #2226 --- .../codex_gemini-cli_request_test.go | 78 +++++++++++++++++++ .../codex/gemini/codex_gemini_request.go | 6 +- 2 files changed, 83 insertions(+), 1 deletion(-) create mode 100644 internal/translator/codex/gemini-cli/codex_gemini-cli_request_test.go diff --git a/internal/translator/codex/gemini-cli/codex_gemini-cli_request_test.go b/internal/translator/codex/gemini-cli/codex_gemini-cli_request_test.go new file mode 100644 index 00000000..fc41452b --- /dev/null +++ b/internal/translator/codex/gemini-cli/codex_gemini-cli_request_test.go @@ -0,0 +1,78 @@ +package geminiCLI + +import ( + "testing" + + "github.com/tidwall/gjson" +) + +func TestConvertGeminiCLIRequestToCodex_PreservesSchemaPropertyNamedType(t *testing.T) { + input := []byte(`{ + "request": { + "tools": [ + { + "functionDeclarations": [ + { + "name": "ask_user", + "description": "Ask the user one or more questions.", + "parametersJsonSchema": { + "type": "object", + "properties": { + "questions": { + "type": "array", + "items": { + "type": "object", + "properties": { + "header": { + "type": "string" + }, + "type": { + "default": "choice", + "description": "Question type.", + "enum": [ + "choice", + "text", + "yesno" + ], + "type": "string" + } + }, + "required": [ + "question", + "header", + "type" + ] + } + } + }, + "required": [ + "questions" + ] + } + } + ] + } + ] + } + }`) + + out := ConvertGeminiCLIRequestToCodex("gpt-5.2", input, true) + tool := gjson.GetBytes(out, "tools.0") + if got := tool.Get("type").String(); got != "function" { + t.Fatalf("expected tool type %q, got %q; output=%s", "function", got, string(out)) + } + + typeProperty := tool.Get("parameters.properties.questions.items.properties.type") + if !typeProperty.IsObject() { + t.Fatalf("expected schema property named type to stay an object; output=%s", string(out)) + } + if got := typeProperty.Get("type").String(); got != "string" { + t.Fatalf("expected schema property type %q, got %q; output=%s", "string", got, string(out)) + } + if got := typeProperty.Get("default").String(); got != "choice" { + t.Fatalf("expected default %q, got %q; output=%s", "choice", got, string(out)) + } + if got := typeProperty.Get("enum.2").String(); got != "yesno" { + t.Fatalf("expected enum value %q, got %q; output=%s", "yesno", got, string(out)) + } +} diff --git a/internal/translator/codex/gemini/codex_gemini_request.go b/internal/translator/codex/gemini/codex_gemini_request.go index 23dae7d7..37399700 100644 --- a/internal/translator/codex/gemini/codex_gemini_request.go +++ b/internal/translator/codex/gemini/codex_gemini_request.go @@ -284,7 +284,11 @@ func ConvertGeminiRequestToCodex(modelName string, inputRawJSON []byte, _ bool) util.Walk(toolsResult, "", "type", &pathsToLower) for _, p := range pathsToLower { fullPath := fmt.Sprintf("tools.%s", p) - out, _ = sjson.SetBytes(out, fullPath, strings.ToLower(gjson.GetBytes(out, fullPath).String())) + typeValue := gjson.GetBytes(out, fullPath) + if typeValue.Type != gjson.String { + continue + } + out, _ = sjson.SetBytes(out, fullPath, strings.ToLower(typeValue.String())) } return out -- 2.51.2 From 8e6ef3fa645caf15466130084760c1ecf6d925bb Mon Sep 17 00:00:00 2001 From: Luis Pater Date: Mon, 4 May 2026 05:23:23 +0800 Subject: [PATCH 36/51] fix(websocket): ensure state consistency on auth errors in streaming - Added logic to reset `pinnedAuthID` and replay transcript on unauthorized, forbidden, or throttling errors. - Enhanced error handling in `forwardResponsesWebsocket` with detailed status inspection. - Introduced `shouldReleaseResponsesWebsocketPinnedAuth` to determine auth reset conditions. - Updated state management to preserve prior request and response data during forced replay. Fixed: #2230 --- .../openai/openai_responses_websocket.go | 53 ++++- .../openai/openai_responses_websocket_test.go | 187 +++++++++++++++++- 2 files changed, 229 insertions(+), 11 deletions(-) diff --git a/sdk/api/handlers/openai/openai_responses_websocket.go b/sdk/api/handlers/openai/openai_responses_websocket.go index caf26f13..7a9d2224 100644 --- a/sdk/api/handlers/openai/openai_responses_websocket.go +++ b/sdk/api/handlers/openai/openai_responses_websocket.go @@ -79,6 +79,7 @@ func (h *OpenAIResponsesAPIHandler) ResponsesWebsocket(c *gin.Context) { var lastRequest []byte lastResponseOutput := []byte("[]") pinnedAuthID := "" + forceTranscriptReplayNextRequest := false for { msgType, payload, errReadMessage := conn.ReadMessage() @@ -115,6 +116,9 @@ func (h *OpenAIResponsesAPIHandler) ResponsesWebsocket(c *gin.Context) { } allowIncrementalInputWithPreviousResponseID = h.websocketUpstreamSupportsIncrementalInputForModel(requestModelName) } + if forceTranscriptReplayNextRequest { + allowIncrementalInputWithPreviousResponseID = false + } allowCompactionReplayBypass := false if pinnedAuthID != "" && h != nil && h.AuthManager != nil { @@ -179,7 +183,13 @@ func (h *OpenAIResponsesAPIHandler) ResponsesWebsocket(c *gin.Context) { requestJSON = repairResponsesWebsocketToolCalls(downstreamSessionKey, requestJSON) updatedLastRequest = bytes.Clone(requestJSON) + previousLastRequest := bytes.Clone(lastRequest) + previousLastResponseOutput := bytes.Clone(lastResponseOutput) + forcedTranscriptReplay := forceTranscriptReplayNextRequest lastRequest = updatedLastRequest + if forcedTranscriptReplay { + forceTranscriptReplayNextRequest = false + } modelName := gjson.GetBytes(requestJSON, "model").String() cliCtx, cliCancel := h.GetContextWithCancel(h, c, context.Background()) @@ -204,12 +214,19 @@ func (h *OpenAIResponsesAPIHandler) ResponsesWebsocket(c *gin.Context) { } dataChan, _, errChan := h.ExecuteStreamWithAuthManager(cliCtx, h.HandlerType(), modelName, requestJSON, "") - completedOutput, errForward := h.forwardResponsesWebsocket(c, conn, cliCancel, dataChan, errChan, &wsTimelineLog, passthroughSessionID) + completedOutput, forwardErrMsg, errForward := h.forwardResponsesWebsocket(c, conn, cliCancel, dataChan, errChan, &wsTimelineLog, passthroughSessionID) if errForward != nil { wsTerminateErr = errForward log.Warnf("responses websocket: forward failed id=%s error=%v", passthroughSessionID, errForward) return } + if shouldReleaseResponsesWebsocketPinnedAuth(forwardErrMsg) { + pinnedAuthID = "" + forceTranscriptReplayNextRequest = true + lastRequest = previousLastRequest + lastResponseOutput = previousLastResponseOutput + continue + } lastResponseOutput = completedOutput } } @@ -810,7 +827,7 @@ func (h *OpenAIResponsesAPIHandler) forwardResponsesWebsocket( errs <-chan *interfaces.ErrorMessage, wsTimelineLog *strings.Builder, sessionID string, -) ([]byte, error) { +) ([]byte, *interfaces.ErrorMessage, error) { completed := false completedOutput := []byte("[]") downstreamSessionKey := "" @@ -822,7 +839,7 @@ func (h *OpenAIResponsesAPIHandler) forwardResponsesWebsocket( select { case <-c.Request.Context().Done(): cancel(c.Request.Context().Err()) - return completedOutput, c.Request.Context().Err() + return completedOutput, nil, c.Request.Context().Err() case errMsg, ok := <-errs: if !ok { errs = nil @@ -847,7 +864,7 @@ func (h *OpenAIResponsesAPIHandler) forwardResponsesWebsocket( // errWrite, // ) cancel(errMsg.Error) - return completedOutput, errWrite + return completedOutput, errMsg, errWrite } } if errMsg != nil { @@ -855,7 +872,7 @@ func (h *OpenAIResponsesAPIHandler) forwardResponsesWebsocket( } else { cancel(nil) } - return completedOutput, nil + return completedOutput, errMsg, nil case chunk, ok := <-data: if !ok { if !completed { @@ -881,13 +898,13 @@ func (h *OpenAIResponsesAPIHandler) forwardResponsesWebsocket( errWrite, ) cancel(errMsg.Error) - return completedOutput, errWrite + return completedOutput, errMsg, errWrite } cancel(errMsg.Error) - return completedOutput, nil + return completedOutput, errMsg, nil } cancel(nil) - return completedOutput, nil + return completedOutput, nil, nil } payloads := websocketJSONPayloadsFromChunk(chunk) @@ -914,13 +931,31 @@ func (h *OpenAIResponsesAPIHandler) forwardResponsesWebsocket( errWrite, ) cancel(errWrite) - return completedOutput, errWrite + return completedOutput, nil, errWrite } } } } } +func shouldReleaseResponsesWebsocketPinnedAuth(errMsg *interfaces.ErrorMessage) bool { + if errMsg == nil { + return false + } + status := errMsg.StatusCode + if status <= 0 && errMsg.Error != nil { + if se, ok := errMsg.Error.(interface{ StatusCode() int }); ok && se != nil { + status = se.StatusCode() + } + } + switch status { + case http.StatusUnauthorized, http.StatusPaymentRequired, http.StatusForbidden, http.StatusTooManyRequests: + return true + default: + return false + } +} + func responseCompletedOutputFromPayload(payload []byte) []byte { output := gjson.GetBytes(payload, "response.output") if output.Exists() && output.IsArray() { diff --git a/sdk/api/handlers/openai/openai_responses_websocket_test.go b/sdk/api/handlers/openai/openai_responses_websocket_test.go index f2c4319e..1d397ecd 100644 --- a/sdk/api/handlers/openai/openai_responses_websocket_test.go +++ b/sdk/api/handlers/openai/openai_responses_websocket_test.go @@ -69,6 +69,22 @@ type websocketAuthCaptureExecutor struct { authIDs []string } +type websocketPinnedFailoverExecutor struct { + mu sync.Mutex + authIDs []string + calls map[string]int + payloads map[string][][]byte +} + +type websocketPinnedFailoverStatusError struct { + status int + msg string +} + +func (e websocketPinnedFailoverStatusError) Error() string { return e.msg } + +func (e websocketPinnedFailoverStatusError) StatusCode() int { return e.status } + func (e *websocketAuthCaptureExecutor) Identifier() string { return "test-provider" } func (e *websocketAuthCaptureExecutor) Execute(context.Context, *coreauth.Auth, coreexecutor.Request, coreexecutor.Options) (coreexecutor.Response, error) { @@ -106,6 +122,76 @@ func (e *websocketAuthCaptureExecutor) AuthIDs() []string { return append([]string(nil), e.authIDs...) } +func (e *websocketPinnedFailoverExecutor) Identifier() string { return "test-provider" } + +func (e *websocketPinnedFailoverExecutor) Execute(context.Context, *coreauth.Auth, coreexecutor.Request, coreexecutor.Options) (coreexecutor.Response, error) { + return coreexecutor.Response{}, errors.New("not implemented") +} + +func (e *websocketPinnedFailoverExecutor) ExecuteStream(_ context.Context, auth *coreauth.Auth, req coreexecutor.Request, _ coreexecutor.Options) (*coreexecutor.StreamResult, error) { + authID := "" + if auth != nil { + authID = auth.ID + } + + e.mu.Lock() + if e.calls == nil { + e.calls = make(map[string]int) + } + if e.payloads == nil { + e.payloads = make(map[string][][]byte) + } + e.authIDs = append(e.authIDs, authID) + e.calls[authID]++ + call := e.calls[authID] + e.payloads[authID] = append(e.payloads[authID], bytes.Clone(req.Payload)) + e.mu.Unlock() + + if authID == "auth-a" && call == 2 { + chunks := make(chan coreexecutor.StreamChunk, 1) + chunks <- coreexecutor.StreamChunk{Err: websocketPinnedFailoverStatusError{ + status: http.StatusTooManyRequests, + msg: `{"error":{"message":"quota exhausted","type":"rate_limit_error","code":"rate_limit_exceeded"}}`, + }} + close(chunks) + return &coreexecutor.StreamResult{Chunks: chunks}, nil + } + + chunks := make(chan coreexecutor.StreamChunk, 1) + chunks <- coreexecutor.StreamChunk{Payload: []byte(fmt.Sprintf(`{"type":"response.completed","response":{"id":"resp-%s-%d","output":[{"type":"message","id":"out-%s-%d"}]}}`, authID, call, authID, call))} + close(chunks) + return &coreexecutor.StreamResult{Chunks: chunks}, nil +} + +func (e *websocketPinnedFailoverExecutor) Refresh(_ context.Context, auth *coreauth.Auth) (*coreauth.Auth, error) { + return auth, nil +} + +func (e *websocketPinnedFailoverExecutor) CountTokens(context.Context, *coreauth.Auth, coreexecutor.Request, coreexecutor.Options) (coreexecutor.Response, error) { + return coreexecutor.Response{}, errors.New("not implemented") +} + +func (e *websocketPinnedFailoverExecutor) HttpRequest(context.Context, *coreauth.Auth, *http.Request) (*http.Response, error) { + return nil, errors.New("not implemented") +} + +func (e *websocketPinnedFailoverExecutor) AuthIDs() []string { + e.mu.Lock() + defer e.mu.Unlock() + return append([]string(nil), e.authIDs...) +} + +func (e *websocketPinnedFailoverExecutor) Payloads(authID string) [][]byte { + e.mu.Lock() + defer e.mu.Unlock() + src := e.payloads[authID] + out := make([][]byte, len(src)) + for i := range src { + out[i] = bytes.Clone(src[i]) + } + return out +} + func (e *websocketCaptureExecutor) Identifier() string { return "test-provider" } func (e *websocketCaptureExecutor) Execute(context.Context, *coreauth.Auth, coreexecutor.Request, coreexecutor.Options) (coreexecutor.Response, error) { @@ -681,7 +767,7 @@ func TestForwardResponsesWebsocketPreservesCompletedEvent(t *testing.T) { close(errCh) var timelineLog strings.Builder - completedOutput, err := (*OpenAIResponsesAPIHandler)(nil).forwardResponsesWebsocket( + completedOutput, errMsg, err := (*OpenAIResponsesAPIHandler)(nil).forwardResponsesWebsocket( ctx, conn, func(...interface{}) {}, @@ -694,6 +780,10 @@ func TestForwardResponsesWebsocketPreservesCompletedEvent(t *testing.T) { serverErrCh <- err return } + if errMsg != nil { + serverErrCh <- fmt.Errorf("unexpected websocket error message: %v", errMsg.Error) + return + } if gjson.GetBytes(completedOutput, "0.id").String() != "out-1" { serverErrCh <- errors.New("completed output not captured") return @@ -760,7 +850,7 @@ func TestForwardResponsesWebsocketLogsAttemptedResponseOnWriteFailure(t *testing return } - _, err = (*OpenAIResponsesAPIHandler)(nil).forwardResponsesWebsocket( + _, _, err = (*OpenAIResponsesAPIHandler)(nil).forwardResponsesWebsocket( ctx, conn, func(...interface{}) {}, @@ -1113,6 +1203,99 @@ func TestResponsesWebsocketPinsOnlyWebsocketCapableAuth(t *testing.T) { } } +func TestResponsesWebsocketReleasesPinnedAuthAfterQuotaError(t *testing.T) { + gin.SetMode(gin.TestMode) + + selector := &orderedWebsocketSelector{order: []string{"auth-a", "auth-b"}} + executor := &websocketPinnedFailoverExecutor{} + manager := coreauth.NewManager(nil, selector, nil) + manager.RegisterExecutor(executor) + + authA := &coreauth.Auth{ + ID: "auth-a", + Provider: executor.Identifier(), + Status: coreauth.StatusActive, + Attributes: map[string]string{"websockets": "true"}, + } + if _, err := manager.Register(context.Background(), authA); err != nil { + t.Fatalf("Register auth A: %v", err) + } + authB := &coreauth.Auth{ + ID: "auth-b", + Provider: executor.Identifier(), + Status: coreauth.StatusActive, + Attributes: map[string]string{"websockets": "true"}, + } + if _, err := manager.Register(context.Background(), authB); err != nil { + t.Fatalf("Register auth B: %v", err) + } + + registry.GetGlobalRegistry().RegisterClient(authA.ID, authA.Provider, []*registry.ModelInfo{{ID: "quota-model"}}) + registry.GetGlobalRegistry().RegisterClient(authB.ID, authB.Provider, []*registry.ModelInfo{{ID: "quota-model"}}) + t.Cleanup(func() { + registry.GetGlobalRegistry().UnregisterClient(authA.ID) + registry.GetGlobalRegistry().UnregisterClient(authB.ID) + }) + + base := handlers.NewBaseAPIHandlers(&sdkconfig.SDKConfig{}, manager) + h := NewOpenAIResponsesAPIHandler(base) + router := gin.New() + router.GET("/v1/responses/ws", h.ResponsesWebsocket) + + server := httptest.NewServer(router) + defer server.Close() + + wsURL := "ws" + strings.TrimPrefix(server.URL, "http") + "/v1/responses/ws" + conn, _, err := websocket.DefaultDialer.Dial(wsURL, nil) + if err != nil { + t.Fatalf("dial websocket: %v", err) + } + defer func() { + if errClose := conn.Close(); errClose != nil { + t.Fatalf("close websocket: %v", errClose) + } + }() + + requests := []string{ + `{"type":"response.create","model":"quota-model","input":[{"type":"message","id":"msg-1"}]}`, + `{"type":"response.create","previous_response_id":"resp-auth-a-1","input":[{"type":"message","id":"msg-2"}]}`, + `{"type":"response.create","previous_response_id":"resp-auth-a-1","input":[{"type":"message","id":"msg-3"}]}`, + } + wantTypes := []string{wsEventTypeCompleted, wsEventTypeError, wsEventTypeCompleted} + for i := range requests { + if errWrite := conn.WriteMessage(websocket.TextMessage, []byte(requests[i])); errWrite != nil { + t.Fatalf("write websocket message %d: %v", i+1, errWrite) + } + _, payload, errReadMessage := conn.ReadMessage() + if errReadMessage != nil { + t.Fatalf("read websocket message %d: %v", i+1, errReadMessage) + } + if got := gjson.GetBytes(payload, "type").String(); got != wantTypes[i] { + t.Fatalf("message %d payload type = %s, want %s: %s", i+1, got, wantTypes[i], payload) + } + if i == 1 && int(gjson.GetBytes(payload, "status").Int()) != http.StatusTooManyRequests { + t.Fatalf("quota payload status = %d, want %d: %s", gjson.GetBytes(payload, "status").Int(), http.StatusTooManyRequests, payload) + } + } + + if got := executor.AuthIDs(); len(got) != 3 || got[0] != "auth-a" || got[1] != "auth-a" || got[2] != "auth-b" { + t.Fatalf("selected auth IDs = %v, want [auth-a auth-a auth-b]", got) + } + + authBPayloads := executor.Payloads("auth-b") + if len(authBPayloads) != 1 { + t.Fatalf("auth-b payload count = %d, want 1", len(authBPayloads)) + } + authBPayload := authBPayloads[0] + if gjson.GetBytes(authBPayload, "previous_response_id").Exists() { + t.Fatalf("previous_response_id leaked after auth failover: %s", authBPayload) + } + authBInput := gjson.GetBytes(authBPayload, "input").Raw + if !strings.Contains(authBInput, `"id":"msg-1"`) || !strings.Contains(authBInput, `"id":"msg-3"`) { + t.Fatalf("auth-b replay input missing expected transcript items: %s", authBInput) + } +} + func TestNormalizeResponsesWebsocketRequestTreatsTranscriptReplacementAsReset(t *testing.T) { lastRequest := []byte(`{"model":"test-model","stream":true,"input":[{"type":"message","id":"msg-1"},{"type":"function_call","id":"fc-1","call_id":"call-1"},{"type":"function_call_output","id":"tool-out-1","call_id":"call-1"},{"type":"message","id":"assistant-1","role":"assistant"}]}`) lastResponseOutput := []byte(`[ -- 2.51.2 From 38dad2afdf8280350e0f09b07a32ba0865596a26 Mon Sep 17 00:00:00 2001 From: Luis Pater Date: Mon, 4 May 2026 05:36:09 +0800 Subject: [PATCH 37/51] chore(docker): upgrade base image to alpine 3.23 Fixed: #2265 --- Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 3e10c4f9..b4caaee3 100644 --- a/Dockerfile +++ b/Dockerfile @@ -14,7 +14,7 @@ ARG BUILD_DATE=unknown RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w -X 'main.Version=${VERSION}' -X 'main.Commit=${COMMIT}' -X 'main.BuildDate=${BUILD_DATE}'" -o ./CLIProxyAPI ./cmd/server/ -FROM alpine:3.22.0 +FROM alpine:3.23 RUN apk add --no-cache tzdata @@ -32,4 +32,4 @@ ENV TZ=Asia/Shanghai RUN cp /usr/share/zoneinfo/${TZ} /etc/localtime && echo "${TZ}" > /etc/timezone -CMD ["./CLIProxyAPI"] \ No newline at end of file +CMD ["./CLIProxyAPI"] -- 2.51.2 From 17be6442a8bfebe69e20631d99b5b6961eb54e4c Mon Sep 17 00:00:00 2001 From: Luis Pater Date: Mon, 4 May 2026 05:50:01 +0800 Subject: [PATCH 38/51] fix(translator): improve tool response handling for non-string content - Added `setToolCallOutputContent` to process various content types, including arrays and fallback cases. - Implemented robust handling for specific tool output types like text, image URLs, and files, ensuring proper serialization. - Improved fallback logic to handle unexpected or missing data. Fixed: #2313 Closes: #2349 --- .../chat-completions/codex_openai_request.go | 89 ++++++++- .../codex_openai_request_test.go | 176 ++++++++++++++++++ 2 files changed, 263 insertions(+), 2 deletions(-) diff --git a/internal/translator/codex/openai/chat-completions/codex_openai_request.go b/internal/translator/codex/openai/chat-completions/codex_openai_request.go index 6cc701e7..569e06e3 100644 --- a/internal/translator/codex/openai/chat-completions/codex_openai_request.go +++ b/internal/translator/codex/openai/chat-completions/codex_openai_request.go @@ -121,13 +121,13 @@ func ConvertOpenAIRequestToCodex(modelName string, inputRawJSON []byte, stream b case "tool": // Handle tool response messages as top-level function_call_output objects toolCallID := m.Get("tool_call_id").String() - content := m.Get("content").String() + content := m.Get("content") // Create function_call_output object funcOutput := []byte(`{}`) funcOutput, _ = sjson.SetBytes(funcOutput, "type", "function_call_output") funcOutput, _ = sjson.SetBytes(funcOutput, "call_id", toolCallID) - funcOutput, _ = sjson.SetBytes(funcOutput, "output", content) + funcOutput = setToolCallOutputContent(funcOutput, content) out, _ = sjson.SetRawBytes(out, "input.-1", funcOutput) default: @@ -359,6 +359,91 @@ func ConvertOpenAIRequestToCodex(modelName string, inputRawJSON []byte, stream b return out } +func setToolCallOutputContent(funcOutput []byte, content gjson.Result) []byte { + switch { + case content.Type == gjson.String: + funcOutput, _ = sjson.SetBytes(funcOutput, "output", content.String()) + case content.IsArray(): + output := []byte(`[]`) + for _, item := range content.Array() { + output = appendToolOutputContentPart(output, item) + } + funcOutput, _ = sjson.SetRawBytes(funcOutput, "output", output) + default: + fallbackOutput := content.Raw + if fallbackOutput == "" { + fallbackOutput = content.String() + } + funcOutput, _ = sjson.SetBytes(funcOutput, "output", fallbackOutput) + } + return funcOutput +} + +func appendToolOutputContentPart(output []byte, item gjson.Result) []byte { + switch item.Get("type").String() { + case "text": + part := []byte(`{}`) + part, _ = sjson.SetBytes(part, "type", "input_text") + part, _ = sjson.SetBytes(part, "text", item.Get("text").String()) + output, _ = sjson.SetRawBytes(output, "-1", part) + case "image_url": + imageURL := item.Get("image_url.url").String() + fileID := item.Get("image_url.file_id").String() + if imageURL == "" && fileID == "" { + return appendToolOutputFallbackPart(output, item) + } + part := []byte(`{}`) + part, _ = sjson.SetBytes(part, "type", "input_image") + if imageURL != "" { + part, _ = sjson.SetBytes(part, "image_url", imageURL) + } + if fileID != "" { + part, _ = sjson.SetBytes(part, "file_id", fileID) + } + if detail := item.Get("image_url.detail").String(); detail != "" { + part, _ = sjson.SetBytes(part, "detail", detail) + } + output, _ = sjson.SetRawBytes(output, "-1", part) + case "file": + fileID := item.Get("file.file_id").String() + fileData := item.Get("file.file_data").String() + fileURL := item.Get("file.file_url").String() + if fileID == "" && fileData == "" && fileURL == "" { + return appendToolOutputFallbackPart(output, item) + } + part := []byte(`{}`) + part, _ = sjson.SetBytes(part, "type", "input_file") + if fileID != "" { + part, _ = sjson.SetBytes(part, "file_id", fileID) + } + if fileData != "" { + part, _ = sjson.SetBytes(part, "file_data", fileData) + } + if fileURL != "" { + part, _ = sjson.SetBytes(part, "file_url", fileURL) + } + if filename := item.Get("file.filename").String(); filename != "" { + part, _ = sjson.SetBytes(part, "filename", filename) + } + output, _ = sjson.SetRawBytes(output, "-1", part) + default: + output = appendToolOutputFallbackPart(output, item) + } + return output +} + +func appendToolOutputFallbackPart(output []byte, item gjson.Result) []byte { + text := item.Raw + if text == "" { + text = item.String() + } + part := []byte(`{}`) + part, _ = sjson.SetBytes(part, "type", "input_text") + part, _ = sjson.SetBytes(part, "text", text) + output, _ = sjson.SetRawBytes(output, "-1", part) + return output +} + // shortenNameIfNeeded applies the simple shortening rule for a single name. // If the name length exceeds 64, it will try to preserve the "mcp__" prefix and last segment. // Otherwise it truncates to 64 characters. diff --git a/internal/translator/codex/openai/chat-completions/codex_openai_request_test.go b/internal/translator/codex/openai/chat-completions/codex_openai_request_test.go index 84c8dad2..e31db6d3 100644 --- a/internal/translator/codex/openai/chat-completions/codex_openai_request_test.go +++ b/internal/translator/codex/openai/chat-completions/codex_openai_request_test.go @@ -176,6 +176,182 @@ func TestToolCallWithContent(t *testing.T) { } } +func TestToolCallOutputWithMultimodalContent(t *testing.T) { + input := []byte(`{ + "model": "gpt-4o", + "messages": [ + {"role": "user", "content": "Show me the generated result."}, + { + "role": "assistant", + "content": null, + "tool_calls": [ + { + "id": "call_output_1", + "type": "function", + "function": {"name": "render_output", "arguments": "{}"} + } + ] + }, + { + "role": "tool", + "tool_call_id": "call_output_1", + "content": [ + {"type":"text","text":"Rendered result attached."}, + {"type":"image_url","image_url":{"url":"https://example.com/generated.png","detail":"high"}}, + {"type":"image_url","image_url":{"file_id":"file-img-123"}}, + {"type":"file","file":{"file_id":"file-doc-123","filename":"doc.pdf"}}, + {"type":"file","file":{"file_data":"SGVsbG8=","filename":"inline.txt"}}, + {"type":"file","file":{"file_url":"https://example.com/report.pdf","filename":"report.pdf"}} + ] + } + ], + "tools": [ + { + "type": "function", + "function": {"name": "render_output", "description": "Render output", "parameters": {"type": "object", "properties": {}}} + } + ] + }`) + + out := ConvertOpenAIRequestToCodex("gpt-4o", input, true) + result := string(out) + + output := gjson.Get(result, "input.2.output") + if !output.IsArray() { + t.Fatalf("expected tool output to be an array, got: %s", output.Raw) + } + + parts := output.Array() + if len(parts) != 6 { + t.Fatalf("expected 6 output parts, got %d: %s", len(parts), output.Raw) + } + if parts[0].Get("type").String() != "input_text" || parts[0].Get("text").String() != "Rendered result attached." { + t.Fatalf("part 0: expected input_text with rendered text, got %s", parts[0].Raw) + } + if parts[1].Get("type").String() != "input_image" { + t.Fatalf("part 1: expected input_image, got %s", parts[1].Raw) + } + if parts[1].Get("image_url").String() != "https://example.com/generated.png" { + t.Errorf("part 1: unexpected image_url %s", parts[1].Get("image_url").String()) + } + if parts[1].Get("detail").String() != "high" { + t.Errorf("part 1: unexpected detail %s", parts[1].Get("detail").String()) + } + if parts[2].Get("type").String() != "input_image" || parts[2].Get("file_id").String() != "file-img-123" { + t.Fatalf("part 2: expected file_id-backed input_image, got %s", parts[2].Raw) + } + if parts[3].Get("type").String() != "input_file" || parts[3].Get("file_id").String() != "file-doc-123" { + t.Fatalf("part 3: expected file_id-backed input_file, got %s", parts[3].Raw) + } + if parts[3].Get("filename").String() != "doc.pdf" { + t.Errorf("part 3: unexpected filename %s", parts[3].Get("filename").String()) + } + if parts[4].Get("type").String() != "input_file" || parts[4].Get("file_data").String() != "SGVsbG8=" { + t.Fatalf("part 4: expected file_data-backed input_file, got %s", parts[4].Raw) + } + if parts[5].Get("type").String() != "input_file" || parts[5].Get("file_url").String() != "https://example.com/report.pdf" { + t.Fatalf("part 5: expected file_url-backed input_file, got %s", parts[5].Raw) + } +} + +func TestToolCallOutputFallsBackForInvalidStructuredParts(t *testing.T) { + input := []byte(`{ + "model": "gpt-4o", + "messages": [ + {"role": "user", "content": "Check tool output."}, + { + "role": "assistant", + "content": null, + "tool_calls": [ + {"id": "call_invalid_parts", "type": "function", "function": {"name": "inspect", "arguments": "{}"}} + ] + }, + { + "role": "tool", + "tool_call_id": "call_invalid_parts", + "content": [ + {"type":"image_url","image_url":{"detail":"low"}}, + {"type":"file","file":{"filename":"orphan.txt"}}, + {"type":"unknown_type","foo":"bar","nested":{"a":1}} + ] + } + ], + "tools": [ + {"type": "function", "function": {"name": "inspect", "description": "Inspect", "parameters": {"type": "object", "properties": {}}}} + ] + }`) + + out := ConvertOpenAIRequestToCodex("gpt-4o", input, true) + result := string(out) + + parts := gjson.Get(result, "input.2.output").Array() + if len(parts) != 3 { + t.Fatalf("expected 3 output parts, got %d: %s", len(parts), gjson.Get(result, "input.2.output").Raw) + } + + expectedFallbacks := []string{ + `{"type":"image_url","image_url":{"detail":"low"}}`, + `{"type":"file","file":{"filename":"orphan.txt"}}`, + `{"type":"unknown_type","foo":"bar","nested":{"a":1}}`, + } + for i, expectedFallback := range expectedFallbacks { + if parts[i].Get("type").String() != "input_text" { + t.Fatalf("part %d: expected input_text fallback, got %s", i, parts[i].Raw) + } + if parts[i].Get("text").String() != expectedFallback { + t.Fatalf("part %d: expected fallback %s, got %s", i, expectedFallback, parts[i].Get("text").String()) + } + } +} + +func TestToolCallOutputWithNonStringJSONContent(t *testing.T) { + tests := []struct { + name string + content string + expectedOutput string + }{ + {name: "null", content: `null`, expectedOutput: `null`}, + {name: "object", content: `{"status":"ok","count":2}`, expectedOutput: `{"status":"ok","count":2}`}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + input := []byte(`{ + "model": "gpt-4o", + "messages": [ + {"role": "user", "content": "Check tool output."}, + { + "role": "assistant", + "content": null, + "tool_calls": [ + {"id": "call_json", "type": "function", "function": {"name": "inspect", "arguments": "{}"}} + ] + }, + { + "role": "tool", + "tool_call_id": "call_json", + "content": ` + tt.content + ` + } + ], + "tools": [ + {"type": "function", "function": {"name": "inspect", "description": "Inspect", "parameters": {"type": "object", "properties": {}}}} + ] + }`) + + out := ConvertOpenAIRequestToCodex("gpt-4o", input, true) + result := string(out) + + output := gjson.Get(result, "input.2.output") + if !output.Exists() { + t.Fatalf("expected output field to exist: %s", gjson.Get(result, "input.2").Raw) + } + if output.String() != tt.expectedOutput { + t.Fatalf("expected output %s, got %s", tt.expectedOutput, output.String()) + } + }) + } +} + // Parallel tool calls: assistant invokes 3 tools at once, all call_ids // and outputs must be translated and paired correctly. func TestMultipleToolCalls(t *testing.T) { -- 2.51.2 From c19ae1d5be32537218b61e26ebe7845720966801 Mon Sep 17 00:00:00 2001 From: Kenny Date: Sun, 3 May 2026 15:56:39 -0700 Subject: [PATCH 39/51] Align Codex websocket protocol semantics --- internal/runtime/executor/codex_executor.go | 2 +- .../executor/codex_websockets_executor.go | 153 +++++++++++--- .../codex_websockets_executor_test.go | 189 +++++++++++++++++- 3 files changed, 310 insertions(+), 34 deletions(-) diff --git a/internal/runtime/executor/codex_executor.go b/internal/runtime/executor/codex_executor.go index aa8223f4..5e892ecd 100644 --- a/internal/runtime/executor/codex_executor.go +++ b/internal/runtime/executor/codex_executor.go @@ -31,7 +31,7 @@ import ( const ( codexUserAgent = "codex-tui/0.118.0 (Mac OS 26.3.1; arm64) iTerm.app/3.6.9 (codex-tui; 0.118.0)" - codexOriginator = "codex-tui" + codexOriginator = "codex_cli_rs" codexDefaultImageToolModel = "gpt-image-2" ) diff --git a/internal/runtime/executor/codex_websockets_executor.go b/internal/runtime/executor/codex_websockets_executor.go index 40ba7e92..87ae0efe 100644 --- a/internal/runtime/executor/codex_websockets_executor.go +++ b/internal/runtime/executor/codex_websockets_executor.go @@ -188,7 +188,6 @@ func (e *CodexWebsocketsExecutor) Execute(ctx context.Context, auth *cliproxyaut body = helps.ApplyPayloadConfigWithRoot(e.cfg, baseModel, to.String(), "", body, originalTranslated, requestedModel, requestPath) body, _ = sjson.SetBytes(body, "model", baseModel) body, _ = sjson.SetBytes(body, "stream", true) - body, _ = sjson.DeleteBytes(body, "previous_response_id") body, _ = sjson.DeleteBytes(body, "prompt_cache_retention") body, _ = sjson.DeleteBytes(body, "safety_identifier") body = normalizeCodexInstructions(body) @@ -776,6 +775,11 @@ func buildCodexResponsesWebsocketURL(httpURL string) (string, error) { parsed.Scheme = "ws" case "https": parsed.Scheme = "wss" + default: + return "", fmt.Errorf("codex websockets executor: unsupported responses websocket URL scheme %q", parsed.Scheme) + } + if strings.TrimSpace(parsed.Host) == "" { + return "", fmt.Errorf("codex websockets executor: responses websocket URL host is empty") } return parsed.String(), nil } @@ -809,6 +813,7 @@ func applyCodexPromptCacheHeaders(from sdktranslator.Format, req cliproxyexecuto if cache.ID != "" { rawJSON, _ = sjson.SetBytes(rawJSON, "prompt_cache_key", cache.ID) + setHeaderCasePreserved(headers, "session_id", cache.ID) headers.Set("Conversation_id", cache.ID) } @@ -828,13 +833,19 @@ func applyCodexWebsocketHeaders(ctx context.Context, headers http.Header, auth * ginHeaders = ginCtx.Request.Header.Clone() } - _, cfgBetaFeatures := codexHeaderDefaults(cfg, auth) + isAPIKey := codexAuthUsesAPIKey(auth) + cfgUserAgent, cfgBetaFeatures := codexHeaderDefaults(cfg, auth) ensureHeaderWithPriority(headers, ginHeaders, "x-codex-beta-features", cfgBetaFeatures, "") misc.EnsureHeader(headers, ginHeaders, "x-codex-turn-state", "") misc.EnsureHeader(headers, ginHeaders, "x-codex-turn-metadata", "") misc.EnsureHeader(headers, ginHeaders, "x-client-request-id", "") misc.EnsureHeader(headers, ginHeaders, "x-responsesapi-include-timing-metrics", "") misc.EnsureHeader(headers, ginHeaders, "Version", "") + if isAPIKey { + ensureHeaderWithPriority(headers, ginHeaders, "User-Agent", "", "") + } else { + ensureHeaderWithConfigPrecedence(headers, ginHeaders, "User-Agent", cfgUserAgent, codexUserAgent) + } betaHeader := strings.TrimSpace(headers.Get("OpenAI-Beta")) if betaHeader == "" && ginHeaders != nil { @@ -845,16 +856,9 @@ func applyCodexWebsocketHeaders(ctx context.Context, headers http.Header, auth * } headers.Set("OpenAI-Beta", betaHeader) if strings.Contains(headers.Get("User-Agent"), "Mac OS") { - misc.EnsureHeader(headers, ginHeaders, "Session_id", uuid.NewString()) - } - headers.Del("User-Agent") - - isAPIKey := false - if auth != nil && auth.Attributes != nil { - if v := strings.TrimSpace(auth.Attributes["api_key"]); v != "" { - isAPIKey = true - } + ensureHeaderCasePreserved(headers, ginHeaders, "session_id", "", uuid.NewString()) } + ensureHeaderCasePreserved(headers, ginHeaders, "session_id", "", "") if originator := strings.TrimSpace(ginHeaders.Get("Originator")); originator != "" { headers.Set("Originator", originator) } else if !isAPIKey { @@ -864,7 +868,7 @@ func applyCodexWebsocketHeaders(ctx context.Context, headers http.Header, auth * if auth != nil && auth.Metadata != nil { if accountID, ok := auth.Metadata["account_id"].(string); ok { if trimmed := strings.TrimSpace(accountID); trimmed != "" { - headers.Set("Chatgpt-Account-Id", trimmed) + headers.Set("ChatGPT-Account-ID", trimmed) } } } @@ -879,6 +883,77 @@ func applyCodexWebsocketHeaders(ctx context.Context, headers http.Header, auth * return headers } +func codexAuthUsesAPIKey(auth *cliproxyauth.Auth) bool { + if auth == nil || auth.Attributes == nil { + return false + } + return strings.TrimSpace(auth.Attributes["api_key"]) != "" +} + +func ensureHeaderCasePreserved(target http.Header, source http.Header, key, configValue, fallbackValue string) { + if target == nil { + return + } + if strings.TrimSpace(headerValueCaseInsensitive(target, key)) != "" { + return + } + if source != nil { + if val := strings.TrimSpace(headerValueCaseInsensitive(source, key)); val != "" { + setHeaderCasePreserved(target, key, val) + return + } + } + if val := strings.TrimSpace(configValue); val != "" { + setHeaderCasePreserved(target, key, val) + return + } + if val := strings.TrimSpace(fallbackValue); val != "" { + setHeaderCasePreserved(target, key, val) + } +} + +func setHeaderCasePreserved(headers http.Header, key string, value string) { + if headers == nil { + return + } + key = strings.TrimSpace(key) + value = strings.TrimSpace(value) + if key == "" || value == "" { + return + } + deleteHeaderCaseInsensitive(headers, key) + headers[key] = []string{value} +} + +func headerValueCaseInsensitive(headers http.Header, key string) string { + key = strings.TrimSpace(key) + if headers == nil || key == "" { + return "" + } + if val := strings.TrimSpace(headers.Get(key)); val != "" { + return val + } + for existingKey, values := range headers { + if !strings.EqualFold(existingKey, key) { + continue + } + for _, value := range values { + if trimmed := strings.TrimSpace(value); trimmed != "" { + return trimmed + } + } + } + return "" +} + +func deleteHeaderCaseInsensitive(headers http.Header, key string) { + for existingKey := range headers { + if strings.EqualFold(existingKey, key) { + delete(headers, existingKey) + } + } +} + func codexHeaderDefaults(cfg *config.Config, auth *cliproxyauth.Auth) (string, string) { if cfg == nil || auth == nil { return "", "" @@ -962,25 +1037,53 @@ func parseCodexWebsocketError(payload []byte) (error, bool) { return nil, false } - out := []byte(`{}`) - if errNode := gjson.GetBytes(payload, "error"); errNode.Exists() { - raw := errNode.Raw - if errNode.Type == gjson.String { - raw = errNode.Raw - } - out, _ = sjson.SetRawBytes(out, "error", []byte(raw)) - } else { - out, _ = sjson.SetBytes(out, "error.type", "server_error") - out, _ = sjson.SetBytes(out, "error.message", http.StatusText(status)) - } - + out := buildCodexWebsocketErrorPayload(payload, status) headers := parseCodexWebsocketErrorHeaders(payload) + statusError := statusErr{code: status, msg: string(out)} + if isCodexWebsocketConnectionLimitError(payload) { + retryAfter := time.Duration(0) + statusError.retryAfter = &retryAfter + } return statusErrWithHeaders{ - statusErr: statusErr{code: status, msg: string(out)}, + statusErr: statusError, headers: headers, }, true } +func buildCodexWebsocketErrorPayload(payload []byte, status int) []byte { + out := []byte(`{}`) + out, _ = sjson.SetBytes(out, "status", status) + + if bodyNode := gjson.GetBytes(payload, "body"); bodyNode.Exists() { + out, _ = sjson.SetRawBytes(out, "body", []byte(bodyNode.Raw)) + if bodyErrorNode := bodyNode.Get("error"); bodyErrorNode.Exists() { + out, _ = sjson.SetRawBytes(out, "error", []byte(bodyErrorNode.Raw)) + return out + } + } + + if errNode := gjson.GetBytes(payload, "error"); errNode.Exists() { + out, _ = sjson.SetRawBytes(out, "error", []byte(errNode.Raw)) + return out + } + + out, _ = sjson.SetBytes(out, "error.type", "server_error") + out, _ = sjson.SetBytes(out, "error.message", http.StatusText(status)) + return out +} + +func isCodexWebsocketConnectionLimitError(payload []byte) bool { + if len(payload) == 0 { + return false + } + for _, path := range []string{"error.code", "error.type", "body.error.code", "body.error.type", "code", "error"} { + if strings.TrimSpace(gjson.GetBytes(payload, path).String()) == "websocket_connection_limit_reached" { + return true + } + } + return false +} + func parseCodexWebsocketErrorHeaders(payload []byte) http.Header { headersNode := gjson.GetBytes(payload, "headers") if !headersNode.Exists() || !headersNode.IsObject() { diff --git a/internal/runtime/executor/codex_websockets_executor_test.go b/internal/runtime/executor/codex_websockets_executor_test.go index dec356de..0b7a546e 100644 --- a/internal/runtime/executor/codex_websockets_executor_test.go +++ b/internal/runtime/executor/codex_websockets_executor_test.go @@ -1,15 +1,20 @@ package executor import ( + "bytes" "context" "net/http" "net/http/httptest" "testing" + "time" "github.com/gin-gonic/gin" + "github.com/gorilla/websocket" "github.com/router-for-me/CLIProxyAPI/v6/internal/config" cliproxyauth "github.com/router-for-me/CLIProxyAPI/v6/sdk/cliproxy/auth" + cliproxyexecutor "github.com/router-for-me/CLIProxyAPI/v6/sdk/cliproxy/executor" sdkconfig "github.com/router-for-me/CLIProxyAPI/v6/sdk/config" + sdktranslator "github.com/router-for-me/CLIProxyAPI/v6/sdk/translator" "github.com/tidwall/gjson" ) @@ -32,14 +37,71 @@ func TestBuildCodexWebsocketRequestBodyPreservesPreviousResponseID(t *testing.T) } } +func TestCodexWebsocketsExecutePreservesPreviousResponseIDUpstream(t *testing.T) { + upgrader := websocket.Upgrader{CheckOrigin: func(*http.Request) bool { return true }} + capturedPayload := make(chan []byte, 1) + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.URL.Path != "/responses" { + t.Fatalf("request path = %s, want /responses", r.URL.Path) + } + conn, err := upgrader.Upgrade(w, r, nil) + if err != nil { + t.Fatalf("upgrade websocket: %v", err) + } + defer func() { _ = conn.Close() }() + + msgType, payload, err := conn.ReadMessage() + if err != nil { + t.Fatalf("read upstream websocket message: %v", err) + } + if msgType != websocket.TextMessage { + t.Fatalf("message type = %d, want text", msgType) + } + capturedPayload <- bytes.Clone(payload) + + completed := []byte(`{"type":"response.completed","response":{"id":"resp-2","output":[],"usage":{"input_tokens":0,"output_tokens":0,"total_tokens":0}}}`) + if errWrite := conn.WriteMessage(websocket.TextMessage, completed); errWrite != nil { + t.Fatalf("write completed websocket message: %v", errWrite) + } + })) + defer server.Close() + + exec := NewCodexWebsocketsExecutor(&config.Config{SDKConfig: config.SDKConfig{DisableImageGeneration: config.DisableImageGenerationAll}}) + auth := &cliproxyauth.Auth{Attributes: map[string]string{"api_key": "sk-test", "base_url": server.URL}} + req := cliproxyexecutor.Request{ + Model: "gpt-5-codex", + Payload: []byte(`{"model":"gpt-5-codex","previous_response_id":"resp-1","input":[{"type":"message","id":"msg-1"}]}`), + } + opts := cliproxyexecutor.Options{SourceFormat: sdktranslator.FromString("codex")} + + if _, err := exec.Execute(context.Background(), auth, req, opts); err != nil { + t.Fatalf("Execute() error = %v", err) + } + + select { + case payload := <-capturedPayload: + if got := gjson.GetBytes(payload, "type").String(); got != "response.create" { + t.Fatalf("upstream type = %s, want response.create; payload=%s", got, payload) + } + if got := gjson.GetBytes(payload, "previous_response_id").String(); got != "resp-1" { + t.Fatalf("upstream previous_response_id = %s, want resp-1; payload=%s", got, payload) + } + case <-time.After(5 * time.Second): + t.Fatal("timed out waiting for upstream websocket payload") + } +} + func TestApplyCodexWebsocketHeadersDefaultsToCurrentResponsesBeta(t *testing.T) { headers := applyCodexWebsocketHeaders(context.Background(), http.Header{}, nil, "", nil) if got := headers.Get("OpenAI-Beta"); got != codexResponsesWebsocketBetaHeaderValue { t.Fatalf("OpenAI-Beta = %s, want %s", got, codexResponsesWebsocketBetaHeaderValue) } - if got := headers.Get("User-Agent"); got != "" { - t.Fatalf("User-Agent = %s, want empty", got) + if got := headers.Get("User-Agent"); got != codexUserAgent { + t.Fatalf("User-Agent = %s, want %s", got, codexUserAgent) + } + if got := headers.Get("Originator"); got != codexOriginator { + t.Fatalf("Originator = %s, want %s", got, codexOriginator) } if got := headers.Get("Version"); got != "" { t.Fatalf("Version = %q, want empty", got) @@ -62,9 +124,11 @@ func TestApplyCodexWebsocketHeadersPassesThroughClientIdentityHeaders(t *testing } ctx := contextWithGinHeaders(map[string]string{ "Originator": "Codex Desktop", + "User-Agent": "codex_cli_rs/0.1.0", "Version": "0.115.0-alpha.27", "X-Codex-Turn-Metadata": `{"turn_id":"turn-1"}`, "X-Client-Request-Id": "019d2233-e240-7162-992d-38df0a2a0e0d", + "session_id": "sess-client", }) headers := applyCodexWebsocketHeaders(ctx, http.Header{}, auth, "", nil) @@ -72,6 +136,9 @@ func TestApplyCodexWebsocketHeadersPassesThroughClientIdentityHeaders(t *testing if got := headers.Get("Originator"); got != "Codex Desktop" { t.Fatalf("Originator = %s, want %s", got, "Codex Desktop") } + if got := headers.Get("User-Agent"); got != "codex_cli_rs/0.1.0" { + t.Fatalf("User-Agent = %s, want %s", got, "codex_cli_rs/0.1.0") + } if got := headers.Get("Version"); got != "0.115.0-alpha.27" { t.Fatalf("Version = %s, want %s", got, "0.115.0-alpha.27") } @@ -81,6 +148,12 @@ func TestApplyCodexWebsocketHeadersPassesThroughClientIdentityHeaders(t *testing if got := headers.Get("X-Client-Request-Id"); got != "019d2233-e240-7162-992d-38df0a2a0e0d" { t.Fatalf("X-Client-Request-Id = %s, want %s", got, "019d2233-e240-7162-992d-38df0a2a0e0d") } + if got := headerValueCaseInsensitive(headers, "session_id"); got != "sess-client" { + t.Fatalf("session_id = %s, want sess-client", got) + } + if _, ok := headers["session_id"]; !ok { + t.Fatalf("expected lowercase session_id header key, got %#v", headers) + } } func TestApplyCodexWebsocketHeadersUsesConfigDefaultsForOAuth(t *testing.T) { @@ -97,8 +170,8 @@ func TestApplyCodexWebsocketHeadersUsesConfigDefaultsForOAuth(t *testing.T) { headers := applyCodexWebsocketHeaders(context.Background(), http.Header{}, auth, "", cfg) - if got := headers.Get("User-Agent"); got != "" { - t.Fatalf("User-Agent = %s, want empty", got) + if got := headers.Get("User-Agent"); got != "my-codex-client/1.0" { + t.Fatalf("User-Agent = %s, want %s", got, "my-codex-client/1.0") } if got := headers.Get("x-codex-beta-features"); got != "feature-a,feature-b" { t.Fatalf("x-codex-beta-features = %s, want %s", got, "feature-a,feature-b") @@ -129,8 +202,8 @@ func TestApplyCodexWebsocketHeadersPrefersExistingHeadersOverClientAndConfig(t * got := applyCodexWebsocketHeaders(ctx, headers, auth, "", cfg) - if gotVal := got.Get("User-Agent"); gotVal != "" { - t.Fatalf("User-Agent = %s, want empty", gotVal) + if gotVal := got.Get("User-Agent"); gotVal != "existing-ua" { + t.Fatalf("User-Agent = %s, want %s", gotVal, "existing-ua") } if gotVal := got.Get("x-codex-beta-features"); gotVal != "existing-beta" { t.Fatalf("x-codex-beta-features = %s, want %s", gotVal, "existing-beta") @@ -155,8 +228,8 @@ func TestApplyCodexWebsocketHeadersConfigUserAgentOverridesClientHeader(t *testi headers := applyCodexWebsocketHeaders(ctx, http.Header{}, auth, "", cfg) - if got := headers.Get("User-Agent"); got != "" { - t.Fatalf("User-Agent = %s, want empty", got) + if got := headers.Get("User-Agent"); got != "config-ua" { + t.Fatalf("User-Agent = %s, want %s", got, "config-ua") } if got := headers.Get("x-codex-beta-features"); got != "client-beta" { t.Fatalf("x-codex-beta-features = %s, want %s", got, "client-beta") @@ -183,6 +256,106 @@ func TestApplyCodexWebsocketHeadersIgnoresConfigForAPIKeyAuth(t *testing.T) { if got := headers.Get("x-codex-beta-features"); got != "" { t.Fatalf("x-codex-beta-features = %q, want empty", got) } + if got := headers.Get("Originator"); got != "" { + t.Fatalf("Originator = %s, want empty", got) + } +} + +func TestApplyCodexWebsocketHeadersPreservesExplicitAPIKeyUserAgent(t *testing.T) { + auth := &cliproxyauth.Auth{Provider: "codex", Attributes: map[string]string{"api_key": "sk-test"}} + ctx := contextWithGinHeaders(map[string]string{"User-Agent": "api-key-client/1.0", "Originator": "explicit-origin"}) + + headers := applyCodexWebsocketHeaders(ctx, http.Header{}, auth, "sk-test", nil) + + if got := headers.Get("User-Agent"); got != "api-key-client/1.0" { + t.Fatalf("User-Agent = %s, want api-key-client/1.0", got) + } + if got := headers.Get("Originator"); got != "explicit-origin" { + t.Fatalf("Originator = %s, want explicit-origin", got) + } +} + +func TestApplyCodexPromptCacheHeadersSetsLowercaseSessionAndLegacyConversation(t *testing.T) { + req := cliproxyexecutor.Request{Model: "gpt-5-codex", Payload: []byte(`{"prompt_cache_key":"cache-1"}`)} + + _, headers := applyCodexPromptCacheHeaders("openai-response", req, []byte(`{"model":"gpt-5-codex"}`)) + + if got := headerValueCaseInsensitive(headers, "session_id"); got != "cache-1" { + t.Fatalf("session_id = %s, want cache-1", got) + } + if _, ok := headers["session_id"]; !ok { + t.Fatalf("expected lowercase session_id key, got %#v", headers) + } + if got := headers.Get("Conversation_id"); got != "cache-1" { + t.Fatalf("Conversation_id = %s, want cache-1", got) + } +} + +func TestApplyCodexWebsocketHeadersUsesCanonicalAccountHeader(t *testing.T) { + auth := &cliproxyauth.Auth{Provider: "codex", Metadata: map[string]any{"account_id": "acct-1"}} + + headers := applyCodexWebsocketHeaders(context.Background(), http.Header{}, auth, "", nil) + + if got := headers.Get("ChatGPT-Account-ID"); got != "acct-1" { + t.Fatalf("ChatGPT-Account-ID = %s, want acct-1", got) + } +} + +func TestBuildCodexResponsesWebsocketURLRequiresHTTPURL(t *testing.T) { + if got, err := buildCodexResponsesWebsocketURL("https://example.com/backend/responses"); err != nil || got != "wss://example.com/backend/responses" { + t.Fatalf("https URL = %q, %v; want wss URL", got, err) + } + if _, err := buildCodexResponsesWebsocketURL("ftp://example.com/responses"); err == nil { + t.Fatalf("expected unsupported scheme error") + } + if _, err := buildCodexResponsesWebsocketURL("https:///responses"); err == nil { + t.Fatalf("expected empty host error") + } +} + +func TestParseCodexWebsocketErrorMarksConnectionLimitRetryable(t *testing.T) { + err, ok := parseCodexWebsocketError([]byte(`{"type":"error","status":429,"error":{"code":"websocket_connection_limit_reached","message":"too many websockets"},"headers":{"retry-after":"1"}}`)) + if !ok { + t.Fatalf("expected websocket error") + } + status, ok := err.(interface{ StatusCode() int }) + if !ok || status.StatusCode() != http.StatusTooManyRequests { + t.Fatalf("status = %#v, want 429", err) + } + retryable, ok := err.(interface{ RetryAfter() *time.Duration }) + if !ok || retryable.RetryAfter() == nil { + t.Fatalf("expected retryable websocket connection limit error") + } + withHeaders, ok := err.(interface{ Headers() http.Header }) + if !ok || withHeaders.Headers().Get("retry-after") != "1" { + t.Fatalf("headers = %#v, want retry-after", err) + } +} + +func TestParseCodexWebsocketErrorPreservesWrappedBodyAndHeaders(t *testing.T) { + err, ok := parseCodexWebsocketError([]byte(`{"type":"error","status":429,"body":{"error":{"code":"websocket_connection_limit_reached","type":"server_error","message":"too many websocket connections"}},"headers":{"x-request-id":"req-1"}}`)) + if !ok { + t.Fatalf("expected websocket error") + } + + parsed := gjson.Parse(err.Error()) + if got := parsed.Get("status").Int(); got != http.StatusTooManyRequests { + t.Fatalf("wrapped status = %d, want 429; payload=%s", got, err.Error()) + } + if got := parsed.Get("body.error.code").String(); got != "websocket_connection_limit_reached" { + t.Fatalf("wrapped body error code = %s, want websocket_connection_limit_reached; payload=%s", got, err.Error()) + } + if got := parsed.Get("error.code").String(); got != "websocket_connection_limit_reached" { + t.Fatalf("surface error code = %s, want websocket_connection_limit_reached; payload=%s", got, err.Error()) + } + retryable, ok := err.(interface{ RetryAfter() *time.Duration }) + if !ok || retryable.RetryAfter() == nil { + t.Fatalf("expected body.error.code websocket connection limit to be retryable") + } + withHeaders, ok := err.(interface{ Headers() http.Header }) + if !ok || withHeaders.Headers().Get("x-request-id") != "req-1" { + t.Fatalf("headers = %#v, want x-request-id", err) + } } func TestApplyCodexHeadersUsesConfigUserAgentForOAuth(t *testing.T) { -- 2.51.2 From 08b0fe6816380dc76ebe7a3f5442d8c7a0bdb661 Mon Sep 17 00:00:00 2001 From: Kenny Date: Sun, 3 May 2026 19:01:44 -0700 Subject: [PATCH 40/51] Fix Codex websocket retry metadata --- .../executor/codex_websockets_executor.go | 6 +++-- .../codex_websockets_executor_test.go | 27 ++++++++++++++++++- 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/internal/runtime/executor/codex_websockets_executor.go b/internal/runtime/executor/codex_websockets_executor.go index 87ae0efe..d6f1de86 100644 --- a/internal/runtime/executor/codex_websockets_executor.go +++ b/internal/runtime/executor/codex_websockets_executor.go @@ -868,7 +868,7 @@ func applyCodexWebsocketHeaders(ctx context.Context, headers http.Header, auth * if auth != nil && auth.Metadata != nil { if accountID, ok := auth.Metadata["account_id"].(string); ok { if trimmed := strings.TrimSpace(accountID); trimmed != "" { - headers.Set("ChatGPT-Account-ID", trimmed) + setHeaderCasePreserved(headers, "ChatGPT-Account-ID", trimmed) } } } @@ -1040,7 +1040,9 @@ func parseCodexWebsocketError(payload []byte) (error, bool) { out := buildCodexWebsocketErrorPayload(payload, status) headers := parseCodexWebsocketErrorHeaders(payload) statusError := statusErr{code: status, msg: string(out)} - if isCodexWebsocketConnectionLimitError(payload) { + if retryAfter := parseCodexRetryAfter(status, out, time.Now()); retryAfter != nil { + statusError.retryAfter = retryAfter + } else if isCodexWebsocketConnectionLimitError(payload) { retryAfter := time.Duration(0) statusError.retryAfter = &retryAfter } diff --git a/internal/runtime/executor/codex_websockets_executor_test.go b/internal/runtime/executor/codex_websockets_executor_test.go index 0b7a546e..bf12ef78 100644 --- a/internal/runtime/executor/codex_websockets_executor_test.go +++ b/internal/runtime/executor/codex_websockets_executor_test.go @@ -296,9 +296,16 @@ func TestApplyCodexWebsocketHeadersUsesCanonicalAccountHeader(t *testing.T) { headers := applyCodexWebsocketHeaders(context.Background(), http.Header{}, auth, "", nil) - if got := headers.Get("ChatGPT-Account-ID"); got != "acct-1" { + if got := headerValueCaseInsensitive(headers, "ChatGPT-Account-ID"); got != "acct-1" { t.Fatalf("ChatGPT-Account-ID = %s, want acct-1", got) } + values, ok := headers["ChatGPT-Account-ID"] + if !ok { + t.Fatalf("expected exact ChatGPT-Account-ID key, got %#v", headers) + } + if len(values) != 1 || values[0] != "acct-1" { + t.Fatalf("ChatGPT-Account-ID values = %#v, want [acct-1]", values) + } } func TestBuildCodexResponsesWebsocketURLRequiresHTTPURL(t *testing.T) { @@ -326,12 +333,30 @@ func TestParseCodexWebsocketErrorMarksConnectionLimitRetryable(t *testing.T) { if !ok || retryable.RetryAfter() == nil { t.Fatalf("expected retryable websocket connection limit error") } + if got := *retryable.RetryAfter(); got != 0 { + t.Fatalf("retryAfter = %v, want connection-limit fallback 0", got) + } withHeaders, ok := err.(interface{ Headers() http.Header }) if !ok || withHeaders.Headers().Get("retry-after") != "1" { t.Fatalf("headers = %#v, want retry-after", err) } } +func TestParseCodexWebsocketErrorUsesUsageLimitRetryMetadata(t *testing.T) { + err, ok := parseCodexWebsocketError([]byte(`{"type":"error","status":429,"body":{"error":{"type":"usage_limit_reached","message":"usage limit reached","resets_in_seconds":7}}}`)) + if !ok { + t.Fatalf("expected websocket error") + } + + retryable, ok := err.(interface{ RetryAfter() *time.Duration }) + if !ok || retryable.RetryAfter() == nil { + t.Fatalf("expected retryable usage limit websocket error") + } + if got := *retryable.RetryAfter(); got != 7*time.Second { + t.Fatalf("retryAfter = %v, want 7s", got) + } +} + func TestParseCodexWebsocketErrorPreservesWrappedBodyAndHeaders(t *testing.T) { err, ok := parseCodexWebsocketError([]byte(`{"type":"error","status":429,"body":{"error":{"code":"websocket_connection_limit_reached","type":"server_error","message":"too many websocket connections"}},"headers":{"x-request-id":"req-1"}}`)) if !ok { -- 2.51.2 From 6b4bc0a9a852d38da2e330178b7902a9f7f7db7b Mon Sep 17 00:00:00 2001 From: Kenny Date: Sun, 3 May 2026 21:13:37 -0700 Subject: [PATCH 41/51] Align Codex default identity and docs --- README.md | 2 +- README_CN.md | 2 +- README_JA.md | 2 +- internal/runtime/executor/codex_executor.go | 2 +- .../runtime/executor/codex_websockets_executor_test.go | 10 ++++++++++ 5 files changed, 14 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 2fd6937a..bcadeb87 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,7 @@ VisionCoder is also offering our users a limited-time Date: Mon, 4 May 2026 16:45:25 +0800 Subject: [PATCH 42/51] fix(executor): adjust ApplyThinking order and add payload override test - Moved `ApplyThinking` logic earlier in `openai_compat_executor` to align with configuration application sequence. - Added test to verify payload override precedence over Thinking suffix configuration. --- .../executor/openai_compat_executor.go | 18 ++++---- .../openai_compat_executor_compact_test.go | 44 +++++++++++++++++++ 2 files changed, 54 insertions(+), 8 deletions(-) diff --git a/internal/runtime/executor/openai_compat_executor.go b/internal/runtime/executor/openai_compat_executor.go index 4e44a7ae..63be2d3c 100644 --- a/internal/runtime/executor/openai_compat_executor.go +++ b/internal/runtime/executor/openai_compat_executor.go @@ -96,6 +96,12 @@ func (e *OpenAICompatExecutor) Execute(ctx context.Context, auth *cliproxyauth.A originalPayload := originalPayloadSource originalTranslated := sdktranslator.TranslateRequest(from, to, baseModel, originalPayload, opts.Stream) translated := sdktranslator.TranslateRequest(from, to, baseModel, req.Payload, opts.Stream) + + translated, err = thinking.ApplyThinking(translated, req.Model, from.String(), to.String(), e.Identifier()) + if err != nil { + return resp, err + } + requestedModel := helps.PayloadRequestedModel(opts, req.Model) requestPath := helps.PayloadRequestPath(opts) translated = helps.ApplyPayloadConfigWithRoot(e.cfg, baseModel, to.String(), "", translated, originalTranslated, requestedModel, requestPath) @@ -105,11 +111,6 @@ func (e *OpenAICompatExecutor) Execute(ctx context.Context, auth *cliproxyauth.A } } - translated, err = thinking.ApplyThinking(translated, req.Model, from.String(), to.String(), e.Identifier()) - if err != nil { - return resp, err - } - url := strings.TrimSuffix(baseURL, "/") + endpoint httpReq, err := http.NewRequestWithContext(ctx, http.MethodPost, url, bytes.NewReader(translated)) if err != nil { @@ -199,15 +200,16 @@ func (e *OpenAICompatExecutor) ExecuteStream(ctx context.Context, auth *cliproxy originalPayload := originalPayloadSource originalTranslated := sdktranslator.TranslateRequest(from, to, baseModel, originalPayload, true) translated := sdktranslator.TranslateRequest(from, to, baseModel, req.Payload, true) - requestedModel := helps.PayloadRequestedModel(opts, req.Model) - requestPath := helps.PayloadRequestPath(opts) - translated = helps.ApplyPayloadConfigWithRoot(e.cfg, baseModel, to.String(), "", translated, originalTranslated, requestedModel, requestPath) translated, err = thinking.ApplyThinking(translated, req.Model, from.String(), to.String(), e.Identifier()) if err != nil { return nil, err } + requestedModel := helps.PayloadRequestedModel(opts, req.Model) + requestPath := helps.PayloadRequestPath(opts) + translated = helps.ApplyPayloadConfigWithRoot(e.cfg, baseModel, to.String(), "", translated, originalTranslated, requestedModel, requestPath) + // Request usage data in the final streaming chunk so that token statistics // are captured even when the upstream is an OpenAI-compatible provider. translated, _ = sjson.SetBytes(translated, "stream_options.include_usage", true) diff --git a/internal/runtime/executor/openai_compat_executor_compact_test.go b/internal/runtime/executor/openai_compat_executor_compact_test.go index fe281262..ac9d9b32 100644 --- a/internal/runtime/executor/openai_compat_executor_compact_test.go +++ b/internal/runtime/executor/openai_compat_executor_compact_test.go @@ -56,3 +56,47 @@ func TestOpenAICompatExecutorCompactPassthrough(t *testing.T) { t.Fatalf("payload = %s", string(resp.Payload)) } } + +func TestOpenAICompatExecutorPayloadOverrideWinsOverThinkingSuffix(t *testing.T) { + var gotBody []byte + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + body, _ := io.ReadAll(r.Body) + gotBody = body + w.Header().Set("Content-Type", "application/json") + _, _ = w.Write([]byte(`{"id":"chatcmpl_1","object":"chat.completion","choices":[{"index":0,"message":{"role":"assistant","content":"ok"},"finish_reason":"stop"}],"usage":{"prompt_tokens":1,"completion_tokens":1,"total_tokens":2}}`)) + })) + defer server.Close() + + executor := NewOpenAICompatExecutor("openai-compatibility", &config.Config{ + Payload: config.PayloadConfig{ + Override: []config.PayloadRule{ + { + Models: []config.PayloadModelRule{ + {Name: "custom-openai", Protocol: "openai"}, + }, + Params: map[string]any{ + "reasoning_effort": "low", + }, + }, + }, + }, + }) + auth := &cliproxyauth.Auth{Attributes: map[string]string{ + "base_url": server.URL + "/v1", + "api_key": "test", + }} + payload := []byte(`{"model":"custom-openai(high)","messages":[{"role":"user","content":"hi"}]}`) + _, err := executor.Execute(context.Background(), auth, cliproxyexecutor.Request{ + Model: "custom-openai(high)", + Payload: payload, + }, cliproxyexecutor.Options{ + SourceFormat: sdktranslator.FromString("openai"), + Stream: false, + }) + if err != nil { + t.Fatalf("Execute error: %v", err) + } + if got := gjson.GetBytes(gotBody, "reasoning_effort").String(); got != "low" { + t.Fatalf("reasoning_effort = %q, want %q; body=%s", got, "low", string(gotBody)) + } +} -- 2.51.2 From 85c015065302ed17bac32b0ec05d94b59cf46eb6 Mon Sep 17 00:00:00 2001 From: Luis Pater Date: Mon, 4 May 2026 16:57:50 +0800 Subject: [PATCH 43/51] feat(translator): add token usage tracking and improve usage handling - Introduced `claudeUsageTokens` struct for detailed token usage tracking. - Replaced `calculateClaudeUsageTokens` with `Merge` and `OpenAIUsage` methods for better modularity. - Enhanced integration of usage tokens into response processing, enabling more accurate reporting of token details. Fixed: #2419 --- .../claude_openai_response.go | 58 ++++++++++++++----- .../claude_openai_response_test.go | 58 +++++++++++++++++++ 2 files changed, 103 insertions(+), 13 deletions(-) diff --git a/internal/translator/claude/openai/chat-completions/claude_openai_response.go b/internal/translator/claude/openai/chat-completions/claude_openai_response.go index 1fd3f2ae..99c75238 100644 --- a/internal/translator/claude/openai/chat-completions/claude_openai_response.go +++ b/internal/translator/claude/openai/chat-completions/claude_openai_response.go @@ -25,10 +25,19 @@ type ConvertAnthropicResponseToOpenAIParams struct { CreatedAt int64 ResponseID string FinishReason string + Usage claudeUsageTokens // Tool calls accumulator for streaming ToolCallsAccumulator map[int]*ToolCallAccumulator } +type claudeUsageTokens struct { + InputTokens int64 + OutputTokens int64 + CacheCreationInputTokens int64 + CacheReadInputTokens int64 + HasUsage bool +} + // ToolCallAccumulator holds the state for accumulating tool call data type ToolCallAccumulator struct { ID string @@ -36,15 +45,30 @@ type ToolCallAccumulator struct { Arguments strings.Builder } -func calculateClaudeUsageTokens(usage gjson.Result) (promptTokens, completionTokens, totalTokens, cachedTokens int64) { - inputTokens := usage.Get("input_tokens").Int() - completionTokens = usage.Get("output_tokens").Int() - cachedTokens = usage.Get("cache_read_input_tokens").Int() - cacheCreationInputTokens := usage.Get("cache_creation_input_tokens").Int() +func (u *claudeUsageTokens) Merge(usage gjson.Result) { + if !usage.Exists() { + return + } + u.HasUsage = true + if inputTokens := usage.Get("input_tokens"); inputTokens.Exists() { + u.InputTokens = inputTokens.Int() + } + if outputTokens := usage.Get("output_tokens"); outputTokens.Exists() { + u.OutputTokens = outputTokens.Int() + } + if cacheCreationInputTokens := usage.Get("cache_creation_input_tokens"); cacheCreationInputTokens.Exists() { + u.CacheCreationInputTokens = cacheCreationInputTokens.Int() + } + if cacheReadInputTokens := usage.Get("cache_read_input_tokens"); cacheReadInputTokens.Exists() { + u.CacheReadInputTokens = cacheReadInputTokens.Int() + } +} - promptTokens = inputTokens + cacheCreationInputTokens + cachedTokens +func (u claudeUsageTokens) OpenAIUsage() (promptTokens, completionTokens, totalTokens, cachedTokens int64) { + cachedTokens = u.CacheReadInputTokens + promptTokens = u.InputTokens + u.CacheCreationInputTokens + cachedTokens + completionTokens = u.OutputTokens totalTokens = promptTokens + completionTokens - return promptTokens, completionTokens, totalTokens, cachedTokens } @@ -112,6 +136,7 @@ func ConvertClaudeResponseToOpenAI(_ context.Context, modelName string, original if (*param).(*ConvertAnthropicResponseToOpenAIParams).ToolCallsAccumulator == nil { (*param).(*ConvertAnthropicResponseToOpenAIParams).ToolCallsAccumulator = make(map[int]*ToolCallAccumulator) } + (*param).(*ConvertAnthropicResponseToOpenAIParams).Usage.Merge(message.Get("usage")) } return [][]byte{template} @@ -215,7 +240,8 @@ func ConvertClaudeResponseToOpenAI(_ context.Context, modelName string, original // Handle usage information for token counts if usage := root.Get("usage"); usage.Exists() { - promptTokens, completionTokens, totalTokens, cachedTokens := calculateClaudeUsageTokens(usage) + (*param).(*ConvertAnthropicResponseToOpenAIParams).Usage.Merge(usage) + promptTokens, completionTokens, totalTokens, cachedTokens := (*param).(*ConvertAnthropicResponseToOpenAIParams).Usage.OpenAIUsage() template, _ = sjson.SetBytes(template, "usage.prompt_tokens", promptTokens) template, _ = sjson.SetBytes(template, "usage.completion_tokens", completionTokens) template, _ = sjson.SetBytes(template, "usage.total_tokens", totalTokens) @@ -296,6 +322,7 @@ func ConvertClaudeResponseToOpenAINonStream(_ context.Context, _ string, origina var stopReason string var contentParts []string var reasoningParts []string + usageTokens := claudeUsageTokens{} toolCallsAccumulator := make(map[int]*ToolCallAccumulator) for _, chunk := range chunks { @@ -309,6 +336,7 @@ func ConvertClaudeResponseToOpenAINonStream(_ context.Context, _ string, origina messageID = message.Get("id").String() model = message.Get("model").String() createdAt = time.Now().Unix() + usageTokens.Merge(message.Get("usage")) } case "content_block_start": @@ -371,15 +399,19 @@ func ConvertClaudeResponseToOpenAINonStream(_ context.Context, _ string, origina } } if usage := root.Get("usage"); usage.Exists() { - promptTokens, completionTokens, totalTokens, cachedTokens := calculateClaudeUsageTokens(usage) - out, _ = sjson.SetBytes(out, "usage.prompt_tokens", promptTokens) - out, _ = sjson.SetBytes(out, "usage.completion_tokens", completionTokens) - out, _ = sjson.SetBytes(out, "usage.total_tokens", totalTokens) - out, _ = sjson.SetBytes(out, "usage.prompt_tokens_details.cached_tokens", cachedTokens) + usageTokens.Merge(usage) } } } + if usageTokens.HasUsage { + promptTokens, completionTokens, totalTokens, cachedTokens := usageTokens.OpenAIUsage() + out, _ = sjson.SetBytes(out, "usage.prompt_tokens", promptTokens) + out, _ = sjson.SetBytes(out, "usage.completion_tokens", completionTokens) + out, _ = sjson.SetBytes(out, "usage.total_tokens", totalTokens) + out, _ = sjson.SetBytes(out, "usage.prompt_tokens_details.cached_tokens", cachedTokens) + } + // Set basic response fields including message ID, creation time, and model out, _ = sjson.SetBytes(out, "id", messageID) out, _ = sjson.SetBytes(out, "created", createdAt) diff --git a/internal/translator/claude/openai/chat-completions/claude_openai_response_test.go b/internal/translator/claude/openai/chat-completions/claude_openai_response_test.go index 7bd6eb1f..5a9a6d3a 100644 --- a/internal/translator/claude/openai/chat-completions/claude_openai_response_test.go +++ b/internal/translator/claude/openai/chat-completions/claude_openai_response_test.go @@ -37,6 +37,44 @@ func TestConvertClaudeResponseToOpenAI_StreamUsageIncludesCachedTokens(t *testin } } +func TestConvertClaudeResponseToOpenAI_StreamUsageMergesMessageStartUsage(t *testing.T) { + ctx := context.Background() + var param any + + ConvertClaudeResponseToOpenAI( + ctx, + "claude-opus-4-6", + nil, + nil, + []byte(`data: {"type":"message_start","message":{"id":"msg_123","model":"claude-opus-4-6","usage":{"input_tokens":13,"output_tokens":1,"cache_read_input_tokens":22000,"cache_creation_input_tokens":31}}}`), + ¶m, + ) + out := ConvertClaudeResponseToOpenAI( + ctx, + "claude-opus-4-6", + nil, + nil, + []byte(`data: {"type":"message_delta","delta":{"stop_reason":"end_turn"},"usage":{"output_tokens":4}}`), + ¶m, + ) + if len(out) != 1 { + t.Fatalf("expected 1 chunk, got %d", len(out)) + } + + if gotPromptTokens := gjson.GetBytes(out[0], "usage.prompt_tokens").Int(); gotPromptTokens != 22044 { + t.Fatalf("expected prompt_tokens %d, got %d", 22044, gotPromptTokens) + } + if gotCompletionTokens := gjson.GetBytes(out[0], "usage.completion_tokens").Int(); gotCompletionTokens != 4 { + t.Fatalf("expected completion_tokens %d, got %d", 4, gotCompletionTokens) + } + if gotTotalTokens := gjson.GetBytes(out[0], "usage.total_tokens").Int(); gotTotalTokens != 22048 { + t.Fatalf("expected total_tokens %d, got %d", 22048, gotTotalTokens) + } + if gotCachedTokens := gjson.GetBytes(out[0], "usage.prompt_tokens_details.cached_tokens").Int(); gotCachedTokens != 22000 { + t.Fatalf("expected cached_tokens %d, got %d", 22000, gotCachedTokens) + } +} + func TestConvertClaudeResponseToOpenAINonStream_UsageIncludesCachedTokens(t *testing.T) { rawJSON := []byte("data: {\"type\":\"message_start\",\"message\":{\"id\":\"msg_123\",\"model\":\"claude-opus-4-6\"}}\n" + "data: {\"type\":\"message_delta\",\"delta\":{\"stop_reason\":\"end_turn\"},\"usage\":{\"input_tokens\":13,\"output_tokens\":4,\"cache_read_input_tokens\":22000,\"cache_creation_input_tokens\":31}}\n") @@ -56,3 +94,23 @@ func TestConvertClaudeResponseToOpenAINonStream_UsageIncludesCachedTokens(t *tes t.Fatalf("expected cached_tokens %d, got %d", 22000, gotCachedTokens) } } + +func TestConvertClaudeResponseToOpenAINonStream_UsageMergesMessageStartUsage(t *testing.T) { + rawJSON := []byte("data: {\"type\":\"message_start\",\"message\":{\"id\":\"msg_123\",\"model\":\"claude-opus-4-6\",\"usage\":{\"input_tokens\":13,\"output_tokens\":1,\"cache_read_input_tokens\":22000,\"cache_creation_input_tokens\":31}}}\n" + + "data: {\"type\":\"message_delta\",\"delta\":{\"stop_reason\":\"end_turn\"},\"usage\":{\"output_tokens\":4}}\n") + + out := ConvertClaudeResponseToOpenAINonStream(context.Background(), "", nil, nil, rawJSON, nil) + + if gotPromptTokens := gjson.GetBytes(out, "usage.prompt_tokens").Int(); gotPromptTokens != 22044 { + t.Fatalf("expected prompt_tokens %d, got %d", 22044, gotPromptTokens) + } + if gotCompletionTokens := gjson.GetBytes(out, "usage.completion_tokens").Int(); gotCompletionTokens != 4 { + t.Fatalf("expected completion_tokens %d, got %d", 4, gotCompletionTokens) + } + if gotTotalTokens := gjson.GetBytes(out, "usage.total_tokens").Int(); gotTotalTokens != 22048 { + t.Fatalf("expected total_tokens %d, got %d", 22048, gotTotalTokens) + } + if gotCachedTokens := gjson.GetBytes(out, "usage.prompt_tokens_details.cached_tokens").Int(); gotCachedTokens != 22000 { + t.Fatalf("expected cached_tokens %d, got %d", 22000, gotCachedTokens) + } +} -- 2.51.2 From bf6fa402e203a1048f065ee8fc8f3e821f528b7a Mon Sep 17 00:00:00 2001 From: Luis Pater Date: Mon, 4 May 2026 17:54:16 +0800 Subject: [PATCH 44/51] fix(executor): strip Vertex OpenAI response tool call IDs for consistency - Integrated `StripVertexOpenAIResponsesToolCallIDs` to remove tool call ID data from request bodies and translated requests. - Ensures uniformity and avoids unnecessary payload data propagation. Fixed: #2549 --- .../executor/gemini_vertex_executor.go | 6 +++ .../executor/helps/vertex_payload_helpers.go | 43 +++++++++++++++++++ 2 files changed, 49 insertions(+) create mode 100644 internal/runtime/executor/helps/vertex_payload_helpers.go diff --git a/internal/runtime/executor/gemini_vertex_executor.go b/internal/runtime/executor/gemini_vertex_executor.go index b147fde9..84a84b3d 100644 --- a/internal/runtime/executor/gemini_vertex_executor.go +++ b/internal/runtime/executor/gemini_vertex_executor.go @@ -338,6 +338,7 @@ func (e *GeminiVertexExecutor) executeWithServiceAccount(ctx context.Context, au requestPath := helps.PayloadRequestPath(opts) body = helps.ApplyPayloadConfigWithRoot(e.cfg, baseModel, to.String(), "", body, originalTranslated, requestedModel, requestPath) body, _ = sjson.SetBytes(body, "model", baseModel) + body = helps.StripVertexOpenAIResponsesToolCallIDs(body, from.String()) } action := getVertexAction(baseModel, false) @@ -459,6 +460,7 @@ func (e *GeminiVertexExecutor) executeWithAPIKey(ctx context.Context, auth *clip requestPath := helps.PayloadRequestPath(opts) body = helps.ApplyPayloadConfigWithRoot(e.cfg, baseModel, to.String(), "", body, originalTranslated, requestedModel, requestPath) body, _ = sjson.SetBytes(body, "model", baseModel) + body = helps.StripVertexOpenAIResponsesToolCallIDs(body, from.String()) action := getVertexAction(baseModel, false) if req.Metadata != nil { @@ -570,6 +572,7 @@ func (e *GeminiVertexExecutor) executeStreamWithServiceAccount(ctx context.Conte requestPath := helps.PayloadRequestPath(opts) body = helps.ApplyPayloadConfigWithRoot(e.cfg, baseModel, to.String(), "", body, originalTranslated, requestedModel, requestPath) body, _ = sjson.SetBytes(body, "model", baseModel) + body = helps.StripVertexOpenAIResponsesToolCallIDs(body, from.String()) action := getVertexAction(baseModel, true) baseURL := vertexBaseURL(location) @@ -700,6 +703,7 @@ func (e *GeminiVertexExecutor) executeStreamWithAPIKey(ctx context.Context, auth requestPath := helps.PayloadRequestPath(opts) body = helps.ApplyPayloadConfigWithRoot(e.cfg, baseModel, to.String(), "", body, originalTranslated, requestedModel, requestPath) body, _ = sjson.SetBytes(body, "model", baseModel) + body = helps.StripVertexOpenAIResponsesToolCallIDs(body, from.String()) action := getVertexAction(baseModel, true) // For API key auth, use simpler URL format without project/location @@ -818,6 +822,7 @@ func (e *GeminiVertexExecutor) countTokensWithServiceAccount(ctx context.Context translatedReq = fixGeminiImageAspectRatio(baseModel, translatedReq) translatedReq, _ = sjson.SetBytes(translatedReq, "model", baseModel) + translatedReq = helps.StripVertexOpenAIResponsesToolCallIDs(translatedReq, from.String()) respCtx := context.WithValue(ctx, "alt", opts.Alt) translatedReq, _ = sjson.DeleteBytes(translatedReq, "tools") translatedReq, _ = sjson.DeleteBytes(translatedReq, "generationConfig") @@ -907,6 +912,7 @@ func (e *GeminiVertexExecutor) countTokensWithAPIKey(ctx context.Context, auth * translatedReq = fixGeminiImageAspectRatio(baseModel, translatedReq) translatedReq, _ = sjson.SetBytes(translatedReq, "model", baseModel) + translatedReq = helps.StripVertexOpenAIResponsesToolCallIDs(translatedReq, from.String()) respCtx := context.WithValue(ctx, "alt", opts.Alt) translatedReq, _ = sjson.DeleteBytes(translatedReq, "tools") translatedReq, _ = sjson.DeleteBytes(translatedReq, "generationConfig") diff --git a/internal/runtime/executor/helps/vertex_payload_helpers.go b/internal/runtime/executor/helps/vertex_payload_helpers.go new file mode 100644 index 00000000..4c84fae4 --- /dev/null +++ b/internal/runtime/executor/helps/vertex_payload_helpers.go @@ -0,0 +1,43 @@ +package helps + +import ( + "fmt" + "strings" + + "github.com/tidwall/gjson" + "github.com/tidwall/sjson" +) + +// StripVertexOpenAIResponsesToolCallIDs removes OpenAI Responses call IDs that +// Vertex rejects in Gemini functionCall/functionResponse payloads. +func StripVertexOpenAIResponsesToolCallIDs(payload []byte, sourceFormat string) []byte { + if !strings.EqualFold(strings.TrimSpace(sourceFormat), "openai-response") { + return payload + } + + contents := gjson.GetBytes(payload, "contents") + if !contents.IsArray() { + return payload + } + + out := payload + for contentIndex, content := range contents.Array() { + parts := content.Get("parts") + if !parts.IsArray() { + continue + } + for partIndex, part := range parts.Array() { + if part.Get("functionCall.id").Exists() { + if updated, errDelete := sjson.DeleteBytes(out, fmt.Sprintf("contents.%d.parts.%d.functionCall.id", contentIndex, partIndex)); errDelete == nil { + out = updated + } + } + if part.Get("functionResponse.id").Exists() { + if updated, errDelete := sjson.DeleteBytes(out, fmt.Sprintf("contents.%d.parts.%d.functionResponse.id", contentIndex, partIndex)); errDelete == nil { + out = updated + } + } + } + } + return out +} -- 2.51.2 From c1caa454b35e9990fe93e67ff3111d69d154d84c Mon Sep 17 00:00:00 2001 From: Luis Pater Date: Mon, 4 May 2026 21:00:33 +0800 Subject: [PATCH 45/51] fix(translator): handle empty tool function names in OpenAI Claude responses - Added check to prevent processing of empty `function.name` values, ensuring valid data is handled. Fixed: #2557 --- .../openai/claude/openai_claude_response.go | 2 +- .../claude/openai_claude_response_test.go | 41 +++++++++++++++++++ 2 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 internal/translator/openai/claude/openai_claude_response_test.go diff --git a/internal/translator/openai/claude/openai_claude_response.go b/internal/translator/openai/claude/openai_claude_response.go index 46c75898..af49d306 100644 --- a/internal/translator/openai/claude/openai_claude_response.go +++ b/internal/translator/openai/claude/openai_claude_response.go @@ -236,7 +236,7 @@ func convertOpenAIStreamingChunkToAnthropic(rawJSON []byte, param *ConvertOpenAI // Handle function name if function := toolCall.Get("function"); function.Exists() { - if name := function.Get("name"); name.Exists() { + if name := function.Get("name"); name.Exists() && name.String() != "" { accumulator.Name = util.MapToolName(param.ToolNameMap, name.String()) stopThinkingContentBlock(param, &results) diff --git a/internal/translator/openai/claude/openai_claude_response_test.go b/internal/translator/openai/claude/openai_claude_response_test.go new file mode 100644 index 00000000..8c36fc3d --- /dev/null +++ b/internal/translator/openai/claude/openai_claude_response_test.go @@ -0,0 +1,41 @@ +package claude + +import ( + "bytes" + "context" + "testing" +) + +func TestConvertOpenAIResponseToClaude_StreamIgnoresNullToolNameDelta(t *testing.T) { + originalRequest := []byte(`{"stream":true}`) + var param any + + firstChunks := ConvertOpenAIResponseToClaude( + context.Background(), + "test-model", + originalRequest, + nil, + []byte(`data: {"id":"chatcmpl_1","model":"test-model","created":1,"choices":[{"index":0,"delta":{"role":"assistant","tool_calls":[{"index":0,"id":"call_1","type":"function","function":{"name":"read_file","arguments":""}}]},"finish_reason":null}]}`), + ¶m, + ) + firstOutput := bytes.Join(firstChunks, nil) + if !bytes.Contains(firstOutput, []byte(`"name":"read_file"`)) { + t.Fatalf("expected first chunk to start read_file tool block, got %s", string(firstOutput)) + } + + secondChunks := ConvertOpenAIResponseToClaude( + context.Background(), + "test-model", + originalRequest, + nil, + []byte(`data: {"id":"chatcmpl_1","model":"test-model","created":1,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"name":null,"arguments":"{\"path\":\"/tmp/a\"}"}}]},"finish_reason":null}]}`), + ¶m, + ) + secondOutput := bytes.Join(secondChunks, nil) + if bytes.Contains(secondOutput, []byte(`content_block_start`)) { + t.Fatalf("did not expect null tool name delta to start a new content block, got %s", string(secondOutput)) + } + if bytes.Contains(secondOutput, []byte(`"name":""`)) { + t.Fatalf("did not expect null tool name delta to emit an empty tool name, got %s", string(secondOutput)) + } +} -- 2.51.2 From ecf1c2590c1b4772e0e0d4d0f0e602d811f15024 Mon Sep 17 00:00:00 2001 From: Luis Pater Date: Mon, 4 May 2026 21:18:18 +0800 Subject: [PATCH 46/51] fix: preserve Antigravity cancellation errors --- internal/runtime/executor/antigravity_executor.go | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/internal/runtime/executor/antigravity_executor.go b/internal/runtime/executor/antigravity_executor.go index c07680e8..418ed7b1 100644 --- a/internal/runtime/executor/antigravity_executor.go +++ b/internal/runtime/executor/antigravity_executor.go @@ -894,19 +894,12 @@ attemptLoop: reporter.Publish(ctx, detail) } - select { - case out <- cliproxyexecutor.StreamChunk{Payload: payload}: - case <-ctx.Done(): - return - } + out <- cliproxyexecutor.StreamChunk{Payload: payload} } if errScan := scanner.Err(); errScan != nil { helps.RecordAPIResponseError(ctx, e.cfg, errScan) reporter.PublishFailure(ctx) - select { - case out <- cliproxyexecutor.StreamChunk{Err: errScan}: - case <-ctx.Done(): - } + out <- cliproxyexecutor.StreamChunk{Err: errScan} } else { reporter.EnsurePublished(ctx) } -- 2.51.2 From e4a93c02c584108b981d65691600fc87012b86ca Mon Sep 17 00:00:00 2001 From: Luis Pater Date: Mon, 4 May 2026 23:42:26 +0800 Subject: [PATCH 47/51] fix(executor): enhance parsing of OpenAI stream data lines - Added trimming for stream input lines to prevent processing of unnecessary whitespace. - Improved handling of unsupported prefixes and malformed JSON responses, ensuring errors are recorded and propagated appropriately. Fixed: #2690 --- .../executor/openai_compat_executor.go | 24 ++++-- .../openai_compat_executor_compact_test.go | 79 +++++++++++++++++++ 2 files changed, 98 insertions(+), 5 deletions(-) diff --git a/internal/runtime/executor/openai_compat_executor.go b/internal/runtime/executor/openai_compat_executor.go index e0a7bd88..7e81637c 100644 --- a/internal/runtime/executor/openai_compat_executor.go +++ b/internal/runtime/executor/openai_compat_executor.go @@ -283,17 +283,31 @@ func (e *OpenAICompatExecutor) ExecuteStream(ctx context.Context, auth *cliproxy if detail, ok := helps.ParseOpenAIStreamUsage(line); ok { reporter.Publish(ctx, detail) } - if len(line) == 0 { + trimmedLine := bytes.TrimSpace(line) + if len(trimmedLine) == 0 { continue } - if !bytes.HasPrefix(line, []byte("data:")) { + if !bytes.HasPrefix(trimmedLine, []byte("data:")) { + if bytes.HasPrefix(trimmedLine, []byte(":")) || bytes.HasPrefix(trimmedLine, []byte("event:")) || + bytes.HasPrefix(trimmedLine, []byte("id:")) || bytes.HasPrefix(trimmedLine, []byte("retry:")) { + continue + } + if bytes.HasPrefix(trimmedLine, []byte("{")) || bytes.HasPrefix(trimmedLine, []byte("[")) { + streamErr := statusErr{code: http.StatusBadGateway, msg: string(trimmedLine)} + helps.RecordAPIResponseError(ctx, e.cfg, streamErr) + reporter.PublishFailure(ctx) + select { + case out <- cliproxyexecutor.StreamChunk{Err: streamErr}: + case <-ctx.Done(): + } + return + } continue } - // OpenAI-compatible streams are SSE: lines typically prefixed with "data: ". - // Pass through translator; it yields one or more chunks for the target schema. - chunks := sdktranslator.TranslateStream(ctx, to, from, req.Model, opts.OriginalRequest, translated, bytes.Clone(line), ¶m) + // OpenAI-compatible streams must use SSE data lines. + chunks := sdktranslator.TranslateStream(ctx, to, from, req.Model, opts.OriginalRequest, translated, bytes.Clone(trimmedLine), ¶m) for i := range chunks { select { case out <- cliproxyexecutor.StreamChunk{Payload: chunks[i]}: diff --git a/internal/runtime/executor/openai_compat_executor_compact_test.go b/internal/runtime/executor/openai_compat_executor_compact_test.go index ac9d9b32..49b2cccb 100644 --- a/internal/runtime/executor/openai_compat_executor_compact_test.go +++ b/internal/runtime/executor/openai_compat_executor_compact_test.go @@ -5,6 +5,7 @@ import ( "io" "net/http" "net/http/httptest" + "strings" "testing" "github.com/router-for-me/CLIProxyAPI/v6/internal/config" @@ -100,3 +101,81 @@ func TestOpenAICompatExecutorPayloadOverrideWinsOverThinkingSuffix(t *testing.T) t.Fatalf("reasoning_effort = %q, want %q; body=%s", got, "low", string(gotBody)) } } + +func TestOpenAICompatExecutorStreamRejectsPlainJSONAfterBlankLines(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "text/event-stream") + _, _ = w.Write([]byte("\n\n: openrouter processing\n\nevent: error\n")) + _, _ = w.Write([]byte(`{"error":{"message":"upstream failed","type":"server_error"}}` + "\n")) + })) + defer server.Close() + + executor := NewOpenAICompatExecutor("openai-compatibility", &config.Config{}) + auth := &cliproxyauth.Auth{Attributes: map[string]string{ + "base_url": server.URL + "/v1", + "api_key": "test", + }} + result, err := executor.ExecuteStream(context.Background(), auth, cliproxyexecutor.Request{ + Model: "openrouter-model", + Payload: []byte(`{"model":"openrouter-model","messages":[{"role":"user","content":"hi"}],"stream":true}`), + }, cliproxyexecutor.Options{ + SourceFormat: sdktranslator.FromString("openai"), + Stream: true, + }) + if err != nil { + t.Fatalf("ExecuteStream error: %v", err) + } + + var gotErr error + for chunk := range result.Chunks { + if chunk.Err != nil { + gotErr = chunk.Err + break + } + } + if gotErr == nil { + t.Fatalf("expected plain JSON stream error") + } + if status, ok := gotErr.(interface{ StatusCode() int }); !ok || status.StatusCode() != http.StatusBadGateway { + t.Fatalf("stream error status = %v, want %d", gotErr, http.StatusBadGateway) + } + if !strings.Contains(gotErr.Error(), "upstream failed") { + t.Fatalf("stream error = %v", gotErr) + } +} + +func TestOpenAICompatExecutorStreamSkipsKeepAliveUntilDataLine(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "text/event-stream") + _, _ = w.Write([]byte("\n\n: openrouter processing\n\nevent: ping\nid: 1\nretry: 1000\n")) + _, _ = w.Write([]byte(`data: {"id":"chatcmpl_1","object":"chat.completion.chunk","choices":[{"index":0,"delta":{"content":"hello"},"finish_reason":null}]}` + "\n")) + })) + defer server.Close() + + executor := NewOpenAICompatExecutor("openai-compatibility", &config.Config{}) + auth := &cliproxyauth.Auth{Attributes: map[string]string{ + "base_url": server.URL + "/v1", + "api_key": "test", + }} + result, err := executor.ExecuteStream(context.Background(), auth, cliproxyexecutor.Request{ + Model: "openrouter-model", + Payload: []byte(`{"model":"openrouter-model","messages":[{"role":"user","content":"hi"}],"stream":true}`), + }, cliproxyexecutor.Options{ + SourceFormat: sdktranslator.FromString("openai"), + Stream: true, + }) + if err != nil { + t.Fatalf("ExecuteStream error: %v", err) + } + + var got strings.Builder + for chunk := range result.Chunks { + if chunk.Err != nil { + t.Fatalf("unexpected stream error: %v", chunk.Err) + } + got.Write(chunk.Payload) + } + if gjson.Get(got.String(), "choices.0.delta.content").String() != "hello" { + t.Fatalf("stream payload = %s", got.String()) + } +} -- 2.51.2 From ba5d8ca7336e78ca2b7c6134638a4054b589c330 Mon Sep 17 00:00:00 2001 From: Luis Pater Date: Tue, 5 May 2026 01:47:53 +0800 Subject: [PATCH 48/51] feat(usage): add support for requested model alias handling - Introduced methods for setting and retrieving model aliases in execution and usage contexts. - Enhanced `UsageReporter` and related structures to include client-requested aliases. - Updated tests to validate alias propagation and ensure correct usage reporting. - Adjusted metadata handling in CLIProxyAPI executors to address alias integration. --- internal/redisqueue/plugin.go | 6 ++++ internal/redisqueue/plugin_test.go | 6 ++++ .../runtime/executor/helps/usage_helpers.go | 7 ++++ .../executor/helps/usage_helpers_test.go | 14 ++++++++ sdk/api/handlers/handlers.go | 6 ++-- sdk/cliproxy/auth/conductor.go | 35 +++++++++++++++++++ .../conductor_oauth_alias_suspension_test.go | 25 +++++++++++-- sdk/cliproxy/usage/manager.go | 32 +++++++++++++++++ 8 files changed, 125 insertions(+), 6 deletions(-) diff --git a/internal/redisqueue/plugin.go b/internal/redisqueue/plugin.go index 97168419..b33bc8fd 100644 --- a/internal/redisqueue/plugin.go +++ b/internal/redisqueue/plugin.go @@ -33,6 +33,10 @@ func (p *usageQueuePlugin) HandleUsage(ctx context.Context, record coreusage.Rec if modelName == "" { modelName = "unknown" } + aliasName := strings.TrimSpace(record.Alias) + if aliasName == "" { + aliasName = modelName + } provider := strings.TrimSpace(record.Provider) if provider == "" { provider = "unknown" @@ -76,6 +80,7 @@ func (p *usageQueuePlugin) HandleUsage(ctx context.Context, record coreusage.Rec requestDetail: detail, Provider: provider, Model: modelName, + Alias: aliasName, Endpoint: resolveEndpoint(ctx), AuthType: authType, APIKey: apiKey, @@ -91,6 +96,7 @@ type queuedUsageDetail struct { requestDetail Provider string `json:"provider"` Model string `json:"model"` + Alias string `json:"alias"` Endpoint string `json:"endpoint"` AuthType string `json:"auth_type"` APIKey string `json:"api_key"` diff --git a/internal/redisqueue/plugin_test.go b/internal/redisqueue/plugin_test.go index 0cc8b9b9..8dcade90 100644 --- a/internal/redisqueue/plugin_test.go +++ b/internal/redisqueue/plugin_test.go @@ -24,6 +24,7 @@ func TestUsageQueuePluginPayloadIncludesStableFieldsAndSuccess(t *testing.T) { plugin.HandleUsage(ctx, coreusage.Record{ Provider: "openai", Model: "gpt-5.4", + Alias: "client-gpt", APIKey: "test-key", AuthIndex: "0", AuthType: "apikey", @@ -40,6 +41,7 @@ func TestUsageQueuePluginPayloadIncludesStableFieldsAndSuccess(t *testing.T) { payload := popSinglePayload(t) requireStringField(t, payload, "provider", "openai") requireStringField(t, payload, "model", "gpt-5.4") + requireStringField(t, payload, "alias", "client-gpt") requireStringField(t, payload, "endpoint", "POST /v1/chat/completions") requireStringField(t, payload, "auth_type", "apikey") requireStringField(t, payload, "request_id", "ctx-request-id") @@ -58,6 +60,7 @@ func TestUsageQueuePluginPayloadIncludesStableFieldsAndFailureAndGinRequestID(t plugin.HandleUsage(ctx, coreusage.Record{ Provider: "openai", Model: "gpt-5.4-mini", + Alias: "client-mini", APIKey: "test-key", AuthIndex: "0", AuthType: "apikey", @@ -74,6 +77,7 @@ func TestUsageQueuePluginPayloadIncludesStableFieldsAndFailureAndGinRequestID(t payload := popSinglePayload(t) requireStringField(t, payload, "provider", "openai") requireStringField(t, payload, "model", "gpt-5.4-mini") + requireStringField(t, payload, "alias", "client-mini") requireStringField(t, payload, "endpoint", "GET /v1/responses") requireStringField(t, payload, "auth_type", "apikey") requireStringField(t, payload, "request_id", "gin-request-id") @@ -102,6 +106,7 @@ func TestUsageQueuePluginAsyncIgnoresRecycledGinContext(t *testing.T) { mgr.Publish(ctx, coreusage.Record{ Provider: "openai", Model: "gpt-5.4", + Alias: "client-gpt", APIKey: "test-key", AuthIndex: "0", AuthType: "apikey", @@ -117,6 +122,7 @@ func TestUsageQueuePluginAsyncIgnoresRecycledGinContext(t *testing.T) { payload := waitForSinglePayload(t, 2*time.Second) requireStringField(t, payload, "endpoint", "POST /v1/chat/completions") + requireStringField(t, payload, "alias", "client-gpt") requireStringField(t, payload, "request_id", "ctx-request-id") requireBoolField(t, payload, "failed", true) }) diff --git a/internal/runtime/executor/helps/usage_helpers.go b/internal/runtime/executor/helps/usage_helpers.go index c5e258c8..312a1d35 100644 --- a/internal/runtime/executor/helps/usage_helpers.go +++ b/internal/runtime/executor/helps/usage_helpers.go @@ -18,6 +18,7 @@ import ( type UsageReporter struct { provider string model string + alias string authID string authIndex string authType string @@ -29,9 +30,14 @@ type UsageReporter struct { func NewUsageReporter(ctx context.Context, provider, model string, auth *cliproxyauth.Auth) *UsageReporter { apiKey := APIKeyFromContext(ctx) + alias := usage.RequestedModelAliasFromContext(ctx) + if alias == "" { + alias = model + } reporter := &UsageReporter{ provider: provider, model: model, + alias: strings.TrimSpace(alias), requestedAt: time.Now(), apiKey: apiKey, source: resolveUsageSource(auth, apiKey), @@ -139,6 +145,7 @@ func (r *UsageReporter) buildRecordForModel(model string, detail usage.Detail, f return usage.Record{ Provider: r.provider, Model: model, + Alias: r.alias, Source: r.source, APIKey: r.apiKey, AuthID: r.authID, diff --git a/internal/runtime/executor/helps/usage_helpers_test.go b/internal/runtime/executor/helps/usage_helpers_test.go index c77335fd..ef2c7de5 100644 --- a/internal/runtime/executor/helps/usage_helpers_test.go +++ b/internal/runtime/executor/helps/usage_helpers_test.go @@ -1,6 +1,7 @@ package helps import ( + "context" "testing" "time" @@ -107,6 +108,19 @@ func TestUsageReporterBuildRecordIncludesLatency(t *testing.T) { } } +func TestUsageReporterBuildRecordIncludesRequestedModelAlias(t *testing.T) { + ctx := usage.WithRequestedModelAlias(context.Background(), "client-gpt") + reporter := NewUsageReporter(ctx, "openai", "gpt-5.4", nil) + + record := reporter.buildRecord(usage.Detail{TotalTokens: 3}, false) + if record.Model != "gpt-5.4" { + t.Fatalf("model = %q, want %q", record.Model, "gpt-5.4") + } + if record.Alias != "client-gpt" { + t.Fatalf("alias = %q, want %q", record.Alias, "client-gpt") + } +} + func TestUsageReporterBuildAdditionalModelRecordSkipsZeroTokens(t *testing.T) { reporter := &UsageReporter{ provider: "codex", diff --git a/sdk/api/handlers/handlers.go b/sdk/api/handlers/handlers.go index 52b2a4fd..e89227aa 100644 --- a/sdk/api/handlers/handlers.go +++ b/sdk/api/handlers/handlers.go @@ -539,7 +539,7 @@ func (h *BaseAPIHandler) ExecuteWithAuthManager(ctx context.Context, handlerType return nil, nil, errMsg } reqMeta := requestExecutionMetadata(ctx) - reqMeta[coreexecutor.RequestedModelMetadataKey] = normalizedModel + reqMeta[coreexecutor.RequestedModelMetadataKey] = modelName payload := rawJSON if len(payload) == 0 { payload = nil @@ -587,7 +587,7 @@ func (h *BaseAPIHandler) ExecuteCountWithAuthManager(ctx context.Context, handle return nil, nil, errMsg } reqMeta := requestExecutionMetadata(ctx) - reqMeta[coreexecutor.RequestedModelMetadataKey] = normalizedModel + reqMeta[coreexecutor.RequestedModelMetadataKey] = modelName payload := rawJSON if len(payload) == 0 { payload = nil @@ -639,7 +639,7 @@ func (h *BaseAPIHandler) ExecuteStreamWithAuthManager(ctx context.Context, handl return nil, nil, errChan } reqMeta := requestExecutionMetadata(ctx) - reqMeta[coreexecutor.RequestedModelMetadataKey] = normalizedModel + reqMeta[coreexecutor.RequestedModelMetadataKey] = modelName payload := rawJSON if len(payload) == 0 { payload = nil diff --git a/sdk/cliproxy/auth/conductor.go b/sdk/cliproxy/auth/conductor.go index d2a3db18..ab3eca49 100644 --- a/sdk/cliproxy/auth/conductor.go +++ b/sdk/cliproxy/auth/conductor.go @@ -22,6 +22,7 @@ import ( "github.com/router-for-me/CLIProxyAPI/v6/internal/thinking" "github.com/router-for-me/CLIProxyAPI/v6/internal/util" cliproxyexecutor "github.com/router-for-me/CLIProxyAPI/v6/sdk/cliproxy/executor" + coreusage "github.com/router-for-me/CLIProxyAPI/v6/sdk/cliproxy/usage" log "github.com/sirupsen/logrus" ) @@ -827,6 +828,7 @@ func (m *Manager) executeStreamWithModelPool(ctx context.Context, executor Provi if executor == nil { return nil, &Error{Code: "executor_not_found", Message: "executor not registered"} } + ctx = contextWithRequestedModelAlias(ctx, opts, routeModel) var lastErr error for idx, execModel := range execModels { resultModel := m.stateModelForExecution(auth, routeModel, execModel, pooled) @@ -1319,6 +1321,7 @@ func (m *Manager) executeMixedOnce(ctx context.Context, providers []string, req execCtx = context.WithValue(execCtx, roundTripperContextKey{}, rt) execCtx = context.WithValue(execCtx, "cliproxy.roundtripper", rt) } + execCtx = contextWithRequestedModelAlias(execCtx, opts, routeModel) models, pooled := m.preparedExecutionModels(auth, routeModel) if len(models) == 0 { @@ -1397,6 +1400,7 @@ func (m *Manager) executeCountMixedOnce(ctx context.Context, providers []string, execCtx = context.WithValue(execCtx, roundTripperContextKey{}, rt) execCtx = context.WithValue(execCtx, "cliproxy.roundtripper", rt) } + execCtx = contextWithRequestedModelAlias(execCtx, opts, routeModel) models, pooled := m.preparedExecutionModels(auth, routeModel) if len(models) == 0 { @@ -1534,6 +1538,36 @@ func hasRequestedModelMetadata(meta map[string]any) bool { } } +func contextWithRequestedModelAlias(ctx context.Context, opts cliproxyexecutor.Options, fallback string) context.Context { + alias := requestedModelAliasFromOptions(opts, fallback) + return coreusage.WithRequestedModelAlias(ctx, alias) +} + +func requestedModelAliasFromOptions(opts cliproxyexecutor.Options, fallback string) string { + fallback = strings.TrimSpace(fallback) + if len(opts.Metadata) == 0 { + return fallback + } + raw, ok := opts.Metadata[cliproxyexecutor.RequestedModelMetadataKey] + if !ok || raw == nil { + return fallback + } + switch value := raw.(type) { + case string: + if strings.TrimSpace(value) == "" { + return fallback + } + return strings.TrimSpace(value) + case []byte: + if len(value) == 0 { + return fallback + } + return strings.TrimSpace(string(value)) + default: + return fallback + } +} + func pinnedAuthIDFromMetadata(meta map[string]any) string { if len(meta) == 0 { return "" @@ -3096,6 +3130,7 @@ func (m *Manager) tryAntigravityCreditsExecute(ctx context.Context, req cliproxy creditsCtx = context.WithValue(creditsCtx, "cliproxy.roundtripper", rt) } creditsOpts := ensureRequestedModelMetadata(opts, routeModel) + creditsCtx = contextWithRequestedModelAlias(creditsCtx, creditsOpts, routeModel) publishSelectedAuthMetadata(creditsOpts.Metadata, c.auth.ID) models := m.executionModelCandidates(c.auth, routeModel) if len(models) == 0 { diff --git a/sdk/cliproxy/auth/conductor_oauth_alias_suspension_test.go b/sdk/cliproxy/auth/conductor_oauth_alias_suspension_test.go index 8bc779e5..b4b72204 100644 --- a/sdk/cliproxy/auth/conductor_oauth_alias_suspension_test.go +++ b/sdk/cliproxy/auth/conductor_oauth_alias_suspension_test.go @@ -10,20 +10,23 @@ import ( internalconfig "github.com/router-for-me/CLIProxyAPI/v6/internal/config" "github.com/router-for-me/CLIProxyAPI/v6/internal/registry" cliproxyexecutor "github.com/router-for-me/CLIProxyAPI/v6/sdk/cliproxy/executor" + coreusage "github.com/router-for-me/CLIProxyAPI/v6/sdk/cliproxy/usage" ) type aliasRoutingExecutor struct { id string - mu sync.Mutex - executeModels []string + mu sync.Mutex + executeModels []string + executeAliases []string } func (e *aliasRoutingExecutor) Identifier() string { return e.id } -func (e *aliasRoutingExecutor) Execute(_ context.Context, _ *Auth, req cliproxyexecutor.Request, _ cliproxyexecutor.Options) (cliproxyexecutor.Response, error) { +func (e *aliasRoutingExecutor) Execute(ctx context.Context, _ *Auth, req cliproxyexecutor.Request, _ cliproxyexecutor.Options) (cliproxyexecutor.Response, error) { e.mu.Lock() e.executeModels = append(e.executeModels, req.Model) + e.executeAliases = append(e.executeAliases, coreusage.RequestedModelAliasFromContext(ctx)) e.mu.Unlock() return cliproxyexecutor.Response{Payload: []byte(req.Model)}, nil } @@ -52,6 +55,14 @@ func (e *aliasRoutingExecutor) ExecuteModels() []string { return out } +func (e *aliasRoutingExecutor) ExecuteAliases() []string { + e.mu.Lock() + defer e.mu.Unlock() + out := make([]string, len(e.executeAliases)) + copy(out, e.executeAliases) + return out +} + func TestManagerExecute_OAuthAliasBypassesBlockedRouteModel(t *testing.T) { const ( provider = "antigravity" @@ -108,4 +119,12 @@ func TestManagerExecute_OAuthAliasBypassesBlockedRouteModel(t *testing.T) { if gotModels[0] != targetModel { t.Fatalf("execute model = %q, want %q", gotModels[0], targetModel) } + + gotAliases := executor.ExecuteAliases() + if len(gotAliases) != 1 { + t.Fatalf("execute aliases len = %d, want 1", len(gotAliases)) + } + if gotAliases[0] != routeModel { + t.Fatalf("execute alias = %q, want %q", gotAliases[0], routeModel) + } } diff --git a/sdk/cliproxy/usage/manager.go b/sdk/cliproxy/usage/manager.go index c3d95f66..72405d75 100644 --- a/sdk/cliproxy/usage/manager.go +++ b/sdk/cliproxy/usage/manager.go @@ -2,6 +2,7 @@ package usage import ( "context" + "strings" "sync" "time" @@ -12,6 +13,7 @@ import ( type Record struct { Provider string Model string + Alias string APIKey string AuthID string AuthIndex string @@ -32,6 +34,36 @@ type Detail struct { TotalTokens int64 } +type requestedModelAliasContextKey struct{} + +// WithRequestedModelAlias stores the client-requested model name for usage sinks. +func WithRequestedModelAlias(ctx context.Context, alias string) context.Context { + if ctx == nil { + ctx = context.Background() + } + alias = strings.TrimSpace(alias) + if alias == "" { + return ctx + } + return context.WithValue(ctx, requestedModelAliasContextKey{}, alias) +} + +// RequestedModelAliasFromContext returns the client-requested model name stored in ctx. +func RequestedModelAliasFromContext(ctx context.Context) string { + if ctx == nil { + return "" + } + raw := ctx.Value(requestedModelAliasContextKey{}) + switch value := raw.(type) { + case string: + return strings.TrimSpace(value) + case []byte: + return strings.TrimSpace(string(value)) + default: + return "" + } +} + // Plugin consumes usage records emitted by the proxy runtime. type Plugin interface { HandleUsage(ctx context.Context, record Record) -- 2.51.2 From 61b39d49bd8cad26c8d74eb0bd0f6b8fda16ab2c Mon Sep 17 00:00:00 2001 From: Luis Pater Date: Tue, 5 May 2026 02:53:04 +0800 Subject: [PATCH 49/51] feat(management): add usage record retrieval endpoint - Implemented `/v0/management/usage` endpoint for fetching queued usage records from Redis. - Included validation for `count` parameter to ensure positive integers. - Added unit tests for queue retrieval and validation, with authentication validation in integration tests. - Updated management routing to include the new endpoint. --- internal/api/handlers/management/usage.go | 55 +++++++++++ .../api/handlers/management/usage_test.go | 98 +++++++++++++++++++ internal/api/server.go | 1 + internal/api/server_test.go | 55 +++++++++++ 4 files changed, 209 insertions(+) create mode 100644 internal/api/handlers/management/usage.go create mode 100644 internal/api/handlers/management/usage_test.go diff --git a/internal/api/handlers/management/usage.go b/internal/api/handlers/management/usage.go new file mode 100644 index 00000000..8cb175eb --- /dev/null +++ b/internal/api/handlers/management/usage.go @@ -0,0 +1,55 @@ +package management + +import ( + "encoding/json" + "errors" + "net/http" + "strconv" + "strings" + + "github.com/gin-gonic/gin" + "github.com/router-for-me/CLIProxyAPI/v6/internal/redisqueue" +) + +type usageQueueRecord []byte + +func (r usageQueueRecord) MarshalJSON() ([]byte, error) { + if json.Valid(r) { + return append([]byte(nil), r...), nil + } + return json.Marshal(string(r)) +} + +// GetUsage pops queued usage records from the Redis-compatible usage queue. +func (h *Handler) GetUsage(c *gin.Context) { + if h == nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": "handler unavailable"}) + return + } + + count, errCount := parseUsageQueueCount(c.Query("count")) + if errCount != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": errCount.Error()}) + return + } + + items := redisqueue.PopOldest(count) + records := make([]usageQueueRecord, 0, len(items)) + for _, item := range items { + records = append(records, usageQueueRecord(append([]byte(nil), item...))) + } + + c.JSON(http.StatusOK, records) +} + +func parseUsageQueueCount(value string) (int, error) { + value = strings.TrimSpace(value) + if value == "" { + return 1, nil + } + count, errCount := strconv.Atoi(value) + if errCount != nil || count <= 0 { + return 0, errors.New("count must be a positive integer") + } + return count, nil +} diff --git a/internal/api/handlers/management/usage_test.go b/internal/api/handlers/management/usage_test.go new file mode 100644 index 00000000..5c5f5c69 --- /dev/null +++ b/internal/api/handlers/management/usage_test.go @@ -0,0 +1,98 @@ +package management + +import ( + "encoding/json" + "net/http" + "net/http/httptest" + "testing" + + "github.com/gin-gonic/gin" + "github.com/router-for-me/CLIProxyAPI/v6/internal/redisqueue" +) + +func TestGetUsagePopsRequestedRecords(t *testing.T) { + gin.SetMode(gin.TestMode) + withManagementUsageQueue(t, func() { + redisqueue.Enqueue([]byte(`{"id":1}`)) + redisqueue.Enqueue([]byte(`{"id":2}`)) + redisqueue.Enqueue([]byte(`{"id":3}`)) + + rec := httptest.NewRecorder() + ginCtx, _ := gin.CreateTestContext(rec) + ginCtx.Request = httptest.NewRequest(http.MethodGet, "/v0/management/usage?count=2", nil) + + h := &Handler{} + h.GetUsage(ginCtx) + + if rec.Code != http.StatusOK { + t.Fatalf("status = %d, want %d body=%s", rec.Code, http.StatusOK, rec.Body.String()) + } + + var payload []json.RawMessage + if errUnmarshal := json.Unmarshal(rec.Body.Bytes(), &payload); errUnmarshal != nil { + t.Fatalf("unmarshal response: %v", errUnmarshal) + } + if len(payload) != 2 { + t.Fatalf("response records = %d, want 2", len(payload)) + } + requireRecordID(t, payload[0], 1) + requireRecordID(t, payload[1], 2) + + remaining := redisqueue.PopOldest(10) + if len(remaining) != 1 || string(remaining[0]) != `{"id":3}` { + t.Fatalf("remaining queue = %q, want third item only", remaining) + } + }) +} + +func TestGetUsageInvalidCountDoesNotPop(t *testing.T) { + gin.SetMode(gin.TestMode) + withManagementUsageQueue(t, func() { + redisqueue.Enqueue([]byte(`{"id":1}`)) + + rec := httptest.NewRecorder() + ginCtx, _ := gin.CreateTestContext(rec) + ginCtx.Request = httptest.NewRequest(http.MethodGet, "/v0/management/usage?count=0", nil) + + h := &Handler{} + h.GetUsage(ginCtx) + + if rec.Code != http.StatusBadRequest { + t.Fatalf("status = %d, want %d body=%s", rec.Code, http.StatusBadRequest, rec.Body.String()) + } + + remaining := redisqueue.PopOldest(10) + if len(remaining) != 1 || string(remaining[0]) != `{"id":1}` { + t.Fatalf("remaining queue = %q, want original item", remaining) + } + }) +} + +func withManagementUsageQueue(t *testing.T, fn func()) { + t.Helper() + + prevQueueEnabled := redisqueue.Enabled() + redisqueue.SetEnabled(false) + redisqueue.SetEnabled(true) + + defer func() { + redisqueue.SetEnabled(false) + redisqueue.SetEnabled(prevQueueEnabled) + }() + + fn() +} + +func requireRecordID(t *testing.T, raw json.RawMessage, want int) { + t.Helper() + + var payload struct { + ID int `json:"id"` + } + if errUnmarshal := json.Unmarshal(raw, &payload); errUnmarshal != nil { + t.Fatalf("unmarshal record: %v", errUnmarshal) + } + if payload.ID != want { + t.Fatalf("record id = %d, want %d", payload.ID, want) + } +} diff --git a/internal/api/server.go b/internal/api/server.go index 2e89ac5a..5c43db48 100644 --- a/internal/api/server.go +++ b/internal/api/server.go @@ -551,6 +551,7 @@ func (s *Server) registerManagementRoutes() { mgmt.PATCH("/api-keys", s.mgmt.PatchAPIKeys) mgmt.DELETE("/api-keys", s.mgmt.DeleteAPIKeys) mgmt.GET("/api-key-usage", s.mgmt.GetAPIKeyUsage) + mgmt.GET("/usage", s.mgmt.GetUsage) mgmt.GET("/gemini-api-key", s.mgmt.GetGeminiKeys) mgmt.PUT("/gemini-api-key", s.mgmt.PutGeminiKeys) diff --git a/internal/api/server_test.go b/internal/api/server_test.go index db1ef27d..d5718091 100644 --- a/internal/api/server_test.go +++ b/internal/api/server_test.go @@ -13,6 +13,7 @@ import ( gin "github.com/gin-gonic/gin" proxyconfig "github.com/router-for-me/CLIProxyAPI/v6/internal/config" internallogging "github.com/router-for-me/CLIProxyAPI/v6/internal/logging" + "github.com/router-for-me/CLIProxyAPI/v6/internal/redisqueue" sdkaccess "github.com/router-for-me/CLIProxyAPI/v6/sdk/access" "github.com/router-for-me/CLIProxyAPI/v6/sdk/cliproxy/auth" sdkconfig "github.com/router-for-me/CLIProxyAPI/v6/sdk/config" @@ -84,6 +85,60 @@ func TestHealthz(t *testing.T) { }) } +func TestManagementUsageRequiresManagementAuthAndPopsArray(t *testing.T) { + t.Setenv("MANAGEMENT_PASSWORD", "test-management-key") + + prevQueueEnabled := redisqueue.Enabled() + redisqueue.SetEnabled(false) + t.Cleanup(func() { + redisqueue.SetEnabled(false) + redisqueue.SetEnabled(prevQueueEnabled) + }) + + server := newTestServer(t) + + redisqueue.Enqueue([]byte(`{"id":1}`)) + redisqueue.Enqueue([]byte(`{"id":2}`)) + + missingKeyReq := httptest.NewRequest(http.MethodGet, "/v0/management/usage?count=2", nil) + missingKeyRR := httptest.NewRecorder() + server.engine.ServeHTTP(missingKeyRR, missingKeyReq) + if missingKeyRR.Code != http.StatusUnauthorized { + t.Fatalf("missing key status = %d, want %d body=%s", missingKeyRR.Code, http.StatusUnauthorized, missingKeyRR.Body.String()) + } + + authReq := httptest.NewRequest(http.MethodGet, "/v0/management/usage?count=2", nil) + authReq.Header.Set("Authorization", "Bearer test-management-key") + authRR := httptest.NewRecorder() + server.engine.ServeHTTP(authRR, authReq) + if authRR.Code != http.StatusOK { + t.Fatalf("authenticated status = %d, want %d body=%s", authRR.Code, http.StatusOK, authRR.Body.String()) + } + + var payload []json.RawMessage + if errUnmarshal := json.Unmarshal(authRR.Body.Bytes(), &payload); errUnmarshal != nil { + t.Fatalf("unmarshal response: %v body=%s", errUnmarshal, authRR.Body.String()) + } + if len(payload) != 2 { + t.Fatalf("response records = %d, want 2", len(payload)) + } + for i, raw := range payload { + var record struct { + ID int `json:"id"` + } + if errUnmarshal := json.Unmarshal(raw, &record); errUnmarshal != nil { + t.Fatalf("unmarshal record %d: %v", i, errUnmarshal) + } + if record.ID != i+1 { + t.Fatalf("record %d id = %d, want %d", i, record.ID, i+1) + } + } + + if remaining := redisqueue.PopOldest(1); len(remaining) != 0 { + t.Fatalf("remaining queue = %q, want empty", remaining) + } +} + func TestAmpProviderModelRoutes(t *testing.T) { testCases := []struct { name string -- 2.51.2 From da6c599efd8da34d23d3668371fbb5ac70399e9d Mon Sep 17 00:00:00 2001 From: Luis Pater Date: Tue, 5 May 2026 03:02:25 +0800 Subject: [PATCH 50/51] refactor(management): rename `GetUsage` to `GetUsageQueue` and update routes/tests - Renamed handler and test methods for better clarity on functionality. - Updated route from `/v0/management/usage` to `/v0/management/usage-queue`. - Adjusted integration and unit tests to reflect new naming and routes. --- internal/api/handlers/management/usage.go | 4 ++-- internal/api/handlers/management/usage_test.go | 12 ++++++------ internal/api/server.go | 2 +- internal/api/server_test.go | 12 ++++++++++-- 4 files changed, 19 insertions(+), 11 deletions(-) diff --git a/internal/api/handlers/management/usage.go b/internal/api/handlers/management/usage.go index 8cb175eb..dfddf503 100644 --- a/internal/api/handlers/management/usage.go +++ b/internal/api/handlers/management/usage.go @@ -20,8 +20,8 @@ func (r usageQueueRecord) MarshalJSON() ([]byte, error) { return json.Marshal(string(r)) } -// GetUsage pops queued usage records from the Redis-compatible usage queue. -func (h *Handler) GetUsage(c *gin.Context) { +// GetUsageQueue pops queued usage records from the usage queue. +func (h *Handler) GetUsageQueue(c *gin.Context) { if h == nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "handler unavailable"}) return diff --git a/internal/api/handlers/management/usage_test.go b/internal/api/handlers/management/usage_test.go index 5c5f5c69..ca46d976 100644 --- a/internal/api/handlers/management/usage_test.go +++ b/internal/api/handlers/management/usage_test.go @@ -10,7 +10,7 @@ import ( "github.com/router-for-me/CLIProxyAPI/v6/internal/redisqueue" ) -func TestGetUsagePopsRequestedRecords(t *testing.T) { +func TestGetUsageQueuePopsRequestedRecords(t *testing.T) { gin.SetMode(gin.TestMode) withManagementUsageQueue(t, func() { redisqueue.Enqueue([]byte(`{"id":1}`)) @@ -19,10 +19,10 @@ func TestGetUsagePopsRequestedRecords(t *testing.T) { rec := httptest.NewRecorder() ginCtx, _ := gin.CreateTestContext(rec) - ginCtx.Request = httptest.NewRequest(http.MethodGet, "/v0/management/usage?count=2", nil) + ginCtx.Request = httptest.NewRequest(http.MethodGet, "/v0/management/usage-queue?count=2", nil) h := &Handler{} - h.GetUsage(ginCtx) + h.GetUsageQueue(ginCtx) if rec.Code != http.StatusOK { t.Fatalf("status = %d, want %d body=%s", rec.Code, http.StatusOK, rec.Body.String()) @@ -45,17 +45,17 @@ func TestGetUsagePopsRequestedRecords(t *testing.T) { }) } -func TestGetUsageInvalidCountDoesNotPop(t *testing.T) { +func TestGetUsageQueueInvalidCountDoesNotPop(t *testing.T) { gin.SetMode(gin.TestMode) withManagementUsageQueue(t, func() { redisqueue.Enqueue([]byte(`{"id":1}`)) rec := httptest.NewRecorder() ginCtx, _ := gin.CreateTestContext(rec) - ginCtx.Request = httptest.NewRequest(http.MethodGet, "/v0/management/usage?count=0", nil) + ginCtx.Request = httptest.NewRequest(http.MethodGet, "/v0/management/usage-queue?count=0", nil) h := &Handler{} - h.GetUsage(ginCtx) + h.GetUsageQueue(ginCtx) if rec.Code != http.StatusBadRequest { t.Fatalf("status = %d, want %d body=%s", rec.Code, http.StatusBadRequest, rec.Body.String()) diff --git a/internal/api/server.go b/internal/api/server.go index 5c43db48..487ea571 100644 --- a/internal/api/server.go +++ b/internal/api/server.go @@ -551,7 +551,7 @@ func (s *Server) registerManagementRoutes() { mgmt.PATCH("/api-keys", s.mgmt.PatchAPIKeys) mgmt.DELETE("/api-keys", s.mgmt.DeleteAPIKeys) mgmt.GET("/api-key-usage", s.mgmt.GetAPIKeyUsage) - mgmt.GET("/usage", s.mgmt.GetUsage) + mgmt.GET("/usage-queue", s.mgmt.GetUsageQueue) mgmt.GET("/gemini-api-key", s.mgmt.GetGeminiKeys) mgmt.PUT("/gemini-api-key", s.mgmt.PutGeminiKeys) diff --git a/internal/api/server_test.go b/internal/api/server_test.go index d5718091..fe37cb72 100644 --- a/internal/api/server_test.go +++ b/internal/api/server_test.go @@ -100,14 +100,22 @@ func TestManagementUsageRequiresManagementAuthAndPopsArray(t *testing.T) { redisqueue.Enqueue([]byte(`{"id":1}`)) redisqueue.Enqueue([]byte(`{"id":2}`)) - missingKeyReq := httptest.NewRequest(http.MethodGet, "/v0/management/usage?count=2", nil) + missingKeyReq := httptest.NewRequest(http.MethodGet, "/v0/management/usage-queue?count=2", nil) missingKeyRR := httptest.NewRecorder() server.engine.ServeHTTP(missingKeyRR, missingKeyReq) if missingKeyRR.Code != http.StatusUnauthorized { t.Fatalf("missing key status = %d, want %d body=%s", missingKeyRR.Code, http.StatusUnauthorized, missingKeyRR.Body.String()) } - authReq := httptest.NewRequest(http.MethodGet, "/v0/management/usage?count=2", nil) + legacyReq := httptest.NewRequest(http.MethodGet, "/v0/management/usage?count=2", nil) + legacyReq.Header.Set("Authorization", "Bearer test-management-key") + legacyRR := httptest.NewRecorder() + server.engine.ServeHTTP(legacyRR, legacyReq) + if legacyRR.Code != http.StatusNotFound { + t.Fatalf("legacy usage status = %d, want %d body=%s", legacyRR.Code, http.StatusNotFound, legacyRR.Body.String()) + } + + authReq := httptest.NewRequest(http.MethodGet, "/v0/management/usage-queue?count=2", nil) authReq.Header.Set("Authorization", "Bearer test-management-key") authRR := httptest.NewRecorder() server.engine.ServeHTTP(authRR, authReq) -- 2.51.2 From ed1458aa6d3430ba59538aeb980b8934f0e80c1f Mon Sep 17 00:00:00 2001 From: Luis Pater Date: Wed, 6 May 2026 00:41:50 +0800 Subject: [PATCH 51/51] chore(docs): update sponsor details in README - Replaced sponsor `z.ai` with `PackyCode` and updated related descriptions, images, and links in `README.md`, `README_CN.md`, and `README_JA.md`. - Removed outdated sponsor entries for `Poixe AI` in all README files. - Added new image assets for PackyCode (`packycode-cn.png` and `packycode-en.png`). --- README.md | 16 ++++------------ README_CN.md | 16 ++++------------ README_JA.md | 16 ++++------------ assets/packycode-cn.png | Bin 0 -> 173559 bytes assets/packycode-en.png | Bin 0 -> 410370 bytes 5 files changed, 12 insertions(+), 36 deletions(-) create mode 100644 assets/packycode-cn.png create mode 100644 assets/packycode-en.png diff --git a/README.md b/README.md index bcadeb87..b1ddb9c0 100644 --- a/README.md +++ b/README.md @@ -10,23 +10,19 @@ So you can use local or multi-account CLI access with OpenAI(include Responses)/ ## Sponsor -[![z.ai](https://assets.router-for.me/english-5-0.jpg)](https://z.ai/subscribe?ic=8JVLJQFSKB) +[![https://www.packyapi.com/register?aff=cliproxyapi](./assets/packycode-en.png)](https://www.packyapi.com/register?aff=cliproxyapi) -This project is sponsored by Z.ai, supporting us with their GLM CODING PLAN. +Thanks to PackyCode for sponsoring this project! -GLM CODING PLAN is a subscription service designed for AI coding, starting at just $10/month. It provides access to their flagship GLM-4.7 & (GLM-5 Only Available for Pro Users)model across 10+ popular AI coding tools (Claude Code, Cline, Roo Code, etc.), offering developers top-tier, fast, and stable coding experiences. +PackyCode is a reliable and efficient API relay service provider, offering relay services for Claude Code, Codex, Gemini, and more. -Get 10% OFF GLM CODING PLAN:https://z.ai/subscribe?ic=8JVLJQFSKB +PackyCode provides special discounts for our software users: register using this link and enter the "cliproxyapi" promo code during recharge to get 10% off. --- - - - - @@ -35,10 +31,6 @@ Get 10% OFF GLM CODING PLAN:https://z.ai/subscribe?ic=8JVLJQFSKB - - - -
PackyCodeThanks to PackyCode for sponsoring this project! PackyCode is a reliable and efficient API relay service provider, offering relay services for Claude Code, Codex, Gemini, and more. PackyCode provides special discounts for our software users: register using this link and enter the "cliproxyapi" promo code during recharge to get 10% off.
AICodeMirror Thanks to AICodeMirror for sponsoring this project! AICodeMirror provides official high-stability relay services for Claude Code / Codex / Gemini CLI, with enterprise-grade concurrency, fast invoicing, and 24/7 dedicated technical support. Claude Code / Codex / Gemini official channels at 38% / 2% / 9% of original price, with extra discounts on top-ups! AICodeMirror offers special benefits for CLIProxyAPI users: register via this link to enjoy 20% off your first top-up, and enterprise customers can get up to 25% off!
Huge thanks to BmoPlus for sponsoring this project! BmoPlus is a highly reliable AI account provider built strictly for heavy AI users and developers. They offer rock-solid, ready-to-use accounts and official top-up services for ChatGPT Plus / ChatGPT Pro (Full Warranty) / Claude Pro / Super Grok / Gemini Pro. By registering and ordering through BmoPlus - Premium AI Accounts & Top-ups, users can unlock the mind-blowing rate of 10% of the official GPT subscription price (90% OFF)!
PoixeAIThanks to Poixe AI for sponsoring this project! Poixe AI provides reliable LLM API services. You can leverage the platform's API endpoints to seamlessly build AI-powered products. Additionally, you can become a vendor by providing AI API resources to the platform and earn revenue. Register through the exclusive CLIProxyAPI referral link and receive a bonus of $5 USD on your first top-up.
VisionCoder Thanks to VisionCoder for supporting this project. VisionCoder Developer Platform is a reliable and efficient API relay service provider, offering access to mainstream AI models such as Claude Code, Codex, and Gemini. It helps developers and teams integrate AI capabilities more easily and improve productivity.

diff --git a/README_CN.md b/README_CN.md index 26602584..e7fa7878 100644 --- a/README_CN.md +++ b/README_CN.md @@ -10,23 +10,19 @@ ## 赞助商 -[![bigmodel.cn](https://assets.router-for.me/chinese-5-0.jpg)](https://www.bigmodel.cn/claude-code?ic=RRVJPB5SII) +[![https://www.packyapi.com/register?aff=cliproxyapi](./assets/packycode-cn.png)](https://www.packyapi.com/register?aff=cliproxyapi) -本项目由 Z智谱 提供赞助, 他们通过 GLM CODING PLAN 对本项目提供技术支持。 +感谢 PackyCode 对本项目的赞助! -GLM CODING PLAN 是专为AI编码打造的订阅套餐,每月最低仅需20元,即可在十余款主流AI编码工具如 Claude Code、Cline、Roo Code 中畅享智谱旗舰模型GLM-4.7(受限于算力,目前仅限Pro用户开放),为开发者提供顶尖的编码体验。 +PackyCode 是一家可靠高效的 API 中转服务商,提供 Claude Code、Codex、Gemini 等多种服务的中转。 -智谱AI为本产品提供了特别优惠,使用以下链接购买可以享受九折优惠:https://www.bigmodel.cn/claude-code?ic=RRVJPB5SII +PackyCode 为本软件用户提供了特别优惠:使用此链接注册,并在充值时输入 "cliproxyapi" 优惠码即可享受九折优惠。 --- - - - - @@ -35,10 +31,6 @@ GLM CODING PLAN 是专为AI编码打造的订阅套餐,每月最低仅需20元 - - - -
PackyCode感谢 PackyCode 对本项目的赞助!PackyCode 是一家可靠高效的 API 中转服务商,提供 Claude Code、Codex、Gemini 等多种服务的中转。PackyCode 为本软件用户提供了特别优惠:使用此链接注册,并在充值时输入 "cliproxyapi" 优惠码即可享受九折优惠。
AICodeMirror 感谢 AICodeMirror 赞助了本项目!AICodeMirror 提供 Claude Code / Codex / Gemini CLI 官方高稳定中转服务,支持企业级高并发、极速开票、7×24 专属技术支持。 Claude Code / Codex / Gemini 官方渠道低至 3.8 / 0.2 / 0.9 折,充值更有折上折!AICodeMirror 为 CLIProxyAPI 的用户提供了特别福利,通过此链接注册的用户,可享受首充8折,企业客户最高可享 7.5 折!
感谢 BmoPlus 赞助了本项目!BmoPlus 是一家专为AI订阅重度用户打造的可靠 AI 账号代充服务商,提供稳定的 ChatGPT Plus / ChatGPT Pro(全程质保) / Claude Pro / Super Grok / Gemini Pro 的官方代充&成品账号。 通过BmoPlus AI成品号专卖/代充注册下单的用户,可享GPT 官网订阅一折 的震撼价格!
PoixeAI感谢 Poixe AI 对本项目的赞助!Poixe AI 提供可靠的 AI 模型接口服务,您可以使用平台提供的 LLM API 接口轻松构建 AI 产品,同时也可以成为供应商,为平台提供大模型资源以赚取收益。通过 CLIProxyAPI 专属链接注册,充值额外赠送 $5 美金
VisionCoder 感谢 VisionCoder 对本项目的支持。VisionCoder 开发平台 是一个可靠高效的 API 中继服务提供商,提供 Claude Code、Codex、Gemini 等主流 AI 模型,帮助开发者和团队更轻松地集成 AI 功能,提升工作效率。

diff --git a/README_JA.md b/README_JA.md index a1eaf1bd..debe4ae5 100644 --- a/README_JA.md +++ b/README_JA.md @@ -10,23 +10,19 @@ OAuth経由でOpenAI Codex(GPTモデル)およびClaude Codeもサポート ## スポンサー -[![z.ai](https://assets.router-for.me/english-5-0.jpg)](https://z.ai/subscribe?ic=8JVLJQFSKB) +[![https://www.packyapi.com/register?aff=cliproxyapi](./assets/packycode-en.png)](https://www.packyapi.com/register?aff=cliproxyapi) -本プロジェクトはZ.aiにスポンサーされており、GLM CODING PLANの提供を受けています。 +PackyCodeのスポンサーシップに感謝します! -GLM CODING PLANはAIコーディング向けに設計されたサブスクリプションサービスで、月額わずか$10から利用可能です。フラッグシップのGLM-4.7および(GLM-5はProユーザーのみ利用可能)モデルを10以上の人気AIコーディングツール(Claude Code、Cline、Roo Codeなど)で利用でき、開発者にトップクラスの高速かつ安定したコーディング体験を提供します。 +PackyCodeは信頼性が高く効率的なAPIリレーサービスプロバイダーで、Claude Code、Codex、Geminiなどのリレーサービスを提供しています。 -GLM CODING PLANを10%割引で取得:https://z.ai/subscribe?ic=8JVLJQFSKB +PackyCodeは当ソフトウェアのユーザーに特別割引を提供しています:こちらのリンクから登録し、チャージ時にプロモーションコード「cliproxyapi」を入力すると10%割引になります。 --- - - - - @@ -35,10 +31,6 @@ GLM CODING PLANを10%割引で取得:https://z.ai/subscribe?ic=8JVLJQFSKB - - - - diff --git a/assets/packycode-cn.png b/assets/packycode-cn.png new file mode 100644 index 0000000000000000000000000000000000000000..3e34d6caed0c2e4b8c8ae19a7f71253c91fd63c3 GIT binary patch literal 173559 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

})M!=cBd0EsV_p^1ug9dxf1#G30 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 z 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±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�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�(o3u+CL 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

PackyCodePackyCodeのスポンサーシップに感謝します!PackyCodeは信頼性が高く効率的なAPIリレーサービスプロバイダーで、Claude Code、Codex、Geminiなどのリレーサービスを提供しています。PackyCodeは当ソフトウェアのユーザーに特別割引を提供しています:こちらのリンクから登録し、チャージ時にプロモーションコード「cliproxyapi」を入力すると10%割引になります。
AICodeMirror AICodeMirrorのスポンサーシップに感謝します!AICodeMirrorはClaude Code / Codex / Gemini CLI向けの公式高安定性リレーサービスを提供しており、エンタープライズグレードの同時接続、迅速な請求書発行、24時間365日の専任技術サポートを備えています。Claude Code / Codex / Geminiの公式チャネルが元の価格の38% / 2% / 9%で利用でき、チャージ時にはさらに割引があります!CLIProxyAPIユーザー向けの特別特典:こちらのリンクから登録すると、初回チャージが20%割引になり、エンタープライズのお客様は最大25%割引を受けられます!
本プロジェクトにご支援いただいた BmoPlus に感謝いたします!BmoPlusは、AIサブスクリプションのヘビーユーザー向けに特化した信頼性の高いAIアカウントサービスプロバイダーであり、安定した ChatGPT Plus / ChatGPT Pro (完全保証) / Claude Pro / Super Grok / Gemini Pro の公式代行チャージおよび即納アカウントを提供しています。こちらのBmoPlus AIアカウント専門店/代行チャージ経由でご登録・ご注文いただいたユーザー様は、GPTを 公式サイト価格の約1割(90% OFF) という驚異的な価格でご利用いただけます!
PoixeAIPoixe AIのスポンサーシップに感謝します!Poixe AIは信頼できるAIモデルAPIサービスを提供しており、プラットフォームが提供するLLM APIを使って簡単にAI製品を構築できます。また、サプライヤーとしてプラットフォームに大規模モデルのリソースを提供し、収益を得ることも可能です。CLIProxyAPIの専用リンクから登録すると、チャージ時に追加で$5が付与されます。
VisionCoder VisionCoderのご支援に感謝します!VisionCoder 開発プラットフォーム は、信頼性が高く効率的なAPIリレーサービスプロバイダーで、Claude Code、Codex、Geminiなどの主要AIモデルを提供し、開発者やチームがより簡単にAI機能を統合して生産性を向上できるよう支援します。さらに、VisionCoderはユーザー向けに Token Plan の期間限定キャンペーン(1か月購入で1か月分プレゼント)も提供しています。