geethareddy commited on
Commit
c44e6ef
·
verified ·
1 Parent(s): 1812cce

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +188 -264
app.py CHANGED
@@ -1,200 +1,31 @@
1
- import os
2
- import speech_recognition as sr
3
- import logging
4
  from flask import Flask, render_template_string, request, jsonify
 
5
  from tempfile import NamedTemporaryFile
 
6
  import ffmpeg
7
- from fuzzywuzzy import process, fuzz
 
8
 
9
- # Initialize Flask app
10
  app = Flask(__name__)
11
  logging.basicConfig(level=logging.INFO)
12
 
13
  # Global variables
14
- cart = [] # Stores items as [item_name, price, quantity] in the cart
15
- menu_preferences = None # Tracks the current menu preference
16
- section_preferences = None # Tracks the current section preference
17
- default_sections = {
18
- "biryanis": ["veg biryani", "paneer biryani", "chicken biryani", "mutton biryani"],
19
- "starters": ["samosa", "onion pakoda", "chilli gobi", "chicken manchurian", "veg manchurian"],
20
- "curries": ["paneer butter", "chicken curry", "fish curry", "chilli chicken"],
21
- "desserts": ["gulab jamun", "ice cream"],
22
- "soft drinks": ["cola", "lemon soda"]
23
  }
24
- prices = {
25
- "samosa": 9,
26
- "onion pakoda": 10,
27
- "chilli gobi": 12,
28
- "chicken biryani": 14,
29
- "mutton biryani": 16,
30
- "veg biryani": 12,
31
- "paneer butter": 10,
32
- "fish curry": 12,
33
- "chicken manchurian": 14,
34
- "veg manchurian": 12,
35
- "chilli chicken": 14,
36
- "paneer biryani": 13,
37
- "chicken curry": 14,
38
- "gulab jamun": 8,
39
- "ice cream": 6,
40
- "cola": 5,
41
- "lemon soda": 6
42
- }
43
-
44
- @app.route("/")
45
- def index():
46
- return render_template_string(html_code)
47
-
48
- @app.route("/reset-cart", methods=["GET"])
49
- def reset_cart():
50
- global cart, menu_preferences, section_preferences
51
- cart = []
52
- menu_preferences = None
53
- section_preferences = None
54
- return "Cart reset successfully."
55
-
56
- @app.route("/process-audio", methods=["POST"])
57
- def process_audio():
58
- try:
59
- # Handle audio input
60
- audio_file = request.files.get("audio")
61
- if not audio_file:
62
- return jsonify({"response": "Oops! I didn't catch any audio. Please try again."}), 400
63
-
64
- # Save and convert audio to WAV format
65
- temp_file = NamedTemporaryFile(delete=False, suffix=".webm")
66
- audio_file.save(temp_file.name)
67
-
68
- converted_file = NamedTemporaryFile(delete=False, suffix=".wav")
69
- ffmpeg.input(temp_file.name).output(
70
- converted_file.name, acodec="pcm_s16le", ac=1, ar="16000"
71
- ).run(overwrite_output=True)
72
-
73
- # Recognize speech
74
- recognizer = sr.Recognizer()
75
- recognizer.dynamic_energy_threshold = True
76
- recognizer.energy_threshold = 4000 # Increased sensitivity
77
-
78
- with sr.AudioFile(converted_file.name) as source:
79
- recognizer.adjust_for_ambient_noise(source, duration=1)
80
- audio_data = recognizer.record(source)
81
-
82
- # Try using the Google API for speech recognition
83
- try:
84
- raw_command = recognizer.recognize_google(audio_data).lower()
85
- except sr.UnknownValueError:
86
- raw_command = "Sorry, I couldn't understand that."
87
- except sr.RequestError as e:
88
- raw_command = f"Request error from the service: {e}"
89
-
90
- logging.info(f"User said: {raw_command}") # Print user speech in the console
91
-
92
- # Display the transcribed text and AI voice response
93
- response = process_command(raw_command)
94
-
95
- except Exception as e:
96
- response = f"An error occurred: {str(e)}"
97
- finally:
98
- os.unlink(temp_file.name)
99
- os.unlink(converted_file.name)
100
-
101
- return jsonify({"response": response})
102
-
103
- def preprocess_command(command):
104
- """
105
- Normalize the user command to improve matching.
106
- """
107
- command = command.strip().lower()
108
- return command
109
-
110
- def process_command(command):
111
- global cart, menu_preferences, section_preferences
112
-
113
- # Finalize order
114
- if "final order" in command or "complete order" in command:
115
- if not cart:
116
- return "Your cart is empty. Please add items before finalizing the order."
117
-
118
- order_summary = "\n".join([f"{item[2]} x {item[0]} for {item[1] * item[2]} INR" for item in cart])
119
- total_price = sum(item[1] * item[2] for item in cart)
120
-
121
- cart.clear() # Clear the cart after finalizing
122
- menu_preferences = None
123
- section_preferences = None
124
-
125
- return f"Your order has been placed successfully:\n{order_summary}\nTotal: {total_price} INR.\nThank you for ordering!"
126
-
127
- # Greet the user and ask for preferences when first started
128
- if menu_preferences is None:
129
- if "hello" in command or "hi" in command:
130
- return "Hello, welcome to Biryani Hub! We have the following categories: Biryanis, Starters, Curries, Desserts, Soft Drinks. Please choose a category."
131
-
132
- preferences = ["non-vegetarian", "vegetarian", "all"]
133
- if command in preferences:
134
- menu_preferences = command
135
- return f"You've selected the {command} menu! Which section would you like to browse next? (e.g., biryanis, starters, curries, desserts, soft drinks)"
136
-
137
- # Use fuzzy matching to help recognize similar inputs
138
- closest_match = process.extractOne(command, preferences, scorer=fuzz.partial_ratio)
139
- if closest_match and closest_match[1] > 75:
140
- menu_preferences = closest_match[0]
141
- return f"Great choice! You've chosen the {menu_preferences} menu. Which section would you like to browse next?"
142
-
143
- return "I couldn't recognize your choice. Please say either 'Non-Vegetarian', 'Vegetarian', or 'All'."
144
-
145
- # Handle section preferences and list items
146
- if section_preferences is None:
147
- sections = list(default_sections.keys())
148
- for section in sections:
149
- if section in command:
150
- section_preferences = section
151
- items_text = ', '.join(default_sections[section_preferences])
152
- response_text = f"Here are the items in the {section_preferences} section: {items_text}. Please choose an item."
153
- # Speak the response
154
- return response_text
155
-
156
- closest_match = process.extractOne(command, sections, scorer=fuzz.partial_ratio)
157
- if closest_match and closest_match[1] > 75:
158
- section_preferences = closest_match[0]
159
- items_text = ', '.join(default_sections[section_preferences])
160
- return f"Here are the items in the {section_preferences} section: {items_text}. What would you like to add?"
161
-
162
- return "I didn't catch that. Please say a section like 'biryanis', 'starters', 'curries', 'desserts', or 'soft drinks'."
163
-
164
- # Filter items based on the menu preference (vegetarian/non-vegetarian)
165
- available_items = []
166
- if menu_preferences == "vegetarian":
167
- available_items = [item for item in default_sections[section_preferences] if item in menus["vegetarian"]]
168
- elif menu_preferences == "non-vegetarian":
169
- available_items = [item for item in default_sections[section_preferences] if item in menus["non-vegetarian"]]
170
- elif menu_preferences == "all":
171
- available_items = [item for item in default_sections[section_preferences]]
172
-
173
- for item in available_items:
174
- if item in command:
175
- quantity = extract_quantity(command)
176
- if quantity:
177
- cart.append([item, prices[item], quantity])
178
- return f"Added {quantity} x {item} to your cart. Your current cart: {', '.join([f'{i[0]} x{i[2]}' for i in cart])}. Would you like to add more items?"
179
-
180
- return "I didn't recognize the item you mentioned. Please say the item name clearly, or choose from the available items."
181
-
182
- def extract_quantity(command):
183
- """
184
- Extract quantity from the command (e.g., 'two', '3', '5').
185
- """
186
- number_words = {
187
- "one": 1, "two": 2, "three": 3, "four": 4, "five": 5,
188
- "six": 6, "seven": 7, "eight": 8, "nine": 9, "ten": 10,
189
- "1": 1, "2": 2, "3": 3, "4": 4, "5": 5, "6": 6, "7": 7, "8": 8, "9": 9, "10": 10
190
- }
191
-
192
- command_words = command.split()
193
- for word in command_words:
194
- if word in number_words:
195
- return number_words[word]
196
- return None
197
 
 
198
  html_code = """
199
  <!DOCTYPE html>
200
  <html lang="en">
@@ -204,43 +35,25 @@ html_code = """
204
  <title>AI Dining Assistant</title>
205
  <style>
206
  body {
207
- display: flex;
208
- flex-direction: column;
209
- align-items: center;
210
- justify-content: center;
211
- min-height: 100vh;
212
- margin: 0;
213
  font-family: Arial, sans-serif;
 
214
  background-color: #f4f4f9;
215
  }
216
  h1 {
217
  color: #333;
218
  }
219
  .mic-button {
220
- font-size: 2rem;
221
- padding: 1rem 2rem;
222
- color: white;
223
  background-color: #007bff;
 
 
224
  border: none;
225
- border-radius: 50px;
226
  cursor: pointer;
227
- transition: background-color 0.3s;
228
- }
229
- .mic-button:hover {
230
- background-color: #0056b3;
231
  }
232
  .status, .response {
233
- margin-top: 1rem;
234
- text-align: center;
235
- color: #555;
236
- font-size: 1.2rem;
237
- }
238
- .response {
239
- background-color: #e8e8ff;
240
- padding: 1rem;
241
- border-radius: 10px;
242
- box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
243
- display: block;
244
  }
245
  </style>
246
  </head>
@@ -248,75 +61,186 @@ html_code = """
248
  <h1>AI Dining Assistant</h1>
249
  <button class="mic-button" id="mic-button">🎤</button>
250
  <div class="status" id="status">Press the mic button to start...</div>
251
- <div class="response" id="response">Response will appear here...</div>
252
  <script>
253
  const micButton = document.getElementById('mic-button');
254
  const status = document.getElementById('status');
255
  const response = document.getElementById('response');
256
- let mediaRecorder;
257
- let audioChunks = [];
258
- let isConversationActive = false;
259
  micButton.addEventListener('click', () => {
260
- if (!isConversationActive) {
261
- isConversationActive = true;
262
- startConversation();
263
  }
264
  });
265
- function startConversation() {
266
- const utterance = new SpeechSynthesisUtterance('Hello, welcome to Biryani Hub! We have the following categories: Biryanis, Starters, Curries, Desserts, Soft Drinks. Please choose a category.');
 
267
  speechSynthesis.speak(utterance);
268
  utterance.onend = () => {
269
- status.textContent = 'Listening...';
270
  startListening();
271
  };
272
  }
273
- function startListening() {
274
- navigator.mediaDevices.getUserMedia({ audio: true }).then(stream => {
275
- mediaRecorder = new MediaRecorder(stream, { mimeType: 'audio/webm;codecs=opus' });
276
- mediaRecorder.start();
277
- audioChunks = [];
278
- mediaRecorder.ondataavailable = event => audioChunks.push(event.data);
279
- mediaRecorder.onstop = async () => {
280
- const audioBlob = new Blob(audioChunks, { type: 'audio/webm' });
281
- const formData = new FormData();
282
- formData.append('audio', audioBlob);
283
- status.textContent = 'Processing...';
284
- try {
285
- const result = await fetch('/process-audio', { method: 'POST', body: formData });
286
- const data = await result.json();
287
- response.textContent = 'You said: ' + data.response; // Display user text
288
- response.style.display = 'block';
289
- const utterance = new SpeechSynthesisUtterance(data.response);
290
- speechSynthesis.speak(utterance);
291
- utterance.onend = () => {
292
- console.log("Speech synthesis completed.");
293
- status.textContent = 'Listening...';
294
- setTimeout(() => {
295
- startListening();
296
- }, 100);
297
- };
298
- utterance.onerror = (e) => {
299
- console.error("Speech synthesis error:", e.error);
300
- status.textContent = 'Error with speech output.';
301
- isConversationActive = false;
302
- };
303
- } catch (error) {
304
- response.textContent = 'Sorry, I could not understand. Please try again.';
305
- response.style.display = 'block';
306
- status.textContent = 'Press the mic button to restart the conversation.';
307
- isConversationActive = false;
308
- }
309
- };
310
- setTimeout(() => mediaRecorder.stop(), 5000);
311
- }).catch(() => {
312
- status.textContent = 'Microphone access denied.';
313
- isConversationActive = false;
314
- });
315
  }
316
  </script>
317
  </body>
318
  </html>
319
  """
320
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
321
  if __name__ == "__main__":
322
  app.run(host="0.0.0.0", port=7860)
 
1
+ voice assistant
2
+
 
3
  from flask import Flask, render_template_string, request, jsonify
4
+ import speech_recognition as sr
5
  from tempfile import NamedTemporaryFile
6
+ import os
7
  import ffmpeg
8
+ import logging
9
+ from werkzeug.exceptions import BadRequest
10
 
 
11
  app = Flask(__name__)
12
  logging.basicConfig(level=logging.INFO)
13
 
14
  # Global variables
15
+ cart = [] # To store items, quantities, and prices
16
+ MENU = {
17
+ "Biryani": {"Chicken Biryani": 250, "Veg Biryani": 200, "Mutton Biryani": 300},
18
+ "Starters": {"Chicken Wings": 220, "Paneer Tikka": 180, "Fish Fingers": 250, "Spring Rolls": 160},
19
+ "Breads": {"Butter Naan": 50, "Garlic Naan": 60, "Roti": 40, "Lachha Paratha": 70},
20
+ "Curries": {"Butter Chicken": 300, "Paneer Butter Masala": 250, "Dal Tadka": 200, "Chicken Tikka Masala": 320},
21
+ "Drinks": {"Coke": 60, "Sprite": 60, "Mango Lassi": 80, "Masala Soda": 70},
22
+ "Desserts": {"Gulab Jamun": 100, "Rasgulla": 90, "Ice Cream": 120, "Brownie with Ice Cream": 180},
 
23
  }
24
+ current_category = None
25
+ current_item = None
26
+ awaiting_quantity = False
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
27
 
28
+ # HTML Template for Frontend
29
  html_code = """
30
  <!DOCTYPE html>
31
  <html lang="en">
 
35
  <title>AI Dining Assistant</title>
36
  <style>
37
  body {
 
 
 
 
 
 
38
  font-family: Arial, sans-serif;
39
+ text-align: center;
40
  background-color: #f4f4f9;
41
  }
42
  h1 {
43
  color: #333;
44
  }
45
  .mic-button {
46
+ width: 80px;
47
+ height: 80px;
48
+ border-radius: 50%;
49
  background-color: #007bff;
50
+ color: white;
51
+ font-size: 24px;
52
  border: none;
 
53
  cursor: pointer;
 
 
 
 
54
  }
55
  .status, .response {
56
+ margin-top: 20px;
 
 
 
 
 
 
 
 
 
 
57
  }
58
  </style>
59
  </head>
 
61
  <h1>AI Dining Assistant</h1>
62
  <button class="mic-button" id="mic-button">🎤</button>
63
  <div class="status" id="status">Press the mic button to start...</div>
64
+ <div class="response" id="response" style="display: none;">Response will appear here...</div>
65
  <script>
66
  const micButton = document.getElementById('mic-button');
67
  const status = document.getElementById('status');
68
  const response = document.getElementById('response');
69
+ let isListening = false;
70
+
 
71
  micButton.addEventListener('click', () => {
72
+ if (!isListening) {
73
+ isListening = true;
74
+ greetUser();
75
  }
76
  });
77
+
78
+ function greetUser() {
79
+ const utterance = new SpeechSynthesisUtterance("Hi. Welcome to Biryani Hub. Can I show you the menu?");
80
  speechSynthesis.speak(utterance);
81
  utterance.onend = () => {
82
+ status.textContent = "Listening...";
83
  startListening();
84
  };
85
  }
86
+
87
+ async function startListening() {
88
+ const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
89
+ const mediaRecorder = new MediaRecorder(stream, { mimeType: "audio/webm;codecs=opus" });
90
+ const audioChunks = [];
91
+
92
+ mediaRecorder.ondataavailable = (event) => audioChunks.push(event.data);
93
+ mediaRecorder.onstop = async () => {
94
+ const audioBlob = new Blob(audioChunks, { type: "audio/webm" });
95
+ const formData = new FormData();
96
+ formData.append("audio", audioBlob);
97
+
98
+ status.textContent = "Processing...";
99
+ try {
100
+ const result = await fetch("/process-audio", { method: "POST", body: formData });
101
+ const data = await result.json();
102
+ response.textContent = data.response;
103
+ response.style.display = "block";
104
+
105
+ const utterance = new SpeechSynthesisUtterance(data.response);
106
+ speechSynthesis.speak(utterance);
107
+ utterance.onend = () => {
108
+ if (!data.response.includes("Goodbye") && !data.response.includes("final order")) {
109
+ startListening(); // Continue listening
110
+ } else {
111
+ status.textContent = "Conversation ended.";
112
+ isListening = false;
113
+ }
114
+ };
115
+ } catch (error) {
116
+ response.textContent = "Error processing your request. Please try again.";
117
+ status.textContent = "Press the mic button to restart.";
118
+ isListening = false;
119
+ }
120
+ };
121
+
122
+ mediaRecorder.start();
123
+ setTimeout(() => mediaRecorder.stop(), 5000); // Stop recording after 5 seconds
 
 
 
 
124
  }
125
  </script>
126
  </body>
127
  </html>
128
  """
129
 
130
+ @app.route("/")
131
+ def index():
132
+ return render_template_string(html_code)
133
+
134
+ @app.route("/process-audio", methods=["POST"])
135
+ def process_audio():
136
+ global current_category, current_item, awaiting_quantity
137
+ try:
138
+ audio_file = request.files.get("audio")
139
+ if not audio_file:
140
+ raise BadRequest("No audio file provided.")
141
+
142
+ temp_file = NamedTemporaryFile(delete=False, suffix=".webm")
143
+ audio_file.save(temp_file.name)
144
+
145
+ if os.path.getsize(temp_file.name) == 0:
146
+ raise BadRequest("Uploaded audio file is empty.")
147
+
148
+ converted_file = NamedTemporaryFile(delete=False, suffix=".wav")
149
+ ffmpeg.input(temp_file.name).output(
150
+ converted_file.name, acodec="pcm_s16le", ac=1, ar="16000"
151
+ ).run(overwrite_output=True)
152
+
153
+ recognizer = sr.Recognizer()
154
+ with sr.AudioFile(converted_file.name) as source:
155
+ audio_data = recognizer.record(source)
156
+ try:
157
+ command = recognizer.recognize_google(audio_data)
158
+ logging.info(f"Recognized command: {command}")
159
+ response = process_command(command)
160
+ except sr.UnknownValueError:
161
+ response = "Sorry, I couldn't understand your command. Could you please repeat?"
162
+ except sr.RequestError as e:
163
+ response = f"Error with the speech recognition service: {e}"
164
+
165
+ return jsonify({"response": response})
166
+
167
+ except BadRequest as br:
168
+ return jsonify({"response": f"Bad Request: {str(br)}"}), 400
169
+ except Exception as e:
170
+ return jsonify({"response": f"An error occurred: {str(e)}"}), 500
171
+ finally:
172
+ os.unlink(temp_file.name)
173
+ os.unlink(converted_file.name)
174
+
175
+ def process_command(command):
176
+ global cart, MENU, current_category, current_item, awaiting_quantity
177
+ command = command.lower()
178
+
179
+ # Handle quantity input
180
+ if awaiting_quantity and command.isdigit():
181
+ quantity = int(command)
182
+ if quantity > 0:
183
+ cart.append((current_item, MENU[current_category][current_item], quantity))
184
+ awaiting_quantity = False
185
+ item = current_item
186
+ current_item = None
187
+ total = sum(i[1] * i[2] for i in cart)
188
+ cart_summary = ", ".join([f"{i[0]} x{i[2]} (₹{i[1] * i[2]})" for i in cart])
189
+ return f"Added {quantity} of {item} to your cart. Cart: {cart_summary}. Total: ₹{total}. Would you like to see the menu again?"
190
+ else:
191
+ return "Quantity must be at least 1. How many would you like to order?"
192
+
193
+ # Handle category selection
194
+ for category, items in MENU.items():
195
+ if category.lower() in command:
196
+ current_category = category
197
+ item_list = ", ".join([f"{item} (₹{price})" for item, price in items.items()])
198
+ return f"{category} menu: {item_list}. What would you like to order?"
199
+
200
+ # Handle item selection with dynamic matching
201
+ if current_category:
202
+ for item in MENU[current_category].keys():
203
+ if item.lower().startswith(command) or command in item.lower():
204
+ current_item = item
205
+ awaiting_quantity = True
206
+ return f"How many quantities of {current_item} would you like?"
207
+
208
+ # Handle item removal
209
+ if "remove" in command:
210
+ for item in cart:
211
+ if item[0].lower() in command:
212
+ cart.remove(item)
213
+ total = sum(i[1] * i[2] for i in cart)
214
+ cart_summary = ", ".join([f"{i[0]} x{i[2]} (₹{i[1] * i[2]})" for i in cart])
215
+ return f"Removed {item[0]} from your cart. Updated cart: {cart_summary}. Total: ₹{total}."
216
+ return "The item you are trying to remove is not in your cart."
217
+
218
+ # Handle final order
219
+ if "final order" in command or "submit" in command:
220
+ if cart:
221
+ order_details = ", ".join([f"{item[0]} x{item[2]} (₹{item[1] * item[2]})" for item in cart])
222
+ total = sum(item[1] * item[2] for item in cart)
223
+ cart.clear()
224
+ return f"Your final order is: {order_details}. Total price: ₹{total}. Thank you for visiting Biryani Hub!"
225
+ else:
226
+ return "Your cart is empty. Please add items before placing the final order."
227
+
228
+ # Handle cart details
229
+ if "cart details" in command:
230
+ if cart:
231
+ cart_summary = "\n".join([f"{i[0]} x{i[2]} (₹{i[1] * i[2]})" for i in cart])
232
+ total = sum(i[1] * i[2] for i in cart)
233
+ return f"Your cart contains:\n{cart_summary}\nTotal: ₹{total}."
234
+ else:
235
+ return "Your cart is empty."
236
+
237
+ # Generic response for menu request
238
+ if "menu" in command:
239
+ categories = ", ".join(MENU.keys())
240
+ return f"We have the following categories: {categories}. Please select a category to proceed."
241
+
242
+ # Default response
243
+ return "Sorry, I didn't understand that. Please try again."
244
+
245
  if __name__ == "__main__":
246
  app.run(host="0.0.0.0", port=7860)