dschandra commited on
Commit
b1431be
·
verified ·
1 Parent(s): e2c4a3d

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +131 -193
app.py CHANGED
@@ -6,22 +6,38 @@ import ffmpeg
6
  import logging
7
  from werkzeug.exceptions import BadRequest
8
 
9
- # Initialize Flask App
10
  app = Flask(__name__)
11
-
12
- # Set up logging
13
  logging.basicConfig(level=logging.INFO)
14
 
15
- # Initialize conversation state
16
- user_order = [] # Stores the current order
17
-
18
- # Define food items (menu) with only 10 items
19
- menu_items = {
20
- 'Veg': ["Vegetable Biryani"], # 1 Veg item
21
- 'Non-Veg': ["Butter Chicken"], # 1 Non-Veg item
22
- 'Both': ["Paneer Butter Masala", "Chicken Biryani"], # 1 Veg + 1 Non-Veg item
23
- 'Drinks': ["Lassi", "Milkshake"], # 2 Drinks
24
- 'Desserts': ["Gulab Jamun", "Ice Cream"] # 2 Desserts
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25
  }
26
 
27
  # HTML Template for Frontend
@@ -35,13 +51,8 @@ html_code = """
35
  <style>
36
  body {
37
  font-family: Arial, sans-serif;
 
38
  background-color: #f4f4f9;
39
- display: flex;
40
- flex-direction: column;
41
- align-items: center;
42
- justify-content: center;
43
- height: 100vh;
44
- margin: 0;
45
  }
46
  h1 {
47
  color: #333;
@@ -54,248 +65,175 @@ html_code = """
54
  color: white;
55
  font-size: 24px;
56
  border: none;
57
- display: flex;
58
- align-items: center;
59
- justify-content: center;
60
  cursor: pointer;
61
- box-shadow: 0px 4px 6px rgba(0, 0, 0, 0.1);
62
- transition: background-color 0.3s;
63
- }
64
- .mic-button:hover {
65
- background-color: #0056b3;
66
  }
67
- .status {
68
  margin-top: 20px;
69
- font-size: 18px;
70
- color: #666;
71
- }
72
- .response {
73
- margin-top: 20px;
74
- padding: 10px;
75
- background-color: #fff;
76
- border: 1px solid #ddd;
77
- border-radius: 5px;
78
- box-shadow: 0px 4px 6px rgba(0, 0, 0, 0.1);
79
- width: 300px;
80
- text-align: center;
81
  }
82
  </style>
83
  </head>
84
  <body>
85
  <h1>AI Dining Assistant</h1>
86
  <button class="mic-button" id="mic-button">🎤</button>
87
- <div class="status" id="status">Press the mic button to start the conversation...</div>
88
  <div class="response" id="response" style="display: none;">Response will appear here...</div>
89
  <script>
90
  const micButton = document.getElementById('mic-button');
91
  const status = document.getElementById('status');
92
  const response = document.getElementById('response');
93
- let mediaRecorder;
94
- let audioChunks = [];
95
  let isListening = false;
96
 
97
  micButton.addEventListener('click', () => {
98
  if (!isListening) {
99
  isListening = true;
100
- startConversation();
101
  }
102
  });
103
 
104
- function startConversation() {
105
- status.textContent = 'Please listen to the instructions. Once you are ready, I will start listening to your commands.';
106
- showInstructions();
107
- }
108
-
109
- function showInstructions() {
110
- setTimeout(() => {
111
- status.textContent = 'Listening...';
112
- response.style.display = 'none';
113
  startListening();
114
- }, 3000); // Wait 3 seconds to ensure user reads instructions
115
  }
116
 
117
- function startListening() {
118
- navigator.mediaDevices.getUserMedia({ audio: true }).then(stream => {
119
- mediaRecorder = new MediaRecorder(stream, { mimeType: 'audio/webm;codecs=opus' });
120
- mediaRecorder.start();
121
- audioChunks = [];
122
-
123
- mediaRecorder.ondataavailable = event => audioChunks.push(event.data);
124
- mediaRecorder.onstop = async () => {
125
- status.textContent = 'Processing...';
126
-
127
- const audioBlob = new Blob(audioChunks, { type: 'audio/webm' });
128
- const formData = new FormData();
129
- formData.append('audio', audioBlob);
130
-
131
- try {
132
- const result = await fetch('/process-audio', { method: 'POST', body: formData });
133
- const data = await result.json();
134
- response.textContent = data.response;
135
- response.style.display = 'block';
136
-
137
- try {
138
- const utterance = new SpeechSynthesisUtterance(data.response);
139
- speechSynthesis.speak(utterance);
140
- utterance.onend = () => {
141
- console.log("Speech synthesis completed.");
142
- };
143
- utterance.onerror = (e) => {
144
- console.error("Speech synthesis error:", e.error);
145
- status.textContent = 'Error with speech output.';
146
- };
147
- } catch (speechError) {
148
- console.error("Speech synthesis not supported or failed:", speechError);
149
- response.textContent = "Speech output unavailable. Please check your browser.";
150
- }
151
-
152
- if (data.response.includes("Goodbye")) {
153
- status.textContent = 'Conversation ended. Press the mic button to start again.';
154
- isListening = false;
155
  } else {
156
- status.textContent = 'Listening...';
157
- setTimeout(startListening, 1000);
158
  }
159
- } catch (error) {
160
- response.textContent = 'Error occurred. Please try again.';
161
- response.style.display = 'block';
162
- status.textContent = 'Press the mic button to restart the conversation.';
163
- isListening = false;
164
- }
165
- };
166
- setTimeout(() => mediaRecorder.stop(), 10000); // Wait for 10 seconds of speaking time
167
- }).catch(() => {
168
- status.textContent = 'Microphone access denied.';
169
- isListening = false;
170
- });
171
  }
172
  </script>
173
  </body>
174
  </html>
175
  """
176
 
177
- @app.route('/')
178
  def index():
179
  return render_template_string(html_code)
180
 
181
- @app.route('/process-audio', methods=['POST'])
182
  def process_audio():
183
- global user_order
184
  try:
185
- # Validate audio file
186
- audio_file = request.files.get('audio')
187
  if not audio_file:
188
  raise BadRequest("No audio file provided.")
189
 
190
  temp_file = NamedTemporaryFile(delete=False, suffix=".webm")
191
  audio_file.save(temp_file.name)
192
- logging.info(f"Saved input audio to {temp_file.name}")
193
 
194
  if os.path.getsize(temp_file.name) == 0:
195
  raise BadRequest("Uploaded audio file is empty.")
196
 
197
- # Convert audio to PCM WAV format (16kHz, mono)
198
  converted_file = NamedTemporaryFile(delete=False, suffix=".wav")
199
- try:
200
- ffmpeg.input(temp_file.name).output(
201
- converted_file.name, acodec='pcm_s16le', ac=1, ar='16000'
202
- ).run(overwrite_output=True)
203
- except Exception as ffmpeg_error:
204
- logging.error(f"FFmpeg conversion error: {str(ffmpeg_error)}")
205
- return jsonify({"response": "Audio conversion failed. Please try again."})
206
 
207
- logging.info(f"Converted audio saved to {converted_file.name}")
208
-
209
- # Recognize speech
210
  recognizer = sr.Recognizer()
211
  with sr.AudioFile(converted_file.name) as source:
212
  audio_data = recognizer.record(source)
213
  try:
214
  command = recognizer.recognize_google(audio_data)
215
- logging.info(f"Recognized command: {command}")
216
  response = process_command(command)
217
- except sr.RequestError as e:
218
- logging.error(f"Error with Google Speech Recognition service: {e}")
219
- response = "Sorry, there was an issue with the speech recognition service."
220
 
221
  return jsonify({"response": response})
222
 
223
  except BadRequest as br:
224
- logging.error(f"Bad request error: {br}")
225
- return jsonify({"response": f"Bad Request: {str(br)}"})
226
-
227
  except Exception as e:
228
- logging.error(f"Error processing audio: {e}")
229
- return jsonify({"response": f"An error occurred: {str(e)}"})
230
-
231
  finally:
232
- # Clean up temporary files
233
- try:
234
- if os.path.exists(temp_file.name):
235
- os.unlink(temp_file.name)
236
- if os.path.exists(converted_file.name):
237
- os.unlink(converted_file.name)
238
- except Exception as cleanup_error:
239
- logging.error(f"Error cleaning up files: {cleanup_error}")
240
 
241
  def process_command(command):
242
- global user_order
243
-
244
- # Normalize the command to lowercase
245
  command = command.lower()
246
 
247
- # Show the menu without categorizing the items
248
- if "menu" in command or "what’s the menu" in command or "Show me the menu" in command:
249
- menu_response = (
250
- "Here are the available food items: "
251
- "Vegetable Biryani, Butter Chicken, Paneer Butter Masala, Chicken Biryani, "
252
- "Lassi, Milkshake, Gulab Jamun, Ice Cream. "
253
- "Please let me know what you'd like to add to your order."
254
- )
255
- return menu_response
256
-
257
- # Add item to order
258
- elif "add" in command:
259
- item_to_add = command.split("add")[-1].strip()
260
-
261
- # Check if the item is in the menu
262
- if item_to_add in menu_items["Veg"] or item_to_add in menu_items["Non-Veg"] or item_to_add in menu_items["Both"] or item_to_add in menu_items["Drinks"] or item_to_add in menu_items["Desserts"]:
263
- user_order.append(item_to_add)
264
- return f"{item_to_add} has been added to your order. Would you like to add more items?"
265
- else:
266
- return f"Sorry, {item_to_add} is not available in the menu. Please choose from the available items."
267
-
268
- # Show current order
269
- elif "show my order" in command or "what's my order" in command or "what's in my order" in command:
270
- if user_order:
271
- return "Your current order includes: " + ", ".join(user_order)
272
- else:
273
- return "You haven't added anything to your order yet."
274
-
275
- # Place order (Ask for confirmation)
276
- elif "place order" in command or "confirm order" in command:
277
- if user_order:
278
- return f"You have the following items in your order: {', '.join(user_order)}. Would you like to confirm? (Say 'yes' to confirm, 'no' to cancel)"
279
- else:
280
- return "You haven't added anything to your order yet. Please add some items first."
281
-
282
- # Final confirmation (user says yes to confirm the order)
283
- elif "yes" in command:
284
- if user_order:
285
- return "Your order has been confirmed and sent to the kitchen. Thank you for ordering!"
286
  else:
287
- return "Please add some items to your order before confirming."
288
 
289
- # Handle unrecognized commands
290
- return (
291
- "Sorry, I didn’t understand your request. You can say things like:\n"
292
- "- Show me the menu\n"
293
- "- Add [item] to my order\n"
294
- "- Show my order\n"
295
- "- Place the order"
296
- )
297
-
298
 
 
299
 
300
  if __name__ == "__main__":
301
  app.run(host="0.0.0.0", port=7860)
 
6
  import logging
7
  from werkzeug.exceptions import BadRequest
8
 
 
9
  app = Flask(__name__)
 
 
10
  logging.basicConfig(level=logging.INFO)
11
 
12
+ # Global variables
13
+ cart = [] # To store items and prices
14
+ MENU = {
15
+ "Biryani": {"Chicken Biryani": 250, "Veg Biryani": 200, "Mutton Biryani": 300},
16
+ "Starters": {
17
+ "Chicken Wings": 220,
18
+ "Paneer Tikka": 180,
19
+ "Fish Fingers": 250,
20
+ "Spring Rolls": 160,
21
+ },
22
+ "Breads": {
23
+ "Butter Naan": 50,
24
+ "Garlic Naan": 60,
25
+ "Roti": 40,
26
+ "Lachha Paratha": 70,
27
+ },
28
+ "Curries": {
29
+ "Butter Chicken": 300,
30
+ "Paneer Butter Masala": 250,
31
+ "Dal Tadka": 200,
32
+ "Chicken Tikka Masala": 320,
33
+ },
34
+ "Drinks": {"Coke": 60, "Sprite": 60, "Mango Lassi": 80, "Masala Soda": 70},
35
+ "Desserts": {
36
+ "Gulab Jamun": 100,
37
+ "Rasgulla": 90,
38
+ "Ice Cream": 120,
39
+ "Brownie with Ice Cream": 180,
40
+ },
41
  }
42
 
43
  # HTML Template for Frontend
 
51
  <style>
52
  body {
53
  font-family: Arial, sans-serif;
54
+ text-align: center;
55
  background-color: #f4f4f9;
 
 
 
 
 
 
56
  }
57
  h1 {
58
  color: #333;
 
65
  color: white;
66
  font-size: 24px;
67
  border: none;
 
 
 
68
  cursor: pointer;
 
 
 
 
 
69
  }
70
+ .status, .response {
71
  margin-top: 20px;
 
 
 
 
 
 
 
 
 
 
 
 
72
  }
73
  </style>
74
  </head>
75
  <body>
76
  <h1>AI Dining Assistant</h1>
77
  <button class="mic-button" id="mic-button">🎤</button>
78
+ <div class="status" id="status">Press the mic button to start...</div>
79
  <div class="response" id="response" style="display: none;">Response will appear here...</div>
80
  <script>
81
  const micButton = document.getElementById('mic-button');
82
  const status = document.getElementById('status');
83
  const response = document.getElementById('response');
 
 
84
  let isListening = false;
85
 
86
  micButton.addEventListener('click', () => {
87
  if (!isListening) {
88
  isListening = true;
89
+ greetUser();
90
  }
91
  });
92
 
93
+ function greetUser() {
94
+ const utterance = new SpeechSynthesisUtterance("Hi. Welcome to Biryani Hub. Can I show you the menu?");
95
+ speechSynthesis.speak(utterance);
96
+ utterance.onend = () => {
97
+ status.textContent = "Listening...";
 
 
 
 
98
  startListening();
99
+ };
100
  }
101
 
102
+ async function startListening() {
103
+ const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
104
+ const mediaRecorder = new MediaRecorder(stream, { mimeType: "audio/webm;codecs=opus" });
105
+ const audioChunks = [];
106
+
107
+ mediaRecorder.ondataavailable = (event) => audioChunks.push(event.data);
108
+ mediaRecorder.onstop = async () => {
109
+ const audioBlob = new Blob(audioChunks, { type: "audio/webm" });
110
+ const formData = new FormData();
111
+ formData.append("audio", audioBlob);
112
+
113
+ status.textContent = "Processing...";
114
+ try {
115
+ const result = await fetch("/process-audio", { method: "POST", body: formData });
116
+ const data = await result.json();
117
+ response.textContent = data.response;
118
+ response.style.display = "block";
119
+
120
+ const utterance = new SpeechSynthesisUtterance(data.response);
121
+ speechSynthesis.speak(utterance);
122
+ utterance.onend = () => {
123
+ if (!data.response.includes("Goodbye") && !data.response.includes("final order")) {
124
+ startListening(); // Continue listening
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
125
  } else {
126
+ status.textContent = "Conversation ended.";
127
+ isListening = false;
128
  }
129
+ };
130
+ } catch (error) {
131
+ response.textContent = "Error processing your request. Please try again.";
132
+ status.textContent = "Press the mic button to restart.";
133
+ isListening = false;
134
+ }
135
+ };
136
+
137
+ mediaRecorder.start();
138
+ setTimeout(() => mediaRecorder.stop(), 5000); // Stop recording after 5 seconds
 
 
139
  }
140
  </script>
141
  </body>
142
  </html>
143
  """
144
 
145
+ @app.route("/")
146
  def index():
147
  return render_template_string(html_code)
148
 
149
+ @app.route("/process-audio", methods=["POST"])
150
  def process_audio():
 
151
  try:
152
+ audio_file = request.files.get("audio")
 
153
  if not audio_file:
154
  raise BadRequest("No audio file provided.")
155
 
156
  temp_file = NamedTemporaryFile(delete=False, suffix=".webm")
157
  audio_file.save(temp_file.name)
 
158
 
159
  if os.path.getsize(temp_file.name) == 0:
160
  raise BadRequest("Uploaded audio file is empty.")
161
 
 
162
  converted_file = NamedTemporaryFile(delete=False, suffix=".wav")
163
+ ffmpeg.input(temp_file.name).output(
164
+ converted_file.name, acodec="pcm_s16le", ac=1, ar="16000"
165
+ ).run(overwrite_output=True)
 
 
 
 
166
 
 
 
 
167
  recognizer = sr.Recognizer()
168
  with sr.AudioFile(converted_file.name) as source:
169
  audio_data = recognizer.record(source)
170
  try:
171
  command = recognizer.recognize_google(audio_data)
 
172
  response = process_command(command)
173
+ except sr.UnknownValueError:
174
+ response = "Sorry, I could not understand. Please try again."
 
175
 
176
  return jsonify({"response": response})
177
 
178
  except BadRequest as br:
179
+ return jsonify({"response": f"Bad Request: {str(br)}"}), 400
 
 
180
  except Exception as e:
181
+ return jsonify({"response": f"An error occurred: {str(e)}"}), 500
 
 
182
  finally:
183
+ os.unlink(temp_file.name)
184
+ os.unlink(converted_file.name)
 
 
 
 
 
 
185
 
186
  def process_command(command):
187
+ global cart, MENU
 
 
188
  command = command.lower()
189
 
190
+ # Handle specific category requests
191
+ for category in MENU.keys():
192
+ if category.lower() in command:
193
+ items = MENU[category]
194
+ item_list = ", ".join([f"{item} (₹{price})" for item, price in items.items()])
195
+ return f"{category} menu: {item_list}. What would you like to order?"
196
+
197
+ # Handle full menu request
198
+ if "menu" in command:
199
+ categories = ", ".join(MENU.keys())
200
+ return f"We have the following categories: {categories}. Which one would you like to explore?"
201
+
202
+ # Add items to the cart
203
+ all_items = {item.lower(): (category, price) for category, items in MENU.items() for item, price in items.items()}
204
+ if command in all_items.keys():
205
+ category, price = all_items[command]
206
+ cart.append((command.title(), price))
207
+ total = sum(item[1] for item in cart)
208
+ cart_summary = ", ".join([f"{i[0]} (₹{i[1]})" for i in cart])
209
+ return f"{command.title()} added to your cart. Your cart: {cart_summary}. Total: ₹{total}. Do you want to order anything else?"
210
+
211
+ # Remove items from the cart
212
+ if "remove" in command:
213
+ for item in cart:
214
+ if item[0].lower() in command:
215
+ cart.remove(item)
216
+ total = sum(i[1] for i in cart)
217
+ cart_summary = ", ".join([f"{i[0]} (₹{i[1]})" for i in cart])
218
+ return f"{item[0]} removed from your cart. Updated cart: {cart_summary}. Total: ₹{total}."
219
+ return "The item you are trying to remove is not in your cart. Please check again."
220
+
221
+ # Handle final order
222
+ if "final order" in command or "submit" in command:
223
+ if cart:
224
+ items = ", ".join([f"{item[0]} (₹{item[1]})" for item in cart])
225
+ total = sum(item[1] for item in cart)
226
+ cart.clear()
227
+ return f"Your final order is: {items}. Total price: ₹{total}. Thank you for ordering!"
 
228
  else:
229
+ return "Your cart is empty. Please add items first."
230
 
231
+ # Handle goodbye
232
+ if "no" in command or "nothing" in command or "goodbye" in command:
233
+ cart.clear()
234
+ return "Goodbye! Thank you for using AI Dining Assistant."
 
 
 
 
 
235
 
236
+ return "Sorry, I didn't understand that. Please try again."
237
 
238
  if __name__ == "__main__":
239
  app.run(host="0.0.0.0", port=7860)