feat(#304): Usage 每对话缓存命中率 + 点击逐轮命中率直方图#305
Conversation
- 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
用户反馈 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
There was a problem hiding this comment.
💡 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()); |
There was a problem hiding this comment.
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 👍 / 👎.
| let Ok(home) = std::env::var("HOME") else { | ||
| return; |
There was a problem hiding this comment.
🟡 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.
Was this helpful? React with 👍 or 👎 to provide feedback.
| let Ok(home) = std::env::var("HOME") else { | ||
| return out; | ||
| }; |
There was a problem hiding this comment.
🟡 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.
| let Ok(home) = std::env::var("HOME") else { | |
| return out; | |
| }; | |
| let Some(home) = vendored_ccusage::home::home_dir() else { | |
| return out; | |
| }; |
Was this helpful? React with 👍 or 👎 to provide feedback.
Summary
承接 #295 / MOC-53 诊断结论(缓存正常、命中率值得可视化)。
cachedInputTokens / inputTokens。codex-modal)用纯 CSS 柱状图画该对话的逐轮(per-prompt)命中率;顶部显示整体 hit% / cached / input,hover 每柱显示轮次区间。Σ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 + 错误映射形态)。点击才调用。frontend/js/app.js加cacheHitCell+openCacheHitModal+renderCacheChart;index.html加 modal;i18n.js中英;usage.css柱状图样式。Test plan
cargo test -p codex-app-transfer-usage-tracker12 passed(含 5 个 bucket_series 单测:≤max / >max 余数从后往前 / 空 / token 加权 / 连续覆盖无空隙)cargo fmt --checkclean;clippy 无新增 warning;node --checkapp.js / i18n.js OKRefs #304