Spaces:
Sleeping
Sleeping
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, | |
} | |