Spaces:
Sleeping
Sleeping
Create app.py
Browse files
app.py
ADDED
@@ -0,0 +1,128 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import gradio as gr
|
2 |
+
import requests
|
3 |
+
import os
|
4 |
+
from fpdf import FPDF
|
5 |
+
import uuid
|
6 |
+
import re
|
7 |
+
import matplotlib.pyplot as plt
|
8 |
+
|
9 |
+
# Load your Nebius API key and Folder ID from environment variables
|
10 |
+
NEBIUS_API_KEY = os.getenv("NEBIUS_API_KEY") or "YOUR_NEBIUS_API_KEY"
|
11 |
+
FOLDER_ID = os.getenv("FOLDER_ID") or "YOUR_FOLDER_ID"
|
12 |
+
|
13 |
+
def generate_meal(preferences, ingredients, time):
|
14 |
+
prompt = f"""
|
15 |
+
You're a smart kitchen agent.
|
16 |
+
|
17 |
+
Given:
|
18 |
+
- Dietary preferences: {preferences}
|
19 |
+
- Ingredients available: {ingredients}
|
20 |
+
- Time available: {time} minutes
|
21 |
+
|
22 |
+
Tasks:
|
23 |
+
1. Suggest one meal idea
|
24 |
+
2. Provide step-by-step recipe instructions
|
25 |
+
3. List missing ingredients (Shopping List)
|
26 |
+
4. Estimate nutrition (calories, protein, carbs, fat)
|
27 |
+
|
28 |
+
Output in this format:
|
29 |
+
Meal Name: ...
|
30 |
+
Steps:
|
31 |
+
1. ...
|
32 |
+
2. ...
|
33 |
+
Shopping List:
|
34 |
+
- ...
|
35 |
+
Nutrition:
|
36 |
+
- Calories: ... kcal
|
37 |
+
- Protein: ... g
|
38 |
+
- Carbs: ... g
|
39 |
+
- Fat: ... g
|
40 |
+
"""
|
41 |
+
headers = {
|
42 |
+
"Authorization": f"Api-Key {NEBIUS_API_KEY}",
|
43 |
+
"Content-Type": "application/json"
|
44 |
+
}
|
45 |
+
data = {
|
46 |
+
"modelUri": f"gpt://{FOLDER_ID}/yandexgpt-lite",
|
47 |
+
"completionOptions": {"stream": False, "temperature": 0.7, "maxTokens": 700},
|
48 |
+
"messages": [{"role": "user", "text": prompt.strip()}]
|
49 |
+
}
|
50 |
+
response = requests.post("https://llm.api.cloud.yandex.net/foundationModels/v1/completion",
|
51 |
+
headers=headers, json=data)
|
52 |
+
try:
|
53 |
+
text = response.json()["result"]["alternatives"][0]["message"]["text"]
|
54 |
+
except Exception as e:
|
55 |
+
return f"❌ Error: Could not fetch recipe. {e}"
|
56 |
+
return text
|
57 |
+
|
58 |
+
def extract_nutrition(recipe_text):
|
59 |
+
pattern = r"Calories:\s*([\d\.]+)\s*kcal.*Protein:\s*([\d\.]+)g.*Carbs:\s*([\d\.]+)g.*Fat:\s*([\d\.]+)g"
|
60 |
+
match = re.search(pattern, recipe_text.replace("\n", " "))
|
61 |
+
if match:
|
62 |
+
calories, protein, carbs, fat = map(float, match.groups())
|
63 |
+
return {"Calories": calories, "Protein": protein, "Carbs": carbs, "Fat": fat}
|
64 |
+
return None
|
65 |
+
|
66 |
+
def plot_nutrition_chart(nutrition):
|
67 |
+
fig, ax = plt.subplots()
|
68 |
+
nutrients = list(nutrition.keys())
|
69 |
+
values = list(nutrition.values())
|
70 |
+
colors = ['#ff9999','#66b3ff','#99ff99','#ffcc99']
|
71 |
+
ax.pie(values, labels=nutrients, autopct='%1.1f%%', startangle=140, colors=colors)
|
72 |
+
ax.axis('equal')
|
73 |
+
plt.tight_layout()
|
74 |
+
filename = f"/tmp/nutrition_{uuid.uuid4().hex}.png"
|
75 |
+
plt.savefig(filename)
|
76 |
+
plt.close(fig)
|
77 |
+
return filename
|
78 |
+
|
79 |
+
def handle_generate(preferences, ingredients, time):
|
80 |
+
recipe = generate_meal(preferences, ingredients, time)
|
81 |
+
nutrition = extract_nutrition(recipe)
|
82 |
+
chart_path = None
|
83 |
+
if nutrition:
|
84 |
+
chart_path = plot_nutrition_chart(nutrition)
|
85 |
+
return recipe, chart_path
|
86 |
+
|
87 |
+
def save_pdf(recipe_text):
|
88 |
+
pdf = FPDF()
|
89 |
+
pdf.add_page()
|
90 |
+
pdf.set_font("Arial", size=12)
|
91 |
+
for line in recipe_text.split('\n'):
|
92 |
+
pdf.multi_cell(0, 10, line)
|
93 |
+
filename = f"/tmp/AgentChef_Recipe_{uuid.uuid4().hex}.pdf"
|
94 |
+
pdf.output(filename)
|
95 |
+
return filename
|
96 |
+
|
97 |
+
with gr.Blocks(css="""
|
98 |
+
body { background-color: #111; color: #eee; font-family: 'Segoe UI', sans-serif; }
|
99 |
+
.gradio-container { max-width: 900px; margin: auto; }
|
100 |
+
.input-label { font-weight: 600; margin-bottom: 5px; }
|
101 |
+
.footer { font-size: 0.8rem; color: #666; padding: 10px; text-align: center; }
|
102 |
+
@media (max-width: 600px) {
|
103 |
+
.gradio-container { padding: 10px; }
|
104 |
+
}
|
105 |
+
""") as demo:
|
106 |
+
gr.Markdown("# 👨🍳 AgentChef: AI Recipe Planner & Smart Kitchen Assistant")
|
107 |
+
|
108 |
+
with gr.Row():
|
109 |
+
with gr.Column(scale=1, min_width=200):
|
110 |
+
preferences = gr.Textbox(label="🥗 Dietary Preferences", placeholder="e.g. vegetarian, keto")
|
111 |
+
ingredients = gr.Textbox(label="🧂 Ingredients You Have", placeholder="e.g. rice, tomato, onion", lines=3)
|
112 |
+
mic = gr.Microphone(label="🎤 Speak Ingredients")
|
113 |
+
mic.stream(lambda audio: audio if audio else "", inputs=None, outputs=ingredients)
|
114 |
+
time = gr.Slider(5, 60, value=20, step=5, label="⏱️ Time Available (minutes)")
|
115 |
+
generate_btn = gr.Button("🍽️ Generate Recipe")
|
116 |
+
|
117 |
+
with gr.Column(scale=1, min_width=300):
|
118 |
+
recipe_output = gr.Textbox(label="📝 Recipe Output", lines=15)
|
119 |
+
nutrition_chart = gr.Image(label="📊 Nutrition Breakdown", interactive=False)
|
120 |
+
pdf_btn = gr.Button("📄 Export as PDF")
|
121 |
+
|
122 |
+
generate_btn.click(handle_generate,
|
123 |
+
inputs=[preferences, ingredients, time],
|
124 |
+
outputs=[recipe_output, nutrition_chart])
|
125 |
+
|
126 |
+
pdf_btn.click(save_pdf, inputs=[recipe_output], outputs=gr.File(label="📥 Download Recipe PDF"))
|
127 |
+
|
128 |
+
demo.launch()
|