Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 12 additions & 8 deletions Sources/CodexLimits/UsageIntelligenceEngine.swift
Original file line number Diff line number Diff line change
Expand Up @@ -745,9 +745,13 @@ private enum RecentMovementBreak {

enum UsageIntelligenceEngine {
static func evaluate(_ input: UsageIntelligenceInput) -> UsageReaderSnapshot {
let samples = UsageHistoryPolicy.aligningWindowResets(
input.samples,
currentReset: input.account?.mainLimit?.window.resetsAt
)
let currentSamples = input.account.flatMap { account in
account.mainLimit.map { weeklyLimit in
input.samples
samples
.filter { sample in
sample.resetsAt == weeklyLimit.window.resetsAt
&& sample.observedAt >= weeklyLimit.window.startsAt
Expand Down Expand Up @@ -792,7 +796,7 @@ enum UsageIntelligenceEngine {
)
let recentMovement = recentAccountMovement(
account: input.account,
samples: input.samples,
samples: samples,
sourceState: input.sourceState,
now: input.now,
accountEpochStartedAt: input.accountEpochStartedAt
Expand All @@ -802,7 +806,7 @@ enum UsageIntelligenceEngine {
let weeklyLimit = account.mainLimit else { return nil }
return ForecastEngine.evaluate(
window: weeklyLimit.window,
samples: input.samples,
samples: samples,
tokenHistory: account.tokenHistory,
safetyBuffer: input.safetyBuffer,
now: input.now,
Expand Down Expand Up @@ -849,7 +853,7 @@ enum UsageIntelligenceEngine {
}
let chart = chart(
account: input.account,
samples: input.samples,
samples: samples,
forecast: forecast,
recentMovement: recentMovement.movement,
sourceState: input.sourceState,
Expand Down Expand Up @@ -888,7 +892,7 @@ enum UsageIntelligenceEngine {
let accountTokenActivity = if let selectedTokenRange {
selectedRangeAccountTokenActivity(
account: input.account,
samples: input.samples,
samples: samples,
range: selectedTokenRange,
now: input.now,
accountPartitionID: input.accountPartitionID,
Expand Down Expand Up @@ -979,7 +983,7 @@ enum UsageIntelligenceEngine {
let evidence: WeeklyUsageEvidenceSet
if let reusableLocalHistory,
reusableLocalHistory.matches(
samples: input.samples,
samples: samples,
observation: input.localActivityObservation,
accountPartitionID: partitionID,
limitID: weekly.limitId,
Expand All @@ -990,7 +994,7 @@ enum UsageIntelligenceEngine {
reusedWeeklyEvidence = true
} else {
evidence = WeeklyUsageEvidenceBuilder.build(
samples: input.samples,
samples: samples,
localFacts: input.localActivityHistoryFacts,
localObservation: input.localActivityObservation,
accountPartitionID: partitionID,
Expand Down Expand Up @@ -1068,7 +1072,7 @@ enum UsageIntelligenceEngine {
let weekly = input.account?.mainLimit {
localHistoryCache = LocalHistoryAggregateCache(
factIndex: localHistoryFactIndex,
samples: input.samples,
samples: samples,
observation: input.localActivityObservation,
accountPartitionID: partitionID,
limitID: weekly.limitId,
Expand Down
27 changes: 27 additions & 0 deletions Sources/CodexLimits/UsageModels.swift
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,33 @@ enum UsageHistoryPolicy {
static let tightBoundary: TimeInterval = 15 * 60
static let maximumComparableGap: TimeInterval = 30 * 60

static func aligningWindowResets(
_ samples: [UsageSample],
currentReset: Date?
) -> [UsageSample] {
var anchors = currentReset.map { [$0] } ?? []
var aligned: [Date: Date] = [:]
for sample in samples.sorted(by: { $0.observedAt > $1.observedAt })
where aligned[sample.resetsAt] == nil {
let anchor = anchors.first {
abs($0.timeIntervalSince(sample.resetsAt)) <= tightBoundary
} ?? sample.resetsAt
aligned[sample.resetsAt] = anchor
if anchor == sample.resetsAt { anchors.append(anchor) }
}
return samples.map { sample in
guard let reset = aligned[sample.resetsAt],
reset != sample.resetsAt else { return sample }
return UsageSample(
observedAt: sample.observedAt,
remainingPercent: sample.remainingPercent,
resetsAt: reset,
lifetimeTokens: sample.lifetimeTokens,
comparisonBreak: sample.comparisonBreak
)
}
}

static func segments(
_ samples: [UsageSample]
) -> [[UsageSample]] {
Expand Down
30 changes: 30 additions & 0 deletions Tests/CodexLimitsTests/UsageIntelligenceEngineTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2394,6 +2394,36 @@ final class UsageIntelligenceEngineTests: XCTestCase {
)
}

func testResetTimeJitterDoesNotCreateASecondAllowanceWindow() {
let now = Date(timeIntervalSince1970: 6_400_000)
let account = makeSnapshot(remaining: 34, fetchedAt: now)
let reset = account.mainLimit!.window.resetsAt
let reader = UsageIntelligenceEngine.evaluate(
UsageIntelligenceInput(
account: account,
samples: [
UsageSample(
observedAt: now.addingTimeInterval(-3 * 86_400),
remainingPercent: 70,
resetsAt: reset.addingTimeInterval(1)
),
UsageSample(
observedAt: now.addingTimeInterval(-60),
remainingPercent: 34,
resetsAt: reset.addingTimeInterval(1)
)
],
safetyBuffer: 3,
sourceState: .available,
now: now,
previousStatus: nil
)
)

XCTAssertEqual(reader.chart.allowanceWindows.count, 1)
XCTAssertEqual(reader.chart.observed.count, 3)
}

func testAccountEpochDoesNotHideStoredChartPoints() {
let now = Date(timeIntervalSince1970: 6_400_000)
let account = makeSnapshot(remaining: 70, fetchedAt: now)
Expand Down