Spaces:
Sleeping
Sleeping
Update app/utils.py
Browse files- app/utils.py +49 -70
app/utils.py
CHANGED
@@ -1,4 +1,4 @@
|
|
1 |
-
# utils.py
|
2 |
import os
|
3 |
import pandas as pd
|
4 |
from transformers import AutoModel, AutoTokenizer
|
@@ -13,11 +13,6 @@ from flask import current_app
|
|
13 |
import base64
|
14 |
|
15 |
logger = logging.getLogger(__name__)
|
16 |
-
FIXED_ALLERGENS = [
|
17 |
-
"dairy", "eggs", "peanuts", "soy",
|
18 |
-
"tree nuts", "wheat", "fish",
|
19 |
-
"shellfish", "sesame"
|
20 |
-
]
|
21 |
|
22 |
class OCRModel:
|
23 |
_instance = None
|
@@ -150,6 +145,46 @@ class AllergyAnalyzer:
|
|
150 |
results.append(allergy)
|
151 |
return results
|
152 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
153 |
def analyze_image(self, image, user_allergens, claude_api_key=None):
|
154 |
"""تحليل الصورة مباشرة للكشف عن الحساسيات"""
|
155 |
try:
|
@@ -175,19 +210,15 @@ class AllergyAnalyzer:
|
|
175 |
database_matches[allergy].append(token)
|
176 |
elif claude_api_key:
|
177 |
# إذا لم توجد في قاعدة البيانات، نستخدم Claude API
|
178 |
-
claude_results = []
|
179 |
for allergy in user_allergens:
|
180 |
-
if self.
|
181 |
-
claude_results.append(allergy)
|
182 |
-
|
183 |
-
if claude_results:
|
184 |
-
for allergy in claude_results:
|
185 |
detected_allergens.add(allergy)
|
186 |
if allergy not in claude_matches:
|
187 |
claude_matches[allergy] = []
|
188 |
claude_matches[allergy].append(token)
|
189 |
|
190 |
return {
|
|
|
191 |
"detected_allergens": list(detected_allergens),
|
192 |
"database_matches": database_matches,
|
193 |
"claude_matches": claude_matches,
|
@@ -197,65 +228,13 @@ class AllergyAnalyzer:
|
|
197 |
except Exception as e:
|
198 |
logger.error(f"Error analyzing image: {str(e)}", exc_info=True)
|
199 |
return {
|
|
|
200 |
"detected_allergens": [],
|
201 |
"database_matches": {},
|
202 |
"claude_matches": {},
|
203 |
-
"analyzed_tokens": []
|
204 |
-
"error": str(e)
|
205 |
}
|
206 |
-
|
207 |
-
def
|
208 |
-
"""
|
209 |
-
|
210 |
-
# تحضير الصورة للطلب
|
211 |
-
img_byte_arr = io.BytesIO()
|
212 |
-
image.save(img_byte_arr, format='JPEG')
|
213 |
-
img_byte_arr = img_byte_arr.getvalue()
|
214 |
-
|
215 |
-
prompt = f"""
|
216 |
-
Analyze if this product contains or is derived from {allergy}.
|
217 |
-
Focus on the ingredient: {token}.
|
218 |
-
Respond ONLY with 'Yes' or 'No'. No explanations.
|
219 |
-
"""
|
220 |
-
|
221 |
-
url = "https://api.anthropic.com/v1/messages"
|
222 |
-
headers = {
|
223 |
-
"x-api-key": api_key,
|
224 |
-
"content-type": "application/json",
|
225 |
-
"anthropic-version": "2023-06-01"
|
226 |
-
}
|
227 |
-
|
228 |
-
data = {
|
229 |
-
"model": "claude-3-opus-20240229",
|
230 |
-
"messages": [
|
231 |
-
{
|
232 |
-
"role": "user",
|
233 |
-
"content": [
|
234 |
-
{
|
235 |
-
"type": "image",
|
236 |
-
"source": {
|
237 |
-
"type": "base64",
|
238 |
-
"media_type": "image/jpeg",
|
239 |
-
"data": base64.b64encode(img_byte_arr).decode('utf-8')
|
240 |
-
}
|
241 |
-
},
|
242 |
-
{
|
243 |
-
"type": "text",
|
244 |
-
"text": prompt
|
245 |
-
}
|
246 |
-
]
|
247 |
-
}
|
248 |
-
],
|
249 |
-
"max_tokens": 10
|
250 |
-
}
|
251 |
-
|
252 |
-
response = requests.post(url, json=data, headers=headers)
|
253 |
-
json_response = response.json()
|
254 |
-
|
255 |
-
if "content" in json_response and isinstance(json_response["content"], list):
|
256 |
-
return json_response["content"][0]["text"].strip().lower() == 'yes'
|
257 |
-
return False
|
258 |
-
|
259 |
-
except Exception as e:
|
260 |
-
logger.error(f"Error querying Claude API: {str(e)}")
|
261 |
-
return False
|
|
|
1 |
+
# utils.py (معدل)
|
2 |
import os
|
3 |
import pandas as pd
|
4 |
from transformers import AutoModel, AutoTokenizer
|
|
|
13 |
import base64
|
14 |
|
15 |
logger = logging.getLogger(__name__)
|
|
|
|
|
|
|
|
|
|
|
16 |
|
17 |
class OCRModel:
|
18 |
_instance = None
|
|
|
145 |
results.append(allergy)
|
146 |
return results
|
147 |
|
148 |
+
def check_allergy_risk(self, ingredient, allergy_type, api_key):
|
149 |
+
"""الاستعلام من Claude API عن الحساسيات"""
|
150 |
+
prompt = f"""
|
151 |
+
You are a professional food safety expert specializing in allergen classification and risk assessment. Your task is critical because allergic reactions to {allergy_type} can be severe or even life-threatening.
|
152 |
+
|
153 |
+
Please analyze the ingredient '{ingredient}' and determine whether it contains or is derived from {allergy_type}, posing a potential allergy risk for individuals with {allergy_type} allergies.
|
154 |
+
|
155 |
+
⚠️ *Important Guidelines:*
|
156 |
+
- Base your answer strictly on scientific classification and allergen presence.
|
157 |
+
- Do NOT rely solely on word similarity—consider actual ingredient composition.
|
158 |
+
- If '{ingredient}' contains {allergy_type} proteins or poses a high cross-allergy risk, answer *'Yes'*.
|
159 |
+
- If it is completely unrelated and safe for individuals with {allergy_type} allergies, answer *'No'*.
|
160 |
+
|
161 |
+
🚨 *Final Answer:* Only respond with *'Yes'* or *'No'*. Do NOT provide explanations.
|
162 |
+
"""
|
163 |
+
url = "https://api.anthropic.com/v1/messages"
|
164 |
+
headers = {
|
165 |
+
"x-api-key": api_key,
|
166 |
+
"content-type": "application/json",
|
167 |
+
"anthropic-version": "2023-06-01"
|
168 |
+
}
|
169 |
+
|
170 |
+
data = {
|
171 |
+
"model": "claude-3-opus-20240229",
|
172 |
+
"messages": [{"role": "user", "content": prompt}],
|
173 |
+
"max_tokens": 10
|
174 |
+
}
|
175 |
+
|
176 |
+
try:
|
177 |
+
response = requests.post(url, json=data, headers=headers)
|
178 |
+
json_response = response.json()
|
179 |
+
|
180 |
+
if "content" in json_response and isinstance(json_response["content"], list):
|
181 |
+
return json_response["content"][0]["text"].strip().lower() == 'yes'
|
182 |
+
return False
|
183 |
+
|
184 |
+
except Exception as e:
|
185 |
+
logger.error(f"Error querying Claude API: {str(e)}")
|
186 |
+
return False
|
187 |
+
|
188 |
def analyze_image(self, image, user_allergens, claude_api_key=None):
|
189 |
"""تحليل الصورة مباشرة للكشف عن الحساسيات"""
|
190 |
try:
|
|
|
210 |
database_matches[allergy].append(token)
|
211 |
elif claude_api_key:
|
212 |
# إذا لم توجد في قاعدة البيانات، نستخدم Claude API
|
|
|
213 |
for allergy in user_allergens:
|
214 |
+
if self.check_allergy_risk(token, allergy, claude_api_key):
|
|
|
|
|
|
|
|
|
215 |
detected_allergens.add(allergy)
|
216 |
if allergy not in claude_matches:
|
217 |
claude_matches[allergy] = []
|
218 |
claude_matches[allergy].append(token)
|
219 |
|
220 |
return {
|
221 |
+
"extracted_text": extracted_text,
|
222 |
"detected_allergens": list(detected_allergens),
|
223 |
"database_matches": database_matches,
|
224 |
"claude_matches": claude_matches,
|
|
|
228 |
except Exception as e:
|
229 |
logger.error(f"Error analyzing image: {str(e)}", exc_info=True)
|
230 |
return {
|
231 |
+
"error": str(e),
|
232 |
"detected_allergens": [],
|
233 |
"database_matches": {},
|
234 |
"claude_matches": {},
|
235 |
+
"analyzed_tokens": []
|
|
|
236 |
}
|
237 |
+
|
238 |
+
def get_allergen_list(self):
|
239 |
+
"""الحصول على قائمة الحساسيات المعروفة"""
|
240 |
+
return list(self.allergy_dict.keys())
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|