false, 'error' => 'No image or barcode provided']); exit; } $productData = null; $errorDetails = null; // Try Barcode Lookup via Open Food Facts if barcode is present if ($barcode) { debugLog("Attempting Open Food Facts lookup for $barcode"); $url = "https://world.openfoodfacts.org/api/v0/product/" . urlencode($barcode) . ".json"; $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_TIMEOUT, 5); curl_setopt($ch, CURLOPT_USERAGENT, 'HomePantryTracker/1.0'); $response = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); if ($httpCode === 200 && $response) { $offData = json_decode($response, true); if (isset($offData['status']) && $offData['status'] == 1) { $p = $offData['product']; $productData = [ 'name' => $p['product_name'] ?? ($p['product_name_en'] ?? 'Unknown Product'), 'category' => 'Pantry', // Default 'quantity' => $p['quantity'] ?? '', 'expiration_date' => null ]; debugLog("Barcode found: " . $productData['name']); // Try to map category if (!empty($p['categories_tags'])) { $tags = implode(' ', $p['categories_tags']); if (stripos($tags, 'dairy') !== false || stripos($tags, 'milk') !== false) $productData['category'] = 'Dairy'; elseif (stripos($tags, 'meat') !== false) $productData['category'] = 'Meat'; elseif (stripos($tags, 'bakery') !== false || stripos($tags, 'bread') !== false) $productData['category'] = 'Bakery'; elseif (stripos($tags, 'fruit') !== false || stripos($tags, 'vegetable') !== false) $productData['category'] = 'Produce'; elseif (stripos($tags, 'frozen') !== false) $productData['category'] = 'Frozen'; } } else { debugLog("Barcode not found in Open Food Facts"); } } else { debugLog("Open Food Facts API error: $httpCode"); } } // If barcode lookup failed or we have an image, use AI if (!$productData) { debugLog("Using AI for identification..."); $prompt = "You are a professional pantry organizer. Identify the product from the image provided. Return ONLY a valid JSON object. Keys: - name: Specific brand and product name. - category: Dairy, Meat, Bakery, Produce, Pantry, Frozen. - quantity: e.g., '1.5 L', '500g'. - expiration_date: Look for 'Best Before', 'Use By', or 'EXP' date in YYYY-MM-DD format, or null. If the package is not in English, translate name/category to English."; if ($image) { // Use "Simple" approach: data URL directly in the content string $content = $prompt . "\nAnalyze this image: " . $image; $messages = [ ['role' => 'user', 'content' => $content] ]; } else { $messages = [ ['role' => 'user', 'content' => $prompt . " Product barcode: $barcode"] ]; } $resp = LocalAIApi::createResponse([ 'input' => $messages, 'temperature' => 0.1 ]); if (!empty($resp['success'])) { debugLog("AI response successful"); $result = LocalAIApi::decodeJsonFromResponse($resp); if ($result) { $productData = $result; } else { $text = LocalAIApi::extractText($resp); debugLog("AI returned text instead of JSON. Extracting..."); if (preg_match('/\{.*\}/s', $text, $matches)) { $productData = json_decode($matches[0], true); } else { $errorDetails = "AI returned text instead of JSON: " . substr($text, 0, 100); debugLog("Error: $errorDetails"); } } } else { $errorDetails = $resp['error'] ?? $resp['message'] ?? 'AI request failed'; $httpStatus = $resp['status'] ?? 'unknown'; debugLog("AI Request failed. Status: $httpStatus, Error: $errorDetails"); if (isset($resp['response'])) { debugLog("Full proxy response: " . json_encode($resp['response'])); } } } if ($productData) { if (isset($productData['expiration_date']) && ($productData['expiration_date'] === 'null' || $productData['expiration_date'] === '')) { $productData['expiration_date'] = null; } debugLog("Returning success for " . ($productData['name'] ?? 'unknown')); echo json_encode(['success' => true, 'data' => $productData]); } else { debugLog("Returning failure: " . ($errorDetails ?? 'Could not identify product')); echo json_encode(['success' => false, 'error' => $errorDetails ?: 'Could not identify product']); }