Spaces:
Runtime error
Runtime error
| from fastapi import FastAPI, HTTPException | |
| from pydantic import BaseModel | |
| from pymongo import MongoClient | |
| from urllib.parse import quote_plus | |
| import uuid | |
| from typing import List, Optional | |
| import json | |
| from fastapi import FastAPI, File, UploadFile, HTTPException | |
| from fastapi.responses import HTMLResponse | |
| import os | |
| import base64 | |
| from groq import Groq | |
| # Initialize Groq client | |
| client = Groq(api_key='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') | |
| # MongoDB connection setup | |
| def get_mongo_client(): | |
| password = quote_plus("momimaad@123") # Change this to your MongoDB password | |
| mongo_uri = f"mongodb+srv://hammad:{password}@cluster0.2a9yu.mongodb.net/" | |
| return MongoClient(mongo_uri) | |
| db_client = get_mongo_client() | |
| db = db_client["recipe"] | |
| user_collection = db["user_info"] | |
| # Pydantic models for user data | |
| class User(BaseModel): | |
| first_name: str | |
| last_name: str | |
| email: str | |
| password: str | |
| class UserData(BaseModel): | |
| email: str | |
| password: str | |
| class UserToken(BaseModel): | |
| token: str | |
| class RecipeData(BaseModel): | |
| name: str | |
| class AltrecipeData(BaseModel): | |
| recipe_name: str | |
| dietary_restrictions: str | |
| allergies: List | |
| class Ingredient(BaseModel): | |
| name: str | |
| quantity: str | |
| class Recipe(BaseModel): | |
| recipe_name: str | |
| ingredients: List[Ingredient] | |
| directions: List[str] | |
| # Data model for LLM to generate | |
| class Alternative_Ingredient(BaseModel): | |
| name: str | |
| quantity: str | |
| class Alternative_Recipe(BaseModel): | |
| recipe_name: str | |
| alternative_ingredients: List[Alternative_Ingredient] | |
| alternative_directions: List[str] | |
| def get_recipe(recipe_name: str) -> Recipe: | |
| chat_completion = client.chat.completions.create( | |
| messages=[ | |
| { | |
| "role": "system", | |
| "content": f"""Your are an expert agent to generate a recipes with proper and corrected ingredients and direction. Your directions should be concise and to the point and dont explain any irrelevant text. | |
| You are a recipe database that outputs recipes in JSON.\n | |
| The JSON object must use the schema: {json.dumps(Recipe.model_json_schema(), indent=2)}""", | |
| }, | |
| { | |
| "role": "user", | |
| "content": f"Fetch a recipe for {recipe_name}", | |
| }, | |
| ], | |
| model="llama-3.2-90b-text-preview", | |
| temperature=0, | |
| # Streaming is not supported in JSON mode | |
| stream=False, | |
| # Enable JSON mode by setting the response format | |
| response_format={"type": "json_object"}, | |
| ) | |
| return Recipe.model_validate_json(chat_completion.choices[0].message.content) | |
| def Suggest_ingredient_alternatives(recipe_name: str, dietary_restrictions: str, allergies: List) -> Alternative_Recipe: | |
| chat_completion = client.chat.completions.create( | |
| messages=[ | |
| { | |
| "role": "system", | |
| "content": f""" | |
| You are an expert agent to suggest alternatives for specific allergies ingredients for the provided recipe {recipe_name}. | |
| Please take the following into account: | |
| - If the user has dietary restrictions, suggest substitutes that align with their needs (e.g., vegan, gluten-free, etc.) in alternative_directions and your alternative_directions should be concise and to the point. | |
| -In ingredient you will recommend the safe ingredient for avoid any allergy and dietary restriction. | |
| - Consider the following allergies {allergies} and recommend the safe ingredient to avoid this allergies. | |
| recipe_name: {recipe_name} | |
| Dietary Restrictions: {dietary_restrictions} | |
| Allergies: {', '.join(allergies)} | |
| You are a recipe database that outputs alternative recipes to avoid allergy and dietary_restrictions in JSON.\n | |
| The JSON object must use the schema: {json.dumps(Alternative_Recipe.model_json_schema(), indent=2)}""", | |
| }, | |
| { | |
| "role": "user", | |
| "content": f"""Fetch a alternative recipe for recipe_name: {recipe_name} | |
| Dietary Restrictions: {dietary_restrictions} | |
| Allergies: {', '.join(allergies)}""", | |
| }, | |
| ], | |
| model="llama-3.2-90b-text-preview", | |
| temperature=0, | |
| # Streaming is not supported in JSON mode | |
| stream=False, | |
| # Enable JSON mode by setting the response format | |
| response_format={"type": "json_object"}, | |
| ) | |
| return Alternative_Recipe.model_validate_json(chat_completion.choices[0].message.content) | |
| def get_status(content): | |
| chat_completion = client.chat.completions.create( | |
| messages=[ | |
| { | |
| "role": "system", | |
| "content": """Your are an expert agent to status yes if any kind of recipe dish present in explanation other no | |
| Json output format: | |
| {'status':return'yes' if any dish present in expalantion return 'no' if not dish present in image} | |
| """, | |
| }, | |
| { | |
| "role": "user", | |
| "content": f"Image Explanation {content}", | |
| }, | |
| ], | |
| model="llama3-groq-70b-8192-tool-use-preview", | |
| temperature=0, | |
| # Streaming is not supported in JSON mode | |
| stream=False, | |
| # Enable JSON mode by setting the response format | |
| response_format={"type": "json_object"}, | |
| ) | |
| return chat_completion.choices[0].message.content | |
| # Function to encode the image | |
| def encode_image(image_path): | |
| with open(image_path, "rb") as image_file: | |
| return base64.b64encode(image_file.read()).decode('utf-8') | |
| def explain_image(base64_image): | |
| text_query = ''' | |
| explain the image. | |
| ''' | |
| chat_completion = client.chat.completions.create( | |
| messages=[ | |
| { | |
| "role": "user", | |
| "content": [ | |
| {"type": "text", "text": text_query}, | |
| { | |
| "type": "image_url", | |
| "image_url": { | |
| "url": f"data:image/jpeg;base64,{base64_image}", | |
| }, | |
| }, | |
| ], | |
| } | |
| ], | |
| model="llama-3.2-90b-vision-preview") | |
| return chat_completion.choices[0].message.content | |
| class get_recipe_name(BaseModel): | |
| recipe_name: List[str] | |
| ingredients: List[List[str]] | |
| def generate_recipe_name(base64_image): | |
| # Example of how the JSON should look to make it clearer | |
| example_json_structure = { | |
| "recipe_name": "Chicken Karhai", | |
| "ingredients": [ | |
| "chicken", | |
| "tomatoes", | |
| "onions", | |
| "ginger", | |
| "garlic", | |
| "green chilies", | |
| "yogurt", | |
| "cumin seeds", | |
| "coriander powder", | |
| "red chili powder", | |
| "turmeric powder", | |
| "garam masala", | |
| "fresh coriander leaves", | |
| "oil", | |
| "salt" | |
| ] | |
| } | |
| # Generating the query prompt to ask for ingredients | |
| text_query = f'''What are the ingredients used in these dishes? Do not add any explanation, just write the names of the ingredients in proper JSON according to the following format: | |
| The JSON object must follow this schema: | |
| {json.dumps(get_recipe_name.model_json_schema(), indent=2)} | |
| Example format: | |
| {json.dumps(example_json_structure, indent=2)} | |
| Write the name of the dish and then list the ingredients used for each recipe, focusing on traditional Pakistani ingredients and terminology. | |
| ''' | |
| chat_completion = client.chat.completions.create( | |
| messages=[ | |
| { | |
| "role": "user", | |
| "content": [ | |
| {"type": "text", "text": text_query}, | |
| { | |
| "type": "image_url", | |
| "image_url": { | |
| "url": f"data:image/jpeg;base64,{base64_image}", | |
| }, | |
| }, | |
| ], | |
| } | |
| ], | |
| response_format={"type": "json_object"}, | |
| model="llama-3.2-90b-vision-preview") | |
| return json.loads(chat_completion.choices[0].message.content) | |
| app = FastAPI() | |
| async def get_recipe_response(token: str, recipe_user: RecipeData): | |
| user = user_collection.find_one({"token": token}) | |
| if not user: | |
| raise HTTPException(status_code=401, detail="Invalid token") | |
| # Find user by email | |
| recipe_name = recipe_user.name | |
| response = get_recipe(recipe_name) | |
| return { | |
| "Response": response | |
| } | |
| async def get_alternative_recipe_response(token: str, altrecipe_user: AltrecipeData): | |
| user = user_collection.find_one({"token": token}) | |
| if not user: | |
| raise HTTPException(status_code=401, detail="Invalid token") | |
| response = Suggest_ingredient_alternatives(altrecipe_user.recipe_name, altrecipe_user.dietary_restrictions, altrecipe_user.allergies) | |
| return { | |
| "Response": response | |
| } | |
| # Directory to save uploaded images | |
| UPLOAD_DIR = "uploads" | |
| # Ensure the upload directory exists | |
| os.makedirs(UPLOAD_DIR, exist_ok=True) | |
| # Endpoint to upload an image | |
| async def upload_image(token: str, file: UploadFile = File(...)): | |
| user = user_collection.find_one({"token": token}) | |
| if not user: | |
| raise HTTPException(status_code=401, detail="Invalid token") | |
| # Validate the file type | |
| if not file.filename.lower().endswith(('.png', '.jpg', '.jpeg')): | |
| raise HTTPException(status_code=400, detail="Invalid file type. Only PNG, JPG, and JPEG are allowed.") | |
| # Create a file path for saving the uploaded file | |
| file_path = os.path.join(UPLOAD_DIR, file.filename) | |
| # Save the file | |
| with open(file_path, "wb") as buffer: | |
| buffer.write(await file.read()) | |
| # Getting the base64 string | |
| base64_image = encode_image(file_path) | |
| status = get_status(explain_image(base64_image)) | |
| status_json = json.loads(status) | |
| if status_json['status'].lower() == 'no': | |
| response = {"recipe_name": [], 'ingredients': []} | |
| else: | |
| response = generate_recipe_name(base64_image) | |
| return { | |
| "Response": response | |
| } | |
| # Endpoint to register a new user | |
| async def register_user(user: User): | |
| # Check if user already exists | |
| existing_user = user_collection.find_one({"email": user.email}) | |
| if existing_user: | |
| raise HTTPException(status_code=400, detail="Email already registered") | |
| # Create user data | |
| user_data = { | |
| "first_name": user.first_name, | |
| "last_name": user.last_name, | |
| "email": user.email, | |
| "password": user.password, # Store plaintext password (not recommended in production) | |
| } | |
| # Insert the user data into the user_info collection | |
| result = user_collection.insert_one(user_data) | |
| return {"msg": "User registered successfully", "user_id": str(result.inserted_id)} | |
| # Endpoint to check user credentials and generate a token | |
| async def check_credentials(user: UserData): | |
| # Find user by email | |
| existing_user = user_collection.find_one({"email": user.email}) | |
| # Check if user exists and password matches | |
| if not existing_user or existing_user["password"] != user.password: | |
| raise HTTPException(status_code=401, detail="Invalid email or password") | |
| # Generate a UUID token | |
| token = str(uuid.uuid4()) | |
| # Update the user document with the token | |
| user_collection.update_one({"email": user.email}, {"$set": {"token": token}}) | |
| return { | |
| "first_name": existing_user["first_name"], | |
| "last_name": existing_user["last_name"], | |
| "token": token, | |
| } | |