Skip to content

Commit 57041da

Browse files
committed
feat: final v0
1 parent 5f224ad commit 57041da

14 files changed

Lines changed: 4256 additions & 254 deletions

diff.txt

Lines changed: 2570 additions & 0 deletions
Large diffs are not rendered by default.

packages/core/src/agent/agent.ts

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4059,12 +4059,23 @@ export class Agent {
40594059
return;
40604060
}
40614061

4062-
const refillPerSecond = updateResult.normalized.refillPerMs * 1000;
4062+
const now = Date.now();
4063+
const effectiveRemaining = Math.max(
4064+
0,
4065+
updateResult.state.remaining - updateResult.state.reserved,
4066+
);
4067+
const resetInMs = Math.max(0, updateResult.state.resetAt - now);
4068+
const nextAllowedInMs = Math.max(0, updateResult.state.nextAllowedAt - now);
40634069
logger?.info?.("[Traffic] Applied rate limit from response headers", {
40644070
rateLimitKey: updateResult.key,
4065-
capacity: updateResult.normalized.capacity,
4066-
refillPerSecond,
4067-
appliedTokens: updateResult.appliedTokens,
4071+
limit: updateResult.state.limit,
4072+
remaining: updateResult.state.remaining,
4073+
reserved: updateResult.state.reserved,
4074+
effectiveRemaining,
4075+
resetAt: updateResult.state.resetAt,
4076+
resetInMs,
4077+
nextAllowedAt: updateResult.state.nextAllowedAt,
4078+
nextAllowedInMs,
40684079
headers: {
40694080
limitRequests: updateResult.headerSnapshot.limitRequests,
40704081
remainingRequests: updateResult.headerSnapshot.remainingRequests,

packages/core/src/traffic/traffic-controller.spec.ts

Lines changed: 125 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -40,22 +40,16 @@ describe("TrafficController priority scheduling", () => {
4040
vi.useFakeTimers();
4141

4242
try {
43-
const controller = new TrafficController({
44-
maxConcurrent: 1,
45-
rateLimits: {
46-
"p0::m0": { capacity: 1, refillPerSecond: 1 },
43+
vi.setSystemTime(new Date(0));
44+
const controller = new TrafficController({ maxConcurrent: 1 });
45+
controller.updateRateLimitFromHeaders(
46+
{ provider: "p0", model: "m0" },
47+
{
48+
"x-ratelimit-limit-requests": "1",
49+
"x-ratelimit-remaining-requests": "0",
50+
"x-ratelimit-reset-requests": "1s",
4751
},
48-
});
49-
50-
// Exhaust the bucket for the P0 key so it initially waits
51-
const buckets = (controller as unknown as { rateLimitBuckets: Map<string, any> })
52-
.rateLimitBuckets;
53-
buckets.set("p0::m0", {
54-
tokens: 0,
55-
capacity: 1,
56-
refillPerMs: 1 / 1000,
57-
lastRefill: Date.now(),
58-
});
52+
);
5953

6054
const order: string[] = [];
6155

@@ -85,3 +79,119 @@ describe("TrafficController priority scheduling", () => {
8579
}
8680
});
8781
});
82+
83+
describe("TrafficController rate limit headers", () => {
84+
it("parses OpenAI-style compound reset durations (e.g. 1m30.951s)", () => {
85+
vi.useFakeTimers();
86+
87+
try {
88+
vi.setSystemTime(new Date(1_000_000));
89+
const controller = new TrafficController({ maxConcurrent: 1 });
90+
const now = Date.now();
91+
92+
const result = controller.updateRateLimitFromHeaders(
93+
{ provider: "openai.responses", model: "gpt-4o-mini" },
94+
{
95+
"x-ratelimit-limit-requests": "10000",
96+
"x-ratelimit-remaining-requests": "9989",
97+
"x-ratelimit-reset-requests": "1m30.951s",
98+
},
99+
);
100+
101+
expect(result).toBeTruthy();
102+
expect(result?.headerSnapshot.resetRequestsMs).toBeCloseTo(90_951, 6);
103+
expect(result?.state.limit).toBe(10000);
104+
expect(result?.state.remaining).toBe(9989);
105+
expect(result?.state.resetAt).toBe(now + 90_951);
106+
expect(result?.state.reserved).toBe(0);
107+
expect(result?.state.nextAllowedAt).toBe(now);
108+
} finally {
109+
vi.useRealTimers();
110+
}
111+
});
112+
113+
it("keeps resetAt monotonic when headers shorten the reset duration", () => {
114+
vi.useFakeTimers();
115+
116+
try {
117+
vi.setSystemTime(new Date(0));
118+
const controller = new TrafficController({ maxConcurrent: 1 });
119+
120+
const first = controller.updateRateLimitFromHeaders(
121+
{ provider: "openai.responses", model: "gpt-4o-mini" },
122+
{
123+
"x-ratelimit-limit-requests": "10000",
124+
"x-ratelimit-remaining-requests": "9999",
125+
"x-ratelimit-reset-requests": "60s",
126+
},
127+
);
128+
129+
expect(first).toBeTruthy();
130+
expect(first?.state.resetAt).toBe(60_000);
131+
132+
vi.setSystemTime(new Date(10_000));
133+
const second = controller.updateRateLimitFromHeaders(
134+
{ provider: "openai.responses", model: "gpt-4o-mini" },
135+
{
136+
"x-ratelimit-limit-requests": "10000",
137+
"x-ratelimit-remaining-requests": "9998",
138+
"x-ratelimit-reset-requests": "5s",
139+
},
140+
);
141+
142+
expect(second).toBeTruthy();
143+
expect(second?.state.resetAt).toBe(60_000);
144+
} finally {
145+
vi.useRealTimers();
146+
}
147+
});
148+
149+
it("never increases remaining within the same window", () => {
150+
vi.useFakeTimers();
151+
152+
try {
153+
vi.setSystemTime(new Date(0));
154+
const controller = new TrafficController({ maxConcurrent: 1 });
155+
156+
const first = controller.updateRateLimitFromHeaders(
157+
{ provider: "openai.responses", model: "gpt-4o-mini" },
158+
{
159+
"x-ratelimit-limit-requests": "10",
160+
"x-ratelimit-remaining-requests": "9",
161+
"x-ratelimit-reset-requests": "60s",
162+
},
163+
);
164+
165+
expect(first?.state.remaining).toBe(9);
166+
expect(first?.state.resetAt).toBe(60_000);
167+
168+
vi.setSystemTime(new Date(10_000));
169+
const second = controller.updateRateLimitFromHeaders(
170+
{ provider: "openai.responses", model: "gpt-4o-mini" },
171+
{
172+
"x-ratelimit-limit-requests": "10",
173+
"x-ratelimit-remaining-requests": "8",
174+
"x-ratelimit-reset-requests": "50s",
175+
},
176+
);
177+
178+
expect(second?.state.remaining).toBe(8);
179+
expect(second?.state.resetAt).toBe(60_000);
180+
181+
vi.setSystemTime(new Date(20_000));
182+
const third = controller.updateRateLimitFromHeaders(
183+
{ provider: "openai.responses", model: "gpt-4o-mini" },
184+
{
185+
"x-ratelimit-limit-requests": "10",
186+
"x-ratelimit-remaining-requests": "9",
187+
"x-ratelimit-reset-requests": "40s",
188+
},
189+
);
190+
191+
expect(third?.state.remaining).toBe(8);
192+
expect(third?.state.resetAt).toBe(60_000);
193+
} finally {
194+
vi.useRealTimers();
195+
}
196+
});
197+
});

0 commit comments

Comments
 (0)