feat: add account balance status bar item (carries #1970)#2257
feat: add account balance status bar item (carries #1970)#2257HUQIANTAO wants to merge 13 commits into
Conversation
- Merge upstream Tokens status item with Balance from PR Hmbown#1970 - Keep Balance opt-in (not in default_footer) - Fix clippy: collapsible if, useless format!, redundant closure - Show balance only when total > 0 Co-Authored-By: MoriTang <ts25504@gmail.com> Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
Warning You have reached your daily quota limit. Please wait up to 24 hours and I will start processing your requests again! |
| let currency = match info.currency.as_str() { | ||
| "CNY" | "cny" => "¥", | ||
| _ => "$", | ||
| }; |
There was a problem hiding this comment.
Unknown currency codes silently display a
$ prefix
Any currency that isn't "CNY" or "cny" — including "USD", "EUR", "KRW", etc. — falls through to the _ => "$" arm. This means a hypothetical future "EUR" balance would show as $42.50 rather than €42.50, which is misleading. Matching "USD" explicitly and falling back to the raw code string is safer.
| let currency = match info.currency.as_str() { | |
| "CNY" | "cny" => "¥", | |
| _ => "$", | |
| }; | |
| let currency: std::borrow::Cow<'_, str> = match info.currency.as_str() { | |
| "CNY" | "cny" => "¥".into(), | |
| "USD" | "usd" => "$".into(), | |
| other => other.into(), | |
| }; |
Previously the balance chip only appeared after a completed turn. Now it also fetches: - On first frame (startup) for DeepSeek/DeepSeekCN providers - After switching to DeepSeek, and clears when switching away Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Balance (account remaining) is more actionable than session cost, so it should drop later when the footer is width-constrained. Tier order: status → cost → balance → model (was: status → balance → cost → model) Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
When a dev build writes new status item variants (e.g. "balance") to config.toml, the stable build must not crash with "unknown variant". Add a tolerant deserializer that filters unrecognized keys via StatusItem::from_key() and logs a warning. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
Independent review (Devin): Carry fidelity: confirmed clean carry of #1970 — no scope creep beyond the target feature. Rate-limiting / caching: balance fetches fire on startup (once), on every Bug — hardcoded base URL: Provider-gating: Silent visual regression: status_picker changes Test coverage: good. 7 new v0.8.48 (PR #2256) overlap: high — both PRs touch |
- Use config.deepseek_base_url() instead of hardcoded api.deepseek.com - Add 60-second debounce (BALANCE_FETCH_COOLDOWN) to prevent rapid consecutive balance API calls during provider switches - Fix [x] → [✓] regression in status_picker Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
Want your agent to iterate on Greptile's feedback? Try greploops. |
Summary
https://api.deepseek.com/user/balancefor DeepSeek/DeepSeekCN providers/statusline— does not appear in the default footerChanges from #1970
main(resolved conflicts with upstreamTokensstatus item)footer_balance_spans_empty_when_balance_is_zerotest — zero balance now hides the chipTest plan
cargo fmt --all -- --checkpassescargo clippy --workspace --all-targets --all-features --locked -- -D warningspassescargo test --workspace --all-features --lockedpasses (4 pre-existing failures unrelated)/statuslinetoggle Balance, verify chip appears in footerSupersedes #1970
Greptile Summary
Adds a DeepSeek account balance footer chip: fetches from
GET /user/balanceon startup and after each turn completion, stores the result in a sharedArc<Mutex<Option<BalanceInfo>>>, and renders it in the left status cluster via a newFooterProps::balancefield. The chip is opt-in via/statuslineand hidden automatically for zero balances and non-DeepSeek providers.crates/tui/src/config.rs— newBalancevariant with a forward-compatible TOML deserializer (deser_status_items) that silently skips unknown items so stable and dev builds can share config files.crates/tui/src/tui/ui.rs— three fetch sites (startup one-shot, per-turn refresh with 60 s cooldown, and provider-switch trigger); balance is cleared from the cell when switching away from DeepSeek.crates/tui/src/tui/widgets/footer.rs— two-pass width-tiered layout updated to include balance between model and cost;build_status_line_spansreceives a newbalanceparameter but its name and position are swapped at every call site.Confidence Score: 4/5
Safe to merge after addressing the unconditional balance fetch, which makes periodic API calls for all DeepSeek users regardless of whether they have opted into the Balance display chip.
The balance API is fetched on startup and after every turn completion for every DeepSeek user, with no guard checking whether the Balance status item is actually enabled. The PR describes the chip as opt-in, so users who have never toggled it on will silently get periodic /user/balance API calls made with their key. Everything else — the Mutex-based shared cell, the 60 s cooldown, provider-switch clearing, the forward-compatible config deserializer, and the unit test coverage — is solid.
The three balance fetch sites in crates/tui/src/tui/ui.rs (startup, turn completion, and provider switch) need a status_items.contains(Balance) guard.
Important Files Changed
Sequence Diagram
sequenceDiagram participant UL as run_event_loop participant BG as tokio::spawn (background) participant DS as DeepSeek /user/balance participant BC as balance_cell (Mutex) participant UI as footer_balance_spans UL->>UL: "startup: balance_initiated=false & DeepSeek provider" UL->>BG: spawn fetch (api_key, base_url) BG->>DS: GET /user/balance (Bearer token) DS-->>BG: "BalanceResponse { balance_infos }" BG->>BC: lock → write BalanceInfo UL->>UL: TurnComplete event UL->>UL: cooldown_expired? UL->>BG: spawn fetch (api_key, base_url) BG->>DS: GET /user/balance DS-->>BG: BalanceResponse BG->>BC: lock → write BalanceInfo UL->>UL: SwitchProvider to DeepSeek UL->>BG: spawn fetch BG->>DS: GET /user/balance DS-->>BG: BalanceResponse BG->>BC: lock → write BalanceInfo UL->>UL: SwitchProvider away from DeepSeek UL->>BC: lock → write None (clear balance) UI->>BC: lock (read) BC-->>UI: "Option<BalanceInfo>" UI->>UI: format label (¥/$ + tier rounding) UI-->>UL: "Vec<Span> for footer render"Comments Outside Diff (2)
crates/tui/src/tui/views/status_picker.rs, line 801 (link)[✓]→[x]visual regressionThe checked mark was silently changed from the Unicode checkmark
[✓]to a plain ASCII[x]. This isn't listed in the PR description's Clippy fixes and isn't related to the balance feature — it changes the visual appearance of the/statuslinepicker for all users, making checked rows look less distinct. If intentional (e.g. terminal-compat), it should be called out explicitly.crates/tui/src/tui/ui.rs, line 602-652 (link)The PR description explicitly calls the Balance chip "opt-in via
/statusline", yet the balance fetch fires unconditionally on startup (here) and after every turn completion — with no check for whetherStatusItem::Balanceis even present inapp.status_items. A DeepSeek user who has never touched/statuslineand never enabled the Balance chip will silently get periodicGET /user/balancecalls made with their API key on every session start and every turn. Addingapp.status_items.contains(&StatusItem::Balance)as an additional guard in all three fetch sites (startup, turn completion, and provider switch) would make the fetch truly opt-in and consistent with the stated design.Reviews (5): Last reviewed commit: "fix(balance): address review feedback on..." | Re-trigger Greptile