feat: improve AI resilience, add background enrichment loading states, and fix duplicate identifier in TagSelector

This commit is contained in:
2025-12-23 11:38:16 +01:00
parent 1d98bb9947
commit c134c78a2c
37 changed files with 1906 additions and 786 deletions

105
.aiideas
View File

@@ -1,28 +1,85 @@
Act as a Senior TypeScript/Next.js Developer.
´
I need a robust client-side image processing utility (an "Image Agent") to optimize user uploads before sending them to an LLM or Supabase.
Act as a Senior Next.js Developer.
**Task:**
Create a utility file `src/utils/image-processing.ts`.
This file should export a function `processImageForAI` that uses the library `browser-image-compression`.
Refactor the whisky analysis logic to optimize for perceived performance using a "Optimistic UI" approach.
Split the current `analyzeBottle` logic into **two separate Server Actions**.
**Requirements:**
1. **Input:** The function takes a raw `File` object (from an HTML input).
2. **Processing Logic:**
- Resize the image to a maximum of **1024x1024** pixels (maintain aspect ratio).
- Convert the image to **WebP** format.
- Limit the file size to approx **0.4MB**.
- Enable `useWebWorker: true` to prevent UI freezing.
3. **Output:** The function must return a Promise that resolves to an object with this interface:
```typescript
interface ProcessedImage {
file: File; // The compressed WebP file (ready for Supabase storage)
base64: string; // The Base64 string (ready for LLM API calls)
originalFile: File; // Pass through the original file
}
```
4. **Helper:** Include a helper function to convert the resulting Blob/File to a Base64 string correctly.
5. **Edge Cases:** Handle errors gracefully (try/catch) and ensure the returned `file` has the correct `.webp` extension and mime type.
### 1. Create `src/app/actions/scan-label.ts` (Fast OCR)
This action handles the image upload via `FormData`.
It must use the model with `safetySettings: BLOCK_NONE`.
It must **NOT** generate flavor tags or search strings.
**Step 1:** Give me the `npm install` command to add the necessary library.
**Step 2:** Write the complete `src/utils/image-processing.ts` code with proper JSDoc comments.
**System Prompt for this Action:**
```text
ROLE: High-Precision OCR Engine for Whisky Labels.
OBJECTIVE: Extract visible metadata strictly from the image.
SPEED PRIORITY: Do NOT analyze flavor. Do NOT provide descriptions. Do NOT add tags.
TASK:
1. Identify if the image contains a whisky/spirit bottle.
2. Extract the following technical details into the JSON schema below.
3. If a value is not visible or cannot be inferred with high certainty, use null.
EXTRACTION RULES:
- Name: Combine Distillery + Age + Edition + Vintage (e.g., "Signatory Vintage Ben Nevis 2019 4 Year Old").
- Distillery: The producer of the spirit.
- Bottler: Independent bottler (e.g., "Signatory", "Gordon & MacPhail") if applicable.
- Batch Info: Capture ALL Cask numbers, Batch IDs, Bottle numbers, Cask Types (e.g., "Refill Oloroso Sherry Butt, Bottle 1135").
- Codes: Look for laser codes etched on glass/label (e.g., "L20394...").
- Dates: Distinguish clearly between Vintage (distilled year), Bottled year, and Age.
OUTPUT SCHEMA (Strict JSON):
{
"name": "string",
"distillery": "string",
"bottler": "stringOrNull",
"category": "string (e.g. Single Malt Scotch Whisky)",
"abv": numberOrNull,
"age": numberOrNull,
"vintage": "stringOrNull",
"distilled_at": "stringOrNull (Year/Date)",
"bottled_at": "stringOrNull (Year/Date)",
"batch_info": "stringOrNull",
"bottleCode": "stringOrNull",
"whiskybaseId": "stringOrNull",
"is_whisky": boolean,
"confidence": number
}
2. Create src/app/actions/enrich-data.ts (Magic/Tags)
This action takes name and distillery (strings) as input. No image upload. It uses gemini-1.5-flash to retrieve knowledge-based data.
System Prompt for this Action:
Plaintext
TASK: You are a Whisky Sommelier.
INPUT: A whisky named "${name}" from distillery "${distillery}".
1. DATABASE LOOKUP:
Retrieve the sensory profile and specific Whiskybase search string for this bottling.
2. TAGGING:
Select the top 5-8 flavor tags strictly from this list:
[${availableTags}]
3. SEARCH STRING:
Create a precise search string for Whiskybase using: "site:whiskybase.com [Distillery] [Vintage/Age] [Bottler/Edition]"
OUTPUT JSON:
{
"suggested_tags": ["tag1", "tag2", "tag3"],
"suggested_custom_tags": ["unique_note_if_missing_in_list"],
"search_string": "string"
}
3. Integration
Update the frontend component to:
Call scan-label first.
Update the UI state with the metadata immediately.
If the scan was successful, automatically trigger enrich-data in the background to fetch tags and search string.