-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Classify Codex rate windows by duration (5h/weekly/30-day) #2600
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
1faa444
cbbc44d
1ccd7c4
352e00b
2e7d47b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -175,8 +175,13 @@ struct CodexConsumerProjection { | |
| enum RateLane: String { | ||
| case session | ||
| case weekly | ||
| case monthly | ||
| } | ||
|
|
||
| static let sessionWindowMinutes = 5 * 60 | ||
| static let weeklyWindowMinutes = 7 * 24 * 60 | ||
| static let monthlyWindowMinutes = 30 * 24 * 60 | ||
|
|
||
| enum SupplementalMetric: String { | ||
| case codeReview | ||
| } | ||
|
|
@@ -311,7 +316,7 @@ struct CodexConsumerProjection { | |
| session: window, | ||
| weekly: self.rateWindowsByLane[.weekly], | ||
| evaluationTime: self.evaluationTime) | ||
| case .weekly: | ||
| case .weekly, .monthly: | ||
| return window | ||
| } | ||
| } | ||
|
|
@@ -335,6 +340,47 @@ struct CodexConsumerProjection { | |
| return nil | ||
| } | ||
|
|
||
| /// Automatic keeps the standard session window unless a longer window (e.g. a 30-day | ||
| /// primary) would hide a genuine weekly quota from the menu bar. | ||
| func automaticMenuBarWindow() -> RateWindow? { | ||
| let windows = self.visibleRateLanes.compactMap { | ||
| self.menuBarSelectableRateWindow(for: $0) | ||
| } | ||
| guard let weekly = self.menuBarSelectableRateWindow(for: .weekly), | ||
| windows.contains(where: { | ||
| $0.windowMinutes.map { $0 > Self.weeklyWindowMinutes } ?? false | ||
| }) | ||
|
Comment on lines
+349
to
+352
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When a Codex snapshot has a 30-day primary and a weekly secondary, this guard makes Useful? React with 👍 / 👎. |
||
| else { | ||
| return windows.first | ||
| } | ||
| return weekly | ||
| } | ||
|
|
||
| static func rateTitle( | ||
| lane: RateLane, | ||
| windowMinutes: Int?, | ||
| sessionLabel: String, | ||
| weeklyLabel: String) -> String | ||
| { | ||
| switch windowMinutes { | ||
| case self.sessionWindowMinutes: | ||
| L(sessionLabel) | ||
| case self.weeklyWindowMinutes: | ||
| L(weeklyLabel) | ||
| case self.monthlyWindowMinutes: | ||
| L("Monthly") | ||
| default: | ||
| switch lane { | ||
| case .session: | ||
| L(sessionLabel) | ||
| case .weekly: | ||
| L(weeklyLabel) | ||
| case .monthly: | ||
| L("Monthly") | ||
| } | ||
| } | ||
| } | ||
|
|
||
| var nextMenuBarStateChangeAt: Date? { | ||
| self.rateWindowsByLane.values.compactMap { window in | ||
| guard window.remainingPercent <= 0, | ||
|
|
@@ -407,7 +453,7 @@ struct CodexConsumerProjection { | |
| } | ||
|
|
||
| private static func planUtilizationLanes(from rateWindowsByLane: [RateLane: RateWindow]) -> [PlanUtilizationLane] { | ||
| let semanticOrder: [RateLane] = [.session, .weekly] | ||
| let semanticOrder: [RateLane] = [.session, .weekly, .monthly] | ||
| return semanticOrder.compactMap { lane in | ||
| guard let window = rateWindowsByLane[lane] else { return nil } | ||
| return PlanUtilizationLane(role: self.planUtilizationRole(for: lane), window: window) | ||
|
|
@@ -420,9 +466,15 @@ struct CodexConsumerProjection { | |
| .session | ||
| case .weekly: | ||
| .weekly | ||
| case .monthly: | ||
| .monthly | ||
|
Comment on lines
+469
to
+470
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
For a Codex snapshot with a 43,200-minute primary, this new role causes Useful? React with 👍 / 👎. |
||
| } | ||
| } | ||
|
|
||
| static func planUtilizationSeriesNames(snapshot: UsageSnapshot) -> Set<PlanUtilizationSeriesName> { | ||
| Set(self.rateWindowsByLane(snapshot: snapshot).keys.map { self.planUtilizationRole(for: $0) }) | ||
| } | ||
|
|
||
| private enum SnapshotSlot { | ||
| case primary | ||
| case secondary | ||
|
|
@@ -432,10 +484,12 @@ struct CodexConsumerProjection { | |
| guard let window else { return nil } | ||
|
|
||
| let lane: RateLane = switch window.windowMinutes { | ||
| case 300: | ||
| case Self.sessionWindowMinutes: | ||
| .session | ||
| case 10080: | ||
| case Self.weeklyWindowMinutes: | ||
| .weekly | ||
| case Self.monthlyWindowMinutes: | ||
| .monthly | ||
|
Comment on lines
+491
to
+492
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When a snapshot contains the targeted 43,200-minute primary plus a weekly secondary and a trusted prior snapshot is available, this classification makes Useful? React with 👍 / 👎. |
||
| default: | ||
| switch slot { | ||
| case .primary: | ||
|
|
@@ -591,7 +645,9 @@ extension UsageStore { | |
| usedPercent: usedPercent, windowMinutes: nil, resetsAt: nil, resetDescription: nil) | ||
| case .primaryAndSecondary: | ||
| return windows.prefix(2).max(by: { $0.usedPercent < $1.usedPercent }) | ||
| case .automatic, .primary, .monthlyPlan: | ||
| case .automatic: | ||
| return projection.automaticMenuBarWindow() | ||
| case .primary, .monthlyPlan: | ||
| return first | ||
| } | ||
| } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When the Codex primary is a 43,200-minute window, this now labels it “Monthly,” but
usageSectionstill passes every raw primary toUsagePaceText.sessionSummary. That helper accepts Codex windows of any duration, so the provider submenu can render a session-style “Projected empty…” pace beneath the monthly quota, unlike the newly classified menu card where monthly pace is explicitly suppressed. Route the submenu through the classified lane or only add the session summary for the canonical 300-minute window.Useful? React with 👍 / 👎.