unit tests fixin
Some checks failed
Deploy to Web Server flyer-crawler.projectium.com / deploy (push) Failing after 35s

This commit is contained in:
2025-11-22 00:48:51 -08:00
parent 680c2636c3
commit 3dc9084d96

View File

@@ -86,20 +86,64 @@ export const extractItemsFromReceiptImage = async (
* @returns A promise that resolves to the extracted core data.
*/
export const extractCoreDataFromFlyerImage = async (
_imagePaths: { path: string; mimetype: string }[],
_masterItems: MasterGroceryItem[]
imagePaths: { path: string; mimetype: string }[],
masterItems: MasterGroceryItem[]
): Promise<{
store_name: string;
valid_from: string | null;
valid_to: string | null;
items: Omit<FlyerItem, 'id' | 'created_at' | 'flyer_id'>[];
}> => {
// This function's logic is now handled by the backend API endpoint in `src/routes/ai.ts`.
// The actual Gemini call logic is complex and has been moved there to keep this file clean
// and demonstrate the architectural separation. For the purpose of this fix, we can
// assume the backend correctly implements the Gemini call.
// This function is now a placeholder to show where the logic *would* live.
throw new Error("extractCoreDataFromFlyerImage is a server-side function and should be called from a backend route.");
// 1. Construct the detailed prompt for the AI.
const prompt = `
Analyze the provided flyer image(s). Your task is to extract key information and a list of all sale items.
First, identify the following core details for the entire flyer:
- "store_name": The name of the grocery store (e.g., "Walmart", "No Frills").
- "valid_from": The start date of the sale period in YYYY-MM-DD format. If not present, use null.
- "valid_to": The end date of the sale period in YYYY-MM-DD format. If not present, use null.
Second, extract each individual sale item. For each item, provide:
- "item": The name of the product (e.g., "Coca-Cola Classic").
- "price_display": The sale price as a string (e.g., "$2.99", "2 for $5.00").
- "price_in_cents": The primary numeric price converted to cents (e.g., for "$2.99", use 299). If a price is "2 for $5.00", use 500. If no price, use null.
- "quantity": A string describing the quantity or weight for the price (e.g., "12x355mL", "500g", "each").
- "master_item_id": From the provided master list, find the best matching item and return its ID. If no good match is found, use null.
- "category_name": The most appropriate category for the item (e.g., "Beverages", "Meat & Seafood").
Here is the master list of grocery items to help with matching:
${JSON.stringify(masterItems)}
Return a single, valid JSON object with the keys "store_name", "valid_from", "valid_to", and "items". The "items" key should contain an array of the extracted item objects.
Do not include any other text, explanations, or markdown formatting.
`;
// 2. Convert all uploaded image files into the format required by the Gemini API.
const imageParts = await Promise.all(
imagePaths.map(file => serverFileToGenerativePart(file.path, file.mimetype))
);
// 3. Make the API call to Gemini.
const response = await model.generateContent({
model: 'gemini-1.5-flash',
contents: [{ parts: [{ text: prompt }, ...imageParts] }]
});
const text = response.text;
// 4. Clean and parse the AI's response.
const jsonMatch = text?.match(/\{[\s\S]*\}/);
if (!jsonMatch) {
logger.error("AI response for flyer processing did not contain a valid JSON object.", { responseText: text });
throw new Error('AI response did not contain a valid JSON object.');
}
try {
return JSON.parse(jsonMatch[0]);
} catch (e) {
logger.error("Failed to parse JSON from AI response in extractCoreDataFromFlyerImage", { responseText: text, error: e });
throw new Error('Failed to parse structured data from the AI response.');
}
};
/**