Skip to content

Import pr 100 into fork#120

Closed
gkachru wants to merge 3 commits into
Haleclipse:masterfrom
gkachru:import-pr-100
Closed

Import pr 100 into fork#120
gkachru wants to merge 3 commits into
Haleclipse:masterfrom
gkachru:import-pr-100

Conversation

@gkachru
Copy link
Copy Markdown

@gkachru gkachru commented May 3, 2026

Import #100

Summary by Sourcery

Split API usage display into separate 5-hour and 7-day segments while sharing rate limit data and caching across them, and wire the new segments through config, themes, and UI.

New Features:

  • Add dedicated Usage5h and Usage7d statusline segments showing separate 5-hour and 7-day rate limit utilization and reset times.
  • Expose rate limit metadata from Claude Code input to segments for direct consumption without extra API calls when available.

Bug Fixes:

  • Ensure API usage cache schema supports separate 5-hour and 7-day reset timestamps to avoid losing five-hour reset information.

Enhancements:

  • Introduce a shared, process-lifetime rate limit cache and helpers to avoid redundant network I/O across usage-related segments.
  • Fallback to Anthropic OAuth usage API with proxy support and on-disk caching when in-process rate limits are not provided.
  • Deprecate the combined Usage segment in favor of the new per-window variants while keeping it functional for backward compatibility.
  • Add automatic config migration to insert the new Usage5h and Usage7d segments based on an existing Usage segment and disable the legacy one.

Tests:

  • Extend preview/mock segment data to cover the new Usage5h and Usage7d segments for UI previews.

qiqizjl and others added 2 commits March 21, 2026 17:34
…1.80 rate_limits

Support the rate_limits field added in Claude Code 2.1.80, which provides
5-hour and 7-day rate limit usage (used_percentage and resets_at) directly
via statusline scripts, eliminating the need for separate API calls.

- Parse rate_limits from Claude Code input (unix timestamp resets_at)
- Split single Usage segment into Usage5h and Usage7d for independent
  display of 5-hour and 7-day windows with their own reset times
- Auto-migrate old configs: insert usage_5h + usage_7d, disable legacy usage
- Mark legacy Usage as "(deprecated)" in GUI
- Fix resets_at type mismatch (i64 vs String) that caused statusline to
  disappear after first invocation
- Update all 9 theme presets with new segment definitions

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…tion

Each theme file now has a single usage_segment_base(id) function that
holds all configuration, with usage_segment/usage_5h_segment/usage_7d_segment
as one-liner wrappers. Eliminates ~535 lines of duplicated config across 9 themes.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@sourcery-ai
Copy link
Copy Markdown

sourcery-ai Bot commented May 3, 2026

Reviewer's Guide

Refactors the usage/rate-limit segment to support separate 5-hour and 7-day views backed by a shared rate‑limit data source, adds new segment IDs and config migration, and updates all themes, presets, and UI to expose the new segments while keeping the legacy combined usage segment deprecated but functional.

Sequence diagram for shared rate limit data fetching for usage segments

sequenceDiagram
    participant Statusline
    participant Usage5hSegment
    participant RateLimitCache
    participant RateLimitFetcher
    participant InputData
    participant FileCache
    participant AnthropicAPI

    Statusline->>Usage5hSegment: collect(input)
    Usage5hSegment->>RateLimitCache: fetch_rate_limit_data(input)
    alt cache already initialized
        RateLimitCache-->>Usage5hSegment: Some(RateLimitData)
    else cache not initialized
        RateLimitCache->>RateLimitFetcher: fetch_rate_limit_data_inner(input)
        alt InputData has rate_limits
            RateLimitFetcher->>InputData: read rate_limits
            InputData-->>RateLimitFetcher: RateLimits
            RateLimitFetcher-->>RateLimitCache: Some(RateLimitData)
        else no rate_limits in InputData
            RateLimitFetcher->>FileCache: load_cache()
            alt cache file exists and fresh
                FileCache-->>RateLimitFetcher: ApiUsageCache
                RateLimitFetcher-->>RateLimitCache: Some(RateLimitData)
            else cache missing or stale
                RateLimitFetcher->>AnthropicAPI: fetch_api_usage()
                alt HTTP success
                    AnthropicAPI-->>RateLimitFetcher: ApiUsageResponse
                    RateLimitFetcher->>FileCache: save_cache(ApiUsageCache)
                    FileCache-->>RateLimitFetcher: ok
                    RateLimitFetcher-->>RateLimitCache: Some(RateLimitData)
                else HTTP failure
                    FileCache-->>RateLimitFetcher: Option<ApiUsageCache>
                    RateLimitFetcher-->>RateLimitCache: Option<RateLimitData> from cache
                end
            end
        end
        RateLimitCache-->>Usage5hSegment: Option<RateLimitData>
    end
    Usage5hSegment-->>Statusline: SegmentData (primary, secondary, metadata)

    %% Usage7dSegment and legacy UsageSegment call the same fetch_rate_limit_data and reuse RateLimitCache in the same way
Loading

Updated class diagram for usage segments and rate limit models

classDiagram
    class Segment {
        <<trait>>
        +collect(input: InputData) SegmentData?
        +id() SegmentId
    }

    class UsageSegment {
        +new() UsageSegment
        +collect(input: InputData) SegmentData?
        +id() SegmentId
    }
    class Usage5hSegment {
        +new() Usage5hSegment
        +collect(input: InputData) SegmentData?
        +id() SegmentId
    }
    class Usage7dSegment {
        +new() Usage7dSegment
        +collect(input: InputData) SegmentData?
        +id() SegmentId
    }

    UsageSegment ..|> Segment
    Usage5hSegment ..|> Segment
    Usage7dSegment ..|> Segment

    class RateLimitData {
        +f64 five_hour_util
        +Option~String~ five_hour_resets_at
        +f64 seven_day_util
        +Option~String~ seven_day_resets_at
    }

    class ApiUsageResponse {
        +UsagePeriod five_hour
        +UsagePeriod seven_day
    }

    class UsagePeriod {
        +f64 utilization
        +Option~String~ resets_at
    }

    class ApiUsageCache {
        +f64 five_hour_utilization
        +Option~String~ five_hour_resets_at
        +f64 seven_day_utilization
        +Option~String~ resets_at
        +String cached_at
    }

    class RateLimitPeriod {
        +f64 used_percentage
        +Option~i64~ resets_at
    }

    class RateLimits {
        +Option~RateLimitPeriod~ five_hour
        +Option~RateLimitPeriod~ seven_day
    }

    class InputData {
        +Model model
        +String cwd
        +String transcript_path
        +Option~Cost~ cost
        +Option~OutputStyle~ output_style
        +Option~RateLimits~ rate_limits
    }

    class SegmentData {
        +String primary
        +String secondary
        +HashMap~String,String~ metadata
    }

    class SegmentId {
        <<enumeration>>
        Usage
        Usage5h
        Usage7d
        Directory
        Git
        ContextWindow
        Cost
        Session
        OutputStyle
        Update
    }

    class Config {
        +Vec~SegmentConfig~ segments
        +migrate() void
    }

    class SegmentConfig {
        +SegmentId id
        +bool enabled
        +IconConfig icon
        +ColorConfig colors
        +TextStyleConfig styles
        +HashMap~String, Value~ options
    }

    class ThemePresetModule {
        <<module>>
        +usage_segment() SegmentConfig
        +usage_5h_segment() SegmentConfig
        +usage_7d_segment() SegmentConfig
        +usage_segment_base(id: SegmentId) SegmentConfig
    }

    Config --> SegmentConfig
    SegmentConfig --> SegmentId
    InputData --> RateLimits
    RateLimits --> RateLimitPeriod
    ApiUsageResponse --> UsagePeriod
    ApiUsageCache --> UsagePeriod
    Usage5hSegment --> RateLimitData
    Usage7dSegment --> RateLimitData
    UsageSegment --> RateLimitData
    Usage5hSegment --> SegmentData
    Usage7dSegment --> SegmentData
    UsageSegment --> SegmentData
    ThemePresetModule --> SegmentConfig
    SegmentId <|.. UsageSegment
    SegmentId <|.. Usage5hSegment
    SegmentId <|.. Usage7dSegment
Loading

Flow diagram for config loading, migration, and exposure of new usage segments

flowchart LR
    A[Config_loader::load_user_config] --> B[Read config.toml]
    B --> C[Deserialize into Config]
    C --> D[Config.migrate]

    D -->|has Usage but no Usage5h or Usage7d| E[Find existing Usage SegmentConfig]
    E --> F[Clone icon, colors, styles, options]
    F --> G[Insert Usage5h SegmentConfig after Usage]
    G --> H[Insert Usage7d SegmentConfig after Usage5h]
    H --> I[Disable original Usage segment]

    D -->|already migrated or no Usage| J[No changes]

    I --> K[Final Config.segments]
    J --> K

    K --> L[ThemePresets::theme_default]
    K --> M[ThemePresets::theme_cometix]
    K --> N[Other theme presets]

    L --> O[theme_default::usage_5h_segment]
    L --> P[theme_default::usage_7d_segment]

    M --> Q[theme_cometix::usage_5h_segment]
    M --> R[theme_cometix::usage_7d_segment]

    O --> S[UI SegmentListComponent]
    P --> S
    Q --> S
    R --> S

    S --> T[User sees Usage 5h and Usage 7d entries]
    S --> U[Legacy Usage shown as Usage - deprecated]
Loading

File-Level Changes

Change Details Files
Refactor usage segment logic into shared helpers and introduce separate 5-hour and 7-day usage segments backed by a process-lifetime rate limit cache.
  • Introduce ApiUsageResponse/UsagePeriod API types and extend ApiUsageCache with an optional five-hour reset timestamp for backward compatibility.
  • Add RateLimitData struct and a process-lifetime OnceLock cache so rate limit data is fetched at most once per statusline render and shared across Usage, Usage5h, and Usage7d segments.
  • Extract shared helpers for circle icon selection, reset-time formatting, cache path/load/save/validation, Claude Code version detection, proxy lookup, and Anthropic API usage fetch with configurable timeout and base URL.
  • Implement fetch_rate_limit_data to prefer rate_limits from InputData (with UNIX timestamps converted via TimeZone::timestamp_opt) and fall back to cached or freshly-fetched Anthropic API usage data.
  • Split the original UsageSegment into three implementations: Usage5hSegment (5-hour only), Usage7dSegment (7-day only), and a legacy UsageSegment that presents five-hour primary text with seven-day-based icon/reset info, all driven by shared RateLimitData.
src/core/segments/usage.rs
Wire the new Usage5h and Usage7d segments into the statusline, themes, presets, preview, and UI, deprecating the original Usage label while keeping it available.
  • Export Usage5hSegment and Usage7dSegment from the segments module and handle their collection in core/statusline::collect_all_segments.
  • Add Usage5h and Usage7d variants to SegmentId (with serde renames) and extend UI components (app, segment_list, settings) to show them with labels and mark Usage as deprecated in labels.
  • Update the preview component to provide mock SegmentData for Usage5h and Usage7d with appropriate dynamic_icon values.
  • Factor each theme’s usage segment into a shared usage_segment_base(SegmentId) and expose usage_segment, usage_5h_segment, and usage_7d_segment constructors; update ThemePresets to include Usage5h and Usage7d instead of the single Usage segment across all themes.
src/core/statusline.rs
src/core/segments/mod.rs
src/config/types.rs
src/ui/app.rs
src/ui/components/segment_list.rs
src/ui/components/settings.rs
src/ui/components/preview.rs
src/ui/themes/theme_default.rs
src/ui/themes/theme_cometix.rs
src/ui/themes/theme_gruvbox.rs
src/ui/themes/theme_minimal.rs
src/ui/themes/theme_nord.rs
src/ui/themes/theme_powerline_dark.rs
src/ui/themes/theme_powerline_light.rs
src/ui/themes/theme_powerline_rose_pine.rs
src/ui/themes/theme_powerline_tokyo_night.rs
src/ui/themes/presets.rs
Add configuration-level support and migration for the new usage segments so existing configs gain Usage5h/Usage7d automatically.
  • Extend InputData with an optional RateLimits structure (RateLimitPeriod for five_hour/seven_day) that can be populated by Claude Code and consumed by fetch_rate_limit_data.
  • Implement Config::migrate to detect existing configs that use the legacy Usage segment without Usage5h/Usage7d, clone that segment into new Usage5h and Usage7d entries (preserving icon/colors/styles/options), disable the original Usage, and insert the new segments immediately after it.
  • Invoke config.migrate() in the config loader after deserializing TOML so all runtime configs are normalized to include the new segments.
src/config/types.rs
src/config/loader.rs

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link
Copy Markdown

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

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

Hey - I've found 1 issue, and left some high level feedback:

  • RATE_LIMIT_CACHE stores an Option<RateLimitData> and caches None as well, which means a transient failure or missing token on the first call will disable rate limit data for the rest of the process; consider only caching successful Some results so later calls can retry.
  • In RateLimitPeriod, the resets_at: Option<i64> is treated as a Unix timestamp in seconds (via Utc.timestamp_opt(ts, 0)); consider making the unit explicit in the type/field name or adding a small conversion helper to avoid future confusion if the source ever returns milliseconds.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- `RATE_LIMIT_CACHE` stores an `Option<RateLimitData>` and caches `None` as well, which means a transient failure or missing token on the first call will disable rate limit data for the rest of the process; consider only caching successful `Some` results so later calls can retry.
- In `RateLimitPeriod`, the `resets_at: Option<i64>` is treated as a Unix timestamp in seconds (via `Utc.timestamp_opt(ts, 0)`); consider making the unit explicit in the type/field name or adding a small conversion helper to avoid future confusion if the source ever returns milliseconds.

## Individual Comments

### Comment 1
<location path="src/core/segments/usage.rs" line_range="44-45" />
<code_context>
-        }
+// Process-lifetime cache: computed once per statusline render, shared across
+// all Usage segments (Usage, Usage5h, Usage7d) to avoid redundant I/O.
+static RATE_LIMIT_CACHE: std::sync::OnceLock<Option<RateLimitData>> =
+    std::sync::OnceLock::new();
+
+// ── Shared helpers ────────────────────────────────────────────────────────────
</code_context>
<issue_to_address>
**issue (bug_risk):** Avoid permanently caching a `None` rate limit result in the `OnceLock` to keep retrying after transient failures.

If the first `fetch_rate_limit_data_inner` call returns `None` (e.g. transient API failure), `RATE_LIMIT_CACHE` will be set to `None` and every subsequent call will short-circuit to `None`, effectively disabling usage segments for the rest of the process.

Consider only caching successful results (e.g. `OnceLock<RateLimitData>` and calling `get_or_init` only on success), or keep `OnceLock<Option<_>>` but avoid initializing it when the result is `None` so later calls can retry.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread src/core/segments/usage.rs Outdated
…amp unit

Cache only successful RateLimitData in the process-lifetime OnceLock so a
None result (transient API failure or missing token) no longer disables
usage segments for the rest of the process.

Rename RateLimitPeriod.resets_at to resets_at_unix_secs (with serde rename
to preserve the wire format) and extract a unix_secs_to_rfc3339 helper to
make the unit assumption explicit at every call site.
@gkachru gkachru closed this May 3, 2026
@gkachru gkachru reopened this May 3, 2026
@gkachru gkachru closed this May 3, 2026
@gkachru gkachru deleted the import-pr-100 branch May 3, 2026 17:47
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.

2 participants