From 8b0d488694fce7ab8ac0d57c0b2e87a1cb16fbce Mon Sep 17 00:00:00 2001 From: OfficialAbhinavSingh Date: Sun, 19 Jul 2026 19:32:51 +0530 Subject: [PATCH] fix: skip elapsed reset times when picking LLMProxy next reset parseSnapshot computed nextResetAt as the globally earliest reset time (quotaGroups.compactMap { parseDate }.min()) with no future filter, so a stale already-elapsed reset_time could be surfaced as the next reset. Filter to reset times after the snapshot updatedAt before taking the minimum, mirroring GrokWebBillingFetcher and ClaudeStatusProbe. --- .../LLMProxy/LLMProxyUsageFetcher.swift | 7 ++- TestsLinux/LLMProxyResetLinuxTests.swift | 53 +++++++++++++++++++ 2 files changed, 59 insertions(+), 1 deletion(-) create mode 100644 TestsLinux/LLMProxyResetLinuxTests.swift diff --git a/Sources/CodexBarCore/Providers/LLMProxy/LLMProxyUsageFetcher.swift b/Sources/CodexBarCore/Providers/LLMProxy/LLMProxyUsageFetcher.swift index 094ec30154..e84720d39d 100644 --- a/Sources/CodexBarCore/Providers/LLMProxy/LLMProxyUsageFetcher.swift +++ b/Sources/CodexBarCore/Providers/LLMProxy/LLMProxyUsageFetcher.swift @@ -264,7 +264,12 @@ public struct LLMProxyUsageFetcher: Sendable { let quotaGroups = providers.values.flatMap { $0.quotaGroups ?? [] } let minRemaining = quotaGroups.compactMap(\.remainingPercent).min() - let reset = quotaGroups.compactMap { Self.parseDate($0.resetTime) }.min() + // Ignore already-elapsed reset times so a stale past reset can't win over the real + // upcoming one; mirrors GrokWebBillingFetcher and ClaudeStatusProbe. + let reset = quotaGroups + .compactMap { Self.parseDate($0.resetTime) } + .filter { $0 > updatedAt } + .min() return LLMProxyUsageSnapshot( providerCount: providers.count, diff --git a/TestsLinux/LLMProxyResetLinuxTests.swift b/TestsLinux/LLMProxyResetLinuxTests.swift new file mode 100644 index 0000000000..0307eee93b --- /dev/null +++ b/TestsLinux/LLMProxyResetLinuxTests.swift @@ -0,0 +1,53 @@ +#if os(Linux) +import CodexBarCore +import Foundation +import Testing + +struct LLMProxyResetLinuxTests { + // 2023-11-14T22:13:20Z — the snapshot time treated as "now". + private static let now = Date(timeIntervalSince1970: 1_700_000_000) + + private func nextReset(resetTimes: [String]) throws -> Date? { + let groups = resetTimes + .map { "{ \"remaining_percent\": 50, \"reset_time\": \"\($0)\" }" } + .joined(separator: ", ") + let json = "{ \"providers\": { \"p\": { \"quota_groups\": [ \(groups) ] } } }" + return try LLMProxyUsageFetcher + ._parseSnapshotForTesting(Data(json.utf8), updatedAt: Self.now) + .nextResetAt + } + + private func date(_ year: Int, _ month: Int, _ day: Int) throws -> Date { + try #require(DateComponents( + calendar: Calendar(identifier: .gregorian), + timeZone: TimeZone(secondsFromGMT: 0), + year: year, month: month, day: day, hour: 0, minute: 0, second: 0).date) + } + + @Test + func `next reset skips already-elapsed reset times`() throws { + // A past reset (stale until the API refreshes) must not be chosen over the soonest upcoming one. + let reset = try self.nextReset(resetTimes: [ + "2023-11-01T00:00:00Z", // past (before now) + "2023-11-20T00:00:00Z", // soonest future + "2023-12-25T00:00:00Z", // later future + ]) + #expect(try abs(#require(reset).timeIntervalSince(self.date(2023, 11, 20))) < 0.001) + } + + @Test + func `all-past reset times yield no next reset`() throws { + let reset = try self.nextReset(resetTimes: [ + "2023-11-01T00:00:00Z", + "2023-10-15T00:00:00Z", + ]) + #expect(reset == nil) + } + + @Test + func `future reset time is preserved`() throws { + let reset = try self.nextReset(resetTimes: ["2023-11-20T00:00:00Z"]) + #expect(try abs(#require(reset).timeIntervalSince(self.date(2023, 11, 20))) < 0.001) + } +} +#endif