fix: resolve magic scan crash and implement context-aware AI languages

- Fixed SQL syntax error in magicScan caused by single quotes
- Implemented dynamic locale-aware AI suggestions (Technical: EN, Custom Tags: Localized)
- Updated Dexie schema to version 2 (added locale to pending_scans)
- Fixed missing bottle_id in UploadQueue synchronization
- Installed missing dexie dependencies via pnpm
This commit is contained in:
2025-12-19 14:06:13 +01:00
parent 60ca3a6190
commit f52cfb80fc
9 changed files with 61 additions and 22 deletions

View File

@@ -9,7 +9,7 @@ import { createHash } from 'crypto';
import { trackApiUsage } from './track-api-usage';
import { checkCreditBalance, deductCredits } from './credit-service';
export async function analyzeBottleNebius(base64Image: string, tags?: string[]): Promise<AnalysisResponse & { search_string?: string }> {
export async function analyzeBottleNebius(base64Image: string, tags?: string[], locale: string = 'de'): Promise<AnalysisResponse & { search_string?: string }> {
const supabase = createServerActionClient({ cookies });
if (!process.env.NEBIUS_API_KEY) {
@@ -48,7 +48,10 @@ export async function analyzeBottleNebius(base64Image: string, tags?: string[]):
};
}
const instruction = GEMINI_SYSTEM_INSTRUCTION.replace('{AVAILABLE_TAGS}', tags ? tags.join(', ') : 'No tags available') + "\nAdditionally, generate a 'search_string' field for Whiskybase in this format: 'site:whiskybase.com [Distillery] [Name] [Vintage]'. Include this field in the JSON object.";
const instruction = GEMINI_SYSTEM_INSTRUCTION
.replace('{AVAILABLE_TAGS}', tags ? tags.join(', ') : 'No tags available')
.replace('{LANGUAGE}', locale === 'en' ? 'English' : 'German')
+ "\nAdditionally, generate a 'search_string' field for Whiskybase in this format: 'site:whiskybase.com [Distillery] [Name] [Vintage]'. Include this field in the JSON object.";
const response = await aiClient.chat.completions.create({
model: "Qwen/Qwen2.5-VL-72B-Instruct",

View File

@@ -8,7 +8,7 @@ import { createHash } from 'crypto';
import { trackApiUsage } from './track-api-usage';
import { checkCreditBalance, deductCredits } from './credit-service';
export async function analyzeBottle(base64Image: string, tags?: string[]): Promise<AnalysisResponse> {
export async function analyzeBottle(base64Image: string, tags?: string[], locale: string = 'de'): Promise<AnalysisResponse> {
const supabase = createServerActionClient({ cookies });
if (!process.env.GEMINI_API_KEY) {
@@ -48,7 +48,9 @@ export async function analyzeBottle(base64Image: string, tags?: string[]): Promi
};
}
const instruction = SYSTEM_INSTRUCTION.replace('{AVAILABLE_TAGS}', tags ? tags.join(', ') : 'No tags available');
const instruction = SYSTEM_INSTRUCTION
.replace('{AVAILABLE_TAGS}', tags ? tags.join(', ') : 'No tags available')
.replace('{LANGUAGE}', locale === 'en' ? 'English' : 'German');
const result = await geminiModel.generateContent([
{

View File

@@ -8,7 +8,7 @@ import { supabase } from '@/lib/supabase';
import { supabaseAdmin } from '@/lib/supabase-admin';
import { AnalysisResponse, BottleMetadata } from '@/types/whisky';
export async function magicScan(base64Image: string, provider: 'gemini' | 'nebius' = 'gemini'): Promise<AnalysisResponse & { wb_id?: string }> {
export async function magicScan(base64Image: string, provider: 'gemini' | 'nebius' = 'gemini', locale: string = 'de'): Promise<AnalysisResponse & { wb_id?: string }> {
try {
// 0. Fetch available tags for constrained generation
const systemTags = await getAllSystemTags();
@@ -17,9 +17,9 @@ export async function magicScan(base64Image: string, provider: 'gemini' | 'nebiu
// 1. AI Analysis
let aiResponse: any;
if (provider === 'nebius') {
aiResponse = await analyzeBottleNebius(base64Image, tagNames);
aiResponse = await analyzeBottleNebius(base64Image, tagNames, locale);
} else {
aiResponse = await analyzeBottle(base64Image, tagNames);
aiResponse = await analyzeBottle(base64Image, tagNames, locale);
}
if (!aiResponse.success || !aiResponse.data) {
@@ -38,7 +38,10 @@ export async function magicScan(base64Image: string, provider: 'gemini' | 'nebiu
const { data: cacheHit } = await supabase
.from('global_products')
.select('wb_id')
.textSearch('search_vector', `'${searchString}'`, { config: 'simple' })
.textSearch('search_vector', searchString, {
config: 'simple',
type: 'websearch'
})
.limit(1)
.maybeSingle();