import spaces # For ZeroGPU decorator import gradio as gr from transformers import pipeline from diffusers import StableDiffusionPipeline import torch import requests import re from reportlab.lib.pagesizes import letter from reportlab.pdfgen import canvas from tempfile import NamedTemporaryFile from gtts import gTTS # --- USDA API Setup --- USDA_API_KEY = "gcwe1gXmMveg7buqddggl6wAZa7Sd7wrZV87P31z" USDA_SEARCH_URL = "https://api.nal.usda.gov/fdc/v1/foods/search" def get_nutrients(ingredient): params = {"api_key": USDA_API_KEY, "query": ingredient, "pageSize": 1} try: res = requests.get(USDA_SEARCH_URL, params=params) data = res.json() if 'foods' in data and len(data['foods']) > 0: return { n['nutrientName']: n['value'] for n in data['foods'][0].get('foodNutrients', []) if n.get('nutrientName') } return {} except: return {} def get_calories(ingredient): nutrients = get_nutrients(ingredient) return nutrients.get("Energy", 0.0) # --- Load CPU-only model globally (safe) --- recipe_model = pipeline("text-generation", model="samdak93/qrit-2") # Globals to cache GPU models after loading image_model = None recipe_model_gpu = None # If you want to move recipe_model to GPU, handle similarly # --- Utilities --- forbidden = ["pork", "bacon", "ham", "lard", "gelatin", "alcohol", "beer", "wine", "rum", "whiskey", "vodka", "gin"] def parse_ingredients(text): match = re.search(r"(?i)ingredients:(.*?)(\n|directions:|instructions:|$)", text, re.DOTALL) if match: ingredients_block = match.group(1).strip() return [line.strip("- *") for line in ingredients_block.split("\n") if line.strip()] return [] def generate_recipe(key_ingredient): for _ in range(5): prompt = f"Create a halal recipe under 2000 calories that includes {key_ingredient}.\nIngredients:" out = recipe_model(prompt, max_length=300, num_return_sequences=1)[0]['generated_text'] if any(h in out.lower() for h in forbidden): continue ingredients = parse_ingredients(out) total_cal = sum(get_calories(i) for i in ingredients) if total_cal <= 2000 and total_cal > 0: return out, ingredients, total_cal return "No valid recipe.", [], 0 def get_nutrient_breakdown(ingredients): breakdown = {} for ing in ingredients: breakdown[ing] = get_nutrients(ing) return breakdown @spaces.GPU(duration=120) def load_image_model(): global image_model if image_model is None: image_model = StableDiffusionPipeline.from_pretrained( "runwayml/stable-diffusion-v1-5", torch_dtype=torch.float16 ).to("cuda") return image_model @spaces.GPU(duration=120) def generate_image(prompt): pipe = load_image_model() with torch.autocast("cuda"): image = pipe(prompt).images[0] return image def export_pdf(recipe_text, nutrients): temp_pdf = NamedTemporaryFile(delete=False, suffix=".pdf") c = canvas.Canvas(temp_pdf.name, pagesize=letter) width, height = letter c.setFont("Helvetica", 12) c.drawString(50, height - 50, "Generated Halal Recipe") text_obj = c.beginText(50, height - 70) for line in recipe_text.split('\n'): text_obj.textLine(line) c.drawText(text_obj) y = text_obj.getY() - 20 c.drawString(50, y, "Nutrient Breakdown:") y -= 20 for ing, data in nutrients.items(): c.drawString(50, y, f"- {ing}") y -= 15 for k, v in data.items(): c.drawString(70, y, f"{k}: {v}") y -= 12 y -= 5 if y < 100: c.showPage() y = height - 50 c.save() return temp_pdf.name def generate_audio(recipe_text): tts = gTTS(text=recipe_text) temp_audio = NamedTemporaryFile(delete=False, suffix=".mp3") tts.save(temp_audio.name) return temp_audio.name def app(key_ingredient): recipe_text, ingredients, calories = generate_recipe(key_ingredient) if not ingredients: return "No valid recipe generated.", None, None, None, None title_match = re.search(r"(?i)^title: (.+)$", recipe_text, re.MULTILINE) title = title_match.group(1).strip() if title_match else f"Dish with {key_ingredient}" image = generate_image(f"A delicious {title}, beautifully plated") nutrients = get_nutrient_breakdown(ingredients) pdf_path = export_pdf(recipe_text, nutrients) audio_path = generate_audio(recipe_text) display_text = f"{recipe_text}\n\nEstimated Calories: {int(calories)} kcal" return display_text, image, nutrients, pdf_path, audio_path # --- Gradio UI --- with gr.Blocks() as demo: gr.Markdown("## 🕌 Halal Recipe Generator - Advanced Edition") with gr.Row(): key_input = gr.Textbox(label="Key Ingredient") submit_btn = gr.Button("Generate Recipe") output_text = gr.Textbox(label="Recipe Text") output_img = gr.Image(label="Generated Dish") output_table = gr.JSON(label="Nutrient Breakdown") output_pdf = gr.File(label="Download Recipe PDF") output_audio = gr.Audio(label="Recipe Narration", autoplay=False) submit_btn.click(app, inputs=key_input, outputs=[output_text, output_img, output_table, output_pdf, output_audio]) demo.launch(share=True, pwa=True, debug=True)