Skip to content

ci: sync branch feat/clean-base-1698-strip-empty-messages-rebase#517

Merged
KooshaPari merged 15 commits intomainfrom
feat/clean-base-1698-strip-empty-messages-rebase
Feb 27, 2026
Merged

ci: sync branch feat/clean-base-1698-strip-empty-messages-rebase#517
KooshaPari merged 15 commits intomainfrom
feat/clean-base-1698-strip-empty-messages-rebase

Conversation

@KooshaPari
Copy link
Owner

@KooshaPari KooshaPari commented Feb 26, 2026

Automated PR for branch feat/clean-base-1698-strip-empty-messages-rebase as requested.

Summary by CodeRabbit

  • New Features

    • Enhanced GitHub Copilot authentication to capture and store user email and name information.
    • Added rate limiting and quota management for Qwen model requests to improve service reliability.
  • Bug Fixes

    • Improved token refresh mechanism to immediately handle non-retryable authentication errors.
    • Fixed request/response compatibility for Claude and Codex model integrations.
  • Tests

    • Added test coverage for authentication flows, token handling, and model operations.

ciberponk and others added 15 commits February 21, 2026 12:56
- Add 60 requests/minute rate limiting per credential using sliding window
- Detect insufficient_quota errors and set cooldown until next day (Beijing time)
- Map quota errors (HTTP 403/429) to 429 with retryAfter for conductor integration
- Cache Beijing timezone at package level to avoid repeated syscalls
- Add redactAuthID function to protect credentials in logs
- Extract wrapQwenError helper to consolidate error handling
…ndling-clean

feat(qwen): add rate limiting and quota error handling
…-refresh-interval

fix(auth): respect configured auto-refresh interval
…resh-token-reused-no-retry

fix(codex): stop retrying refresh_token_reused errors
…paction-compat

feat: add codex responses compatibility for compaction payloads
- Expand OAuth scope to include read:user for full profile access
- Add GitHubUserInfo struct with Login, Email, Name fields
- Update FetchUserInfo to return complete user profile
- Add Email and Name fields to CopilotTokenStorage and CopilotAuthBundle
- Fix provider string bug: 'github' -> 'github-copilot' in auth_files.go
- Fix semantic bug: email field was storing username
- Update Label to prefer email over username in both CLI and Web API paths
- Add 9 unit tests covering new functionality
- Add 'github-user' fallback in WaitForAuthorization when FetchUserInfo
  returns empty Login (fixes malformed 'github-copilot-.json' filenames)
- Standardize Web API file name to 'github-copilot-<user>.json' to match
  CLI path convention (was 'github-<user>.json')

Addresses Gemini Code Assist review comments on PR #291.
feat(copilot): fetch and persist user email and display name on login
Cherry-picked from merge/1698-strip-empty-messages-openai-to-claude into aligned base
@coderabbitai
Copy link

coderabbitai bot commented Feb 26, 2026

Warning

Rate limit exceeded

@KooshaPari has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 23 minutes and 27 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 03b2fdf and ae4484a.

📒 Files selected for processing (13)
  • internal/api/handlers/management/auth_files.go
  • internal/auth/codex/openai_auth.go
  • internal/auth/codex/openai_auth_test.go
  • internal/auth/copilot/copilot_auth.go
  • internal/auth/copilot/oauth.go
  • internal/auth/copilot/oauth_test.go
  • internal/auth/copilot/token.go
  • internal/runtime/executor/qwen_executor.go
  • internal/translator/claude/openai/chat-completions/claude_openai_request.go
  • internal/translator/codex/openai/responses/codex_openai-responses_request.go
  • internal/translator/codex/openai/responses/codex_openai-responses_request_test.go
  • sdk/auth/github_copilot.go
  • sdk/cliproxy/auth/conductor.go
📝 Walkthrough

Walkthrough

Changes enhance GitHub Copilot authentication by replacing the "github" provider identifier with "github-copilot," adding user email and name fields to token storage and metadata, and fetching complete user information. OpenAI authentication now handles non-retryable refresh errors to prevent unnecessary retries. Qwen executor introduces comprehensive rate limiting with quota detection. Translator layers add empty-field guards and compatibility field stripping. Minor refactoring relaxes interval clamping logic.

Changes

Cohort / File(s) Summary
GitHub Copilot Authentication Flow
internal/api/handlers/management/auth_files.go, internal/auth/copilot/copilot_auth.go, internal/auth/copilot/oauth.go, internal/auth/copilot/token.go, sdk/auth/github_copilot.go
Provider identifier changed from "github" to "github-copilot" throughout authentication path. User information (email, name, login) now fetched via GitHub API and stored in token records and metadata. File naming updated to "github-copilot-<username>.json". Label derived from email with username fallback.
Copilot OAuth Tests
internal/auth/copilot/oauth_test.go
Comprehensive test suite added for FetchUserInfo covering full profiles, null emails, missing tokens/logins, HTTP errors, and CopilotTokenStorage/CopilotAuthBundle JSON serialization with email and name fields.
OpenAI Token Refresh
internal/auth/codex/openai_auth.go, internal/auth/codex/openai_auth_test.go
Non-retryable error detection added to RefreshTokensWithRetry; errors containing "refresh_token_reused" are logged and returned immediately without retry attempts. New helper function and unit test verify single-attempt behavior for this error path.
Qwen Rate Limiting & Quota Handling
internal/runtime/executor/qwen_executor.go
Introduces per-minute rate limiting with sliding-window logic, quota-error detection, quota reset timing (Beijing timezone), and error response mapping to 429-like semantics. Rate limit checks and error wrapping integrated into Execute and ExecuteStream flows. Redacted credential logging added.
Claude Translator Compatibility
internal/translator/claude/openai/chat-completions/claude_openai_request.go
Empty text field guards added to prevent emitting empty {"type":"text","text":""} blocks when transforming OpenAI messages to Claude format across system and user/assistant message types.
Codex Response Compatibility
internal/translator/codex/openai/responses/codex_openai-responses_request.go, internal/translator/codex/openai/responses/codex_openai-responses_request_test.go
Helper function added to strip context_management field from OpenAI responses before forwarding to Codex. Two new tests verify removal of context_management and truncation fields for compatibility.
Conductor Interval Handling
sdk/cliproxy/auth/conductor.go
StartAutoRefresh no longer clamps intervals exceeding refreshCheckInterval; only non-positive intervals default to refreshCheckInterval. Relaxes previous upper-bound constraint.

Sequence Diagrams

sequenceDiagram
    actor Client
    participant CopilotAuth as Copilot Auth Handler
    participant GitHub as GitHub API
    participant Storage as Token Storage
    
    Client->>CopilotAuth: Initiate authentication
    CopilotAuth->>GitHub: Request device code (scope: user:email, read:user)
    GitHub-->>CopilotAuth: Return device code
    CopilotAuth-->>Client: Display device code
    
    Client->>GitHub: Approve authorization (user flow)
    
    Client->>CopilotAuth: Poll for authorization completion
    CopilotAuth->>GitHub: Exchange device code for access token
    GitHub-->>CopilotAuth: Return access token
    CopilotAuth->>GitHub: Fetch user info (Login, Email, Name)
    GitHub-->>CopilotAuth: Return user details
    CopilotAuth->>Storage: Save token with user info (email, name)
    Storage-->>CopilotAuth: Token persisted
    CopilotAuth-->>Client: Return auth bundle with user info
Loading
sequenceDiagram
    actor Client
    participant Executor as Qwen Executor
    participant RateLimiter as Rate Limiter
    participant Qwen as Qwen API
    participant ErrorHandler as Error Handler
    
    Client->>Executor: Execute request (credential ID)
    Executor->>RateLimiter: Check sliding-window rate limit
    alt Rate limit exceeded
        RateLimiter-->>Executor: Reject (retry-after calculated)
        Executor-->>ErrorHandler: Map to quota error with retry guidance
        ErrorHandler-->>Client: Return 429-like error
    else Within limits
        Executor->>Qwen: Send request
        Qwen-->>Executor: Receive response
        alt Quota exceeded response
            Executor->>ErrorHandler: Detect quota error code
            ErrorHandler-->>Executor: Map to retry semantics with reset time
            Executor-->>Client: Return normalized quota error
        else Success
            Executor-->>Client: Return response
        end
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Poem

🐰 A copilot hops with GitHub's grace,
Email and name now find their place,
Qwen learns to pace, quota-aware,
While translators trim empty air—
All stitched with care, a flowing affair! 🌟

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Title check ⚠️ Warning The PR title describes a CI synchronization action, but the changeset contains substantial feature implementations across authentication, rate limiting, and translation layers unrelated to CI or branch synchronization. Retitle to reflect the main changes, such as: 'feat: add GitHub Copilot user info persistence, Qwen rate limiting, and message translation improvements' or similar.
Docstring Coverage ⚠️ Warning Docstring coverage is 73.91% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/clean-base-1698-strip-empty-messages-rebase

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@gemini-code-assist
Copy link

Summary of Changes

Hello @KooshaPari, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request primarily focuses on refining authentication processes for GitHub Copilot and improving API interaction logic for Qwen and Codex. It enhances user data handling for Copilot, introduces robust rate limiting and quota management for Qwen, and ensures better compatibility for OpenAI Responses API requests with Codex. Additionally, it refines token refresh error handling for Codex and optimizes request translation for Claude.

Highlights

  • GitHub Copilot Authentication Enhancement: Enhanced GitHub Copilot authentication to fetch and store additional user information (email and name) during the OAuth flow. Updated OAuth session and file naming conventions to specifically reference 'github-copilot' instead of just 'github'.
  • Qwen Rate Limiting and Quota Handling: Implemented new rate limiting and quota management logic for Qwen API calls, including a per-credential rate limiter (60 requests/minute) and detection of daily quota exhaustion. These are mapped to appropriate HTTP status codes and 'retryAfter' durations.
  • Codex API Compatibility for OpenAI Responses: Improved compatibility for OpenAI Responses API requests when translating to Codex by removing unsupported 'truncation' and 'context_management' parameters.
  • Claude OpenAI Request Translation Improvement: Modified the translation of OpenAI requests to Claude format to skip empty text content parts, preventing the creation of empty text blocks in the Claude request.
  • Codex Token Refresh Retry Logic: Improved the Codex authentication refresh token mechanism to identify and immediately fail on non-retryable errors (e.g., 'refresh_token_reused') instead of retrying.
Changelog
  • internal/api/handlers/management/auth_files.go
    • Removed commented-out import suggestions for Copilot.
    • Updated OAuth session registration to 'github-copilot'.
    • Modified user info fetching to retrieve 'Login', 'Email', and 'Name'.
    • Updated token storage and metadata to include 'Email' and 'Name', and changed provider to 'github-copilot'.
    • Changed 'fileName' generation to 'github-copilot-.json'.
    • Updated 'CompleteOAuthSessionsByProvider' to use 'github-copilot'.
  • internal/auth/codex/openai_auth.go
    • Added 'isNonRetryableRefreshErr' function to identify non-retryable refresh token errors.
    • Modified 'RefreshTokensWithRetry' to immediately return on non-retryable errors.
  • internal/auth/codex/openai_auth_test.go
    • Added a new test 'TestRefreshTokensWithRetry_NonRetryableOnlyAttemptsOnce' to verify non-retryable refresh errors are handled correctly.
  • internal/auth/copilot/copilot_auth.go
    • Updated 'WaitForAuthorization' to fetch and store 'Email' and 'Name' from user info.
    • Modified 'ValidateToken' to return 'userInfo.Login' instead of the full 'userInfo' object.
    • Updated 'CreateTokenStorage' to include 'Email' and 'Name' in the token storage.
  • internal/auth/copilot/oauth.go
    • Modified 'RequestDeviceCode' to request 'read:user' scope in addition to 'user:email'.
    • Changed 'FetchUserInfo' to return a 'GitHubUserInfo' struct containing 'Login', 'Email', and 'Name', instead of just the username string.
    • Added 'GitHubUserInfo' struct definition.
  • internal/auth/copilot/oauth_test.go
    • Added new tests for 'FetchUserInfo' covering full profile, empty email, empty token, empty login, and HTTP errors.
    • Added tests for 'CopilotTokenStorage' to verify 'Email' and 'Name' serialization and 'omitempty' behavior.
    • Added a test for 'CopilotAuthBundle' to verify 'Email' and 'Name' fields.
    • Added a test for 'GitHubUserInfo' struct accessibility.
  • internal/auth/copilot/token.go
    • Added 'Email' and 'Name' fields to 'CopilotTokenStorage' struct with 'omitempty' tags.
    • Added 'Email' and 'Name' fields to 'CopilotAuthBundle' struct.
  • internal/runtime/executor/qwen_executor.go
    • Imported 'sync' package.
    • Defined constants for Qwen rate limiting ('qwenRateLimitPerMin', 'qwenRateLimitWindow').
    • Added 'qwenBeijingLoc' for Beijing timezone handling.
    • Added 'qwenQuotaCodes' map for quota error detection.
    • Implemented 'qwenRateLimiter' with a mutex to track request timestamps per credential.
    • Added 'redactAuthID' for safe logging of auth IDs.
    • Implemented 'checkQwenRateLimit' to enforce per-credential rate limits.
    • Implemented 'isQwenQuotaError' to detect Qwen quota exhaustion from API responses.
    • Implemented 'wrapQwenError' to map Qwen quota errors to HTTP 429 and calculate 'retryAfter' until next day.
    • Implemented 'timeUntilNextDay' to calculate duration until midnight Beijing time.
    • Integrated 'checkQwenRateLimit' into 'Execute' and 'ExecuteStream' methods.
    • Modified error handling in 'Execute' and 'ExecuteStream' to use 'wrapQwenError' for status code mapping and 'retryAfter' calculation.
    • Removed 'authID' from logging variables where it's no longer directly used.
  • internal/translator/claude/openai/chat-completions/claude_openai_request.go
    • Modified 'ConvertOpenAIRequestToClaude' to skip empty text content parts when converting OpenAI requests to Claude format.
  • internal/translator/codex/openai/responses/codex_openai-responses_request.go
    • Added 'truncation' to the list of fields to be deleted from the raw JSON.
    • Implemented 'applyResponsesCompactionCompatibility' to remove 'context_management' for Codex upstream compatibility.
  • internal/translator/codex/openai/responses/codex_openai-responses_request_test.go
    • Added 'TestContextManagementCompactionCompatibility' to verify 'context_management' and 'truncation' are removed.
    • Added 'TestTruncationRemovedForCodexCompatibility' to specifically test 'truncation' removal.
  • sdk/auth/github_copilot.go
    • Added 'email' and 'name' to the metadata stored during GitHub Copilot login.
    • Modified the 'Label' for the 'Auth' record to prioritize 'authBundle.Email' over 'authBundle.Username'.
  • sdk/cliproxy/auth/conductor.go
    • Simplified the 'StartAutoRefresh' interval logic to only check if the interval is less than or equal to zero.
Activity
  • The pull request was automatically created to sync the branch 'feat/clean-base-1698-strip-empty-messages-rebase'.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces several valuable improvements across the codebase. Key changes include enhancing the GitHub Copilot authentication to fetch more user details, adding robust rate limiting and quota handling to the Qwen executor, and improving the Codex auth refresh logic to handle non-retryable errors. Additionally, the Claude translator now correctly strips empty messages, and a bug in the auth conductor's auto-refresh interval has been fixed. The inclusion of new tests for these features is a great practice. I've identified one medium-severity issue in the new Qwen rate-limiting logic that could lead to a minor memory leak and have provided a suggestion for a fix. Overall, these are solid improvements to the system's reliability and functionality.

Comment on lines +100 to +113
if len(validTimestamps) >= qwenRateLimitPerMin {
// Calculate when the oldest request will expire
oldestInWindow := validTimestamps[0]
retryAfter := oldestInWindow.Add(qwenRateLimitWindow).Sub(now)
if retryAfter < time.Second {
retryAfter = time.Second
}
retryAfterSec := int(retryAfter.Seconds())
return statusErr{
code: http.StatusTooManyRequests,
msg: fmt.Sprintf(`{"error":{"code":"rate_limit_exceeded","message":"Qwen rate limit: %d requests/minute exceeded, retry after %ds","type":"rate_limit_exceeded"}}`, qwenRateLimitPerMin, retryAfterSec),
retryAfter: &retryAfter,
}
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

There's a potential for a minor memory leak here. If the rate limit is exceeded, the function returns without pruning the expired timestamps from qwenRateLimiter.requests[authID]. The expired timestamps will remain in memory until a request is allowed for this authID.

To fix this, you should update the timestamps slice for the authID with the pruned validTimestamps before returning the rate limit error.

Suggested change
if len(validTimestamps) >= qwenRateLimitPerMin {
// Calculate when the oldest request will expire
oldestInWindow := validTimestamps[0]
retryAfter := oldestInWindow.Add(qwenRateLimitWindow).Sub(now)
if retryAfter < time.Second {
retryAfter = time.Second
}
retryAfterSec := int(retryAfter.Seconds())
return statusErr{
code: http.StatusTooManyRequests,
msg: fmt.Sprintf(`{"error":{"code":"rate_limit_exceeded","message":"Qwen rate limit: %d requests/minute exceeded, retry after %ds","type":"rate_limit_exceeded"}}`, qwenRateLimitPerMin, retryAfterSec),
retryAfter: &retryAfter,
}
}
if len(validTimestamps) >= qwenRateLimitPerMin {
// Prune expired timestamps to prevent memory leak while rate-limited.
if len(validTimestamps) < len(timestamps) {
qwenRateLimiter.requests[authID] = validTimestamps
}
// Calculate when the oldest request will expire
oldestInWindow := validTimestamps[0]
retryAfter := oldestInWindow.Add(qwenRateLimitWindow).Sub(now)
if retryAfter < time.Second {
retryAfter = time.Second
}
retryAfterSec := int(retryAfter.Seconds())
return statusErr{
code: http.StatusTooManyRequests,
msg: fmt.Sprintf("{\"error\":{\"code\":\"rate_limit_exceeded\",\"message\":\"Qwen rate limit: %d requests/minute exceeded, retry after %ds\",\"type\":\"rate_limit_exceeded\"}}", qwenRateLimitPerMin, retryAfterSec),
retryAfter: &retryAfter,
}
}

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (4)
internal/translator/claude/openai/chat-completions/claude_openai_request.go (1)

22-26: 🧹 Nitpick | 🔵 Trivial

Pre-existing: Package-level mutable state without synchronization.

The global variables user, account, session are initialized lazily in ConvertOpenAIRequestToClaude (lines 48-59) without synchronization. If this function is called concurrently, there's a potential data race. Consider using sync.Once for thread-safe initialization.

This is outside the scope of the current PR changes but worth addressing separately.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/translator/claude/openai/chat-completions/claude_openai_request.go`
around lines 22 - 26, The package-level variables user, account, and session are
lazily initialized in ConvertOpenAIRequestToClaude which can race under
concurrent calls; replace the ad-hoc lazy init with a thread-safe one-time
initializer using sync.Once (e.g., add a package-level sync.Once variable and
move the initialization logic for user/account/session into a function invoked
via once.Do), ensuring ConvertOpenAIRequestToClaude reads those vars only after
the once.Do has run.
sdk/auth/github_copilot.go (1)

35-118: 🛠️ Refactor suggestion | 🟠 Major

Refactor Login into smaller units to meet function-size policy.

The function is significantly over the 40-line limit and now combines device-flow UX, browser launching, token verification, metadata construction, and record assembly.

As per coding guidelines, Maximum function length: 40 lines.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@sdk/auth/github_copilot.go` around lines 35 - 118, The Login method in
GitHubCopilotAuthenticator is too long and should be split into focused helpers:
extract device-flow initiation and user prompt into a helper (e.g.,
startDeviceFlowAndPrompt(ctx, authSvc, opts) that returns deviceCode), move the
polling/authorization into waitForAuthorization(ctx, authSvc, deviceCode)
(returns authBundle), isolate Copilot token verification into
verifyCopilotAccess(ctx, authSvc, accessToken) (returns apiToken), and build
metadata + assemble the coreauth.Auth into buildAuthResult(authBundle,
tokenStorage, apiToken) so that Login delegates to these smaller functions
(preserve existing symbols: GitHubCopilotAuthenticator.Login,
copilot.NewCopilotAuth, authSvc.StartDeviceFlow, authSvc.WaitForAuthorization,
authSvc.GetCopilotAPIToken, authSvc.CreateTokenStorage) and ensure Login becomes
a short orchestrator that calls the new helpers.
internal/auth/copilot/oauth.go (1)

225-271: 🛠️ Refactor suggestion | 🟠 Major

Split FetchUserInfo into smaller helpers.

This function now exceeds the 40-line max and is handling request construction, transport, status handling, decode, and validation in one block. Please extract at least response parsing/validation into helpers.

As per coding guidelines, Maximum function length: 40 lines.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/auth/copilot/oauth.go` around lines 225 - 271, FetchUserInfo is too
long; split response parsing/validation into helpers to meet the 40-line rule:
extract decoding/validation into a function like parseGitHubUserInfo(resp
*http.Response) (GitHubUserInfo, error) that reads/decodes the JSON, checks
raw.Login != "" and returns the typed GitHubUserInfo or a
NewAuthenticationError, and optionally extract request creation into
buildUserInfoRequest(ctx context.Context, accessToken string) (*http.Request,
error); then simplify FetchUserInfo to call these helpers, keep the HTTP call
and response close there, and return the helper's result so FetchUserInfo stays
under 40 lines.
internal/api/handlers/management/auth_files.go (1)

1924-2015: 🛠️ Refactor suggestion | 🟠 Major

RequestGitHubToken should be decomposed to satisfy function-length policy.

This handler is far above the configured 40-line maximum and now contains multiple responsibilities (polling, user-info fallback, storage, record persistence, session completion).

As per coding guidelines, Maximum function length: 40 lines.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/api/handlers/management/auth_files.go` around lines 1924 - 2015,
RequestGitHubToken is too long and mixes responsibilities (device initiation,
polling, user-info handling, token storage, session updates); refactor by
extracting the goroutine body into smaller helpers: e.g.,
InitGitHubDeviceFlow(ctx, deviceClient) to perform RequestDeviceCode and return
(deviceCode, authURL, userCode), PollAndFetchToken(ctx, deviceClient,
deviceCode) to handle PollForToken and FetchUserInfo, BuildTokenRecord(userInfo,
tokenData) to construct the coreauth.Auth and copilot.CopilotTokenStorage, and
SaveAndCompleteSession(ctx, h, state, record) to call h.saveTokenRecord,
SetOAuthSessionError, CompleteOAuthSession and CompleteOAuthSessionsByProvider.
Keep RequestGitHubToken to orchestration only (call RegisterOAuthSession, start
the goroutine that calls these helpers, and return the initial JSON), ensuring
RegisterOAuthSession, SetOAuthSessionError, CompleteOAuthSession,
CompleteOAuthSessionsByProvider, and h.saveTokenRecord usages remain in the
appropriate helpers so the public handler stays under 40 lines.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@internal/auth/copilot/token.go`:
- Around line 29-32: The new PII fields Email and Name are persisted by
SaveTokenToFile which currently uses os.Create (respecting umask) and can
produce world-readable files; modify SaveTokenToFile to open the file with
os.OpenFile and explicit owner-only permissions (0o600 / 0600) when creating the
token file, ensuring you pass os.O_WRONLY|os.O_CREATE|os.O_TRUNC and
FileMode(0o600) so the token containing Email/Name is guaranteed owner-only;
locate and update the SaveTokenToFile function in token.go and remove reliance
on os.Create so owner-only permissions are enforced at creation.

In `@internal/runtime/executor/qwen_executor.go`:
- Around line 71-76: The current early return when authID == "" in the
rate-limit check (see the authID conditional and log.Debug call) creates a
bypass; instead, remove the return and derive a stable fallback bucket key for
rate limiting (e.g., an "anon" bucket or a token-derived hash) so requests with
empty or malformed authID still hit the limiter; update the rate-limit call to
use that fallback key and keep the debug log but do not skip enforcement.
- Around line 70-120: Split checkQwenRateLimit into small helpers: implement
pruneTimestamps(authID string, windowStart time.Time) []time.Time that reads
qwenRateLimiter.requests[authID], returns the pruned valid timestamps and
deletes the map entry if empty; implement calculateRetryAfter(oldest time.Time,
now time.Time) time.Duration that computes the retryAfter (with a minimum of 1s)
using qwenRateLimitWindow; and implement recordRequest(authID string, timestamps
[]time.Time, now time.Time) to append now and write back to
qwenRateLimiter.requests; keep the lock/unlock inside checkQwenRateLimit, call
these helpers in sequence, and preserve the existing error return using
statusErr, qwenRateLimitPerMin and other semantics exactly as before.
- Around line 49-54: Replace the custom sliding-window qwenRateLimiter struct
and its associated logic with per-auth rate.Limiter instances from
golang.org/x/time/rate: create a map[string]*rate.Limiter (e.g., qwenLimiters)
protected by a mutex and a GetLimiter(authID) helper that returns/initializes a
limiter with the desired rate/burst, ensure empty authID is treated as a valid
key (do not bypass limiting), and implement TTL-based cleanup (store lastSeen
timestamps alongside limiters and run a goroutine to remove entries not used for
X duration). Update functions that referenced the old qwenRateLimiter (e.g., any
allow/request-check functions) to call GetLimiter(authID).Allow/Reserve and
remove the old sliding-window timestamp pruning code.

In
`@internal/translator/codex/openai/responses/codex_openai-responses_request_test.go`:
- Around line 284-305: The test TestContextManagementCompactionCompatibility
currently asserts removal of both "context_management" and "truncation"; narrow
its scope by removing the redundant assertions that check for "truncation" (the
gjson.Get(..., "truncation").Exists() check and its t.Fatalf), since truncation
removal is already covered by TestTruncationRemovedForCodexCompatibility; keep
the existing call to ConvertOpenAIResponsesRequestToCodex("gpt-5.2", inputJSON,
false) and the assertion that "context_management" is removed.

---

Outside diff comments:
In `@internal/api/handlers/management/auth_files.go`:
- Around line 1924-2015: RequestGitHubToken is too long and mixes
responsibilities (device initiation, polling, user-info handling, token storage,
session updates); refactor by extracting the goroutine body into smaller
helpers: e.g., InitGitHubDeviceFlow(ctx, deviceClient) to perform
RequestDeviceCode and return (deviceCode, authURL, userCode),
PollAndFetchToken(ctx, deviceClient, deviceCode) to handle PollForToken and
FetchUserInfo, BuildTokenRecord(userInfo, tokenData) to construct the
coreauth.Auth and copilot.CopilotTokenStorage, and SaveAndCompleteSession(ctx,
h, state, record) to call h.saveTokenRecord, SetOAuthSessionError,
CompleteOAuthSession and CompleteOAuthSessionsByProvider. Keep
RequestGitHubToken to orchestration only (call RegisterOAuthSession, start the
goroutine that calls these helpers, and return the initial JSON), ensuring
RegisterOAuthSession, SetOAuthSessionError, CompleteOAuthSession,
CompleteOAuthSessionsByProvider, and h.saveTokenRecord usages remain in the
appropriate helpers so the public handler stays under 40 lines.

In `@internal/auth/copilot/oauth.go`:
- Around line 225-271: FetchUserInfo is too long; split response
parsing/validation into helpers to meet the 40-line rule: extract
decoding/validation into a function like parseGitHubUserInfo(resp
*http.Response) (GitHubUserInfo, error) that reads/decodes the JSON, checks
raw.Login != "" and returns the typed GitHubUserInfo or a
NewAuthenticationError, and optionally extract request creation into
buildUserInfoRequest(ctx context.Context, accessToken string) (*http.Request,
error); then simplify FetchUserInfo to call these helpers, keep the HTTP call
and response close there, and return the helper's result so FetchUserInfo stays
under 40 lines.

In `@internal/translator/claude/openai/chat-completions/claude_openai_request.go`:
- Around line 22-26: The package-level variables user, account, and session are
lazily initialized in ConvertOpenAIRequestToClaude which can race under
concurrent calls; replace the ad-hoc lazy init with a thread-safe one-time
initializer using sync.Once (e.g., add a package-level sync.Once variable and
move the initialization logic for user/account/session into a function invoked
via once.Do), ensuring ConvertOpenAIRequestToClaude reads those vars only after
the once.Do has run.

In `@sdk/auth/github_copilot.go`:
- Around line 35-118: The Login method in GitHubCopilotAuthenticator is too long
and should be split into focused helpers: extract device-flow initiation and
user prompt into a helper (e.g., startDeviceFlowAndPrompt(ctx, authSvc, opts)
that returns deviceCode), move the polling/authorization into
waitForAuthorization(ctx, authSvc, deviceCode) (returns authBundle), isolate
Copilot token verification into verifyCopilotAccess(ctx, authSvc, accessToken)
(returns apiToken), and build metadata + assemble the coreauth.Auth into
buildAuthResult(authBundle, tokenStorage, apiToken) so that Login delegates to
these smaller functions (preserve existing symbols:
GitHubCopilotAuthenticator.Login, copilot.NewCopilotAuth,
authSvc.StartDeviceFlow, authSvc.WaitForAuthorization,
authSvc.GetCopilotAPIToken, authSvc.CreateTokenStorage) and ensure Login becomes
a short orchestrator that calls the new helpers.

ℹ️ Review info

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 03b2fdf and ae4484a.

📒 Files selected for processing (13)
  • internal/api/handlers/management/auth_files.go
  • internal/auth/codex/openai_auth.go
  • internal/auth/codex/openai_auth_test.go
  • internal/auth/copilot/copilot_auth.go
  • internal/auth/copilot/oauth.go
  • internal/auth/copilot/oauth_test.go
  • internal/auth/copilot/token.go
  • internal/runtime/executor/qwen_executor.go
  • internal/translator/claude/openai/chat-completions/claude_openai_request.go
  • internal/translator/codex/openai/responses/codex_openai-responses_request.go
  • internal/translator/codex/openai/responses/codex_openai-responses_request_test.go
  • sdk/auth/github_copilot.go
  • sdk/cliproxy/auth/conductor.go
📜 Review details
🧰 Additional context used
📓 Path-based instructions (1)
**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

**/*.go: NEVER create a v2 file - refactor the original instead
NEVER create a new class if an existing one can be made generic
NEVER create custom implementations when an OSS library exists - search pkg.go.dev for existing libraries before writing code
Build generic building blocks (provider interface + registry) before application logic
Use chi for HTTP routing (NOT custom routers)
Use zerolog for logging (NOT fmt.Print)
Use viper for configuration (NOT manual env parsing)
Use go-playground/validator for validation (NOT manual if/else validation)
Use golang.org/x/time/rate for rate limiting (NOT custom limiters)
Use template strings for messages instead of hardcoded messages and config-driven logic instead of code-driven
Zero new lint suppressions without inline justification
All new code must pass: go fmt, go vet, golint
Maximum function length: 40 lines
No placeholder TODOs in committed code

Files:

  • internal/auth/copilot/token.go
  • sdk/cliproxy/auth/conductor.go
  • internal/translator/codex/openai/responses/codex_openai-responses_request_test.go
  • internal/auth/copilot/copilot_auth.go
  • internal/auth/copilot/oauth.go
  • sdk/auth/github_copilot.go
  • internal/auth/copilot/oauth_test.go
  • internal/runtime/executor/qwen_executor.go
  • internal/auth/codex/openai_auth_test.go
  • internal/translator/codex/openai/responses/codex_openai-responses_request.go
  • internal/translator/claude/openai/chat-completions/claude_openai_request.go
  • internal/api/handlers/management/auth_files.go
  • internal/auth/codex/openai_auth.go
🧠 Learnings (1)
📚 Learning: 2026-02-25T10:11:41.448Z
Learnt from: CR
Repo: KooshaPari/cliproxyapi-plusplus PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-02-25T10:11:41.448Z
Learning: Applies to **/*.go : Use golang.org/x/time/rate for rate limiting (NOT custom limiters)

Applied to files:

  • internal/runtime/executor/qwen_executor.go
🧬 Code graph analysis (7)
internal/translator/codex/openai/responses/codex_openai-responses_request_test.go (1)
internal/translator/codex/openai/responses/codex_openai-responses_request.go (1)
  • ConvertOpenAIResponsesRequestToCodex (10-39)
internal/auth/copilot/copilot_auth.go (1)
internal/auth/copilot/token.go (1)
  • CopilotAuthBundle (48-57)
internal/auth/copilot/oauth.go (2)
internal/api/modules/modules.go (1)
  • Context (17-22)
internal/auth/copilot/errors.go (2)
  • NewAuthenticationError (123-130)
  • ErrUserInfoFailed (115-119)
sdk/auth/github_copilot.go (1)
sdk/cliproxy/auth/types.go (1)
  • Auth (46-96)
internal/auth/copilot/oauth_test.go (1)
internal/auth/copilot/oauth.go (2)
  • DeviceFlowClient (35-38)
  • GitHubUserInfo (215-222)
internal/auth/codex/openai_auth_test.go (1)
internal/auth/codex/openai_auth.go (1)
  • CodexAuth (33-35)
internal/api/handlers/management/auth_files.go (2)
internal/api/handlers/management/oauth_sessions.go (2)
  • RegisterOAuthSession (176-176)
  • CompleteOAuthSessionsByProvider (182-184)
sdk/cliproxy/auth/types.go (1)
  • Auth (46-96)
🪛 GitHub Check: Analyze (Go) (go)
sdk/auth/github_copilot.go

[failure] 90-90:
authBundle.Name undefined (type *copilot.CopilotAuthBundle has no field or method Name)


[failure] 89-89:
authBundle.Email undefined (type *copilot.CopilotAuthBundle has no field or method Email)


[failure] 103-103:
authBundle.Email undefined (type *copilot.CopilotAuthBundle has no field or method Email)

🪛 GitHub Check: build
sdk/auth/github_copilot.go

[failure] 90-90:
authBundle.Name undefined (type *copilot.CopilotAuthBundle has no field or method Name)


[failure] 89-89:
authBundle.Email undefined (type *copilot.CopilotAuthBundle has no field or method Email)


[failure] 103-103:
authBundle.Email undefined (type *copilot.CopilotAuthBundle has no field or method Email)

🔇 Additional comments (15)
internal/translator/claude/openai/chat-completions/claude_openai_request.go (2)

159-164: LGTM!

Good defensive guard that prevents emitting empty text blocks. Extracting textContent once and reusing it is cleaner than calling part.Get("text").String() twice.


185-190: LGTM!

Consistent with the system message handling above. This correctly filters out empty text parts from user/assistant messages.

internal/translator/codex/openai/responses/codex_openai-responses_request.go (2)

29-30: LGTM!

The truncation field deletion and the new compatibility function call are appropriately placed after the other field deletions and before the user field deletion. The ordering is logical.


41-56: LGTM!

The function is well-documented with clear rationale for the compatibility handling. The early return pattern and consistent error handling align with the rest of the file.

internal/translator/codex/openai/responses/codex_openai-responses_request_test.go (1)

307-320: LGTM!

Focused test that clearly validates truncation field removal for Codex compatibility. Good coverage for the feature added at line 29 of the implementation.

internal/runtime/executor/qwen_executor.go (1)

149-159: Quota normalization to 429 with retryAfter propagation looks solid.

The mapping and downstream propagation are consistent across sync and stream paths.

Also applies to: 292-295, 397-403

sdk/cliproxy/auth/conductor.go (1)

1831-1833: Interval fallback logic looks correct.

Line 1831 now defaults only invalid values and preserves explicit positive intervals, which matches the intended refresh configuration behavior.

internal/auth/copilot/copilot_auth.go (1)

85-100: Email/Name propagation across bundle, validation, and storage is consistent.

The updated flow correctly carries Login/Email/Name from user-info fetch through CopilotAuthBundle and into persisted token storage.

Also applies to: 159-165, 174-176

internal/api/handlers/management/auth_files.go (1)

1945-2005: Provider/session rename and metadata enrichment are coherent.

github-copilot is now used consistently for session registration/completion and persisted auth records, and the email/name additions are propagated correctly.

internal/auth/copilot/oauth_test.go (1)

30-124: Strong targeted coverage for the new GitHub profile fields and edge cases.

These tests validate both happy-path profile mapping and critical edge behavior (email: null, empty login/token, HTTP failures), plus JSON persistence semantics for Email/Name.

Also applies to: 126-213

internal/auth/codex/openai_auth.go (1)

279-282: Non-retryable refresh detection is a good reliability fix.

Short-circuiting on refresh_token_reused avoids redundant retries and surfaces the actionable error earlier.

Also applies to: 291-297

sdk/auth/github_copilot.go (1)

89-90: No action needed on Email/Name fields.

The CopilotAuthBundle struct in internal/auth/copilot/token.go properly defines both Email and Name as string fields. The code at lines 89–90 and 103–106 correctly accesses these fields; no compile error should occur.

Likely an incorrect or invalid review comment.

internal/auth/copilot/oauth.go (1)

253-260: Go's JSON decoder does not fail on null email; it silently treats it as empty string.

JSON null unmarshalled into a non-pointer string field does not error—it simply leaves the field unchanged (as the zero value ""). If GitHub returns email: null for private accounts, the current code will decode it to an empty string without raising an error. Only use *string if the application must distinguish between null (user chose privacy) and "" (no email provided). If empty string is acceptable for both cases, no change is required.

Likely an incorrect or invalid review comment.

internal/auth/codex/openai_auth_test.go (2)

18-44: Good regression coverage for non-retryable refresh errors.

The test clearly validates immediate failure behavior and confirms retry suppression via single-attempt assertion.


12-16: No changes needed; custom RoundTripper is the idiomatic pattern for this test.

The roundTripFunc helper (lines 12-16) is a standard, idiomatic Go approach for mocking http.Client.Transport at the request level. It directly implements the http.RoundTripper interface to intercept and control client behavior in unit tests—which is the exact use case here.

net/http/httptest is designed for different purposes: testing HTTP server handlers via NewRequest/ResponseRecorder, or spinning up real in-process test servers via NewServer. It does not replace transport-layer mocking via custom RoundTripper implementations.

The guideline against custom implementations applies to replacing existing libraries/functionality, not to implementing standard interfaces for test mocking. This pattern is correct and widely used across Go codebases.

Likely an incorrect or invalid review comment.

Comment on lines +29 to +32
// Email is the GitHub email address associated with this token.
Email string `json:"email,omitempty"`
// Name is the GitHub display name associated with this token.
Name string `json:"name,omitempty"`
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

cat -n internal/auth/copilot/token.go | sed -n '25,40p'

Repository: KooshaPari/cliproxyapi-plusplus

Length of output: 925


🏁 Script executed:

cat -n internal/auth/copilot/token.go | sed -n '80,105p'

Repository: KooshaPari/cliproxyapi-plusplus

Length of output: 866


🏁 Script executed:

rg -n 'SaveTokenToFile|os\.Create|os\.OpenFile' internal/auth/copilot/token.go -A 3 -B 1

Repository: KooshaPari/cliproxyapi-plusplus

Length of output: 746


Explicit 0600 file mode required when persisting PII.

Lines 29–32 add PII fields (Email, Name) to the token storage. However, SaveTokenToFile at line 89 uses os.Create(), which respects the process umask and may create world-readable files. Replace with os.OpenFile() and explicit 0o600 mode to guarantee owner-only access.

Proposed fix
-	f, err := os.Create(authFilePath)
+	f, err := os.OpenFile(authFilePath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0o600)
 	if err != nil {
 		return fmt.Errorf("failed to create token file: %w", err)
 	}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/auth/copilot/token.go` around lines 29 - 32, The new PII fields
Email and Name are persisted by SaveTokenToFile which currently uses os.Create
(respecting umask) and can produce world-readable files; modify SaveTokenToFile
to open the file with os.OpenFile and explicit owner-only permissions (0o600 /
0600) when creating the token file, ensuring you pass
os.O_WRONLY|os.O_CREATE|os.O_TRUNC and FileMode(0o600) so the token containing
Email/Name is guaranteed owner-only; locate and update the SaveTokenToFile
function in token.go and remove reliance on os.Create so owner-only permissions
are enforced at creation.

Comment on lines +49 to +54
var qwenRateLimiter = struct {
sync.Mutex
requests map[string][]time.Time // authID -> request timestamps
}{
requests: make(map[string][]time.Time),
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

cat -n internal/runtime/executor/qwen_executor.go | head -170

Repository: KooshaPari/cliproxyapi-plusplus

Length of output: 7233


🏁 Script executed:

# Check for x/time/rate imports and custom limiter usage
rg -n 'import|golang.org/x/time/rate|qwenRateLimiter|checkQwenRateLimit|rate\.Limiter|rate\.NewLimiter' internal/runtime/executor/qwen_executor.go

Repository: KooshaPari/cliproxyapi-plusplus

Length of output: 657


🏁 Script executed:

# Get the exact function checkQwenRateLimit to count lines
rg -A 100 'func checkQwenRateLimit' internal/runtime/executor/qwen_executor.go | head -80

Repository: KooshaPari/cliproxyapi-plusplus

Length of output: 3019


Replace the custom rate limiter with golang.org/x/time/rate to follow Go best practices.

This custom sliding-window implementation (lines 49–54, 70–120) violates the established guideline requiring golang.org/x/time/rate for rate limiting in Go. Additionally:

  • The function exceeds the 40-line maximum (currently 51 lines)
  • Empty authID bypasses rate limiting entirely (lines 71–76)
  • The pruning logic (line 94) does not update the map with filtered timestamps, leaving stale entries resident indefinitely

Refactor to use per-auth rate.Limiter entries with TTL-based cleanup.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/runtime/executor/qwen_executor.go` around lines 49 - 54, Replace the
custom sliding-window qwenRateLimiter struct and its associated logic with
per-auth rate.Limiter instances from golang.org/x/time/rate: create a
map[string]*rate.Limiter (e.g., qwenLimiters) protected by a mutex and a
GetLimiter(authID) helper that returns/initializes a limiter with the desired
rate/burst, ensure empty authID is treated as a valid key (do not bypass
limiting), and implement TTL-based cleanup (store lastSeen timestamps alongside
limiters and run a goroutine to remove entries not used for X duration). Update
functions that referenced the old qwenRateLimiter (e.g., any allow/request-check
functions) to call GetLimiter(authID).Allow/Reserve and remove the old
sliding-window timestamp pruning code.

Comment on lines +70 to +120
func checkQwenRateLimit(authID string) error {
if authID == "" {
// Empty authID should not bypass rate limiting in production
// Use debug level to avoid log spam for certain auth flows
log.Debug("qwen rate limit check: empty authID, skipping rate limit")
return nil
}

now := time.Now()
windowStart := now.Add(-qwenRateLimitWindow)

qwenRateLimiter.Lock()
defer qwenRateLimiter.Unlock()

// Get and filter timestamps within the window
timestamps := qwenRateLimiter.requests[authID]
var validTimestamps []time.Time
for _, ts := range timestamps {
if ts.After(windowStart) {
validTimestamps = append(validTimestamps, ts)
}
}

// Always prune expired entries to prevent memory leak
// Delete empty entries, otherwise update with pruned slice
if len(validTimestamps) == 0 {
delete(qwenRateLimiter.requests, authID)
}

// Check if rate limit exceeded
if len(validTimestamps) >= qwenRateLimitPerMin {
// Calculate when the oldest request will expire
oldestInWindow := validTimestamps[0]
retryAfter := oldestInWindow.Add(qwenRateLimitWindow).Sub(now)
if retryAfter < time.Second {
retryAfter = time.Second
}
retryAfterSec := int(retryAfter.Seconds())
return statusErr{
code: http.StatusTooManyRequests,
msg: fmt.Sprintf(`{"error":{"code":"rate_limit_exceeded","message":"Qwen rate limit: %d requests/minute exceeded, retry after %ds","type":"rate_limit_exceeded"}}`, qwenRateLimitPerMin, retryAfterSec),
retryAfter: &retryAfter,
}
}

// Record this request and update the map with pruned timestamps
validTimestamps = append(validTimestamps, now)
qwenRateLimiter.requests[authID] = validTimestamps

return nil
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Split checkQwenRateLimit into smaller helpers to satisfy the function-size rule.

This function is 51 lines. Break out pruning, retry-after calculation, and state update into focused helpers.

As per coding guidelines, "Maximum function length: 40 lines".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/runtime/executor/qwen_executor.go` around lines 70 - 120, Split
checkQwenRateLimit into small helpers: implement pruneTimestamps(authID string,
windowStart time.Time) []time.Time that reads qwenRateLimiter.requests[authID],
returns the pruned valid timestamps and deletes the map entry if empty;
implement calculateRetryAfter(oldest time.Time, now time.Time) time.Duration
that computes the retryAfter (with a minimum of 1s) using qwenRateLimitWindow;
and implement recordRequest(authID string, timestamps []time.Time, now
time.Time) to append now and write back to qwenRateLimiter.requests; keep the
lock/unlock inside checkQwenRateLimit, call these helpers in sequence, and
preserve the existing error return using statusErr, qwenRateLimitPerMin and
other semantics exactly as before.

Comment on lines +71 to +76
if authID == "" {
// Empty authID should not bypass rate limiting in production
// Use debug level to avoid log spam for certain auth flows
log.Debug("qwen rate limit check: empty authID, skipping rate limit")
return nil
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Empty auth IDs currently bypass throttling.

Returning early for empty authID creates a bypass path; malformed or missing IDs can evade the limiter entirely. Use a fallback bucket key (or token-derived stable key) instead of skipping.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/runtime/executor/qwen_executor.go` around lines 71 - 76, The current
early return when authID == "" in the rate-limit check (see the authID
conditional and log.Debug call) creates a bypass; instead, remove the return and
derive a stable fallback bucket key for rate limiting (e.g., an "anon" bucket or
a token-derived hash) so requests with empty or malformed authID still hit the
limiter; update the rate-limit call to use that fallback key and keep the debug
log but do not skip enforcement.

Comment on lines +284 to +305
func TestContextManagementCompactionCompatibility(t *testing.T) {
inputJSON := []byte(`{
"model": "gpt-5.2",
"context_management": [
{
"type": "compaction",
"compact_threshold": 12000
}
],
"input": [{"role":"user","content":"hello"}]
}`)

output := ConvertOpenAIResponsesRequestToCodex("gpt-5.2", inputJSON, false)
outputStr := string(output)

if gjson.Get(outputStr, "context_management").Exists() {
t.Fatalf("context_management should be removed for Codex compatibility")
}
if gjson.Get(outputStr, "truncation").Exists() {
t.Fatalf("truncation should be removed for Codex compatibility")
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Consider narrowing the test scope to match the function name.

TestContextManagementCompactionCompatibility tests both context_management and truncation removal, but the name suggests it focuses only on context management compaction. Since TestTruncationRemovedForCodexCompatibility already covers truncation, the assertion at lines 302-304 is redundant and could be removed for clarity.

💡 Suggested simplification
 func TestContextManagementCompactionCompatibility(t *testing.T) {
 	inputJSON := []byte(`{
 		"model": "gpt-5.2",
 		"context_management": [
 			{
 				"type": "compaction",
 				"compact_threshold": 12000
 			}
 		],
 		"input": [{"role":"user","content":"hello"}]
 	}`)

 	output := ConvertOpenAIResponsesRequestToCodex("gpt-5.2", inputJSON, false)
 	outputStr := string(output)

 	if gjson.Get(outputStr, "context_management").Exists() {
 		t.Fatalf("context_management should be removed for Codex compatibility")
 	}
-	if gjson.Get(outputStr, "truncation").Exists() {
-		t.Fatalf("truncation should be removed for Codex compatibility")
-	}
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
func TestContextManagementCompactionCompatibility(t *testing.T) {
inputJSON := []byte(`{
"model": "gpt-5.2",
"context_management": [
{
"type": "compaction",
"compact_threshold": 12000
}
],
"input": [{"role":"user","content":"hello"}]
}`)
output := ConvertOpenAIResponsesRequestToCodex("gpt-5.2", inputJSON, false)
outputStr := string(output)
if gjson.Get(outputStr, "context_management").Exists() {
t.Fatalf("context_management should be removed for Codex compatibility")
}
if gjson.Get(outputStr, "truncation").Exists() {
t.Fatalf("truncation should be removed for Codex compatibility")
}
}
func TestContextManagementCompactionCompatibility(t *testing.T) {
inputJSON := []byte(`{
"model": "gpt-5.2",
"context_management": [
{
"type": "compaction",
"compact_threshold": 12000
}
],
"input": [{"role":"user","content":"hello"}]
}`)
output := ConvertOpenAIResponsesRequestToCodex("gpt-5.2", inputJSON, false)
outputStr := string(output)
if gjson.Get(outputStr, "context_management").Exists() {
t.Fatalf("context_management should be removed for Codex compatibility")
}
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@internal/translator/codex/openai/responses/codex_openai-responses_request_test.go`
around lines 284 - 305, The test TestContextManagementCompactionCompatibility
currently asserts removal of both "context_management" and "truncation"; narrow
its scope by removing the redundant assertions that check for "truncation" (the
gjson.Get(..., "truncation").Exists() check and its t.Fatalf), since truncation
removal is already covered by TestTruncationRemovedForCodexCompatibility; keep
the existing call to ConvertOpenAIResponsesRequestToCodex("gpt-5.2", inputJSON,
false) and the assertion that "context_management" is removed.

@KooshaPari
Copy link
Owner Author

@coderabbitai full review

@coderabbitai
Copy link

coderabbitai bot commented Feb 26, 2026

✅ Actions performed

Full review triggered.

@KooshaPari KooshaPari merged commit ae4484a into main Feb 27, 2026
4 of 12 checks passed
@KooshaPari KooshaPari deleted the feat/clean-base-1698-strip-empty-messages-rebase branch February 27, 2026 09:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants