Skip to content

Commit 8ce1bda

Browse files
committed
feat(ccproxy): add request statistics and token usage tracking with dashboard UI
1 parent a10647c commit 8ce1bda

File tree

21 files changed

+968
-216
lines changed

21 files changed

+968
-216
lines changed

RELEASE.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,28 @@
22

33
# Release Notes
44

5+
## [1.1.25] - 2026-01-27
6+
7+
### 🚀 New Features
8+
9+
- **New Proxy Statistics Feature**:
10+
- **Full Data Monitoring**: The system now automatically records all requests passing through the proxy module, including the requested model, provider, client protocol, HTTP status code, and detailed error messages.
11+
- **Precise Token Tracking**: Supports recording cumulative input, output, and cache token counts, providing users with a clear understanding of traffic consumption.
12+
- **Dual-Mode Support**: Automatic statistics recording is implemented for both protocol conversion mode and direct forward mode.
13+
- **Brand New Statistics & Analytics UI**:
14+
- **Visual Dashboard**: A new "Statistics" tab has been added to the Proxy Settings page, supporting daily usage summaries.
15+
- **Multi-dimensional Drill-down**: Click on daily summaries to expand "Statistics Details," viewing granular distribution by provider, protocol, and tool mode.
16+
- **Deep Error Tracking**: Click on error counts to view error code distribution and specific failure reasons for that date, facilitating rapid troubleshooting.
17+
- **Smart Unit Conversion**: Token counts are automatically converted to "Ten Thousand" (万) or "Hundred Million" (亿) units for more intuitive display of large values.
18+
19+
### 🪄 Improvements
20+
21+
- **System Architecture Optimizations**:
22+
- **Database Evolution**: Introduced V4 database migration logic, adding the `ccproxy_stats` table with indexes on key fields to ensure high-performance querying of massive statistical data.
23+
- **Enhanced Robustness**: Optimized token capture logic during streaming transmissions to ensure the accuracy and completeness of statistical data.
24+
25+
---
26+
527
## [1.1.24] - 2026-01-27
628

729
### 🚀 New Features

RELEASE.zh-CN.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,28 @@
22

33
# 发布日志
44

5+
## [1.1.25] - 2026-01-27
6+
7+
### 🚀 新特性
8+
9+
- **新增代理统计功能**
10+
- **全量数据监控**:现在系统会自动记录所有通过代理模块的请求,包括请求模型、供应商、客户端协议、HTTP 状态码及详细的错误信息。
11+
- **精准 Token 统计**:支持记录累计输入、输出及缓存 Token 数,帮助用户清晰掌握流量消耗。
12+
- **双模式支持**:无论是协议转换模式还是直连转发模式,均已实现自动统计入库。
13+
- **全新统计分析界面**
14+
- **可视化看板**:在代理设置页面新增“统计信息”标签页,支持按天汇总显示使用情况。
15+
- **多维度下钻**:点击每日统计可展开“统计详情”,按供应商、协议和工具模式查看更细致的分布数据。
16+
- **错误深度追踪**:支持点击错误次数查看该日期下的错误码分布及具体错误原因,便于快速定位问题。
17+
- **智能单位换算**:Token 数量支持自动换算为“万”或“亿”单位,大额数据展示更直观。
18+
19+
### 🪄 改进
20+
21+
- **系统架构优化**
22+
- **数据库版本演进**:引入了 V4 数据库迁移逻辑,新增 `ccproxy_stats` 表并配置了关键字段索引,确保海量统计数据下的查询性能。
23+
- **健壮性增强**:优化了流式传输下的 Token 捕获逻辑,确保统计数据的准确性和完整性。
24+
25+
---
26+
527
## [1.1.24] - 2026-01-27
628

729
### 🚀 新特性

src-tauri/src/ccproxy/handler/chat_handler.rs

Lines changed: 53 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ use crate::ccproxy::{
2828
types::ollama::OllamaChatCompletionRequest,
2929
};
3030
use crate::constants::{CFG_CCPROXY_LOG_PROXY_TO_FILE, CFG_CCPROXY_LOG_TO_FILE};
31-
use crate::db::MainStore;
31+
use crate::db::{CcproxyStat, MainStore};
3232

3333
fn get_proxy_alias_from_body(
3434
chat_protocol: &ChatProtocol,
@@ -277,7 +277,7 @@ pub async fn handle_chat_completion(
277277
Some("compat") => true,
278278
Some("native") => false,
279279
Some("auto") | None => tool_compat_mode, // Use route parameter for auto mode
280-
_ => tool_compat_mode, // Fallback to route parameter
280+
_ => tool_compat_mode, // Fallback to route parameter
281281
};
282282

283283
if chat_protocol == proxy_model.chat_protocol && !final_tool_compat_mode {
@@ -540,6 +540,23 @@ pub async fn handle_chat_completion(
540540
"code": status_code.as_u16().to_string()
541541
});
542542

543+
// Record error statistics
544+
if let Ok(store) = main_store_arc.read() {
545+
let _ = store.record_ccproxy_stat(CcproxyStat {
546+
id: None,
547+
model: proxy_model.model.clone(),
548+
provider: proxy_model.provider.clone(),
549+
protocol: chat_protocol.to_string(),
550+
tool_compat_mode: if final_tool_compat_mode { 1 } else { 0 },
551+
status_code: status_code.as_u16() as i32,
552+
error_message: Some(message_content),
553+
input_tokens: 0,
554+
output_tokens: 0,
555+
cache_tokens: 0,
556+
request_at: None,
557+
});
558+
}
559+
543560
let mut response =
544561
(status_code, Json(json!({ "error": final_error_json }))).into_response();
545562

@@ -635,11 +652,16 @@ pub async fn handle_chat_completion(
635652
if is_streaming_request {
636653
let res = handle_streamed_response(
637654
Arc::new(proxy_model.chat_protocol),
655+
chat_protocol,
638656
target_response,
639657
backend_adapter,
640658
output_adapter,
641659
sse_status,
642660
log_org_to_file,
661+
main_store_arc.clone(),
662+
proxy_model.model.clone(),
663+
proxy_model.provider.clone(),
664+
final_tool_compat_mode,
643665
)
644666
.await?;
645667
Ok(res.into_response())
@@ -662,6 +684,35 @@ pub async fn handle_chat_completion(
662684
.await
663685
.map_err(|e| CCProxyError::InternalError(e.to_string()))?;
664686

687+
// Record success statistics
688+
if let Ok(store) = main_store_arc.read() {
689+
log::info!(
690+
"Recording ccproxy stats for non-streaming response: model={}, provider={}",
691+
&proxy_model.model,
692+
&proxy_model.provider
693+
);
694+
let cache_tokens = unified_response
695+
.usage
696+
.cache_read_input_tokens
697+
.or(unified_response.usage.prompt_cached_tokens)
698+
.or(unified_response.usage.cached_content_tokens)
699+
.unwrap_or(0);
700+
701+
let _ = store.record_ccproxy_stat(CcproxyStat {
702+
id: None,
703+
model: proxy_model.model.clone(),
704+
provider: proxy_model.provider.clone(),
705+
protocol: chat_protocol.to_string(),
706+
tool_compat_mode: if final_tool_compat_mode { 1 } else { 0 },
707+
status_code: 200,
708+
error_message: None,
709+
input_tokens: unified_response.usage.input_tokens as i64,
710+
output_tokens: unified_response.usage.output_tokens as i64,
711+
cache_tokens: cache_tokens as i64,
712+
request_at: None,
713+
});
714+
}
715+
665716
let response = output_adapter
666717
.adapt_response(unified_response, sse_status)
667718
.map_err(|e| CCProxyError::InternalError(e.to_string()))?;

0 commit comments

Comments
 (0)