fix: Add retry logic for OpenRouter 429 rate limit errors

- Retries up to 3 times with exponential backoff (2s, 4s, 8s)
- Also handles 503 service unavailable errors
- Logs retry attempts for debugging
- Only retries rate limit errors, other errors fail immediately
This commit is contained in:
2025-12-26 00:06:21 +01:00
parent 8ccd600dcb
commit 8cf51d4aea
3 changed files with 64 additions and 32 deletions

View File

@@ -71,50 +71,82 @@ Respond ONLY with valid JSON in this format:
"confidence": 0.85
}`;
/**
* Sleep helper for retry delays
*/
function sleep(ms: number): Promise<void> {
return new Promise(resolve => setTimeout(resolve, ms));
}
/**
* Analyze whisky label with OpenRouter (Gemma 3 27B)
* Includes retry logic for 429 rate limit errors
*/
async function analyzeWithOpenRouter(base64Data: string, mimeType: string): Promise<{ data: any; apiTime: number }> {
const client = getOpenRouterClient();
const startApi = performance.now();
const maxRetries = 3;
let lastError: any = null;
const response = await client.chat.completions.create({
model: OPENROUTER_VISION_MODEL,
messages: [
{
role: 'user',
content: [
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
const response = await client.chat.completions.create({
model: OPENROUTER_VISION_MODEL,
messages: [
{
type: 'image_url',
image_url: {
url: `data:${mimeType};base64,${base64Data}`,
},
},
{
type: 'text',
text: VISION_PROMPT,
role: 'user',
content: [
{
type: 'image_url',
image_url: {
url: `data:${mimeType};base64,${base64Data}`,
},
},
{
type: 'text',
text: VISION_PROMPT,
},
],
},
],
},
],
temperature: 0.1,
max_tokens: 1024,
});
temperature: 0.1,
max_tokens: 1024,
});
const endApi = performance.now();
const content = response.choices[0]?.message?.content || '{}';
const endApi = performance.now();
const content = response.choices[0]?.message?.content || '{}';
// Extract JSON from response (may have markdown code blocks)
let jsonStr = content;
const jsonMatch = content.match(/```(?:json)?\s*([\s\S]*?)```/);
if (jsonMatch) {
jsonStr = jsonMatch[1].trim();
// Extract JSON from response (may have markdown code blocks)
let jsonStr = content;
const jsonMatch = content.match(/```(?:json)?\s*([\s\S]*?)```/);
if (jsonMatch) {
jsonStr = jsonMatch[1].trim();
}
return {
data: JSON.parse(jsonStr),
apiTime: endApi - startApi,
};
} catch (error: any) {
lastError = error;
const status = error?.status || error?.response?.status;
// Only retry on 429 (rate limit) or 503 (service unavailable)
if (status === 429 || status === 503) {
const delay = Math.pow(2, attempt) * 1000; // 2s, 4s, 8s
console.log(`[OpenRouter] Rate limited (${status}), retry ${attempt}/${maxRetries} in ${delay}ms...`);
await sleep(delay);
continue;
}
// Other errors - don't retry
throw error;
}
}
return {
data: JSON.parse(jsonStr),
apiTime: endApi - startApi,
};
// All retries exhausted
throw lastError || new Error('OpenRouter request failed after retries');
}
/**

View File

@@ -37,4 +37,4 @@ export function getOpenRouterClient(): OpenAI {
}
// Default OpenRouter model for vision tasks
export const OPENROUTER_VISION_MODEL = 'google/gemma-3-27b-it';
export const OPENROUTER_VISION_MODEL = 'google/gemma-3-27b-it:free';