Skip to content

feat(circuit-breaker): endpoint CB default-off + 524 decision chain audit#773

Merged
ding113 merged 2 commits intodevfrom
feat/endpoint-cb-default-off-and-524-chain
Feb 12, 2026
Merged

feat(circuit-breaker): endpoint CB default-off + 524 decision chain audit#773
ding113 merged 2 commits intodevfrom
feat/endpoint-cb-default-off-and-524-chain

Conversation

@ding113
Copy link
Owner

@ding113 ding113 commented Feb 12, 2026

Summary

Two operational improvements to the endpoint circuit breaker system: make it opt-in (default OFF) via a new env var, and fix a gap where 524 vendor-type timeouts were invisible in the decision chain.

Problem

  1. Endpoint circuit breaker too aggressive by default: After feat(circuit): unify provider-endpoint circuit visibility and notifications #755 unified endpoint circuit visibility, the endpoint-level CB was always active. In environments with unstable networks, this caused endpoints to be silently blocked, confusing operators who saw "enabled" endpoints that weren't actually serving traffic (similar to the pattern reported in bug:最新版本5.4,如果把供应商管理里面的端点监测关掉,直接全部报错无法请求 #732).

  2. 524 timeout invisible in decision chain: When all endpoints for a vendor-type timed out (HTTP 524), the forwarder triggered the vendor-type circuit breaker but never called addProviderToChain, making the timeout completely invisible in logs and the UI timeline. This was part of the broader observability gap described in 端点熔断在 UI 和决策链中的可见性不足 #754 and logic and display of provider errors #760.

  3. Hardcoded Chinese strings in filter details: provider-selector.ts contained hardcoded Chinese strings for circuit breaker states ("供应商类型临时熔断", "熔断器打开", etc.) instead of i18n keys, breaking display for non-Chinese locales.

Related Issues/PRs:

Solution

1. Endpoint CB default-off (ENABLE_ENDPOINT_CIRCUIT_BREAKER)

  • New env var ENABLE_ENDPOINT_CIRCUIT_BREAKER (default: false) gates all endpoint-level CB functions
  • When disabled: isEndpointCircuitOpen returns false, recordEndpointFailure/recordEndpointSuccess/triggerAlert are no-ops
  • endpoint-selector.ts skips circuit checks entirely when disabled, returning all enabled endpoints
  • Startup init (initEndpointCircuitBreaker) clears stale Redis keys when the feature is disabled, preventing leftover open states from blocking endpoints after toggling off

2. 524 decision chain audit

  • forwarder.ts: calls session.addProviderToChain() with reason: "vendor_type_all_timeout" BEFORE triggering the vendor-type circuit breaker
  • New reason vendor_type_all_timeout added to the ProviderChainItem type union
  • provider-chain-formatter.ts: full timeline block rendering for the new reason (provider, status code, error details, note)
  • provider-chain-popover.tsx: recognizes vendor_type_all_timeout and endpoint_pool_exhausted as actual requests with proper status icons

3. i18n fix for filter details

  • Replaced hardcoded Chinese strings in provider-selector.ts with i18n keys (vendor_type_circuit_open, circuit_open, circuit_half_open, rate_limited)
  • Added filterDetails section to all 5 language files
  • LogicTraceTab.tsx and provider-chain-formatter.ts now resolve filter detail keys through i18n with fallback to raw value

Changes (22 files, +657/-34)

Area Files Description
Config env.schema.ts, .env.example, deploy.sh/ps1 Add ENABLE_ENDPOINT_CIRCUIT_BREAKER env var
Backend endpoint-circuit-breaker.ts, endpoint-selector.ts Gate all CB functions + startup cleanup
Proxy forwarder.ts, session.ts, provider-selector.ts Record 524 in chain, i18n-safe details
Frontend LogicTraceTab.tsx, provider-chain-popover.tsx Render new reasons, i18n filterDetails
Formatter provider-chain-formatter.ts Timeline block for vendor_type_all_timeout
Types message.ts Add vendor_type_all_timeout to reason union
i18n messages/{zh-CN,zh-TW,en,ja,ru}/provider-chain.json Translations for new keys + filterDetails
Startup instrumentation.ts Call initEndpointCircuitBreaker() on boot
Tests 3 test files (+396 lines) CB disable gating, selector bypass, formatter coverage

Testing

Automated Tests

  • Unit tests for CB disabled behavior (all functions become no-ops)
  • Unit tests for initEndpointCircuitBreaker (Redis cleanup when disabled, no-op when enabled)
  • Unit tests for endpoint-selector bypass when CB disabled
  • Unit tests for vendor_type_all_timeout formatter (with and without error details)
  • Existing CB tests updated to mock ENABLE_ENDPOINT_CIRCUIT_BREAKER=true

Manual Testing

  • Set ENABLE_ENDPOINT_CIRCUIT_BREAKER=false (default) -> endpoint CB inactive, all enabled endpoints available
  • Set ENABLE_ENDPOINT_CIRCUIT_BREAKER=true -> endpoint CB active, normal circuit breaker behavior
  • Simulate 524 timeout -> verify chain entry with vendor_type_all_timeout in UI timeline
  • Verify filter details display correctly in all 5 languages

Pre-commit

  • bun run typecheck - pass
  • bun run test - 2221 passed, 0 failed
  • bun run lint - clean
  • bun run build - success

Checklist

  • Code follows project conventions
  • Self-review completed
  • Tests pass locally
  • i18n strings added for all 5 languages
  • No breaking changes (new env var defaults to false, preserving current behavior)

Description enhanced by Claude AI

Greptile Overview

Greptile Summary

This PR makes the endpoint circuit breaker opt-in (default OFF) via ENABLE_ENDPOINT_CIRCUIT_BREAKER, fixes a critical observability gap where 524 timeouts were invisible in the decision chain, and replaces hardcoded Chinese strings with i18n keys.

Key Changes:

  • Endpoint CB default-off: New env var gates all circuit breaker functions; when disabled, isEndpointCircuitOpen returns false, recording functions become no-ops, and startup cleanup removes stale Redis keys
  • 524 decision chain audit: forwarder.ts now calls session.addProviderToChain() with vendor_type_all_timeout reason BEFORE triggering the vendor-type circuit breaker, making timeouts visible in logs and UI
  • i18n fix: Replaced hardcoded strings like "供应商类型临时熔断" with i18n keys (vendor_type_circuit_open, circuit_open, etc.) in provider-selector.ts, with proper fallback rendering in UI components

Quality:

  • Comprehensive test coverage (+396 lines across 3 test files)
  • All 5 languages updated with new translations
  • Backward compatible (new env var defaults to false, preserving current behavior)
  • Clean implementation with proper error handling and logging

Confidence Score: 5/5

  • This PR is safe to merge with minimal risk
  • The implementation is well-architected with comprehensive test coverage (396 new test lines, 2221 tests passing). All circuit breaker functions are properly gated with early returns, the new env var defaults to false (preserving current behavior), and the 524 timeout recording happens before the vendor-type CB trigger. The i18n changes replace hardcoded strings with proper keys across all 5 languages. Type safety is maintained throughout, and the startup cleanup logic prevents stale states.
  • No files require special attention

Important Files Changed

Filename Overview
src/lib/endpoint-circuit-breaker.ts Added feature flag gating for all CB functions and startup cleanup logic - implementation is clean and well-tested
src/app/v1/_lib/proxy/forwarder.ts Added decision chain recording for 524 timeouts before vendor-type CB trigger - fixes observability gap
src/lib/provider-endpoints/endpoint-selector.ts Added early-return bypass when endpoint CB disabled, skipping circuit checks entirely
src/app/v1/_lib/proxy/provider-selector.ts Replaced hardcoded Chinese strings with i18n keys for circuit breaker filter details
src/lib/utils/provider-chain-formatter.ts Added complete rendering support for vendor_type_all_timeout with i18n fallback for filterDetails
tests/unit/lib/endpoint-circuit-breaker.test.ts Comprehensive test coverage for disabled CB behavior, init cleanup, and existing tests updated with env mocks

Sequence Diagram

sequenceDiagram
    participant Client
    participant Forwarder
    participant EndpointSelector
    participant EndpointCB as Endpoint Circuit Breaker
    participant Session
    participant VendorTypeCB as Vendor-Type Circuit Breaker
    participant Redis

    Note over EndpointCB,Redis: Startup: initEndpointCircuitBreaker()
    EndpointCB->>EndpointCB: Check ENABLE_ENDPOINT_CIRCUIT_BREAKER
    alt Feature Disabled
        EndpointCB->>Redis: SCAN endpoint_circuit_breaker:state:*
        Redis-->>EndpointCB: Return stale keys
        EndpointCB->>Redis: DEL stale keys
        Note over EndpointCB: Clear in-memory state
    end

    Client->>Forwarder: POST /v1/messages
    Forwarder->>EndpointSelector: getPreferredProviderEndpoints()
    
    alt ENABLE_ENDPOINT_CIRCUIT_BREAKER=false
        EndpointSelector-->>Forwarder: Return all enabled endpoints (skip circuit check)
    else ENABLE_ENDPOINT_CIRCUIT_BREAKER=true
        EndpointSelector->>EndpointCB: isEndpointCircuitOpen(endpointId)
        EndpointCB->>Redis: Load circuit state
        Redis-->>EndpointCB: State
        EndpointCB-->>EndpointSelector: true/false
        EndpointSelector-->>Forwarder: Return available endpoints
    end

    Forwarder->>Forwarder: Attempt all endpoints
    Note over Forwarder: All endpoints timeout (524)

    alt All endpoints for vendor-type timed out
        Forwarder->>Session: addProviderToChain(reason: vendor_type_all_timeout)
        Note over Session: Record decision with full error details
        Forwarder->>VendorTypeCB: recordVendorTypeAllEndpointsTimeout()
        Note over VendorTypeCB: Trigger vendor-type circuit breaker
    end

    Forwarder-->>Client: Error response with decision chain
Loading

…ision chain audit

- Add ENABLE_ENDPOINT_CIRCUIT_BREAKER env var (default: false) to gate endpoint-level circuit breaker
- Gate isEndpointCircuitOpen, recordEndpointFailure, recordEndpointSuccess, triggerEndpointCircuitBreakerAlert behind env switch
- Add initEndpointCircuitBreaker() startup cleanup: clear stale Redis keys when feature disabled
- Gate endpoint filtering in endpoint-selector (getPreferredProviderEndpoints, getEndpointFilterStats)
- Fix 524 vendor-type timeout missing from decision chain: add chain entry with reason=vendor_type_all_timeout in forwarder
- Add vendor_type_all_timeout to ProviderChainItem reason union type (both backend session.ts and frontend message.ts)
- Add timeline rendering for vendor_type_all_timeout in provider-chain-formatter
- Replace hardcoded Chinese strings in provider-selector circuit_open details with i18n keys
- Add i18n translations for vendor_type_all_timeout and filterDetails (5 languages: zh-CN, zh-TW, en, ja, ru)
- Enhance LogicTraceTab to render filterDetails via i18n lookup with fallback
- Add endpoint_pool_exhausted and vendor_type_all_timeout to provider-chain-popover isActualRequest/getItemStatus
- Add comprehensive unit tests for all changes (endpoint-circuit-breaker, endpoint-selector, provider-chain-formatter)
@coderabbitai
Copy link

coderabbitai bot commented Feb 12, 2026

📝 Walkthrough

总体说明

此变更引入了一个新的特性开关 ENABLE_ENDPOINT_CIRCUIT_BREAKER,用于控制端点级熔断器,并添加了对 vendor_type_all_timeout 场景的端到端支持,包括配置、本地化、业务逻辑、UI 渲染和测试覆盖。

变更清单

变更组 / 文件 变更摘要
环境配置与特性开关
.env.example, scripts/deploy.ps1, scripts/deploy.sh, src/lib/config/env.schema.ts
新增 ENABLE_ENDPOINT_CIRCUIT_BREAKER 环境变量,默认值为 false,控制端点级熔断器功能的启用/禁用。
多语言本地化
messages/en/provider-chain.json, messages/ja/provider-chain.json, messages/ru/provider-chain.json, messages/zh-CN/provider-chain.json, messages/zh-TW/provider-chain.json
添加 vendor_type_all_timeoutvendor_type_circuit_opencircuit_opencircuit_half_openrate_limited 等相关的本地化键值对,覆盖 description、reasons、filterDetails 和 details 等多个部分。
端点熔断器实现
src/lib/endpoint-circuit-breaker.ts, src/instrumentation.ts
实现 initEndpointCircuitBreaker() 初始化函数,在特性开关禁用时清空内存和 Redis 状态;现有函数添加动态特性开关守卫。
提供商链路核心逻辑
src/app/v1/_lib/proxy/forwarder.ts, src/app/v1/_lib/proxy/provider-selector.ts, src/app/v1/_lib/proxy/session.ts
在全端点超时路径中添加审计日志记录(reason: vendor_type_all_timeout);更新提供商过滤详情文本(替换中文描述为国际化键值);扩展 addProviderToChain 方法签名以接受新的 reason 值。
前端 UI 组件
src/app/[locale]/dashboard/logs/_components/error-details-dialog/components/LogicTraceTab.tsx, src/app/[locale]/dashboard/logs/_components/provider-chain-popover.tsx
条件化处理 filterDetails 的本地化渲染;添加 endpoint_pool_exhaustedvendor_type_all_timeout 至实际请求判断和状态映射逻辑。
类型定义
src/types/message.ts
ProviderChainItem.reason 联合类型中新增 vendor_type_all_timeout 字面量。
提供商链路格式化工具与测试
src/lib/utils/provider-chain-formatter.ts, src/lib/utils/provider-chain-formatter.test.ts
扩展状态判断和时间线渲染逻辑,专门处理 vendor_type_all_timeout 场景(包括提供商信息、状态码、错误文本、请求持续时间及错误详情);添加 104 行新的综合测试用例,覆盖多个本地化场景和降级处理。
端点选择器与熔断器集成
src/lib/provider-endpoints/endpoint-selector.ts, tests/unit/lib/endpoint-selector.test.ts
在特性开关禁用时跳过熔断器检查,直接返回排序后的端点;新增 90 行测试覆盖禁用场景下的行为验证。
端点熔断器单元测试
tests/unit/lib/endpoint-circuit-breaker.test.ts
引入虚拟定时器和动态环境模拟,添加 202 行新测试用例,包括禁用模式下的功能验证和初始化逻辑覆盖。

预计代码审核工作量

🎯 4 (复杂) | ⏱️ ~65 分钟

可能相关的 PR

  • PR #654:同时修改了提供商链路 UI 和本地化内容(provider-chain JSON 键、provider-chain-popover 和 LogicTraceTab 行为),添加新的原因和详情处理。
  • PR #608:直接相关,同时涉及端点/提供商类型熔断器和路由代码路径(env.schema、endpoint-circuit-breaker.ts、forwarder.ts、provider-selector.ts、instrumentation.ts),为 vendor_type_all_timeout 添加特性开关和审计消息。
  • PR #730:涉及端点熔断器与提供商端点的集成(该 PR 添加初始化和特性开关,相关 PR 添加事务型端点同步和熔断器重置调用)。
🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 52.94% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed PR标题清晰准确地总结了主要变更:端点电路断路器默认关闭和524决策链审计记录。
Description check ✅ Passed Pull request description clearly explains the problem, solution, and all changes made across multiple areas of the codebase.

✏️ 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 feat/endpoint-cb-default-off-and-524-chain

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 enhances the system's resilience and observability by introducing a configurable toggle for the endpoint-level circuit breaker, ensuring it is off by default for safer deployment. It also significantly improves the auditing of vendor-type timeouts, making critical failure reasons visible in the decision chain. Furthermore, the changes address internationalization issues and update the user interface to accurately display these new timeout and circuit breaker states.

Highlights

  • Endpoint Circuit Breaker Default Off: Introduced a new environment variable, ENABLE_ENDPOINT_CIRCUIT_BREAKER, which defaults to false, effectively disabling endpoint-level circuit breaker functions unless explicitly enabled. When disabled, a startup cleanup process removes any stale Redis keys related to endpoint circuit breaker states.
  • 524 Vendor-Type Timeout Auditing: The system now records 524 vendor-type timeouts in the decision chain with the reason vendor_type_all_timeout. Previously, these timeouts were not properly logged in the decision chain, making them invisible in logs.
  • Internationalization (i18n) Improvements: Replaced hardcoded Chinese strings in the provider-selector's circuit open details with i18n keys, and added translations for these new keys across five languages (English, Japanese, Russian, Simplified Chinese, Traditional Chinese).
  • Frontend Rendering Updates: Updated the LogicTraceTab, provider-chain-popover, and provider-chain-formatter components to correctly render the new vendor_type_all_timeout and endpoint_pool_exhausted reasons, including proper i18n for filter details.
Changelog
  • .env.example
    • Added ENABLE_ENDPOINT_CIRCUIT_BREAKER environment variable with a default value of false.
  • messages/en/provider-chain.json
    • Added vendorTypeAllTimeout key to description and reasons sections.
    • Added filterDetails section with vendor_type_circuit_open, circuit_open, circuit_half_open, and rate_limited keys.
    • Added vendorTypeAllTimeout and vendorTypeAllTimeoutNote keys to the main object.
  • messages/ja/provider-chain.json
    • Added vendorTypeAllTimeout key to description and reasons sections.
    • Added filterDetails section with vendor_type_circuit_open, circuit_open, circuit_half_open, and rate_limited keys.
    • Added vendorTypeAllTimeout and vendorTypeAllTimeoutNote keys to the main object.
  • messages/ru/provider-chain.json
    • Added vendorTypeAllTimeout key to description and reasons sections.
    • Added filterDetails section with vendor_type_circuit_open, circuit_open, circuit_half_open, and rate_limited keys.
    • Added vendorTypeAllTimeout and vendorTypeAllTimeoutNote keys to the main object.
  • messages/zh-CN/provider-chain.json
    • Added vendorTypeAllTimeout key to description and reasons sections.
    • Added filterDetails section with vendor_type_circuit_open, circuit_open, circuit_half_open, and rate_limited keys.
    • Added vendorTypeAllTimeout and vendorTypeAllTimeoutNote keys to the main object.
  • messages/zh-TW/provider-chain.json
    • Added vendorTypeAllTimeout key to description and reasons sections.
    • Added filterDetails section with vendor_type_circuit_open, circuit_open, circuit_half_open, and rate_limited keys.
    • Added vendorTypeAllTimeout and vendorTypeAllTimeoutNote keys to the main object.
  • scripts/deploy.ps1
    • Added ENABLE_ENDPOINT_CIRCUIT_BREAKER=false to the circuit breaker configuration.
  • scripts/deploy.sh
    • Added ENABLE_ENDPOINT_CIRCUIT_BREAKER=false to the circuit breaker configuration.
  • src/app/[locale]/dashboard/logs/_components/error-details-dialog/components/LogicTraceTab.tsx
    • Updated LogicTraceTab to use i18n keys for filterDetails when available.
  • src/app/[locale]/dashboard/logs/_components/provider-chain-popover.tsx
    • Updated isActualRequest to include endpoint_pool_exhausted, vendor_type_all_timeout, and client_error_non_retryable reasons.
    • Added status icon and background color for endpoint_pool_exhausted and vendor_type_all_timeout reasons in getItemStatus.
  • src/app/v1/_lib/proxy/forwarder.ts
    • Added a call to session.addProviderToChain to record vendor_type_all_timeout with status code 524 before triggering the vendor-type circuit breaker.
  • src/app/v1/_lib/proxy/provider-selector.ts
    • Replaced hardcoded Chinese strings for details in context.filteredProviders with i18n keys (vendor_type_circuit_open, circuit_open, circuit_half_open, rate_limited).
  • src/app/v1/_lib/proxy/session.ts
    • Added vendor_type_all_timeout to the ProviderChainReason union type.
  • src/instrumentation.ts
    • Added a call to initEndpointCircuitBreaker() during application startup to initialize and clean up endpoint circuit breaker states.
  • src/lib/config/env.schema.ts
    • Added ENABLE_ENDPOINT_CIRCUIT_BREAKER to the environment schema, defaulting to false.
  • src/lib/endpoint-circuit-breaker.ts
    • Gated isEndpointCircuitOpen, recordEndpointFailure, recordEndpointSuccess, and triggerEndpointCircuitBreakerAlert functions with the ENABLE_ENDPOINT_CIRCUIT_BREAKER environment variable.
    • Added initEndpointCircuitBreaker function to clear in-memory and Redis circuit breaker states if the feature is disabled on startup.
  • src/lib/provider-endpoints/endpoint-selector.ts
    • Gated circuit breaker checks in getPreferredProviderEndpoints and getEndpointFilterStats with the ENABLE_ENDPOINT_CIRCUIT_BREAKER environment variable.
  • src/lib/utils/provider-chain-formatter.test.ts
    • Added comprehensive test suite for vendor_type_all_timeout reason in formatProviderSummary, formatProviderDescription, and formatProviderTimeline.
  • src/lib/utils/provider-chain-formatter.ts
    • Updated getProviderStatus and isActualRequest to include vendor_type_all_timeout as a failure reason.
    • Updated formatProviderDescription to display vendorTypeAllTimeout.
    • Modified formatProviderTimeline to use i18n keys for filterDetails when rendering filtered providers.
    • Added a new block in formatProviderTimeline to handle and format vendor_type_all_timeout events, including provider details, status code, error, and a specific note.
  • src/types/message.ts
    • Added vendor_type_all_timeout to the ProviderChainReason union type.
  • tests/unit/lib/endpoint-circuit-breaker.test.ts
    • Refactored existing tests to ensure ENABLE_ENDPOINT_CIRCUIT_BREAKER is explicitly set to true for circuit breaker functionality tests.
    • Added new test cases to verify that circuit breaker functions (isEndpointCircuitOpen, recordEndpointFailure, recordEndpointSuccess, triggerEndpointCircuitBreakerAlert) are no-ops when ENABLE_ENDPOINT_CIRCUIT_BREAKER is false.
    • Added tests for initEndpointCircuitBreaker to confirm it clears stale Redis keys when disabled and does nothing when enabled.
  • tests/unit/lib/provider-endpoints/endpoint-selector.test.ts
    • Refactored existing tests to ensure ENABLE_ENDPOINT_CIRCUIT_BREAKER is explicitly set to true for endpoint selection tests.
    • Added new test cases to verify that getPreferredProviderEndpoints skips circuit checks and getEndpointFilterStats returns circuitOpen=0 when ENABLE_ENDPOINT_CIRCUIT_BREAKER is false.
Activity
  • Ran bun run typecheck and it passed.
  • Executed bun run test with 2221 tests passed and 0 failed.
  • Confirmed bun run lint resulted in a clean output.
  • Successfully completed bun run build.
  • Manually tested setting ENABLE_ENDPOINT_CIRCUIT_BREAKER=false to verify endpoint circuit breaker inactivity.
  • Manually tested setting ENABLE_ENDPOINT_CIRCUIT_BREAKER=true to verify endpoint circuit breaker activity.
  • Manually simulated a 524 timeout to confirm the presence of vendor_type_all_timeout in the UI timeline chain entry.
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
Copy link
Contributor

🧪 测试结果

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

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

@github-actions github-actions bot added the size/L Large PR (< 1000 lines) label Feb 12, 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

This pull request introduces several valuable improvements. Making the endpoint circuit breaker default to OFF via an environment variable is a safe and flexible approach for feature rollout. The addition of 524 timeout auditing to the decision chain significantly enhances observability and debugging capabilities. Furthermore, the internationalization fixes by replacing hardcoded strings with i18n keys are a great step towards better maintainability. The code is well-tested, covering the new logic extensively. I have a few suggestions to improve code consistency and maintainability.

Comment on lines +415 to +419
const detailsText = f.details
? t(`filterDetails.${f.details}`) !== `filterDetails.${f.details}`
? t(`filterDetails.${f.details}`)
: f.details
: f.reason;
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

For consistency and better readability, consider using t.has() to check for the existence of a translation key, similar to how it's used in LogicTraceTab.tsx. The current method t(...) !== ... works, but t.has() is more explicit about the intent.

Suggested change
const detailsText = f.details
? t(`filterDetails.${f.details}`) !== `filterDetails.${f.details}`
? t(`filterDetails.${f.details}`)
: f.details
: f.reason;
const detailsText = f.details
? t.has(`filterDetails.${f.details}`)
? t(`filterDetails.${f.details}`)
: f.details
: f.reason;

Comment on lines +755 to +793
if (item.reason === "vendor_type_all_timeout") {
timeline += `${t("timeline.vendorTypeAllTimeout")}\n\n`;

if (item.errorDetails?.provider) {
const p = item.errorDetails.provider;
timeline += `${t("timeline.provider", { provider: p.name })}\n`;
timeline += `${t("timeline.statusCode", { code: p.statusCode })}\n`;
timeline += `${t("timeline.error", { error: p.statusText })}\n`;

if (i > 0 && item.timestamp && chain[i - 1]?.timestamp) {
const duration = item.timestamp - (chain[i - 1]?.timestamp || 0);
timeline += `${t("timeline.requestDuration", { duration })}\n`;
}

if (p.upstreamParsed) {
timeline += `\n${t("timeline.errorDetails")}:\n`;
timeline += JSON.stringify(p.upstreamParsed, null, 2);
} else if (p.upstreamBody) {
timeline += `\n${t("timeline.errorDetails")}:\n${p.upstreamBody}`;
}

if (item.errorDetails?.request) {
timeline += formatRequestDetails(item.errorDetails.request, t);
}
} else {
timeline += `${t("timeline.provider", { provider: item.name })}\n`;
if (item.statusCode) {
timeline += `${t("timeline.statusCode", { code: item.statusCode })}\n`;
}
timeline += `${t("timeline.error", { error: item.errorMessage || t("timeline.unknown") })}\n`;

if (item.errorDetails?.request) {
timeline += formatRequestDetails(item.errorDetails.request, t);
}
}

timeline += `\n${t("timeline.vendorTypeAllTimeoutNote")}`;
continue;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

This block for handling vendor_type_all_timeout seems to duplicate a lot of the logic for formatting provider error details that likely exists for other error reasons like retry_failed. To improve maintainability and reduce code duplication, consider extracting the common logic for rendering provider error details (provider name, status code, error message, duration, upstream body, request details) into a shared helper function.

Comment on lines +117 to +120
const { getEnvConfig } = await import("@/lib/config/env.schema");
if (!getEnvConfig().ENABLE_ENDPOINT_CIRCUIT_BREAKER) {
return false;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

To improve maintainability and reduce code repetition, consider refactoring the repeated dynamic import and check for ENABLE_ENDPOINT_CIRCUIT_BREAKER. This pattern appears in isEndpointCircuitOpen, recordEndpointFailure, recordEndpointSuccess, and triggerEndpointCircuitBreakerAlert in this file, as well as in src/lib/provider-endpoints/endpoint-selector.ts. You could cache the configuration promise at the module level to avoid multiple import() calls.

For example:

// At module scope
const envConfigPromise = import("@/lib/config/env.schema").then(m => m.getEnvConfig());

async function isEndpointCbEnabled(): Promise<boolean> {
    return (await envConfigPromise).ENABLE_ENDPOINT_CIRCUIT_BREAKER;
}

// In your functions
export async function isEndpointCircuitOpen(endpointId: number): Promise<boolean> {
    if (!(await isEndpointCbEnabled())) {
        return false;
    }
    // ...
}

Comment on lines +432 to +439
scan: vi
.fn()
.mockResolvedValueOnce([
"0",
["endpoint_circuit_breaker:state:1", "endpoint_circuit_breaker:state:2"],
]),
del: vi.fn(async () => {}),
};
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

This test for initEndpointCircuitBreaker is a great start. To make it more robust, consider enhancing the redis.scan mock to test the pagination logic in the do...while loop. Currently, it only covers a single scan call because the mocked cursor is immediately '0'. You could chain mockResolvedValueOnce to simulate multiple pages of keys.

Example of a multi-page scan mock:

const redisMock = {
  scan: vi.fn()
    .mockResolvedValueOnce(["1", ["key1", "key2"]]) // Page 1
    .mockResolvedValueOnce(["0", ["key3"]]),       // Page 2 (last page)
  del: vi.fn().mockResolvedValue(1),
};

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: 7

Caution

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

⚠️ Outside diff range comments (1)
src/lib/endpoint-circuit-breaker.ts (1)

109-114: ⚠️ Potential issue | 🟡 Minor

getEndpointHealthInforesetEndpointCircuit 缺少特性开关保护需要说明设计意图

其他四个公开函数(isEndpointCircuitOpenrecordEndpointFailurerecordEndpointSuccesstriggerEndpointCircuitBreakerAlert)均添加了 ENABLE_ENDPOINT_CIRCUIT_BREAKER 守卫,但这两个函数没有。如果此设计是为了即使在特性关闭时也允许查看或重置状态,建议添加注释说明,特别是 resetEndpointCircuitprobe.ts 中被调用且不检查特性开关。

🤖 Fix all issues with AI agents
In `@messages/ru/provider-chain.json`:
- Around line 72-77: The translation for the key rate_limited is inconsistent:
update filterDetails.rate_limited (in messages/ru/provider-chain.json) to match
filterReasons.rate_limited by replacing "Ограничение стоимости" with the correct
"Ограничение скорости" so both entries use the same "rate limit" wording; verify
no other duplicate keys (e.g., filterDetails.rate_limited vs
filterReasons.rate_limited) remain inconsistent.
- Around line 41-42: The Russian translation for the key "endpointPoolExhausted"
is grammatically incorrect ("Пул конечная точкаов"); update the value for
"endpointPoolExhausted" to use the correct genitive plural form "Пул конечных
точек исчерпан" so it matches other occurrences (e.g., the form used for
"vendorTypeAllTimeout"); ensure only the string value for the symbol
"endpointPoolExhausted" is replaced.
- Around line 208-210: The value for the "strictBlockSelectorError" message
contains a Russian typo "конечная точкаов"; update the translation to correct
Russian grammar (e.g., replace with "ошибка селектора конечных точек") so the
full string reads like "Строгий режим: ошибка селектора конечных точек,
провайдер пропущен без отката". Also scan for and fix other identical typos
elsewhere (keys around the file that currently contain "конечная
точкаов"/"конечная точкаы"/"конечная точкаа") in follow-up changes.
- Around line 54-55: The translation for the JSON key "endpoint_pool_exhausted"
contains a typo ("Пул конечная точкаов исчерпан"); update the value for
endpoint_pool_exhausted to the correct Russian phrase "Пул конечных точек
исчерпан" (leave vendor_type_all_timeout unchanged) so the file's entries for
"endpoint_pool_exhausted" and "vendor_type_all_timeout" are accurate.

In `@messages/zh-CN/provider-chain.json`:
- Around line 72-77: The translation for filterDetails.rate_limited is
inconsistent with filterReasons.rate_limited; update the value of
filterDetails.rate_limited to "速率限制" to match filterReasons.rate_limited, or if
the meanings differ, rename one of the keys (e.g., filterDetails.fee_limited)
and provide an appropriate translation to avoid ambiguity; locate these keys in
the JSON (filterDetails.rate_limited and filterReasons.rate_limited) and make
the translations/keys consistent.

In `@messages/zh-TW/provider-chain.json`:
- Around line 72-77: filterDetails.rate_limited 的中文翻译错误,将当前值 "費用限制" 改为与同文件中
filterReasons.rate_limited 一致的 "速率限制";定位并更新键名 filterDetails.rate_limited
的值,确保与其他相同语义的键(例如 filterReasons.rate_limited)保持一致。

In `@src/instrumentation.ts`:
- Around line 352-360: The development branch is missing the same endpoint
circuit breaker initialization used in production; add an await import and call
to initEndpointCircuitBreaker() inside the dev-environment block (the same
pattern used for startEndpointProbeScheduler and startEndpointProbeLogCleanup)
and wrap it in the same try/catch that logs via logger.warn using error
instanceof Error ? error.message : String(error) so the dev path cleans up Redis
keys and mirrors production behavior.
🧹 Nitpick comments (5)
src/lib/provider-endpoints/endpoint-selector.ts (1)

44-48: 建议将 getEnvConfig 改为顶层导入。

getPreferredProviderEndpoints 是每次请求都会调用的热路径函数。虽然 await import(...) 在首次加载后会被运行时缓存,但此文件已标记为 "server-only",可以安全地在顶层导入 getEnvConfig,避免不必要的异步开销,且与顶层已有的 isEndpointCircuitOpen 导入风格一致。

建议的重构
 import "server-only";
 
 import { isEndpointCircuitOpen } from "@/lib/endpoint-circuit-breaker";
+import { getEnvConfig } from "@/lib/config/env.schema";
 import { findProviderEndpointsByVendorAndType } from "@/repository";
 import type { ProviderEndpoint, ProviderType } from "@/types/provider";

然后在两处使用中去掉动态导入:

-  const { getEnvConfig } = await import("@/lib/config/env.schema");
   if (!getEnvConfig().ENABLE_ENDPOINT_CIRCUIT_BREAKER) {
src/lib/endpoint-circuit-breaker.ts (1)

117-120: 重复的动态导入模式可考虑提取为内部辅助函数。

五个被保护的函数中都重复了相同的动态导入和特性开关检查逻辑。可考虑提取为一个内部辅助函数以减少重复。

♻️ 建议的重构方案
+async function isEndpointCBEnabled(): Promise<boolean> {
+  const { getEnvConfig } = await import("@/lib/config/env.schema");
+  return getEnvConfig().ENABLE_ENDPOINT_CIRCUIT_BREAKER;
+}
+
 export async function isEndpointCircuitOpen(endpointId: number): Promise<boolean> {
-  const { getEnvConfig } = await import("@/lib/config/env.schema");
-  if (!getEnvConfig().ENABLE_ENDPOINT_CIRCUIT_BREAKER) {
+  if (!(await isEndpointCBEnabled())) {
     return false;
   }
   // ...

Also applies to: 143-146, 191-194, 258-261, 312-315

tests/unit/lib/provider-endpoints/endpoint-selector.test.ts (1)

27-27: 测试名称风格不一致:已有用例使用中文,新增用例使用英文。

已有的测试描述使用中文(如第 27、91 行),而新增的测试描述使用英文(如第 264、302 行)。建议统一风格以提高可读性和一致性。

Also applies to: 91-91, 264-264, 302-302

src/lib/utils/provider-chain-formatter.ts (1)

415-420: t() 被调用了两次,可简化为一次。

t(\filterDetails.${f.details}`)` 被调用了两次:第一次用于判断翻译是否存在,第二次用于获取翻译值。可以用临时变量避免重复调用。

♻️ 建议的优化
-          const detailsText = f.details
-            ? t(`filterDetails.${f.details}`) !== `filterDetails.${f.details}`
-              ? t(`filterDetails.${f.details}`)
-              : f.details
-            : f.reason;
+          let detailsText: string;
+          if (f.details) {
+            const key = `filterDetails.${f.details}`;
+            const translated = t(key);
+            detailsText = translated !== key ? translated : f.details;
+          } else {
+            detailsText = f.reason;
+          }
tests/unit/lib/endpoint-circuit-breaker.test.ts (1)

80-82: fake timers 下动态 import 缓存预热的处理方式值得注意。

vi.useFakeTimers() 环境下,对 await import("@/lib/config/env.schema") 进行预热以确保模块缓存命中是必要的 workaround。注释说明清晰,有助于后续维护者理解。但这也暗示了生产代码中每次调用都执行动态导入可能引入的微妙时序依赖——进一步支持在源码中提取 isEndpointCBEnabled() 辅助函数的建议。

Also applies to: 284-288

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.

PR Size: L

  • Lines changed: 691
  • Files changed: 22
  • Split suggestions:
    • Extract endpoint circuit breaker gating + init cleanup + env var/docs/tests.
    • Extract 524 decision-chain recording + new vendor_type_all_timeout reason + formatter/UI rendering.
    • Extract i18n message updates (messages/*/provider-chain.json) into a dedicated PR.

Review Coverage

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

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.

  • Reviewed PR #773 (head feat/endpoint-cb-default-off-and-524-chain → base dev) using the diff-only + multi-perspective criteria.
  • Classified PR size as L (+657/-34, 22 files) and applied label size/L.
  • No issues met the report threshold, so no inline review comments were posted; submitted the required summary review comment to the PR (including split suggestions).

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.

Review submitted

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 makes two solid operational improvements: gating the endpoint circuit breaker behind an opt-in env var and closing the 524 timeout visibility gap in the decision chain. The i18n fix for hardcoded Chinese strings is a welcome cleanup. One minor logic issue found in the i18n fallback pattern.

PR Size: L

  • Lines changed: 691 (657 additions, 34 deletions)
  • Files changed: 22

Issues Found

Category Critical High Medium Low
Logic/Bugs 0 0 1 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 0 0 0
Simplification 0 0 0 0

High Priority Issues (Should Fix)

  1. provider-chain-formatter.ts:416 - i18n fallback check t(key) \!== key does not account for the project's getMessageFallback which returns namespace.key (see inline comment). Low practical impact since all current keys exist, but the fallback path is broken for any future unknown detail values.

Review Coverage

  • Logic and correctness
  • Security (OWASP Top 10) - Clean
  • Error handling - Clean
  • Type safety - Clean
  • Documentation accuracy - Clean
  • Test coverage - Adequate (396 new test lines covering CB disable gating, selector bypass, and formatter)
  • Code clarity - Good

Automated review by Claude AI

const icon = f.reason === "circuit_open" ? "⚡" : "💰";
timeline += ` ${icon} ${f.name} (${f.details || f.reason})\n`;
const detailsText = f.details
? t(`filterDetails.${f.details}`) !== `filterDetails.${f.details}`
Copy link
Contributor

Choose a reason for hiding this comment

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

[Medium] [LOGIC-BUG] i18n fallback check t(key) !== key is incorrect given the project's getMessageFallback configuration

Why this is a problem: In src/i18n/request.ts:40-42, the getMessageFallback is configured to return ${namespace}.${key} (e.g., provider-chain.filterDetails.circuit_open). However, this fallback check compares against the key without the namespace prefix (filterDetails.circuit_open). Since the fallback return value will always differ from the bare key, this condition will always be truthy, and the raw f.details fallback will never be reached for unknown keys.

In contrast, LogicTraceTab.tsx correctly uses tChain.has() which is the proper next-intl API for key existence checks.

Currently all 4 filterDetails keys exist in all 5 language files, so this only affects future unknown detail values. When triggered, the UI would display the namespace-prefixed key (e.g., provider-chain.filterDetails.unknown_key) instead of the raw value.

Suggested fix:

const detailsText = f.details
  ? (() => {
      const translated = t(`filterDetails.${f.details}`);
      // getMessageFallback returns namespace.key for missing keys,
      // so check if the result ends with the lookup key
      return translated.endsWith(`filterDetails.${f.details}`)
        ? f.details
        : translated;
    })()
  : f.reason;

Alternatively, consider passing a has-like capability to the formatter, or simply accept the current known keys are covered and add a comment documenting this limitation.

- Fix Russian: "конечная точкаов" -> "конечных точек" (11 occurrences)
- Fix Russian: "Ограничение стоимости" -> "Ограничение скорости" (rate_limited)
- Fix zh-CN: "费用限制" -> "速率限制" (filterDetails.rate_limited)
- Fix zh-TW: "費用限制" -> "速率限制" (filterDetails.rate_limited)
- Add initEndpointCircuitBreaker() to dev environment in instrumentation.ts
@github-actions
Copy link
Contributor

🧪 测试结果

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

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

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: 1

🤖 Fix all issues with AI agents
In `@messages/ru/provider-chain.json`:
- Line 42: The string for description.vendorTypeAllTimeout is missing the "типа
поставщика" qualifier and should match other entries
(reasons.vendor_type_all_timeout and timeline.vendorTypeAllTimeout); update the
value of description.vendorTypeAllTimeout to include "типа поставщика" so it
reads the same as the other locales and entries (e.g., "Тайм-аут всех конечных
точек типа поставщика").

"clientError": "Ошибка клиента",
"endpointPoolExhausted": "Пул конечная точкаов исчерпан"
"endpointPoolExhausted": "Пул конечных точек исчерпан",
"vendorTypeAllTimeout": "Тайм-аут всех конечных точек"
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

description.vendorTypeAllTimeout 缺少"типа поставщика"(供应商类型),与其他 locale 不一致。

对比同文件第 55 行 reasons.vendor_type_all_timeout("Тайм-аут всех конечных точек типа поставщика")和第 209 行 timeline.vendorTypeAllTimeout(同样包含 "типа поставщика"),以及 zh-TW 第 42 行("供應商類型全端點逾時"),此处第 42 行的 description.vendorTypeAllTimeout 仅为 "Тайм-аут всех конечных точек",缺少"供应商类型"的限定。

建议修复
-    "vendorTypeAllTimeout": "Тайм-аут всех конечных точек"
+    "vendorTypeAllTimeout": "Тайм-аут всех конечных точек типа поставщика"
🤖 Prompt for AI Agents
In `@messages/ru/provider-chain.json` at line 42, The string for
description.vendorTypeAllTimeout is missing the "типа поставщика" qualifier and
should match other entries (reasons.vendor_type_all_timeout and
timeline.vendorTypeAllTimeout); update the value of
description.vendorTypeAllTimeout to include "типа поставщика" so it reads the
same as the other locales and entries (e.g., "Тайм-аут всех конечных точек типа
поставщика").

@ding113 ding113 merged commit aebb722 into dev Feb 12, 2026
12 checks passed
@github-project-automation github-project-automation bot moved this from Backlog to Done in Claude Code Hub Roadmap Feb 12, 2026
ding113 added a commit that referenced this pull request Feb 12, 2026
* fix(circuit-breaker): key errors should not trip endpoint circuit breaker

Remove 3 recordEndpointFailure calls from response-handler streaming
error paths (fake-200, non-200 HTTP, stream abort). These are key-level
errors where the endpoint itself responded successfully. Only
forwarder-level failures (timeout, network error) and probe failures
should penalize the endpoint circuit breaker.

Previously, a single bad API key could trip the endpoint breaker
(threshold=3, open=5min), making ALL keys on that endpoint unavailable.

* chore: format code (dev-3d584e5)

* Merge pull request #767 from ding113/fix/provider-clone-deep-copy

fix: 修复供应商克隆时因浅拷贝引用共享导致源供应商数据被意外污染的问题

* 增强配置表单输入警告提示 (#768)

* feat: 增强配置表单输入警告提示

* fix: 修复 expiresAt 显示与配额刷新输入边界

* fix: 修复 expiresAt 解析兜底并改善刷新间隔输入体验

* fix: 刷新间隔输入取整并复用 clamp

---------

Co-authored-by: tesgth032 <tesgth032@users.noreply.github.com>

* feat(circuit-breaker): endpoint CB default-off + 524 decision chain audit (#773)

* feat(circuit-breaker): endpoint circuit breaker default-off + 524 decision chain audit

- Add ENABLE_ENDPOINT_CIRCUIT_BREAKER env var (default: false) to gate endpoint-level circuit breaker
- Gate isEndpointCircuitOpen, recordEndpointFailure, recordEndpointSuccess, triggerEndpointCircuitBreakerAlert behind env switch
- Add initEndpointCircuitBreaker() startup cleanup: clear stale Redis keys when feature disabled
- Gate endpoint filtering in endpoint-selector (getPreferredProviderEndpoints, getEndpointFilterStats)
- Fix 524 vendor-type timeout missing from decision chain: add chain entry with reason=vendor_type_all_timeout in forwarder
- Add vendor_type_all_timeout to ProviderChainItem reason union type (both backend session.ts and frontend message.ts)
- Add timeline rendering for vendor_type_all_timeout in provider-chain-formatter
- Replace hardcoded Chinese strings in provider-selector circuit_open details with i18n keys
- Add i18n translations for vendor_type_all_timeout and filterDetails (5 languages: zh-CN, zh-TW, en, ja, ru)
- Enhance LogicTraceTab to render filterDetails via i18n lookup with fallback
- Add endpoint_pool_exhausted and vendor_type_all_timeout to provider-chain-popover isActualRequest/getItemStatus
- Add comprehensive unit tests for all changes (endpoint-circuit-breaker, endpoint-selector, provider-chain-formatter)

* fix(i18n): fix Russian grammar errors and rate_limited translations

- Fix Russian: "конечная точкаов" -> "конечных точек" (11 occurrences)
- Fix Russian: "Ограничение стоимости" -> "Ограничение скорости" (rate_limited)
- Fix zh-CN: "费用限制" -> "速率限制" (filterDetails.rate_limited)
- Fix zh-TW: "費用限制" -> "速率限制" (filterDetails.rate_limited)
- Add initEndpointCircuitBreaker() to dev environment in instrumentation.ts

* fix(circuit-breaker): vendor type CB respects ENABLE_ENDPOINT_CIRCUIT_BREAKER

Make vendor type circuit breaker controlled by the same
ENABLE_ENDPOINT_CIRCUIT_BREAKER switch as endpoint circuit breaker.
When disabled (default), vendor type CB will never trip or block
providers, resolving user confusion about "vendor type temporary
circuit breaker" skip reasons in decision chain.

Changes:
- Add ENABLE_ENDPOINT_CIRCUIT_BREAKER check in isVendorTypeCircuitOpen()
- Add switch check in recordVendorTypeAllEndpointsTimeout()
- Add tests for switch on/off behavior

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

* 修复 Key 并发限制继承用户并发上限 (#772)

* fix: Key 并发上限默认继承用户限制

- RateLimitGuard:Key limitConcurrentSessions=0 时回退到 User limitConcurrentSessions\n- Key 配额/使用量接口:并发上限按有效值展示\n- 单测覆盖并发继承逻辑;补齐 probe 测试的 endpoint-circuit-breaker mock 导出\n- 同步更新 biome.json schema 版本以匹配当前 Biome CLI

* docs: 补齐并发上限解析工具注释

* refactor: 合并 Key 限额查询并补充并发单测

- getKeyQuotaUsage/getKeyLimitUsage:通过 leftJoin 一次取回 User 并发上限,避免额外查询\n- 新增 resolveKeyConcurrentSessionLimit 单测,覆盖关键分支\n- 修复 vacuum-filter bench 中的 Biome 报错

* fix: my-usage 并发上限继承用户限制

- getMyQuota:Key 并发为 0/null 时回退到 User 并发上限,保持与 Guard/Key 配额一致\n- 新增单测覆盖 Key->User 并发继承

* test: 补齐 my-usage 并发继承场景

- MyUsageQuota.keyLimitConcurrentSessions 收敛为 number(0 表示无限制)\n- OpenAPI 响应 schema 同步为非 nullable\n- my-usage 并发继承测试补充 Key>0 与 User=0 场景

---------

Co-authored-by: tesgth032 <tesgth032@users.noreply.github.com>

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: hank9999 <hank9999@qq.com>
Co-authored-by: tesgth032 <tesgth032@hotmail.com>
Co-authored-by: tesgth032 <tesgth032@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
@github-actions github-actions bot mentioned this pull request Feb 12, 2026
5 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

1 participant