TimInf commited on
Commit
eb874d4
·
verified ·
1 Parent(s): a0c619c

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +48 -28
app.py CHANGED
@@ -1,4 +1,4 @@
1
- from transformers import AutoTokenizer, AutoModel # Entfernt: FlaxAutoModelForSeq2SeqLM
2
  import torch
3
  import numpy as np
4
  import random
@@ -16,7 +16,7 @@ bert_model.eval() # Setze das Modell in den Evaluationsmodus
16
  # T5-Modell und -Logik KOMPLETT ENTFERNT für diesen Schritt
17
  # special_tokens und tokens_map sind nicht mehr relevant, bleiben aber als Kommentar
18
 
19
- # --- RecipeBERT-spezifische Funktionen (die jetzt die Kernlogik sind) ---
20
  def get_embedding(text):
21
  """Berechnet das Embedding für einen Text mit Mean Pooling über alle Tokens."""
22
  inputs = bert_tokenizer(text, return_tensors="pt", truncation=True, padding=True)
@@ -29,6 +29,11 @@ def get_embedding(text):
29
  sum_mask = torch.clamp(input_mask_expanded.sum(1), min=1e-9)
30
  return (sum_embeddings / sum_mask).squeeze(0)
31
 
 
 
 
 
 
32
  def get_cosine_similarity(vec1, vec2):
33
  """Berechnet die Cosinus-Ähnlichkeit zwischen zwei Vektoren."""
34
  if torch.is_tensor(vec1): vec1 = vec1.detach().numpy()
@@ -42,35 +47,48 @@ def get_cosine_similarity(vec1, vec2):
42
  return dot_product / (norm_a * norm_b)
43
 
44
 
45
- # find_best_ingredients (modifiziert, um KEINE Embeddings für T5-ähnliche Auswahl zu nutzen,
46
- # sondern nur grundlegende Zutatenbearbeitung und Optionalen Test für RecipeBERT-Laden)
47
- def find_best_ingredients(required_ingredients, available_ingredients, max_ingredients=6, avg_weight=0.6):
48
  """
49
- Für diesen Test: Gibt einfach die benötigten Zutaten plus ein paar zufällige verfügbare Zutaten zurück.
50
- Die semantische Auswahl von RecipeBERT ist hier nicht aktiv (da T5-Generierung fehlt).
51
  """
52
  required_ingredients = list(set(required_ingredients))
53
  available_ingredients = list(set([i for i in available_ingredients if i not in required_ingredients]))
54
 
55
  final_ingredients = required_ingredients.copy()
56
- num_to_add = min(max_ingredients - len(final_ingredients), len(available_ingredients))
57
- if num_to_add > 0:
58
- final_ingredients.extend(random.sample(available_ingredients, num_to_add))
59
-
60
- # Optional: Ein kleiner Test-Print, ob RecipeBERT erfolgreich geladen wurde
61
- try:
62
  if final_ingredients:
63
- # Versuche ein Embedding für die erste Zutat zu generieren
64
- test_embedding = get_embedding(final_ingredients[0])
65
- print(f"INFO: Successfully generated embedding for '{final_ingredients[0]}'. RecipeBERT is loaded.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
66
  else:
67
- print("INFO: No ingredients to test embedding with.")
68
- except Exception as e:
69
- print(f"ERROR: RecipeBERT embedding test failed: {e}")
 
 
 
 
70
 
71
- return final_ingredients
72
 
73
- # mock_generate_recipe (ersetzt generate_recipe_with_t5)
74
  def mock_generate_recipe(ingredients_list):
75
  """Generiert ein Mock-Rezept, da T5-Modell entfernt ist."""
76
  title = f"Einfaches Rezept mit {', '.join(ingredients_list[:3])}" if ingredients_list else "Einfaches Testrezept"
@@ -79,9 +97,10 @@ def mock_generate_recipe(ingredients_list):
79
  "ingredients": ingredients_list, # Die "generierten" Zutaten sind einfach die Eingabe
80
  "directions": [
81
  "Dies ist ein generierter Text von RecipeBERT (ohne T5).",
82
- "Fügen Sie Ihre Zutaten zusammen und kochen Sie es nach Belieben.",
83
- "Das Laden des RecipeBERT-Modells war erfolgreich!"
84
- ]
 
85
  }
86
 
87
 
@@ -93,6 +112,7 @@ def process_recipe_request_logic(required_ingredients, available_ingredients, ma
93
  if not required_ingredients and not available_ingredients:
94
  return {"error": "Keine Zutaten angegeben"}
95
  try:
 
96
  optimized_ingredients = find_best_ingredients(
97
  required_ingredients, available_ingredients, max_ingredients
98
  )
@@ -104,7 +124,7 @@ def process_recipe_request_logic(required_ingredients, available_ingredients, ma
104
  'title': recipe['title'],
105
  'ingredients': recipe['ingredients'],
106
  'directions': recipe['directions'],
107
- 'used_ingredients': optimized_ingredients
108
  }
109
  return result
110
  except Exception as e:
@@ -117,7 +137,7 @@ class RecipeRequest(BaseModel):
117
  required_ingredients: list[str] = []
118
  available_ingredients: list[str] = []
119
  max_ingredients: int = 7
120
- max_retries: int = 5
121
  ingredients: list[str] = [] # Für Abwärtskompatibilität
122
 
123
  @app.post("/generate_recipe") # Der API-Endpunkt für Flutter
@@ -130,12 +150,12 @@ async def generate_recipe_api(request_data: RecipeRequest):
130
  final_required_ingredients,
131
  request_data.available_ingredients,
132
  request_data.max_ingredients,
133
- request_data.max_retries
134
  )
135
  return JSONResponse(content=result_dict)
136
 
137
  @app.get("/")
138
  async def read_root():
139
- return {"message": "AI Recipe Generator API is running (RecipeBERT only)!"} # Angepasste Nachricht
140
 
141
  print("INFO: FastAPI application script finished execution and defined 'app' variable.")
 
1
+ from transformers import AutoTokenizer, AutoModel
2
  import torch
3
  import numpy as np
4
  import random
 
16
  # T5-Modell und -Logik KOMPLETT ENTFERNT für diesen Schritt
17
  # special_tokens und tokens_map sind nicht mehr relevant, bleiben aber als Kommentar
18
 
19
+ # --- RecipeBERT-spezifische Funktionen ---
20
  def get_embedding(text):
21
  """Berechnet das Embedding für einen Text mit Mean Pooling über alle Tokens."""
22
  inputs = bert_tokenizer(text, return_tensors="pt", truncation=True, padding=True)
 
29
  sum_mask = torch.clamp(input_mask_expanded.sum(1), min=1e-9)
30
  return (sum_embeddings / sum_mask).squeeze(0)
31
 
32
+ def average_embedding(embedding_list):
33
+ """Berechnet den Durchschnitt einer Liste von Embeddings."""
34
+ tensors = torch.stack(embedding_list) # embedding_list enthält hier direkt die Tensoren
35
+ return tensors.mean(dim=0)
36
+
37
  def get_cosine_similarity(vec1, vec2):
38
  """Berechnet die Cosinus-Ähnlichkeit zwischen zwei Vektoren."""
39
  if torch.is_tensor(vec1): vec1 = vec1.detach().numpy()
 
47
  return dot_product / (norm_a * norm_b)
48
 
49
 
50
+ # find_best_ingredients (modifiziert, um die ähnlichste Zutat mit RecipeBERT zu finden)
51
+ def find_best_ingredients(required_ingredients, available_ingredients, max_ingredients=6):
 
52
  """
53
+ Findet die besten Zutaten: Alle benötigten + EINE ähnlichste aus den verfügbaren Zutaten.
 
54
  """
55
  required_ingredients = list(set(required_ingredients))
56
  available_ingredients = list(set([i for i in available_ingredients if i not in required_ingredients]))
57
 
58
  final_ingredients = required_ingredients.copy()
59
+
60
+ # Nur wenn wir noch Platz haben und zusätzliche Zutaten verfügbar sind
61
+ if len(final_ingredients) < max_ingredients and len(available_ingredients) > 0:
 
 
 
62
  if final_ingredients:
63
+ # Berechne den Durchschnitts-Embedding der benötigten Zutaten
64
+ required_embeddings = [get_embedding(ing) for ing in required_ingredients]
65
+ avg_required_embedding = average_embedding(required_embeddings)
66
+
67
+ best_additional_ingredient = None
68
+ highest_similarity = -1.0
69
+
70
+ # Finde die ähnlichste Zutat aus den verfügbaren
71
+ for avail_ing in available_ingredients:
72
+ avail_embedding = get_embedding(avail_ing)
73
+ similarity = get_cosine_similarity(avg_required_embedding, avail_embedding)
74
+ if similarity > highest_similarity:
75
+ highest_similarity = similarity
76
+ best_additional_ingredient = avail_ing
77
+
78
+ if best_additional_ingredient:
79
+ final_ingredients.append(best_additional_ingredient)
80
+ print(f"INFO: Added '{best_additional_ingredient}' (similarity: {highest_similarity:.2f}) as most similar.")
81
  else:
82
+ # Wenn keine benötigten Zutaten, wähle zufällig eine aus den verfügbaren (wie zuvor)
83
+ random_ingredient = random.choice(available_ingredients)
84
+ final_ingredients.append(random_ingredient)
85
+ print(f"INFO: No required ingredients. Added random available ingredient: '{random_ingredient}'.")
86
+
87
+ # Begrenze auf max_ingredients, falls durch Zufall/ähnlichster Auswahl zu viele hinzugefügt wurden
88
+ return final_ingredients[:max_ingredients]
89
 
 
90
 
91
+ # mock_generate_recipe (bleibt gleich)
92
  def mock_generate_recipe(ingredients_list):
93
  """Generiert ein Mock-Rezept, da T5-Modell entfernt ist."""
94
  title = f"Einfaches Rezept mit {', '.join(ingredients_list[:3])}" if ingredients_list else "Einfaches Testrezept"
 
97
  "ingredients": ingredients_list, # Die "generierten" Zutaten sind einfach die Eingabe
98
  "directions": [
99
  "Dies ist ein generierter Text von RecipeBERT (ohne T5).",
100
+ "Das Laden des RecipeBERT-Modells war erfolgreich!",
101
+ f"Basierend auf deinen Eingaben wurde '{ingredients_list[-1]}' als ähnlichste Zutat hinzugefügt." if len(ingredients_list) > 1 else "Keine zusätzliche Zutat hinzugefügt."
102
+ ],
103
+ "used_ingredients": ingredients_list # In diesem Mock-Fall sind alle "used"
104
  }
105
 
106
 
 
112
  if not required_ingredients and not available_ingredients:
113
  return {"error": "Keine Zutaten angegeben"}
114
  try:
115
+ # Hier wird die neue find_best_ingredients verwendet
116
  optimized_ingredients = find_best_ingredients(
117
  required_ingredients, available_ingredients, max_ingredients
118
  )
 
124
  'title': recipe['title'],
125
  'ingredients': recipe['ingredients'],
126
  'directions': recipe['directions'],
127
+ 'used_ingredients': optimized_ingredients # Jetzt wirklich die vom find_best_ingredients
128
  }
129
  return result
130
  except Exception as e:
 
137
  required_ingredients: list[str] = []
138
  available_ingredients: list[str] = []
139
  max_ingredients: int = 7
140
+ max_retries: int = 5 # Wird hier nicht direkt genutzt, aber im Payload beibehalten
141
  ingredients: list[str] = [] # Für Abwärtskompatibilität
142
 
143
  @app.post("/generate_recipe") # Der API-Endpunkt für Flutter
 
150
  final_required_ingredients,
151
  request_data.available_ingredients,
152
  request_data.max_ingredients,
153
+ request_data.max_retries # max_retries wird nur an die Logik übergeben, aber nicht verwendet
154
  )
155
  return JSONResponse(content=result_dict)
156
 
157
  @app.get("/")
158
  async def read_root():
159
+ return {"message": "AI Recipe Generator API is running (RecipeBERT only, 1 similar ingredient)!"} # Angepasste Nachricht
160
 
161
  print("INFO: FastAPI application script finished execution and defined 'app' variable.")