fix(flow-monitor): 修复 Kiro 请求 Token 显示为 0 的问题 (#44)

- 在 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
This commit is contained in:
coso
2025-12-25 08:16:42 +08:00
parent 0231243be3
commit 3d7d820f7a
6 changed files with 54 additions and 10 deletions
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "proxycast",
"private": true,
"version": "0.17.11",
"version": "0.17.12",
"type": "module",
"repository": {
"type": "git",
+1 -1
View File
@@ -3377,7 +3377,7 @@ dependencies = [
[[package]]
name = "proxycast"
version = "0.17.11"
version = "0.17.12"
dependencies = [
"anyhow",
"async-stream",
+1 -1
View File
@@ -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"
+26 -6
View File
@@ -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) =
+24
View File
@@ -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<char> = s.chars().collect();
+1 -1
View File
@@ -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",