Skip to content

Commit 6893b11

Browse files
committed
Fix Kimi window ordering: prioritize rate limit over weekly quota
This change restores the original intent from PR #310 by @ajaxjiang96, which was partially reverted in PR #390 (commit ad6c751). PR #390 took a view-layer approach that required special-case logic in StatusItemController and UsageStore to pick the secondary window for Kimi in automatic mode. This created architectural inconsistency: - Claude: primary=5h session, secondary=weekly - Kimi: primary=weekly, secondary=5h rate limit (but automatic mode picked secondary) This fix takes a model-layer approach that aligns Kimi with Claude: 1. Swaps the primary/secondary mapping in KimiUsageSnapshot: - When rate limit exists: primary=rate limit (5h), secondary=weekly - When no rate limit: primary=weekly, secondary=nil 2. Updates menu labels to match: sessionLabel="Rate Limit", weeklyLabel="Weekly" 3. Removes the now-unnecessary .kimi special-case from automatic mode selection in both StatusItemController and UsageStore+HighestUsage 4. Updates tests to reflect new window ordering: - KimiProviderTests.swift: Updated window assertions - StatusItemAnimationTests.swift: Swapped test data values - UsageStoreHighestUsageTests.swift: Swapped test data values - UsageStoreCoverageTests.swift: Swapped test data values Result: Kimi now displays the 5-hour rate limit first in the menu (matching Claude's display order), and the menubar icon shows the rate limit percentage.
1 parent dde0d18 commit 6893b11

8 files changed

Lines changed: 24 additions & 20 deletions

Sources/CodexBar/StatusItemController.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ final class StatusItemController: NSObject, NSMenuDelegate, StatusItemControllin
134134
let usedPercent = (primary.usedPercent + secondary.usedPercent) / 2
135135
return RateWindow(usedPercent: usedPercent, windowMinutes: nil, resetsAt: nil, resetDescription: nil)
136136
case .automatic:
137-
if provider == .factory || provider == .kimi {
137+
if provider == .factory {
138138
return snapshot?.secondary ?? snapshot?.primary
139139
}
140140
if provider == .copilot,

Sources/CodexBar/UsageStore+HighestUsage.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ extension UsageStore {
3838
let usedPercent = (primary.usedPercent + secondary.usedPercent) / 2
3939
return RateWindow(usedPercent: usedPercent, windowMinutes: nil, resetsAt: nil, resetDescription: nil)
4040
case .automatic:
41-
if provider == .factory || provider == .kimi {
41+
if provider == .factory {
4242
return snapshot.secondary ?? snapshot.primary
4343
}
4444
if provider == .copilot,

Sources/CodexBarCore/Providers/Kimi/KimiProviderDescriptor.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ public enum KimiProviderDescriptor {
1010
metadata: ProviderMetadata(
1111
id: .kimi,
1212
displayName: "Kimi",
13-
sessionLabel: "Weekly",
14-
weeklyLabel: "Rate Limit",
13+
sessionLabel: "Rate Limit",
14+
weeklyLabel: "Weekly",
1515
opusLabel: nil,
1616
supportsOpus: false,
1717
supportsCredits: false,

Sources/CodexBarCore/Providers/Kimi/KimiUsageSnapshot.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,8 @@ extension KimiUsageSnapshot {
7373
loginMethod: nil)
7474

7575
return UsageSnapshot(
76-
primary: weeklyWindow,
77-
secondary: rateLimitWindow,
76+
primary: rateLimitWindow ?? weeklyWindow,
77+
secondary: rateLimitWindow != nil ? weeklyWindow : nil,
7878
tertiary: nil,
7979
providerCost: nil,
8080
updatedAt: self.updatedAt,

Tests/CodexBarTests/KimiProviderTests.swift

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -191,16 +191,16 @@ struct KimiUsageSnapshotConversionTests {
191191
let usageSnapshot = snapshot.toUsageSnapshot()
192192

193193
#expect(usageSnapshot.primary != nil)
194-
let weeklyExpected = 375.0 / 2048.0 * 100.0
195-
#expect(abs((usageSnapshot.primary?.usedPercent ?? 0.0) - weeklyExpected) < 0.01)
196-
#expect(usageSnapshot.primary?.resetDescription == "375/2048 requests")
197-
#expect(usageSnapshot.primary?.windowMinutes == nil)
194+
let rateExpected = 200.0 / 200.0 * 100.0
195+
#expect(abs((usageSnapshot.primary?.usedPercent ?? 0.0) - rateExpected) < 0.01)
196+
#expect(usageSnapshot.primary?.windowMinutes == 300) // 5 hours
197+
#expect(usageSnapshot.primary?.resetDescription == "Rate: 200/200 per 5 hours")
198198

199199
#expect(usageSnapshot.secondary != nil)
200-
let rateExpected = 200.0 / 200.0 * 100.0
201-
#expect(abs((usageSnapshot.secondary?.usedPercent ?? 0.0) - rateExpected) < 0.01)
202-
#expect(usageSnapshot.secondary?.windowMinutes == 300) // 5 hours
203-
#expect(usageSnapshot.secondary?.resetDescription == "Rate: 200/200 per 5 hours")
200+
let weeklyExpected = 375.0 / 2048.0 * 100.0
201+
#expect(abs((usageSnapshot.secondary?.usedPercent ?? 0.0) - weeklyExpected) < 0.01)
202+
#expect(usageSnapshot.secondary?.resetDescription == "375/2048 requests")
203+
#expect(usageSnapshot.secondary?.windowMinutes == nil)
204204

205205
#expect(usageSnapshot.tertiary == nil)
206206
#expect(usageSnapshot.updatedAt == now)

Tests/CodexBarTests/StatusItemAnimationTests.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -308,8 +308,8 @@ struct StatusItemAnimationTests {
308308
statusBar: self.makeStatusBarForTesting())
309309

310310
let snapshot = UsageSnapshot(
311-
primary: RateWindow(usedPercent: 12, windowMinutes: nil, resetsAt: nil, resetDescription: nil),
312-
secondary: RateWindow(usedPercent: 42, windowMinutes: 300, resetsAt: nil, resetDescription: nil),
311+
primary: RateWindow(usedPercent: 42, windowMinutes: 300, resetsAt: nil, resetDescription: nil),
312+
secondary: RateWindow(usedPercent: 12, windowMinutes: nil, resetsAt: nil, resetDescription: nil),
313313
updatedAt: Date())
314314

315315
store._setSnapshotForTesting(snapshot, provider: .kimi)

Tests/CodexBarTests/UsageStoreCoverageTests.swift

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,10 +95,12 @@ struct UsageStoreCoverageTests {
9595
secondary: nil,
9696
updatedAt: now),
9797
provider: .codex)
98+
// With the fix, Kimi's primary is now rate limit and secondary is weekly.
99+
// In automatic mode, Kimi uses primary like other providers.
98100
store._setSnapshotForTesting(
99101
UsageSnapshot(
100-
primary: RateWindow(usedPercent: 10, windowMinutes: nil, resetsAt: nil, resetDescription: nil),
101-
secondary: RateWindow(usedPercent: 80, windowMinutes: 300, resetsAt: nil, resetDescription: nil),
102+
primary: RateWindow(usedPercent: 80, windowMinutes: 300, resetsAt: nil, resetDescription: nil),
103+
secondary: RateWindow(usedPercent: 10, windowMinutes: nil, resetsAt: nil, resetDescription: nil),
102104
updatedAt: now),
103105
provider: .kimi)
104106

Tests/CodexBarTests/UsageStoreHighestUsageTests.swift

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,9 +104,11 @@ struct UsageStoreHighestUsageTests {
104104
primary: RateWindow(usedPercent: 70, windowMinutes: nil, resetsAt: nil, resetDescription: nil),
105105
secondary: nil,
106106
updatedAt: Date())
107+
// With the fix, Kimi's primary is now rate limit (typically lower usage %)
108+
// and secondary is weekly. In automatic mode, Kimi uses primary like other providers.
107109
let kimiSnapshot = UsageSnapshot(
108-
primary: RateWindow(usedPercent: 90, windowMinutes: nil, resetsAt: nil, resetDescription: nil),
109-
secondary: RateWindow(usedPercent: 20, windowMinutes: nil, resetsAt: nil, resetDescription: nil),
110+
primary: RateWindow(usedPercent: 20, windowMinutes: nil, resetsAt: nil, resetDescription: nil),
111+
secondary: RateWindow(usedPercent: 90, windowMinutes: nil, resetsAt: nil, resetDescription: nil),
110112
updatedAt: Date())
111113

112114
store._setSnapshotForTesting(codexSnapshot, provider: .codex)

0 commit comments

Comments
 (0)