File size: 5,414 Bytes
2a842e2
a030b31
107d2dc
a030b31
f1db4d5
2a842e2
 
 
b62963d
 
 
 
6246831
2a842e2
6246831
 
b62963d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
107d2dc
2a842e2
 
107d2dc
 
 
2a842e2
6246831
b62963d
 
 
 
 
 
7c9b96e
b62963d
f1de91b
b62963d
 
 
2a842e2
b62963d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
107d2dc
 
 
 
 
 
 
 
 
 
 
b62963d
107d2dc
 
 
 
b62963d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2a842e2
b62963d
 
 
 
 
 
 
 
6246831
b62963d
2a842e2
b62963d
 
 
 
 
 
 
 
 
 
 
 
 
2a842e2
6246831
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160


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)