Spaces:
Running
Running
| import os | |
| from typing import List, Dict, Any, Optional | |
| from langchain_core.messages import HumanMessage | |
| from langchain_google_genai import ChatGoogleGenerativeAI | |
| from logger_manager import log_error, log_info | |
| from interfaces.ingredientModels import IngredientAnalysisResult | |
| # Load environment variables | |
| from env import LLM_API_KEY, LLM_MODEL_NAME | |
| async def analyze_product_ingredients( | |
| ingredients_data: List[IngredientAnalysisResult], | |
| user_preferences: Optional[Dict[str, Any]] = None | |
| ) -> Dict[str, Any]: | |
| """ | |
| Analyze multiple ingredients to provide a comprehensive product analysis | |
| for AR display, considering user preferences and dietary restrictions. | |
| """ | |
| log_info(f"Analyzing product with {len(ingredients_data)} ingredients") | |
| # Initialize LLM | |
| llm = ChatGoogleGenerativeAI( | |
| google_LLM_API_KEY=LLM_API_KEY, | |
| model=LLM_MODEL_NAME, | |
| temperature=0.2 # Lower temperature for more factual responses | |
| ) | |
| # Prepare ingredient data for the prompt | |
| ingredients_summary = [] | |
| ingredient_ids = [] | |
| for i, ingredient in enumerate(ingredients_data): | |
| ingredient_info = f""" | |
| Ingredient {i+1}: {ingredient.name} | |
| Safety Rating: {ingredient.safety_rating}/10 | |
| Diet Type: {ingredient.diet_type if hasattr(ingredient, 'diet_type') else 'Unknown'} | |
| Allergic Info: {', '.join(ingredient.allergic_info) if hasattr(ingredient, 'allergic_info') and ingredient.allergic_info else 'None known'} | |
| Health Effects: {', '.join(ingredient.health_effects) if ingredient.health_effects else 'Unknown'} | |
| Description: {ingredient.description[:200] + '...' if len(ingredient.description) > 200 else ingredient.description} | |
| """ | |
| ingredients_summary.append(ingredient_info) | |
| ingredient_ids.append(ingredient.id) | |
| # Add user preferences context if available | |
| user_context = "" | |
| if user_preferences: | |
| allergies = user_preferences.get("allergies", "None specified") | |
| diet = user_preferences.get("dietary_restrictions", "None specified") | |
| user_context = f""" | |
| ## Also consider the following user preferences: | |
| User has the following preferences: | |
| - Dietary Restrictions: {diet} | |
| - Allergies: {allergies} | |
| """ | |
| # Create the analysis prompt | |
| analysis_prompt = f""" | |
| # PRODUCT INGREDIENT ANALYSIS TASK | |
| You are an expert food scientist and nutritionist analyzing a product's ingredients. | |
| Based on the detailed information about each ingredient below, provide a comprehensive | |
| analysis that would be helpful for a consumer viewing this while shopping and using that product. | |
| ## INGREDIENTS INFORMATION: | |
| {''.join(ingredients_summary)} | |
| {user_context} | |
| ## REQUIRED ANALYSIS: | |
| 1. Overall Safety Score (1-10): Calculate this based on individual ingredient safety scores 1 means product is not safe at all (like a toxin) and 10 means product is completely safe | |
| 2. Suitable Diet Types: Determine if this product is for vegan, vegetarian, or Non-Vegetarian | |
| 3. Allergy Warnings: Flag any potential allergens present related to food not more than 5 combine if needed | |
| 4. Usage Recommendations: Provide safe consumption limits or usage guidance | |
| 5. Health Insights: Summarize health benefits and concerns of the product not more than 3 for each and also focus on health not other aspects, may combine if needed but keep short | |
| 6. Ingredient Interactions: Note any ingredients that may interact when combined | |
| 7. Key Takeaway: A single sentence summarizing if this product is recommended | |
| ## FORMAT YOUR RESPONSE AS JSON: | |
| {{ | |
| "overall_safety_score": (number between 1-10), | |
| "suitable_diet_types": (strings from "Vegan", "Vegetarian", "Non-Vegetarian"), | |
| "allergy_warnings": (array of strings), | |
| "usage_recommendations": (string with specific guidance), | |
| "health_insights": {{ | |
| "benefits": (array of strings), | |
| "concerns": (array of strings) | |
| }}, | |
| "ingredient_interactions": (array of strings), | |
| "key_takeaway": (string) | |
| }} | |
| Only include factual information based on the provided data. If information is unavailable for any field, use appropriate default values. If the data required is too obvious then give appropriate answer. | |
| IMPORTANT: Ensure your response is valid JSON with double quotes (") around property names and string values. | |
| Avoid single quotes (') for JSON properties and values. | |
| Ensure all elements in arrays and objects are separated by commas, and don't include trailing commas. | |
| Also strictly follow the JSON format in your response. | |
| """ | |
| log_info("Sending product analysis prompt to LLM") | |
| try: | |
| # Process with LLM | |
| message = HumanMessage(content=analysis_prompt) | |
| llm_response = llm.invoke([message]) | |
| analysis_text = llm_response.content | |
| # Extract JSON from response | |
| import json | |
| import re | |
| # Find JSON in the response using regex | |
| json_match = re.search(r'({.*})', analysis_text.replace('\n', ' '), re.DOTALL) | |
| if json_match: | |
| try: | |
| analysis = json.loads(json_match.group(0)) | |
| analysis["ingredient_ids"] = ingredient_ids | |
| log_info("Successfully parsed product analysis") | |
| return analysis | |
| except json.JSONDecodeError as e: | |
| log_error(f"JSON parsing error: {e}",e) | |
| # Return a simplified analysis on error | |
| return { | |
| "overall_safety_score": calculate_average_safety(ingredients_data), | |
| "error": "Failed to parse complete analysis", | |
| "ingredient_count": len(ingredients_data), | |
| "key_takeaway": "Analysis error occurred, please check individual ingredients", | |
| "ingredient_ids": ingredient_ids | |
| } | |
| else: | |
| log_error("Could not find JSON in LLM response") | |
| return { | |
| "overall_safety_score": calculate_average_safety(ingredients_data), | |
| "error": "Failed to generate structured analysis", | |
| "ingredient_count": len(ingredients_data), | |
| "ingredient_ids": ingredient_ids | |
| } | |
| except Exception as e: | |
| log_error(f"Error in product analysis: {e}",e) | |
| # Fallback analysis based on simple calculations | |
| return generate_fallback_analysis(ingredients_data, ingredient_ids) | |
| def calculate_average_safety(ingredients_data: List[IngredientAnalysisResult]) -> float: | |
| """Calculate average safety score from ingredients.""" | |
| safety_scores = [i.safety_rating for i in ingredients_data if i.safety_rating is not None] | |
| if not safety_scores: | |
| return 5.0 # Default middle value | |
| return round(sum(safety_scores) / len(safety_scores), 1) | |
| def generate_fallback_analysis(ingredients_data: List[IngredientAnalysisResult], ingredient_ids: List[int]) -> Dict[str, Any]: | |
| """Generate a basic analysis when LLM processing fails.""" | |
| # Extract known allergens | |
| allergens = [] | |
| for ingredient in ingredients_data: | |
| if hasattr(ingredient, 'allergic_info') and ingredient.allergic_info: | |
| allergens.extend(ingredient.allergic_info) | |
| # Determine diet type based on ingredients | |
| diet_types = [] | |
| all_vegan = all(getattr(i, 'diet_type', '') == 'vegan' for i in ingredients_data | |
| if hasattr(i, 'diet_type') and i.diet_type) | |
| all_vegetarian = all(getattr(i, 'diet_type', '') in ['vegan', 'vegetarian'] | |
| for i in ingredients_data if hasattr(i, 'diet_type') and i.diet_type) | |
| if all_vegan: | |
| diet_types.append("Vegan") | |
| if all_vegetarian: | |
| diet_types.append("Vegetarian") | |
| # Calculate safety score | |
| safety_score = calculate_average_safety(ingredients_data) | |
| return { | |
| "overall_safety_score": safety_score, | |
| "suitable_diet_types": diet_types, | |
| "allergy_warnings": list(set(allergens)), | |
| "usage_recommendations": "Please refer to product packaging for usage guidelines", | |
| "health_insights": { | |
| "benefits": [], | |
| "concerns": ["Analysis system encountered an error, please check individual ingredients"] | |
| }, | |
| "key_takeaway": f"Product has {len(ingredients_data)} ingredients with average safety score of {safety_score}/10", | |
| "ingredient_ids": ingredient_ids | |
| } | |