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:
@@ -71,50 +71,82 @@ Respond ONLY with valid JSON in this format:
|
|||||||
"confidence": 0.85
|
"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)
|
* 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 }> {
|
async function analyzeWithOpenRouter(base64Data: string, mimeType: string): Promise<{ data: any; apiTime: number }> {
|
||||||
const client = getOpenRouterClient();
|
const client = getOpenRouterClient();
|
||||||
const startApi = performance.now();
|
const startApi = performance.now();
|
||||||
|
const maxRetries = 3;
|
||||||
|
let lastError: any = null;
|
||||||
|
|
||||||
const response = await client.chat.completions.create({
|
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
||||||
model: OPENROUTER_VISION_MODEL,
|
try {
|
||||||
messages: [
|
const response = await client.chat.completions.create({
|
||||||
{
|
model: OPENROUTER_VISION_MODEL,
|
||||||
role: 'user',
|
messages: [
|
||||||
content: [
|
|
||||||
{
|
{
|
||||||
type: 'image_url',
|
role: 'user',
|
||||||
image_url: {
|
content: [
|
||||||
url: `data:${mimeType};base64,${base64Data}`,
|
{
|
||||||
},
|
type: 'image_url',
|
||||||
},
|
image_url: {
|
||||||
{
|
url: `data:${mimeType};base64,${base64Data}`,
|
||||||
type: 'text',
|
},
|
||||||
text: VISION_PROMPT,
|
},
|
||||||
|
{
|
||||||
|
type: 'text',
|
||||||
|
text: VISION_PROMPT,
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
temperature: 0.1,
|
||||||
],
|
max_tokens: 1024,
|
||||||
temperature: 0.1,
|
});
|
||||||
max_tokens: 1024,
|
|
||||||
});
|
|
||||||
|
|
||||||
const endApi = performance.now();
|
const endApi = performance.now();
|
||||||
const content = response.choices[0]?.message?.content || '{}';
|
const content = response.choices[0]?.message?.content || '{}';
|
||||||
|
|
||||||
// Extract JSON from response (may have markdown code blocks)
|
// Extract JSON from response (may have markdown code blocks)
|
||||||
let jsonStr = content;
|
let jsonStr = content;
|
||||||
const jsonMatch = content.match(/```(?:json)?\s*([\s\S]*?)```/);
|
const jsonMatch = content.match(/```(?:json)?\s*([\s\S]*?)```/);
|
||||||
if (jsonMatch) {
|
if (jsonMatch) {
|
||||||
jsonStr = jsonMatch[1].trim();
|
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 {
|
// All retries exhausted
|
||||||
data: JSON.parse(jsonStr),
|
throw lastError || new Error('OpenRouter request failed after retries');
|
||||||
apiTime: endApi - startApi,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -37,4 +37,4 @@ export function getOpenRouterClient(): OpenAI {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Default OpenRouter model for vision tasks
|
// 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';
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user