RantoG commited on
Commit
d0a2c9e
Β·
verified Β·
1 Parent(s): fbe94dd

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +30 -39
app.py CHANGED
@@ -1,7 +1,7 @@
1
  from flask import Flask, request, jsonify
2
  from flask_cors import CORS
3
  from google import genai
4
- from google.genai import types # PENTING: Import types untuk konfigurasi JSON
5
  import os
6
  import json
7
  import random
@@ -29,7 +29,6 @@ FS_TOKEN_EXPIRY = 0
29
  # --- HELPER FUNCTIONS ---
30
 
31
  def clean_json_text(text):
32
- """Membersihkan markdown ```json ... ``` jika AI tidak sengaja mengirimnya"""
33
  text = text.strip()
34
  if text.startswith("```"):
35
  parts = text.split("\n", 1)
@@ -153,7 +152,7 @@ def format_user_context(context_data):
153
  def home():
154
  return jsonify({
155
  "status": "online",
156
- "message": "GastroGuard AI Backend is Running (Stable JSON Mode)!",
157
  "endpoints": ["/analyze-text", "/analyze-image", "/chat"]
158
  })
159
 
@@ -166,13 +165,11 @@ def analyze_text():
166
  if not query:
167
  return jsonify({"error": "No query provided"}), 400
168
 
169
- # 1. Try FatSecret First
170
  if FS_CLIENT_ID and FS_CLIENT_SECRET:
171
  fs_data = search_fatsecret(query)
172
  if fs_data:
173
  return jsonify(fs_data)
174
 
175
- # 2. Check API Key
176
  if not client:
177
  return jsonify(mock_analyze_food(query))
178
 
@@ -180,18 +177,14 @@ def analyze_text():
180
  user_context_str = format_user_context(user_context)
181
 
182
  prompt_content = f"""
183
- ROLE:
184
- You are GastroGuard AI. You are a STRICT CALORIE LOGGING ASSISTANT.
185
-
186
- CONTEXT:
187
- {user_context_str}
188
  USER INPUT: "{query}"
189
 
190
  INSTRUCTIONS:
191
  1. Identify if the user mentioned a food.
192
  2. If yes, ESTIMATE the nutrition facts accurately.
193
- 3. If no (e.g. general chat), keep nutrition values at 0.
194
- 4. Your 'chat_response' must be friendly but SHORT (max 2 sentences).
195
 
196
  OUTPUT JSON FORMAT:
197
  {{
@@ -220,7 +213,6 @@ OUTPUT JSON FORMAT:
220
 
221
  result = json.loads(clean_json_text(response.text))
222
 
223
- # Mapping
224
  food_data = result.get("data_makanan", {})
225
  nutrisi = food_data.get("nutrisi", {})
226
  decision = result.get("keputusan_sistem", {})
@@ -271,7 +263,6 @@ def analyze_image():
271
  user_context = {}
272
  user_context_str = format_user_context(user_context)
273
 
274
- # 1. System Instruction (Aturan Main)
275
  system_instruction_text = f"""
276
  SYSTEM OVERRIDE: YOU ARE A JSON-ONLY API.
277
  ROLE: GastroGuard AI Vision Engine.
@@ -301,21 +292,17 @@ OUTPUT SCHEMA (STRICT):
301
  }}
302
  }}
303
  """
304
-
305
- # 2. Build Content Parts
306
  request_contents = [
307
- system_instruction_text, # Part 1: Instruksi
308
- types.Part.from_bytes( # Part 2: Gambar
309
  data=image_bytes,
310
  mime_type=file.content_type or "image/jpeg"
311
  )
312
  ]
313
 
314
- # Part 3: User Query
315
  if user_prompt:
316
  request_contents.append(f"USER QUERY: {user_prompt}")
317
 
318
- # 3. Execute with JSON Config
319
  response_vision = client.models.generate_content(
320
  model="gemini-2.5-flash",
321
  contents=request_contents,
@@ -328,14 +315,12 @@ OUTPUT SCHEMA (STRICT):
328
  text_res = clean_json_text(response_vision.text)
329
  result = json.loads(text_res)
330
 
331
- # 4. Mapping Result
332
  food_data = result.get("data_makanan", {})
333
  nutrisi = food_data.get("nutrisi", {})
334
  decision = result.get("keputusan_sistem", {})
335
 
336
  health_msg = f"[{decision.get('safety_score', 'Info')}] {decision.get('alasan_utama', '')}"
337
 
338
- # APPEND LENGKAP MACROS KE TEKS
339
  nutrition_text = f"\n\nπŸ“Š **{food_data.get('nama_menu', 'Food')} Info:**\nπŸ”₯ {nutrisi.get('kalori', 0)} kcal | πŸ₯© P: {nutrisi.get('protein', 0)}g | 🍞 C: {nutrisi.get('karbohidrat', 0)}g | πŸ₯‘ F: {nutrisi.get('lemak_total', 0)}g"
340
 
341
  final_reply = result.get("chat_response", "Food detected.") + nutrition_text
@@ -377,20 +362,22 @@ def chat():
377
  try:
378
  user_context_str = format_user_context(user_context)
379
 
380
- # --- PERBAIKAN PROMPT CHAT (DIPERKETAT) ---
 
 
381
  prompt_content = f"""
382
  ROLE: GastroGuard AI (Health & Nutrition Expert).
383
  CONTEXT: {user_context_str}
384
  USER MESSAGE: "{message}"
385
 
386
- INSTRUCTIONS:
387
- 1. Analyze the user's message.
388
- 2. IF USER MENTIONS A FOOD or intent to eat (e.g., "I want to eat ayam geprek", "Nasi goreng calories?"):
389
- - IDENTIFY the food name.
390
- - ESTIMATE nutritional values (Calories, Protein, Carbs, Fat) based on standard serving.
391
- - DO NOT leave values as 0. ESTIMATION IS MANDATORY.
392
- 3. IF GENERAL CHAT (No food mentioned): Keep nutrition values at 0.
393
- 4. Output MUST be valid JSON.
394
 
395
  OUTPUT JSON SCHEMA:
396
  {{
@@ -411,8 +398,7 @@ OUTPUT JSON SCHEMA:
411
  contents=prompt_content,
412
  config=types.GenerateContentConfig(
413
  response_mime_type="application/json",
414
- # Temperature diturunkan agar lebih patuh pada estimasi data
415
- temperature=0.3
416
  )
417
  )
418
 
@@ -422,17 +408,22 @@ OUTPUT JSON SCHEMA:
422
  food_data = result.get("data_makanan", {})
423
  nutrisi = food_data.get("nutrisi", {})
424
 
425
- # --- PERBAIKAN DISPLAY OUTPUT CHAT ---
426
- # Jika ada makanan terdeteksi, tambahkan info makro lengkap ke teks balasan
427
- # agar user bisa lihat di chat bubble.
428
- if food_data.get("nama_menu") and nutrisi.get("kalori", 0) > 0:
429
- nutrition_text = f"\n\nπŸ“Š **{food_data.get('nama_menu')} Info:**\nπŸ”₯ {nutrisi.get('kalori')} kcal | πŸ₯© P: {nutrisi.get('protein', 0)}g | 🍞 C: {nutrisi.get('karbohidrat', 0)}g | πŸ₯‘ F: {nutrisi.get('lemak_total', 0)}g"
 
 
 
 
 
430
  final_reply += nutrition_text
431
 
432
  mapped_result = {
433
  "reply": final_reply,
434
  "food_name": food_data.get("nama_menu"),
435
- "calories": nutrisi.get("kalori", 0),
436
  "protein": nutrisi.get("protein", 0),
437
  "carbs": nutrisi.get("karbohidrat", 0),
438
  "fat": nutrisi.get("lemak_total", 0),
 
1
  from flask import Flask, request, jsonify
2
  from flask_cors import CORS
3
  from google import genai
4
+ from google.genai import types
5
  import os
6
  import json
7
  import random
 
29
  # --- HELPER FUNCTIONS ---
30
 
31
  def clean_json_text(text):
 
32
  text = text.strip()
33
  if text.startswith("```"):
34
  parts = text.split("\n", 1)
 
152
  def home():
153
  return jsonify({
154
  "status": "online",
155
+ "message": "GastroGuard AI Backend is Running (Aggressive Data Mode)!",
156
  "endpoints": ["/analyze-text", "/analyze-image", "/chat"]
157
  })
158
 
 
165
  if not query:
166
  return jsonify({"error": "No query provided"}), 400
167
 
 
168
  if FS_CLIENT_ID and FS_CLIENT_SECRET:
169
  fs_data = search_fatsecret(query)
170
  if fs_data:
171
  return jsonify(fs_data)
172
 
 
173
  if not client:
174
  return jsonify(mock_analyze_food(query))
175
 
 
177
  user_context_str = format_user_context(user_context)
178
 
179
  prompt_content = f"""
180
+ ROLE: You are GastroGuard AI. STRICT CALORIE LOGGING ASSISTANT.
181
+ CONTEXT: {user_context_str}
 
 
 
182
  USER INPUT: "{query}"
183
 
184
  INSTRUCTIONS:
185
  1. Identify if the user mentioned a food.
186
  2. If yes, ESTIMATE the nutrition facts accurately.
187
+ 3. Your 'chat_response' must be friendly but SHORT.
 
188
 
189
  OUTPUT JSON FORMAT:
190
  {{
 
213
 
214
  result = json.loads(clean_json_text(response.text))
215
 
 
216
  food_data = result.get("data_makanan", {})
217
  nutrisi = food_data.get("nutrisi", {})
218
  decision = result.get("keputusan_sistem", {})
 
263
  user_context = {}
264
  user_context_str = format_user_context(user_context)
265
 
 
266
  system_instruction_text = f"""
267
  SYSTEM OVERRIDE: YOU ARE A JSON-ONLY API.
268
  ROLE: GastroGuard AI Vision Engine.
 
292
  }}
293
  }}
294
  """
 
 
295
  request_contents = [
296
+ system_instruction_text,
297
+ types.Part.from_bytes(
298
  data=image_bytes,
299
  mime_type=file.content_type or "image/jpeg"
300
  )
301
  ]
302
 
 
303
  if user_prompt:
304
  request_contents.append(f"USER QUERY: {user_prompt}")
305
 
 
306
  response_vision = client.models.generate_content(
307
  model="gemini-2.5-flash",
308
  contents=request_contents,
 
315
  text_res = clean_json_text(response_vision.text)
316
  result = json.loads(text_res)
317
 
 
318
  food_data = result.get("data_makanan", {})
319
  nutrisi = food_data.get("nutrisi", {})
320
  decision = result.get("keputusan_sistem", {})
321
 
322
  health_msg = f"[{decision.get('safety_score', 'Info')}] {decision.get('alasan_utama', '')}"
323
 
 
324
  nutrition_text = f"\n\nπŸ“Š **{food_data.get('nama_menu', 'Food')} Info:**\nπŸ”₯ {nutrisi.get('kalori', 0)} kcal | πŸ₯© P: {nutrisi.get('protein', 0)}g | 🍞 C: {nutrisi.get('karbohidrat', 0)}g | πŸ₯‘ F: {nutrisi.get('lemak_total', 0)}g"
325
 
326
  final_reply = result.get("chat_response", "Food detected.") + nutrition_text
 
362
  try:
363
  user_context_str = format_user_context(user_context)
364
 
365
+ # --- PERBAIKAN LOGIKA CHAT ---
366
+ # Prompt ini memaksa AI untuk "EXTRACT DATA" jika ada nama makanan
367
+ # meskipun user menggunakan kata "mau" (intent).
368
  prompt_content = f"""
369
  ROLE: GastroGuard AI (Health & Nutrition Expert).
370
  CONTEXT: {user_context_str}
371
  USER MESSAGE: "{message}"
372
 
373
+ MANDATORY INSTRUCTIONS:
374
+ 1. If the user mentions ANY food name (e.g., "I want to eat X", "X calories?", "Eating X"):
375
+ - YOU MUST ESTIMATE THE NUTRITION FACTS in 'data_makanan'.
376
+ - DO NOT leave calorie values as 0.
377
+ - FILL 'nama_menu' with the specific food name.
378
+ - IGNORE verbs like "want to" or "planning to". If food is named, DATA IS REQUIRED.
379
+ 2. If no food is mentioned (e.g., "Hello", "My stomach hurts"), keep nutrition 0.
380
+ 3. RETURN JSON ONLY.
381
 
382
  OUTPUT JSON SCHEMA:
383
  {{
 
398
  contents=prompt_content,
399
  config=types.GenerateContentConfig(
400
  response_mime_type="application/json",
401
+ temperature=0.3 # Temperature rendah = patuh instruksi
 
402
  )
403
  )
404
 
 
408
  food_data = result.get("data_makanan", {})
409
  nutrisi = food_data.get("nutrisi", {})
410
 
411
+ # --- PYTHON LOGIC FIX ---
412
+ # Pastikan kalori dianggap angka (bukan string) untuk pengecekan
413
+ try:
414
+ cal_val = float(nutrisi.get("kalori", 0))
415
+ except:
416
+ cal_val = 0
417
+
418
+ # Jika ada nama menu DAN kalori > 0, tempelkan teks nutrisi
419
+ if food_data.get("nama_menu") and cal_val > 0:
420
+ nutrition_text = f"\n\nπŸ“Š **{food_data.get('nama_menu')} Info:**\nπŸ”₯ {cal_val} kcal | πŸ₯© P: {nutrisi.get('protein', 0)}g | 🍞 C: {nutrisi.get('karbohidrat', 0)}g | πŸ₯‘ F: {nutrisi.get('lemak_total', 0)}g"
421
  final_reply += nutrition_text
422
 
423
  mapped_result = {
424
  "reply": final_reply,
425
  "food_name": food_data.get("nama_menu"),
426
+ "calories": cal_val,
427
  "protein": nutrisi.get("protein", 0),
428
  "carbs": nutrisi.get("karbohidrat", 0),
429
  "fat": nutrisi.get("lemak_total", 0),