Spaces:
Running
Running
Prathamesh Sable
commited on
Commit
·
b37d30d
1
Parent(s):
21c0d12
Add ingredient IDs to product analysis and storage
Browse files* **db/models.py**: Add `ingredient_ids` column to `Product` model.
* **db/repositories.py**: Refactor `add_product` method in `ProductRepository` to handle analysis of product data.
* **routers/product.py**: Update `create_product` function to ensure analysis of product data is stored correctly.
* **services/productAnalyzerAgent.py**: Ensure `analyze_product_ingredients` function returns analysis data in the correct format. Update `analyze_product_ingredients` function to include ingredient IDs in the analysis data.
- db/models.py +1 -1
- db/repositories.py +12 -1
- routers/product.py +18 -0
- services/productAnalyzerAgent.py +14 -7
db/models.py
CHANGED
|
@@ -112,4 +112,4 @@ class ScanHistory(Base):
|
|
| 112 |
scan_date = Column(DateTime, default=datetime.now)
|
| 113 |
|
| 114 |
# Relationships
|
| 115 |
-
user = relationship("User", back_populates="scan_history")
|
|
|
|
| 112 |
scan_date = Column(DateTime, default=datetime.now)
|
| 113 |
|
| 114 |
# Relationships
|
| 115 |
+
user = relationship("User", back_populates="scan_history")
|
db/repositories.py
CHANGED
|
@@ -90,11 +90,17 @@ class IngredientRepository:
|
|
| 90 |
self.db.refresh(db_ingredient)
|
| 91 |
return db_ingredient
|
| 92 |
return None
|
|
|
|
| 93 |
class ProductRepository:
|
| 94 |
def __init__(self, db: Session):
|
| 95 |
self.db = db
|
| 96 |
|
| 97 |
def add_product(self, product_create: ProductCreate):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 98 |
db_product = models.Product(
|
| 99 |
product_name=product_create.product_name,
|
| 100 |
ingredients=product_create.ingredients,
|
|
@@ -113,4 +119,9 @@ class ProductRepository:
|
|
| 113 |
self.db.add(db_product)
|
| 114 |
self.db.commit()
|
| 115 |
self.db.refresh(db_product)
|
| 116 |
-
return db_product
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 90 |
self.db.refresh(db_ingredient)
|
| 91 |
return db_ingredient
|
| 92 |
return None
|
| 93 |
+
|
| 94 |
class ProductRepository:
|
| 95 |
def __init__(self, db: Session):
|
| 96 |
self.db = db
|
| 97 |
|
| 98 |
def add_product(self, product_create: ProductCreate):
|
| 99 |
+
db_product = self._create_product(product_create)
|
| 100 |
+
self._store_analysis_data(db_product, product_create.ingredients_analysis)
|
| 101 |
+
return db_product
|
| 102 |
+
|
| 103 |
+
def _create_product(self, product_create: ProductCreate):
|
| 104 |
db_product = models.Product(
|
| 105 |
product_name=product_create.product_name,
|
| 106 |
ingredients=product_create.ingredients,
|
|
|
|
| 119 |
self.db.add(db_product)
|
| 120 |
self.db.commit()
|
| 121 |
self.db.refresh(db_product)
|
| 122 |
+
return db_product
|
| 123 |
+
|
| 124 |
+
def _store_analysis_data(self, db_product, ingredients_analysis):
|
| 125 |
+
db_product.ingredients_analysis = ingredients_analysis
|
| 126 |
+
self.db.commit()
|
| 127 |
+
self.db.refresh(db_product)
|
routers/product.py
CHANGED
|
@@ -17,6 +17,7 @@ from dotenv import load_dotenv
|
|
| 17 |
import requests
|
| 18 |
import json
|
| 19 |
from services.ingredients import IngredientService
|
|
|
|
| 20 |
|
| 21 |
load_dotenv()
|
| 22 |
|
|
@@ -155,6 +156,23 @@ async def create_product(
|
|
| 155 |
if ingredient:
|
| 156 |
product_create_data.ingredient_ids.append(ingredient.id)
|
| 157 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 158 |
# use repository to add product
|
| 159 |
product_repo = ProductRepository(db)
|
| 160 |
product = product_repo.add_product(product_create_data)
|
|
|
|
| 17 |
import requests
|
| 18 |
import json
|
| 19 |
from services.ingredients import IngredientService
|
| 20 |
+
from services.productAnalyzerAgent import analyze_product_ingredients
|
| 21 |
|
| 22 |
load_dotenv()
|
| 23 |
|
|
|
|
| 156 |
if ingredient:
|
| 157 |
product_create_data.ingredient_ids.append(ingredient.id)
|
| 158 |
|
| 159 |
+
# Analyze product ingredients and store analysis data
|
| 160 |
+
ingredient_results = []
|
| 161 |
+
for ingredient_name in product_create_data.ingredients:
|
| 162 |
+
ingredient = ingredient_repo.get_ingredient_by_name(ingredient_name)
|
| 163 |
+
if ingredient:
|
| 164 |
+
ingredient_results.append(ingredient)
|
| 165 |
+
|
| 166 |
+
product_analysis = await analyze_product_ingredients(
|
| 167 |
+
ingredients_data=ingredient_results,
|
| 168 |
+
user_preferences={
|
| 169 |
+
"user_id": product_create_data.user_id,
|
| 170 |
+
"allergies": None,
|
| 171 |
+
"dietary_restrictions": None
|
| 172 |
+
}
|
| 173 |
+
)
|
| 174 |
+
product_create_data.ingredients_analysis = product_analysis
|
| 175 |
+
|
| 176 |
# use repository to add product
|
| 177 |
product_repo = ProductRepository(db)
|
| 178 |
product = product_repo.add_product(product_create_data)
|
services/productAnalyzerAgent.py
CHANGED
|
@@ -31,6 +31,7 @@ async def analyze_product_ingredients(
|
|
| 31 |
|
| 32 |
# Prepare ingredient data for the prompt
|
| 33 |
ingredients_summary = []
|
|
|
|
| 34 |
for i, ingredient in enumerate(ingredients_data):
|
| 35 |
ingredient_info = f"""
|
| 36 |
Ingredient {i+1}: {ingredient.name}
|
|
@@ -41,6 +42,7 @@ Health Effects: {', '.join(ingredient.health_effects) if ingredient.health_effec
|
|
| 41 |
Description: {ingredient.description[:200] + '...' if len(ingredient.description) > 200 else ingredient.description}
|
| 42 |
"""
|
| 43 |
ingredients_summary.append(ingredient_info)
|
|
|
|
| 44 |
|
| 45 |
# Add user preferences context if available
|
| 46 |
user_context = ""
|
|
@@ -87,7 +89,8 @@ analysis that would be helpful for a consumer viewing this in an AR application.
|
|
| 87 |
"concerns": (array of strings)
|
| 88 |
}},
|
| 89 |
"ingredient_interactions": (array of strings),
|
| 90 |
-
"key_takeaway": (string)
|
|
|
|
| 91 |
}}
|
| 92 |
|
| 93 |
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.
|
|
@@ -111,6 +114,7 @@ Only include factual information based on the provided data. If information is u
|
|
| 111 |
if json_match:
|
| 112 |
try:
|
| 113 |
analysis = json.loads(json_match.group(0))
|
|
|
|
| 114 |
logger.info("Successfully parsed product analysis")
|
| 115 |
return analysis
|
| 116 |
except json.JSONDecodeError as e:
|
|
@@ -120,20 +124,22 @@ Only include factual information based on the provided data. If information is u
|
|
| 120 |
"overall_safety_score": calculate_average_safety(ingredients_data),
|
| 121 |
"error": "Failed to parse complete analysis",
|
| 122 |
"ingredient_count": len(ingredients_data),
|
| 123 |
-
"key_takeaway": "Analysis error occurred, please check individual ingredients"
|
|
|
|
| 124 |
}
|
| 125 |
else:
|
| 126 |
logger.error("Could not find JSON in LLM response")
|
| 127 |
return {
|
| 128 |
"overall_safety_score": calculate_average_safety(ingredients_data),
|
| 129 |
"error": "Failed to generate structured analysis",
|
| 130 |
-
"ingredient_count": len(ingredients_data)
|
|
|
|
| 131 |
}
|
| 132 |
|
| 133 |
except Exception as e:
|
| 134 |
logger.error(f"Error in product analysis: {e}")
|
| 135 |
# Fallback analysis based on simple calculations
|
| 136 |
-
return generate_fallback_analysis(ingredients_data)
|
| 137 |
|
| 138 |
|
| 139 |
def calculate_average_safety(ingredients_data: List[IngredientAnalysisResult]) -> float:
|
|
@@ -144,7 +150,7 @@ def calculate_average_safety(ingredients_data: List[IngredientAnalysisResult]) -
|
|
| 144 |
return round(sum(safety_scores) / len(safety_scores), 1)
|
| 145 |
|
| 146 |
|
| 147 |
-
def generate_fallback_analysis(ingredients_data: List[IngredientAnalysisResult]) -> Dict[str, Any]:
|
| 148 |
"""Generate a basic analysis when LLM processing fails."""
|
| 149 |
# Extract known allergens
|
| 150 |
allergens = []
|
|
@@ -176,5 +182,6 @@ def generate_fallback_analysis(ingredients_data: List[IngredientAnalysisResult])
|
|
| 176 |
"benefits": [],
|
| 177 |
"concerns": ["Analysis system encountered an error, please check individual ingredients"]
|
| 178 |
},
|
| 179 |
-
"key_takeaway": f"Product has {len(ingredients_data)} ingredients with average safety score of {safety_score}/10"
|
| 180 |
-
|
|
|
|
|
|
| 31 |
|
| 32 |
# Prepare ingredient data for the prompt
|
| 33 |
ingredients_summary = []
|
| 34 |
+
ingredient_ids = []
|
| 35 |
for i, ingredient in enumerate(ingredients_data):
|
| 36 |
ingredient_info = f"""
|
| 37 |
Ingredient {i+1}: {ingredient.name}
|
|
|
|
| 42 |
Description: {ingredient.description[:200] + '...' if len(ingredient.description) > 200 else ingredient.description}
|
| 43 |
"""
|
| 44 |
ingredients_summary.append(ingredient_info)
|
| 45 |
+
ingredient_ids.append(ingredient.id)
|
| 46 |
|
| 47 |
# Add user preferences context if available
|
| 48 |
user_context = ""
|
|
|
|
| 89 |
"concerns": (array of strings)
|
| 90 |
}},
|
| 91 |
"ingredient_interactions": (array of strings),
|
| 92 |
+
"key_takeaway": (string),
|
| 93 |
+
"ingredient_ids": (array of integers)
|
| 94 |
}}
|
| 95 |
|
| 96 |
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.
|
|
|
|
| 114 |
if json_match:
|
| 115 |
try:
|
| 116 |
analysis = json.loads(json_match.group(0))
|
| 117 |
+
analysis["ingredient_ids"] = ingredient_ids
|
| 118 |
logger.info("Successfully parsed product analysis")
|
| 119 |
return analysis
|
| 120 |
except json.JSONDecodeError as e:
|
|
|
|
| 124 |
"overall_safety_score": calculate_average_safety(ingredients_data),
|
| 125 |
"error": "Failed to parse complete analysis",
|
| 126 |
"ingredient_count": len(ingredients_data),
|
| 127 |
+
"key_takeaway": "Analysis error occurred, please check individual ingredients",
|
| 128 |
+
"ingredient_ids": ingredient_ids
|
| 129 |
}
|
| 130 |
else:
|
| 131 |
logger.error("Could not find JSON in LLM response")
|
| 132 |
return {
|
| 133 |
"overall_safety_score": calculate_average_safety(ingredients_data),
|
| 134 |
"error": "Failed to generate structured analysis",
|
| 135 |
+
"ingredient_count": len(ingredients_data),
|
| 136 |
+
"ingredient_ids": ingredient_ids
|
| 137 |
}
|
| 138 |
|
| 139 |
except Exception as e:
|
| 140 |
logger.error(f"Error in product analysis: {e}")
|
| 141 |
# Fallback analysis based on simple calculations
|
| 142 |
+
return generate_fallback_analysis(ingredients_data, ingredient_ids)
|
| 143 |
|
| 144 |
|
| 145 |
def calculate_average_safety(ingredients_data: List[IngredientAnalysisResult]) -> float:
|
|
|
|
| 150 |
return round(sum(safety_scores) / len(safety_scores), 1)
|
| 151 |
|
| 152 |
|
| 153 |
+
def generate_fallback_analysis(ingredients_data: List[IngredientAnalysisResult], ingredient_ids: List[int]) -> Dict[str, Any]:
|
| 154 |
"""Generate a basic analysis when LLM processing fails."""
|
| 155 |
# Extract known allergens
|
| 156 |
allergens = []
|
|
|
|
| 182 |
"benefits": [],
|
| 183 |
"concerns": ["Analysis system encountered an error, please check individual ingredients"]
|
| 184 |
},
|
| 185 |
+
"key_takeaway": f"Product has {len(ingredients_data)} ingredients with average safety score of {safety_score}/10",
|
| 186 |
+
"ingredient_ids": ingredient_ids
|
| 187 |
+
}
|