Skip to content

feat(#304): Usage 每对话缓存命中率 + 点击逐轮命中率直方图#305

Open
Cmochance wants to merge 5 commits into
mainfrom
feat/usage-cache-hit-chart
Open

feat(#304): Usage 每对话缓存命中率 + 点击逐轮命中率直方图#305
Cmochance wants to merge 5 commits into
mainfrom
feat/usage-cache-hit-chart

Conversation

@Cmochance
Copy link
Copy Markdown
Owner

@Cmochance Cmochance commented May 28, 2026

Summary

承接 #295 / MOC-53 诊断结论(缓存正常、命中率值得可视化)。

  • Usage tab「按对话」每行新增可点的「命中率」列 = cachedInputTokens / inputTokens
  • 点击数字 → 弹窗(复用 codex-modal)用纯 CSS 柱状图画该对话的逐轮(per-prompt)命中率;顶部显示整体 hit% / cached / input,hover 每柱显示轮次区间。
  • 横坐标至多 10 根柱:轮数 ≤10 一轮一柱;>10 等分成 10 桶(余数从后往前递加),每柱 = 桶内 Σcached/Σinput(token 加权)。

实现

  • 后端(usage_tracker):bucket_series(纯函数,≤10 桶分桶算法)+ cache_series_for_conversation(session_id)(逐轮 events 按 session 过滤 + ts 升序 + 分桶)。
  • 端点:GET /api/usage/conversation/cache-series?session=<id>(admin/handlers/usage.rs,复用 usage_summary 的 spawn_blocking + 错误映射形态)。点击才调用。
  • 前端(vanilla,无图表库):frontend/js/app.jscacheHitCell + openCacheHitModal + renderCacheChart;index.html 加 modal;i18n.js 中英;usage.css 柱状图样式。

Test plan

  • cargo test -p codex-app-transfer-usage-tracker 12 passed(含 5 个 bucket_series 单测:≤max / >max 余数从后往前 / 空 / token 加权 / 连续覆盖无空隙)
  • cargo fmt --check clean;clippy 无新增 warning;node --check app.js / i18n.js OK
  • code-reviewer:bucketing 无 panic / 全覆盖、XSS 全转义、div-by-zero 有 guard、modal 事件委托正确
  • UI 视觉验证:本地 .app 已打包(Usage → 按对话 → 点命中率数字)——本机未授 Accessibility,改由维护者/真机眼检

Refs #304


Open in Devin Review

- usage_tracker: cache_series_for_conversation + bucket_series(≤10 桶,
  余数从后往前递加、token 加权;5 单测覆盖 ≤max/>max/空/加权/连续覆盖)
- admin: GET /api/usage/conversation/cache-series 端点(复用 usage_summary 形态)
- 前端:byConversation 表新增可点「命中率」列 → modal 纯 CSS 柱状图画逐轮命中率,
  顶部显示整体 hit%/cached/input,hover 显示每柱轮次区间;中英 i18n + CSS

Refs #304
chatgpt-codex-connector[bot]

This comment was marked as resolved.

Copy link
Copy Markdown

@devin-ai-integration devin-ai-integration Bot left a comment

Choose a reason for hiding this comment

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

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no potential bugs to report.

View in Devin Review to see 4 additional findings.

Open in Devin Review

Cmochance added 4 commits May 29, 2026 02:49
用户反馈 rollout 路径对不上。byConversation 行新增 display_name = Codex
session_index.jsonl 的 thread_name(按 group 末尾 uuid 查);前端首列显示名称
前 5 字 + hover 全名/路径,无名回退日期 MM/DD。session_uuid_from_group 单测。

Refs #304
- 后端 CacheBucket 加 output_tokens(bucket_series / cache_series 同步,单测更新)
- 柱:单柱高 = 该桶总输入(按全局最大缩放);命中部分含于其中(底部 accent 色),
  不再两列
- hover 三行:命中输入 / 总计输入 / 输出 token
- 底部横轴改对话进度百分比(turnEnd / 总轮次),不再用重叠的轮次区间
- 顶部摘要追加总计输出 token

Refs #304
UI:
- 命中率列前移到 模型 ↔ Input 之间;按对话设为默认最左视图
- sticky 表头 sticky+背景落到 th —— 修 border-collapse 下下滑透出后方行(背景与顶部相接)

真实上游模型(不进 rollout / 不影响对话):
- proxy 读 Codex `x-session-id`/`session_id` 头 + adapter 解析的真实上游模型,append 到本地
  `~/.codex-app-transfer/session-models.jsonl`(best-effort,绝不阻塞转发)
- usage_tracker 读该文件按 session uuid join,填 by_conversation 行 upstream_model
- 前端「按对话」模型列优先显示真实上游模型,无记录(历史对话)回退 rollout 客户端名

Refs #304
回应 codex-connector P2:用户可见 UI 改动需同 PR 更新 README.md / README.en.md。
中英「能做什么 / What it does」列表补「用量统计」条目。

Refs #304
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: fa444004c0

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

}
// [#304] 本地记录 session → 真实上游模型,供 Usage 页显示真实模型而非 Codex
// 客户端占位名。只落本地 jsonl,**不进 Codex rollout / 不影响对话**;forward-only。
record_session_upstream_model(&parts.headers, upstream_model.as_deref());
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Use resolved model when recording sessions

For adapters that move the model out of the JSON body, this skips the new session→model record entirely. I checked GeminiNativeAdapter: its prepared request puts the model in upstream_path (/v1alpha/models/...) and the body only contains Gemini wire fields, so body_model(&plan.body) is None; passing only upstream_model here means Gemini conversations never get a session-models.jsonl entry and the Usage conversation view keeps showing the Codex placeholder instead of the real upstream model. Falling back to resolved_model covers these adapters.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown

@devin-ai-integration devin-ai-integration Bot left a comment

Choose a reason for hiding this comment

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

Devin Review found 2 new potential issues.

View 14 additional findings in Devin Review.

Open in Devin Review

Comment on lines +908 to +909
let Ok(home) = std::env::var("HOME") else {
return;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 record_session_upstream_model uses HOME env var only, silently fails on Windows

The new record_session_upstream_model function at crates/proxy/src/forward.rs:908 uses std::env::var("HOME") to locate the home directory. On Windows, HOME is typically not set — the equivalent is USERPROFILE. The rest of the codebase consistently uses a resolve_home() pattern that falls back to USERPROFILE (see crates/registry/src/paths.rs:24-43, src-tauri/src/admin/handlers/skills.rs:21-26, and many other callsites). Since Windows is a supported platform (NSIS/MSI installers are provided), this means the upstream model recording feature silently does nothing on Windows — the session-models.jsonl file is never written, so the Usage page always falls back to showing the Codex client placeholder model names instead of real upstream model names.

Prompt for agents
In record_session_upstream_model (crates/proxy/src/forward.rs around line 908), replace std::env::var("HOME") with a cross-platform home directory resolution. The codebase pattern is to check HOME first then USERPROFILE: see crates/registry/src/paths.rs:24 resolve_home() or the vendored_ccusage home::home_dir() at crates/usage_tracker/src/vendored_ccusage/home.rs:3. Either add the proxy crate as a dependency of the registry crate and use its resolve_home(), or inline the two-env-var fallback pattern used in src-tauri/src/admin/handlers/skills.rs:21-26. The same fix is needed in crates/usage_tracker/src/lib.rs read_session_upstream_models (line 381) which has the same bug.
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment on lines +381 to +383
let Ok(home) = std::env::var("HOME") else {
return out;
};
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 read_session_upstream_models uses HOME env var only, silently fails on Windows

The new read_session_upstream_models function at crates/usage_tracker/src/lib.rs:381 uses std::env::var("HOME") to locate the session-models JSONL file. On Windows, HOME is not set. This is especially inconsistent because within the same file, read_session_index_titles() (line 329) uses codex_usage_paths() which internally calls home::home_dir() (crates/usage_tracker/src/vendored_ccusage/home.rs:3) — a function that properly falls back to USERPROFILE and HOMEDRIVE+HOMEPATH on Windows. On Windows, the upstream model lookup map will always be empty, so row.upstream_model is never populated and the Usage page never shows real upstream model names.

Suggested change
let Ok(home) = std::env::var("HOME") else {
return out;
};
let Some(home) = vendored_ccusage::home::home_dir() else {
return out;
};
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant