TimInf commited on
Commit
88f38f2
·
verified ·
1 Parent(s): bcc13b5

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +85 -63
app.py CHANGED
@@ -4,6 +4,9 @@ import torch
4
  import numpy as np
5
  import random
6
  import json
 
 
 
7
 
8
  # Lade RecipeBERT Modell (für semantische Zutat-Kombination)
9
  bert_model_name = "alexdseo/RecipeBERT"
@@ -89,15 +92,6 @@ def get_combined_scores(query_vector, embedding_list, all_good_embeddings, avg_w
89
  def find_best_ingredients(required_ingredients, available_ingredients, max_ingredients=6, avg_weight=0.6):
90
  """
91
  Findet die besten Zutaten basierend auf RecipeBERT Embeddings.
92
-
93
- Args:
94
- required_ingredients (list): Benötigte Zutaten, die verwendet werden müssen
95
- available_ingredients (list): Verfügbare Zutaten zur Auswahl
96
- max_ingredients (int): Maximale Anzahl von Zutaten für das Rezept
97
- avg_weight (float): Gewicht für den Durchschnittsvektor
98
-
99
- Returns:
100
- list: Die optimale Kombination von Zutaten
101
  """
102
  # Stelle sicher, dass keine Duplikate in den Listen sind
103
  required_ingredients = list(set(required_ingredients))
@@ -282,32 +276,16 @@ def generate_recipe_with_t5(ingredients_list, max_retries=5):
282
  "directions": ["Fehler beim Generieren der Rezeptanweisungen"]
283
  }
284
 
285
- def flutter_api_generate_recipe(ingredients_data):
 
286
  """
287
- Flutter-freundliche API-Funktion, die JSON-Eingaben verarbeitet
288
- und strukturierte JSON-Ausgaben zurückgibt, die deiner ursprünglichen Flask-API entsprechen.
289
  """
290
- try:
291
- # Eingabe parsen - behandle sowohl String- als auch Dict-Formate
292
- if isinstance(ingredients_data, str):
293
- data = json.loads(ingredients_data)
294
- else:
295
- data = ingredients_data # Ist bereits ein Dict (z.B. von Gradio UI)
296
-
297
- # Parameter extrahieren (wie deine ursprüngliche Flask-API)
298
- required_ingredients = data.get('required_ingredients', [])
299
- available_ingredients = data.get('available_ingredients', [])
300
-
301
- # Abwärtskompatibilität: Wenn nur 'ingredients' angegeben ist, behandle es als required_ingredients
302
- if data.get('ingredients') and not required_ingredients:
303
- required_ingredients = data.get('ingredients', [])
304
-
305
- max_ingredients = data.get('max_ingredients', 7)
306
- max_retries = data.get('max_retries', 5)
307
-
308
- if not required_ingredients and not available_ingredients:
309
- return json.dumps({"error": "Keine Zutaten angegeben"})
310
 
 
311
  # Optimale Zutaten finden
312
  optimized_ingredients = find_best_ingredients(
313
  required_ingredients,
@@ -318,15 +296,36 @@ def flutter_api_generate_recipe(ingredients_data):
318
  # Rezept mit optimierten Zutaten generieren
319
  recipe = generate_recipe_with_t5(optimized_ingredients, max_retries)
320
 
321
- # Für die Flutter-App formatieren - strukturiertes Format
322
  result = {
323
  'title': recipe['title'],
324
  'ingredients': recipe['ingredients'],
325
  'directions': recipe['directions'],
326
  'used_ingredients': optimized_ingredients
327
  }
 
328
 
329
- return json.dumps(result)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
330
 
331
  except Exception as e:
332
  return json.dumps({"error": f"Fehler bei der Rezeptgenerierung: {str(e)}"})
@@ -334,34 +333,17 @@ def flutter_api_generate_recipe(ingredients_data):
334
  def gradio_ui_generate_recipe(required_ingredients_text, available_ingredients_text, max_ingredients_val, max_retries_val):
335
  """Gradio UI Funktion für die Web-Oberfläche"""
336
  try:
337
- # Text-Eingaben parsen
338
  required_ingredients = [ing.strip() for ing in required_ingredients_text.split(',') if ing.strip()]
339
  available_ingredients = [ing.strip() for ing in available_ingredients_text.split(',') if ing.strip()]
340
 
341
- # Erstelle ein Dictionary im Format der Flutter API
342
- data_for_flutter_api = {
343
- 'required_ingredients': required_ingredients,
344
- 'available_ingredients': available_ingredients,
345
- 'max_ingredients': max_ingredients_val, # Verwende den Parameter aus dem Slider
346
- 'max_retries': max_retries_val # Verwende den Parameter aus dem Slider
347
- }
348
-
349
- # --- WICHTIG: Wandle das Python-Dictionary in einen JSON-String um,
350
- # da flutter_api_generate_recipe intern dieses Format erwartet,
351
- # wenn es über einen Gradio-Input aufgerufen wird, der einen String liefert.
352
- # Dies simuliert den JSON-String, den die Flutter-App senden würde.
353
- data_json_string = json.dumps(data_for_flutter_api)
354
- # ---------------------------------------------------------------------
355
-
356
- # Verwende dieselbe Funktion wie die Flutter API
357
- result_json = flutter_api_generate_recipe(data_json_string) # <-- Hier die Änderung
358
-
359
- result = json.loads(result_json)
360
 
361
  if 'error' in result:
362
  return result['error'], "", "", ""
363
 
364
- # Für die Gradio-Anzeige formatieren
365
  ingredients_list = '\n'.join([f"• {ing}" for ing in result['ingredients']])
366
  directions_list = '\n'.join([f"{i+1}. {dir}" for i, dir in enumerate(result['directions'])])
367
  used_ingredients = ', '.join(result['used_ingredients'])
@@ -374,7 +356,6 @@ def gradio_ui_generate_recipe(required_ingredients_text, available_ingredients_t
374
  )
375
 
376
  except Exception as e:
377
- # Fehlermeldung für die Gradio UI
378
  return f"Fehler: {str(e)}", "", "", ""
379
 
380
  # Erstelle die Gradio Oberfläche
@@ -395,7 +376,6 @@ with gr.Blocks(title="AI Rezept Generator") as demo:
395
  placeholder="Knoblauch, Tomate, Pfeffer, Kräuter",
396
  lines=2
397
  )
398
- # Die Parameter-Namen für Slider müssen mit den Argumenten der Gradio UI Funktion übereinstimmen
399
  max_ing = gr.Slider(3, 10, value=7, step=1, label="Maximale Zutaten")
400
  max_retries = gr.Slider(1, 10, value=5, step=1, label="Max. Wiederholungsversuche")
401
 
@@ -409,13 +389,13 @@ with gr.Blocks(title="AI Rezept Generator") as demo:
409
 
410
  generate_btn.click(
411
  fn=gradio_ui_generate_recipe,
412
- inputs=[required_ing, available_ing, max_ing, max_retries], # Hier die Slider-Komponenten übergeben
413
  outputs=[title_output, ingredients_output, directions_output, used_ingredients_output]
414
  )
415
 
416
  with gr.Tab("API-Test"):
417
- gr.Markdown("### Teste die Flutter API")
418
- gr.Markdown("Dieser Tab verwendet dieselbe Funktion, die Flutter-Apps über die API aufrufen werden.")
419
 
420
  api_input = gr.Textbox(
421
  label="JSON-Eingabe (Flutter API-Format)",
@@ -425,11 +405,12 @@ with gr.Blocks(title="AI Rezept Generator") as demo:
425
  api_output = gr.Textbox(label="JSON-Ausgabe", lines=15, interactive=False)
426
  api_test_btn = gr.Button("API testen", variant="secondary")
427
 
 
428
  api_test_btn.click(
429
  fn=flutter_api_generate_recipe,
430
  inputs=[api_input],
431
  outputs=[api_output],
432
- api_name="generate_recipe_for_flutter" # <-- Dies ist der von Flutter verwendete API-Name
433
  )
434
 
435
  gr.Examples(
@@ -440,5 +421,46 @@ with gr.Blocks(title="AI Rezept Generator") as demo:
440
  inputs=[api_input]
441
  )
442
 
443
- if __name__ == "__main__":
444
- demo.launch()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4
  import numpy as np
5
  import random
6
  import json
7
+ from fastapi import FastAPI, Request
8
+ from fastapi.responses import JSONResponse
9
+ from pydantic import BaseModel
10
 
11
  # Lade RecipeBERT Modell (für semantische Zutat-Kombination)
12
  bert_model_name = "alexdseo/RecipeBERT"
 
92
  def find_best_ingredients(required_ingredients, available_ingredients, max_ingredients=6, avg_weight=0.6):
93
  """
94
  Findet die besten Zutaten basierend auf RecipeBERT Embeddings.
 
 
 
 
 
 
 
 
 
95
  """
96
  # Stelle sicher, dass keine Duplikate in den Listen sind
97
  required_ingredients = list(set(required_ingredients))
 
276
  "directions": ["Fehler beim Generieren der Rezeptanweisungen"]
277
  }
278
 
279
+ # Diese Funktion wird von der Gradio-UI und der neuen FastAPI-Route aufgerufen.
280
+ def process_recipe_request_logic(required_ingredients, available_ingredients, max_ingredients, max_retries):
281
  """
282
+ Kernlogik zur Verarbeitung einer Rezeptgenerierungsanfrage.
283
+ Ausgelagert, um von verschiedenen Endpunkten aufgerufen zu werden.
284
  """
285
+ if not required_ingredients and not available_ingredients:
286
+ return {"error": "Keine Zutaten angegeben"}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
287
 
288
+ try:
289
  # Optimale Zutaten finden
290
  optimized_ingredients = find_best_ingredients(
291
  required_ingredients,
 
296
  # Rezept mit optimierten Zutaten generieren
297
  recipe = generate_recipe_with_t5(optimized_ingredients, max_retries)
298
 
299
+ # Ergebnis formatieren
300
  result = {
301
  'title': recipe['title'],
302
  'ingredients': recipe['ingredients'],
303
  'directions': recipe['directions'],
304
  'used_ingredients': optimized_ingredients
305
  }
306
+ return result
307
 
308
+ except Exception as e:
309
+ return {"error": f"Fehler bei der Rezeptgenerierung: {str(e)}"}
310
+
311
+ def flutter_api_generate_recipe(ingredients_data):
312
+ """
313
+ Flutter-freundliche API-Funktion für den Gradio-API-Test-Tab.
314
+ Verarbeitet JSON-String-Eingabe und gibt JSON-String-Ausgabe zurück.
315
+ """
316
+ try:
317
+ data = json.loads(ingredients_data) # Muss ein JSON-String sein
318
+
319
+ required_ingredients = data.get('required_ingredients', [])
320
+ available_ingredients = data.get('available_ingredients', [])
321
+ max_ingredients = data.get('max_ingredients', 7)
322
+ max_retries = data.get('max_retries', 5)
323
+
324
+ # Rufe die Kernlogik auf
325
+ result_dict = process_recipe_request_logic(
326
+ required_ingredients, available_ingredients, max_ingredients, max_retries
327
+ )
328
+ return json.dumps(result_dict) # Gibt einen JSON-String zurück
329
 
330
  except Exception as e:
331
  return json.dumps({"error": f"Fehler bei der Rezeptgenerierung: {str(e)}"})
 
333
  def gradio_ui_generate_recipe(required_ingredients_text, available_ingredients_text, max_ingredients_val, max_retries_val):
334
  """Gradio UI Funktion für die Web-Oberfläche"""
335
  try:
 
336
  required_ingredients = [ing.strip() for ing in required_ingredients_text.split(',') if ing.strip()]
337
  available_ingredients = [ing.strip() for ing in available_ingredients_text.split(',') if ing.strip()]
338
 
339
+ # Rufe die Kernlogik auf
340
+ result = process_recipe_request_logic(
341
+ required_ingredients, available_ingredients, max_ingredients_val, max_retries_val
342
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
343
 
344
  if 'error' in result:
345
  return result['error'], "", "", ""
346
 
 
347
  ingredients_list = '\n'.join([f"• {ing}" for ing in result['ingredients']])
348
  directions_list = '\n'.join([f"{i+1}. {dir}" for i, dir in enumerate(result['directions'])])
349
  used_ingredients = ', '.join(result['used_ingredients'])
 
356
  )
357
 
358
  except Exception as e:
 
359
  return f"Fehler: {str(e)}", "", "", ""
360
 
361
  # Erstelle die Gradio Oberfläche
 
376
  placeholder="Knoblauch, Tomate, Pfeffer, Kräuter",
377
  lines=2
378
  )
 
379
  max_ing = gr.Slider(3, 10, value=7, step=1, label="Maximale Zutaten")
380
  max_retries = gr.Slider(1, 10, value=5, step=1, label="Max. Wiederholungsversuche")
381
 
 
389
 
390
  generate_btn.click(
391
  fn=gradio_ui_generate_recipe,
392
+ inputs=[required_ing, available_ing, max_ing, max_retries],
393
  outputs=[title_output, ingredients_output, directions_output, used_ingredients_output]
394
  )
395
 
396
  with gr.Tab("API-Test"):
397
+ gr.Markdown("### Teste die Gradio API (für interne Tests)")
398
+ gr.Markdown("Dieser Tab verwendet die interne Gradio API für Testzwecke.")
399
 
400
  api_input = gr.Textbox(
401
  label="JSON-Eingabe (Flutter API-Format)",
 
405
  api_output = gr.Textbox(label="JSON-Ausgabe", lines=15, interactive=False)
406
  api_test_btn = gr.Button("API testen", variant="secondary")
407
 
408
+ # Hier wird die Funktion weiterhin für den Gradio-eigenen API-Test-Tab verwendet.
409
  api_test_btn.click(
410
  fn=flutter_api_generate_recipe,
411
  inputs=[api_input],
412
  outputs=[api_output],
413
+ api_name="generate_recipe_for_flutter" # Gradio-interner API-Name
414
  )
415
 
416
  gr.Examples(
 
421
  inputs=[api_input]
422
  )
423
 
424
+ # --- FastAPI-Integration ---
425
+ app = FastAPI()
426
+
427
+ class RecipeRequest(BaseModel):
428
+ required_ingredients: list[str] = []
429
+ available_ingredients: list[str] = []
430
+ max_ingredients: int = 7
431
+ max_retries: int = 5
432
+
433
+ @app.post("/api/generate_recipe_rest")
434
+ async def generate_recipe_rest_api(request_data: RecipeRequest):
435
+ """
436
+ Standard-REST-API-Endpunkt für die Flutter-App.
437
+ Nimmt direkt JSON-Daten an und gibt direkt JSON zurück.
438
+ """
439
+ required_ingredients = request_data.required_ingredients
440
+ available_ingredients = request_data.available_ingredients
441
+ max_ingredients = request_data.max_ingredients
442
+ max_retries = request_data.max_retries
443
+
444
+ # Abwärtskompatibilität, falls 'ingredients' statt 'required_ingredients' gesendet wird
445
+ # Dies ist in der FastAPI-Pydantic-Modelldefinition nicht direkt abbildbar,
446
+ # aber du könntest es manuell hinzufügen, falls nötig, wenn das Pydantic-Modell flexibler wäre.
447
+ # Für den Einfachheit halber gehen wir davon aus, dass Flutter die korrekten Felder sendet.
448
+
449
+ result_dict = process_recipe_request_logic(
450
+ required_ingredients, available_ingredients, max_ingredients, max_retries
451
+ )
452
+
453
+ return JSONResponse(content=result_dict)
454
+
455
+ # Gradio-App als Sub-App in die FastAPI-App mounten
456
+ # Dies ist der Standardweg, um Gradio in eine FastAPI-Anwendung einzubetten.
457
+ # Der Gradio-Teil wird dann unter dem Wurzelpfad '/'.
458
+ app = gr.mount_gradio_app(app, demo, path="/") # Gradio unter dem Wurzelpfad mounten
459
+
460
+ # Wenn du deine App lokal ausführst, kannst du FastAPI mit Uvicorn starten:
461
+ # if __name__ == "__main__":
462
+ # import uvicorn
463
+ # uvicorn.run(app, host="0.0.0.0", port=8000)
464
+
465
+ # Für Hugging Face Spaces ist der if __name__ == "__main__": Block nicht nötig,
466
+ # da Spaces Uvicorn automatisch startet und die "app"-Variable sucht.