Skip to content
This repository was archived by the owner on Jul 30, 2026. It is now read-only.

perf(cli): route wrapped and clients through cached folds - #101

Merged
makoMakoGo merged 4 commits into
personal/local-clientsfrom
codex/issue-92-cache-wrapped-clients
Jul 3, 2026
Merged

perf(cli): route wrapped and clients through cached folds#101
makoMakoGo merged 4 commits into
personal/local-clientsfrom
codex/issue-92-cache-wrapped-clients

Conversation

@makoMakoGo

@makoMakoGo makoMakoGo commented Jul 2, 2026

Copy link
Copy Markdown
Owner

Summary

  • add a cached AGENTS aggregation view that preserves the client dimension
  • route wrapped graph and OpenCode agent ranking through one cached aggregation fold
  • route tokscale clients counts through a cached lightweight core count sink

Closes #92

Validation

  • cargo fmt --check
  • cargo clippy --workspace --all-targets
  • cargo test --workspace
  • cargo test -p tokscale-core aggregate::parity_tests -- --nocapture
  • cargo run -p tokscale-cli -- --no-spinner clients --json
  • cargo run -p tokscale-cli -- --no-spinner wrapped --output /tmp/tokscale-issue-92-wrapped.png --client opencode

Local memory check

Measured on WSL2 Ubuntu 24.04 with live local client data. Base is the previously installed local tokscale binary; PR is target/release/tokscale from this branch. Each command had one warmup run, then 3 measured runs with /usr/bin/time -f "%M,%e"; RSS values are %M / 1024.

Command Base median RSS PR median RSS RSS change Base median time PR median time
tokscale --no-spinner clients --json 357.9 MiB 44.2 MiB -87.7% 9.98s 8.39s
tokscale wrapped --year 2026 --agents --no-spinner --output ... 431.0 MiB 140.1 MiB -67.5% 19.87s 9.64s
tokscale wrapped --year 2026 --clients --no-spinner --output ... 140.0 MiB 139.7 MiB -0.2% 9.52s 9.53s

The wrapped commands exited successfully and produced non-empty PNG output. clients --json kept the same JSON shape; exact live message counts can move during measurement while local Codex sessions are being written.


Summary by cubic

Routes the wrapped and clients CLI paths through cached aggregation folds to avoid rescans and speed up results. Adds a cached AGENTS view that preserves per-client agent usage and fixes message counting for accurate rankings. Closes #92.

  • New Features

    • New ViewSet::AGENTS and AgentUsage in tokscale-core for per-client agent metrics (tokens, cost, messages).
    • Agent names are normalized per client (opencode, copilot, others) and sorted for stable ranking.
  • Bug Fixes

    • Count cc-mirror/* usage as claude in client message counts.
    • Count folded agent messages using message_count to avoid undercounting.
    • Use folded message_count for headless Codex in clients stats for accurate totals.

Written for commit 7011c32. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • 新功能

    • 新增按代理/客户端查看的聚合统计,可显示代理使用量、消息数、Token 和成本。
    • 本地客户端统计结果更完整,支持更准确地汇总各客户端消息数量,并识别更多客户端变体。
    • 命令行和界面中的相关统计展示已同步更新。
  • Bug Fixes

    • 修复了部分代理统计在不同客户端下的归类不一致问题。
    • 改进了宽列数据显示的稳定性,避免内容被意外截断。

@coderabbitai

coderabbitai Bot commented Jul 2, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

新增 AGENTS 聚合视图(AgentUsage 结构、ViewSet::AGENTS 位、AgentEntries 累加器)并接线进 AggregationEngine;wrapped 命令改为单次调用 load_aggregated_views_with_pricing 获取图与 agent 用量;clients 命令改为调用新增的 count_local_client_messages 入口获取计数,替代原双路径解析。

Changes

核心聚合视图新增

Layer / File(s) Summary
AgentUsage 数据结构与视图字段
crates/tokscale-core/src/aggregate/views.rs, .../mod.rs, .../config.rs, crates/tokscale-core/src/lib.rs
新增 AgentUsage 结构体(client/agent/tokens/cost/message_count)及 AggregatedViews.agent_usage 字段,新增 ViewSet::AGENTS 位,并重导出 AgentUsage。
AgentEntries 累加器
crates/tokscale-core/src/aggregate/accumulators.rs
按 (client, 归一化后 agent) 累加 token/cost/message_count,finish 时确定性排序输出 Vec<AgentUsage>。
AggregationEngine 接线
crates/tokscale-core/src/aggregate/engine.rs, .../parity_tests.rs
新增 agent_entries 字段,按 ViewSet::AGENTS 条件初始化/推送/生成 agent_usage 并写入 AggregatedViews,新增契约测试验证 client 维度保留。

Estimated code review effort: 3 (Moderate) | ~25 minutes

wrapped 与 clients 命令数据路径统一

Layer / File(s) Summary
wrapped 单次聚合获取
crates/tokscale-cli/src/commands/wrapped.rs
load_wrapped_data 移除 parse_local_clients/generate_graph,改为组装 ViewSet、初始化 PricingService 并调用 load_aggregated_views_with_pricing,取 graph 与 agent_usage。
build_top_agents 消费 AgentUsage
crates/tokscale-cli/src/commands/wrapped.rs
签名从 ParsedMessages 改为 &[AgentUsage],基于 agent.client=="opencode" 过滤、agent.tokens/message_count 聚合,新增单元测试。
Core 本地客户端计数入口
crates/tokscale-core/src/lib.rs, crates/tokscale-core/src/clients.rs
新增 LocalClientMessageCounts、client_count_bucket(处理 cc-mirror/*)、ClientCountSink 及 count_local_client_messages 函数,ClientCounts 新增 Debug 派生,新增测试。
clients 命令改用计数入口
crates/tokscale-cli/src/main.rs
run_clients_command 改用 count_local_client_messages 结果计算 headless_codex_count 与 ClientRow.message_count。
TUI agents 测试签名调整
crates/tokscale-cli/src/tui/ui/agents.rs
length_at 调用参数从 &widths 改为 widths,断言期望值保持不变。

Estimated code review effort: 3 (Moderate) | ~30 minutes

Sequence Diagram(s)

sequenceDiagram
  participant WrappedCmd as load_wrapped_data
  participant Engine as AggregationEngine
  participant Pricing as PricingService
  participant Views as AggregatedViews

  WrappedCmd->>Pricing: 初始化定价服务
  WrappedCmd->>Engine: load_aggregated_views_with_pricing(ViewSet)
  Engine->>Engine: 累积 graph / agent_entries
  Engine-->>Views: 返回 graph, agent_usage
  Views-->>WrappedCmd: aggregated.graph, aggregated.agent_usage
  WrappedCmd->>WrappedCmd: build_top_agents(agent_usage)
Loading

Possibly related PRs

  • makoMakoGo/tokscale#59: 主 PR 在该 PR 重构的集中式 AggregationEngine/ViewSet 路由基础上,新增 ViewSet::AGENTS 位并接线 engine.rs 累积/输出 AgentUsage(agent_entries)到 AggregatedViews。
  • makoMakoGo/tokscale#67: 主 PR 扩展该 PR 中基于定价的 AggregationEngine 聚合以产出 AgentUsage(通过 ViewSet::AGENTS 与 agent_entries),两者均通过消息流入聚合引擎的方式直接关联。
  • makoMakoGo/tokscale#19: 两者均影响 CLI 的本地客户端消息计数路径,主 PR 切换到 count_local_client_messages,该 PR 改变了 AMP 会话消息的收集方式,直接影响计数总量。

Poem

兔子敲着键盘蹦跳跳,🐰
一次聚合,图与代理都拿到,
不再解析两遍把内存耗,
cc-mirror 也归入 Claude 巢,
测试绿灯亮闪闪,合并回家睡个好觉~ 🥕

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Linked Issues check ✅ Passed 实现了 #92 要求的 AGENTS 视图、wrapped 单次 fold、clients 缓存计数路径,并保留了 opencode 过滤。
Out of Scope Changes check ✅ Passed 未见明显超出 #92 范围的功能性改动,额外变更看起来都是为新路径配套的测试或接口调整。
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 标题准确概括了主要变更:将 wrapped 和 clients 路由到缓存的 fold 聚合路径。
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/issue-92-cache-wrapped-clients

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@codspeed-hq

codspeed-hq Bot commented Jul 2, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 20 untouched benchmarks
⏩ 7 skipped benchmarks1


Comparing codex/issue-92-cache-wrapped-clients (7011c32) with personal/local-clients (2c77ddb)

Open in CodSpeed

Footnotes

  1. 7 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

@makoMakoGo makoMakoGo left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Review note: one inline comment left for the AGENTS message_count aggregation issue.

Comment thread crates/tokscale-core/src/aggregate/accumulators.rs
@makoMakoGo
makoMakoGo marked this pull request as ready for review July 3, 2026 06:46

@makoMakoGo makoMakoGo left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Re-reviewed latest head. The prior AGENTS message_count issue is fixed: the accumulator now uses msg.message_count.max(0), and the parity test covers a folded opencode message with message_count = 3.

I did not find new blockers in the updated diff. Overall design still matches #92: wrapped uses one cached aggregation fold for graph/time-metrics/agents, clients uses the lightweight count sink, opencode-only wrapped agent semantics are preserved, and TUI behavior remains untouched.

LGTM from code review. Note: the Native test workflow was still running when I reviewed; Core CI, Test & Coverage, and CodSpeed were green.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

3 issues found across 11 files

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread crates/tokscale-core/src/aggregate/parity_tests.rs
Comment thread crates/tokscale-core/src/lib.rs Outdated
Comment thread crates/tokscale-core/src/aggregate/accumulators.rs

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
crates/tokscale-cli/src/commands/wrapped.rs (1)

358-358: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

"opencode" 改为 ClientId::OpenCode.as_str():这里和本文件其它客户端比较保持同一套常量来源,也能避免后续规范 ID 变更时出现字符串漂移。

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/tokscale-cli/src/commands/wrapped.rs` at line 358, Replace the
hardcoded "opencode" string check in wrapped.rs with ClientId::OpenCode.as_str()
so the client comparison uses the same identifier source as the rest of the
file. Update the conditional around the agent.client comparison to reference
ClientId::OpenCode directly, keeping client ID usage consistent and avoiding
string drift if the canonical ID changes later.

Source: Coding guidelines

crates/tokscale-core/src/aggregate/accumulators.rs (1)

14-18: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

已修复历史问题;建议核查与 TuiAcc 的重复归一化逻辑。

message_count 累加已按 msg.message_count.max(0) 处理,解决了此前"固定 +1 导致折叠消息被低估"的问题,新增的 agents_view_keeps_client_dimension 测试也覆盖了 message_count = 3 场景,无需再关注。

另外,push() 中按 opencode/copilot/其它 client 分支选择不同的 normalize_*_agent_name 函数,这个分发逻辑与 aggregate/tui.rsTuiAcc(存在 agent_map/agent_clients/agent_instances)很可能存在相似实现。建议核实是否可抽取共享辅助函数,避免两处归一化规则未来出现分叉。

🔍 核查脚本
rg -n -B2 -A15 'normalize_opencode_agent_name|normalize_copilot_agent_name' crates/tokscale-core/src/aggregate/tui.rs

Also applies to: 318-369

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/tokscale-core/src/aggregate/accumulators.rs` around lines 14 - 18, The
normalization dispatch in push() is likely duplicating the same agent-name
canonicalization logic that TuiAcc uses in aggregate/tui.rs. Review the existing
normalize_opencode_agent_name, normalize_copilot_agent_name, and related
client-specific branches, then factor the shared normalization behavior into a
common helper or shared path used by both accumulators. Keep the client-based
selection intact, but ensure both Accumulators rely on the same normalization
rules to avoid future divergence.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@crates/tokscale-cli/src/commands/wrapped.rs`:
- Line 358: Replace the hardcoded "opencode" string check in wrapped.rs with
ClientId::OpenCode.as_str() so the client comparison uses the same identifier
source as the rest of the file. Update the conditional around the agent.client
comparison to reference ClientId::OpenCode directly, keeping client ID usage
consistent and avoiding string drift if the canonical ID changes later.

In `@crates/tokscale-core/src/aggregate/accumulators.rs`:
- Around line 14-18: The normalization dispatch in push() is likely duplicating
the same agent-name canonicalization logic that TuiAcc uses in aggregate/tui.rs.
Review the existing normalize_opencode_agent_name, normalize_copilot_agent_name,
and related client-specific branches, then factor the shared normalization
behavior into a common helper or shared path used by both accumulators. Keep the
client-based selection intact, but ensure both Accumulators rely on the same
normalization rules to avoid future divergence.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 8c8ef6b3-74d6-4608-adfd-9be09341e699

📥 Commits

Reviewing files that changed from the base of the PR and between 2c77ddb and a7c24f3.

📒 Files selected for processing (11)
  • crates/tokscale-cli/src/commands/wrapped.rs
  • crates/tokscale-cli/src/main.rs
  • crates/tokscale-cli/src/tui/ui/agents.rs
  • crates/tokscale-core/src/aggregate/accumulators.rs
  • crates/tokscale-core/src/aggregate/config.rs
  • crates/tokscale-core/src/aggregate/engine.rs
  • crates/tokscale-core/src/aggregate/mod.rs
  • crates/tokscale-core/src/aggregate/parity_tests.rs
  • crates/tokscale-core/src/aggregate/views.rs
  • crates/tokscale-core/src/clients.rs
  • crates/tokscale-core/src/lib.rs

@makoMakoGo makoMakoGo left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Final re-review on latest head. The follow-up commit addresses the valid bot findings cleanly:

  • ClientCountSink now uses message.message_count.max(0) for headless Codex counts, matching the client totals basis.
  • A folded headless Codex count test covers message_count = 4.
  • The AGENTS parity test no longer mutates TZ unnecessarily.
  • The wrapped opencode filter now uses ClientId::OpenCode.as_str().

No new blockers found. LGTM from code review. Core CI, Test & Coverage, and CodSpeed are green; Native test was still running at the time of review.

@makoMakoGo
makoMakoGo merged commit 28361db into personal/local-clients Jul 3, 2026
9 checks passed
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

P1: wrapped 与 clients 走一次解析和缓存路径

1 participant