AIVoice8 / app.py
nagasurendra's picture
Update app.py
28f3ba6 verified
from flask import Flask, render_template_string, request, jsonify
import speech_recognition as sr
from tempfile import NamedTemporaryFile
import os
import ffmpeg
import logging
from werkzeug.exceptions import BadRequest
from fuzzywuzzy import process
app = Flask(__name__)
logging.basicConfig(level=logging.INFO)
# Global variables
cart = []
menu_preferences = "all"
prices = {
"samosa": 9,
"onion pakoda": 10,
"chilli gobi": 12,
"chicken biryani": 14,
"mutton biryani": 16,
"veg biryani": 12,
"panner butter": 10,
"fish curry": 12,
"chicken manchurian": 14,
"veg manchurian": 12,
"chilli chicken": 14,
"panner biryani": 13,
"chicken curry": 14
}
menus = {
"all": list(prices.keys()),
"vegetarian": [
"samosa", "onion pakoda", "chilli gobi", "veg biryani", "panner butter", "veg manchurian", "panner biryani"
],
"non-vegetarian": [
"chicken biryani", "mutton biryani", "fish curry", "chicken manchurian", "chilli chicken", "chicken curry"
],
"guilt-free": ["samosa", "onion pakoda"]
}
html_code = """
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>AI Dining Assistant</title>
<style>
body {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
min-height: 100vh;
margin: 0;
font-family: Arial, sans-serif;
background-color: #f4f4f9;
}
h1 {
color: #333;
}
.mic-button {
font-size: 2rem;
padding: 1rem 2rem;
color: white;
background-color: #007bff;
border: none;
border-radius: 50px;
cursor: pointer;
transition: background-color 0.3s;
}
.mic-button:hover {
background-color: #0056b3;
}
.status, .response {
margin-top: 1rem;
text-align: center;
color: #555;
font-size: 1.2rem;
}
.response {
background-color: #e8e8ff;
padding: 1rem;
border-radius: 10px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
display: none;
}
</style>
</head>
<body>
<h1>AI Dining Assistant</h1>
<button class="mic-button" id="mic-button">🎤</button>
<div class="status" id="status">Press the mic button to start...</div>
<div class="response" id="response">Response will appear here...</div>
<script>
const micButton = document.getElementById('mic-button');
const status = document.getElementById('status');
const response = document.getElementById('response');
let mediaRecorder;
let audioChunks = [];
let isConversationActive = false;
micButton.addEventListener('click', () => {
if (!isConversationActive) {
isConversationActive = true;
startConversation();
}
});
function startConversation() {
const utterance = new SpeechSynthesisUtterance('Please choose your preference: All, Vegetarian, Non-Vegetarian, or Guilt-Free.');
speechSynthesis.speak(utterance);
utterance.onend = () => {
status.textContent = 'Listening...';
startListening();
};
}
function startListening() {
navigator.mediaDevices.getUserMedia({ audio: true }).then(stream => {
mediaRecorder = new MediaRecorder(stream, { mimeType: 'audio/webm;codecs=opus' });
mediaRecorder.start();
audioChunks = [];
mediaRecorder.ondataavailable = event => audioChunks.push(event.data);
mediaRecorder.onstop = async () => {
const audioBlob = new Blob(audioChunks, { type: 'audio/webm' });
const formData = new FormData();
formData.append('audio', audioBlob);
status.textContent = 'Processing...';
try {
const result = await fetch('/process-audio', { method: 'POST', body: formData });
const data = await result.json();
response.textContent = data.response;
response.style.display = 'block';
const utterance = new SpeechSynthesisUtterance(data.response);
speechSynthesis.speak(utterance);
utterance.onend = () => {
console.log("Speech synthesis completed.");
if (data.response.includes("Goodbye")) {
status.textContent = 'Conversation ended. Press the mic button to start again.';
isConversationActive = false;
fetch('/reset-cart'); // Reset the cart dynamically on end
} else if (data.response.includes("Your order is complete")) {
status.textContent = 'Order complete. Thank you for using AI Dining Assistant.';
isConversationActive = false;
fetch('/reset-cart'); // Reset the cart after final order
} else {
status.textContent = 'Listening...';
setTimeout(() => {
startListening();
}, 500);
}
};
utterance.onerror = (e) => {
console.error("Speech synthesis error:", e.error);
status.textContent = 'Error with speech output.';
isConversationActive = false;
};
} catch (error) {
response.textContent = 'Sorry, I could not understand. Please try again.';
response.style.display = 'block';
status.textContent = 'Press the mic button to restart the conversation.';
isConversationActive = false;
}
};
setTimeout(() => mediaRecorder.stop(), 5000);
}).catch(() => {
status.textContent = 'Microphone access denied.';
isConversationActive = false;
});
}
</script>
</body>
</html>
"""
@app.route('/')
def index():
return render_template_string(html_code)
@app.route('/reset-cart', methods=['GET'])
def reset_cart():
global cart
global menu_preferences
cart = []
menu_preferences = "all"
return "Cart reset successfully."
@app.route('/process-audio', methods=['POST'])
def process_audio():
try:
audio_file = request.files.get('audio')
if not audio_file:
raise BadRequest("No audio file provided.")
temp_file = NamedTemporaryFile(delete=False, suffix=".webm")
audio_file.save(temp_file.name)
if os.path.getsize(temp_file.name) == 0:
raise BadRequest("Uploaded audio file is empty.")
converted_file = NamedTemporaryFile(delete=False, suffix=".wav")
ffmpeg.input(temp_file.name).output(
converted_file.name, acodec='pcm_s16le', ac=1, ar='16000'
).run(overwrite_output=True)
recognizer = sr.Recognizer()
recognizer.energy_threshold = 300 # Adjust for low-volume recognition
with sr.AudioFile(converted_file.name) as source:
audio_data = recognizer.record(source)
try:
command = recognizer.recognize_google(audio_data)
response = process_command(command)
except sr.UnknownValueError:
response = "Sorry, I could not understand. Please try again."
return jsonify({"response": response})
except BadRequest as br:
return jsonify({"response": f"Bad Request: {str(br)}"}), 400
except Exception as e:
return jsonify({"response": f"An error occurred: {str(e)}"}), 500
finally:
os.unlink(temp_file.name)
os.unlink(converted_file.name)
# Add this to the imports
def process_command(command):
global cart, menu_preferences
command = command.lower()
# Recognize menu preferences first
if menu_preferences == "all":
if "vegetarian" in command and "non-vegetarian" not in command:
menu_preferences = "vegetarian"
return "You have chosen the Vegetarian menu. To view menu say menu"
elif "non-vegetarian" in command:
menu_preferences = "non-vegetarian"
return "You have chosen the Non-Vegetarian menu. To view menu say menu"
elif "guilt-free" in command:
menu_preferences = "guilt-free"
return "You have chosen the Guilt-Free menu. To view menu say menu"
elif "all" in command:
menu_preferences = "all"
return "You have chosen the complete menu. To view menu say menu"
# Use filtered menu based on preference
menu = menus.get(menu_preferences, menus["all"])
if "menu" in command:
return f"Here is your menu: {', '.join(menu)}. To select an item say item name"
elif "price of" in command:
item = command.replace("price of", "").strip()
closest_match = process.extractOne(item, prices.keys())
if closest_match and closest_match[1] > 80: # Match threshold
matched_item = closest_match[0]
return f"The price of {matched_item} is ${prices[matched_item]}."
return "Sorry, I couldn't find that item in the menu."
elif "remove" in command:
item = command.replace("remove", "").strip()
closest_match = process.extractOne(item, cart)
if closest_match and closest_match[1] > 80:
matched_item = closest_match[0]
cart.remove(matched_item)
return f"{matched_item.capitalize()} has been removed from your cart."
return "Sorry, that item is not in your cart."
elif any(item in command for item in menu):
matched_item = next((item for item in menu if item in command), None)
if matched_item:
cart.append(matched_item)
return f"{matched_item.capitalize()} added to your cart. Your cart now has: {', '.join(cart)}. To add another item say item name. To submit your order say final order. To know the price of an item say price of item name. To remove an item say remove item name."
else:
return "Sorry, I couldn't recognize the item. Could you try again?"
elif "final order" in command or "submit" in command:
if cart:
total = sum(prices[item] for item in cart if item in prices)
response = f"Your final order is: {', '.join(cart)}. Your total bill is ${total}. Thank you for ordering! To stop this conversation say nothing or good bye!"
cart.clear() # Reset the cart after final order
return response
else:
return "Your cart is empty. Please add items to your cart first."
elif "no" in command or "nothing" in command or "goodbye" in command:
cart.clear() # Clear cart on exit
menu_preferences = "all"
return "Goodbye! Thank you for using AI Dining Assistant."
return "Sorry, I couldn't understand that. Please try again."
if __name__ == "__main__":
app.run(host="0.0.0.0", port=7860)