feat(api): implement retry logic for Mistral API calls in receipt import and AI services
This commit is contained in:
@@ -38,6 +38,33 @@ ${text}`;
|
||||
@Injectable()
|
||||
export class ReceiptImportService {
|
||||
private readonly logger = new Logger(ReceiptImportService.name);
|
||||
private readonly MAX_RETRIES = 3;
|
||||
|
||||
private async callMistralWithRetry(body: object, apiKey: string, source: string): Promise<Response> {
|
||||
for (let attempt = 1; attempt <= this.MAX_RETRIES; attempt++) {
|
||||
const response = await fetch(MISTRAL_API_URL, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${apiKey}`,
|
||||
},
|
||||
body: JSON.stringify(body),
|
||||
});
|
||||
|
||||
if (response.status === 503 || response.status === 429) {
|
||||
const err = await response.text();
|
||||
this.logger.warn(`Mistral ${response.status} (${source}, försök ${attempt}/${this.MAX_RETRIES}): ${err}`);
|
||||
if (attempt < this.MAX_RETRIES) {
|
||||
await new Promise((r) => setTimeout(r, attempt * 2000));
|
||||
continue;
|
||||
}
|
||||
throw new ServiceUnavailableException('Mistral API returnerade ett fel: Tjänsten tillfälligt otillgänglig (503)');
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
throw new ServiceUnavailableException('Kunde inte nå Mistral API efter flera försök');
|
||||
}
|
||||
|
||||
constructor(
|
||||
private readonly prisma: PrismaService,
|
||||
@@ -72,30 +99,23 @@ export class ReceiptImportService {
|
||||
apiKey: string,
|
||||
): Promise<ParsedReceiptItem[]> {
|
||||
const base64 = buffer.toString('base64');
|
||||
const response = await fetch(MISTRAL_API_URL, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${apiKey}`,
|
||||
},
|
||||
body: JSON.stringify({
|
||||
model: RECEIPT_IMPORT_MODEL,
|
||||
messages: [
|
||||
{
|
||||
role: 'user',
|
||||
content: [
|
||||
{
|
||||
type: 'image_url',
|
||||
image_url: { url: `data:${mimeType};base64,${base64}` },
|
||||
},
|
||||
{ type: 'text', text: IMAGE_PROMPT },
|
||||
],
|
||||
},
|
||||
],
|
||||
max_tokens: 2000,
|
||||
temperature: 0.1,
|
||||
}),
|
||||
});
|
||||
const response = await this.callMistralWithRetry({
|
||||
model: RECEIPT_IMPORT_MODEL,
|
||||
messages: [
|
||||
{
|
||||
role: 'user',
|
||||
content: [
|
||||
{
|
||||
type: 'image_url',
|
||||
image_url: { url: `data:${mimeType};base64,${base64}` },
|
||||
},
|
||||
{ type: 'text', text: IMAGE_PROMPT },
|
||||
],
|
||||
},
|
||||
],
|
||||
max_tokens: 2000,
|
||||
temperature: 0.1,
|
||||
}, apiKey, 'bild');
|
||||
|
||||
return this.extractItemsFromMistralResponse(response, 'bild');
|
||||
}
|
||||
@@ -120,19 +140,12 @@ export class ReceiptImportService {
|
||||
|
||||
this.logger.log(`PDF-text extraherad (${pdfText.length} tecken)`);
|
||||
|
||||
const response = await fetch(MISTRAL_API_URL, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${apiKey}`,
|
||||
},
|
||||
body: JSON.stringify({
|
||||
model: RECEIPT_IMPORT_MODEL,
|
||||
messages: [{ role: 'user', content: TEXT_PROMPT(pdfText) }],
|
||||
max_tokens: 2000,
|
||||
temperature: 0.1,
|
||||
}),
|
||||
});
|
||||
const response = await this.callMistralWithRetry({
|
||||
model: RECEIPT_IMPORT_MODEL,
|
||||
messages: [{ role: 'user', content: TEXT_PROMPT(pdfText) }],
|
||||
max_tokens: 2000,
|
||||
temperature: 0.1,
|
||||
}, apiKey, 'PDF');
|
||||
|
||||
return this.extractItemsFromMistralResponse(response, 'PDF');
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user