Skip to content

fix(combo): fallback to next model on all-accounts-rate-limited 503 (… - #1523

Merged
diegosouzapw merged 5 commits into
release/v3.7.0from
release/v3.6.8
Apr 23, 2026
Merged

diegosouzapw merged 5 commits into
release/v3.7.0from
release/v3.6.8

Conversation

@diegosouzapw

Copy link
Copy Markdown
Owner

…#1398)

Integrated into release/v3.6.9

Summary

  • Describe the user-facing or operational change.

Related Issues

  • Closes #
  • Related to #

Validation

  • npm run lint
  • npm run test:unit
  • npm run test:coverage
  • Coverage is still >= 60% for statements, lines, functions, and branches
  • SonarQube PR analysis is green or any remaining issues are explicitly documented below

Tests Added Or Updated

  • List every changed or added automated test file.
  • If no production code changed, state that here.

Coverage Notes

  • If this PR changes src/, open-sse/, electron/, or bin/, explain which tests cover the change.
  • If coverage moved down in any touched file, explain why and what follow-up task will recover it.

Reviewer Notes

  • Call out any risky areas, migrations, feature flags, or manual validation that reviewers should know about.

benzntech and others added 4 commits April 18, 2026 15:10
…pter

This commit addresses issues #1514, #1520, and #1522. It ensures cache_control is only injected for supported Anthropic models and removes Gemini-specific validation tools from Vertex AI Claude adapter envelopes.

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

Copy link
Copy Markdown
Contributor

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 logic to detect when all accounts for a provider are rate-limited by checking for 503 responses with specific error patterns, allowing the system to fall back to the next model in both priority and round-robin strategies. Feedback was provided to simplify the regex patterns by removing redundant entries and to improve the robustness of the content-type check using case-insensitive comparison.

Comment on lines +65 to +70
const ALL_ACCOUNTS_RATE_LIMITED_PATTERNS = [/unavailable/i, /service temporarily unavailable/i];

function isAllAccountsRateLimitedResponse(status: number, contentType: string | null, errorText: string): boolean {
if (status !== 503) return false;
if (!contentType?.includes("application/json")) return false;
return ALL_ACCOUNTS_RATE_LIMITED_PATTERNS.some((p) => p.test(errorText));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

There are a couple of minor improvements for this utility function:

  1. Redundant Regex: In ALL_ACCOUNTS_RATE_LIMITED_PATTERNS, the pattern /unavailable/i is a subset of /service temporarily unavailable/i. Any string matching the latter will already match the former, so the second pattern can be removed.
  2. Case Sensitivity: While header names are case-insensitive, header values might not be normalized depending on the environment. It is safer to use .toLowerCase() when checking the content-type value.
const ALL_ACCOUNTS_RATE_LIMITED_PATTERNS = [/unavailable/i];

function isAllAccountsRateLimitedResponse(status: number, contentType: string | null, errorText: string): boolean {
  if (status !== 503) return false;
  if (!contentType?.toLowerCase().includes("application/json")) return false;
  return ALL_ACCOUNTS_RATE_LIMITED_PATTERNS.some((p) => p.test(errorText));
}
Suggested change
const ALL_ACCOUNTS_RATE_LIMITED_PATTERNS = [/unavailable/i, /service temporarily unavailable/i];
function isAllAccountsRateLimitedResponse(status: number, contentType: string | null, errorText: string): boolean {
if (status !== 503) return false;
if (!contentType?.includes("application/json")) return false;
return ALL_ACCOUNTS_RATE_LIMITED_PATTERNS.some((p) => p.test(errorText));
const ALL_ACCOUNTS_RATE_LIMITED_PATTERNS = [/unavailable/i];
function isAllAccountsRateLimitedResponse(status: number, contentType: string | null, errorText: string): boolean {
if (status !== 503) return false;
if (!contentType?.toLowerCase().includes("application/json")) return false;
return ALL_ACCOUNTS_RATE_LIMITED_PATTERNS.some((p) => p.test(errorText));
}

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3bc97b3e37

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread tests/unit/combo-routing-engine.test.ts Outdated
Comment on lines +1780 to +1790
handleSingleModel: async (_body, modelStr) => {
calls.push(modelStr);
// Simulate handleNoCredentials returning a 503 with "unavailable" message
// This is the signal emitted when getProviderCredentialsWithQuotaPreflight exhausts all accounts
return new Response(
JSON.stringify({ error: { message: `[provider/model] Service temporarily unavailable` } }),
{
status: 503,
headers: { "content-type": "application/json" },
}
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Make new fallback test return success on the second model

This test’s handleSingleModel stub always returns a 503 for every model, so the combo can never produce the asserted result.ok === true / "ok" payload. As written, it will fail instead of validating the new fallback behavior; you need to make at least one fallback target (e.g., model-b) return a 200 response. The same pattern appears in the adjacent round-robin fallback test.

Useful? React with 👍 / 👎.

@diegosouzapw

Copy link
Copy Markdown
Owner Author

Thanks @diegosouzapw for this great contribution! 🎉 This resilience improvement was already integrated into our pipeline during the v3.6.9 patch, but we are officially merging this PR now to ensure it's tracked properly and included in the release/v3.7.0 history. We appreciate your effort!

@diegosouzapw

Copy link
Copy Markdown
Owner Author

Thanks @diegosouzapw for this great contribution! 🎉 This resilience improvement was already partially integrated into our pipeline during the v3.6.9 patch, but we are officially merging this PR now to ensure it's tracked properly and included in the release/v3.7.0 history. We appreciate your effort!

@diegosouzapw
diegosouzapw merged commit 7388623 into release/v3.7.0 Apr 23, 2026
1 check was pending

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c6389c4f82

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +151 to +152
const supportsPromptCaching =
provider === "claude" || provider?.startsWith?.("anthropic-compatible-");

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Include native Anthropic in prompt-caching allowlist

prepareClaudeRequest now gates all cache marker insertion on supportsPromptCaching, but that predicate only matches "claude" and anthropic-compatible-*. The primary API-key Anthropic provider ID is "anthropic" (see open-sse/config/providerRegistry.ts), so Anthropic-bound requests will have cache_control stripped and never re-added, silently disabling prompt caching and increasing latency/cost relative to previous behavior. Please include provider === "anthropic" in this check.

Useful? React with 👍 / 👎.

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.

2 participants