Ritesh-hf commited on
Commit
a8375a0
·
2 Parent(s): 5a5ba32 a9428d6
Files changed (4) hide show
  1. Dockerfile +2 -2
  2. app.py +7 -66
  3. test-1.py +0 -439
  4. test.ipynb +0 -1186
Dockerfile CHANGED
@@ -1,7 +1,7 @@
1
  # Read the doc: https://huggingface.co/docs/hub/spaces-sdks-docker
2
  # you will also find guides on how best to write your Dockerfile
3
 
4
- FROM python:3.9
5
 
6
  # Install required system dependencies for OpenCV
7
  RUN apt-get update && apt-get install -y \
@@ -28,4 +28,4 @@ RUN pip install --no-cache-dir --upgrade -r requirements.txt
28
  COPY --chown=user . /app
29
 
30
  # Set the command to start the application
31
- CMD ["gunicorn", "-b", "0.0.0.0:7860", "-k", "eventlet", "app:app"]
 
1
  # Read the doc: https://huggingface.co/docs/hub/spaces-sdks-docker
2
  # you will also find guides on how best to write your Dockerfile
3
 
4
+ FROM python:3.10
5
 
6
  # Install required system dependencies for OpenCV
7
  RUN apt-get update && apt-get install -y \
 
28
  COPY --chown=user . /app
29
 
30
  # Set the command to start the application
31
+ CMD ["gunicorn", "-b", "0.0.0.0:7860", "-k", "eventlet", "app:app", "--reload"]
app.py CHANGED
@@ -1,3 +1,5 @@
 
 
1
  import pandas as pd
2
  import json
3
  from PIL import Image
@@ -29,58 +31,6 @@ from flask import Flask, request, render_template
29
  from flask_cors import CORS
30
  from flask_socketio import SocketIO, emit
31
 
32
-
33
- # GROQ_API_KEY = os.getenv("GROQ_API_KEY")
34
- GROQ_API_KEY = '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'
35
- # load_dotenv(".env")
36
- USER_AGENT = os.getenv("USER_AGENT")
37
- GROQ_API_KEY = os.getenv("GROQ_API_KEY")
38
- SECRET_KEY = os.getenv("SECRET_KEY")
39
-
40
-
41
- # Set environment variables
42
- os.environ['USER_AGENT'] = USER_AGENT
43
- os.environ["GROQ_API_KEY"] = GROQ_API_KEY
44
- os.environ["TOKENIZERS_PARALLELISM"] = 'true'
45
-
46
- # Initialize Flask app and SocketIO with CORS
47
- app = Flask(__name__)
48
- CORS(app)
49
- app.config['MAX_CONTENT_LENGTH'] = 1e7
50
- app.config['SESSION_COOKIE_SECURE'] = True # Use HTTPS
51
- app.config['SESSION_COOKIE_HTTPONLY'] = True
52
- app.config['SESSION_COOKIE_SAMESITE'] = 'Lax'
53
- socketio = SocketIO(app, cors_allowed_origins="*", logger=True, max_http_buffer_size=1e7)
54
- app.config['SECRET_KEY'] = SECRET_KEY
55
-
56
- import pandas as pd
57
- from PIL import Image
58
- import numpy as np
59
- import os
60
-
61
- import torch
62
- import torch.nn.functional as F
63
-
64
- # from src.data.embs import ImageDataset
65
- from src.model.blip_embs import blip_embs
66
- from src.data.transforms import transform_test
67
-
68
- from transformers import StoppingCriteria, StoppingCriteriaList, TextIteratorStreamer
69
- import gradio as gr
70
- # import spaces
71
-
72
- from langchain.chains import ConversationChain
73
- from langchain_community.chat_message_histories import ChatMessageHistory
74
- from langchain_core.runnables import RunnableWithMessageHistory
75
- from langchain_core.output_parsers import StrOutputParser
76
- from langchain_core.prompts import ChatPromptTemplate
77
- from langchain_groq import ChatGroq
78
-
79
- from dotenv import load_dotenv
80
- from flask import Flask, request, render_template
81
- from flask_cors import CORS
82
- from flask_socketio import SocketIO, emit
83
-
84
  import json
85
  from openai import OpenAI
86
 
@@ -100,12 +50,11 @@ os.environ["TOKENIZERS_PARALLELISM"] = 'true'
100
  # Initialize Flask app and SocketIO with CORS
101
  app = Flask(__name__)
102
  CORS(app)
103
- socketio = SocketIO(app, cors_allowed_origins="*", logger=True)
104
- app.config['SESSION_COOKIE_SECURE'] = True # Use HTTPS
105
- app.config['SESSION_COOKIE_HTTPONLY'] = True
106
- app.config['SESSION_COOKIE_SAMESITE'] = 'Lax'
107
  app.config['SECRET_KEY'] = SECRET_KEY
108
 
 
109
  # Initialize LLM
110
  llm = ChatGroq(model="llama-3.1-8b-instant", temperature=0, max_tokens=1024, max_retries=2)
111
 
@@ -179,15 +128,11 @@ model = blip_embs(
179
 
180
  model = model.to(device)
181
  model.eval()
182
- print("Model Loaded !")
183
- print("="*50)
184
 
185
  transform = transform_test(384)
186
 
187
- print("Loading Data")
188
  df = pd.read_json("my_recipes.json")
189
 
190
- print("Loading Target Embedding")
191
  tar_img_feats = []
192
  for _id in df["id_"].tolist():
193
  tar_img_feats.append(torch.load("./datasets/sidechef/blip-embs-large/{:07d}.pth".format(_id)).unsqueeze(0))
@@ -234,7 +179,6 @@ class Chat:
234
 
235
 
236
  chat = Chat(model,transform,df,tar_img_feats, device)
237
- print("Chat Initialized !")
238
 
239
 
240
  def answer_generator(formated_input, session_id):
@@ -516,7 +460,7 @@ def handle_message(data):
516
  context = "No data available"
517
  session_id = request.sid
518
  if session_id not in session_store:
519
- session_store[session_id] = {'image_data': b"", 'message': None, 'image_received': False}
520
 
521
  if 'message' in data:
522
  session_store[session_id]['message'] = data['message']
@@ -614,9 +558,6 @@ def handle_message(data):
614
  emit('response', response, room=session_id)
615
  return response
616
 
617
-
618
-
619
-
620
  # Home route
621
  @app.route("/")
622
  def index_view():
@@ -624,4 +565,4 @@ def index_view():
624
 
625
  # Main function to run the app
626
  if __name__ == '__main__':
627
- socketio.run(app, debug=True)
 
1
+ import eventlet
2
+
3
  import pandas as pd
4
  import json
5
  from PIL import Image
 
31
  from flask_cors import CORS
32
  from flask_socketio import SocketIO, emit
33
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
34
  import json
35
  from openai import OpenAI
36
 
 
50
  # Initialize Flask app and SocketIO with CORS
51
  app = Flask(__name__)
52
  CORS(app)
53
+ app.config['MAX_CONTENT_LENGTH'] = 1024 * 1024 * 1024
54
+ socketio = SocketIO(app, cors_allowed_origins="*", logger=True, max_http_buffer_size=1024 * 1024 * 1024)
 
 
55
  app.config['SECRET_KEY'] = SECRET_KEY
56
 
57
+
58
  # Initialize LLM
59
  llm = ChatGroq(model="llama-3.1-8b-instant", temperature=0, max_tokens=1024, max_retries=2)
60
 
 
128
 
129
  model = model.to(device)
130
  model.eval()
 
 
131
 
132
  transform = transform_test(384)
133
 
 
134
  df = pd.read_json("my_recipes.json")
135
 
 
136
  tar_img_feats = []
137
  for _id in df["id_"].tolist():
138
  tar_img_feats.append(torch.load("./datasets/sidechef/blip-embs-large/{:07d}.pth".format(_id)).unsqueeze(0))
 
179
 
180
 
181
  chat = Chat(model,transform,df,tar_img_feats, device)
 
182
 
183
 
184
  def answer_generator(formated_input, session_id):
 
460
  context = "No data available"
461
  session_id = request.sid
462
  if session_id not in session_store:
463
+ session_store[session_id] = {'image_data': "", 'message': None, 'image_received': False}
464
 
465
  if 'message' in data:
466
  session_store[session_id]['message'] = data['message']
 
558
  emit('response', response, room=session_id)
559
  return response
560
 
 
 
 
561
  # Home route
562
  @app.route("/")
563
  def index_view():
 
565
 
566
  # Main function to run the app
567
  if __name__ == '__main__':
568
+ socketio.run(app, debug=False)
test-1.py DELETED
@@ -1,439 +0,0 @@
1
- # %%
2
- import pandas as pd
3
- from PIL import Image
4
- import numpy as np
5
- import os
6
-
7
- import torch
8
- import torch.nn.functional as F
9
-
10
- # from src.data.embs import ImageDataset
11
- from src.model.blip_embs import blip_embs
12
- from src.data.transforms import transform_test
13
-
14
- from transformers import StoppingCriteria, StoppingCriteriaList, TextIteratorStreamer
15
- import gradio as gr
16
- # import spaces
17
-
18
- from langchain.chains import ConversationChain
19
- from langchain_community.chat_message_histories import ChatMessageHistory
20
- from langchain_core.runnables import RunnableWithMessageHistory
21
- from langchain_core.output_parsers import StrOutputParser
22
- from langchain_core.prompts import ChatPromptTemplate
23
- from langchain_groq import ChatGroq
24
-
25
- from dotenv import load_dotenv
26
- from flask import Flask, request, render_template
27
- from flask_cors import CORS
28
- from flask_socketio import SocketIO, emit
29
-
30
- import json
31
- from openai import OpenAI
32
-
33
- # %%
34
- # GROQ_API_KEY = os.getenv("GROQ_API_KEY")
35
- GROQ_API_KEY = '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'
36
- OPENAI_API_KEY="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"
37
- load_dotenv(".env")
38
- USER_AGENT = os.getenv("USER_AGENT")
39
- GROQ_API_KEY = os.getenv("GROQ_API_KEY")
40
-
41
- SECRET_KEY = os.getenv("SECRET_KEY")
42
-
43
- # Set environment variables
44
- os.environ['USER_AGENT'] = USER_AGENT
45
- os.environ["GROQ_API_KEY"] = GROQ_API_KEY
46
- os.environ['OPENAI_API_KEY'] = OPENAI_API_KEY
47
- os.environ["TOKENIZERS_PARALLELISM"] = 'true'
48
-
49
- # %%
50
- # Initialize Flask app and SocketIO with CORS
51
- app = Flask(__name__)
52
- CORS(app)
53
- socketio = SocketIO(app, cors_allowed_origins="*", logger=True)
54
- app.config['SESSION_COOKIE_SECURE'] = True # Use HTTPS
55
- app.config['SESSION_COOKIE_HTTPONLY'] = True
56
- app.config['SESSION_COOKIE_SAMESITE'] = 'Lax'
57
- app.config['SECRET_KEY'] = SECRET_KEY
58
-
59
- # %%
60
- # Initialize LLM
61
- llm = ChatGroq(model="llama-3.1-8b-instant", temperature=0, max_tokens=1024, max_retries=2)
62
-
63
- # Initialize Router
64
- router = ChatGroq(model="llama-3.2-3b-preview", temperature=0, max_tokens=1024, max_retries=2, model_kwargs={"response_format": {"type": "json_object"}})
65
-
66
- # Initialized recommendation LLM
67
- client = OpenAI()
68
-
69
- # %%
70
- class StoppingCriteriaSub(StoppingCriteria):
71
-
72
- def __init__(self, stops=[], encounters=1):
73
- super().__init__()
74
- self.stops = stops
75
-
76
- def __call__(self, input_ids: torch.LongTensor, scores: torch.FloatTensor):
77
- for stop in self.stops:
78
- if torch.all(input_ids[:, -len(stop):] == stop).item():
79
- return True
80
-
81
- return False
82
-
83
- device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
84
-
85
- def get_blip_config(model="base"):
86
- config = dict()
87
- if model == "base":
88
- config[
89
- "pretrained"
90
- ] = "https://storage.googleapis.com/sfr-vision-language-research/BLIP/models/model_base_capfilt_large.pth "
91
- config["vit"] = "base"
92
- config["batch_size_train"] = 32
93
- config["batch_size_test"] = 16
94
- config["vit_grad_ckpt"] = True
95
- config["vit_ckpt_layer"] = 4
96
- config["init_lr"] = 1e-5
97
- elif model == "large":
98
- config[
99
- "pretrained"
100
- ] = "https://storage.googleapis.com/sfr-vision-language-research/BLIP/models/model_large_retrieval_coco.pth"
101
- config["vit"] = "large"
102
- config["batch_size_train"] = 16
103
- config["batch_size_test"] = 32
104
- config["vit_grad_ckpt"] = True
105
- config["vit_ckpt_layer"] = 12
106
- config["init_lr"] = 5e-6
107
-
108
- config["image_size"] = 384
109
- config["queue_size"] = 57600
110
- config["alpha"] = 0.4
111
- config["k_test"] = 256
112
- config["negative_all_rank"] = True
113
-
114
- return config
115
-
116
- # %%
117
- print("Creating model")
118
- config = get_blip_config("large")
119
-
120
- model = blip_embs(
121
- pretrained=config["pretrained"],
122
- image_size=config["image_size"],
123
- vit=config["vit"],
124
- vit_grad_ckpt=config["vit_grad_ckpt"],
125
- vit_ckpt_layer=config["vit_ckpt_layer"],
126
- queue_size=config["queue_size"],
127
- negative_all_rank=config["negative_all_rank"],
128
- )
129
-
130
- model = model.to(device)
131
- model.eval()
132
- print("Model Loaded !")
133
- print("="*50)
134
-
135
- # %%
136
- transform = transform_test(384)
137
-
138
- print("Loading Data")
139
- df = pd.read_json("datasets/sidechef/my_recipes.json")
140
-
141
- print("Loading Target Embedding")
142
- tar_img_feats = []
143
- for _id in df["id_"].tolist():
144
- tar_img_feats.append(torch.load("datasets/sidechef/blip-embs-large/{:07d}.pth".format(_id)).unsqueeze(0))
145
-
146
- tar_img_feats = torch.cat(tar_img_feats, dim=0)
147
-
148
- # %%
149
- class Chat:
150
-
151
- def __init__(self, model, transform, dataframe, tar_img_feats, device='cuda:0', stopping_criteria=None):
152
- self.device = device
153
- self.model = model
154
- self.transform = transform
155
- self.df = dataframe
156
- self.tar_img_feats = tar_img_feats
157
- self.img_feats = None
158
- self.target_recipe = None
159
- self.messages = []
160
-
161
- if stopping_criteria is not None:
162
- self.stopping_criteria = stopping_criteria
163
- else:
164
- stop_words_ids = [torch.tensor([2]).to(self.device)]
165
- self.stopping_criteria = StoppingCriteriaList([StoppingCriteriaSub(stops=stop_words_ids)])
166
-
167
- def encode_image(self, image_path):
168
- img = Image.fromarray(image_path).convert("RGB")
169
- img = self.transform(img).unsqueeze(0)
170
- img = img.to(self.device)
171
- img_embs = model.visual_encoder(img)
172
- img_feats = F.normalize(model.vision_proj(img_embs[:, 0, :]), dim=-1).cpu()
173
-
174
- self.img_feats = img_feats
175
-
176
- self.get_target(self.img_feats, self.tar_img_feats)
177
-
178
- def get_target(self, img_feats, tar_img_feats) :
179
- score = (img_feats @ tar_img_feats.t()).squeeze(0).cpu().detach().numpy()
180
- index = np.argsort(score)[::-1][0]
181
- self.target_recipe = df.iloc[index]
182
-
183
- def ask(self):
184
- return json.dumps(self.target_recipe.to_json())
185
-
186
-
187
-
188
- # %%
189
- chat = Chat(model,transform,df,tar_img_feats, device)
190
- print("Chat Initialized !")
191
-
192
- # %%
193
-
194
- def answer_generator(formated_input):
195
- # QA system prompt and chain
196
- qa_system_prompt = """
197
- You are an AI assistant developed by Nutrigenics AI, specializing in intelligent recipe information retrieval and recipe suggestions. Your purpose is to help users by recommending recipes, providing detailed nutritional values, listing ingredients, offering step-by-step cooking instructions, and filtering recipes based on provide context ans user query.
198
- Operational Guidelines:
199
- 1. Input Structure:
200
- - Context: You may receive contextual information related to recipes, such as specific data sets, user preferences, dietary restrictions, or previously selected dishes.
201
- - User Query: Users will pose questions or requests related to recipes, nutritional information, ingredient substitutions, cooking instructions, and more.
202
- 2. Response Strategy:
203
- - Utilize Provided Context: If the context contains relevant information that addresses the user's query, base your response on this provided data to ensure accuracy and relevance.
204
- - Respond to User Query Directly: If the context does not contain the necessary information to answer the user's query, generate a response based solely on the user's input and your trained knowledge.
205
- Core Functionalities:
206
- - Nutritional Information: Accurately provide nutritional values for each recipe, including calories, macronutrients (proteins, fats, carbohydrates), and essential vitamins and minerals, using contextual data when available.
207
- - Ingredient Details: List all ingredients required for recipes, including substitute options for dietary restrictions or ingredient availability, utilizing context when relevant.
208
- - Step-by-Step Cooking Instructions: Deliver clear, easy-to-follow instructions for preparing and cooking meals, informed by any provided contextual data.
209
- - Recipe Recommendations: Suggest dishes based on user preferences, dietary restrictions, available ingredients, and contextual data if provided.
210
- Additional Instructions:
211
- - Precision and Personalization: Always aim to provide precise, personalized, and relevant information to users based on both the provided context and their specific queries.
212
- - Clarity and Coherence: Ensure that all responses are clear, well-structured, and easy to understand, facilitating a seamless user experience.
213
- - Substitute Suggestions: When suggesting ingredient substitutes, consider user preferences and dietary restrictions outlined in the context or user query.
214
- - Dynamic Adaptation: Adapt your responses dynamically based on whether the context is relevant to the user's current request, ensuring optimal use of available information.
215
- Don't mention about context in the response, format the answer in a natural and friendly way.
216
- Context:
217
- {context}
218
- """
219
- qa_prompt = ChatPromptTemplate.from_messages(
220
- [
221
- ("system", qa_system_prompt),
222
- ("human", "{input}")
223
- ]
224
- )
225
-
226
- # Create the base chain
227
- base_chain = qa_prompt | llm | StrOutputParser()
228
-
229
- # Wrap the chain with message history
230
- question_answer_chain = RunnableWithMessageHistory(
231
- base_chain,
232
- lambda session_id: ChatMessageHistory(), # This creates a new history for each session
233
- input_messages_key="input",
234
- history_messages_key="chat_history"
235
- )
236
-
237
- response = question_answer_chain.invoke(formated_input, config={"configurable": {"session_id": 'abc123'}})
238
-
239
- return response
240
-
241
-
242
-
243
- # %%
244
- ### Router
245
- import json
246
- from langchain_core.messages import HumanMessage, SystemMessage
247
-
248
- def router_node(query):
249
- # Prompt
250
- router_instructions = """You are an expert at determining the appropriate task for a user’s question based on chat history and the current query context. You have two available tasks:
251
-
252
- 1. Retrieval: Fetch information based on user's chat history and current query.
253
- 2. Recommendation/Suggestion: Recommend recipes to users based on the query.
254
-
255
- Return a JSON response with a single key named “task” indicating either “retrieval” or “recommendation” based on your decision.
256
- """
257
-
258
-
259
-
260
- response = router.invoke(
261
- [SystemMessage(content=router_instructions)]
262
- + [
263
- HumanMessage(
264
- content=query
265
- )
266
- ]
267
- )
268
- res = json.loads(response.content)
269
- return res['task']
270
-
271
- # %%
272
- def recommendation_node(query):
273
- prompt = """
274
- You are a helpful assistant that writes Python code to filter recipes from a JSON filr based o the user query. \n
275
- JSON file path = 'recipes.json' \n
276
- The JSON file is a list of recipes with the following structure: \n
277
- {
278
- "recipe_name": string,
279
- "recipe_time": integer,
280
- "recipe_yields": string,
281
- "recipe_ingredients": list of ingredients,
282
- "recipe_instructions": list of instruections,
283
- "recipe_image": string,
284
- "blogger": string,
285
- "recipe_nutrients": JSON object with key value pairs such as "protein: 10g",
286
- "tags": list of tags related to recipe
287
- } \n
288
-
289
- Here is the example of an recipe json object from the JSON data: \n
290
- {
291
- "recipe_name": "Asian Potato Salad with Seven Minute Egg",
292
- "recipe_time": 0,
293
- "recipe_yields": "4 servings",
294
- "recipe_ingredients": [
295
- "2 1/2 cup Multi-Colored Fingerling Potato",
296
- "3/4 cup Celery",
297
- "1/4 cup Red Onion",
298
- "2 tablespoon Fresh Parsley",
299
- "1/3 cup Mayonnaise",
300
- "1 tablespoon Chili Garlic Sauce",
301
- "1 teaspoon Hoisin Sauce",
302
- "1 splash Soy Sauce",
303
- "to taste Salt",
304
- "to taste Ground Black Pepper",
305
- "4 Egg"
306
- ],
307
- "recipe_instructions": "Fill a large stock pot with water.\nAdd the Multi-Colored Fingerling Potato (2 1/2 cup) and bring water to a boil. Boil the potatoes for 20 minutes or until fork tender.\nDrain the potatoes and let them cool completely.\nMeanwhile, mix together in a small bowl Mayonnaise (1/3 cup), Chili Garlic Sauce (1 tablespoon), Hoisin Sauce (1 teaspoon), and Soy Sauce (1 splash).\nTo make the Egg (4), fill a stock pot with water and bring to a boil Gently add the eggs to the water and set a timer for seven minutes.\nThen move the eggs to an ice bath to cool completely. Once cooled, crack the egg slightly and remove the shell. Slice in half when ready to serve.\nNext, halve the cooled potatoes and place into a large serving bowl. Add the Ground Black Pepper (to taste), Celery (3/4 cup), Red Onion (1/4 cup), and mayo mixture. Toss to combine adding Salt (to taste) and Fresh Parsley (2 tablespoon).\nTop with seven minute eggs and serve. Enjoy!",
308
- "recipe_image": "https://www.sidechef.com/recipe/eeeeeceb-493e-493d-8273-66c800821b13.jpg?d=1408x1120",
309
- "blogger": "sidechef.com",
310
- "recipe_nutrients": {
311
- "calories": "80 calories",
312
- "proteinContent": "2.1 g",
313
- "fatContent": "6.2 g",
314
- "carbohydrateContent": "3.9 g",
315
- "fiberContent": "0.5 g",
316
- "sugarContent": "0.4 g",
317
- "sodiumContent": "108.0 mg",
318
- "saturatedFatContent": "1.2 g",
319
- "transFatContent": "0.0 g",
320
- "cholesterolContent": "47.4 mg",
321
- "unsaturatedFatContent": "3.8 g"
322
- },
323
- "tags": [
324
- "Salad",
325
- "Lunch",
326
- "Brunch",
327
- "Appetizers",
328
- "Side Dish",
329
- "Budget-Friendly",
330
- "Vegetarian",
331
- "Pescatarian",
332
- "Eggs",
333
- "Potatoes",
334
- "Dairy-Free",
335
- "Shellfish-Free"
336
- ]
337
- } \n
338
-
339
- Based on the user query, provide a Python function to filter the JSON data. The output of the function should be a list of json objects. \n
340
-
341
- Your output instructions:
342
- - The function name should be filter_recipes. The input to the function should be file name.
343
- - The length of output recipes should not be more than 10.
344
- - Only give me output function. Do not call the function.
345
- - Give the python function as a key named "code" in a json format.
346
- - Do not include any other text with the output, only give python code.
347
- - If you do not follow the above given instructions, the chat may be terminated.
348
- """
349
- max_tries = 3
350
- while True:
351
- try:
352
- # llm = ChatGroq(model="llama-3.1-8b-instant", temperature=0, max_tokens=1024, max_retries=2, model_kwargs={"response_format": {"type": "json_object"}})
353
- response = client.chat.completions.create(
354
- model="gpt-4o-mini",
355
- messages=[
356
- {"role": "system", "content": prompt},
357
- {
358
- "role": "user",
359
- "content": query
360
- }
361
- ]
362
- )
363
-
364
- content = response.choices[0].message.content
365
-
366
- res = json.loads(content)
367
- script = res['code']
368
- exec(script)
369
- recipes = filter_recipes('recipes.json')
370
- if recipes:
371
- break
372
- except Exception as e:
373
- if max_tries <= 0:
374
- return []
375
- else:
376
- max_tries -= 1
377
- return recipes
378
-
379
- # %%
380
- curr_context = ''
381
-
382
- # %%
383
- # @spaces.GPU
384
- def respond_to_user(image=[], message=''):
385
- if len(image) > 0:
386
- try:
387
- # Process the image and message here
388
- device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
389
- chat = Chat(model,transform,df,tar_img_feats, device)
390
- chat.encode_image(image)
391
- data = chat.ask()
392
- curr_context = data
393
- formated_input = {
394
- 'input': message,
395
- 'context': data
396
- }
397
- response = answer_generator(formated_input)
398
- except Exception as e:
399
- print(e)
400
- response = {'content':"An error occurred while processing your request."}
401
- elif len(image) == 0 and message is not None:
402
- print("I am here")
403
- task = router_node(message)
404
- if task == 'retrieval':
405
- response = recommendation_node(message)
406
- if response:
407
- response = {'content':"An error occurred while processing your request."}
408
- else:
409
- formated_input = {
410
- 'input': message,
411
- 'context': curr_context
412
- }
413
- response = answer_generator(formated_input)
414
-
415
- return response
416
-
417
- # %%
418
- image_path = "./test_images/15-Second_Creamy_Scrambled_Eggs_0000200.png"
419
- message = "list out recipes with most carbohydrates"
420
-
421
- # %%
422
- from PIL import Image
423
- import numpy as np
424
-
425
- # Load the image
426
- image = Image.open(image_path)
427
-
428
- # Convert the image to a NumPy array
429
- image_array = np.array(image)
430
-
431
- # %%
432
- import pprint
433
- res = respond_to_user(message=message)
434
- pprint.pprint(res)
435
-
436
- # %%
437
- curr_context
438
-
439
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
test.ipynb DELETED
@@ -1,1186 +0,0 @@
1
- {
2
- "cells": [
3
- {
4
- "cell_type": "code",
5
- "execution_count": 147,
6
- "metadata": {},
7
- "outputs": [],
8
- "source": [
9
- "import pandas as pd\n",
10
- "from PIL import Image\n",
11
- "import numpy as np\n",
12
- "import os\n",
13
- "\n",
14
- "import torch\n",
15
- "import torch.nn.functional as F\n",
16
- "\n",
17
- "# from src.data.embs import ImageDataset\n",
18
- "from src.model.blip_embs import blip_embs\n",
19
- "from src.data.transforms import transform_test\n",
20
- "\n",
21
- "from transformers import StoppingCriteria, StoppingCriteriaList, TextIteratorStreamer\n",
22
- "import gradio as gr\n",
23
- "# import spaces\n",
24
- "\n",
25
- "from langchain.chains import ConversationChain\n",
26
- "from langchain_community.chat_message_histories import ChatMessageHistory\n",
27
- "from langchain_core.runnables import RunnableWithMessageHistory\n",
28
- "from langchain_core.output_parsers import StrOutputParser\n",
29
- "from langchain_core.prompts import ChatPromptTemplate\n",
30
- "from langchain_groq import ChatGroq\n",
31
- "\n",
32
- "from dotenv import load_dotenv\n",
33
- "from flask import Flask, request, render_template\n",
34
- "from flask_cors import CORS\n",
35
- "from flask_socketio import SocketIO, emit\n",
36
- "\n",
37
- "import json\n",
38
- "from openai import OpenAI"
39
- ]
40
- },
41
- {
42
- "cell_type": "code",
43
- "execution_count": 148,
44
- "metadata": {},
45
- "outputs": [],
46
- "source": [
47
- "# GROQ_API_KEY = os.getenv(\"GROQ_API_KEY\")\n",
48
- "GROQ_API_KEY = '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'\n",
49
- "OPENAI_API_KEY=\"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\"\n",
50
- "load_dotenv(\".env\")\n",
51
- "USER_AGENT = os.getenv(\"USER_AGENT\")\n",
52
- "GROQ_API_KEY = os.getenv(\"GROQ_API_KEY\")\n",
53
- "\n",
54
- "SECRET_KEY = os.getenv(\"SECRET_KEY\")\n",
55
- "\n",
56
- "# Set environment variables\n",
57
- "os.environ['USER_AGENT'] = USER_AGENT\n",
58
- "os.environ[\"GROQ_API_KEY\"] = GROQ_API_KEY\n",
59
- "os.environ['OPENAI_API_KEY'] = OPENAI_API_KEY\n",
60
- "os.environ[\"TOKENIZERS_PARALLELISM\"] = 'true'"
61
- ]
62
- },
63
- {
64
- "cell_type": "code",
65
- "execution_count": 149,
66
- "metadata": {},
67
- "outputs": [],
68
- "source": [
69
- "# Initialize Flask app and SocketIO with CORS\n",
70
- "app = Flask(__name__)\n",
71
- "CORS(app)\n",
72
- "socketio = SocketIO(app, cors_allowed_origins=\"*\", logger=True)\n",
73
- "app.config['SESSION_COOKIE_SECURE'] = True # Use HTTPS\n",
74
- "app.config['SESSION_COOKIE_HTTPONLY'] = True\n",
75
- "app.config['SESSION_COOKIE_SAMESITE'] = 'Lax'\n",
76
- "app.config['SECRET_KEY'] = SECRET_KEY"
77
- ]
78
- },
79
- {
80
- "cell_type": "code",
81
- "execution_count": 150,
82
- "metadata": {},
83
- "outputs": [],
84
- "source": [
85
- "# Initialize LLM\n",
86
- "llm = ChatGroq(model=\"llama-3.1-8b-instant\", temperature=0, max_tokens=1024, max_retries=2)\n",
87
- "\n",
88
- "# Initialize Router\n",
89
- "router = ChatGroq(model=\"llama-3.2-3b-preview\", temperature=0, max_tokens=1024, max_retries=2, model_kwargs={\"response_format\": {\"type\": \"json_object\"}})\n",
90
- "\n",
91
- "# Initialized recommendation LLM\n",
92
- "client = OpenAI()"
93
- ]
94
- },
95
- {
96
- "cell_type": "code",
97
- "execution_count": 151,
98
- "metadata": {},
99
- "outputs": [],
100
- "source": [
101
- "class StoppingCriteriaSub(StoppingCriteria):\n",
102
- "\n",
103
- " def __init__(self, stops=[], encounters=1):\n",
104
- " super().__init__()\n",
105
- " self.stops = stops\n",
106
- "\n",
107
- " def __call__(self, input_ids: torch.LongTensor, scores: torch.FloatTensor):\n",
108
- " for stop in self.stops:\n",
109
- " if torch.all(input_ids[:, -len(stop):] == stop).item():\n",
110
- " return True\n",
111
- "\n",
112
- " return False\n",
113
- "\n",
114
- "device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n",
115
- "\n",
116
- "def get_blip_config(model=\"base\"):\n",
117
- " config = dict()\n",
118
- " if model == \"base\":\n",
119
- " config[\n",
120
- " \"pretrained\"\n",
121
- " ] = \"https://storage.googleapis.com/sfr-vision-language-research/BLIP/models/model_base_capfilt_large.pth \"\n",
122
- " config[\"vit\"] = \"base\"\n",
123
- " config[\"batch_size_train\"] = 32\n",
124
- " config[\"batch_size_test\"] = 16\n",
125
- " config[\"vit_grad_ckpt\"] = True\n",
126
- " config[\"vit_ckpt_layer\"] = 4\n",
127
- " config[\"init_lr\"] = 1e-5\n",
128
- " elif model == \"large\":\n",
129
- " config[\n",
130
- " \"pretrained\"\n",
131
- " ] = \"https://storage.googleapis.com/sfr-vision-language-research/BLIP/models/model_large_retrieval_coco.pth\"\n",
132
- " config[\"vit\"] = \"large\"\n",
133
- " config[\"batch_size_train\"] = 16\n",
134
- " config[\"batch_size_test\"] = 32\n",
135
- " config[\"vit_grad_ckpt\"] = True\n",
136
- " config[\"vit_ckpt_layer\"] = 12\n",
137
- " config[\"init_lr\"] = 5e-6\n",
138
- "\n",
139
- " config[\"image_size\"] = 384\n",
140
- " config[\"queue_size\"] = 57600\n",
141
- " config[\"alpha\"] = 0.4\n",
142
- " config[\"k_test\"] = 256\n",
143
- " config[\"negative_all_rank\"] = True\n",
144
- "\n",
145
- " return config"
146
- ]
147
- },
148
- {
149
- "cell_type": "code",
150
- "execution_count": 152,
151
- "metadata": {},
152
- "outputs": [
153
- {
154
- "name": "stdout",
155
- "output_type": "stream",
156
- "text": [
157
- "Creating model\n",
158
- "load checkpoint from https://storage.googleapis.com/sfr-vision-language-research/BLIP/models/model_large_retrieval_coco.pth\n",
159
- "missing keys:\n",
160
- "[]\n",
161
- "Model Loaded !\n",
162
- "==================================================\n"
163
- ]
164
- }
165
- ],
166
- "source": [
167
- "print(\"Creating model\")\n",
168
- "config = get_blip_config(\"large\")\n",
169
- "\n",
170
- "model = blip_embs(\n",
171
- " pretrained=config[\"pretrained\"],\n",
172
- " image_size=config[\"image_size\"],\n",
173
- " vit=config[\"vit\"],\n",
174
- " vit_grad_ckpt=config[\"vit_grad_ckpt\"],\n",
175
- " vit_ckpt_layer=config[\"vit_ckpt_layer\"],\n",
176
- " queue_size=config[\"queue_size\"],\n",
177
- " negative_all_rank=config[\"negative_all_rank\"],\n",
178
- " )\n",
179
- "\n",
180
- "model = model.to(device)\n",
181
- "model.eval()\n",
182
- "print(\"Model Loaded !\")\n",
183
- "print(\"=\"*50)"
184
- ]
185
- },
186
- {
187
- "cell_type": "code",
188
- "execution_count": 153,
189
- "metadata": {},
190
- "outputs": [
191
- {
192
- "name": "stdout",
193
- "output_type": "stream",
194
- "text": [
195
- "Loading Data\n",
196
- "Loading Target Embedding\n"
197
- ]
198
- }
199
- ],
200
- "source": [
201
- "transform = transform_test(384)\n",
202
- "\n",
203
- "print(\"Loading Data\")\n",
204
- "df = pd.read_json(\"datasets/sidechef/my_recipes.json\")\n",
205
- "\n",
206
- "print(\"Loading Target Embedding\")\n",
207
- "tar_img_feats = []\n",
208
- "for _id in df[\"id_\"].tolist(): \n",
209
- " tar_img_feats.append(torch.load(\"datasets/sidechef/blip-embs-large/{:07d}.pth\".format(_id)).unsqueeze(0))\n",
210
- "\n",
211
- "tar_img_feats = torch.cat(tar_img_feats, dim=0)"
212
- ]
213
- },
214
- {
215
- "cell_type": "code",
216
- "execution_count": 154,
217
- "metadata": {},
218
- "outputs": [],
219
- "source": [
220
- "class Chat:\n",
221
- "\n",
222
- " def __init__(self, model, transform, dataframe, tar_img_feats, device='cuda:0', stopping_criteria=None):\n",
223
- " self.device = device\n",
224
- " self.model = model\n",
225
- " self.transform = transform\n",
226
- " self.df = dataframe\n",
227
- " self.tar_img_feats = tar_img_feats\n",
228
- " self.img_feats = None\n",
229
- " self.target_recipe = None\n",
230
- " self.messages = []\n",
231
- "\n",
232
- " if stopping_criteria is not None:\n",
233
- " self.stopping_criteria = stopping_criteria\n",
234
- " else:\n",
235
- " stop_words_ids = [torch.tensor([2]).to(self.device)]\n",
236
- " self.stopping_criteria = StoppingCriteriaList([StoppingCriteriaSub(stops=stop_words_ids)])\n",
237
- "\n",
238
- " def encode_image(self, image_path):\n",
239
- " img = Image.fromarray(image_path).convert(\"RGB\")\n",
240
- " img = self.transform(img).unsqueeze(0)\n",
241
- " img = img.to(self.device)\n",
242
- " img_embs = model.visual_encoder(img)\n",
243
- " img_feats = F.normalize(model.vision_proj(img_embs[:, 0, :]), dim=-1).cpu()\n",
244
- "\n",
245
- " self.img_feats = img_feats \n",
246
- "\n",
247
- " self.get_target(self.img_feats, self.tar_img_feats)\n",
248
- "\n",
249
- " def get_target(self, img_feats, tar_img_feats) : \n",
250
- " score = (img_feats @ tar_img_feats.t()).squeeze(0).cpu().detach().numpy()\n",
251
- " index = np.argsort(score)[::-1][0]\n",
252
- " self.target_recipe = df.iloc[index]\n",
253
- "\n",
254
- " def ask(self):\n",
255
- " return json.dumps(self.target_recipe.to_json())\n",
256
- "\n"
257
- ]
258
- },
259
- {
260
- "cell_type": "code",
261
- "execution_count": 155,
262
- "metadata": {},
263
- "outputs": [
264
- {
265
- "name": "stdout",
266
- "output_type": "stream",
267
- "text": [
268
- "Chat Initialized !\n"
269
- ]
270
- }
271
- ],
272
- "source": [
273
- "chat = Chat(model,transform,df,tar_img_feats, device)\n",
274
- "print(\"Chat Initialized !\")"
275
- ]
276
- },
277
- {
278
- "cell_type": "code",
279
- "execution_count": 156,
280
- "metadata": {},
281
- "outputs": [],
282
- "source": [
283
- "\n",
284
- "def answer_generator(formated_input):\n",
285
- " # QA system prompt and chain\n",
286
- " qa_system_prompt = \"\"\"\n",
287
- " You are an AI assistant developed by Nutrigenics AI, specializing in intelligent recipe information retrieval and recipe suggestions. Your purpose is to help users by recommending recipes, providing detailed nutritional values, listing ingredients, offering step-by-step cooking instructions, and filtering recipes based on provide context ans user query.\n",
288
- " Operational Guidelines:\n",
289
- " 1. Input Structure:\n",
290
- " - Context: You may receive contextual information related to recipes, such as specific data sets, user preferences, dietary restrictions, or previously selected dishes.\n",
291
- " - User Query: Users will pose questions or requests related to recipes, nutritional information, ingredient substitutions, cooking instructions, and more.\n",
292
- " 2. Response Strategy:\n",
293
- " - Utilize Provided Context: If the context contains relevant information that addresses the user's query, base your response on this provided data to ensure accuracy and relevance.\n",
294
- " - Respond to User Query Directly: If the context does not contain the necessary information to answer the user's query, generate a response based solely on the user's input and your trained knowledge.\n",
295
- " Core Functionalities:\n",
296
- " - Nutritional Information: Accurately provide nutritional values for each recipe, including calories, macronutrients (proteins, fats, carbohydrates), and essential vitamins and minerals, using contextual data when available.\n",
297
- " - Ingredient Details: List all ingredients required for recipes, including substitute options for dietary restrictions or ingredient availability, utilizing context when relevant.\n",
298
- " - Step-by-Step Cooking Instructions: Deliver clear, easy-to-follow instructions for preparing and cooking meals, informed by any provided contextual data.\n",
299
- " - Recipe Recommendations: Suggest dishes based on user preferences, dietary restrictions, available ingredients, and contextual data if provided.\n",
300
- " Additional Instructions:\n",
301
- " - Precision and Personalization: Always aim to provide precise, personalized, and relevant information to users based on both the provided context and their specific queries.\n",
302
- " - Clarity and Coherence: Ensure that all responses are clear, well-structured, and easy to understand, facilitating a seamless user experience.\n",
303
- " - Substitute Suggestions: When suggesting ingredient substitutes, consider user preferences and dietary restrictions outlined in the context or user query.\n",
304
- " - Dynamic Adaptation: Adapt your responses dynamically based on whether the context is relevant to the user's current request, ensuring optimal use of available information.\n",
305
- " Don't mention about context in the response, format the answer in a natural and friendly way.\n",
306
- " Context:\n",
307
- " {context}\n",
308
- " \"\"\"\n",
309
- " qa_prompt = ChatPromptTemplate.from_messages(\n",
310
- " [\n",
311
- " (\"system\", qa_system_prompt),\n",
312
- " (\"human\", \"{input}\")\n",
313
- " ]\n",
314
- " )\n",
315
- "\n",
316
- " # Create the base chain\n",
317
- " base_chain = qa_prompt | llm | StrOutputParser()\n",
318
- "\n",
319
- " # Wrap the chain with message history\n",
320
- " question_answer_chain = RunnableWithMessageHistory(\n",
321
- " base_chain,\n",
322
- " lambda session_id: ChatMessageHistory(), # This creates a new history for each session\n",
323
- " input_messages_key=\"input\",\n",
324
- " history_messages_key=\"chat_history\"\n",
325
- " )\n",
326
- "\n",
327
- " response = question_answer_chain.invoke(formated_input, config={\"configurable\": {\"session_id\": 'abc123'}})\n",
328
- "\n",
329
- " return response\n",
330
- "\n"
331
- ]
332
- },
333
- {
334
- "cell_type": "code",
335
- "execution_count": 157,
336
- "metadata": {},
337
- "outputs": [],
338
- "source": [
339
- "### Router\n",
340
- "import json\n",
341
- "from langchain_core.messages import HumanMessage, SystemMessage\n",
342
- "\n",
343
- "def router_node(query):\n",
344
- " # Prompt\n",
345
- " router_instructions = \"\"\"You are an expert at determining the appropriate task for a user’s question based on chat history and the current query context. You have two available tasks:\n",
346
- "\n",
347
- " 1.\tRetrieval: Fetch information based on user's chat history and current query.\n",
348
- " 2.\tRecommendation/Suggestion: Recommend recipes to users based on the query.\n",
349
- "\n",
350
- " Return a JSON response with a single key named “task” indicating either “retrieval” or “recommendation” based on your decision.\n",
351
- " \"\"\"\n",
352
- "\n",
353
- " \n",
354
- "\n",
355
- " response = router.invoke(\n",
356
- " [SystemMessage(content=router_instructions)]\n",
357
- " + [\n",
358
- " HumanMessage(\n",
359
- " content=query\n",
360
- " )\n",
361
- " ]\n",
362
- " )\n",
363
- " res = json.loads(response.content)\n",
364
- " return res['task']"
365
- ]
366
- },
367
- {
368
- "cell_type": "code",
369
- "execution_count": 158,
370
- "metadata": {},
371
- "outputs": [],
372
- "source": [
373
- "def recommendation_node(query):\n",
374
- " prompt = \"\"\"\n",
375
- " You are a helpful assistant that writes Python code to filter recipes from a JSON filr based o the user query. \\n\n",
376
- " JSON file path = 'recipes.json' \\n\n",
377
- " The JSON file is a list of recipes with the following structure: \\n\n",
378
- " {\n",
379
- " \"recipe_name\": string,\n",
380
- " \"recipe_time\": integer,\n",
381
- " \"recipe_yields\": string,\n",
382
- " \"recipe_ingredients\": list of ingredients,\n",
383
- " \"recipe_instructions\": list of instruections,\n",
384
- " \"recipe_image\": string,\n",
385
- " \"blogger\": string,\n",
386
- " \"recipe_nutrients\": JSON object with key value pairs such as \"protein: 10g\",\n",
387
- " \"tags\": list of tags related to recipe\n",
388
- " } \\n\n",
389
- "\n",
390
- " Here is the example of an recipe json object from the JSON data: \\n\n",
391
- " {\n",
392
- " \"recipe_name\": \"Asian Potato Salad with Seven Minute Egg\",\n",
393
- " \"recipe_time\": 0,\n",
394
- " \"recipe_yields\": \"4 servings\",\n",
395
- " \"recipe_ingredients\": [\n",
396
- " \"2 1/2 cup Multi-Colored Fingerling Potato\",\n",
397
- " \"3/4 cup Celery\",\n",
398
- " \"1/4 cup Red Onion\",\n",
399
- " \"2 tablespoon Fresh Parsley\",\n",
400
- " \"1/3 cup Mayonnaise\",\n",
401
- " \"1 tablespoon Chili Garlic Sauce\",\n",
402
- " \"1 teaspoon Hoisin Sauce\",\n",
403
- " \"1 splash Soy Sauce\",\n",
404
- " \"to taste Salt\",\n",
405
- " \"to taste Ground Black Pepper\",\n",
406
- " \"4 Egg\"\n",
407
- " ],\n",
408
- " \"recipe_instructions\": \"Fill a large stock pot with water.\\nAdd the Multi-Colored Fingerling Potato (2 1/2 cup) and bring water to a boil. Boil the potatoes for 20 minutes or until fork tender.\\nDrain the potatoes and let them cool completely.\\nMeanwhile, mix together in a small bowl Mayonnaise (1/3 cup), Chili Garlic Sauce (1 tablespoon), Hoisin Sauce (1 teaspoon), and Soy Sauce (1 splash).\\nTo make the Egg (4), fill a stock pot with water and bring to a boil Gently add the eggs to the water and set a timer for seven minutes.\\nThen move the eggs to an ice bath to cool completely. Once cooled, crack the egg slightly and remove the shell. Slice in half when ready to serve.\\nNext, halve the cooled potatoes and place into a large serving bowl. Add the Ground Black Pepper (to taste), Celery (3/4 cup), Red Onion (1/4 cup), and mayo mixture. Toss to combine adding Salt (to taste) and Fresh Parsley (2 tablespoon).\\nTop with seven minute eggs and serve. Enjoy!\",\n",
409
- " \"recipe_image\": \"https://www.sidechef.com/recipe/eeeeeceb-493e-493d-8273-66c800821b13.jpg?d=1408x1120\",\n",
410
- " \"blogger\": \"sidechef.com\",\n",
411
- " \"recipe_nutrients\": {\n",
412
- " \"calories\": \"80 calories\",\n",
413
- " \"proteinContent\": \"2.1 g\",\n",
414
- " \"fatContent\": \"6.2 g\",\n",
415
- " \"carbohydrateContent\": \"3.9 g\",\n",
416
- " \"fiberContent\": \"0.5 g\",\n",
417
- " \"sugarContent\": \"0.4 g\",\n",
418
- " \"sodiumContent\": \"108.0 mg\",\n",
419
- " \"saturatedFatContent\": \"1.2 g\",\n",
420
- " \"transFatContent\": \"0.0 g\",\n",
421
- " \"cholesterolContent\": \"47.4 mg\",\n",
422
- " \"unsaturatedFatContent\": \"3.8 g\"\n",
423
- " },\n",
424
- " \"tags\": [\n",
425
- " \"Salad\",\n",
426
- " \"Lunch\",\n",
427
- " \"Brunch\",\n",
428
- " \"Appetizers\",\n",
429
- " \"Side Dish\",\n",
430
- " \"Budget-Friendly\",\n",
431
- " \"Vegetarian\",\n",
432
- " \"Pescatarian\",\n",
433
- " \"Eggs\",\n",
434
- " \"Potatoes\",\n",
435
- " \"Dairy-Free\",\n",
436
- " \"Shellfish-Free\"\n",
437
- " ]\n",
438
- " } \\n\n",
439
- "\n",
440
- " Based on the user query, provide a Python function to filter the JSON data. The output of the function should be a list of json objects. \\n\n",
441
- "\n",
442
- " Your output instructions:\n",
443
- " - The function name should be filter_recipes. The input to the function should be file name.\n",
444
- " - The length of output recipes should not be more than 10.\n",
445
- " - Only give me output function. Do not call the function.\n",
446
- " - Give the python function as a key named \"code\" in a json format.\n",
447
- " - Do not include any other text with the output, only give python code.\n",
448
- " - If you do not follow the above given instructions, the chat may be terminated.\n",
449
- " \"\"\"\n",
450
- " max_tries = 3\n",
451
- " while True:\n",
452
- " try:\n",
453
- " # llm = ChatGroq(model=\"llama-3.1-8b-instant\", temperature=0, max_tokens=1024, max_retries=2, model_kwargs={\"response_format\": {\"type\": \"json_object\"}})\n",
454
- " response = client.chat.completions.create(\n",
455
- " model=\"gpt-4o-mini\",\n",
456
- " messages=[\n",
457
- " {\"role\": \"system\", \"content\": prompt},\n",
458
- " {\n",
459
- " \"role\": \"user\",\n",
460
- " \"content\": query\n",
461
- " }\n",
462
- " ]\n",
463
- " )\n",
464
- "\n",
465
- " content = response.choices[0].message.content\n",
466
- "\n",
467
- " res = json.loads(content)\n",
468
- " script = res['code']\n",
469
- " exec(script)\n",
470
- " recipes = filter_recipes('recipes.json')\n",
471
- " if recipes:\n",
472
- " break\n",
473
- " except Exception as e:\n",
474
- " if max_tries <= 0:\n",
475
- " return []\n",
476
- " else:\n",
477
- " max_tries -= 1\n",
478
- " return recipes"
479
- ]
480
- },
481
- {
482
- "cell_type": "code",
483
- "execution_count": 167,
484
- "metadata": {},
485
- "outputs": [],
486
- "source": [
487
- "CURR_CONTEXT = ''"
488
- ]
489
- },
490
- {
491
- "cell_type": "code",
492
- "execution_count": 168,
493
- "metadata": {},
494
- "outputs": [],
495
- "source": [
496
- "# @spaces.GPU\n",
497
- "def respond_to_user(image=[], message=''):\n",
498
- " global curr_context\n",
499
- " if len(image) > 0:\n",
500
- " try:\n",
501
- " # Process the image and message here\n",
502
- " device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n",
503
- " chat = Chat(model,transform,df,tar_img_feats, device)\n",
504
- " chat.encode_image(image)\n",
505
- " data = chat.ask()\n",
506
- " curr_context = data\n",
507
- " formated_input = {\n",
508
- " 'input': message,\n",
509
- " 'context': data\n",
510
- " }\n",
511
- " response = answer_generator(formated_input)\n",
512
- " except Exception as e:\n",
513
- " print(e)\n",
514
- " response = {'content':\"An error occurred while processing your request.\"}\n",
515
- " elif len(image) == 0 and message is not None:\n",
516
- " print(\"I am here\")\n",
517
- " task = router_node(message)\n",
518
- " if task == 'retrieval':\n",
519
- " response = recommendation_node(message)\n",
520
- " if response:\n",
521
- " response = {'content':\"An error occurred while processing your request.\"}\n",
522
- " else:\n",
523
- " formated_input = {\n",
524
- " 'input': message,\n",
525
- " 'context': curr_context\n",
526
- " }\n",
527
- " response = answer_generator(formated_input)\n",
528
- "\n",
529
- " return response"
530
- ]
531
- },
532
- {
533
- "cell_type": "code",
534
- "execution_count": 169,
535
- "metadata": {},
536
- "outputs": [],
537
- "source": [
538
- "image_path = \"./test_images/15-Second_Creamy_Scrambled_Eggs_0000200.png\"\n",
539
- "message = \"give me nutritional information of this dish\""
540
- ]
541
- },
542
- {
543
- "cell_type": "code",
544
- "execution_count": 170,
545
- "metadata": {},
546
- "outputs": [],
547
- "source": [
548
- "from PIL import Image\n",
549
- "import numpy as np\n",
550
- "\n",
551
- "# Load the image\n",
552
- "image = Image.open(image_path)\n",
553
- "\n",
554
- "# Convert the image to a NumPy array\n",
555
- "image_array = np.array(image)"
556
- ]
557
- },
558
- {
559
- "cell_type": "code",
560
- "execution_count": 172,
561
- "metadata": {},
562
- "outputs": [
563
- {
564
- "name": "stdout",
565
- "output_type": "stream",
566
- "text": [
567
- "('For the \"15-Second Creamy Scrambled Eggs\" recipe, you\\'ll need the following '\n",
568
- " 'ingredients:\\n'\n",
569
- " '\\n'\n",
570
- " '1. 3 large Eggs\\n'\n",
571
- " '2. 1 1/2 tablespoon Milk\\n'\n",
572
- " '3. 1 3/4 teaspoon Corn Starch\\n'\n",
573
- " '4. Salt (to taste)\\n'\n",
574
- " '5. 3 tablespoon Unsalted Butter\\n'\n",
575
- " '\\n'\n",
576
- " 'These ingredients will help you create the creamiest, fastest, and easiest '\n",
577
- " 'scrambled eggs ever!')\n"
578
- ]
579
- }
580
- ],
581
- "source": [
582
- "import pprint\n",
583
- "res = respond_to_user(image=image_array, message=\"give me ingredients fot his dish\")\n",
584
- "pprint.pprint(res)"
585
- ]
586
- },
587
- {
588
- "cell_type": "code",
589
- "execution_count": 166,
590
- "metadata": {},
591
- "outputs": [
592
- {
593
- "data": {
594
- "text/plain": [
595
- "''"
596
- ]
597
- },
598
- "execution_count": 166,
599
- "metadata": {},
600
- "output_type": "execute_result"
601
- }
602
- ],
603
- "source": [
604
- "curr_context"
605
- ]
606
- },
607
- {
608
- "cell_type": "code",
609
- "execution_count": 173,
610
- "metadata": {},
611
- "outputs": [
612
- {
613
- "data": {
614
- "text/plain": [
615
- "[{'recipe_name': 'Farmers Market Breakfast Pizza',\n",
616
- " 'recipe_time': 0,\n",
617
- " 'recipe_yields': '2 servings',\n",
618
- " 'recipe_ingredients': ['1/2 Pizza Dough',\n",
619
- " '1/2 cup Kale',\n",
620
- " '1/2 cup Onion',\n",
621
- " '1/2 Zucchini',\n",
622
- " '1/2 Yellow Squash',\n",
623
- " '1/2 cup Shredded Mozzarella Cheese',\n",
624
- " '3 Egg',\n",
625
- " '1 tablespoon Coconut Oil',\n",
626
- " '3 clove Garlic',\n",
627
- " '1 cup Sweet Onion',\n",
628
- " '1/4 cup Water',\n",
629
- " '3 cup Cherry Tomato',\n",
630
- " '1/4 teaspoon Salt',\n",
631
- " '1/4 teaspoon Ground Black Pepper',\n",
632
- " '1 teaspoon Granulated Sugar',\n",
633
- " '1 tablespoon Dried Parsley',\n",
634
- " '1 teaspoon Dried Basil',\n",
635
- " '10 Fresh Basil Leaf',\n",
636
- " 'as needed Coconut Oil Cooking Spray'],\n",
637
- " 'recipe_instructions': 'For homemade pizza sauce, finely chop the Sweet Onion (1 cup), and mince the Garlic (3 clove). To a large sauce pan over medium heat, add the Coconut Oil (1 tablespoon), garlic and onions. Cook until onions are translucent, about 5 to 6 minutes.\\nAdd Water (1/4 cup), chopped Cherry Tomato (3 cup), Salt (1/4 teaspoon), Ground Black Pepper (1/4 teaspoon), Granulated Sugar (1 teaspoon), Dried Parsley (1 tablespoon), Dried Basil (1 teaspoon), finely chopped Fresh Basil Leaf (10), and continue cooking another 2 to 3 minutes.\\nReduce heat and simmer 5 more minutes, or until tomatoes have released their juices and cooked down.\\nRemove sauce pan from stove, allow to cool 2 to 3 minutes, then add to NutriBullet or food processor and process 8 to 10 seconds or until sauce reaches a thick, slightly chunky consistency.\\nPreheat oven to 400 degrees F (200 degrees C).\\nSpray pizza pan with Pizza Dough (1/2), roll out Coconut Oil Cooking Spray (as needed), and bake crust for 5 minutes.\\nThinly slice the Zucchini (1/2) and Yellow Squash (1/2).\\nRemove from the oven, add homemade sauce, chopped Kale (1/2 cup), sliced Onion (1/2 cup), zucchini, squash, and Shredded Mozzarella Cheese (1/2 cup), then top with the Egg (3). Bake an additional 5 minutes.\\nTurn heat up to 425 degrees F (220 degrees C), and bake 5 to 7 minutes or until the whites of the eggs are cooked through and the yoke is over medium.\\nEnjoy!',\n",
638
- " 'recipe_image': 'https://www.sidechef.com/recipe/1cd15944-9411-4a9f-9cc9-18cb2050041e.jpg?d=1408x1120',\n",
639
- " 'blogger': 'sidechef.com',\n",
640
- " 'recipe_nutrients': {'calories': '315 calories',\n",
641
- " 'proteinContent': '16.2 g',\n",
642
- " 'fatContent': '10.3 g',\n",
643
- " 'carbohydrateContent': '43.1 g',\n",
644
- " 'fiberContent': '7.8 g',\n",
645
- " 'sugarContent': '8.7 g',\n",
646
- " 'sodiumContent': '586.3 mg',\n",
647
- " 'saturatedFatContent': '5.3 g',\n",
648
- " 'transFatContent': '0.0 g',\n",
649
- " 'cholesterolContent': '144.3 mg',\n",
650
- " 'unsaturatedFatContent': '2.5 g'},\n",
651
- " 'tags': ['Breakfast',\n",
652
- " 'Brunch',\n",
653
- " 'Main Dish',\n",
654
- " 'Budget-Friendly',\n",
655
- " 'Vegetarian',\n",
656
- " 'Pescatarian',\n",
657
- " 'Eggs',\n",
658
- " 'Pizza',\n",
659
- " 'Vegetables',\n",
660
- " 'American',\n",
661
- " 'Shellfish-Free',\n",
662
- " 'Soy-Free',\n",
663
- " \"Mothers' Day\",\n",
664
- " \"Father's Day\",\n",
665
- " 'Food Processor',\n",
666
- " 'Fish-Free',\n",
667
- " 'Peanut-Free',\n",
668
- " 'Tree Nut-Free',\n",
669
- " 'Oven',\n",
670
- " 'Stove',\n",
671
- " ''],\n",
672
- " 'id_': '0000004'},\n",
673
- " {'recipe_name': 'Fettuccini Carbonara',\n",
674
- " 'recipe_time': 0,\n",
675
- " 'recipe_yields': '2 servings',\n",
676
- " 'recipe_ingredients': ['2 Shallot',\n",
677
- " '1 clove Garlic',\n",
678
- " '2 Egg',\n",
679
- " '6 slice Bacon',\n",
680
- " '1/2 cup Heavy Cream',\n",
681
- " '1/4 cup Grated Parmesan Cheese',\n",
682
- " '8 ounce Fettuccine',\n",
683
- " '1 tablespoon Olive Oil',\n",
684
- " 'to taste Salt',\n",
685
- " 'to taste Ground Black Pepper',\n",
686
- " 'to taste Fresh Parsley'],\n",
687
- " 'recipe_instructions': \"Put a generously salted pot of water on to boil for the pasta.\\nIn a pan over medium-low heat, add the Bacon (6 slice) and cook until done but flexible. Sauté Shallot (2) and Garlic (1 clove) until soft.\\nTurn off heat. Add Grated Parmesan Cheese (1/4 cup), Heavy Cream (1/2 cup), and Egg (2) to the shallots and bacon. Mix well.\\nBoil Fettuccine (8 ounce) until al dente. If you're using frozen fresh, pasta about 6 minutes. If it's fresh, 4 minutes. If using dry pasta, follow package instructions.\\nLift pasta out of pot and place into cream mixture. If a little of the pasta water gets into the sauce, that's ok, since the starch in the water with help thicken it up. Gently toss to combine. Add Salt (to taste) and Ground Black Pepper (to taste).\\nDrizzle with Fresh Parsley (to taste). Top with Olive Oil (1 tablespoon) and grated parmesan cheese.\",\n",
688
- " 'recipe_image': 'https://www.sidechef.com/recipe/9e5df75f-bf1a-4e68-b8a9-096842ea6bd6.jpg?d=1408x1120',\n",
689
- " 'blogger': 'sidechef.com',\n",
690
- " 'recipe_nutrients': {'calories': '495 calories',\n",
691
- " 'proteinContent': '15.9 g',\n",
692
- " 'fatContent': '27.1 g',\n",
693
- " 'sugarContent': '3.2 g',\n",
694
- " 'sodiumContent': '282.9 mg',\n",
695
- " 'saturatedFatContent': '12.0 g',\n",
696
- " 'transFatContent': '0.0 g',\n",
697
- " 'cholesterolContent': '150.1 mg',\n",
698
- " 'carbohydrateContent': '47.5 g',\n",
699
- " 'fiberContent': '2.5 g',\n",
700
- " 'unsaturatedFatContent': '9.9 g'},\n",
701
- " 'tags': ['Pasta',\n",
702
- " 'Dinner',\n",
703
- " 'Side Dish',\n",
704
- " 'Main Dish',\n",
705
- " 'Quick and Easy',\n",
706
- " 'Pork',\n",
707
- " 'Eggs',\n",
708
- " 'Cheese',\n",
709
- " 'Date Night',\n",
710
- " '30 or Less',\n",
711
- " 'Comfort Food',\n",
712
- " 'Easy',\n",
713
- " 'Quick',\n",
714
- " 'Italian',\n",
715
- " 'Shellfish-Free',\n",
716
- " 'Gluten-Free',\n",
717
- " 'Soy-Free',\n",
718
- " 'Fish-Free',\n",
719
- " 'Peanut-Free',\n",
720
- " 'Tree Nut-Free',\n",
721
- " 'Sugar-Free',\n",
722
- " 'Classic',\n",
723
- " 'Tomato-Free',\n",
724
- " 'Stove',\n",
725
- " ''],\n",
726
- " 'id_': '0000006'},\n",
727
- " {'recipe_name': 'Huevos Rancheros',\n",
728
- " 'recipe_time': 0,\n",
729
- " 'recipe_yields': '1 serving',\n",
730
- " 'recipe_ingredients': ['2 Yellow Corn Tortilla',\n",
731
- " '2 tablespoon Pinto Beans',\n",
732
- " '2 Egg',\n",
733
- " '2 tablespoon Salsa',\n",
734
- " 'as needed Nonstick Cooking Spray',\n",
735
- " 'to taste Avocado',\n",
736
- " 'to taste Cotija Cheese',\n",
737
- " 'to taste Bacon Bits',\n",
738
- " 'to taste Fresh Cilantro'],\n",
739
- " 'recipe_instructions': 'In a small frying pan, spray a little Nonstick Cooking Spray (as needed) in the pan and heat over medium-high heat. Once hot, place Yellow Corn Tortilla (2), then spray the top of tortilla with oil. Lightly fry for about 30 seconds on each side.\\nCook the Egg (2) to your liking: sunny side up, over easy, or scrambled.\\nAssemble Huevos Rancheros by spreading Salsa (2 tablespoon) over the fried tortillas. Top with cooked eggs and Pinto Beans (2 tablespoon).\\nAdd Fresh Cilantro (to taste), Cotija Cheese (to taste), Bacon Bits (to taste), and Avocado (to taste) if desired. Enjoy!',\n",
740
- " 'recipe_image': 'https://www.sidechef.com/recipe/5284bc88-1305-4379-90c1-59b74a7e9660.jpeg?d=1408x1120',\n",
741
- " 'blogger': 'sidechef.com',\n",
742
- " 'recipe_nutrients': {'calories': '290 calories',\n",
743
- " 'proteinContent': '19.2 g',\n",
744
- " 'fatContent': '10.2 g',\n",
745
- " 'carbohydrateContent': '37.6 g',\n",
746
- " 'fiberContent': '11.7 g',\n",
747
- " 'sugarContent': '2.3 g',\n",
748
- " 'sodiumContent': '380.4 mg',\n",
749
- " 'saturatedFatContent': '3.1 g',\n",
750
- " 'transFatContent': '0.0 g',\n",
751
- " 'cholesterolContent': '364.6 mg',\n",
752
- " 'unsaturatedFatContent': '5.5 g'},\n",
753
- " 'tags': ['Breakfast',\n",
754
- " 'Brunch',\n",
755
- " 'Quick and Easy',\n",
756
- " 'Beans and Legumes',\n",
757
- " 'Eggs',\n",
758
- " '30 or Less',\n",
759
- " 'Easy',\n",
760
- " 'Quick',\n",
761
- " 'Mexican',\n",
762
- " 'Shellfish-Free',\n",
763
- " 'Full Meal',\n",
764
- " 'Gluten-Free',\n",
765
- " 'Soy-Free',\n",
766
- " 'Fish-Free',\n",
767
- " 'Peanut-Free',\n",
768
- " 'Tree Nut-Free',\n",
769
- " 'Sugar-Free',\n",
770
- " 'Tomato-Free',\n",
771
- " 'Stove',\n",
772
- " ''],\n",
773
- " 'id_': '0000009'},\n",
774
- " {'recipe_name': 'Corn & Bacon Hash',\n",
775
- " 'recipe_time': 0,\n",
776
- " 'recipe_yields': '2 servings',\n",
777
- " 'recipe_ingredients': ['6 slice Thick-Cut Bacon',\n",
778
- " '1 pound Red Potato',\n",
779
- " '2 ear Corn',\n",
780
- " '1 bunch Scallion',\n",
781
- " '1/2 teaspoon Salt',\n",
782
- " '1/4 teaspoon Ground Black Pepper',\n",
783
- " '2 tablespoon Butter',\n",
784
- " '2 Egg'],\n",
785
- " 'recipe_instructions': 'Thinly slice the Scallion (1 bunch). Cut the Red Potato (1 pound) into small cubes. Cut the kernels from the Corn (2 ear). Dice the Thick-Cut Bacon (6 slice).\\nCook bacon in a large frying pan over medium heat until the fat is rendered. Once it is crisp, use a slotted spoon to remove the bacon to a plate lined with paper towels.\\nLeave the fat in the pan and add the potatoes. Increase the heat to medium-high. Season with half of the Salt (1/2 teaspoon) and Ground Black Pepper (1/4 teaspoon). Cook for 15 to 20 minutes or until potatoes can easily be pierced with a fork and are golden-brown on the the outside.\\nAdd corn to the skillet and bump the heat up just a bit. Cook the potatoes and corn together for 5 to 6 minutes, stirring frequently, until the corn browns a bit.\\nHeat a small frying pan over medium heat. Add the Butter (2 tablespoon) and crack the Egg (2) in, taking care not to break the yolk. Allow to cook for 1-2 minutes, then flip the egg to cook the other side. Cook for a minute more for an over-medium egg. Remove to a small plate.\\nWhile the eggs finish cooking, add the drained bacon and the green onions to the corn and potatoes and mix well. Turn off the heat and season to taste with the remaining salt and pepper.\\nServe a couple scoops of hash and top with one of the eggs.',\n",
786
- " 'recipe_image': 'https://www.sidechef.com/recipe/385d1878-283d-47e1-9f3f-e591298a92b6.jpg?d=1408x1120',\n",
787
- " 'blogger': 'sidechef.com',\n",
788
- " 'recipe_nutrients': {'calories': '1230 calories',\n",
789
- " 'proteinContent': '37.0 g',\n",
790
- " 'fatContent': '32.0 g',\n",
791
- " 'carbohydrateContent': '205.9 g',\n",
792
- " 'fiberContent': '20.9 g',\n",
793
- " 'sugarContent': '3.8 g',\n",
794
- " 'sodiumContent': '973.3 mg',\n",
795
- " 'saturatedFatContent': '11.4 g',\n",
796
- " 'transFatContent': '0.0 g',\n",
797
- " 'cholesterolContent': '132.8 mg',\n",
798
- " 'unsaturatedFatContent': '12.0 g'},\n",
799
- " 'tags': ['Breakfast',\n",
800
- " 'Brunch',\n",
801
- " 'Pork',\n",
802
- " 'Eggs',\n",
803
- " 'Potatoes',\n",
804
- " 'Easy',\n",
805
- " 'American',\n",
806
- " 'Shellfish-Free',\n",
807
- " 'Gluten-Free',\n",
808
- " 'Soy-Free',\n",
809
- " 'Spring',\n",
810
- " 'Summer',\n",
811
- " 'Fish-Free',\n",
812
- " 'Peanut-Free',\n",
813
- " 'Tree Nut-Free',\n",
814
- " 'Sugar-Free',\n",
815
- " 'Tomato-Free',\n",
816
- " 'Stove',\n",
817
- " ''],\n",
818
- " 'id_': '0000014'},\n",
819
- " {'recipe_name': 'The 1-Minute Breakfast Sandwich',\n",
820
- " 'recipe_time': 0,\n",
821
- " 'recipe_yields': '1 serving',\n",
822
- " 'recipe_ingredients': ['1 English Muffin',\n",
823
- " '1 Egg',\n",
824
- " '1 slice Cheese',\n",
825
- " 'to taste Fresh Spinach',\n",
826
- " 'to taste Carrot',\n",
827
- " 'to taste Alfalfa Sprouts',\n",
828
- " 'to taste Butter',\n",
829
- " 'to taste Mayonnaise',\n",
830
- " 'to taste Sea Salt',\n",
831
- " 'to taste Ground Black Pepper'],\n",
832
- " 'recipe_instructions': 'Place a small pat of Ground Black Pepper (to taste) at the bottom of a small round microwave-safe dish. We like to use our 4 1/2-inch ramekin. Crack an Sea Salt (to taste) over the top. Add Egg (1) and Butter (to taste). Cover dish and microwave for 20 to 30 seconds.\\nMeanwhile place English Muffin (1) into your toaster and toast.\\nSpread Mayonnaise (to taste) over both side of the toasted muffin. Run a butter knife around the edge of the egg dish to release. Add it to the muffin. Add the Cheese (1 slice), Fresh Spinach (to taste), Carrot (to taste), and Alfalfa Sprouts (to taste).\\nTop with the other half of the muffin. Wrap in parchment and away you go!',\n",
833
- " 'recipe_image': 'https://www.sidechef.com/recipe/93e294f3-99e8-4953-a4b6-09b452fd4fa6.jpg?d=1408x1120',\n",
834
- " 'blogger': 'sidechef.com',\n",
835
- " 'recipe_nutrients': {'calories': '310 calories',\n",
836
- " 'proteinContent': '17.7 g',\n",
837
- " 'fatContent': '15.3 g',\n",
838
- " 'carbohydrateContent': '26.9 g',\n",
839
- " 'fiberContent': '2.1 g',\n",
840
- " 'sugarContent': '2.2 g',\n",
841
- " 'sodiumContent': '649.5 mg',\n",
842
- " 'saturatedFatContent': '6.9 g',\n",
843
- " 'transFatContent': '0.0 g',\n",
844
- " 'cholesterolContent': '214.4 mg',\n",
845
- " 'unsaturatedFatContent': '3.2 g'},\n",
846
- " 'tags': ['Sandwich',\n",
847
- " 'Breakfast',\n",
848
- " 'Brunch',\n",
849
- " 'Vegetarian',\n",
850
- " 'Low-Carb',\n",
851
- " 'Pescatarian',\n",
852
- " 'Eggs',\n",
853
- " 'Easy',\n",
854
- " 'Quick',\n",
855
- " 'American',\n",
856
- " 'Shellfish-Free',\n",
857
- " 'Full Meal',\n",
858
- " 'Beginner',\n",
859
- " 'Soy-Free',\n",
860
- " 'Microwave',\n",
861
- " 'Fish-Free',\n",
862
- " 'Peanut-Free',\n",
863
- " 'Tree Nut-Free',\n",
864
- " 'Sugar-Free',\n",
865
- " 'Tomato-Free',\n",
866
- " 'Microwave',\n",
867
- " ''],\n",
868
- " 'id_': '0000017'},\n",
869
- " {'recipe_name': 'Baked Cheesecake',\n",
870
- " 'recipe_time': 0,\n",
871
- " 'recipe_yields': '1 serving',\n",
872
- " 'recipe_ingredients': ['125 gram Butter',\n",
873
- " '50 gram Caster Sugar',\n",
874
- " '150 gram All-Purpose Flour',\n",
875
- " '30 gram Corn Flour',\n",
876
- " '1 teaspoon Vanilla Essence',\n",
877
- " '1 pinch Salt',\n",
878
- " '500 gram Cream Cheese',\n",
879
- " '150 gram Granulated Sugar',\n",
880
- " '7 Egg',\n",
881
- " '150 gram Sour Cream',\n",
882
- " '5 gram Vanilla Essence',\n",
883
- " '1 Lemon'],\n",
884
- " 'recipe_instructions': 'Put Butter (125 gram), Caster Sugar (50 gram), All-Purpose Flour (150 gram), Corn Flour (30 gram), Vanilla Essence (1 teaspoon), and Salt (1 pinch) in the bowl of a stand mixer, with the beater attachment. Mix on low speed until dough forms, don’t over mix.\\nFlatten into a disc, wrap with cling film and refrigerate for 30 minutes.\\nPreheat the oven to 180 degrees C (350 degrees F) steam bake.\\nPut Cream Cheese (500 gram), Granulated Sugar (150 gram), Egg (7), Sour Cream (150 gram), Vanilla Essence (5 gram), Lemon (1) into the bowl of a stand mixer, use the whisk attachment on slow speed until the mixture is smooth.\\nRoll pastry to about 2 to 3-millimeters thick on baking paper in a baking tray. Dock well, make sure pastry is larger than the cake ring.\\nPut the baking tray into shelf 2 and bake for 15 minutes.\\nAllow to cool slightly, press the cake ring into a disc and allow to cool completely, it will be very fragile. Reduce the oven temperature to 130 degrees C (270 degrees F).\\nPut the pastry on the greased and parchment-lined cake tin base, line the sides of the cake ring, and clamp.\\nPour in filling and bake for 35 minutes.\\nAfter 35 minutes, turn off the oven and keep the door closed for 1 hour. After that leave a gap in the oven door for another half hour. Take the cheesecake out of the oven and cool completely before putting it into the fridge.\\nAllow to cool completely before cutting, leaving overnight in the refrigerator is best. Serve.',\n",
885
- " 'recipe_image': 'https://www.sidechef.com/recipe/8b1073e8-de06-4f2e-8715-1b563363ad36.jpg?d=1408x1120',\n",
886
- " 'blogger': 'sidechef.com',\n",
887
- " 'recipe_nutrients': {'calories': '4917 calories',\n",
888
- " 'proteinContent': '98.4 g',\n",
889
- " 'fatContent': '334.1 g',\n",
890
- " 'carbohydrateContent': '386.7 g',\n",
891
- " 'sugarContent': '233.7 g',\n",
892
- " 'sodiumContent': '2354.3 mg',\n",
893
- " 'saturatedFatContent': '195.3 g',\n",
894
- " 'cholesterolContent': '2150.2 mg',\n",
895
- " 'fiberContent': '8.6 g',\n",
896
- " 'transFatContent': '6.0 g',\n",
897
- " 'unsaturatedFatContent': '105.8 g'},\n",
898
- " 'tags': ['Dessert',\n",
899
- " 'Vegetarian',\n",
900
- " 'Pescatarian',\n",
901
- " 'Eggs',\n",
902
- " 'Cheese',\n",
903
- " 'Baked Goods',\n",
904
- " 'Baking',\n",
905
- " 'American',\n",
906
- " 'Shellfish-Free',\n",
907
- " 'Weekend Project',\n",
908
- " 'Soy-Free',\n",
909
- " 'Entertaining',\n",
910
- " 'Stand Mixer',\n",
911
- " 'Fish-Free',\n",
912
- " 'Fridge',\n",
913
- " 'Peanut-Free',\n",
914
- " 'Tree Nut-Free',\n",
915
- " 'Tomato-Free',\n",
916
- " 'Oven',\n",
917
- " 'Electrolux APAC',\n",
918
- " ''],\n",
919
- " 'id_': '0000018'},\n",
920
- " {'recipe_name': 'Keto Sausage and Egg McMuffin',\n",
921
- " 'recipe_time': 0,\n",
922
- " 'recipe_yields': '1 serving',\n",
923
- " 'recipe_ingredients': ['3 tablespoon Almond Flour',\n",
924
- " '1/2 teaspoon Psyllium Powder',\n",
925
- " '1/2 teaspoon Baking Powder',\n",
926
- " '1 pinch Salt',\n",
927
- " '1 tablespoon Butter',\n",
928
- " '1 Large Egg',\n",
929
- " '1 pound Ground Pork',\n",
930
- " '1 teaspoon Ground Sage',\n",
931
- " '1 teaspoon Dried Rosemary',\n",
932
- " '1 teaspoon Salt',\n",
933
- " '1 teaspoon Ground Black Pepper',\n",
934
- " '1/8 teaspoon Chili Powder',\n",
935
- " '1 tablespoon Olive Oil',\n",
936
- " '2 Large Egg',\n",
937
- " '1 slice Cheddar Cheese'],\n",
938
- " 'recipe_instructions': 'To make the Keto Bread, add Almond Flour (3 tablespoon), Psyllium Powder (1/2 teaspoon), and Baking Powder (1/2 teaspoon) into a jug or bowl. Season with Salt (1 pinch). Mix everything until well combined.\\nAdd in Butter (1 tablespoon) and a Large Egg (1). Mix the dry and wet ingredients really well until smooth like a cake batter.\\nNow grease a ramekin with butter and add in the mixture, smoothing out all sides and tapping the ramekin to avoid large air pockets.\\nPlace into the microwave on high for 90 seconds, when you bring it out it will have shrunk in from the sides and risen slightly. Carefully remove the muffin and leave it aside.\\nIn the same sized ramekin that you used for the bread, grease the outside with butter, crack the Large Egg (2) into it, and gently break up the yolks.\\nMicrowave the eggs on medium for 60-90 seconds. Again it will shrink away from the sides and can be easily removed.\\nFor the sausage, into a bowl add in the Salt (1 teaspoon), season with Dried Rosemary (1 teaspoon), Ground Pork (1 pound), Chili Powder (1/8 teaspoon), and finally season with Ground Sage (1 teaspoon) and Ground Black Pepper (1 teaspoon). Mix gently with a fork, don’t over mix otherwise it will become tough and dry when we cook them.\\nRoll into balls and shape into patties the size of the ramekins used for the bread and eggs.\\nIn a nonstick pan over medium to high heat, add in Olive Oil (1 tablespoon) and the sausage patty. Cook the sausage patty for 3 minutes on each side. You’re after a nice crust on the outside but juicy and tender in the middle.\\nRemove the patty and leave to rest for 1 minute.\\nIn a nonstick pan, cut the bread in half and toast the muffin.\\nTo assemble, on the muffin, place the sausage patty, the egg, Cheddar Cheese (1 slice), and top with the other half of the muffin. Serve immediately.',\n",
939
- " 'recipe_image': 'https://www.sidechef.com/recipe/2a4aec1f-edeb-49f5-be20-33477d41e5af.jpg?d=1408x1120',\n",
940
- " 'blogger': 'sidechef.com',\n",
941
- " 'recipe_nutrients': {'saturatedFatContent': '57.0 g',\n",
942
- " 'calories': '1924 calories',\n",
943
- " 'proteinContent': '109.3 g',\n",
944
- " 'fiberContent': '4.4 g',\n",
945
- " 'carbohydrateContent': '9.2 g',\n",
946
- " 'fatContent': '160.5 g',\n",
947
- " 'sugarContent': '1.0 g',\n",
948
- " 'sodiumContent': '3327.9 mg',\n",
949
- " 'transFatContent': '0 g',\n",
950
- " 'cholesterolContent': '1021.2 mg',\n",
951
- " 'unsaturatedFatContent': '79.7 g'},\n",
952
- " 'tags': ['Lunch',\n",
953
- " 'Dinner',\n",
954
- " 'Main Dish',\n",
955
- " 'Quick and Easy',\n",
956
- " 'Eggs',\n",
957
- " 'Cheese',\n",
958
- " 'Weeknight Dinners',\n",
959
- " 'Bread',\n",
960
- " '30 or Less',\n",
961
- " 'Easy',\n",
962
- " 'Quick',\n",
963
- " 'American',\n",
964
- " 'Shellfish-Free',\n",
965
- " 'Gluten-Free',\n",
966
- " 'Soy-Free',\n",
967
- " 'Game Day',\n",
968
- " \"Father's Day\",\n",
969
- " 'Microwave',\n",
970
- " 'Fish-Free',\n",
971
- " 'Peanut-Free',\n",
972
- " 'Grain-Free',\n",
973
- " 'Sugar-Free',\n",
974
- " 'Tomato-Free',\n",
975
- " 'Stove',\n",
976
- " ''],\n",
977
- " 'id_': '0000020'},\n",
978
- " {'recipe_name': 'Croque-Monsieur With Poached Eggs (Croque-Madame)',\n",
979
- " 'recipe_time': 0,\n",
980
- " 'recipe_yields': '2 servings',\n",
981
- " 'recipe_ingredients': ['2 slice Bread',\n",
982
- " '4 slice Black Forest Ham',\n",
983
- " 'to taste Gruyère Cheese',\n",
984
- " 'to taste Fresh Thyme Leaves',\n",
985
- " '1 tablespoon Butter',\n",
986
- " '2 tablespoon Onion',\n",
987
- " 'to taste Kosher Salt',\n",
988
- " '1 tablespoon All-Purpose Flour',\n",
989
- " '1 cup Milk',\n",
990
- " '1 Bay Leaf',\n",
991
- " '2 Egg',\n",
992
- " 'to taste Distilled White Vinegar',\n",
993
- " 'to taste Freshly Ground Black Pepper'],\n",
994
- " 'recipe_instructions': \"First, prepare pot for eggs: Fill a shallow saucepan with 2-3 inches water and bring to a simmer.\\nThen, prepare the béchamel: In a medium saucepan over medium heat, melt the Butter (1 tablespoon).\\nAdd the Onion (2 tablespoon) and the Kosher Salt (to taste) and cook about 5 to 7 minutes or until the onion is soft but has not begun to color.\\nTurn the heat to very low, add the All-Purpose Flour (1 tablespoon) and stir to combine it with the onion and butter.\\nContinue to cook over low heat until the flour is absorbed, stirring constantly so that it doesn't brown, about 2 minutes or so. Slowly stir in the Milk (1 cup). Drop in the Bay Leaf (1).\\nOver medium to medium-high heat, bring the mixture to a boil then reduce the heat to its lowest setting and cook for about 15 minutes, stirring occasionally to prevent the sauce from burning on the bottom of the pan.\\nTaste and cook longer if the taste of raw flour is still detectable. The mixture should be thick, but if it's too thick and becoming difficult to stir, you'll need to whisk in a little more milk. Remove the bay leaf and discard.\\nMeanwhile, preheat the broiler. Place the slices of Bread (2 slice) on a rack on a sheet pan (or a broiling pan) and broil them about a minute on each side. Remove pan from the oven.\\nSpread about 1 tablespoon of béchamel over each slice of bread. Top with Black Forest Ham (4 slice). Top with Gruyère Cheese (to taste). Set aside.\\nCrack Egg (2) into a small bowl or ramekin. Add Distilled White Vinegar (to taste) into the pot of simmering shallow water. Adjust the heat so that the water is barely simmering — get the water to a simmer, then turn it down so you don't see any bubbles.\\nUse the handle of a wooden spoon to make a whirlpool in the water, then drop one egg into the center of the whirlpool. Repeat with the other egg. Adjust the heat to keep the water just below a simmer. Set the timer for 3 minutes.\\nWhen the eggs have cooked for 3 minutes, place the toasts under the broiler and cook until the cheese is bubbling and starting to brown. Remove from the oven. Sprinkle with the Fresh Thyme Leaves (to taste).\\nMeanwhile, using a slotted spoon, lift one egg up from the water and shake it. The yolk should jiggle a little bit, but shouldn't look too loose. When the eggs look cooked to your liking, remove them with a slotted spoon and transfer to a paper towel-lined plate.\\nTop each sandwich with a poached egg. Sprinkle with a pinch of Kosher Salt (to taste) and fresh Freshly Ground Black Pepper (to taste).\",\n",
995
- " 'recipe_image': 'https://www.sidechef.com/recipe/e74cae36-f763-473b-b714-10a7a8e8915c.jpg?d=1408x1120',\n",
996
- " 'blogger': 'sidechef.com',\n",
997
- " 'recipe_nutrients': {'calories': '191 calories',\n",
998
- " 'proteinContent': '11.2 g',\n",
999
- " 'fatContent': '7.9 g',\n",
1000
- " 'carbohydrateContent': '18.1 g',\n",
1001
- " 'fiberContent': '0.8 g',\n",
1002
- " 'sugarContent': '5.4 g',\n",
1003
- " 'sodiumContent': '318.0 mg',\n",
1004
- " 'saturatedFatContent': '3.7 g',\n",
1005
- " 'transFatContent': '0.0 g',\n",
1006
- " 'cholesterolContent': '113.8 mg',\n",
1007
- " 'unsaturatedFatContent': '2.9 g'},\n",
1008
- " 'tags': ['Sandwich',\n",
1009
- " 'Breakfast',\n",
1010
- " 'Lunch',\n",
1011
- " 'Brunch',\n",
1012
- " 'Main Dish',\n",
1013
- " 'Low-Carb',\n",
1014
- " 'Eggs',\n",
1015
- " 'Sauce',\n",
1016
- " 'American',\n",
1017
- " 'Shellfish-Free',\n",
1018
- " 'Advanced',\n",
1019
- " 'French',\n",
1020
- " 'Soy-Free',\n",
1021
- " 'Intermediate',\n",
1022
- " 'Fall',\n",
1023
- " 'Winter',\n",
1024
- " 'Fish-Free',\n",
1025
- " 'Peanut-Free',\n",
1026
- " 'Tree Nut-Free',\n",
1027
- " 'Sugar-Free',\n",
1028
- " 'International',\n",
1029
- " 'Tomato-Free',\n",
1030
- " 'Oven',\n",
1031
- " 'Stove',\n",
1032
- " ''],\n",
1033
- " 'id_': '0000022'},\n",
1034
- " {'recipe_name': 'Tacottata',\n",
1035
- " 'recipe_time': 0,\n",
1036
- " 'recipe_yields': '4 servings',\n",
1037
- " 'recipe_ingredients': ['1 pound Tortilla Chips',\n",
1038
- " '1 pound Lean Ground Beef',\n",
1039
- " '1 carton Egg Beaters',\n",
1040
- " '10 ounce Queso Fresco',\n",
1041
- " '1 cup Shredded Cheddar Cheese',\n",
1042
- " '1 can Ro-Tel® Diced Tomatoes & Green Chilies',\n",
1043
- " '1/2 cup Onion',\n",
1044
- " '1/2 cup Egg Beaters',\n",
1045
- " '1 package Taco Seasoning',\n",
1046
- " 'to taste Lettuce',\n",
1047
- " '1/4 cup Canned Diced Tomatoes',\n",
1048
- " '1 cup Sour Cream',\n",
1049
- " '1 Jalapeño Pepper',\n",
1050
- " 'to taste Fresh Cilantro',\n",
1051
- " 'to taste Hot Sauce',\n",
1052
- " 'as needed Nonstick Cooking Spray',\n",
1053
- " 'to taste Salt',\n",
1054
- " 'to taste Ground Black Pepper'],\n",
1055
- " 'recipe_instructions': 'In a medium-large pan, cook up the Lean Ground Beef (1 pound) with some Salt (to taste) and Ground Black Pepper (to taste).\\nAdd in the Onion (1/2 cup) and Taco Seasoning (1 package). Set aside to cool completely.\\nIn a food processor (or with a zip bag and aggression) crush up the Tortilla Chips (1 pound) into coarse crumbs.\\nAdd in the Egg Beaters (1/2 cup) and pulse to combine.\\nSpray your frittata pan with Nonstick Cooking Spray (as needed) and gently press the Tortilla crumbs evenly into the pan, working them up the sides. Bake it in the oven at 350 degrees F (180 degrees C) for about 10-12 minutes, just until it’s slightly golden. Set aside.\\nAdd in the Jalapeño Pepper (1/2) as well, if you prefer extra heat.\\nFold in half of the Queso Fresco (10 ounce), and Shredded Cheddar Cheese (1 cup).\\nNow in a large bowl combine the Egg Beaters (1 carton) with the fully-cooked seasoned meat.\\nStir in the (drained) can of Ro-Tel® Diced Tomatoes & Green Chilies (1 can) and mix it all up. Pour the egg mixture into the tortilla crust, making sure not to go all the way up to the top (leave maybe a 1.8-inch lip around).\\nBake it at 350 degrees F (180 degrees C) for about 20 minutes, or until the eggs are slightly golden on top and firm all the way through.\\nNow, top it with the Sour Cream (1 cup), Fresh Cilantro (to taste), more queso fresco cheese, Jalapeño Pepper (1/2), Lettuce (to taste), some crumbled chips, and Canned Diced Tomatoes (1/4 cup). Serve with some Hot Sauce (to taste).',\n",
1056
- " 'recipe_image': 'https://www.sidechef.com/recipe/c8377cec-841c-419b-a848-a341adf987d7.jpeg?d=1408x1120',\n",
1057
- " 'blogger': 'sidechef.com',\n",
1058
- " 'recipe_nutrients': {'calories': '334 calories',\n",
1059
- " 'proteinContent': '20.6 g',\n",
1060
- " 'fatContent': '16.8 g',\n",
1061
- " 'carbohydrateContent': '24.9 g',\n",
1062
- " 'fiberContent': '1.8 g',\n",
1063
- " 'sugarContent': '2.4 g',\n",
1064
- " 'sodiumContent': '598.7 mg',\n",
1065
- " 'saturatedFatContent': '7.4 g',\n",
1066
- " 'transFatContent': '0.5 g',\n",
1067
- " 'cholesterolContent': '47.7 mg',\n",
1068
- " 'unsaturatedFatContent': '7.4 g'},\n",
1069
- " 'tags': ['Lunch',\n",
1070
- " 'Dinner',\n",
1071
- " 'Brunch',\n",
1072
- " 'Appetizers',\n",
1073
- " 'Main Dish',\n",
1074
- " 'Beef',\n",
1075
- " 'Eggs',\n",
1076
- " 'Cheese',\n",
1077
- " 'Vegetables',\n",
1078
- " 'Baking',\n",
1079
- " 'American',\n",
1080
- " 'Shellfish-Free',\n",
1081
- " 'Weekend Project',\n",
1082
- " 'Egg-Free',\n",
1083
- " 'Soy-Free',\n",
1084
- " 'Intermediate',\n",
1085
- " 'Entertaining',\n",
1086
- " 'Fish-Free',\n",
1087
- " 'Peanut-Free',\n",
1088
- " 'Tree Nut-Free',\n",
1089
- " 'Sugar-Free',\n",
1090
- " 'Oven',\n",
1091
- " 'Stove',\n",
1092
- " ''],\n",
1093
- " 'id_': '0000024'},\n",
1094
- " {'recipe_name': 'Egg in a Hole',\n",
1095
- " 'recipe_time': 0,\n",
1096
- " 'recipe_yields': '1 serving',\n",
1097
- " 'recipe_ingredients': ['1 tablespoon Salted Butter',\n",
1098
- " '1 slice Whole Wheat Bread',\n",
1099
- " '1 Egg',\n",
1100
- " 'to taste Salt',\n",
1101
- " 'to taste Ground Black Pepper'],\n",
1102
- " 'recipe_instructions': 'Heat Salted Butter (1 tablespoon) in a small skillet over medium heat.\\nUse a round cookie cutter or biscuit cutter to cut a hole out of the Whole Wheat Bread (1 slice).\\nOnce butter has fully melted and has begun to bubble slightly, place the bread into the skillet and the center piece to the side. Carefully crack the Egg (1) into the hole in the bread.\\nSprinkle a TINY bit of Salt (to taste) and Ground Black Pepper (to taste) on the egg. Cook for about two minutes.\\nThen carefully slide a spatula under the bread and flip. Sprinkle a bit more pepper on the second side, then flip the cut-out circle to grill the other side.\\nLift the bread onto a plate and eat. Use your little center circle to soak up the warm, luscious yolk!',\n",
1103
- " 'recipe_image': 'https://www.sidechef.com/recipe/9962c2f5-ad92-4acb-a176-4255f1cda802.jpg?d=1408x1120',\n",
1104
- " 'blogger': 'sidechef.com',\n",
1105
- " 'recipe_nutrients': {'calories': '298 calories',\n",
1106
- " 'proteinContent': '12.5 g',\n",
1107
- " 'fatContent': '17.9 g',\n",
1108
- " 'carbohydrateContent': '21.7 g',\n",
1109
- " 'sugarContent': '2.4 g',\n",
1110
- " 'sodiumContent': '388.3 mg',\n",
1111
- " 'saturatedFatContent': '9.2 g',\n",
1112
- " 'transFatContent': '0.5 g',\n",
1113
- " 'cholesterolContent': '212.8 mg',\n",
1114
- " 'fiberContent': '3.0 g',\n",
1115
- " 'unsaturatedFatContent': '7.2 g'},\n",
1116
- " 'tags': ['Breakfast',\n",
1117
- " 'Brunch',\n",
1118
- " 'Vegetarian',\n",
1119
- " 'Low-Carb',\n",
1120
- " 'Pescatarian',\n",
1121
- " 'Eggs',\n",
1122
- " 'Kid-Friendly',\n",
1123
- " 'Easy',\n",
1124
- " 'Quick',\n",
1125
- " 'American',\n",
1126
- " 'Shellfish-Free',\n",
1127
- " 'Beginner',\n",
1128
- " 'Soy-Free',\n",
1129
- " 'Fish-Free',\n",
1130
- " 'Peanut-Free',\n",
1131
- " 'Tree Nut-Free',\n",
1132
- " 'Sugar-Free',\n",
1133
- " 'Tomato-Free',\n",
1134
- " 'Stove',\n",
1135
- " ''],\n",
1136
- " 'id_': '0000037'}]"
1137
- ]
1138
- },
1139
- "execution_count": 173,
1140
- "metadata": {},
1141
- "output_type": "execute_result"
1142
- }
1143
- ],
1144
- "source": [
1145
- "def filter_recipes(file_name):\n",
1146
- " with open(file_name, 'r') as file:\n",
1147
- " recipes = json.load(file)\n",
1148
- "\n",
1149
- " high_protein_recipes = []\n",
1150
- " for recipe in recipes:\n",
1151
- " protein_content = recipe['recipe_nutrients'].get('proteinContent', '0 g')\n",
1152
- " protein_value = float(protein_content.split(' ')[0])\n",
1153
- " if protein_value > 10:\n",
1154
- " high_protein_recipes.append(recipe)\n",
1155
- " if len(high_protein_recipes) >= 10:\n",
1156
- " break\n",
1157
- "\n",
1158
- " return high_protein_recipes\n",
1159
- "\n",
1160
- "\n",
1161
- "filter_recipes(\"recipes.json\")"
1162
- ]
1163
- }
1164
- ],
1165
- "metadata": {
1166
- "kernelspec": {
1167
- "display_name": "chatbot-env",
1168
- "language": "python",
1169
- "name": "python3"
1170
- },
1171
- "language_info": {
1172
- "codemirror_mode": {
1173
- "name": "ipython",
1174
- "version": 3
1175
- },
1176
- "file_extension": ".py",
1177
- "mimetype": "text/x-python",
1178
- "name": "python",
1179
- "nbconvert_exporter": "python",
1180
- "pygments_lexer": "ipython3",
1181
- "version": "3.9.6"
1182
- }
1183
- },
1184
- "nbformat": 4,
1185
- "nbformat_minor": 2
1186
- }