mgbam commited on
Commit
2677642
·
verified ·
1 Parent(s): bd44786

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +179 -0
app.py ADDED
@@ -0,0 +1,179 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # MCP-Powered Culinary Voice Assistant
2
+ # Hugging Face Space Implementation
3
+
4
+ import gradio as gr
5
+ import numpy as np
6
+ from mcp.server.fastmcp import FastMCP
7
+ from agents import Agent, trace
8
+ from agents.mcp import MCPServerSse, MCPServerStdio
9
+ from agents.voice import VoicePipeline, TTSModelSettings, AudioInput
10
+ import sqlite3
11
+ import json
12
+ import requests
13
+ from PIL import Image
14
+ import io
15
+
16
+ # ------ Custom MCP Cooking Tools Server ------
17
+ mcp = FastMCP("Culinary Tools Server")
18
+
19
+ @mcp.tool()
20
+ def get_recipe_by_ingredients(ingredients: list) -> dict:
21
+ """Find recipes based on available ingredients"""
22
+ print(f"[Culinary Server] Finding recipes with: {', '.join(ingredients)}")
23
+ # In a real implementation, this would call a recipe API
24
+ return {
25
+ "recipes": [
26
+ {"name": "Vegetable Stir Fry", "time": 20, "difficulty": "Easy"},
27
+ {"name": "Pasta Primavera", "time": 30, "difficulty": "Medium"}
28
+ ]
29
+ }
30
+
31
+ @mcp.tool()
32
+ def get_recipe_image(recipe_name: str) -> str:
33
+ """Generate an image of the finished recipe"""
34
+ print(f"[Culinary Server] Generating image for: {recipe_name}")
35
+ # This would call DALL-E or Stable Diffusion in production
36
+ return "https://example.com/recipe-image.jpg"
37
+
38
+ @mcp.tool()
39
+ def convert_measurements(amount: float, from_unit: str, to_unit: str) -> dict:
40
+ """Convert cooking measurements between units"""
41
+ print(f"[Culinary Server] Converting {amount} {from_unit} to {to_unit}")
42
+ # Simple conversion logic - real implementation would handle more units
43
+ conversions = {
44
+ ("tbsp", "tsp"): lambda x: x * 3,
45
+ ("cups", "ml"): lambda x: x * 240,
46
+ ("oz", "g"): lambda x: x * 28.35
47
+ }
48
+ conversion_key = (from_unit.lower(), to_unit.lower())
49
+ if conversion_key in conversions:
50
+ return {"result": conversions[conversion_key](amount), "unit": to_unit}
51
+ return {"error": "Conversion not supported"}
52
+
53
+ # ------ Recipe Database (SQLite) ------
54
+ def init_recipe_db():
55
+ conn = sqlite3.connect('file:recipes.db?mode=memory&cache=shared', uri=True)
56
+ c = conn.cursor()
57
+ c.execute('''CREATE TABLE IF NOT EXISTS recipes
58
+ (id INTEGER PRIMARY KEY, name TEXT, ingredients TEXT, instructions TEXT, prep_time INT)''')
59
+
60
+ # Sample recipes
61
+ recipes = [
62
+ ("Classic Pancakes", "['flour', 'eggs', 'milk', 'baking powder']",
63
+ "1. Mix dry ingredients\n2. Add wet ingredients\n3. Cook on griddle", 15),
64
+ ("Tomato Soup", "['tomatoes', 'onion', 'garlic', 'vegetable stock']",
65
+ "1. Sauté onions\n2. Add tomatoes\n3. Simmer and blend", 30)
66
+ ]
67
+
68
+ c.executemany("INSERT INTO recipes (name, ingredients, instructions, prep_time) VALUES (?,?,?,?)", recipes)
69
+ conn.commit()
70
+ return conn
71
+
72
+ # ------ Voice Assistant Setup ------
73
+ def create_culinary_agent(mcp_servers):
74
+ """Create the culinary assistant agent"""
75
+ culinary_agent = Agent(
76
+ name="ChefAssistant",
77
+ instructions="""
78
+ You are a professional chef assistant. Help users with cooking tasks:
79
+ 1. Use get_recipe_by_ingredients when users have specific ingredients
80
+ 2. Use get_recipe_details for known recipes
81
+ 3. Use convert_measurements for unit conversions
82
+ 4. Use get_recipe_image when the user asks to see a dish
83
+ 5. Keep responses concise and practical for kitchen use
84
+ 6. Use a warm, encouraging tone suitable for cooking
85
+ """,
86
+ mcp_servers=mcp_servers,
87
+ model="gpt-4.1-mini",
88
+ )
89
+ return culinary_agent
90
+
91
+ # ------ Gradio Interface ------
92
+ def process_voice_command(audio, state):
93
+ """Process voice command through the agent system"""
94
+ sr, audio_data = audio
95
+ audio_array = (audio_data / np.iinfo(audio_data.dtype).max).astype(np.float32)
96
+
97
+ # Initialize on first run
98
+ if state is None:
99
+ init_recipe_db()
100
+ state = {
101
+ "mcp_servers": [],
102
+ "agent": None,
103
+ "voice_pipeline": VoicePipeline(
104
+ workflow=None,
105
+ config=VoicePipelineConfig(
106
+ tts_settings=TTSModelSettings(
107
+ instructions="Warm, encouraging chef voice"
108
+ )
109
+ )
110
+ )
111
+ }
112
+
113
+ # Start MCP servers
114
+ with MCPServerSse(
115
+ name="Culinary Tools",
116
+ params={"url": "http://localhost:8000/sse"},
117
+ client_session_timeout_seconds=15,
118
+ ) as culinary_server:
119
+ with MCPServerStdio(
120
+ params={"command": "uvx", "args": ["mcp-server-sqlite", "--db-path", "file:recipes.db?mode=memory&cache=shared"]},
121
+ ) as db_server:
122
+ state["mcp_servers"] = [culinary_server, db_server]
123
+ state["agent"] = create_culinary_agent(state["mcp_servers"])
124
+
125
+ # Process audio through agent
126
+ audio_input = AudioInput(buffer=audio_array, sample_rate=sr)
127
+ response = state["voice_pipeline"].run(state["agent"], audio_input)
128
+
129
+ # For demo purposes, return mock response
130
+ return (
131
+ "https://example.com/response.wav",
132
+ "I found 3 recipes for your ingredients! Vegetable Stir Fry (20 mins) and Pasta Primavera (30 mins).",
133
+ "https://example.com/stir-fry.jpg",
134
+ state
135
+ )
136
+
137
+ # ------ Hugging Face Space UI ------
138
+ with gr.Blocks(title="MCP Culinary Voice Assistant") as demo:
139
+ state = gr.State(value=None)
140
+
141
+ with gr.Row():
142
+ gr.Markdown("# 🧑‍🍳 MCP-Powered Culinary Voice Assistant")
143
+
144
+ with gr.Row():
145
+ audio_input = gr.Audio(source="microphone", type="numpy", label="Speak to Chef Assistant")
146
+ audio_output = gr.Audio(label="Assistant Response", interactive=False)
147
+
148
+ with gr.Row():
149
+ text_output = gr.Textbox(label="Transcription", interactive=False)
150
+ image_output = gr.Image(label="Recipe Image", interactive=False)
151
+
152
+ with gr.Row():
153
+ submit_btn = gr.Button("Process Command", variant="primary")
154
+
155
+ submit_btn.click(
156
+ fn=process_voice_command,
157
+ inputs=[audio_input, state],
158
+ outputs=[audio_output, text_output, image_output, state]
159
+ )
160
+
161
+ gr.Examples(
162
+ examples=[
163
+ ["What can I make with eggs and flour?", "", ""],
164
+ ["Show me how tomato soup looks", "", ""],
165
+ ["Convert 2 cups to milliliters", "", ""]
166
+ ],
167
+ inputs=[text_output],
168
+ label="Example Queries"
169
+ )
170
+
171
+ if __name__ == "__main__":
172
+ # Start MCP server in background thread
173
+ import threading
174
+ server_thread = threading.Thread(target=mcp.run, kwargs={"transport": "sse"})
175
+ server_thread.daemon = True
176
+ server_thread.start()
177
+
178
+ # Launch Gradio interface
179
+ demo.launch(server_name="0.0.0.0", server_port=7860)