Ritesh-hf commited on
Commit
1233717
·
verified ·
1 Parent(s): 1d11079

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +98 -321
app.py CHANGED
@@ -12,13 +12,11 @@ from pathlib import Path
12
  import torch
13
  import torch.nn.functional as F
14
 
15
- # from src.data.embs import ImageDataset
16
  from src.model.blip_embs import blip_embs
17
  from src.data.transforms import transform_test
18
 
19
- from transformers import StoppingCriteria, StoppingCriteriaList, TextIteratorStreamer
20
  import gradio as gr
21
- # import spaces
22
 
23
  from langchain.chains import ConversationChain
24
  from langchain_community.chat_message_histories import ChatMessageHistory
@@ -35,7 +33,6 @@ from flask_socketio import SocketIO, emit
35
  import json
36
  from openai import OpenAI
37
 
38
- # GROQ_API_KEY = os.getenv("GROQ_API_KEY")
39
  load_dotenv(".env")
40
  USER_AGENT = os.getenv("USER_AGENT")
41
  GROQ_API_KEY = os.getenv("GROQ_API_KEY")
@@ -55,7 +52,6 @@ app.config['MAX_CONTENT_LENGTH'] = 1024 * 1024 * 1024
55
  socketio = SocketIO(app, cors_allowed_origins="*", logger=True, max_http_buffer_size=1024 * 1024 * 1024)
56
  app.config['SECRET_KEY'] = SECRET_KEY
57
 
58
-
59
  # Initialize LLM
60
  llm = ChatGroq(model="llama-3.1-8b-instant", temperature=0, max_tokens=1024, max_retries=2)
61
 
@@ -65,7 +61,7 @@ json_llm = ChatGroq(model="llama-3.1-70b-versatile", temperature=0, max_tokens=1
65
  # Initialize Router
66
  router = ChatGroq(model="llama-3.2-3b-preview", temperature=0, max_tokens=1024, max_retries=2, model_kwargs={"response_format": {"type": "json_object"}})
67
 
68
- # Initialize Router
69
  answer_formatter = ChatGroq(model="llama-3.1-8b-instant", temperature=0, max_tokens=1024, max_retries=2)
70
 
71
  # Initialized recommendation LLM
@@ -180,66 +176,30 @@ class Chat:
180
  def ask(self):
181
  return json.dumps(self.target_recipe.to_json())
182
 
183
-
184
-
185
  chat = Chat(model,transform,df,tar_img_feats, device)
186
 
187
-
188
  def answer_generator(formated_input, session_id):
189
  # QA system prompt and chain
190
  qa_system_prompt = """
191
- 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 context and user queries.
192
- Operational Guidelines: \n
193
- 1. Input Structure: \n
194
- - Context: You may receive contextual information related to recipes, such as specific recipe name, ingredients, nutritional informations, intsructions, recipe tags, or previously selected dishes. \n
195
- - User Query: Users will pose questions or requests related to recipes, nutritional information, ingredient, cooking instructions, and more. \n
196
- 2. Response Strategy: \n
197
- - 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
198
- - Respond to User Query Directly: If the context does not contain the necessary information to answer the user's query, kindly state that you do not have the required information. \n
199
- Output Format: \n
200
- - The output format should be JSON.
201
- - The output should have a key 'header' with response message header such as "Here is your ....",
202
- - Then there should be other key with the actual response information. If the user query asks recipe ingredients then the key should be named "ingredients" with
203
- JSON object as its value. The JSON object should have ingredient and its measurement as key-value pairs. Similarly if user asked for nutritional information then the output should have 'header' key with header text and 'nutrients' key
204
- with a JSON object og nutrient and its content as key-value pairs. Similarly if the user query asks for recipe instructions then JSON output should include 'header key with header text and
205
- 'instructions' key with a list of instructions as its value.
206
- Following are the output formats for some cases:
207
- 1. if user query asks for all recipe information, then output should be of following format:
208
- {
209
- header: header text,
210
- recipe_name: Recipe Name,
211
- recipe_instructions: List of recipe instructions,
212
- recipe_nutrients: key-value pairs of nutrients name and its content,
213
- recipe_ingredients: key-value pairs of ingredients name and its content,
214
- recipe_tags: List of tags related to recipe,
215
- .
216
- .
217
- .
218
- }
219
-
220
- 2. if user query asks for recipe nutrients information, then output should be of following format:
221
- {
222
- header: header text,
223
- recipe_nutrients: key-value pairs of nutrients name and its content.
224
- }
225
-
226
- 3. if user query asks for recipe instructions information, then output should be of following format:
227
- {
228
- header: header text,
229
- recipe_instructions: List of recipe instructions,
230
- }
231
- 4. if user query asks for recipe instructions information, then output should be of following format:
232
- {
233
- header: header text,
234
- recipe_instructions: List of recipe instructions,
235
- }
236
  Additional Instructions:
237
- - Precision and Personalization: Always aim to provide precise, personalized, and relevant information to users based on both the provided context and their specific queries.
238
- - Clarity and Coherence: Ensure all responses are clear, well-structured, and easy to understand, facilitating a seamless user experience.
239
- - Substitute Suggestions: Consider user preferences and dietary restrictions outlined in the context or user query when suggesting ingredient substitutes.
240
- - 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.
241
- - Don't mention about the context in the response, format the answer in a natural and friendly way.
242
-
243
  """
244
  qa_prompt = ChatPromptTemplate.from_messages(
245
  [
@@ -263,119 +223,26 @@ def answer_generator(formated_input, session_id):
263
 
264
  return response
265
 
266
-
267
  def json_answer_generator(user_query, context):
268
  system_prompt = """
269
- Given a recipe context in JSON format, respond to user queries by extracting and returning the requested information in JSON format with an additional `"header"` key containing a response starter. Use the following rules:
270
- 1. **Recipe Information Extraction**:
271
- - If the user query explicitly requests specific recipe data (e.g., ingredients, nutrients, or instructions), return only those JSON objects from the provided recipe context.
272
- - For example, if the user asks, “What are the ingredients?” or “Show me the nutrient details,” your output should be limited to only the requested JSON objects (e.g., `recipe_ingredients`, `recipe_nutrients`).
273
- - Include `"header": "Here is the information you requested:"` at the start of each response.
274
- 2. **Multiple Information Points**:
275
- - If a user query asks for more than one piece of information, return each requested JSON object from the recipe context in a combined JSON response.
276
- - For example, if the query is “Give me the ingredients and instructions,” the output should include both `recipe_ingredients` and `recipe_instructions` objects.
277
- - Include `"header": "Here is the information you requested:"` at the start of each response.
278
- 3. **Non-Specific Recipe Information**:
279
- - If the query does not directly refer to recipe data but instead asks for a general response based on the context, return a JSON object with a single key `"content"` and a descriptive response as its value.
280
- - Include `"header": "Here is a suggestion based on the recipe:"` as the response starter.
281
- - For example, if the query is “How can I use this recipe for a healthy lunch?” return a response like:
282
- ```json
283
- {
284
- "header": "Here is a suggestion based on the recipe:",
285
- "content": "This Asian Potato Salad with Seven Minute Egg is a nutritious and light option, ideal for a balanced lunch. It provides protein and essential nutrients with low calories."
286
- }
287
- ```
288
- **Example Context**:
289
- ```json
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. Add the Multi-Colored Fingerling Potato...",
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
- "Easy",
335
- "Dairy-Free",
336
- "Shellfish-Free",
337
- "Entertaining",
338
- "Fish-Free",
339
- "Peanut-Free",
340
- "Tree Nut-Free",
341
- "Sugar-Free",
342
- "Global",
343
- "Tomato-Free",
344
- "Stove",
345
- ""
346
- ],
347
- "id_": "0000001"
348
- }
349
- **Example Query & Output**:
350
- **Query**: "What are the ingredients and calories?"
351
- **Output**:
352
- ```json
353
- {
354
- "header": "Here is the information you requested:",
355
- "recipe_ingredients": [
356
- "2 1/2 cup Multi-Colored Fingerling Potato",
357
- "3/4 cup Celery",
358
- "1/4 cup Red Onion",
359
- "2 tablespoon Fresh Parsley",
360
- "1/3 cup Mayonnaise",
361
- "1 tablespoon Chili Garlic Sauce",
362
- "1 teaspoon Hoisin Sauce",
363
- "1 splash Soy Sauce",
364
- "to taste Salt",
365
- "to taste Ground Black Pepper",
366
- "4 Egg"
367
- ],
368
- "recipe_nutrients": {
369
- "calories": "80 calories"
370
- }
371
- }
372
- Try to format the output as JSON object with key value pairs.
373
  """
374
 
375
  formatted_input = f"""
376
- User Query: {user_query}
377
- Recipe data as Context:
378
- {context}
379
  """
380
  response = json_llm.invoke(
381
  [SystemMessage(content=system_prompt)]
@@ -388,19 +255,13 @@ def json_answer_generator(user_query, context):
388
  res = json.loads(response.content)
389
  return res
390
 
391
-
392
-
393
-
394
- ### Router
395
- import json
396
- from langchain_core.messages import HumanMessage, SystemMessage
397
-
398
  def router_node(query):
399
  # Prompt
400
- 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:
401
- 1. Retrieval: Fetch information based on user's chat history and current query.
402
- 2. Recommendation/Suggestion: Recommend recipes to users based on the query.
403
- Return a JSON response with a single key named “task” indicating either “retrieval” or “recommendation” based on your decision.
 
404
  """
405
  response = router.invoke(
406
  [SystemMessage(content=router_instructions)]
@@ -415,9 +276,11 @@ def router_node(query):
415
 
416
  def recommendation_node(query):
417
  prompt = """
418
- You are a helpful assistant that writes Python code to filter recipes from a JSON filr based o the user query. \n
419
- JSON file path = 'recipes.json' \n
420
- The JSON file is a list of recipes with the following structure: \n
 
 
421
  {
422
  "recipe_name": string,
423
  "recipe_time": integer,
@@ -428,72 +291,25 @@ def recommendation_node(query):
428
  "blogger": string,
429
  "recipe_nutrients": JSON object with key-value pairs such as "protein: 10g",
430
  "tags": list of tags related to a recipe
431
- } \n
432
- Here is the example of a recipe json object from the JSON data: \n
433
- {
434
- "recipe_name": "Asian Potato Salad with Seven Minute Egg",
435
- "recipe_time": 0,
436
- "recipe_yields": "4 servings",
437
- "recipe_ingredients": [
438
- "2 1/2 cup Multi-Colored Fingerling Potato",
439
- "3/4 cup Celery",
440
- "1/4 cup Red Onion",
441
- "2 tablespoon Fresh Parsley",
442
- "1/3 cup Mayonnaise",
443
- "1 tablespoon Chili Garlic Sauce",
444
- "1 teaspoon Hoisin Sauce",
445
- "1 splash Soy Sauce",
446
- "to taste Salt",
447
- "to taste Ground Black Pepper",
448
- "4 Egg"
449
- ],
450
- "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!",
451
- "recipe_image": "https://www.sidechef.com/recipe/eeeeeceb-493e-493d-8273-66c800821b13.jpg?d=1408x1120",
452
- "blogger": "sidechef.com",
453
- "recipe_nutrients": {
454
- "calories": "80 calories",
455
- "proteinContent": "2.1 g",
456
- "fatContent": "6.2 g",
457
- "carbohydrateContent": "3.9 g",
458
- "fiberContent": "0.5 g",
459
- "sugarContent": "0.4 g",
460
- "sodiumContent": "108.0 mg",
461
- "saturatedFatContent": "1.2 g",
462
- "transFatContent": "0.0 g",
463
- "cholesterolContent": "47.4 mg",
464
- "unsaturatedFatContent": "3.8 g"
465
- },
466
- "tags": [
467
- "Salad",
468
- "Lunch",
469
- "Brunch",
470
- "Appetizers",
471
- "Side Dish",
472
- "Budget-Friendly",
473
- "Vegetarian",
474
- "Pescatarian",
475
- "Eggs",
476
- "Potatoes",
477
- "Dairy-Free",
478
- "Shellfish-Free"
479
- ]
480
- } \n
481
- 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
482
  Recipe filtering instructions:
483
  - If a user asked for the highest nutrient recipe such as "high protein or high calories" then filtered recipes should be the top highest recipes from all the recipes with high nutrient.
484
- - sort or rearrange recipes based which recipes are more appropriate for the user.
 
485
  Your output instructions:
486
  - The function name should be filter_recipes. The input to the function should be file name.
487
  - The length of output recipes should not be more than 6.
488
- - Only give me output function. Do not call the function.
489
- - Give the python function as a key named "code" in a json format.
490
  - Do not include any other text with the output, only give python code.
491
  - If you do not follow the above given instructions, the chat may be terminated.
492
  """
493
  max_tries = 3
494
  while True:
495
  try:
496
- # llm = ChatGroq(model="llama-3.1-8b-instant", temperature=0, max_tokens=1024, max_retries=2, model_kwargs={"response_format": {"type": "json_object"}})
497
  response = client.chat.completions.create(
498
  model="gpt-4o-mini",
499
  messages=[
@@ -521,10 +337,9 @@ def recommendation_node(query):
521
  max_tries -= 1
522
  return filtered_recipes
523
 
524
-
525
  def answer_formatter_node(question, context):
526
- prompt = f""" You are an highly clever question-answering assistant trained to provide clear and concise answers based on the user query and provided context.
527
- Your task is to generated answers for the user query based on the context provided.
528
  Instructions for your response:
529
  1. Directly answer the user query using only the information provided in the context.
530
  2. Ensure your response is clear and concise.
@@ -543,19 +358,16 @@ def answer_formatter_node(question, context):
543
  return res
544
 
545
  def reguar_answer_node(question, context):
546
- prompt = f""" You are an highly clever question-answering assistant trained to provide clear and concise answers based on the user query and provided context.
547
- Your task is to generated answers for the user query based on the context provided.
548
  Instructions for your response:
549
  1. Directly answer the user query. Make use of provided context if necessary.
550
  2. Ensure your response is clear and concise.
551
- 3. Give the answer in json format with a single key named 'content' with value as your response.
552
- 4. It is important to give response in json format, otherwise the chat may terminate.
553
-
554
  Please format an answer based on the following user question and context provided:
555
-
556
  User Question:
557
  {question}
558
-
559
  Context:
560
  {context}
561
  """
@@ -565,14 +377,27 @@ def reguar_answer_node(question, context):
565
  res = response.content
566
  return res
567
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
568
  CURR_CONTEXT = ''
569
 
570
- # @spaces.GPU
571
  def get_answer(image=[], message='', sessionID='abc123'):
572
  global CURR_CONTEXT
573
  if len(image) > 0:
574
  try:
575
- # Process the image and message here
576
  device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
577
  chat = Chat(model,transform,df,tar_img_feats, device)
578
  chat.encode_image(image)
@@ -587,29 +412,34 @@ def get_answer(image=[], message='', sessionID='abc123'):
587
  print(e)
588
  response = {'content':"An error occurred while processing your request."}
589
  elif len(image) == 0 and message is not None:
590
- print("I am here")
591
  task = router_node(message)
592
  if task == 'retrieval':
 
 
 
 
 
 
593
  recipes = recommendation_node(message)
594
- print(recipes)
595
  if not recipes:
596
  response = {'content':"An error occurred while processing your request."}
597
  response = answer_formatter_node(message, recipes)
 
 
 
 
598
  else:
599
- formated_input = {
600
- 'input': message,
601
- 'context': CURR_CONTEXT
602
- }
603
- response = json_answer_generator(message, data)
604
 
605
  return response
606
 
607
  # Function to handle WebSocket connection
608
  @socketio.on('ping')
609
- def handle_connect():
610
  emit('Ping-return', {'message': 'Connected'}, room=request.sid)
611
 
612
-
613
  # Function to handle WebSocket connection
614
  @socketio.on('connect')
615
  def handle_connect():
@@ -620,9 +450,7 @@ def handle_connect():
620
  def handle_disconnect():
621
  print(f"Client disconnected: {request.sid}")
622
 
623
- import json
624
  import base64
625
- from PIL import Image
626
  from io import BytesIO
627
  import torchvision.transforms as transforms
628
 
@@ -655,7 +483,6 @@ def handle_message(data):
655
  if session_store[session_id]['image_data'] or session_store[session_id]['message']:
656
  try:
657
  image_bytes = session_store[session_id]['image_data']
658
- # print("checkpoint 2")
659
  if isinstance(image_bytes, str):
660
  image_bytes = base64.b64decode(image_bytes)
661
  image = Image.open(BytesIO(image_bytes))
@@ -670,7 +497,6 @@ def handle_message(data):
670
  'input': message,
671
  'context': json.dumps(context)
672
  }
673
- # Invoke question_answer_chain and stream the response
674
  response = json_answer_generator(message, context)
675
  emit('response', response, room=session_id)
676
 
@@ -687,74 +513,25 @@ def handle_message(data):
687
  if task == 'retrieval':
688
  formated_input = {
689
  'input': message,
690
- 'context': json.dumps(CURR_CONTEXT)
691
  }
692
  response = json_answer_generator(message, CURR_CONTEXT)
693
  emit('response', response, room=session_id)
694
  elif task == "recommendation":
695
- response = recommendation_node(message)
696
- # response = answer_formatter_node(message, recipes)
697
- if response is None:
698
  response = {'content':"An error occurred while processing your request."}
699
-
700
  emit('json_response', response, room=session_id)
701
- else:
702
- response = reguar_answer_node(message, CURR_CONTEXT)
703
  if response is None:
704
  response = {'content':"An error occurred while processing your request."}
705
  emit('json_response', response, room=session_id)
 
 
 
706
  session_store.pop(session_id, None)
707
-
708
-
709
-
710
- import requests
711
- from PIL import Image
712
- import numpy as np
713
- from io import BytesIO
714
-
715
- def download_image_to_numpy(url):
716
- print("Image URL: ", url)
717
- # Send a GET request to the URL to download the image
718
- response = requests.get(url)
719
-
720
- # Check if the request was successful
721
- if response.status_code == 200:
722
- # Open the image using PIL and convert it to RGB format
723
- image = Image.open(BytesIO(response.content)).convert('RGB')
724
-
725
- # Convert the image to a NumPy array
726
- image_array = np.array(image)
727
-
728
- return image_array
729
- else:
730
- raise Exception(f"Failed to download image. Status code: {response.status_code}")
731
-
732
- import base64
733
- import numpy as np
734
- from io import BytesIO
735
- from PIL import Image
736
-
737
- def base64_to_numpy(base64_string):
738
- # Decode the base64 string
739
- image_data = base64.b64decode(base64_string)
740
-
741
- # Convert the byte data to a PIL image
742
- image = Image.open(BytesIO(image_data))
743
-
744
- # Convert the PIL image to a NumPy array
745
- image_np = np.array(image)
746
-
747
- return image_np
748
-
749
- @socketio.on('example')
750
- def handle_message(data):
751
- img_url = data['img_url']
752
- message = data['message']
753
- session_id = request.sid
754
- image_array = base64_to_numpy(img_url)
755
- response = get_answer(image=image_array, message=message, sessionID=request.sid)
756
- emit('response', response, room=session_id)
757
- return response
758
 
759
  # Home route
760
  @app.route("/")
@@ -763,4 +540,4 @@ def index_view():
763
 
764
  # Main function to run the app
765
  if __name__ == '__main__':
766
- socketio.run(app, debug=True)
 
12
  import torch
13
  import torch.nn.functional as F
14
 
 
15
  from src.model.blip_embs import blip_embs
16
  from src.data.transforms import transform_test
17
 
18
+ from transformers import StoppingCriteria, StoppingCriteriaList
19
  import gradio as gr
 
20
 
21
  from langchain.chains import ConversationChain
22
  from langchain_community.chat_message_histories import ChatMessageHistory
 
33
  import json
34
  from openai import OpenAI
35
 
 
36
  load_dotenv(".env")
37
  USER_AGENT = os.getenv("USER_AGENT")
38
  GROQ_API_KEY = os.getenv("GROQ_API_KEY")
 
52
  socketio = SocketIO(app, cors_allowed_origins="*", logger=True, max_http_buffer_size=1024 * 1024 * 1024)
53
  app.config['SECRET_KEY'] = SECRET_KEY
54
 
 
55
  # Initialize LLM
56
  llm = ChatGroq(model="llama-3.1-8b-instant", temperature=0, max_tokens=1024, max_retries=2)
57
 
 
61
  # Initialize Router
62
  router = ChatGroq(model="llama-3.2-3b-preview", temperature=0, max_tokens=1024, max_retries=2, model_kwargs={"response_format": {"type": "json_object"}})
63
 
64
+ # Initialize answer formatter
65
  answer_formatter = ChatGroq(model="llama-3.1-8b-instant", temperature=0, max_tokens=1024, max_retries=2)
66
 
67
  # Initialized recommendation LLM
 
176
  def ask(self):
177
  return json.dumps(self.target_recipe.to_json())
178
 
 
 
179
  chat = Chat(model,transform,df,tar_img_feats, device)
180
 
 
181
  def answer_generator(formated_input, session_id):
182
  # QA system prompt and chain
183
  qa_system_prompt = """
184
+ You are an AI assistant developed by Nutrigenics AI. Your purpose is to help users by providing accurate and relevant answers to their questions.
185
+ Operational Guidelines:
186
+
187
+ 1. Input Structure:
188
+ - Context: You may receive contextual information related to recipes or other topics.
189
+ - User Query: Users will pose questions or requests on various topics.
190
+
191
+ 2. Response Strategy:
192
+ - Utilize Provided Context: If the context contains relevant information that addresses the user's query, base your response on this provided data.
193
+ - Respond to User Query Directly: If the context does not contain the necessary information, answer the question to the best of your ability.
194
+
195
+ Output Format:
196
+ - Provide clear and concise answers.
197
+ - Format your response in JSON with a key 'content' containing your answer.
198
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
199
  Additional Instructions:
200
+ - Precision and Personalization: Always aim to provide precise, personalized, and relevant information.
201
+ - Clarity and Coherence: Ensure all responses are clear, well-structured, and easy to understand.
202
+ - Do not mention about the context in the response, format the answer in a natural and friendly way.
 
 
 
203
  """
204
  qa_prompt = ChatPromptTemplate.from_messages(
205
  [
 
223
 
224
  return response
225
 
 
226
  def json_answer_generator(user_query, context):
227
  system_prompt = """
228
+ Given a context in JSON format, respond to user queries by extracting and returning the requested information in JSON format with an additional `"header"` key containing a response starter. Use the following rules:
229
+
230
+ 1. **Information Extraction**:
231
+ - If the user query explicitly requests specific data (e.g., ingredients, nutrients, or instructions), return only those JSON objects from the provided context.
232
+ - Include `"header": "Here is the information you requested:"` at the start of each response.
233
+
234
+ 2. **General Responses**:
235
+ - If the query is not directly related to the context, provide a helpful and accurate answer.
236
+ - Include `"header": "Here is your answer:"` at the start of the response.
237
+ - Return a JSON object with a single key `"content"` and your response as its value.
238
+
239
+ Try to format the output as a JSON object with key-value pairs.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
240
  """
241
 
242
  formatted_input = f"""
243
+ User Query: {user_query}
244
+ Context:
245
+ {context}
246
  """
247
  response = json_llm.invoke(
248
  [SystemMessage(content=system_prompt)]
 
255
  res = json.loads(response.content)
256
  return res
257
 
 
 
 
 
 
 
 
258
  def router_node(query):
259
  # Prompt
260
+ 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 three available tasks:
261
+ 1. Retrieval: Fetch information based on user's chat history and current query.
262
+ 2. Recommendation/Suggestion: Recommend recipes to users based on the query.
263
+ 3. General: Answer general questions not related to recipes or the current context.
264
+ Return a JSON response with a single key named “task” indicating either “retrieval”, “recommendation”, or “general” based on your decision.
265
  """
266
  response = router.invoke(
267
  [SystemMessage(content=router_instructions)]
 
276
 
277
  def recommendation_node(query):
278
  prompt = """
279
+ You are a helpful assistant that writes Python code to filter recipes from a JSON file based on the user query.
280
+
281
+ JSON file path = 'recipes.json'
282
+
283
+ The JSON file is a list of recipes with the following structure:
284
  {
285
  "recipe_name": string,
286
  "recipe_time": integer,
 
291
  "blogger": string,
292
  "recipe_nutrients": JSON object with key-value pairs such as "protein: 10g",
293
  "tags": list of tags related to a recipe
294
+ }
295
+
296
+ 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.
297
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
298
  Recipe filtering instructions:
299
  - If a user asked for the highest nutrient recipe such as "high protein or high calories" then filtered recipes should be the top highest recipes from all the recipes with high nutrient.
300
+ - Sort or rearrange recipes based on which recipes are more appropriate for the user.
301
+
302
  Your output instructions:
303
  - The function name should be filter_recipes. The input to the function should be file name.
304
  - The length of output recipes should not be more than 6.
305
+ - Only give me the output function. Do not call the function.
306
+ - Give the python function as a key named "code" in a JSON format.
307
  - Do not include any other text with the output, only give python code.
308
  - If you do not follow the above given instructions, the chat may be terminated.
309
  """
310
  max_tries = 3
311
  while True:
312
  try:
 
313
  response = client.chat.completions.create(
314
  model="gpt-4o-mini",
315
  messages=[
 
337
  max_tries -= 1
338
  return filtered_recipes
339
 
 
340
  def answer_formatter_node(question, context):
341
+ prompt = f"""You are a highly clever question-answering assistant trained to provide clear and concise answers based on the user query and provided context.
342
+ Your task is to generate answers for the user query based on the context provided.
343
  Instructions for your response:
344
  1. Directly answer the user query using only the information provided in the context.
345
  2. Ensure your response is clear and concise.
 
358
  return res
359
 
360
  def reguar_answer_node(question, context):
361
+ prompt = f"""You are a highly clever question-answering assistant trained to provide clear and concise answers based on the user query and provided context.
362
+ Your task is to generate answers for the user query based on the context provided.
363
  Instructions for your response:
364
  1. Directly answer the user query. Make use of provided context if necessary.
365
  2. Ensure your response is clear and concise.
366
+ 3. Give the answer in JSON format with a single key named 'content' with value as your response.
367
+ 4. It is important to give response in JSON format, otherwise the chat may terminate.
 
368
  Please format an answer based on the following user question and context provided:
 
369
  User Question:
370
  {question}
 
371
  Context:
372
  {context}
373
  """
 
377
  res = response.content
378
  return res
379
 
380
+ def general_answer_node(question):
381
+ prompt = f"""You are an assistant that provides helpful and accurate answers to any question. Please answer the following question in a JSON format with a single key 'content' containing your answer.
382
+
383
+ Question: {question}
384
+ """
385
+ response = llm.invoke(
386
+ [SystemMessage(content=prompt)]
387
+ )
388
+ try:
389
+ res = json.loads(response.content)
390
+ return res
391
+ except json.JSONDecodeError:
392
+ # If the response is not valid JSON, wrap it
393
+ return {'content': response.content}
394
+
395
  CURR_CONTEXT = ''
396
 
 
397
  def get_answer(image=[], message='', sessionID='abc123'):
398
  global CURR_CONTEXT
399
  if len(image) > 0:
400
  try:
 
401
  device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
402
  chat = Chat(model,transform,df,tar_img_feats, device)
403
  chat.encode_image(image)
 
412
  print(e)
413
  response = {'content':"An error occurred while processing your request."}
414
  elif len(image) == 0 and message is not None:
 
415
  task = router_node(message)
416
  if task == 'retrieval':
417
+ formated_input = {
418
+ 'input': message,
419
+ 'context': CURR_CONTEXT
420
+ }
421
+ response = json_answer_generator(message, CURR_CONTEXT)
422
+ elif task == "recommendation":
423
  recipes = recommendation_node(message)
 
424
  if not recipes:
425
  response = {'content':"An error occurred while processing your request."}
426
  response = answer_formatter_node(message, recipes)
427
+ elif task == "general":
428
+ response = general_answer_node(message)
429
+ if response is None:
430
+ response = {'content':"An error occurred while processing your request."}
431
  else:
432
+ response = {'content':"Sorry, I didn't understand your request."}
433
+ else:
434
+ response = {'content':"Please provide a message to process."}
 
 
435
 
436
  return response
437
 
438
  # Function to handle WebSocket connection
439
  @socketio.on('ping')
440
+ def handle_ping():
441
  emit('Ping-return', {'message': 'Connected'}, room=request.sid)
442
 
 
443
  # Function to handle WebSocket connection
444
  @socketio.on('connect')
445
  def handle_connect():
 
450
  def handle_disconnect():
451
  print(f"Client disconnected: {request.sid}")
452
 
 
453
  import base64
 
454
  from io import BytesIO
455
  import torchvision.transforms as transforms
456
 
 
483
  if session_store[session_id]['image_data'] or session_store[session_id]['message']:
484
  try:
485
  image_bytes = session_store[session_id]['image_data']
 
486
  if isinstance(image_bytes, str):
487
  image_bytes = base64.b64decode(image_bytes)
488
  image = Image.open(BytesIO(image_bytes))
 
497
  'input': message,
498
  'context': json.dumps(context)
499
  }
 
500
  response = json_answer_generator(message, context)
501
  emit('response', response, room=session_id)
502
 
 
513
  if task == 'retrieval':
514
  formated_input = {
515
  'input': message,
516
+ 'context': CURR_CONTEXT
517
  }
518
  response = json_answer_generator(message, CURR_CONTEXT)
519
  emit('response', response, room=session_id)
520
  elif task == "recommendation":
521
+ recipes = recommendation_node(message)
522
+ if not recipes:
 
523
  response = {'content':"An error occurred while processing your request."}
524
+ response = answer_formatter_node(message, recipes)
525
  emit('json_response', response, room=session_id)
526
+ elif task == "general":
527
+ response = general_answer_node(message)
528
  if response is None:
529
  response = {'content':"An error occurred while processing your request."}
530
  emit('json_response', response, room=session_id)
531
+ else:
532
+ response = {'content':"Sorry, I didn't understand your request."}
533
+ emit('json_response', response, room=session_id)
534
  session_store.pop(session_id, None)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
535
 
536
  # Home route
537
  @app.route("/")
 
540
 
541
  # Main function to run the app
542
  if __name__ == '__main__':
543
+ socketio.run(app, debug=True)