From 475b52f6c8f05e3f544c58db13ce849a71a183a0 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 10 Dec 2025 12:03:28 +0000 Subject: [PATCH 1/4] docs: update changelog for v0.3.28 [skip ci] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 --- CHANGELOG.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e8d1c35ed..fd5580930 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,24 @@ --- +## [v0.3.28](https://github.com/ding113/claude-code-hub/releases/tag/v0.3.28) - 2025-12-10 + +### 新增 + +- 日志页面新增快速日期筛选器(今日/昨日/近7天/近30天)和 CSV 导出功能 (#314) +- Session 监控页面新增分页功能,支持分别对活跃和非活跃 Session 进行分页浏览 (#314) + +### 优化 + +- 每日排行榜改用滚动 24 小时窗口计算,替代原先基于日历日的统计方式 (#314) +- 上游 404 错误现在触发供应商故障切换而不计入熔断器,提升中转服务兼容性 (#314) + +### 修复 + +- 修复 Anthropic SSE 流式响应中 output_tokens 提取问题,现在从 message_delta 事件正确获取 (#313) + +--- + ## [v0.3.27](https://github.com/ding113/claude-code-hub/releases/tag/v0.3.27) - 2025-12-10 ### 新增 From 180b1b29a6b363b8afac8566e4f8ffe861303eb6 Mon Sep 17 00:00:00 2001 From: sususu98 Date: Fri, 12 Dec 2025 18:04:57 +0800 Subject: [PATCH 2/4] =?UTF-8?q?fix:=20=E6=94=AF=E6=8C=81=20Gemini=20though?= =?UTF-8?q?tsTokenCount=20=E8=AE=A1=E8=B4=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在 GeminiUsageMetadata 类型中添加 thoughtsTokenCount 字段 - 新增 GeminiTokenDetail 类型支持按 modality 分类的 token 详情 - 将 thoughtsTokenCount 累加到 output_tokens 进行计费 (Gemini 思考 token 价格与输出 token 相同) - 添加 promptTokensDetails、cacheTokensDetails、 candidatesTokensDetails 字段支持 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- src/app/v1/_lib/gemini/types.ts | 12 +++++++++++- src/app/v1/_lib/proxy/response-handler.ts | 5 +++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/app/v1/_lib/gemini/types.ts b/src/app/v1/_lib/gemini/types.ts index 076a03819..dd409fddc 100644 --- a/src/app/v1/_lib/gemini/types.ts +++ b/src/app/v1/_lib/gemini/types.ts @@ -51,11 +51,21 @@ export interface GeminiRequest { }; } +export interface GeminiTokenDetail { + modality: "TEXT" | "IMAGE" | "AUDIO" | "VIDEO"; + tokenCount: number; +} + export interface GeminiUsageMetadata { promptTokenCount: number; candidatesTokenCount: number; totalTokenCount: number; - cachedContentTokenCount?: number; // Gemini 缓存支持 + cachedContentTokenCount?: number; // Gemini 缓存命中的 token 数 + thoughtsTokenCount?: number; // Gemini 思考模型的推理 token + // 详细信息(按 modality 分类) + promptTokensDetails?: GeminiTokenDetail[]; + cacheTokensDetails?: GeminiTokenDetail[]; + candidatesTokensDetails?: GeminiTokenDetail[]; } export interface GeminiCandidate { diff --git a/src/app/v1/_lib/proxy/response-handler.ts b/src/app/v1/_lib/proxy/response-handler.ts index 21e9c048a..45fc79f29 100644 --- a/src/app/v1/_lib/proxy/response-handler.ts +++ b/src/app/v1/_lib/proxy/response-handler.ts @@ -1206,6 +1206,11 @@ function extractUsageMetrics(value: unknown): UsageMetrics | null { result.cache_read_input_tokens = usage.cachedContentTokenCount; hasAny = true; } + // Gemini 思考/推理 token:直接累加到 output_tokens(思考价格与输出价格相同) + if (typeof usage.thoughtsTokenCount === "number" && usage.thoughtsTokenCount > 0) { + result.output_tokens = (result.output_tokens ?? 0) + usage.thoughtsTokenCount; + hasAny = true; + } if (typeof usage.output_tokens === "number") { result.output_tokens = usage.output_tokens; From 742c60e1085c7fbc5647843c375e9f2fb02a8277 Mon Sep 17 00:00:00 2001 From: sususu98 Date: Fri, 12 Dec 2025 18:24:55 +0800 Subject: [PATCH 3/4] =?UTF-8?q?fix:=20=E6=94=BE=E5=9C=A8=20output=5Ftokens?= =?UTF-8?q?=20=E8=B5=8B=E5=80=BC=E4=B9=8B=E5=90=8E=EF=BC=8C=E9=81=BF?= =?UTF-8?q?=E5=85=8D=E8=A2=AB=E8=A6=86=E7=9B=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/v1/_lib/proxy/response-handler.ts | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/app/v1/_lib/proxy/response-handler.ts b/src/app/v1/_lib/proxy/response-handler.ts index 45fc79f29..4fb43575e 100644 --- a/src/app/v1/_lib/proxy/response-handler.ts +++ b/src/app/v1/_lib/proxy/response-handler.ts @@ -651,7 +651,7 @@ export class ProxyResponseHandler { const openAIChunk = GeminiAdapter.transformResponse(geminiResponse, true); const output = `data: ${JSON.stringify(openAIChunk)}\n\n`; controller.enqueue(new TextEncoder().encode(output)); - } catch {} + } catch { } } }, }); @@ -1206,17 +1206,21 @@ function extractUsageMetrics(value: unknown): UsageMetrics | null { result.cache_read_input_tokens = usage.cachedContentTokenCount; hasAny = true; } - // Gemini 思考/推理 token:直接累加到 output_tokens(思考价格与输出价格相同) - if (typeof usage.thoughtsTokenCount === "number" && usage.thoughtsTokenCount > 0) { - result.output_tokens = (result.output_tokens ?? 0) + usage.thoughtsTokenCount; - hasAny = true; - } if (typeof usage.output_tokens === "number") { result.output_tokens = usage.output_tokens; hasAny = true; } + // Gemini 思考/推理 token:直接累加到 output_tokens(思考价格与输出价格相同) + // 注意:放在 output_tokens 赋值之后,避免被覆盖 + // output_tokens 是转换的时候才存在的,gemini原生接口的没有该值 + // 通常存在 output_tokens的时候,thoughtsTokenCount=0 + if (typeof usage.thoughtsTokenCount === "number" && usage.thoughtsTokenCount > 0) { + result.output_tokens = (result.output_tokens ?? 0) + usage.thoughtsTokenCount; + hasAny = true; + } + if (typeof usage.cache_creation_input_tokens === "number") { result.cache_creation_input_tokens = usage.cache_creation_input_tokens; hasAny = true; From 8892449c23851e4d5bb633344e9c5fcbb29c5792 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 12 Dec 2025 10:25:52 +0000 Subject: [PATCH 4/4] chore: format code (fix-gemini-thoughts-token-support-742c60e) --- src/app/v1/_lib/proxy/response-handler.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/v1/_lib/proxy/response-handler.ts b/src/app/v1/_lib/proxy/response-handler.ts index 4fb43575e..ccc8649d5 100644 --- a/src/app/v1/_lib/proxy/response-handler.ts +++ b/src/app/v1/_lib/proxy/response-handler.ts @@ -651,7 +651,7 @@ export class ProxyResponseHandler { const openAIChunk = GeminiAdapter.transformResponse(geminiResponse, true); const output = `data: ${JSON.stringify(openAIChunk)}\n\n`; controller.enqueue(new TextEncoder().encode(output)); - } catch { } + } catch {} } }, });