Skip to content

Fix provider exhaustion after model redirect (refs #629)#633

Merged
ding113 merged 3 commits intodevfrom
fix/629-provider-fallback-toolresult
Jan 20, 2026
Merged

Fix provider exhaustion after model redirect (refs #629)#633
ding113 merged 3 commits intodevfrom
fix/629-provider-fallback-toolresult

Conversation

@ding113
Copy link
Owner

@ding113 ding113 commented Jan 20, 2026

Summary

  • Fixes provider failover logic to use the original (pre-redirect) model for provider selection, preventing premature provider exhaustion when a previous provider applied modelRedirects.
  • Makes Claude non-stream response converters tolerant of tool_result content blocks (which do not have a .text field), avoiding crashes like ClaudeContentBlockToolResult has no attribute 'text'.

Problem

Fixes #629 - When a provider redirects the model (e.g. claude-* -> glm-*), subsequent failover attempts were selecting/validating providers against the redirected model name, which could incorrectly filter out the remaining healthy Claude providers and end in "no available providers" after only a couple attempts.

The issue manifested as:

  • System repeatedly retrying the same failed provider instead of switching to backup providers
  • Provider exhaustion occurring prematurely due to model name mismatch after redirect
  • Circuit breaker triggering but failover not executing properly

Solution

Use session.getOriginalModel() instead of session.getCurrentModel() throughout the provider selection pipeline to ensure:

  • Provider selection is based on the user's originally requested model
  • Model support validation checks against the correct model name
  • Session reuse logic matches providers correctly
  • Decision context tracking reflects the actual user request

Additionally, make response converters resilient to unexpected tool_result blocks in Claude responses.

Changes

Core Changes

  • src/app/v1/_lib/proxy/provider-selector.ts (5 locations): Replace getCurrentModel() with getOriginalModel() for:
    • Provider reuse validation
    • Model support checks
    • Decision context tracking
    • Provider selection logic

Supporting Changes

  • src/app/v1/_lib/converters/openai-to-claude/response.ts: Add tool_result case handler to ignore these blocks in non-stream conversion
  • src/app/v1/_lib/converters/claude-to-codex/response.ts: Add tool_result case handler to ignore these blocks in non-stream conversion
  • tests/unit/proxy/converters-tool-result-nonstream.test.ts: New test coverage for tool_result tolerance (86 lines)
  • tests/unit/proxy/provider-selector-total-limit.test.ts: Update mock session to include getOriginalModel() method

Testing

Automated Tests

  • Unit tests added for tool_result block handling
  • Existing provider selector tests updated
  • All tests pass (bun run test)

Verification

  • bun run typecheck (pass)
  • bun run lint (pass)
  • bun run test (pass)
  • bun run build (pass)

Related


Description enhanced by Claude AI

Use original model for provider selection during failover and tolerate tool_result blocks in non-stream converters to prevent premature provider exhaustion. References #629.
@coderabbitai
Copy link

coderabbitai bot commented Jan 20, 2026

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

📝 Walkthrough

Walkthrough

在两个 Claude 非流响应转换器中添加对 tool_result 块的显式忽略逻辑;将 provider-selector 中所有 session.getCurrentModel() 调用替换为 session.getOriginalModel();新增/更新单元测试覆盖这些更改。

Changes

Cohort / File(s) 变更摘要
Claude → Codex / OpenAI 非流响应转换器
src/app/v1/_lib/converters/claude-to-codex/response.ts, src/app/v1/_lib/converters/openai-to-claude/response.ts
在非流处理的内容块 switch 中新增 tool_result 分支(显式忽略),并在 switch 添加默认分支以跳过未知块类型。
Provider 选择器(模型来源改用 original)
src/app/v1/_lib/proxy/provider-selector.ts
将所有 session.getCurrentModel() 调用替换为 session.getOriginalModel(),影响 ensure / pickRandomProvider / reuse 等决策路径中使用的模型值。
单元测试
tests/unit/proxy/converters-tool-result-nonstream.test.ts, tests/unit/proxy/provider-selector-total-limit.test.ts, tests/unit/proxy/provider-selector-model-redirect.test.ts
新增测试以验证 tool_result 在 Claude→OpenAI/Claude→Codex 转换中被忽略;更新/新增 provider-selector 相关测试以包含 getOriginalModel mock。

Estimated code review effort

🎯 3 (中等) | ⏱️ ~22 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed 标题清晰准确地总结了PR的主要变更:修复模型重定向后的提供商耗尽问题,并引用了相关的issue #629
Description check ✅ Passed 描述详细阐述了问题、解决方案和变更内容,与代码改动紧密相关,包括核心问题诊断和测试验证。
Linked Issues check ✅ Passed PR完全满足issue #629的目标:通过将provider-selector中的getCurrentModel()替换为getOriginalModel()来修复模型重定向后的提供商选择问题;并通过增加tool_result块处理来解决Claude响应转换崩溃问题。
Out of Scope Changes check ✅ Passed 所有改动都与修复provider exhaustion和tool_result处理直接相关,包括provider-selector核心逻辑、响应转换器更新和相关单元测试,无超出范围的变更。
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

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

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/629-provider-fallback-toolresult

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
Contributor

Summary of Changes

Hello @ding113, 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 addresses two critical areas: improving provider resilience and enhancing response converter robustness. It refactors the provider failover mechanism to consistently use the initially requested model for selection, even after a model redirect, which prevents incorrect filtering and premature exhaustion of available providers. Additionally, it fortifies Claude's non-stream response converters by making them tolerant to tool_result content blocks, thereby preventing application crashes when such blocks are encountered in responses.

Highlights

  • Provider Failover Logic: The system now correctly uses the original (pre-redirect) model for provider selection during failover, preventing premature provider exhaustion when a model redirect occurs.
  • Claude Response Conversion Robustness: Non-stream response converters for Claude are updated to gracefully handle tool_result content blocks, avoiding crashes that previously occurred when these blocks lacked a .text field.
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.

@github-actions github-actions bot added bug Something isn't working area:provider area:core labels Jan 20, 2026
@github-actions
Copy link
Contributor

🧪 测试结果

测试类型 状态
代码质量
单元测试
集成测试
API 测试

总体结果: ✅ 所有测试通过

@github-actions github-actions bot added the size/S Small PR (< 200 lines) label Jan 20, 2026
Copy link
Contributor

@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

The pull request effectively addresses the identified issues related to provider exhaustion during model redirects and handling tool_result blocks in non-stream responses. The core logic change in provider-selector.ts correctly switches to using the original model for provider selection, which is crucial for proper failover. The converter updates gracefully ignore tool_result blocks, preventing crashes, and new unit tests have been added to ensure this behavior. Overall, the changes enhance the system's robustness and reliability.

Comment on lines +407 to +409
default:
// Unknown block types are ignored for non-stream output.
break;
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

The default case currently ignores unknown block types. While this prevents crashes, it might be beneficial to log a warning if an unknown block type is encountered. This would provide better observability and help detect if new, potentially important, block types are introduced by the Claude API that should be explicitly handled by the converter.

Suggested change
default:
// Unknown block types are ignored for non-stream output.
break;
default:
logger.warn("[Claude->Codex] Unknown content block type encountered", { blockType });
break;

Comment on lines +429 to +431
default:
// Unknown block types are ignored for non-stream output.
break;
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

Similar to the Claude-to-Codex converter, silently ignoring unknown block types in the default case could lead to missed opportunities for handling new API features or debugging unexpected responses. Consider adding a warning log here.

Suggested change
default:
// Unknown block types are ignored for non-stream output.
break;
default:
logger.warn("[Claude->OpenAI] Unknown content block type encountered", { blockType });
break;

Copy link

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

No files reviewed, no comments

Edit Code Review Agent Settings | Greptile

Copy link
Contributor

@github-actions github-actions 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 Summary

No significant issues identified in this PR. The changes correctly address the provider exhaustion bug by consistently using getOriginalModel() instead of getCurrentModel() for provider selection, and properly handle tool_result content blocks in response converters.

PR Size: S

  • Lines changed: 118
  • Files changed: 5

Review Coverage

  • Logic and correctness - Clean
  • Security (OWASP Top 10) - Clean
  • Error handling - Clean
  • Type safety - Clean
  • Documentation accuracy - Clean
  • Test coverage - Adequate
  • Code clarity - Good

Validation Notes

Provider Selector Changes (5 locations):
All getCurrentModel()getOriginalModel() changes are in provider selection/validation contexts and correctly ensure the original user-requested model is used for provider matching during failover scenarios.

Converter Changes:
The tool_result case handlers with empty break statements are intentional no-ops - they gracefully handle unexpected content blocks that may appear in Claude responses from certain proxies. This is a defensive fix preventing crashes, not a silent failure.

Test Coverage:

  • New unit tests added for tool_result block handling in both OpenAI and Codex converters
  • Mock session object updated to include getOriginalModel() method

Automated review by Claude AI

Copy link
Contributor

@github-actions github-actions 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 Summary

No significant issues identified in this PR. The changes correctly address the provider exhaustion bug by consistently using getOriginalModel() instead of getCurrentModel() for provider selection, and properly handle tool_result content blocks in response converters.

PR Size: S

  • Lines changed: 118
  • Files changed: 5

Review Coverage

  • Logic and correctness - Clean
  • Security (OWASP Top 10) - Clean
  • Error handling - Clean
  • Type safety - Clean
  • Documentation accuracy - Clean
  • Test coverage - Adequate
  • Code clarity - Good

Validation Notes

Provider Selector Changes (5 locations):
All getCurrentModel() to getOriginalModel() changes are in provider selection/validation contexts and correctly ensure the original user-requested model is used for provider matching during failover scenarios.

Converter Changes:
The tool_result case handlers with empty break statements are intentional no-ops - they gracefully handle unexpected content blocks that may appear in Claude responses from certain proxies. This is a defensive fix preventing crashes, not a silent failure.

Test Coverage:

  • New unit tests added for tool_result block handling in both OpenAI and Codex converters
  • Mock session object updated to include getOriginalModel() method

Automated review by Claude AI

const allProviders = session ? await session.getProvidersSnapshot() : await findAllProviders();
const requestedModel = session?.getCurrentModel() || "";
const requestedModel = session?.getOriginalModel() || "";

Copy link
Contributor

Choose a reason for hiding this comment

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

[HIGH] [TEST-MISSING-CRITICAL] No regression test for model-redirect provider selection

Why this is a problem: This change switches provider filtering to use session.getOriginalModel() instead of the (potentially redirected) current model. This is core failover behavior; without a unit test that simulates currentModel being redirected (e.g. claude-* -> glm-*) while originalModel remains claude-*, it is easy to reintroduce the "no available providers" exhaustion bug during future refactors.

Suggested fix:

// tests/unit/proxy/provider-selector-model-redirect.test.ts
import { describe, expect, test, vi } from "vitest";
import type { Provider } from "@/types/provider";
import { ProxyProviderResolver } from "@/app/v1/_lib/proxy/provider-selector";

describe("ProxyProviderResolver.pickRandomProvider - model redirect", () => {
  test("filters providers using the original model (not redirected current model)", async () => {
    vi.spyOn(ProxyProviderResolver as any, "filterByLimits").mockImplementation(
      async (providers: Provider[]) => providers
    );
    vi.spyOn(ProxyProviderResolver as any, "selectTopPriority").mockImplementation(
      (providers: Provider[]) => providers
    );
    vi.spyOn(ProxyProviderResolver as any, "selectOptimal").mockImplementation(
      (providers: Provider[]) => providers[0] as Provider
    );

    const providers: Provider[] = [
      {
        id: 1,
        name: "p1",
        isEnabled: true,
        providerType: "claude",
        groupTag: null,
        weight: 1,
        priority: 0,
        costMultiplier: 1,
        allowedModels: ["claude-test"],
      } as unknown as Provider,
    ];

    const session = {
      originalFormat: "claude",
      authState: null,
      getProvidersSnapshot: async () => providers,
      getOriginalModel: () => "claude-test",
      getCurrentModel: () => "glm-test",
      clientRequestsContext1m: () => false,
    } as any;

    const { provider, context } = await (ProxyProviderResolver as any).pickRandomProvider(
      session,
      []
    );

    expect(context.requestedModel).toBe("claude-test");
    expect(provider?.id).toBe(1);
  });
});

Copy link
Contributor

@github-actions github-actions 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 Summary

This PR adjusts provider selection to use the original (pre-redirect) model across multiple paths and makes non-stream converters ignore Claude tool_result blocks.
Converter tolerance is covered by a new unit test, but the provider-selection change lacks a regression test for the redirected-model scenario that triggered provider exhaustion.

PR Size: S

  • Lines changed: 118
  • Files changed: 5

Issues Found

Category Critical High Medium Low
Logic/Bugs 0 0 0 0
Security 0 0 0 0
Error Handling 0 0 0 0
Types 0 0 0 0
Comments/Docs 0 0 0 0
Tests 0 1 0 0
Simplification 0 0 0 0

Critical Issues (Must Fix)

None.

High Priority Issues (Should Fix)

  • [TEST-MISSING-CRITICAL] Add a unit test that simulates currentModel being redirected while originalModel remains claude-*, and asserts provider selection still filters by the original model (Confidence: 90)

Review Coverage

  • Logic and correctness
  • Security (OWASP Top 10)
  • Error handling
  • Type safety
  • Documentation accuracy
  • Test coverage
  • Code clarity

Automated review by Codex AI

Copy link
Contributor

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

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

Applied PR size label size/S and submitted the review on PR #633 (including 1 inline comment + summary).

Findings (diff-only)

  • [HIGH] [TEST-MISSING-CRITICAL] Missing regression coverage for the redirected-model provider selection path in src/app/v1/_lib/proxy/provider-selector.ts:651
    • Evidence (new behavior line):
      const requestedModel = session?.getOriginalModel() || "";
    • Why this is a problem: this line is the key behavioral change that prevents failover from filtering providers using a redirected model name. Without a unit test that forces getCurrentModel() to differ from getOriginalModel() (e.g. claude-*glm-*), a future refactor can silently revert the bug (“no available providers” after redirect) with no test catching it.
    • Concrete fix (test suggestion): add a unit test that sets:
      • getOriginalModel(): "claude-test"
      • getCurrentModel(): "glm-test"
      • providers: only providerType: "claude" with allowedModels: ["claude-test"]
      • and asserts pickRandomProvider returns a provider + context.requestedModel === "claude-test"
        (I included a full, copy/paste-ready test snippet in the inline PR comment attached to this line.)

ding113 and others added 2 commits January 20, 2026 19:14
Ensures provider selection uses original model even when current model is redirected (refs #629).
@ding113 ding113 merged commit a11dc76 into dev Jan 20, 2026
@github-project-automation github-project-automation bot moved this from Backlog to Done in Claude Code Hub Roadmap Jan 20, 2026
ding113 added a commit that referenced this pull request Jan 20, 2026
* fix: 清理 usage-doc / big-screen i18n 硬编码 + 修复 Next.js params Promise 报错

* feat(leaderboard): add user tag and group filters for user ranking (#607)

* feat(leaderboard): add user tag and group filters for user ranking (#606)

Add filtering capability to the leaderboard user ranking by:
- userTags: filter users by their tags (OR logic)
- userGroups: filter users by their providerGroup (OR logic)

Changes:
- Repository: Add UserLeaderboardFilters interface and SQL filtering
- Cache: Extend LeaderboardFilters and include filters in cache key
- API: Parse userTags/userGroups query params (CSV format, max 20)
- Frontend: Add TagInput filters (admin-only, user scope only)
- i18n: Add translation keys for 5 languages

Closes #606

* refactor: apply reviewer suggestions for leaderboard filters

- Use JSONB ? operator instead of @> for better performance
- Extract parseListParam helper to reduce code duplication

* feat(leaderboard): add tag/group suggestions dropdown for better UX

- Fetch all user tags and groups via getAllUserTags/getAllUserKeyGroups
- Pass suggestions to TagInput for autocomplete dropdown
- Validate input against available suggestions
- Consistent with /dashboard/users filter behavior

* docs: update Privnode offer details in README files

* fix: 修复 1M 上下文标头兼容问题

* fix: resolve container name conflicts in multi-user environments (#625)

* fix: resolve container name conflicts in multi-user environments

- Add top-level `name` field with COMPOSE_PROJECT_NAME env var support
- Remove hardcoded container_name from all services
- Users can now set COMPOSE_PROJECT_NAME in .env for complete isolation

Closes #624

* fix: add top-level name field for project isolation

Add `name: ${COMPOSE_PROJECT_NAME:-claude-code-hub}` to enable
complete project isolation via environment variable.

* feat(dashboard): improve user management, statistics reset, and i18n (#610)

* fix(dashboard/logs): add reset options to filters and use short time format

- Add "All keys" SelectItem to API Key filter dropdown
- Add "All status codes" SelectItem to Status Code filter dropdown
- Use __all__ value instead of empty string (Radix Select requirement)
- Add formatDateDistanceShort() for compact time display (2h ago, 3d ago)
- Update RelativeTime component to use short format

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(providers): add Auto Sort button to dashboard and fix i18n

- Add AutoSortPriorityDialog to dashboard/providers page
- EN: "Auto Sort Priority" -> "Auto Sort"
- RU: "Авто сортировка приоритета" -> "Автосорт"
- RU: "Добавить провайдера" -> "Добавить поставщика"

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(i18n): improve Russian localization and fix login errors

Russian localization improvements:
- Menu: "Управление поставщиками" -> "Поставщики"
- Menu: "Доступность" -> "Мониторинг"
- Filters: "Последние 7/30 дней" -> "7д/30д"
- Dashboard: "Статистика использования" -> "Статистика"
- Dashboard: "Показать статистику..." -> "Только ваши ключи"
- Quota: add missing translations (manageNotice, withQuotas, etc.)

Login error localization:
- Fix issue where login errors displayed in Chinese ("无效或已过期") regardless of locale
- Add locale detection from NEXT_LOCALE cookie and Accept-Language header
- Add 3 new error keys: apiKeyRequired, apiKeyInvalidOrExpired, serverError
- Support all 5 languages: EN, JA, RU, ZH-CN, ZH-TW
- Remove product name from login privacyNote for all locales

Files changed:
- messages/*/auth.json: new error keys, update privacyNote
- messages/ru/dashboard.json, messages/ru/quota.json: Russian improvements
- src/app/api/auth/login/route.ts: add getLocaleFromRequest()

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* feat(dashboard/users): improve user management with key quotas and tokens

- Add access/model restrictions support (allowedClients/allowedModels)
- Add tokens column and refresh button to users table
- Add todayTokens calculation in repository layer (sum all token types)
- Add visual status indicators with color-coded icons (active/disabled/expiring/expired)
- Allow users to view their own key quota (was admin-only)
- Fix React Query cache invalidation on status toggle
- Fix filter logic: change tag/keyGroup from OR to AND
- Refactor time display: move formatDateDistanceShort to component with i18n
- Add fixed header/footer to key dialogs for better UX

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* feat(dashboard/users): add reset statistics with optimized Redis pipeline

- Implement reset all statistics functionality for admins
- Optimize Redis operations: replace sequential redis.keys() with parallel SCAN
- Add scanPattern() helper for production-safe key scanning
- Comprehensive error handling and performance metrics logging
- 50-100x performance improvement with no Redis blocking

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* fix(lint): apply biome formatting and fix React hooks dependencies

- Fix useEffect dependencies in RelativeTime component (wrap formatShortDistance in useCallback)
- Remove unused effectiveGroupText variable in key-row-item.tsx
- Apply consistent LF line endings across modified files

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: address code review feedback from PR #610

- Remove duplicate max-h class in edit-key-dialog.tsx (keep max-h-[90dvh] only)
- Add try-catch fallback for getTranslations in login route catch block

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: translate Russian comments to English for consistency

Addresses Gemini Code Assist review feedback.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* test(users): add unit tests for resetUserAllStatistics function

Cover all requirement scenarios:
- Permission check (admin-only)
- User not found handling
- Success path with DB + Redis cleanup
- Redis not ready graceful handling
- Redis partial failure warning
- scanPattern failure warning
- pipeline.exec failure error logging
- Unexpected error handling
- Empty keys list handling

10 test cases with full mock coverage.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: John Doe <johndoe@example.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>

* feat(my-usage): cache statistics and timezone fixes (#623)

* feat(my-usage): add cache token statistics to model breakdown

Add cacheCreationTokens and cacheReadTokens fields to ModelBreakdownItem
interface and related DB queries for displaying cache statistics in the
statistics summary card modal.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(my-usage): use configured timezone for date filtering

Use server timezone (TZ config) instead of browser locale when:
- Filtering statistics by date (getMyStatsSummary)
- Calculating daily quota time ranges (getTimeRangeForPeriod)

This ensures consistent date interpretation across different client
timezones. Fixes discrepancy between Daily Quota and Statistics
Summary when user is in a different timezone than the server.

Added comprehensive unit tests covering:
- Date parsing in configured timezone
- Timezone offset calculations
- Day boundary edge cases

* refactor(my-usage): clean up expiration displays

Remove duplicate expiration information:
- Remove Key Expires chip from Welcome header (keep User Expires only)
- Remove "Expiring Soon" warning block from Quota Usage cards

Improve countdown display in ExpirationInfo component:
- Add Clock icon
- Increase font size to match date display
- Use monospace font for better readability
- Color-coded by status (emerald/amber/red)

ExpirationInfo remains the single source of expiration data.

* fix: correct cache hit rate formula to exclude output tokens

Output tokens are never cached (per Anthropic docs), so they should not be
included in cache hit rate calculation. Changed formula from:
  cacheReadTokens / (input + output + cacheCreate + cacheRead)
to:
  cacheReadTokens / (input + cacheCreate + cacheRead)

Affects:
- /my-usage model breakdown cache hit rate display
- /dashboard/leaderboard provider cache hit rate ranking

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* fix: address PR #623 review feedback

- Fix outdated cache hit rate comment (leaderboard.ts:430)
- Add keyboard accessibility to model breakdown rows (a11y)
- Rename totalTokens -> totalInputTokens in ProviderCacheHitRateLeaderboardEntry
- Extract parseDateRangeInServerTimezone helper to reduce duplication

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: add backward compatibility for totalTokens field

Keep returning totalTokens (deprecated) alongside totalInputTokens
for API consumers that haven't migrated yet.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: John Doe <johndoe@example.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>

* fix: Set HOSTNAME environment variable in Dockerfile (#622)

Add HOSTNAME environment variable for container

* fix: inherit 1M flag from client

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>

* fix(my-usage): make date range DST-safe

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>

* test(api): cover leaderboard comma-list parsing

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>

* fix(auth): avoid hardcoded server error fallback

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>

* fix(actions): localize key quota permission errors

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>

* docs: clarify context 1M inherit behavior

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>

* test(settings): fix model multi-select messages loader

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>

* test(dashboard): provide QueryClientProvider in edit key form test

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>

* chore(scripts): avoid hardcoded docker container names

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>

* fix(i18n): replace fullwidth parentheses in ja dashboard

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>

* fix(ui): add a11y label and clean up dialog styles

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>

* chore: ignore tmp scratch directory

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>

* feat: Dashboard Logs:秒级时间筛选 + Session ID 精确筛选/联想/展示(含回归修复) (#611)

* feat: add sessionId filter for usage logs

* feat: add seconds-level time filters for logs

* feat: wire sessionId into logs URL filters

* chore: add i18n keys for logs sessionId

* feat: add sessionId column to logs tables

* feat: add sessionId suggestions for logs

* docs: document dashboard logs call chain

* test: add logs sessionId/time filter coverage config

* fix: keep sessionId search input focused

* fix: drop leaked page param on logs apply

* fix: reload sessionId suggestions on scope change

* fix: harden logs url params and time parsing

* fix: avoid keys join for sessionId suggestions

* test: strengthen empty sessionId filter assertions

* chore: format logs sessionId suggestions test

* Update src/actions/usage-logs.ts

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>

* feat: 补充公共静态变量 SESSION_ID_SUGGESTION_LIMIT

* fix: use prefix LIKE for sessionId suggestions

* refactor: centralize usage logs sessionId suggestion constants

* refactor: simplify logs url filters parsing

* refactor: reuse clipboard util for sessionId copy

* chore(db): add sessionId prefix index

* docs: clarify sessionId suggestion semantics

* test: add escapeLike unit tests

* chore: apply biome fixes for sessionId search

* feat: include session id in error responses

* test: add coverage suite for session id errors

* docs: add guide for error session id

* chore: format code (feat-logs-sessionid-time-filter-233f96a)

* feat(dashboard/logs): add fullscreen mode (#632)

* Fix provider exhaustion after model redirect (refs #629) (#633)

* Fix provider fallback after model redirect

Use original model for provider selection during failover and tolerate tool_result blocks in non-stream converters to prevent premature provider exhaustion. References #629.

* Add regression test for model redirect provider selection

Ensures provider selection uses original model even when current model is redirected (refs #629).

* chore: format code (fix-629-provider-fallback-toolresult-b17f671)

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>

---------

Co-authored-by: YangQing-Lin <56943790+YangQing-Lin@users.noreply.github.com>
Co-authored-by: SaladDay <92240037+SaladDay@users.noreply.github.com>
Co-authored-by: miraserver <20286838+miraserver@users.noreply.github.com>
Co-authored-by: John Doe <johndoe@example.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: hwa <yll2002mail@gmail.com>
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Co-authored-by: Ding <ding113@users.noreply.github.com>
Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
@ding113 ding113 deleted the fix/629-provider-fallback-toolresult branch January 27, 2026 09:21
@github-actions github-actions bot mentioned this pull request Feb 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:core area:provider bug Something isn't working size/S Small PR (< 200 lines)

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

1 participant