File size: 8,360 Bytes
59ab782
 
 
 
9b92ec5
59ab782
 
 
0f54ea3
59ab782
 
 
 
 
 
 
 
 
9b92ec5
59ab782
0f54ea3
59ab782
0f54ea3
 
59ab782
 
 
 
 
b37d30d
59ab782
 
 
 
 
 
 
 
 
 
b37d30d
59ab782
 
 
 
 
 
 
8986db1
 
59ab782
 
 
 
 
 
 
 
 
 
 
f746ed1
59ab782
 
 
 
 
 
 
f746ed1
48e95f0
 
59ab782
48e95f0
59ab782
 
 
 
 
 
48e95f0
59ab782
 
 
 
 
 
 
9b92ec5
59ab782
 
48e95f0
9b92ec5
 
 
 
 
59ab782
 
9b92ec5
59ab782
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b37d30d
9b92ec5
59ab782
 
9b92ec5
59ab782
 
 
 
 
b37d30d
 
59ab782
 
9b92ec5
59ab782
 
 
b37d30d
 
59ab782
 
 
9b92ec5
59ab782
b37d30d
59ab782
 
 
 
 
 
 
 
 
 
b37d30d
59ab782
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b37d30d
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
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
    }