File size: 13,575 Bytes
184f91f
af3db37
b2c4de5
184f91f
 
 
 
408a4d8
184f91f
 
4038fb7
 
184f91f
 
2475fd0
184f91f
e8c5011
184f91f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
e8c5011
 
 
 
 
 
 
 
 
184f91f
 
 
 
 
 
 
2475fd0
184f91f
2475fd0
184f91f
 
 
 
57d1c8e
 
53dd297
57d1c8e
 
0ff8b33
722989a
0ff8b33
57d1c8e
 
 
 
 
 
 
722989a
53dd297
57d1c8e
 
10a4f25
27ba021
57d1c8e
53dd297
57d1c8e
 
e8c5011
d43322c
 
 
 
 
 
 
808d1d6
57d1c8e
10a4f25
 
57d1c8e
 
 
 
 
 
 
 
70af9cc
184f91f
 
 
 
 
 
 
53dd297
2475fd0
53dd297
c48eddf
 
 
 
 
 
 
 
 
2475fd0
c48eddf
 
 
 
e8c5011
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
0ff8b33
 
e8c5011
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
82c9131
0ff8b33
 
70af9cc
0ff8b33
 
 
dd6c67f
e8c5011
dd6c67f
53dd297
 
0ff8b33
53dd297
 
 
 
 
 
 
 
 
 
 
 
184f91f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
e8c5011
184f91f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
e8c5011
 
 
 
 
 
 
 
 
184f91f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
e8c5011
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
import os
import speech_recognition as sr
import logging
from flask import Flask, render_template_string, request, jsonify
from tempfile import NamedTemporaryFile
import ffmpeg
from fuzzywuzzy import process, fuzz

# Initialize Flask app
app = Flask(__name__)
logging.basicConfig(level=logging.INFO)

# Global variables
cart = []  # Stores items as [item_name, price, quantity] in the cart
menu_preferences = None  # Tracks the current menu preference
section_preferences = None  # Tracks the current section preference
default_menu_preferences = "all"  # To reset menu preferences
default_sections = {
    "biryanis": ["veg biryani", "paneer biryani", "chicken biryani", "mutton biryani"],
    "starters": ["samosa", "onion pakoda", "chilli gobi", "chicken manchurian", "veg manchurian"],
    "curries": ["paneer butter", "chicken curry", "fish curry", "chilli chicken"],
    "desserts": ["gulab jamun", "ice cream"],
    "soft drinks": ["cola", "lemon soda"]
}
prices = {
    "samosa": 9,
    "onion pakoda": 10,
    "chilli gobi": 12,
    "chicken biryani": 14,
    "mutton biryani": 16,
    "veg biryani": 12,
    "paneer butter": 10,
    "fish curry": 12,
    "chicken manchurian": 14,
    "veg manchurian": 12,
    "chilli chicken": 14,
    "paneer biryani": 13,
    "chicken curry": 14,
    "gulab jamun": 8,
    "ice cream": 6,
    "cola": 5,
    "lemon soda": 6
}
menus = {
    "all": list(prices.keys()),
    "vegetarian": [
        "samosa", "onion pakoda", "chilli gobi", "veg biryani", "paneer butter", "veg manchurian", "paneer biryani", "gulab jamun", "ice cream", "cola", "lemon soda"
    ],
    "non-vegetarian": [
        "chicken biryani", "mutton biryani", "fish curry", "chicken manchurian", "chilli chicken", "chicken curry", "gulab jamun", "ice cream", "cola", "lemon soda"
    ]
}

@app.route("/")
def index():
    return render_template_string(html_code)

@app.route("/reset-cart", methods=["GET"])
def reset_cart():
    global cart, menu_preferences, section_preferences
    cart = []
    menu_preferences = None
    section_preferences = None
    return "Cart reset successfully."

@app.route("/process-audio", methods=["POST"])
def process_audio():
    try:
        # Handle audio input
        audio_file = request.files.get("audio")
        if not audio_file:
            return jsonify({"response": "Oops! I didn't catch any audio. Please try again."}), 400

        # Save and convert audio to WAV format
        temp_file = NamedTemporaryFile(delete=False, suffix=".webm")
        audio_file.save(temp_file.name)

        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)

        # Recognize speech
        recognizer = sr.Recognizer()
        recognizer.dynamic_energy_threshold = True
        recognizer.energy_threshold = 4000  # Increased sensitivity

        with sr.AudioFile(converted_file.name) as source:
            recognizer.adjust_for_ambient_noise(source, duration=1)
            audio_data = recognizer.record(source)

        # Use multiple recognition services with fallbacks
        try:
            raw_command = recognizer.recognize_google(audio_data).lower()
        except sr.UnknownValueError:
            raw_command = "Sorry, I couldn't understand that."
        except sr.RequestError as e:
            raw_command = f"Request error from the service: {e}"

        logging.info(f"User said: {raw_command}")  # Print user speech in the console

        # Display the transcribed text and AI voice response
        response = process_command(raw_command)

    except Exception as e:
        response = f"An error occurred: {str(e)}"
    finally:
        os.unlink(temp_file.name)
        os.unlink(converted_file.name)

    return jsonify({"response": response})

def preprocess_command(command):
    """
    Normalize the user command to improve matching.
    """
    command = command.strip().lower()
    return command

def process_command(command):
    global cart, menu_preferences, section_preferences

    # Finalize order
    if "final order" in command or "complete order" in command:
        if not cart:
            return "Your cart is empty. Please add items before finalizing the order."
        
        order_summary = "\n".join([f"{item[2]} x {item[0]} for {item[1] * item[2]} INR" for item in cart])
        total_price = sum(item[1] * item[2] for item in cart)
        
        cart.clear()  # Clear the cart after finalizing
        menu_preferences = None
        section_preferences = None

        return f"Your order has been placed successfully:\n{order_summary}\nTotal: {total_price} INR.\nThank you for ordering!"

    # Greet the user and ask for preferences when first started
    if menu_preferences is None:
        if "hello" in command or "hi" in command:
            return "Hello, welcome to Biryani Hub! Please choose your preference: All, Vegetarian, or Non-Vegetarian."

        preferences = ["non-vegetarian", "vegetarian", "all"]
        if command in preferences:
            menu_preferences = command
            return f"You've selected the {command} menu! Which section would you like to browse next? (e.g., biryanis, starters, curries, desserts, soft drinks)"

        # Use fuzzy matching to help recognize similar inputs
        closest_match = process.extractOne(command, preferences, scorer=fuzz.partial_ratio)
        if closest_match and closest_match[1] > 75:
            menu_preferences = closest_match[0]
            return f"Great choice! You've chosen the {menu_preferences} menu. Which section would you like to browse next?"

        return "I couldn't recognize your choice. Please say either 'Non-Vegetarian', 'Vegetarian', or 'All'."

    if section_preferences is None:
        sections = list(default_sections.keys())
        for section in sections:
            if section in command:
                section_preferences = section
                return f"Here are the items in the {section_preferences} section: {', '.join(default_sections[section_preferences])}. Please choose an item."

        closest_match = process.extractOne(command, sections, scorer=fuzz.partial_ratio)
        if closest_match and closest_match[1] > 75:
            section_preferences = closest_match[0]
            return f"Here are the items in the {section_preferences} section: {', '.join(default_sections[section_preferences])}. What would you like to add?"

        return "I didn't catch that. Please say a section like 'biryanis', 'starters', 'curries', 'desserts', or 'soft drinks'."

    # Filter items based on the menu preference (vegetarian/non-vegetarian)
    available_items = []
    if menu_preferences == "vegetarian":
        available_items = [item for item in default_sections[section_preferences] if item in menus["vegetarian"]]
    elif menu_preferences == "non-vegetarian":
        available_items = [item for item in default_sections[section_preferences] if item in menus["non-vegetarian"]]
    elif menu_preferences == "all":
        available_items = [item for item in default_sections[section_preferences]]

    for item in available_items:
        if item in command:
            quantity = extract_quantity(command)
            if quantity:
                cart.append([item, prices[item], quantity])
                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?"

    return "I didn't recognize the item you mentioned. Please say the item name clearly, or choose from the available items."

def extract_quantity(command):
    """
    Extract quantity from the command (e.g., 'two', '3', '5').
    """
    number_words = {
        "one": 1, "two": 2, "three": 3, "four": 4, "five": 5,
        "six": 6, "seven": 7, "eight": 8, "nine": 9, "ten": 10,
        "1": 1, "2": 2, "3": 3, "4": 4, "5": 5, "6": 6, "7": 7, "8": 8, "9": 9, "10": 10
    }

    command_words = command.split()
    for word in command_words:
        if word in number_words:
            return number_words[word]
    return None

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('Hello, welcome to Biryani Hub! Please choose your preference: All, Vegetarian, or Non-Vegetarian.');
            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 = 'You said: ' + data.response; // Display user text
                        response.style.display = 'block';
                        const utterance = new SpeechSynthesisUtterance(data.response);
                        speechSynthesis.speak(utterance);
                        utterance.onend = () => {
                            console.log("Speech synthesis completed.");
                            if (data.response.includes("final order") || data.response.includes("Thank you for ordering")) {
                                status.textContent = 'Order completed. Press the mic button to start again.';
                                isConversationActive = false;
                            } else {
                                status.textContent = 'Listening...';
                                setTimeout(() => {
                                    startListening();
                                }, 100);
                            }
                        };
                        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>
"""

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=7860)