From 3d7d820f7a748a17c19dfb996f87bdcc220857ad Mon Sep 17 00:00:00 2001 From: coso Date: Thu, 25 Dec 2025 08:16:42 +0800 Subject: [PATCH] =?UTF-8?q?fix(flow-monitor):=20=E4=BF=AE=E5=A4=8D=20Kiro?= =?UTF-8?q?=20=E8=AF=B7=E6=B1=82=20Token=20=E6=98=BE=E7=A4=BA=E4=B8=BA=200?= =?UTF-8?q?=20=E7=9A=84=E9=97=AE=E9=A2=98=20(#44)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在 CWParsedResponse 中添加 estimate_tokens() 方法 - 基于 context_usage_percentage 估算 input_tokens (100% = 200k tokens) - 基于响应内容长度估算 output_tokens (约 4 字符 = 1 token) - 修复 api.rs 中所有调用 build_llm_response 时传入 None 的位置 - 版本更新至 0.17.12 --- package.json | 2 +- src-tauri/Cargo.lock | 2 +- src-tauri/Cargo.toml | 2 +- src-tauri/src/server/handlers/api.rs | 32 ++++++++++++++++++++++------ src-tauri/src/server_utils.rs | 24 +++++++++++++++++++++ src-tauri/tauri.conf.json | 2 +- 6 files changed, 54 insertions(+), 10 deletions(-) diff --git a/package.json b/package.json index 956c4b0a3..09873a5cc 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "proxycast", "private": true, - "version": "0.17.11", + "version": "0.17.12", "type": "module", "repository": { "type": "git", diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 0da72972d..289aa51c9 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -3377,7 +3377,7 @@ dependencies = [ [[package]] name = "proxycast" -version = "0.17.11" +version = "0.17.12" dependencies = [ "anyhow", "async-stream", diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 31afc958b..85788db8d 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "proxycast" -version = "0.17.11" +version = "0.17.12" description = "AI API Proxy Desktop App" authors = ["you"] edition = "2021" diff --git a/src-tauri/src/server/handlers/api.rs b/src-tauri/src/server/handlers/api.rs index 00da50ca2..ec55b9123 100644 --- a/src-tauri/src/server/handlers/api.rs +++ b/src-tauri/src/server/handlers/api.rs @@ -1232,8 +1232,13 @@ pub async fn chat_completions( // 完成 Flow 捕获并检查响应拦截(重试成功) // **Validates: Requirements 2.1, 2.5** if let Some(fid) = &flow_id { - let llm_response = - build_llm_response(200, &parsed.content, None); + let (est_input, est_output) = + parsed.estimate_tokens(); + let llm_response = build_llm_response( + 200, + &parsed.content, + Some((est_input, est_output)), + ); // 检查是否需要拦截响应 if let Some(modified_response) = @@ -1896,7 +1901,12 @@ pub async fn anthropic_messages( // 完成 Flow 捕获并检查响应拦截(流式) // **Validates: Requirements 2.1, 2.5** if let Some(fid) = &flow_id { - let llm_response = build_llm_response(200, &parsed.content, None); + let (est_input, est_output) = parsed.estimate_tokens(); + let llm_response = build_llm_response( + 200, + &parsed.content, + Some((est_input, est_output)), + ); // 检查是否需要拦截响应 if let Some(modified_response) = check_response_intercept( @@ -1955,7 +1965,12 @@ pub async fn anthropic_messages( // 完成 Flow 捕获并检查响应拦截(非流式) // **Validates: Requirements 2.1, 2.5** if let Some(fid) = &flow_id { - let llm_response = build_llm_response(200, &parsed.content, None); + let (est_input, est_output) = parsed.estimate_tokens(); + let llm_response = build_llm_response( + 200, + &parsed.content, + Some((est_input, est_output)), + ); // 检查是否需要拦截响应 if let Some(modified_response) = check_response_intercept( @@ -2080,8 +2095,13 @@ pub async fn anthropic_messages( // 完成 Flow 捕获并检查响应拦截(重试成功) // **Validates: Requirements 2.1, 2.5** if let Some(fid) = &flow_id { - let llm_response = - build_llm_response(200, &parsed.content, None); + let (est_input, est_output) = + parsed.estimate_tokens(); + let llm_response = build_llm_response( + 200, + &parsed.content, + Some((est_input, est_output)), + ); // 检查是否需要拦截响应 if let Some(modified_response) = diff --git a/src-tauri/src/server_utils.rs b/src-tauri/src/server_utils.rs index ee3c8f790..9aa7dada1 100644 --- a/src-tauri/src/server_utils.rs +++ b/src-tauri/src/server_utils.rs @@ -21,6 +21,30 @@ pub struct CWParsedResponse { pub context_usage_percentage: f64, } +impl CWParsedResponse { + /// 估算 Token 使用量 + /// + /// 基于响应内容长度和上下文使用百分比估算 Token 数量: + /// - output_tokens: 基于响应内容长度(约 4 字符 = 1 token) + /// - input_tokens: 基于 context_usage_percentage(假设 100% = 200k tokens) + /// + /// # 返回 + /// (input_tokens, output_tokens) 元组 + pub fn estimate_tokens(&self) -> (u32, u32) { + // 估算 output tokens: 基于响应内容长度 (约 4 字符 = 1 token) + let mut output_tokens: u32 = (self.content.len() / 4) as u32; + for tc in &self.tool_calls { + output_tokens += (tc.function.arguments.len() / 4) as u32; + } + + // 从 context_usage_percentage 估算 input tokens + // 假设 100% = 200k tokens (Claude 的上下文窗口) + let input_tokens = ((self.context_usage_percentage / 100.0) * 200000.0) as u32; + + (input_tokens, output_tokens) + } +} + /// 安全截断字符串到指定字符数,避免 UTF-8 边界问题 pub fn safe_truncate(s: &str, max_chars: usize) -> String { let chars: Vec = s.chars().collect(); diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index 09d6041e0..b22859591 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -1,7 +1,7 @@ { "$schema": "https://schema.tauri.app/config/2", "productName": "ProxyCast", -"version": "0.17.11", +"version": "0.17.12", "identifier": "com.proxycast.app", "build": { "beforeDevCommand": "npm run dev",