Skip to content

Commit 4d24fa6

Browse files
authored
Merge pull request #7 from ben-vargas/feat/rate-limit-retry-headers
feat: add retry-after headers from Google rate limit responses
2 parents 83cdd66 + a19f4eb commit 4d24fa6

File tree

1 file changed

+32
-0
lines changed

1 file changed

+32
-0
lines changed

src/plugin/request.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,38 @@ export async function transformGeminiResponse(
162162
try {
163163
const text = await response.text();
164164
const headers = new Headers(response.headers);
165+
166+
// Extract retry timing from Google's structured error response
167+
// Google returns retry timing in error.details[].retryDelay: "55.846891726s"
168+
if (!response.ok && text) {
169+
try {
170+
const errorBody = JSON.parse(text);
171+
if (errorBody?.error?.details && Array.isArray(errorBody.error.details)) {
172+
// Look for RetryInfo type
173+
const retryInfo = errorBody.error.details.find(
174+
(detail: any) => detail['@type'] === 'type.googleapis.com/google.rpc.RetryInfo'
175+
);
176+
177+
if (retryInfo?.retryDelay) {
178+
// Parse "55.846891726s" format
179+
const match = retryInfo.retryDelay.match(/^([\d.]+)s$/);
180+
if (match && match[1]) {
181+
const retrySeconds = parseFloat(match[1]);
182+
if (!isNaN(retrySeconds) && retrySeconds > 0) {
183+
// Add both formats for compatibility
184+
const retryAfterSec = Math.ceil(retrySeconds).toString();
185+
const retryAfterMs = Math.ceil(retrySeconds * 1000).toString();
186+
headers.set('Retry-After', retryAfterSec);
187+
headers.set('retry-after-ms', retryAfterMs);
188+
}
189+
}
190+
}
191+
}
192+
} catch (parseError) {
193+
// If JSON parsing fails, continue without retry headers
194+
}
195+
}
196+
165197
const init = {
166198
status: response.status,
167199
statusText: response.statusText,

0 commit comments

Comments
 (0)