Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -4,7 +4,7 @@ from google import genai
|
|
| 4 |
from google.genai import types
|
| 5 |
import os
|
| 6 |
import json
|
| 7 |
-
import re
|
| 8 |
import random
|
| 9 |
from dotenv import load_dotenv
|
| 10 |
import requests
|
|
@@ -133,28 +133,17 @@ def format_user_context(context_data):
|
|
| 133 |
2. DAILY STATUS: Target Calories: {stats.get('target_calories', 2000)} kcal, CONSUMED: {stats.get('consumed_calories', 0)} kcal.
|
| 134 |
"""
|
| 135 |
|
| 136 |
-
# --- NEW HELPER: SUPER ROBUST NUMBER EXTRACTOR ---
|
| 137 |
def get_safe_float(data, targets):
|
| 138 |
-
"""
|
| 139 |
-
Mencari nilai float dari dict SECARA CASE-INSENSITIVE.
|
| 140 |
-
Juga menangani jika nilai berupa string (e.g., "350 kcal").
|
| 141 |
-
"""
|
| 142 |
-
# 1. Normalkan semua key di data menjadi huruf kecil
|
| 143 |
data_lower = {k.lower(): v for k, v in data.items()}
|
| 144 |
-
|
| 145 |
for t in targets:
|
| 146 |
-
# Cek target (huruf kecil)
|
| 147 |
if t in data_lower:
|
| 148 |
val = data_lower[t]
|
| 149 |
try:
|
| 150 |
-
|
| 151 |
-
if isinstance(val, (int, float)):
|
| 152 |
-
return float(val)
|
| 153 |
-
# Jika string ("350 kcal"), ambil angkanya saja
|
| 154 |
if isinstance(val, str):
|
| 155 |
nums = re.findall(r"[-+]?\d*\.\d+|\d+", val)
|
| 156 |
-
if nums:
|
| 157 |
-
return float(nums[0])
|
| 158 |
except:
|
| 159 |
continue
|
| 160 |
return 0.0
|
|
@@ -165,7 +154,7 @@ def get_safe_float(data, targets):
|
|
| 165 |
def home():
|
| 166 |
return jsonify({
|
| 167 |
"status": "online",
|
| 168 |
-
"message": "GastroGuard AI Backend
|
| 169 |
"endpoints": ["/analyze-text", "/analyze-image", "/chat"]
|
| 170 |
})
|
| 171 |
|
|
@@ -220,12 +209,10 @@ OUTPUT JSON FORMAT:
|
|
| 220 |
)
|
| 221 |
|
| 222 |
result = json.loads(clean_json_text(response.text))
|
| 223 |
-
|
| 224 |
food_data = result.get("data_makanan", {})
|
| 225 |
nutrisi = food_data.get("nutrisi", {})
|
| 226 |
decision = result.get("keputusan_sistem", {})
|
| 227 |
|
| 228 |
-
# Ekstraksi Aman (Bilingual & Case Insensitive)
|
| 229 |
cal = get_safe_float(nutrisi, ["kalori", "calories", "energy", "kcal"])
|
| 230 |
prot = get_safe_float(nutrisi, ["protein", "protien"])
|
| 231 |
carb = get_safe_float(nutrisi, ["karbohidrat", "carbs", "carbohydrate", "karbo"])
|
|
@@ -315,7 +302,6 @@ OUTPUT SCHEMA (STRICT):
|
|
| 315 |
nutrisi = food_data.get("nutrisi", {})
|
| 316 |
decision = result.get("keputusan_sistem", {})
|
| 317 |
|
| 318 |
-
# Ekstraksi Aman
|
| 319 |
cal = get_safe_float(nutrisi, ["kalori", "calories", "energy", "kcal"])
|
| 320 |
prot = get_safe_float(nutrisi, ["protein"])
|
| 321 |
carb = get_safe_float(nutrisi, ["karbohidrat", "carbs"])
|
|
@@ -361,10 +347,9 @@ CONTEXT: {user_context_str}
|
|
| 361 |
USER MESSAGE: "{message}"
|
| 362 |
|
| 363 |
MANDATORY INSTRUCTIONS:
|
| 364 |
-
1. If the user mentions ANY food name (e.g., "I want to eat X", "X calories?"
|
| 365 |
-
-
|
| 366 |
-
-
|
| 367 |
-
- FILL 'nama_menu' with the specific food name.
|
| 368 |
2. If no food is mentioned, keep nutrition 0.
|
| 369 |
3. RETURN JSON ONLY.
|
| 370 |
|
|
@@ -393,22 +378,36 @@ OUTPUT JSON SCHEMA:
|
|
| 393 |
base_reply = result.get("chat_response", "")
|
| 394 |
food_data = result.get("data_makanan", {})
|
| 395 |
nutrisi = food_data.get("nutrisi", {})
|
|
|
|
| 396 |
|
| 397 |
-
#
|
| 398 |
-
print(f"DEBUG CHAT JSON: {json.dumps(result, indent=2)}")
|
| 399 |
-
|
| 400 |
-
# --- FIX UTAMA: EKSTRAKSI AMAN ---
|
| 401 |
cal_val = get_safe_float(nutrisi, ["kalori", "calories", "energy", "kcal"])
|
| 402 |
prot = get_safe_float(nutrisi, ["protein", "protien"])
|
| 403 |
carb = get_safe_float(nutrisi, ["karbohidrat", "carbs", "carbohydrate", "gula"])
|
| 404 |
fat = get_safe_float(nutrisi, ["lemak_total", "fat", "fats", "lemak"])
|
| 405 |
|
| 406 |
menu_name = food_data.get("nama_menu")
|
| 407 |
-
|
| 408 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 409 |
if cal_val > 0:
|
| 410 |
display_name = menu_name if menu_name else "Food"
|
| 411 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 412 |
final_reply = base_reply + nutrition_text
|
| 413 |
else:
|
| 414 |
final_reply = base_reply
|
|
@@ -420,7 +419,7 @@ OUTPUT JSON SCHEMA:
|
|
| 420 |
"protein": prot,
|
| 421 |
"carbs": carb,
|
| 422 |
"fat": fat,
|
| 423 |
-
"health_tip":
|
| 424 |
}
|
| 425 |
return jsonify(mapped_result)
|
| 426 |
|
|
|
|
| 4 |
from google.genai import types
|
| 5 |
import os
|
| 6 |
import json
|
| 7 |
+
import re # Import Regex untuk "mencuri" angka dari teks
|
| 8 |
import random
|
| 9 |
from dotenv import load_dotenv
|
| 10 |
import requests
|
|
|
|
| 133 |
2. DAILY STATUS: Target Calories: {stats.get('target_calories', 2000)} kcal, CONSUMED: {stats.get('consumed_calories', 0)} kcal.
|
| 134 |
"""
|
| 135 |
|
|
|
|
| 136 |
def get_safe_float(data, targets):
|
| 137 |
+
"""Mencari nilai float dari dict SECARA CASE-INSENSITIVE."""
|
|
|
|
|
|
|
|
|
|
|
|
|
| 138 |
data_lower = {k.lower(): v for k, v in data.items()}
|
|
|
|
| 139 |
for t in targets:
|
|
|
|
| 140 |
if t in data_lower:
|
| 141 |
val = data_lower[t]
|
| 142 |
try:
|
| 143 |
+
if isinstance(val, (int, float)): return float(val)
|
|
|
|
|
|
|
|
|
|
| 144 |
if isinstance(val, str):
|
| 145 |
nums = re.findall(r"[-+]?\d*\.\d+|\d+", val)
|
| 146 |
+
if nums: return float(nums[0])
|
|
|
|
| 147 |
except:
|
| 148 |
continue
|
| 149 |
return 0.0
|
|
|
|
| 154 |
def home():
|
| 155 |
return jsonify({
|
| 156 |
"status": "online",
|
| 157 |
+
"message": "GastroGuard AI Backend (Regex Fallback Enabled)",
|
| 158 |
"endpoints": ["/analyze-text", "/analyze-image", "/chat"]
|
| 159 |
})
|
| 160 |
|
|
|
|
| 209 |
)
|
| 210 |
|
| 211 |
result = json.loads(clean_json_text(response.text))
|
|
|
|
| 212 |
food_data = result.get("data_makanan", {})
|
| 213 |
nutrisi = food_data.get("nutrisi", {})
|
| 214 |
decision = result.get("keputusan_sistem", {})
|
| 215 |
|
|
|
|
| 216 |
cal = get_safe_float(nutrisi, ["kalori", "calories", "energy", "kcal"])
|
| 217 |
prot = get_safe_float(nutrisi, ["protein", "protien"])
|
| 218 |
carb = get_safe_float(nutrisi, ["karbohidrat", "carbs", "carbohydrate", "karbo"])
|
|
|
|
| 302 |
nutrisi = food_data.get("nutrisi", {})
|
| 303 |
decision = result.get("keputusan_sistem", {})
|
| 304 |
|
|
|
|
| 305 |
cal = get_safe_float(nutrisi, ["kalori", "calories", "energy", "kcal"])
|
| 306 |
prot = get_safe_float(nutrisi, ["protein"])
|
| 307 |
carb = get_safe_float(nutrisi, ["karbohidrat", "carbs"])
|
|
|
|
| 347 |
USER MESSAGE: "{message}"
|
| 348 |
|
| 349 |
MANDATORY INSTRUCTIONS:
|
| 350 |
+
1. If the user mentions ANY food name (e.g., "I want to eat X", "X calories?"):
|
| 351 |
+
- IGNORE Safety checks for a moment, and FOCUS ON ESTIMATING NUTRITION in 'data_makanan'.
|
| 352 |
+
- ESTIMATE VALUES EVEN IF UNSURE.
|
|
|
|
| 353 |
2. If no food is mentioned, keep nutrition 0.
|
| 354 |
3. RETURN JSON ONLY.
|
| 355 |
|
|
|
|
| 378 |
base_reply = result.get("chat_response", "")
|
| 379 |
food_data = result.get("data_makanan", {})
|
| 380 |
nutrisi = food_data.get("nutrisi", {})
|
| 381 |
+
decision = result.get("keputusan_sistem", {})
|
| 382 |
|
| 383 |
+
# 1. Cek JSON Normal
|
|
|
|
|
|
|
|
|
|
| 384 |
cal_val = get_safe_float(nutrisi, ["kalori", "calories", "energy", "kcal"])
|
| 385 |
prot = get_safe_float(nutrisi, ["protein", "protien"])
|
| 386 |
carb = get_safe_float(nutrisi, ["karbohidrat", "carbs", "carbohydrate", "gula"])
|
| 387 |
fat = get_safe_float(nutrisi, ["lemak_total", "fat", "fats", "lemak"])
|
| 388 |
|
| 389 |
menu_name = food_data.get("nama_menu")
|
| 390 |
+
|
| 391 |
+
# 2. JURUS PAMUNGKAS (REGEX FALLBACK)
|
| 392 |
+
# Jika JSON kosong (0), tapi AI nulis angka di teks (misal: "Adding 350 kcal..."), kita curi angkanya.
|
| 393 |
+
if cal_val == 0:
|
| 394 |
+
combined_text = base_reply + " " + decision.get("alasan_utama", "")
|
| 395 |
+
# Cari pola angka + kcal/cal/calories
|
| 396 |
+
found_cals = re.findall(r"(\d+)\s*(?:kcal|cal|calories)", combined_text, re.IGNORECASE)
|
| 397 |
+
if found_cals:
|
| 398 |
+
cal_val = float(found_cals[0]) # Ambil angka pertama yang ketemu
|
| 399 |
+
if not menu_name: # Jika nama menu kosong, coba ambil dari chat
|
| 400 |
+
menu_name = "Detected Food"
|
| 401 |
+
|
| 402 |
+
# 3. LOGIKA DISPLAY: Jika kalori > 0 (baik dari JSON atau curian), TEMPEL TEKS.
|
| 403 |
if cal_val > 0:
|
| 404 |
display_name = menu_name if menu_name else "Food"
|
| 405 |
+
# Jika protein/carb/fat masih 0 (karena hasil curian regex cuma kalori), set "-" agar rapi
|
| 406 |
+
p_str = f"{int(prot)}g" if prot > 0 else "?"
|
| 407 |
+
c_str = f"{int(carb)}g" if carb > 0 else "?"
|
| 408 |
+
f_str = f"{int(fat)}g" if fat > 0 else "?"
|
| 409 |
+
|
| 410 |
+
nutrition_text = f"\n\n📊 **{display_name} Info:**\n🔥 {int(cal_val)} kcal | 🥩 P: {p_str} | 🍞 C: {c_str} | 🥑 F: {f_str}"
|
| 411 |
final_reply = base_reply + nutrition_text
|
| 412 |
else:
|
| 413 |
final_reply = base_reply
|
|
|
|
| 419 |
"protein": prot,
|
| 420 |
"carbs": carb,
|
| 421 |
"fat": fat,
|
| 422 |
+
"health_tip": decision.get("alasan_utama", "")
|
| 423 |
}
|
| 424 |
return jsonify(mapped_result)
|
| 425 |
|