Skip to content
Source
Sign inSign up

usageLimits.ts

130 lines4.3 KB

1import type {
2 ProviderUsageLimitsUpdate,
3 ServerProviderUsageLimits,
4 ServerProviderUsageWindow,
5} from "@t3tools/contracts";
6​
7const WINDOW_KIND_ORDER: Record<ServerProviderUsageWindow["kind"], number> = {
8 session: 0,
9 weekly: 1,
10 monthly: 2,
11 other: 3,
12};
13​
14export function clampPercent(value: number): number {
15 return Number.isFinite(value) ? Math.max(0, Math.min(100, value)) : 0;
17​
18function sortWindows(
19 windows: Iterable<ServerProviderUsageWindow>,
20): ReadonlyArray<ServerProviderUsageWindow> {
21 return [...windows].toSorted(
22 (left, right) =>
23 WINDOW_KIND_ORDER[left.kind] - WINDOW_KIND_ORDER[right.kind] ||
24 left.id.localeCompare(right.id),
25 );
27​
28export function makeUsageLimits(input: {
29 readonly checkedAt: string;
30 readonly windows: Iterable<ServerProviderUsageWindow>;
31}): ServerProviderUsageLimits {
32 return { checkedAt: input.checkedAt, windows: sortWindows(input.windows) };
34​
35export function makeUnavailableUsageLimits(input: {
36 readonly checkedAt: string;
37 readonly reason: "unsupported" | "probeFailed";
38 readonly message?: string;
39}): ServerProviderUsageLimits {
40 return {
41 checkedAt: input.checkedAt,
42 windows: [],
43 unavailable: {
44 reason: input.reason,
45 ...(input.message ? { message: input.message } : {}),
46 },
47 };
49​
50export function applyUsageLimitsUpdate(input: {
51 readonly previous: ServerProviderUsageLimits | undefined;
52 readonly update: ProviderUsageLimitsUpdate;
53 readonly checkedAt: string;
54}): ServerProviderUsageLimits | undefined {
55 const { previous, update } = input;
56 if (update.windows.length === 0 || previous?.unavailable?.reason === "unsupported") {
57 return previous;
58 }
59 const merged = new Map(previous?.windows.map((window) => [window.id, window] as const));
60 // Codex repeats unchanged limits on token ticks; avoid allocating snapshots for no-ops.
61 let changed = false;
62 for (const window of update.windows) {
63 const existing = merged.get(window.id);
64 const next: ServerProviderUsageWindow = {
65 ...window,
66 usedPercent: clampPercent(window.usedPercent),
67 ...(window.resetsAt === undefined && existing?.resetsAt !== undefined
68 ? { resetsAt: existing.resetsAt }
69 : {}),
70 ...(window.windowDurationMins === undefined && existing?.windowDurationMins !== undefined
71 ? { windowDurationMins: existing.windowDurationMins }
72 : {}),
73 };
74 if (existing === undefined || !usageWindowEquals(existing, next)) {
75 merged.set(window.id, next);
76 changed = true;
77 }
78 }
79 if (!changed && previous !== undefined) {
80 return previous;
81 }
82 const unavailable =
83 previous === undefined ? { reason: "probeFailed" as const } : previous.unavailable;
84 return {
85 ...makeUsageLimits({ checkedAt: input.checkedAt, windows: merged.values() }),
86 ...(previous?.resetCredits !== undefined ? { resetCredits: previous.resetCredits } : {}),
87 ...(unavailable !== undefined ? { unavailable } : {}),
88 };
90​
91function usageWindowEquals(a: ServerProviderUsageWindow, b: ServerProviderUsageWindow): boolean {
92 return (
93 a.id === b.id &&
94 a.kind === b.kind &&
95 a.label === b.label &&
96 a.usedPercent === b.usedPercent &&
97 (a.modelIds === b.modelIds ||
98 (a.modelIds !== undefined &&
99 b.modelIds !== undefined &&
100 a.modelIds.length === b.modelIds.length &&
101 a.modelIds.every((id, index) => id === b.modelIds?.[index]))) &&
102 (a.providerIds === b.providerIds ||
103 (a.providerIds !== undefined &&
104 b.providerIds !== undefined &&
105 a.providerIds.length === b.providerIds.length &&
106 a.providerIds.every((id, index) => id === b.providerIds?.[index]))) &&
107 a.resetsAt === b.resetsAt &&
108 a.windowDurationMins === b.windowDurationMins
109 );
111​
112export function resolveUsageLimitsAfterProbe(input: {
113 readonly published: ServerProviderUsageLimits | undefined;
114 readonly probed: ServerProviderUsageLimits | undefined;
115}): ServerProviderUsageLimits | undefined {
116 const { published, probed } = input;
117 if (probed?.unavailable?.reason === "probeFailed" && published && !published.unavailable) {
118 return published;
119 }
120 if (
121 published &&
122 probed &&
123 !published.unavailable &&
124 !probed.unavailable &&
125 Date.parse(published.checkedAt) > Date.parse(probed.checkedAt)
126 ) {
127 return { ...probed, checkedAt: published.checkedAt, windows: published.windows };
128 }
129 return probed;