Hammad712 commited on
Commit
b02ebf7
·
verified ·
1 Parent(s): a488b5b

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +339 -189
app.py CHANGED
@@ -1,198 +1,348 @@
1
- from fastapi import FastAPI, UploadFile, HTTPException
2
- from fastapi.responses import JSONResponse
3
- from pydantic import BaseModel, Field
 
 
 
 
 
 
 
4
  import base64
5
  from groq import Groq
6
- import shutil
7
- import os
8
 
9
- app = FastAPI()
 
 
 
 
10
 
11
- # Initialize Groq API with your API key
12
- client = Groq(api_key='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')
 
13
 
14
- # Pydantic models for input validation
15
- class RecipeRequest(BaseModel):
16
- meal_name: str = Field(..., description="Name of the meal to generate a recipe for")
17
-
18
- class IngredientAlternativesRequest(BaseModel):
19
- ingredients: list[str] = Field(..., description="List of ingredients to suggest alternatives for")
20
- dietary_restrictions: str = Field(..., description="Dietary restrictions, if any")
21
- allergies: list[str] = Field(..., description="List of allergies to consider")
22
-
23
-
24
- # Helper function to encode the image to base64
25
- def encode_image(image_path: str) -> str:
26
- try:
27
- with open(image_path, "rb") as image_file:
28
- return base64.b64encode(image_file.read()).decode('utf-8')
29
- except FileNotFoundError:
30
- raise HTTPException(status_code=404, detail="Image not found.")
31
- except Exception as e:
32
- raise HTTPException(status_code=500, detail=f"Error encoding image: {str(e)}")
33
-
34
-
35
- # POST /infer (Image inference route)
36
- @app.post("/infer")
37
- async def infer_image(file: UploadFile):
38
- """
39
- Perform inference on an uploaded image and return the result.
40
- """
41
- try:
42
- # Save the uploaded image to a temporary file
43
- temp_image_path = f"temp_{file.filename}"
44
- with open(temp_image_path, "wb") as buffer:
45
- shutil.copyfileobj(file.file, buffer)
46
-
47
- # Encode the image to base64
48
- base64_image = encode_image(temp_image_path)
49
-
50
- # Prepare the query and image data for Groq API
51
- chat_completion = client.chat.completions.create(
52
- messages=[
53
- {
54
- "role": "user",
55
- "content": [
56
- {"type": "text", "text": "What are the ingredients used in this dish?. Do not add any explanation just write the names of the ingredients. Write the name of the dish and then write the ingredients used"},
57
- {
58
- "type": "image_url",
59
- "image_url": {
60
- "url": f"data:image/jpeg;base64,{base64_image}",
61
- },
62
- },
63
- ],
64
- }
65
- ],
66
- model="llama-3.2-11b-vision-preview"
67
- )
68
-
69
- # Clean up the temporary image file
70
- os.remove(temp_image_path)
71
-
72
- # Get the response from the API and return the result
73
- result = chat_completion.choices[0].message.content
74
- return JSONResponse(status_code=200, content={"result": result})
75
-
76
- except Exception as e:
77
- raise HTTPException(status_code=500, detail=f"Error during inference: {str(e)}")
78
-
79
-
80
- # POST /recipes (Recipe generation route)
81
- @app.post("/recipes")
82
- async def generate_recipe(request: RecipeRequest):
83
- """
84
- Generate a recipe based on the meal name.
85
- """
86
- try:
87
- # Generate the recipe based on the meal name
88
- recipe = Generate_recipe(request.meal_name)
89
- return JSONResponse(status_code=200, content={"recipe": recipe})
90
- except Exception as e:
91
- raise HTTPException(status_code=500, detail=f"Error generating recipe: {str(e)}")
92
-
93
-
94
- # Helper function to generate a recipe based on meal name
95
- def Generate_recipe(meal_name: str) -> str:
96
- prompt = f"""
97
- You are a recipe-creating agent. Your task is to create a recipe based on the meal name provided by the user.
98
- The recipe should be detailed and include the following information:
99
-
100
- - A list of ingredients required for the meal.
101
- - Step-by-step cooking instructions.
102
- - Approximate preparation and cooking time.
103
- - Serving suggestions or tips for best results.
104
-
105
- Please process the user's meal name and create the appropriate recipe.
106
-
107
- Meal Name: {meal_name}
108
- """
109
-
110
- # Create a chat completion request using the llama-3.1-70b-versatile model
111
- completion = client.chat.completions.create(
112
- model="llama-3.1-70b-versatile",
113
  messages=[
114
- {"role": "system", "content": prompt},
115
- {"role": "user", "content": f"Meal Name: {meal_name}"}
 
 
 
 
 
 
 
 
116
  ],
117
- temperature=1,
118
- max_tokens=2048,
119
- top_p=1,
120
- stream=True,
121
- stop=None,
 
122
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
123
 
124
- # Stream and collect the response from the model
125
- recipe_result = ""
126
- for chunk in completion:
127
- recipe_result += chunk.choices[0].delta.content or ""
128
-
129
- return recipe_result
130
-
131
-
132
- # POST /ingredients/alternatives (Ingredient alternatives route)
133
- @app.post("/ingredients/alternatives")
134
- async def suggest_ingredient_alternatives(request: IngredientAlternativesRequest):
135
- """
136
- Suggest alternatives for specific ingredients based on dietary restrictions and allergies.
137
- """
138
- try:
139
- # Generate ingredient alternatives
140
- alternatives = Suggest_ingredient_alternatives(
141
- request.ingredients,
142
- request.dietary_restrictions,
143
- request.allergies
144
- )
145
- return JSONResponse(status_code=200, content={"alternatives": alternatives})
146
- except Exception as e:
147
- raise HTTPException(status_code=500, detail=f"Error suggesting ingredient alternatives: {str(e)}")
148
-
149
-
150
- # Helper function to suggest ingredient alternatives based on user input
151
- def Suggest_ingredient_alternatives(ingredients, dietary_restrictions, allergies):
152
- alternative_suggestions = ""
153
-
154
- # Iterate over each ingredient to provide an alternative
155
- for ingredient in ingredients:
156
- prompt = f"""
157
- You are an ingredient substitution agent. Your task is to suggest alternatives for specific ingredients based on the user's input, particularly for biryani recipes.
158
-
159
- Please take the following into account:
160
- - If the user has dietary restrictions, suggest substitutes that align with their needs (e.g., vegan, gluten-free, etc.).
161
- - Consider the following allergies and suggest safe alternatives: {', '.join(allergies)}.
162
- - The alternative should be commonly available, and you should provide options if multiple substitutes exist.
163
- - Explain how the suggested alternative will impact the recipe, in terms of taste, texture, or cooking time.
164
-
165
- Ingredient: {ingredient}
166
- Dietary Restrictions: {dietary_restrictions}
167
- Allergies: {', '.join(allergies)}
168
- """
169
-
170
- # Create a chat completion request using the llama-3.1-70b-versatile model
171
- completion = client.chat.completions.create(
172
- model="llama-3.1-70b-versatile",
173
- messages=[
174
- {"role": "system", "content": prompt},
175
- {"role": "user", "content": f"Ingredient: {ingredient}, Dietary Restrictions: {dietary_restrictions}, Allergies: {', '.join(allergies)}"}
176
- ],
177
- temperature=1,
178
- max_tokens=1024,
179
- top_p=1,
180
- stream=True,
181
- stop=None,
182
- )
183
-
184
- # Collect the response for each ingredient
185
- suggestion = ""
186
- for chunk in completion:
187
- suggestion += chunk.choices[0].delta.content or ""
188
-
189
- # Append the suggestion to the final result
190
- alternative_suggestions += f"Substitute suggestions for {ingredient}:\n{suggestion}\n{'-'*50}\n"
191
-
192
- return alternative_suggestions
193
-
194
-
195
- # Root endpoint to check if the API is running
196
- @app.get("/")
197
- async def root():
198
- return {"message": "API is running!"}
 
1
+ from fastapi import FastAPI, HTTPException
2
+ from pydantic import BaseModel
3
+ from pymongo import MongoClient
4
+ from urllib.parse import quote_plus
5
+ import uuid
6
+ from typing import List, Optional
7
+ import json
8
+ from fastapi import FastAPI, File, UploadFile, HTTPException
9
+ from fastapi.responses import HTMLResponse
10
+ import os
11
  import base64
12
  from groq import Groq
13
+ # Initialize Groq client
14
+ client = Groq(api_key='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')
15
 
16
+ # MongoDB connection setup
17
+ def get_mongo_client():
18
+ password = quote_plus("momimaad@123") # Change this to your MongoDB password
19
+ mongo_uri = f"mongodb+srv://hammad:{password}@cluster0.2a9yu.mongodb.net/"
20
+ return MongoClient(mongo_uri)
21
 
22
+ db_client = get_mongo_client()
23
+ db = db_client["recipe"]
24
+ user_collection = db["user_info"]
25
 
26
+ # Pydantic models for user data
27
+ class User(BaseModel):
28
+ first_name: str
29
+ last_name: str
30
+ email: str
31
+ password: str
32
+
33
+ class UserData(BaseModel):
34
+ email: str
35
+ password: str
36
+
37
+ class UserToken(BaseModel):
38
+ token: str
39
+
40
+ class RecipeData(BaseModel):
41
+ name: str
42
+
43
+ class AltrecipeData(BaseModel):
44
+ recipe_name: str
45
+ dietary_restrictions: str
46
+ allergies: List
47
+
48
+ class Ingredient(BaseModel):
49
+ name: str
50
+ quantity: str
51
+
52
+
53
+ class Recipe(BaseModel):
54
+ recipe_name: str
55
+ ingredients: List[Ingredient]
56
+ directions: List[str]
57
+
58
+
59
+ # Data model for LLM to generate
60
+ class Alternative_Ingredient(BaseModel):
61
+ name: str
62
+ quantity: str
63
+
64
+
65
+ class Alternative_Recipe(BaseModel):
66
+ recipe_name: str
67
+ alternative_ingredients: List[Alternative_Ingredient]
68
+ alternative_directions: List[str]
69
+
70
+ def get_recipe(recipe_name: str) -> Recipe:
71
+ chat_completion = client.chat.completions.create(
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
72
  messages=[
73
+ {
74
+ "role": "system",
75
+ "content": f"""Your are an expert agent to generate a recipes with proper and corrected ingredients and direction. Your directions should be concise and to the point and dont explain any irrelevant text.
76
+ You are a recipe database that outputs recipes in JSON.\n
77
+ The JSON object must use the schema: {json.dumps(Recipe.model_json_schema(), indent=2)}""",
78
+ },
79
+ {
80
+ "role": "user",
81
+ "content": f"Fetch a recipe for {recipe_name}",
82
+ },
83
  ],
84
+ model="llama3-8b-8192",
85
+ temperature=0,
86
+ # Streaming is not supported in JSON mode
87
+ stream=False,
88
+ # Enable JSON mode by setting the response format
89
+ response_format={"type": "json_object"},
90
  )
91
+ return Recipe.model_validate_json(chat_completion.choices[0].message.content)
92
+
93
+
94
+
95
+
96
+
97
+ def Suggest_ingredient_alternatives(recipe_name: str, dietary_restrictions: str, allergies: List) -> Alternative_Recipe:
98
+ chat_completion = client.chat.completions.create(
99
+ messages=[
100
+ {
101
+ "role": "system",
102
+ "content": f"""
103
+ You are an expert agent to suggest alternatives for specific allergies ingredients for the provided recipe {recipe_name}.
104
+
105
+ Please take the following into account:
106
+ - If the user has dietary restrictions, suggest substitutes that align with their needs (e.g., vegan, gluten-free, etc.) in alternative_directions and your alternative_directions should be concise and to the point.
107
+ -In ingredient you will recommend the safe ingredient for avoid any allergy and dietary restriction.
108
+ - Consider the following allergies {allergies} and recommend the safe ingredient to avoid this allergies.
109
+
110
+ recipe_name: {recipe_name}
111
+ Dietary Restrictions: {dietary_restrictions}
112
+ Allergies: {', '.join(allergies)}
113
+
114
+ You are a recipe database that outputs alternative recipes to avoid allergy and dietary_restrictions in JSON.\n
115
+ The JSON object must use the schema: {json.dumps(Alternative_Recipe.model_json_schema(), indent=2)}""",
116
+ },
117
+ {
118
+ "role": "user",
119
+ "content": f"""Fetch a alternative recipe for recipe_name: {recipe_name}
120
+ Dietary Restrictions: {dietary_restrictions}
121
+ Allergies: {', '.join(allergies)}""",
122
+ },
123
+ ],
124
+ model="llama3-8b-8192",
125
+ temperature=0,
126
+ # Streaming is not supported in JSON mode
127
+ stream=False,
128
+ # Enable JSON mode by setting the response format
129
+ response_format={"type": "json_object"},
130
+ )
131
+ return Alternative_Recipe.model_validate_json(chat_completion.choices[0].message.content)
132
+
133
+
134
+ def get_status(content):
135
+ chat_completion = client.chat.completions.create(
136
+ messages=[
137
+ {
138
+ "role": "system",
139
+ "content": """Your are an expert agent to status yes if any kind of recipe dish present in explanation other no
140
+
141
+ Json output format:
142
+ {'status':return'yes' if any dish present in expalantion return 'no' if not dish present in image}
143
+ """,
144
+ },
145
+ {
146
+ "role": "user",
147
+ "content": f"Image Explanation {content}",
148
+ },
149
+ ],
150
+ model="llama3-groq-70b-8192-tool-use-preview",
151
+ temperature=0,
152
+ # Streaming is not supported in JSON mode
153
+ stream=False,
154
+ # Enable JSON mode by setting the response format
155
+ response_format={"type": "json_object"},
156
+ )
157
+ return chat_completion.choices[0].message.content
158
+
159
+ # Function to encode the image
160
+ def encode_image(image_path):
161
+ with open(image_path, "rb") as image_file:
162
+ return base64.b64encode(image_file.read()).decode('utf-8')
163
+
164
+ def explain_image(base64_image):
165
+ text_query = '''
166
+ explain the image
167
+ '''
168
+ chat_completion = client.chat.completions.create(
169
+ messages=[
170
+ {
171
+ "role": "user",
172
+ "content": [
173
+ {"type": "text", "text": text_query},
174
+ {
175
+ "type": "image_url",
176
+ "image_url": {
177
+ "url": f"data:image/jpeg;base64,{base64_image}",
178
+ },
179
+ },
180
+ ],
181
+
182
+ }
183
+ ],
184
+ model="llama-3.2-11b-vision-preview")
185
+ return chat_completion.choices[0].message.content
186
+
187
+
188
+ class get_recipe_name(BaseModel):
189
+ recipe_name: List[str]
190
+ ingredients: List[List[str]]
191
+
192
+
193
+ def generate_recipe_name(base64_image):
194
+ # Example of how the JSON should look to make it clearer
195
+ example_json_structure = {
196
+ "recipe_name": ["Chicken Karahi", "Pasta Alfredo"],
197
+ "ingredients": [
198
+ ["chicken", "tomatoes", "onion", "ginger", "garlic", "red chili pepper", "oil"],
199
+ ["pasta", "cream", "butter", "parmesan cheese", "garlic", "salt", "pepper"]
200
+ ]
201
+ }
202
+
203
+ # Generating the query prompt to ask for ingredients
204
+ text_query = f'''What are the ingredients used in these dishes? Do not add any explanation, just write the names of the ingredients in proper JSON according to the following format:
205
+ The JSON object must follow this schema:
206
+ {json.dumps(get_recipe_name.model_json_schema(), indent=2)}
207
+
208
+ Example format:
209
+ {json.dumps(example_json_structure, indent=2)}
210
+
211
+ Write the name of the dish and then write the ingredients used for each recipe.
212
+ '''
213
+
214
+ chat_completion = client.chat.completions.create(
215
+ messages=[
216
+ {
217
+ "role": "user",
218
+ "content": [
219
+ {"type": "text", "text": text_query},
220
+ {
221
+ "type": "image_url",
222
+ "image_url": {
223
+ "url": f"data:image/jpeg;base64,{base64_image}",
224
+ },
225
+ },
226
+ ],
227
+
228
+ }
229
+ ],
230
+ response_format={"type": "json_object"},
231
+ model="llama-3.2-11b-vision-preview")
232
+ return json.loads(chat_completion.choices[0].message.content)
233
+
234
+
235
+
236
+
237
+
238
+ app = FastAPI()
239
+
240
+
241
+
242
+
243
+ @app.post("/get_recipe/{token}")
244
+ async def get_recipe_response(token: str, recipe_user: RecipeData):
245
+ user = user_collection.find_one({"token": token})
246
+ if not user:
247
+ raise HTTPException(status_code=401, detail="Invalid token")
248
+
249
+ # Find user by email
250
+ recipe_name = recipe_user.name
251
+ response = get_recipe(recipe_name)
252
+ return {
253
+ "Response": response
254
+ }
255
+
256
+ @app.post("/get_recipe_alternative/{token}")
257
+ async def get_alternative_recipe_response(token: str, altrecipe_user: AltrecipeData):
258
+ user = user_collection.find_one({"token": token})
259
+ if not user:
260
+ raise HTTPException(status_code=401, detail="Invalid token")
261
+
262
+ response = Suggest_ingredient_alternatives(altrecipe_user.recipe_name, altrecipe_user.dietary_restrictions, altrecipe_user.allergies)
263
+ return {
264
+ "Response": response
265
+ }
266
+
267
+
268
+ # Directory to save uploaded images
269
+ UPLOAD_DIR = "uploads"
270
+
271
+ # Ensure the upload directory exists
272
+ os.makedirs(UPLOAD_DIR, exist_ok=True)
273
+
274
+
275
+ # Endpoint to upload an image
276
+ @app.post("/upload-image/{token}")
277
+ async def upload_image(token: str, file: UploadFile = File(...)):
278
+ user = user_collection.find_one({"token": token})
279
+ if not user:
280
+ raise HTTPException(status_code=401, detail="Invalid token")
281
+
282
+ # Validate the file type
283
+ if not file.filename.lower().endswith(('.png', '.jpg', '.jpeg')):
284
+ raise HTTPException(status_code=400, detail="Invalid file type. Only PNG, JPG, and JPEG are allowed.")
285
+
286
+ # Create a file path for saving the uploaded file
287
+ file_path = os.path.join(UPLOAD_DIR, file.filename)
288
+
289
+ # Save the file
290
+ with open(file_path, "wb") as buffer:
291
+ buffer.write(await file.read())
292
+
293
+ # Getting the base64 string
294
+ base64_image = encode_image(file_path)
295
+
296
+ status = get_status(explain_image(base64_image))
297
+ status_json = json.loads(status)
298
+ if status_json['status'].lower() == 'no':
299
+ response = {"recipe_name": [], 'ingredients': []}
300
+ else:
301
+ response = generate_recipe_name(base64_image)
302
+
303
+ return {
304
+ "Response": response
305
+ }
306
+
307
+
308
+ # Endpoint to register a new user
309
+ @app.post("/register")
310
+ async def register_user(user: User):
311
+ # Check if user already exists
312
+ existing_user = user_collection.find_one({"email": user.email})
313
+ if existing_user:
314
+ raise HTTPException(status_code=400, detail="Email already registered")
315
+
316
+ # Create user data
317
+ user_data = {
318
+ "first_name": user.first_name,
319
+ "last_name": user.last_name,
320
+ "email": user.email,
321
+ "password": user.password, # Store plaintext password (not recommended in production)
322
+ }
323
+
324
+ # Insert the user data into the user_info collection
325
+ result = user_collection.insert_one(user_data)
326
+ return {"msg": "User registered successfully", "user_id": str(result.inserted_id)}
327
+
328
+ # Endpoint to check user credentials and generate a token
329
+ @app.post("/get_token")
330
+ async def check_credentials(user: UserData):
331
+ # Find user by email
332
+ existing_user = user_collection.find_one({"email": user.email})
333
+
334
+ # Check if user exists and password matches
335
+ if not existing_user or existing_user["password"] != user.password:
336
+ raise HTTPException(status_code=401, detail="Invalid email or password")
337
 
338
+ # Generate a UUID token
339
+ token = str(uuid.uuid4())
340
+
341
+ # Update the user document with the token
342
+ user_collection.update_one({"email": user.email}, {"$set": {"token": token}})
343
+
344
+ return {
345
+ "first_name": existing_user["first_name"],
346
+ "last_name": existing_user["last_name"],
347
+ "token": token,
348
+ }