@@ -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