Skip to content

feat: 可选拦截 Anthropic Warmup 请求#526

Closed
ding113 wants to merge 1 commit into
devfrom
feat/anthropic-warmup-intercept
Closed

feat: 可选拦截 Anthropic Warmup 请求#526
ding113 wants to merge 1 commit into
devfrom
feat/anthropic-warmup-intercept

Conversation

@ding113

@ding113 ding113 commented Jan 3, 2026

Copy link
Copy Markdown
Owner

概述 / Summary

可选拦截 Anthropic Warmup 请求,避免不必要的上游调用与 token 浪费。

Related PRs:


背景:Claude/Anthropic 请求偶发 Warmup(user.content 含 Warmup + ephemeral),会产生不必要的上游请求与 token 浪费。

改动:

  • 新增系统设置开关 enableAnthropicWarmupIntercept(默认关闭)。
  • Proxy guard pipeline 增加 warmup guard:仅对 Claude 格式命中 Warmup 时,本地抢答并短路后续流程。
  • 非流式/流式响应均添加 x-cch-intercepted: warmup 标记头。
  • 写入请求日志但不计费:provider_id=0、cost_usd=0、blocked_by=warmup,便于审计。
  • 日志列表费用栏与详情弹窗明确标注"已跳过(CCH 抢答)"。
  • 统计/概览/排行榜/Key 等聚合口径排除 blocked_by=warmup,不计入任何统计。

验证:bun run lint/typecheck/test/build/test --coverage

Greptile Summary

This PR implements optional interception of Anthropic warmup requests to prevent unnecessary upstream API calls and token waste. When enabled via the enableAnthropicWarmupIntercept system setting, the proxy detects Claude warmup requests (identified by user.content containing "Warmup" with cache_control.type="ephemeral") and returns a local mock response instead of forwarding to upstream providers.

Key Changes:

  • Added warmup detection and response building logic in warmup-intercept.ts with support for both streaming (SSE) and non-streaming (JSON) formats
  • Integrated ProxyWarmupGuard into the guard pipeline after session initialization, before request filtering
  • Intercepted requests are logged to the database with provider_id=0, cost_usd=0, and blocked_by=warmup for audit purposes
  • All statistics, leaderboard, overview, and usage queries comprehensively exclude warmup requests using blocked_by IS DISTINCT FROM 'warmup' filters
  • Session aggregation uses FILTER clauses to exclude warmup from statistics while maintaining session existence checks with requestCountAll
  • UI displays "Skipped" label for warmup requests in logs with orange highlighting and localized labels in detail dialogs
  • Comprehensive test coverage validates warmup detection logic, response formatting, and edge cases
  • Database migration adds the new setting with safe IF NOT EXISTS clause and default false value

Confidence Score: 5/5

  • This PR is safe to merge with minimal risk
  • The implementation is well-architected with proper separation of concerns, comprehensive test coverage, and defensive coding practices. All database queries properly exclude warmup requests from statistics using consistent filtering. The feature is opt-in (default disabled), reducing deployment risk. Edge cases are handled appropriately, and the PR includes proper i18n support across multiple languages.
  • No files require special attention

Important Files Changed

Filename Overview
src/app/v1/_lib/proxy/warmup-intercept.ts New utility module for detecting and building warmup intercept responses with proper SSE/JSON formatting
src/app/v1/_lib/proxy/warmup-guard.ts New guard that intercepts warmup requests, logs them with provider_id=0 and blocked_by=warmup, and stores session data
src/app/v1/_lib/proxy/guard-pipeline.ts Added warmup guard step to CHAT_PIPELINE after session initialization, before request filtering
src/repository/statistics.ts Added blocked_by IS DISTINCT FROM 'warmup' filter to all statistics queries across multiple time ranges
src/repository/leaderboard.ts Excluded warmup requests from all leaderboard queries (user, provider, cache hit rate, model)
src/repository/message.ts Added FILTER clauses to exclude warmup from session aggregations while preserving existence checks with requestCountAll
tests/unit/proxy/warmup-intercept.test.ts Comprehensive test coverage for warmup detection, SSE/JSON response building, and stream flag detection

Sequence Diagram

sequenceDiagram
    participant Client
    participant GuardPipeline
    participant WarmupGuard
    participant SystemSettings
    participant WarmupIntercept
    participant SessionManager
    participant Database
    participant ProviderResolver

    Client->>GuardPipeline: POST /v1/messages (Claude format)
    GuardPipeline->>GuardPipeline: Execute auth, client, model, version, probe, session steps
    
    GuardPipeline->>WarmupGuard: ensure(session)
    WarmupGuard->>WarmupGuard: Check if originalFormat === "claude"
    
    alt originalFormat is not claude
        WarmupGuard-->>GuardPipeline: return null (skip)
    else originalFormat is claude
        WarmupGuard->>SystemSettings: getCachedSystemSettings()
        SystemSettings-->>WarmupGuard: settings
        
        alt enableAnthropicWarmupIntercept is false
            WarmupGuard-->>GuardPipeline: return null (skip)
        else enableAnthropicWarmupIntercept is true
            WarmupGuard->>WarmupIntercept: isClaudeWarmupRequestBody(request.message)
            WarmupIntercept-->>WarmupGuard: boolean
            
            alt Not a warmup request
                WarmupGuard-->>GuardPipeline: return null (continue)
            else Is warmup request
                WarmupGuard->>WarmupIntercept: getClaudeStreamFlag(message, accept header)
                WarmupIntercept-->>WarmupGuard: stream flag
                
                WarmupGuard->>WarmupIntercept: buildClaudeWarmupInterceptResponse({model, stream})
                WarmupIntercept->>WarmupIntercept: buildWarmupMessageResponse()
                
                alt stream is true
                    WarmupIntercept->>WarmupIntercept: buildClaudeWarmupSse()
                    WarmupIntercept-->>WarmupGuard: {response (SSE), responseBodyForStore, responseHeaders}
                else stream is false
                    WarmupIntercept-->>WarmupGuard: {response (JSON), responseBodyForStore, responseHeaders}
                end
                
                WarmupGuard->>SessionManager: storeSessionResponse(sessionId, body, sequence)
                WarmupGuard->>SessionManager: storeSessionResponseHeaders(sessionId, headers, sequence)
                WarmupGuard->>SessionManager: storeSessionRequestHeaders(sessionId, headers, sequence)
                
                WarmupGuard->>Database: insert messageRequest
                Note right of Database: providerId=0<br/>costUsd=0<br/>blocked_by=warmup<br/>statusCode=200
                
                WarmupGuard-->>GuardPipeline: return response (short-circuit)
            end
        end
    end
    
    GuardPipeline-->>Client: Return response (if intercepted)
    
    opt If not intercepted
        GuardPipeline->>GuardPipeline: Continue with requestFilter, sensitive, rateLimit steps
        GuardPipeline->>ProviderResolver: Select provider and forward request
        ProviderResolver-->>Client: Upstream response
    end
Loading

Description enhanced by Claude AI

@coderabbitai

coderabbitai Bot commented Jan 3, 2026

Copy link
Copy Markdown

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

本PR引入Anthropic预热请求拦截功能。添加数据库列、系统配置、国际化文本、业务逻辑处理和统计查询过滤,使CCH能检测并本地响应Claude/Anthropic预热请求。

Changes

队列 / 文件 变更总结
数据库架构
drizzle/0044_add_anthropic_warmup_intercept.sql, drizzle/meta/_journal.json, src/drizzle/schema.ts
添加enableAnthropicWarmupIntercept布尔列至system_settings表,默认值为false;更新数据库迁移记录
国际化配置
messages/en/dashboard.json, messages/en/settings.json, messages/ja/dashboard.json, messages/ja/settings.json, messages/ru/dashboard.json, messages/ru/settings.json, messages/zh-CN/dashboard.json, messages/zh-CN/settings.json, messages/zh-TW/dashboard.json, messages/zh-TW/settings.json
5种语言新增UI标签:表格"skipped"列、拦截详情"warmupIntercept"以及设置表单的启用选项和描述
核心拦截逻辑
src/app/v1/_lib/proxy/warmup-intercept.ts, src/app/v1/_lib/proxy/warmup-guard.ts
实现预热请求检测、响应构建、流式传输支持和数据库持久化;新增常量、类型和公开函数
守卫管道集成
src/app/v1/_lib/proxy/guard-pipeline.ts
添加"warmup"守卫步骤至CHAT_PIPELINE,扩展GuardStepKey联合类型
系统配置
src/actions/system-config.ts, src/types/system-config.ts, src/lib/config/system-settings-cache.ts, src/repository/system-config.ts, src/lib/validation/schemas.ts
新增enableAnthropicWarmupIntercept字段至系统设置类型、验证模式和缓存;更新保存流程
UI组件
src/app/[locale]/settings/config/_components/system-settings-form.tsx, src/app/[locale]/settings/config/page.tsx
添加新开关控件以启用/禁用预热拦截功能,初始化新字段并纳入提交流程
日志和展示
src/app/[locale]/dashboard/logs/_components/error-details-dialog.tsx, src/app/[locale]/dashboard/logs/_components/usage-logs-table.tsx, src/app/[locale]/dashboard/logs/_components/virtualized-logs-table.tsx
新增对WARMUP_INTERCEPT_BLOCKED_BY的处理,在成本/状态单元显示"skipped"标签
统计和性能指标
src/repository/message.ts, src/repository/key.ts, src/repository/client-versions.ts, src/repository/leaderboard.ts, src/repository/overview.ts, src/repository/statistics.ts, src/actions/my-usage.ts
在多个聚合查询中添加blockedBy IS DISTINCT FROM 'warmup'过滤条件,排除预热请求对统计数据的影响
性能常量和工具
src/lib/utils/performance-formatter.ts
新增WARMUP_INTERCEPT_BLOCKED_BY常量定义为"warmup"
数据转换和类型
src/repository/_shared/transformers.ts
在SystemSettings映射中添加enableAnthropicWarmupIntercept字段
单元测试
tests/unit/proxy/warmup-intercept.test.ts, tests/unit/proxy/session.test.ts
新增预热拦截功能的单元测试套件;更新测试夹具以包含新配置字段

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 分钟

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 46.88% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed PR 标题准确地反映了主要变更:新增 Anthropic Warmup 请求拦截功能。
Description check ✅ Passed PR描述详细阐述了Anthropic Warmup拦截功能的背景、目标、实现方案和验证方式,与提供的变更集内容完全对应。
✨ 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/anthropic-warmup-intercept

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

@gemini-code-assist

Copy link
Copy Markdown
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!

此拉取请求旨在通过引入一个可选的 Anthropic Warmup 请求拦截机制来优化资源使用。该机制允许系统在检测到特定的 Warmup 请求时,不将其转发给上游供应商,而是直接在本地生成并返回一个响应。这不仅减少了不必要的 API 调用和潜在的费用支出,还确保了相关的日志记录和统计数据能够准确反映实际的计费请求,从而提升了系统的效率和成本控制能力。

Highlights

  • 新增 Anthropic Warmup 请求拦截功能: 引入了一个新的系统设置开关 enableAnthropicWarmupIntercept(默认关闭),允许系统识别并拦截 Anthropic/Claude 的 Warmup 请求。
  • 本地抢答与资源优化: 当检测到符合条件的 Warmup 请求时,代理管道会进行本地抢答,直接返回一个简短响应,从而避免不必要的上游请求和 token 浪费。
  • 日志与统计排除: 被拦截的 Warmup 请求会写入请求日志,但明确标记为不计费(cost_usd=0),provider_id=0blocked_by=warmup。这些请求将从所有统计数据(如费用、token 使用、排行榜、用户活跃度等)中排除,确保数据准确性。
  • 用户界面更新: 仪表盘日志列表和详情弹窗中,被拦截的 Warmup 请求将明确显示为“已跳过(CCH 抢答)”,并添加了相应的多语言支持。
  • 支持流式与非流式响应: 拦截功能兼容 Anthropic 的流式和非流式请求,均会添加 x-cch-intercepted: warmup 标记头,以便客户端识别。
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 commented Jan 3, 2026

Copy link
Copy Markdown
Contributor

🧪 测试结果

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

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

@github-actions github-actions Bot added the size/XL Extra Large PR (> 1000 lines) label Jan 3, 2026

@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

这个 PR 引入了拦截 Anthropic Warmup 请求的功能,这是一个非常好的优化,可以节省成本并减少不必要的上游流量。实现非常全面,不仅包括了拦截逻辑,还涵盖了系统设置、日志记录、UI 展示,并确保这些被拦截的请求被排除在所有统计口径之外。代码结构清晰,并且增加了新的单元测试来验证核心逻辑。

不过,我在被拦截请求的日志记录逻辑中发现了一个关键问题,它会导致这些请求无法被正确记录。具体细节请查看我的评论。修复此问题后,这将是一个非常出色的贡献。

Comment on lines +68 to +77
if (
session.authState?.success &&
session.authState.user &&
session.authState.apiKey &&
session.sessionId
) {
await db.insert(messageRequest).values({
providerId: 0, // 特殊值:表示未选择供应商(本地抢答)
userId: session.authState.user.id,
key: session.authState.apiKey,

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.

critical

在记录已拦截的 Warmup 请求日志时,存在一个关键错误。if 条件和 db.insert 调用中使用了 session.authState.apiKey,但 session.authState 对象上并不存在 apiKey 属性。authStatekey 属性是一个 Key 对象,真正的密钥字符串应该是 session.authState.key.key

这个错误会导致 if 条件始终为假,从而无法执行 db.insert 操作,所有被拦截的 Warmup 请求都不会被记入日志。这违背了此 PR 的一个主要目标(“写入请求日志但不计费...便于审计”)。

此外,if 条件中的 session.authState.user 检查是多余的,因为 session.authState.successtrue 时,userkey 对象必然存在。

建议进行如下修改以修复此问题并简化代码:

      if (session.authState?.success && session.sessionId) {
        await db.insert(messageRequest).values({
          providerId: 0, // 特殊值:表示未选择供应商(本地抢答)
          userId: session.authState.user.id,
          key: session.authState.key.key,

@coderabbitai coderabbitai 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.

Actionable comments posted: 2

🧹 Nitpick comments (5)
src/repository/message.ts (1)

299-316: 统计过滤逻辑正确,建议使用常量提升可维护性。

使用 IS DISTINCT FROM 'warmup' 正确处理了 NULL 值情况。requestCountAll 保留了 session 存在性判断,而 requestCount 则排除 warmup 用于统计。

建议将硬编码的 'warmup' 字符串替换为 WARMUP_INTERCEPT_BLOCKED_BY 常量(已在 src/lib/utils/performance-formatter.ts 中定义),确保全局一致性。

🔎 建议的改进
import { WARMUP_INTERCEPT_BLOCKED_BY } from "@/lib/utils/performance-formatter";

// 在 SQL 中使用参数化方式
sql`${messageRequest.blockedBy} IS DISTINCT FROM ${WARMUP_INTERCEPT_BLOCKED_BY}`
src/repository/client-versions.ts (1)

45-50: 过滤逻辑正确,与其他仓库查询保持一致。

IS DISTINCT FROM 'warmup' 确保 warmup 请求不计入客户端版本统计,符合 PR 中「统计口径排除 warmup」的设计目标。

同样建议引用 WARMUP_INTERCEPT_BLOCKED_BY 常量以提升可维护性:

🔎 建议的改进
+import { WARMUP_INTERCEPT_BLOCKED_BY } from "@/lib/utils/performance-formatter";

 .where(
   and(
     isNull(messageRequest.deletedAt),
-    sql`${messageRequest.blockedBy} IS DISTINCT FROM 'warmup'`,
+    sql`${messageRequest.blockedBy} IS DISTINCT FROM ${WARMUP_INTERCEPT_BLOCKED_BY}`,
     gte(messageRequest.createdAt, cutoffDate),
     sql`${messageRequest.userAgent} IS NOT NULL`
   )
 )
src/repository/statistics.ts (1)

49-50: 统计查询对 warmup 记录的排除方式一致且语义正确

  • 在用户/Key/混合统计的原生 SQL 中,将 mr.deleted_at IS NULL 旁边统一增加 mr.blocked_by IS DISTINCT FROM 'warmup',只排除被 warmup 拦截的记录,同时保留正常与 blocked_by IS NULL 的请求,确保 COUNT(mr.id)SUM(mr.cost_usd) 都不受 warmup 干扰。
  • sumUserCostToday 同样排除 blocked_by='warmup',即便该函数标记为 deprecated,也避免历史调用把 warmup 次数纳入日用量。
  • 其它基于 SUM(cost_usd) 的 Drizzle 查询在 warmup 场景下因 cost_usd=0 自然不会增加额度,与这里的显式过滤逻辑保持整体一致性。后续如新增统计 SQL,建议复用同样的 blocked_by 过滤条件。Based on learnings, ...

Also applies to: 86-87, 123-124, 160-161, 237-238, 279-280, 321-322, 363-364, 447-448, 477-478, 521-522, 551-552, 595-596, 625-626, 668-669, 699-700, 747-748

src/app/v1/_lib/proxy/warmup-intercept.ts (2)

10-31: 建议为类型属性添加 readonly 修饰符

这些类型表示不可变的 API 响应数据结构。根据编码规范("Use readonly or const assertions for immutable data structures"),建议为所有属性添加 readonly 修饰符以增强类型安全性,防止意外修改。

🔎 建议的类型定义改进
 type ClaudeTextContentBlock = {
-  type: "text";
-  text: string;
+  readonly type: "text";
+  readonly text: string;
 };
 
 type ClaudeMessageUsage = {
-  input_tokens: number;
-  output_tokens: number;
-  cache_creation_input_tokens: number;
-  cache_read_input_tokens: number;
+  readonly input_tokens: number;
+  readonly output_tokens: number;
+  readonly cache_creation_input_tokens: number;
+  readonly cache_read_input_tokens: number;
 };
 
 type ClaudeMessageResponse = {
-  model: string;
-  id: string;
-  type: "message";
-  role: "assistant";
-  content: ClaudeTextContentBlock[];
-  stop_reason: "end_turn";
-  stop_sequence: null;
-  usage: ClaudeMessageUsage;
+  readonly model: string;
+  readonly id: string;
+  readonly type: "message";
+  readonly role: "assistant";
+  readonly content: readonly ClaudeTextContentBlock[];
+  readonly stop_reason: "end_turn";
+  readonly stop_sequence: null;
+  readonly usage: ClaudeMessageUsage;
 };

基于编码规范要求。


80-154: 响应构建逻辑实现正确

  • buildClaudeWarmupMessageResponse 正确生成零 token 消耗的合成响应,符合 PR 目标(不计费、不消耗 token)
  • buildClaudeWarmupSse 准确构建 SSE 事件序列(message_start → content_block_start → content_block_delta → content_block_stop → message_delta → message_stop),格式符合 Claude 流式 API 规范
  • Line 127 的可选链操作符使用是防御性编程,虽然 content[0] 始终存在,但这增强了代码健壮性

可选改进:考虑在 buildClaudeWarmupMessageResponse 中验证 model 参数非空,但鉴于该参数可能在上游 guard 已验证,当前实现可接受。

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Cache: Disabled due to Reviews > Disable Cache setting

📥 Commits

Reviewing files that changed from the base of the PR and between 83290bc and 2ef907d.

📒 Files selected for processing (37)
  • drizzle/0044_add_anthropic_warmup_intercept.sql
  • drizzle/meta/_journal.json
  • messages/en/dashboard.json
  • messages/en/settings.json
  • messages/ja/dashboard.json
  • messages/ja/settings.json
  • messages/ru/dashboard.json
  • messages/ru/settings.json
  • messages/zh-CN/dashboard.json
  • messages/zh-CN/settings.json
  • messages/zh-TW/dashboard.json
  • messages/zh-TW/settings.json
  • src/actions/my-usage.ts
  • src/actions/system-config.ts
  • src/app/[locale]/dashboard/logs/_components/error-details-dialog.tsx
  • src/app/[locale]/dashboard/logs/_components/usage-logs-table.tsx
  • src/app/[locale]/dashboard/logs/_components/virtualized-logs-table.tsx
  • src/app/[locale]/settings/config/_components/system-settings-form.tsx
  • src/app/[locale]/settings/config/page.tsx
  • src/app/v1/_lib/proxy/guard-pipeline.ts
  • src/app/v1/_lib/proxy/warmup-guard.ts
  • src/app/v1/_lib/proxy/warmup-intercept.ts
  • src/drizzle/schema.ts
  • src/lib/config/system-settings-cache.ts
  • src/lib/utils/performance-formatter.ts
  • src/lib/validation/schemas.ts
  • src/repository/_shared/transformers.ts
  • src/repository/client-versions.ts
  • src/repository/key.ts
  • src/repository/leaderboard.ts
  • src/repository/message.ts
  • src/repository/overview.ts
  • src/repository/statistics.ts
  • src/repository/system-config.ts
  • src/types/system-config.ts
  • tests/unit/proxy/session.test.ts
  • tests/unit/proxy/warmup-intercept.test.ts
🧰 Additional context used
📓 Path-based instructions (21)
**/*.{ts,tsx,js,jsx,json}

📄 CodeRabbit inference engine (CLAUDE.md)

Use 2-space indentation in all code files

Files:

  • src/lib/validation/schemas.ts
  • src/repository/client-versions.ts
  • drizzle/meta/_journal.json
  • messages/ja/dashboard.json
  • messages/zh-TW/settings.json
  • messages/zh-CN/settings.json
  • src/repository/leaderboard.ts
  • src/repository/key.ts
  • messages/en/dashboard.json
  • messages/ja/settings.json
  • src/lib/utils/performance-formatter.ts
  • src/repository/statistics.ts
  • messages/ru/settings.json
  • src/app/[locale]/settings/config/page.tsx
  • src/actions/system-config.ts
  • src/app/[locale]/dashboard/logs/_components/virtualized-logs-table.tsx
  • src/repository/system-config.ts
  • src/app/v1/_lib/proxy/warmup-guard.ts
  • messages/zh-TW/dashboard.json
  • messages/en/settings.json
  • src/drizzle/schema.ts
  • tests/unit/proxy/warmup-intercept.test.ts
  • src/types/system-config.ts
  • src/app/[locale]/dashboard/logs/_components/usage-logs-table.tsx
  • src/app/[locale]/dashboard/logs/_components/error-details-dialog.tsx
  • messages/ru/dashboard.json
  • src/app/v1/_lib/proxy/guard-pipeline.ts
  • src/app/[locale]/settings/config/_components/system-settings-form.tsx
  • src/repository/message.ts
  • src/actions/my-usage.ts
  • src/repository/_shared/transformers.ts
  • src/app/v1/_lib/proxy/warmup-intercept.ts
  • src/repository/overview.ts
  • src/lib/config/system-settings-cache.ts
  • tests/unit/proxy/session.test.ts
  • messages/zh-CN/dashboard.json
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx,js,jsx}: Use double quotes for strings instead of single quotes
Use trailing commas in multi-line structures
Enforce maximum line length of 100 characters
Use path alias @/* to reference files from ./src/* directory

**/*.{ts,tsx,js,jsx}: Use Biome for linting and formatting with 2-space indent, double quotes, trailing commas, and 100 character max line length
Use path alias @/* to reference files in ./src/* directory

Files:

  • src/lib/validation/schemas.ts
  • src/repository/client-versions.ts
  • src/repository/leaderboard.ts
  • src/repository/key.ts
  • src/lib/utils/performance-formatter.ts
  • src/repository/statistics.ts
  • src/app/[locale]/settings/config/page.tsx
  • src/actions/system-config.ts
  • src/app/[locale]/dashboard/logs/_components/virtualized-logs-table.tsx
  • src/repository/system-config.ts
  • src/app/v1/_lib/proxy/warmup-guard.ts
  • src/drizzle/schema.ts
  • tests/unit/proxy/warmup-intercept.test.ts
  • src/types/system-config.ts
  • src/app/[locale]/dashboard/logs/_components/usage-logs-table.tsx
  • src/app/[locale]/dashboard/logs/_components/error-details-dialog.tsx
  • src/app/v1/_lib/proxy/guard-pipeline.ts
  • src/app/[locale]/settings/config/_components/system-settings-form.tsx
  • src/repository/message.ts
  • src/actions/my-usage.ts
  • src/repository/_shared/transformers.ts
  • src/app/v1/_lib/proxy/warmup-intercept.ts
  • src/repository/overview.ts
  • src/lib/config/system-settings-cache.ts
  • tests/unit/proxy/session.test.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Use TypeScript strict mode for type safety
Use readonly or const assertions for immutable data structures

Files:

  • src/lib/validation/schemas.ts
  • src/repository/client-versions.ts
  • src/repository/leaderboard.ts
  • src/repository/key.ts
  • src/lib/utils/performance-formatter.ts
  • src/repository/statistics.ts
  • src/app/[locale]/settings/config/page.tsx
  • src/actions/system-config.ts
  • src/app/[locale]/dashboard/logs/_components/virtualized-logs-table.tsx
  • src/repository/system-config.ts
  • src/app/v1/_lib/proxy/warmup-guard.ts
  • src/drizzle/schema.ts
  • tests/unit/proxy/warmup-intercept.test.ts
  • src/types/system-config.ts
  • src/app/[locale]/dashboard/logs/_components/usage-logs-table.tsx
  • src/app/[locale]/dashboard/logs/_components/error-details-dialog.tsx
  • src/app/v1/_lib/proxy/guard-pipeline.ts
  • src/app/[locale]/settings/config/_components/system-settings-form.tsx
  • src/repository/message.ts
  • src/actions/my-usage.ts
  • src/repository/_shared/transformers.ts
  • src/app/v1/_lib/proxy/warmup-intercept.ts
  • src/repository/overview.ts
  • src/lib/config/system-settings-cache.ts
  • tests/unit/proxy/session.test.ts
src/**/*.ts

📄 CodeRabbit inference engine (AGENTS.md)

src/**/*.ts: Hash API keys using SHA-256 before storing in database, never store plaintext keys
Mask API keys and sensitive data in application logs
Validate required environment variables at startup with clear error messages

Files:

  • src/lib/validation/schemas.ts
  • src/repository/client-versions.ts
  • src/repository/leaderboard.ts
  • src/repository/key.ts
  • src/lib/utils/performance-formatter.ts
  • src/repository/statistics.ts
  • src/actions/system-config.ts
  • src/repository/system-config.ts
  • src/app/v1/_lib/proxy/warmup-guard.ts
  • src/drizzle/schema.ts
  • src/types/system-config.ts
  • src/app/v1/_lib/proxy/guard-pipeline.ts
  • src/repository/message.ts
  • src/actions/my-usage.ts
  • src/repository/_shared/transformers.ts
  • src/app/v1/_lib/proxy/warmup-intercept.ts
  • src/repository/overview.ts
  • src/lib/config/system-settings-cache.ts
src/lib/**/*.ts

📄 CodeRabbit inference engine (AGENTS.md)

Use connection pooling for database and Redis connections

Files:

  • src/lib/validation/schemas.ts
  • src/lib/utils/performance-formatter.ts
  • src/lib/config/system-settings-cache.ts
src/repository/**/*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

Use Repository pattern in src/repository/ to wrap Drizzle queries

Files:

  • src/repository/client-versions.ts
  • src/repository/leaderboard.ts
  • src/repository/key.ts
  • src/repository/statistics.ts
  • src/repository/system-config.ts
  • src/repository/message.ts
  • src/repository/_shared/transformers.ts
  • src/repository/overview.ts
src/{repository,actions}/**/*.ts

📄 CodeRabbit inference engine (AGENTS.md)

Avoid N+1 queries by using eager loading and batch queries for statistics

Files:

  • src/repository/client-versions.ts
  • src/repository/leaderboard.ts
  • src/repository/key.ts
  • src/repository/statistics.ts
  • src/actions/system-config.ts
  • src/repository/system-config.ts
  • src/repository/message.ts
  • src/actions/my-usage.ts
  • src/repository/_shared/transformers.ts
  • src/repository/overview.ts
**/*.{tsx,json}

📄 CodeRabbit inference engine (AGENTS.md)

Use next-intl for internationalization with 5 locales: en, ja, ru, zh-CN, zh-TW

Files:

  • drizzle/meta/_journal.json
  • messages/ja/dashboard.json
  • messages/zh-TW/settings.json
  • messages/zh-CN/settings.json
  • messages/en/dashboard.json
  • messages/ja/settings.json
  • messages/ru/settings.json
  • src/app/[locale]/settings/config/page.tsx
  • src/app/[locale]/dashboard/logs/_components/virtualized-logs-table.tsx
  • messages/zh-TW/dashboard.json
  • messages/en/settings.json
  • src/app/[locale]/dashboard/logs/_components/usage-logs-table.tsx
  • src/app/[locale]/dashboard/logs/_components/error-details-dialog.tsx
  • messages/ru/dashboard.json
  • src/app/[locale]/settings/config/_components/system-settings-form.tsx
  • messages/zh-CN/dashboard.json
messages/**/*.json

📄 CodeRabbit inference engine (CLAUDE.md)

Support 5 locales via next-intl: en, ja, ru, zh-CN, zh-TW with messages in messages/{locale}/*.json

Store message translations in messages/{locale}/*.json files

Files:

  • messages/ja/dashboard.json
  • messages/zh-TW/settings.json
  • messages/zh-CN/settings.json
  • messages/en/dashboard.json
  • messages/ja/settings.json
  • messages/ru/settings.json
  • messages/zh-TW/dashboard.json
  • messages/en/settings.json
  • messages/ru/dashboard.json
  • messages/zh-CN/dashboard.json
src/**/*.{tsx,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

src/**/*.{tsx,jsx}: Use lucide-react for icons, no custom SVGs
Use React's automatic escaping to prevent XSS vulnerabilities

Files:

  • src/app/[locale]/settings/config/page.tsx
  • src/app/[locale]/dashboard/logs/_components/virtualized-logs-table.tsx
  • src/app/[locale]/dashboard/logs/_components/usage-logs-table.tsx
  • src/app/[locale]/dashboard/logs/_components/error-details-dialog.tsx
  • src/app/[locale]/settings/config/_components/system-settings-form.tsx
src/actions/**/*.ts

📄 CodeRabbit inference engine (AGENTS.md)

src/actions/**/*.ts: Validate all user inputs with Zod schemas before processing
Use Server Actions in next-safe-action with OpenAPI generation for admin API endpoints
Use Next.js API Routes and Server Actions for admin operations and REST endpoints

Files:

  • src/actions/system-config.ts
  • src/actions/my-usage.ts
src/app/v1/_lib/**/*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

Guard pipeline must execute in order: ProxyAuthenticator, SensitiveWordGuard, VersionGuard, ProxySessionGuard, ProxyRateLimitGuard, ProxyProviderResolver

Files:

  • src/app/v1/_lib/proxy/warmup-guard.ts
  • src/app/v1/_lib/proxy/guard-pipeline.ts
  • src/app/v1/_lib/proxy/warmup-intercept.ts
src/app/v1/_lib/proxy/**/*.ts

📄 CodeRabbit inference engine (AGENTS.md)

src/app/v1/_lib/proxy/**/*.ts: Implement guard pipeline pattern for cross-cutting concerns in request processing (auth, rate limiting, session)
Use undici library for HTTP requests instead of node-fetch for better performance

Files:

  • src/app/v1/_lib/proxy/warmup-guard.ts
  • src/app/v1/_lib/proxy/guard-pipeline.ts
  • src/app/v1/_lib/proxy/warmup-intercept.ts
src/**/*{guard,auth}*.ts

📄 CodeRabbit inference engine (AGENTS.md)

Use constant-time comparison for API key validation to prevent timing attacks

Files:

  • src/app/v1/_lib/proxy/warmup-guard.ts
  • src/app/v1/_lib/proxy/guard-pipeline.ts
src/app/**/*.ts

📄 CodeRabbit inference engine (AGENTS.md)

Implement Content-Security-Policy headers for XSS prevention

Files:

  • src/app/v1/_lib/proxy/warmup-guard.ts
  • src/app/v1/_lib/proxy/guard-pipeline.ts
  • src/app/v1/_lib/proxy/warmup-intercept.ts
src/app/v1/_lib/proxy/**/*guard*.ts

📄 CodeRabbit inference engine (AGENTS.md)

Implement fail-open strategy: allow requests when Redis is unavailable

Files:

  • src/app/v1/_lib/proxy/warmup-guard.ts
  • src/app/v1/_lib/proxy/guard-pipeline.ts
src/app/v1/**/*.ts

📄 CodeRabbit inference engine (AGENTS.md)

Use Hono router for ultrafast, lightweight routing in proxy endpoints

Files:

  • src/app/v1/_lib/proxy/warmup-guard.ts
  • src/app/v1/_lib/proxy/guard-pipeline.ts
  • src/app/v1/_lib/proxy/warmup-intercept.ts
src/drizzle/**/*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

Use Drizzle ORM with PostgreSQL for database operations

src/drizzle/**/*.ts: Use Drizzle ORM with parameterized queries to prevent SQL injection
Use soft delete pattern with deletedAt column instead of hard deletes
Use JSON columns in PostgreSQL for flexible data structures (modelRedirects, providerChain, etc.)
Implement proper indexing strategy for common queries and foreign keys

Files:

  • src/drizzle/schema.ts
**/*.test.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use Vitest for unit testing with Node environment, coverage thresholds: 50% lines/functions, 40% branches

Files:

  • tests/unit/proxy/warmup-intercept.test.ts
  • tests/unit/proxy/session.test.ts
**/*.test.ts

📄 CodeRabbit inference engine (AGENTS.md)

Ensure test database names contain 'test' keyword for safety validation

Files:

  • tests/unit/proxy/warmup-intercept.test.ts
  • tests/unit/proxy/session.test.ts
src/**/*{message,response,log}*.ts

📄 CodeRabbit inference engine (AGENTS.md)

Log request duration, token usage, and cost to message_request table for analytics

Files:

  • src/repository/message.ts
🧠 Learnings (12)
📚 Learning: 2026-01-03T09:08:20.573Z
Learnt from: CR
Repo: ding113/claude-code-hub PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-03T09:08:20.573Z
Learning: Applies to messages/**/*.json : Support 5 locales via next-intl: en, ja, ru, zh-CN, zh-TW with messages in `messages/{locale}/*.json`

Applied to files:

  • messages/ja/dashboard.json
  • messages/zh-TW/settings.json
  • messages/zh-CN/settings.json
  • messages/ja/settings.json
  • messages/ru/settings.json
  • messages/en/settings.json
📚 Learning: 2026-01-03T09:08:49.019Z
Learnt from: CR
Repo: ding113/claude-code-hub PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-03T09:08:49.019Z
Learning: Applies to src/{repository,actions}/**/*.ts : Avoid N+1 queries by using eager loading and batch queries for statistics

Applied to files:

  • src/repository/key.ts
  • src/repository/statistics.ts
  • src/repository/message.ts
📚 Learning: 2026-01-03T09:08:49.019Z
Learnt from: CR
Repo: ding113/claude-code-hub PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-03T09:08:49.019Z
Learning: Applies to src/**/*{message,response,log}*.ts : Log request duration, token usage, and cost to message_request table for analytics

Applied to files:

  • src/lib/utils/performance-formatter.ts
  • src/app/[locale]/dashboard/logs/_components/virtualized-logs-table.tsx
  • src/app/[locale]/dashboard/logs/_components/usage-logs-table.tsx
  • src/repository/message.ts
📚 Learning: 2026-01-03T09:08:20.573Z
Learnt from: CR
Repo: ding113/claude-code-hub PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-03T09:08:20.573Z
Learning: Applies to src/lib/rate-limit/**/*.ts : Rate Limiting must track dimensions: RPM, cost (5h/week/month), concurrent sessions at User, Key, and Provider levels using Redis Lua scripts

Applied to files:

  • src/app/[locale]/dashboard/logs/_components/virtualized-logs-table.tsx
  • src/app/[locale]/dashboard/logs/_components/usage-logs-table.tsx
  • src/repository/message.ts
📚 Learning: 2026-01-03T09:08:49.019Z
Learnt from: CR
Repo: ding113/claude-code-hub PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-03T09:08:49.019Z
Learning: Applies to src/app/v1/_lib/proxy/**/*.ts : Implement guard pipeline pattern for cross-cutting concerns in request processing (auth, rate limiting, session)

Applied to files:

  • src/app/v1/_lib/proxy/warmup-guard.ts
  • src/app/v1/_lib/proxy/guard-pipeline.ts
  • src/app/v1/_lib/proxy/warmup-intercept.ts
📚 Learning: 2026-01-03T09:08:20.573Z
Learnt from: CR
Repo: ding113/claude-code-hub PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-03T09:08:20.573Z
Learning: Applies to src/app/v1/_lib/**/*.ts : Guard pipeline must execute in order: ProxyAuthenticator, SensitiveWordGuard, VersionGuard, ProxySessionGuard, ProxyRateLimitGuard, ProxyProviderResolver

Applied to files:

  • src/app/v1/_lib/proxy/warmup-guard.ts
  • src/app/v1/_lib/proxy/guard-pipeline.ts
  • src/app/v1/_lib/proxy/warmup-intercept.ts
📚 Learning: 2026-01-03T09:08:20.573Z
Learnt from: CR
Repo: ding113/claude-code-hub PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-03T09:08:20.573Z
Learning: Applies to src/app/v1/_lib/proxy-handler.ts : Structure request flow in proxy handler through: ProxySession.fromContext() -> detectFormat() -> GuardPipelineBuilder.run() -> ProxyForwarder.send() -> ProxyResponseHandler.dispatch()

Applied to files:

  • src/app/v1/_lib/proxy/warmup-guard.ts
  • src/app/v1/_lib/proxy/guard-pipeline.ts
  • src/app/v1/_lib/proxy/warmup-intercept.ts
📚 Learning: 2026-01-03T09:08:49.019Z
Learnt from: CR
Repo: ding113/claude-code-hub PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-03T09:08:49.019Z
Learning: Applies to src/app/v1/_lib/proxy/**/*guard*.ts : Implement fail-open strategy: allow requests when Redis is unavailable

Applied to files:

  • src/app/v1/_lib/proxy/warmup-guard.ts
  • src/app/v1/_lib/proxy/guard-pipeline.ts
📚 Learning: 2026-01-03T09:10:02.182Z
Learnt from: NieiR
Repo: ding113/claude-code-hub PR: 517
File: src/app/v1/_lib/proxy/auth-guard.ts:205-232
Timestamp: 2026-01-03T09:10:02.182Z
Learning: In `src/app/v1/_lib/proxy/auth-guard.ts`, the `extractApiKeyFromHeaders` function is intentionally a standalone simplified version for non-Guard flows. It deliberately does not check for multi-source conflicts (unlike `ProxyAuthenticator.validate`), and the code duplication is intentional to avoid coupling between Guard and non-Guard authentication flows.

Applied to files:

  • src/app/v1/_lib/proxy/warmup-guard.ts
  • src/app/v1/_lib/proxy/guard-pipeline.ts
📚 Learning: 2026-01-03T09:08:49.019Z
Learnt from: CR
Repo: ding113/claude-code-hub PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-03T09:08:49.019Z
Learning: Applies to src/app/v1/_lib/proxy/*handler*.ts : Stream responses with proper backpressure handling and chunked transfer encoding

Applied to files:

  • src/app/v1/_lib/proxy/warmup-guard.ts
  • src/app/v1/_lib/proxy/warmup-intercept.ts
📚 Learning: 2026-01-03T09:08:49.019Z
Learnt from: CR
Repo: ding113/claude-code-hub PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-03T09:08:49.019Z
Learning: Applies to src/app/v1/_lib/proxy/**/*.ts : Use undici library for HTTP requests instead of node-fetch for better performance

Applied to files:

  • src/app/v1/_lib/proxy/warmup-guard.ts
  • src/app/v1/_lib/proxy/warmup-intercept.ts
📚 Learning: 2026-01-03T09:08:49.019Z
Learnt from: CR
Repo: ding113/claude-code-hub PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-03T09:08:49.019Z
Learning: Applies to src/drizzle/**/*.ts : Use soft delete pattern with `deletedAt` column instead of hard deletes

Applied to files:

  • src/drizzle/schema.ts
🧬 Code graph analysis (13)
src/repository/client-versions.ts (1)
src/drizzle/schema.ts (1)
  • messageRequest (264-345)
src/repository/leaderboard.ts (1)
src/drizzle/schema.ts (1)
  • messageRequest (264-345)
src/repository/key.ts (1)
src/drizzle/schema.ts (1)
  • messageRequest (264-345)
src/app/[locale]/dashboard/logs/_components/virtualized-logs-table.tsx (1)
src/lib/utils/performance-formatter.ts (2)
  • WARMUP_INTERCEPT_BLOCKED_BY (2-2)
  • NON_BILLING_ENDPOINT (1-1)
src/repository/system-config.ts (1)
src/drizzle/schema.ts (1)
  • systemSettings (439-472)
src/app/v1/_lib/proxy/warmup-guard.ts (4)
src/lib/config/system-settings-cache.ts (1)
  • getCachedSystemSettings (40-94)
src/app/v1/_lib/proxy/warmup-intercept.ts (4)
  • isClaudeWarmupRequestBody (37-64)
  • getClaudeStreamFlag (66-73)
  • buildClaudeWarmupInterceptResponse (156-197)
  • WARMUP_BLOCKED_BY (8-8)
src/lib/session-manager.ts (1)
  • SessionManager (79-1955)
src/drizzle/schema.ts (1)
  • messageRequest (264-345)
src/app/[locale]/dashboard/logs/_components/usage-logs-table.tsx (1)
src/lib/utils/performance-formatter.ts (2)
  • WARMUP_INTERCEPT_BLOCKED_BY (2-2)
  • NON_BILLING_ENDPOINT (1-1)
src/app/[locale]/dashboard/logs/_components/error-details-dialog.tsx (1)
src/lib/utils/performance-formatter.ts (1)
  • WARMUP_INTERCEPT_BLOCKED_BY (2-2)
src/app/v1/_lib/proxy/guard-pipeline.ts (1)
src/app/v1/_lib/proxy/warmup-guard.ts (1)
  • ProxyWarmupGuard (14-102)
src/repository/message.ts (1)
src/drizzle/schema.ts (1)
  • messageRequest (264-345)
src/actions/my-usage.ts (1)
src/drizzle/schema.ts (1)
  • messageRequest (264-345)
src/repository/overview.ts (1)
src/drizzle/schema.ts (1)
  • messageRequest (264-345)
src/lib/config/system-settings-cache.ts (1)
src/types/system-config.ts (1)
  • SystemSettings (6-37)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (8)
  • GitHub Check: Greptile Review
  • GitHub Check: pr-description
  • GitHub Check: 🌐 API Tests
  • GitHub Check: pr-label
  • GitHub Check: pr-review
  • GitHub Check: Docker Build Test
  • GitHub Check: check-codex-status
  • GitHub Check: dev-build-deploy
🔇 Additional comments (54)
drizzle/meta/_journal.json (2)

313-319: ✅ 迁移日志条目格式正确

新增的迁移记录(idx 44)遵循了既有的结构规范:

  • 字段完整性:idxversionwhentagbreakpoints 均已填充
  • 索引序号递增正确(43 → 44)
  • 标签名称遵循命名约定:0044_add_anthropic_warmup_intercept
  • JSON 格式语法正确,缩进保持 2 空格一致性

312-320: 此迁移文件已正确创建。对应的 SQL 文件 drizzle/0044_add_anthropic_warmup_intercept.sql 存在,且包含有效的数据库模式变更逻辑,为 system_settings 表添加了 enable_anthropic_warmup_intercept 布尔列。

messages/zh-TW/dashboard.json (2)

123-131: 表格列 skipped 翻译合理

"skipped": "已跳過" 用词自然、含义清晰,和英文含义一致,也与同区域其他状态文案(已攔截 / 非計費)风格统一,可保留。


156-161: Warmup 拦截详情文案清晰

"warmupIntercept": "Warmup 搶答(CCH)" 能准确表达由 CCH 本地抢答的含义,括号说明来源也方便审计查看;与其他 locale 的含义保持一致,无需调整。

messages/ru/dashboard.json (2)

123-131: table.skipped 俄文翻译准确

"Пропущено" 语气和其他状态列一致,能直观表达“已跳过”的含义,适合作为表格标签。


156-161: Warmup 拦截说明在俄文中表达自然

"Warmup перехвачен (CCH)" 清楚说明请求被 CCH 在 warmup 阶段拦截,本地响应;与英文版语义一致,便于运维排查。

messages/en/dashboard.json (2)

123-131: table.skipped 标签命名合适

使用 "Skipped" 作为状态列文本,语义明确,和 blocked / nonBilling 并列时也容易理解,无歧义。


156-161: Warmup Intercept 详情文案与功能一致

"Warmup Intercept (CCH)" 准确描述由 CCH 本地拦截 Warmup 请求的场景,和其他 blocked 类型(如 Sensitive Word Blocking)风格统一,便于用户区分拦截原因。

messages/en/settings.json (1)

99-107: Anthropic Warmup 开关文案与命名都很合理

  • key 命名 enableAnthropicWarmupIntercept 与现有布尔配置命名风格一致,易于前后端联动。
  • 描述清楚交代了“检测 + 短路 + 本地短响应 + 避免上游浪费”的行为,和 PR 说明吻合。

从多语言支持的角度,请确保其它 4 个 locale 的 settings.json 也已添加对应 key 并保持语义一致(当前 PR 描述看起来已覆盖)。Based on learnings, 多语言 messages 需保持 5 个 locale 同步。

messages/ja/dashboard.json (2)

123-131: table.skipped 日文翻译自然

"スキップ" 是常见 UI 用语,搭配其他状态列(ブロック済み / 非課金)阅读体验一致,能准确表达“已跳过”的含义。


156-161: Warmup 拦截说明在日文里表达准确

"Warmup 先行応答(CCH)" 把 Warmup 本地抢答解释成“先行応答”,语义贴合功能(本地优先响应避免上游调用),同时保留 CCH 标注,方便审计;整体表达顺畅,无需修改。

messages/zh-TW/settings.json (1)

103-104: LGTM!本地化文案清晰准确。

新增的繁体中文翻译与功能描述一致,格式符合现有文件规范。

src/drizzle/schema.ts (1)

465-468: LGTM!Schema 定义正确。

新增的 enableAnthropicWarmupIntercept 列:

  • 使用 notNull().default(false) 确保安全的默认行为(需主动开启)
  • 列命名遵循 snake_case 惯例
  • 注释清晰说明功能用途
src/repository/message.ts (3)

332-347: 供应商和模型子查询过滤逻辑正确。

providerId > 0 条件与 warmup 过滤配合使用,确保只统计真实的供应商调用记录(warmup 请求设置 provider_id=0)。


458-468: 批量聚合查询与单 session 查询保持一致。

多 session 聚合路径的过滤逻辑与单 session 路径完全对齐,避免了统计口径不一致的问题。


607-609: 跳过条件更新正确。

使用 requestCountAll === 0 判断 session 是否存在,而非 requestCount,确保即使只有 warmup 请求的 session 也不会被错误地包含在结果中。

drizzle/0044_add_anthropic_warmup_intercept.sql (1)

1-2: LGTM!迁移脚本安全且幂等。

  • IF NOT EXISTS 确保重复执行不会报错
  • NOT NULL DEFAULT false 与 schema 定义一致
  • 现有记录会自动获得默认值 false
src/lib/utils/performance-formatter.ts (1)

1-2: LGTM!常量定义有助于维护一致性。

WARMUP_INTERCEPT_BLOCKED_BY 常量将 blockedBy 字段的魔法字符串集中管理。建议在仓库层查询(如 src/repository/message.ts)中也引用此常量,确保前后端使用相同的值。

tests/unit/proxy/session.test.ts (1)

34-34: LGTM!测试夹具已正确更新。

新增的 enableAnthropicWarmupIntercept: false 与生产环境默认值一致,确保测试用例继续有效。

src/app/[locale]/dashboard/logs/_components/error-details-dialog.tsx (2)

29-29: LGTM!

导入语句正确,符合项目路径别名约定。


276-278: LGTM!

Warmup 拦截的条件逻辑实现正确,与现有 sensitive_word 处理模式保持一致,使用了适当的国际化翻译键。

src/app/[locale]/settings/config/page.tsx (1)

43-43: LGTM!

新字段 enableAnthropicWarmupIntercept 的初始化逻辑正确,与其他设置字段的模式保持一致。

src/actions/my-usage.ts (1)

326-326: LGTM!

在明细查询中一致地应用了相同的过滤条件,确保聚合数据和明细数据的一致性。

src/repository/overview.ts (1)

42-42: LGTM! 过滤逻辑在统计查询中一致应用。

概览统计查询中正确应用了 Warmup 请求过滤条件,与 my-usage.ts 中的实现保持一致。这确保了今日请求数、费用、响应时间和错误率等指标排除了 Warmup 拦截的请求。

src/actions/my-usage.ts 中的过滤条件相同,同样需要关注 blocked_by 列在大数据量场景下的查询性能。

src/types/system-config.ts (2)

65-66: LGTM!

UpdateSystemSettingsInput 接口中的字段定义正确,作为可选字段支持部分更新操作,与接口中其他设置字段的模式保持一致。


32-34: 数据库默认值配置已验证正确。

字段类型定义正确(非可选的 boolean),与其他系统设置字段保持一致。验证结果显示:

  • Schema (drizzle/schema.ts, line 466-468):.notNull().default(false) 正确配置
  • Transformer (repository/_shared/transformers.ts, line 154):使用 ?? false 确保安全的默认值处理
  • Repository (system-config.ts):初始化(line 89)和所有更新操作(lines 114, 147, 172, 275)中均正确处理,默认值设为 false

默认值在数据库、数据转换和数据库访问层都正确配置为 false,符合 PR 描述要求。实现完整无误。

src/repository/_shared/transformers.ts (1)

154-154: SystemSettings 新增开关映射合理且兼容旧数据

enableAnthropicWarmupInterceptdbSettings 映射并默认 false,符合“默认关闭”需求,且不会影响旧记录加载,逻辑可以接受。

src/app/[locale]/dashboard/logs/_components/usage-logs-table.tsx (1)

24-25: Warmup 拦截日志被正确标记为非计费并以“已跳过”展示

  • 通过 log.blockedBy === WARMUP_INTERCEPT_BLOCKED_BY 识别 warmup 行,并纳入 isNonBilling,保证整行样式与非计费一致,符合“统计口径不计费”的需求。
  • 费用列对 warmup 单独显示橙色 t("logs.table.skipped"),对其它非计费请求仍用 "-",区分度清晰,用户一眼能看出是 CCH 抢答跳过。
  • aria-label 仍基于 isNonBilling,无额外无障碍回归风险。请同步确认 5 个 locale 都已提供 logs.table.skipped 翻译键以避免运行时缺失。As per coding guidelines, ...

Also applies to: 93-95, 100-103, 307-311

messages/ru/settings.json (1)

103-104: 俄文配置文案与功能含义匹配

enableAnthropicWarmupIntercept 及其描述准确表达了“检测 Claude/Anthropic Warmup 请求、本地抢答并跳过上游”的含义,语气与周围系统设置项保持一致,无需调整。

src/app/[locale]/dashboard/logs/_components/virtualized-logs-table.tsx (1)

20-21: 虚拟化日志表对 warmup 拦截的处理与普通表保持一致

  • 通过 log.blockedBy === WARMUP_INTERCEPT_BLOCKED_BY 识别 warmup 行,并将其纳入 isNonBilling,行样式统一走“非计费”分支,满足 warmup 不参与统计/费用的需求。
  • 费用列对 warmup 明确显示橙色 t("logs.table.skipped"),其余非计费仍显示 "-",与 UsageLogsTable 的行为完全一致,避免两种视图表现不一。
  • loader 行在计算 isWarmupIntercept 之前提前返回,不会出现空值访问问题。As per coding guidelines, ...

Also applies to: 254-256, 270-271, 472-476

messages/ja/settings.json (1)

103-104: 日文 Warmup 拦截配置文案自然且与功能一致

新增的日文标题与描述清晰说明了 Anthropic Warmup 请求会被 CCH 本地截获并跳过上游调用,措辞与周围配置项风格统一,无需进一步修改。

messages/zh-CN/dashboard.json (1)

130-130: 翻译准确,表达清晰。

新增的两个翻译键值对表达准确:

  • "已跳过" 准确对应被拦截的 warmup 请求
  • "Warmup 抢答(CCH)" 形象地描述了拦截机制

Also applies to: 160-160

src/lib/validation/schemas.ts (1)

701-702: Schema 定义正确。

新增字段 enableAnthropicWarmupIntercept 的定义符合 Zod 最佳实践:

  • 使用 .optional() 支持部分更新
  • 类型为 boolean,语义明确
messages/zh-CN/settings.json (1)

86-87: 翻译质量良好。

新增的设置项翻译清晰准确,描述详细说明了拦截功能的作用和效果。

src/actions/system-config.ts (1)

40-40: 数据流处理正确。

新增字段正确地通过验证层传递到 repository 层:

  • formData 类型定义完整
  • 通过 UpdateSystemSettingsSchema.parse 验证
  • 正确传递给 updateSystemSettings

Also applies to: 61-61

src/repository/key.ts (1)

580-586: SQL 条件包装正确。

and() 子句中正确添加了 warmup 过滤条件,查询逻辑清晰。

Also applies to: 711-717

src/repository/leaderboard.ts (1)

173-179: LGTM! 一致地排除了 warmup 请求。

使用 IS DISTINCT FROM 'warmup' 是正确的选择,它能正确处理 blockedByNULL 的情况(正常请求会被包含),同时排除 warmup 拦截的记录。四个排行榜查询函数都应用了相同的过滤逻辑,保持了一致性。

Also applies to: 315-315, 415-415, 558-564

src/lib/config/system-settings-cache.ts (1)

27-30: LGTM! 新设置字段正确集成到缓存层。

enableAnthropicWarmupIntercept 遵循与 enableHttp2 相同的模式:默认关闭、包含在调试日志中、在回退设置中提供安全默认值。这符合 fail-open 策略的要求。

Also applies to: 58-58, 89-89

tests/unit/proxy/warmup-intercept.test.ts (2)

12-87: LGTM! 测试覆盖全面。

测试用例覆盖了 isClaudeWarmupRequestBody 的主要分支:有效的 warmup 请求、多消息、错误的 role、缺失/错误的 cache_control、以及错误的文本内容。


89-153: 流式与非流式响应测试完整。

测试验证了 getClaudeStreamFlag 的流判断逻辑(body.stream、Accept header、默认行为),以及 buildClaudeWarmupInterceptResponse 对两种响应模式的正确处理,包括响应头和拦截标记。

src/app/[locale]/settings/config/_components/system-settings-form.tsx (2)

32-32: LGTM! 状态管理和表单提交逻辑正确。

新设置字段的状态初始化、表单提交载荷、以及服务器响应后的状态刷新都遵循了现有模式。

Also applies to: 54-56, 75-75, 90-90


201-216: UI 组件实现规范。

新的开关组件遵循了现有设置项的布局模式,正确使用了 htmlForid 关联,并通过 next-intl 实现了国际化。

src/app/v1/_lib/proxy/warmup-guard.ts (4)

14-28: Guard 逻辑正确,符合 pipeline 模式。

三层早期返回条件清晰:仅针对 Claude 格式、需要启用系统设置、请求体必须匹配 warmup 特征。这遵循了 guard pipeline 的 fail-open 策略。


39-64: 非阻塞存储模式正确。

使用 void ... .catch() 模式确保 Session 存储失败不会阻塞 warmup 响应,符合 fail-open 策略。每个存储操作都有独立的错误处理和日志记录。


67-94: 日志记录条件较严格但合理。

仅当认证成功、用户信息完整且存在 sessionId 时才写入日志。这意味着无 session 的 warmup 请求不会被记录,但考虑到 warmup 请求通常在正常会话中发生,这是可接受的设计。


74-75: 验证 providerId: 0 的外键约束兼容性。

messageRequest 表中 providerId 字段的 Drizzle 关系定义使用 references([providers.id]),但未包含显式的外键约束参数(如 onDeleteonUpdate)。在不显式指定约束的情况下,Drizzle 不会在数据库层级强制创建外键约束,只为 ORM 提供类型信息。

代码在四处使用 providerId: 0 作为特殊值(本地抢答、敏感词拦截、测试、webhook),且在查询时明确过滤 providerId > 0,说明设计已考虑此场景。然而,建议显式验证:

  • 如果数据库已通过迁移添加了外键约束,需删除或修改该约束以允许 providerId: 0
  • 如果希望强制外键关系,需重新设计 providerId: 0 的语义(如添加可空字段或分离日志表)
src/app/v1/_lib/proxy/guard-pipeline.ts (2)

13-13: LGTM! Warmup guard 正确集成到 pipeline。

warmup step 定义遵循了现有 guard 的模式,通过静态方法 ProxyWarmupGuard.ensure(session) 返回 Response | null

Also applies to: 35-35, 96-101


181-181: Warmup 步骤位置合理。

warmup 放在 session 之后、requestFiltersensitive 之前是正确的设计:

  1. 需要先建立 session 以获取 sessionId 用于日志记录
  2. Warmup 请求无需经过敏感词检查和速率限制
  3. Warmup 请求不需要选择 provider,直接本地响应

注意:COUNT_TOKENS_PIPELINE 不包含 warmup 步骤,这是正确的,因为 token 计数请求不应被拦截。

src/repository/system-config.ts (2)

89-89: LGTM! 数据库操作全面覆盖新字段。

enableAnthropicWarmupIntercept 字段在所有数据库操作中保持一致:fallback 设置、读取投影、插入返回、以及并发回退查询。

Also applies to: 114-114, 147-147, 171-171


253-256: 更新逻辑正确处理可选字段。

使用 !== undefined 检查确保只有明确提供的字段才会被更新,符合部分更新(partial update)模式。返回投影也正确包含了新字段。

Also applies to: 275-275

src/app/v1/_lib/proxy/warmup-intercept.ts (4)

1-8: 常量和导入定义合理

使用 node:crypto 生成随机 ID 是合适的选择,常量命名清晰且作用域正确。


33-64: Warmup 请求检测逻辑严谨

类型守卫和检测逻辑实现正确:

  • 严格验证单条 user 消息
  • 准确匹配 "Warmup" 文本和 ephemeral cache_control
  • 类型安全的属性访问避免了运行时错误

156-197: 拦截响应构建实现完整且正确

该函数正确处理流式和非流式两种响应模式:

流式响应(lines 163-184):

  • SSE 头部设置完整(Content-Type、Cache-Control、Connection)
  • ReadableStream 实现适合小型固定响应(立即入队并关闭),无需复杂的背压处理
  • 正确添加 x-cch-intercepted: warmup 标记

非流式响应(lines 186-196):

  • JSON 格式响应配置正确
  • 同样添加拦截标记头部

返回结构设计合理,同时提供客户端响应、存储用的响应体和响应头,便于日志记录和审计,完全符合 PR 目标。


66-78: 流式标志检测和消息 ID 生成逻辑正确

  • getClaudeStreamFlag 正确优先检查显式 stream 标志,然后回退到 Accept 头检查
  • buildWarmupMessageId 使用加密安全的随机字节生成唯一 ID,格式为 msg_[24位十六进制字符]

Claude 官方文档未规定消息 ID 的固定格式或长度要求,并明确说明格式和长度可能会随时间变化。当前实现生成的 ID 格式与 Claude 实际 ID 模式(msg_ 前缀)相符,无需调整。

Comment thread src/actions/my-usage.ts
and(
eq(messageRequest.key, session.key.key),
isNull(messageRequest.deletedAt),
sql`${messageRequest.blockedBy} IS DISTINCT FROM 'warmup'`,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, let's examine the code at line 306 in src/actions/my-usage.ts
head -320 src/actions/my-usage.ts | tail -30

Repository: ding113/claude-code-hub

Length of output: 1314


🏁 Script executed:

# Let's also check the broader context around line 306
sed -n '290,320p' src/actions/my-usage.ts

Repository: ding113/claude-code-hub

Length of output: 1359


🏁 Script executed:

# Check if the file exists and get its structure
wc -l src/actions/my-usage.ts

Repository: ding113/claude-code-hub

Length of output: 94


🏁 Script executed:

# Search for the messageRequest table and blockedBy field in schema
rg "messageRequest|blockedBy|blocked_by" src/drizzle/schema.ts -A 3 -B 3

Repository: ding113/claude-code-hub

Length of output: 2680


🏁 Script executed:

# Look for index definitions related to blocked_by
rg "index\(|\.index\(" src/drizzle/schema.ts -A 2 -B 2

Repository: ding113/claude-code-hub

Length of output: 4394


🏁 Script executed:

# Check for any index() or uniqueIndex() definitions on the messageRequest table
fd "schema" src/drizzle --type f

Repository: ding113/claude-code-hub

Length of output: 88


添加 blockedBy 列的索引以优化性能。

过滤逻辑正确使用了 IS DISTINCT FROM 操作符,能够正确排除 blockedBy = 'warmup' 的记录同时保留 NULL 值。但 blockedBy 列当前没有索引,在大数据量下会导致全表扫描。建议添加独立索引或将其包含在复合索引中,特别是对于频繁的统计查询。

🤖 Prompt for AI Agents
In src/actions/my-usage.ts around line 306, the query filters on blockedBy using
IS DISTINCT FROM 'warmup' but blockedBy lacks an index causing full table scans
on large datasets; add a database index for the blockedBy column (either a
standalone index or include blockedBy in the relevant composite index used by
these stats queries), implement the change via a migration script (or schema
change) so it’s applied in all environments, and ensure the migration is tested
on representative data; optionally consider a partial index that excludes the
'warmup' value if that better matches query patterns.

Comment thread src/repository/key.ts
and(
eq(messageRequest.key, key.key),
isNull(messageRequest.deletedAt),
sql`${messageRequest.blockedBy} IS DISTINCT FROM 'warmup'`,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, let's explore the repository structure and locate the key.ts file
find . -name "key.ts" -type f | head -20

Repository: ding113/claude-code-hub

Length of output: 109


🏁 Script executed:

# Get the total lines in the file to understand its size
wc -l src/repository/key.ts

Repository: ding113/claude-code-hub

Length of output: 92


🏁 Script executed:

# Let's examine the specific lines mentioned in the comment (566, 602, 688, 743)
sed -n '560,570p; 596,606p; 682,692p; 737,747p' src/repository/key.ts

Repository: ding113/claude-code-hub

Length of output: 1496


🏁 Script executed:

# Now let's look at the findKeyUsageToday function around line 279
sed -n '270,310p' src/repository/key.ts

Repository: ding113/claude-code-hub

Length of output: 1160


🏁 Script executed:

# And the findKeyUsageTodayBatch function around line 318
sed -n '310,360p' src/repository/key.ts

Repository: ding113/claude-code-hub

Length of output: 1506


🏁 Script executed:

# Search for usages of findKeyUsageToday and findKeyUsageTodayBatch
rg -nP --type=ts 'findKeyUsageToday(Batch)?\(' --glob '!src/repository/key.ts' -C3

Repository: ding113/claude-code-hub

Length of output: 891


🏁 Script executed:

# Look for PR-related files or comments about warmup handling
rg -nP --type=ts -i 'warmup' src/repository/key.ts | head -30

Repository: ding113/claude-code-hub

Length of output: 502


🏁 Script executed:

# Get more context around the functions that do have warmup filtering to understand the pattern
sed -n '550,610p' src/repository/key.ts

Repository: ding113/claude-code-hub

Length of output: 1885


🏁 Script executed:

# Check if there are any comments or documentation about warmup cost being zero
rg -nP --type=ts -C2 'cost.*warmup|warmup.*cost|costUsd.*0'

Repository: ding113/claude-code-hub

Length of output: 19581


🏁 Script executed:

# Check for any TODO or FIXME comments related to warmup in key.ts
rg -nP --type=ts '(TODO|FIXME).*warmup|warmup.*(TODO|FIXME)' src/repository/key.ts

Repository: ding113/claude-code-hub

Length of output: 49


🏁 Script executed:

# Look at the complete context of findKeyUsageToday and findKeyUsageTodayBatch for any comments
sed -n '275,365p' src/repository/key.ts

Repository: ding113/claude-code-hub

Length of output: 2567


🏁 Script executed:

# Check if there are any test files that might clarify the intended behavior
find . -name "*.test.ts" -o -name "*.spec.ts" | xargs grep -l "findKeyUsageToday" 2>/dev/null

Repository: ding113/claude-code-hub

Length of output: 51


findKeyUsageTodayfindKeyUsageTodayBatch 中添加 warmup 过滤条件,确保统计口径一致。

findKeyUsageToday(第 279 行)和 findKeyUsageTodayBatch(第 318 行)的 leftJoin 条件中缺少对 warmup 请求的过滤。虽然 warmup 请求的 costUsd=0,但为了与其他统计函数保持一致(见 message.ts 中的明确说明"统计口径:排除 warmup"),应添加相同的过滤条件。

findKeyUsageToday 的 leftJoin 条件中(第 290-296 行)添加:

     .leftJoin(
       messageRequest,
       and(
         eq(messageRequest.key, keys.key),
         isNull(messageRequest.deletedAt),
+        sql`${messageRequest.blockedBy} IS DISTINCT FROM 'warmup'`,
         gte(messageRequest.createdAt, today),
         lt(messageRequest.createdAt, tomorrow)
       )
     )

findKeyUsageTodayBatch 的 leftJoin 条件中(第 330-336 行)添加相同的过滤。

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In src/repository/key.ts around lines 279-296 (findKeyUsageToday) and 318-336
(findKeyUsageTodayBatch), the leftJoin conditions currently omit filtering out
warmup requests; add the clause sql`${messageRequest.blockedBy} IS DISTINCT FROM
'warmup'` to the leftJoin predicates in both functions so warmup requests are
excluded (matching the statistical rules described in message.ts). Ensure the
same exact filter is applied in both leftJoin conditions.

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

This PR implements optional interception of Anthropic warmup requests. After comprehensive review through 6 specialized perspectives (Comments, Tests, Error Handling, Types, General Code, and Simplification), no significant issues were identified.

PR Size: XL

  • Lines changed: 699 (665 additions + 34 deletions)
  • Files changed: 37

Note: This is a large PR that touches multiple layers (proxy guards, database queries, UI, i18n). Consider breaking similar features into smaller PRs in the future (e.g., core logic → statistics filtering → UI updates).

Review Coverage

  • Logic and correctness - Clean
  • Security (OWASP Top 10) - Clean
  • Error handling - Clean (intentional fail-open design for logging)
  • Type safety - Clean
  • Documentation accuracy - Clean
  • Test coverage - Adequate (11 test cases covering detection, response building, streaming/non-streaming)
  • Code clarity - Good

Key Strengths

  1. Comprehensive Filtering: Warmup requests are consistently excluded from all statistics queries using blocked_by IS DISTINCT FROM 'warmup' (17 instances in statistics.ts, plus leaderboard.ts, overview.ts, message.ts, my-usage.ts, key.ts)
  2. Well-Tested: Covers edge cases for warmup detection (wrong role, missing cache_control, multiple messages, etc.)
  3. Fail-Safe Design: Feature is opt-in (default disabled), reducing deployment risk
  4. Complete i18n: All 5 locales updated with proper labels
  5. Audit Trail: Warmup requests are logged with provider_id=0, cost_usd=0, and blocked_by=warmup for visibility

Architecture Notes

  • Guard is correctly positioned after session initialization but before request filtering in the pipeline
  • Both streaming (SSE) and non-streaming (JSON) responses are properly formatted according to Claude API spec
  • Session storage failures are intentionally non-blocking (logged but don't fail the request)

Automated review by Claude AI - Examined 37 files across proxy guards, database repositories, UI components, and tests

"version",
"probe",
"session",
"warmup",

@github-actions github-actions Bot Jan 3, 2026

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.

[HIGH] [SECURITY-VULNERABILITY] Warmup intercept bypasses rate limiting on the chat pipeline

Evidence (src/app/v1/_lib/proxy/guard-pipeline.ts:181):

steps: [
  // ...
  "session",
  "warmup", // returns early before rate limiting
  "requestFilter",
  "sensitive",
  "rateLimit",
  // ...
]

Why this is a problem: ProxyWarmupGuard.ensure() can return a 200 Response before ProxyRateLimitGuard.ensure() runs, so an authenticated client can repeatedly send a warmup-shaped Claude request to bypass concurrent-session / RPM protection and generate unbounded local work (Redis session writes + DB inserts). This creates an easy DoS / quota-bypass vector on the main chat endpoint.

Suggested fix:

// Keep `/v1/messages` requests rate-limited: run warmup after `rateLimit`
steps: [
  // ...
  "session",
  "requestFilter",
  "sensitive",
  "rateLimit",
  "warmup",
  // ...
]

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

This PR introduces an optional Claude/Anthropic warmup intercept path plus broad analytics/UI updates to exclude warmup entries from billing/aggregations. The main risk is that the new intercept currently short-circuits before rate limiting in the chat pipeline, creating an unbounded request path on /v1/messages.

PR Size: XL

  • Lines changed: 699
  • Files changed: 37
  • Split suggestions: (1) proxy warmup intercept + tests, (2) DB/schema + repository aggregation changes, (3) dashboard/UI + i18n strings.

Issues Found

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

Critical Issues (Must Fix)

None.

High Priority Issues (Should Fix)

  • src/app/v1/_lib/proxy/guard-pipeline.ts:181 [HIGH] [SECURITY-VULNERABILITY] Warmup intercept bypasses ProxyRateLimitGuard on the chat pipeline (Confidence: 92).

Review Coverage

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

Automated review by Codex AI

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

  • Reviewed PR #526 and applied label size/XL.
  • Posted 1 inline review comment: warmup intercept can short-circuit before rate limiting in src/app/v1/_lib/proxy/guard-pipeline.ts:181, enabling an authenticated DoS / quota-bypass path on /v1/messages (suggested moving warmup after rateLimit in CHAT_PIPELINE).
  • Submitted the required summary review on the PR, including split suggestions for this XL change set.

@ding113 ding113 closed this Jan 4, 2026
@github-project-automation github-project-automation Bot moved this from Backlog to Done in Claude Code Hub Roadmap Jan 4, 2026
@ding113
ding113 deleted the feat/anthropic-warmup-intercept branch January 27, 2026 09:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

1 participant