Repository navigation
feat: add Grok Build CLI usage adapter - #1447
AgileInnov8tor wants to merge 6 commits into
Conversation
Add multi-candidate pricing lookup and override-key tracking so adapters can try display labels, raw model ids, and provider-qualified keys without hand-rolling first-hit loops. Route pi and OpenCode through the shared helpers, and clarify missing-pricing warnings for dynamic routers versus fixed models.
Discover sessions under ~/.grok (GROK_HOME / custom path), parse turn_completed modelUsage into LoadedEntry rows, bill reasoning at the output rate, and document parent/child session load rules.
Register Command::Grok, --grok-path / config grokPath, usage-load progress, and all-agent daily/monthly/session detection so unified reports include Grok when local session data exists.
Add the Grok guide and nav entry, reverse the Source Support Q&A unsupported claim, list Grok in READMEs and agent-sources, and record fixed-rate notes for grok-4.5 and provisional composer pricing.
Update the local pricing notes to use a worktree-independent binary under ~/.local/lib/ccusage, the install-ccusage-local reinstall flow, and PATH shims so Grok-capable reports survive branch switches.
Capture the accepted plan for the Grok Build CLI adapter so branch context and acceptance criteria stay with the feature work.
|
@coderabbitai review @cubic-dev-ai review this pull request |
@AgileInnov8tor I have started the AI code review. It will take a few minutes to complete. |
|
This PR was auto-closed. Only contributors approved with Maintainers review auto-closed issues and reopen worthwhile ones. Issues that do not meet the quality bar in CONTRIBUTING.md may not be reopened or receive a reply. If a maintainer replies See CONTRIBUTING.md. |
|
✅ Action performedReview finished.
|
|
Caution Review failedThe pull request is closed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: ⛔ Files ignored due to path filters (3)
📒 Files selected for processing (34)
📝 WalkthroughWalkthroughAdds first-class Grok Build CLI support to ChangesGrok usage integration
Estimated code review effort: 4 (Complex) | ~60 minutes Possibly related PRs
Suggested labels: Suggested reviewers: ✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
9 issues found across 37 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="rust/crates/ccusage/src/adapter/grok/report.rs">
<violation number="1" location="rust/crates/ccusage/src/adapter/grok/report.rs:11">
P3: Grok report behavior now has a byte-for-byte copy of OpenClaw's report implementation. A shared agent-report helper would prevent future JSON/aggregation fixes from drifting between adapters.</violation>
</file>
<file name="rust/crates/ccusage/src/output.rs">
<violation number="1" location="rust/crates/ccusage/src/output.rs:374">
P3: Dynamic-router users with a known blended/contract rate are incorrectly told not to configure it unless logs expose the underlying model. An override keyed to `{model}` works; describe the resolved model as an accuracy improvement rather than a prerequisite.</violation>
</file>
<file name="rust/crates/ccusage/src/main.rs">
<violation number="1" location="rust/crates/ccusage/src/main.rs:157">
P2: The `None` (default `ccusage`) arm does not hydrate `grok_path` from config. Other agent commands (pi, openclaw, grok) call `config.apply_agent_args(...)` with their respective path parameter. If a user has configured `grokPath` in their config file and runs bare `ccusage`, the Grok adapter will not pick up the configured path, potentially causing missed Grok data in the unified report.</violation>
</file>
<file name="rust/crates/ccusage/src/pricing.rs">
<violation number="1" location="rust/crates/ccusage/src/pricing.rs:424">
P2: Candidate-based reports fail to price configured aliases for models available only through models.dev, producing a missing-pricing warning and $0 calculated cost. Resolve each candidate's configured alias before both fallback lookups, matching `find()` behavior.</violation>
</file>
<file name="docs/guide/grok/index.md">
<violation number="1" location="docs/guide/grok/index.md:105">
P3: The example table's Total Tokens (124,015) exceeds the sum of visible columns (116,944) by 7,071, and the $0.24 cost doesn't match the documented pricing rates. The difference is reasoning tokens, but there's no reasoning column or inline note in the table. Consider either adding a reasoning tokens column, showing a footnote in the table, or making the example numbers balance cleanly (total = input + output + cache_create + cache_read) so users aren't confused by the gap.</violation>
</file>
<file name="rust/crates/ccusage/src/adapter/grok/parser.rs">
<violation number="1" location="rust/crates/ccusage/src/adapter/grok/parser.rs:212">
P2: `summary.info.id` is parsed from `summary.json` but discarded — the comment says to prefer it over the path-derived session ID, but the assignment was never completed. Either complete the override (`session_id = id;`) or remove the dead parsing block and its comment.</violation>
<violation number="2" location="rust/crates/ccusage/src/adapter/grok/parser.rs:325">
P2: Rows with only `totalTokens`, or totals exceeding their breakdown, are omitted or undercounted in reports and cost. Normalize each model row with `apply_total_token_fallback` before this filter, billing the remainder as output-equivalent tokens.</violation>
<violation number="3" location="rust/crates/ccusage/src/adapter/grok/parser.rs:356">
P2: Distinct no-`eventId` turns for one model in the same timestamp are dropped, underreporting usage and cost. Include the token tuple in this fallback ID so only identical rows dedupe.</violation>
</file>
<file name="docs/guide/all-reports.md">
<violation number="1" location="docs/guide/all-reports.md:52">
P3: Same naming inconsistency as in the index.md table: the all-reports summary table says "Grok Build" instead of "Grok Build CLI". Consider aligning to match the product name used in the sidebar, Grok guide, and apps/ccusage README.</violation>
</file>
Tip: cubic can generate docs of your entire codebase and keep them up to date. Try it here.
Re-trigger cubic
| kind: AgentReportKind::Daily, | ||
| pi_path: None, | ||
| open_claw_path: None, | ||
| grok_path: None, |
There was a problem hiding this comment.
P2: The None (default ccusage) arm does not hydrate grok_path from config. Other agent commands (pi, openclaw, grok) call config.apply_agent_args(...) with their respective path parameter. If a user has configured grokPath in their config file and runs bare ccusage, the Grok adapter will not pick up the configured path, potentially causing missed Grok data in the unified report.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At rust/crates/ccusage/src/main.rs, line 157:
<comment>The `None` (default `ccusage`) arm does not hydrate `grok_path` from config. Other agent commands (pi, openclaw, grok) call `config.apply_agent_args(...)` with their respective path parameter. If a user has configured `grokPath` in their config file and runs bare `ccusage`, the Grok adapter will not pick up the configured path, potentially causing missed Grok data in the unified report.</comment>
<file context>
@@ -146,12 +147,14 @@ fn main() -> Result<()> {
kind: AgentReportKind::Daily,
pi_path: None,
open_claw_path: None,
+ grok_path: None,
codex_speed: cli::CodexSpeed::Auto,
};
</file context>
| models_dev_pricing().and_then(|pricing| { | ||
| candidates | ||
| .iter() | ||
| .find_map(|candidate| pricing.find_entry_or_alias(candidate)) |
There was a problem hiding this comment.
P2: Candidate-based reports fail to price configured aliases for models available only through models.dev, producing a missing-pricing warning and $0 calculated cost. Resolve each candidate's configured alias before both fallback lookups, matching find() behavior.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At rust/crates/ccusage/src/pricing.rs, line 424:
<comment>Candidate-based reports fail to price configured aliases for models available only through models.dev, producing a missing-pricing warning and $0 calculated cost. Resolve each candidate's configured alias before both fallback lookups, matching `find()` behavior.</comment>
<file context>
@@ -387,6 +388,62 @@ impl PricingMap {
+ models_dev_pricing().and_then(|pricing| {
+ candidates
+ .iter()
+ .find_map(|candidate| pricing.find_entry_or_alias(candidate))
+ })
+ })
</file context>
| let output_tokens = model_usage.output_tokens; | ||
| let cache_read = model_usage.cached_read_tokens; | ||
| let reasoning_tokens = model_usage.reasoning_tokens; | ||
| if input_tokens == 0 && output_tokens == 0 && cache_read == 0 && reasoning_tokens == 0 { |
There was a problem hiding this comment.
P2: Rows with only totalTokens, or totals exceeding their breakdown, are omitted or undercounted in reports and cost. Normalize each model row with apply_total_token_fallback before this filter, billing the remainder as output-equivalent tokens.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At rust/crates/ccusage/src/adapter/grok/parser.rs, line 325:
<comment>Rows with only `totalTokens`, or totals exceeding their breakdown, are omitted or undercounted in reports and cost. Normalize each model row with `apply_total_token_fallback` before this filter, billing the remainder as output-equivalent tokens.</comment>
<file context>
@@ -0,0 +1,736 @@
+ let output_tokens = model_usage.output_tokens;
+ let cache_read = model_usage.cached_read_tokens;
+ let reasoning_tokens = model_usage.reasoning_tokens;
+ if input_tokens == 0 && output_tokens == 0 && cache_read == 0 && reasoning_tokens == 0 {
+ // Skip zero-token rows (including empty totalTokens-only metadata).
+ if model_usage.total_tokens == 0 {
</file context>
| let message_id = event_id | ||
| .as_ref() | ||
| .map(|id| format!("{id}:{raw_model}")) | ||
| .or_else(|| Some(format!("{session_id}:{timestamp_text}:{raw_model}"))); |
There was a problem hiding this comment.
P2: Distinct no-eventId turns for one model in the same timestamp are dropped, underreporting usage and cost. Include the token tuple in this fallback ID so only identical rows dedupe.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At rust/crates/ccusage/src/adapter/grok/parser.rs, line 356:
<comment>Distinct no-`eventId` turns for one model in the same timestamp are dropped, underreporting usage and cost. Include the token tuple in this fallback ID so only identical rows dedupe.</comment>
<file context>
@@ -0,0 +1,736 @@
+ let message_id = event_id
+ .as_ref()
+ .map(|id| format!("{id}:{raw_model}"))
+ .or_else(|| Some(format!("{session_id}:{timestamp_text}:{raw_model}")));
+ let data = UsageEntry {
+ session_id: Some(session_id.clone()),
</file context>
| .and_then(|info| info.id.clone()) | ||
| .filter(|id| !id.is_empty()) | ||
| { | ||
| // Prefer explicit summary session id when present. |
There was a problem hiding this comment.
P2: summary.info.id is parsed from summary.json but discarded — the comment says to prefer it over the path-derived session ID, but the assignment was never completed. Either complete the override (session_id = id;) or remove the dead parsing block and its comment.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At rust/crates/ccusage/src/adapter/grok/parser.rs, line 212:
<comment>`summary.info.id` is parsed from `summary.json` but discarded — the comment says to prefer it over the path-derived session ID, but the assignment was never completed. Either complete the override (`session_id = id;`) or remove the dead parsing block and its comment.</comment>
<file context>
@@ -0,0 +1,736 @@
+ .and_then(|info| info.id.clone())
+ .filter(|id| !id.is_empty())
+ {
+ // Prefer explicit summary session id when present.
+ let _ = id;
+ }
</file context>
| @@ -0,0 +1,73 @@ | |||
| use std::collections::BTreeMap; | |||
There was a problem hiding this comment.
P3: Grok report behavior now has a byte-for-byte copy of OpenClaw's report implementation. A shared agent-report helper would prevent future JSON/aggregation fixes from drifting between adapters.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At rust/crates/ccusage/src/adapter/grok/report.rs, line 11:
<comment>Grok report behavior now has a byte-for-byte copy of OpenClaw's report implementation. A shared agent-report helper would prevent future JSON/aggregation fixes from drifting between adapters.</comment>
<file context>
@@ -0,0 +1,73 @@
+ summarize_by_key, summarize_summaries_by_bucket, totals_json,
+};
+
+pub(crate) fn report_from_rows(rows: &[crate::UsageSummary], kind: AgentReportKind) -> Value {
+ let rows_json = rows
+ .iter()
</file context>
| if is_dynamic_router_model(model) { | ||
| format!( | ||
| "WARN Missing embedded pricing for {model}; cost excludes this model. Run without --offline or update ccusage after pricing is added." | ||
| "WARN No deterministic token rate for dynamic model router {model}; cost excludes this usage. Configure pricingOverrides only if your logs expose the resolved underlying model." |
There was a problem hiding this comment.
P3: Dynamic-router users with a known blended/contract rate are incorrectly told not to configure it unless logs expose the underlying model. An override keyed to {model} works; describe the resolved model as an accuracy improvement rather than a prerequisite.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At rust/crates/ccusage/src/output.rs, line 374:
<comment>Dynamic-router users with a known blended/contract rate are incorrectly told not to configure it unless logs expose the underlying model. An override keyed to `{model}` works; describe the resolved model as an accuracy improvement rather than a prerequisite.</comment>
<file context>
@@ -369,19 +369,42 @@ pub(crate) fn missing_pricing_warnings_for_models<'a>(
+ if is_dynamic_router_model(model) {
format!(
- "WARN Missing embedded pricing for {model}; cost excludes this model. Run without --offline or update ccusage after pricing is added."
+ "WARN No deterministic token rate for dynamic model router {model}; cost excludes this usage. Configure pricingOverrides only if your logs expose the resolved underlying model."
+ )
+ } else if is_unsupported_or_undetected_model(model) {
</file context>
| "WARN No deterministic token rate for dynamic model router {model}; cost excludes this usage. Configure pricingOverrides only if your logs expose the resolved underlying model." | |
| "WARN No deterministic token rate for dynamic model router {model}; cost excludes this usage. Configure pricingOverrides for {model} when you have an appropriate estimated rate; a resolved underlying model improves accuracy." |
| ┌────────────┬──────────────────────────┬───────────┬───────────┬──────────────┬────────────┬──────────────┬──────────────┐ | ||
| │ Date │ Models │ Input │ Output │ Cache Create │ Cache Read │ Total Tokens │ Cost (USD) │ | ||
| ├────────────┼──────────────────────────┼───────────┼───────────┼──────────────┼────────────┼──────────────┼──────────────┤ | ||
| │ 2026-07-13 │ - [grok] grok-4.5 │ 70,378 │ 8,294 │ 0 │ 38,272 │ 124,015 │ $0.24 │ |
There was a problem hiding this comment.
P3: The example table's Total Tokens (124,015) exceeds the sum of visible columns (116,944) by 7,071, and the $0.24 cost doesn't match the documented pricing rates. The difference is reasoning tokens, but there's no reasoning column or inline note in the table. Consider either adding a reasoning tokens column, showing a footnote in the table, or making the example numbers balance cleanly (total = input + output + cache_create + cache_read) so users aren't confused by the gap.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At docs/guide/grok/index.md, line 105:
<comment>The example table's Total Tokens (124,015) exceeds the sum of visible columns (116,944) by 7,071, and the $0.24 cost doesn't match the documented pricing rates. The difference is reasoning tokens, but there's no reasoning column or inline note in the table. Consider either adding a reasoning tokens column, showing a footnote in the table, or making the example numbers balance cleanly (total = input + output + cache_create + cache_read) so users aren't confused by the gap.</comment>
<file context>
@@ -0,0 +1,174 @@
+┌────────────┬──────────────────────────┬───────────┬───────────┬──────────────┬────────────┬──────────────┬──────────────┐
+│ Date │ Models │ Input │ Output │ Cache Create │ Cache Read │ Total Tokens │ Cost (USD) │
+├────────────┼──────────────────────────┼───────────┼───────────┼──────────────┼────────────┼──────────────┼──────────────┤
+│ 2026-07-13 │ - [grok] grok-4.5 │ 70,378 │ 8,294 │ 0 │ 38,272 │ 124,015 │ $0.24 │
+├────────────┼──────────────────────────┼───────────┼───────────┼──────────────┼────────────┼──────────────┼──────────────┤
+│ Total │ │ 70,378 │ 8,294 │ 0 │ 38,272 │ 124,015 │ $0.24 │
</file context>
| | pi-agent | `pi` | `ccusage pi monthly` | | ||
| | Goose | `goose` | `ccusage goose daily` | | ||
| | OpenClaw | `openclaw` | `ccusage openclaw daily` | | ||
| | Grok Build | `grok` | `ccusage grok daily` | |
There was a problem hiding this comment.
P3: Same naming inconsistency as in the index.md table: the all-reports summary table says "Grok Build" instead of "Grok Build CLI". Consider aligning to match the product name used in the sidebar, Grok guide, and apps/ccusage README.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At docs/guide/all-reports.md, line 52:
<comment>Same naming inconsistency as in the index.md table: the all-reports summary table says "Grok Build" instead of "Grok Build CLI". Consider aligning to match the product name used in the sidebar, Grok guide, and apps/ccusage README.</comment>
<file context>
@@ -49,6 +49,7 @@ Unified tables include an **Agent** column so you can compare sources in one vie
| pi-agent | `pi` | `ccusage pi monthly` |
| Goose | `goose` | `ccusage goose daily` |
| OpenClaw | `openclaw` | `ccusage openclaw daily` |
+| Grok Build | `grok` | `ccusage grok daily` |
| Kilo | `kilo` | `ccusage kilo daily` |
| Kimi | `kimi` | `ccusage kimi daily` |
</file context>
| | Grok Build | `grok` | `ccusage grok daily` | | |
| | Grok Build CLI | `grok` | `ccusage grok daily` | |
|
Historical audit: this pull request was auto-closed by the legacy contributor gate. That closure did not assess technical importance. Audit result: needs review. The current state does not prove resolution, but a fresh technical or product check is required before deciding whether the underlying request is still relevant. This closed PR will not be revived as-is; create a new PR only after reviewing the related issue and current |
Summary
Adds a first-class Grok Build CLI agent source so local Grok TUI usage appears in
ccusage grok …and unified multi-agent reports (ccusage daily, etc.), with token totals and estimated USD cost.What changed
adapter/grokmodule: discover~/.grok/sessions/**/updates.jsonl, parseturn_completedusage /modelUsage, load into shared report pathsccusage grok daily|monthly|session, config schema (grokPath/GROK_HOME/--grok-path), all-agent rollup + progress labelgrok-4.5,grok-composer-2.5-fast, plus existing keys)Why
Grok Build CLI already writes per-turn usage under
~/.grok, but without an adapter those sessions never show up in ccusage. Users who run Grok heavily get incomplete multi-agent cost reports.Testing
~/.grok/sessions:ccusage grok dailyand unifiedccusage dailyshow Grok with model labels like[grok] grok-4.5Notes
~/.grok(override withGROK_HOMEor--grok-path)mainbefore merge (main has moved since the feature base)LOCAL_PRICING_PATCH.mdanddocs/plans/…are local/planning notes on the fork branch; happy to drop them from the PR if preferredPlan
See
docs/plans/2026-07-13-001-feat-grok-cli-usage-adapter-plan.mdfor acceptance criteria and design notes.Need help on this PR? Tag
/codesmithwith what you need. Autofix is disabled.Summary by cubic
Adds a first-class Grok Build CLI adapter so local sessions under
~/.grokshow up inccusage grok …and unified daily/monthly/session reports with token totals and estimated USD cost. This expands multi-agent coverage and keeps Grok usage in the same views as other agents.New Features
~/.grok/sessions/**/updates.jsonl, readsturn_completedmodelUsage, and loads per-model rows with token totals.ccusage grok daily|monthly|sessioncommands, Grok now included inccusage daily|monthly|session, and a Grok progress label.--grok-path,GROK_HOME, orgrok.defaults.grokPath.grok-4.5andgrok-composer-2.5-fast, and reasoning tokens billed at the output rate.Migration
~/.grok; override withGROK_HOMEor--grok-path.ccusage grok daily|monthly|sessionfor focused reports, orccusage dailyfor all agents.pricingOverridesin config; token-based costs are estimates.Written for commit c9f8a96. Summary will update on new commits.
Summary by CodeRabbit
ccusagedata source, includingccusage grok daily|monthly|sessionand--grok-pathtargeting.updates.jsonldata and included Grok in combined/all reports.