File size: 13,613 Bytes
945d875
4038fb7
af3db37
4038fb7
710a6ce
af3db37
 
 
b2c4de5
408a4d8
4038fb7
 
 
af3db37
9d03ef3
5796daf
 
ace7441
5796daf
177a208
5796daf
 
 
 
 
4038fb7
 
 
 
 
 
 
ace7441
4038fb7
 
 
 
ace7441
5796daf
 
 
 
 
4038fb7
 
 
 
5796daf
4038fb7
 
050716b
5796daf
4038fb7
 
b2c4de5
 
 
 
 
 
5796daf
9d03ef3
5796daf
 
b2c4de5
 
 
 
 
ace7441
b2c4de5
 
 
 
ace7441
b2c4de5
 
 
 
 
ace7441
b2c4de5
 
ace7441
af3db37
 
050716b
af3db37
 
 
 
ace7441
710a6ce
b2c4de5
ace7441
5796daf
b2c4de5
ace7441
710a6ce
b2c4de5
ace7441
 
b2c4de5
 
 
 
 
 
 
 
5796daf
b2c4de5
948d939
b2c4de5
948d939
 
 
b2c4de5
5796daf
b2c4de5
be9516d
 
 
ace7441
5796daf
44e324b
948d939
050716b
948d939
44e324b
 
 
 
 
 
 
b2c4de5
177a208
 
44e324b
 
 
 
 
177a208
 
 
 
be9516d
 
 
 
 
 
 
 
 
 
050716b
be9516d
 
177a208
5796daf
9d03ef3
be9516d
 
 
050716b
be9516d
9d03ef3
b2c4de5
5796daf
9d03ef3
 
 
050716b
be9516d
 
 
050716b
9d03ef3
80c1dcb
be9516d
9d03ef3
5796daf
 
 
 
769aae3
 
 
1b17dea
 
769aae3
 
1b17dea
 
769aae3
1b17dea
 
769aae3
 
 
5796daf
 
948d939
af3db37
 
4038fb7
 
 
 
 
af3db37
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4038fb7
 
 
af3db37
 
 
4038fb7
ddf45e9
af3db37
 
 
 
 
ddf45e9
af3db37
 
 
 
ddf45e9
af3db37
5796daf
af3db37
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
769aae3
 
 
 
 
 
 
 
 
af3db37
9d03ef3
 
 
 
 
af3db37
 
 
 
 
 
 
 
 
 
 
 
 
4038fb7
 
af3db37
4f833eb
ace7441
945d875
4f833eb
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
335
336
337

from flask import Flask, render_template_string, request, jsonify
import speech_recognition as sr
from tempfile import NamedTemporaryFile
import os
import ffmpeg
from fuzzywuzzy import process, fuzz
from metaphone import doublemetaphone
import logging

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": "No audio file provided."}), 400

        # Save audio file and convert 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 = 60
        with sr.AudioFile(converted_file.name) as source:
            recognizer.adjust_for_ambient_noise(source, duration=1)
            audio_data = recognizer.record(source)

        raw_command = recognizer.recognize_google(audio_data).lower()
        logging.info(f"Raw recognized command: {raw_command}")

        # Preprocess the command
        command = preprocess_command(raw_command)

        # Process the command and get a response
        response = process_command(command)

    except sr.UnknownValueError:
        response = "Sorry, I couldn't understand. Please try again."
    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

    # Log command and current preferences for debugging
    logging.info(f"Command: {command}, Menu Preferences: {menu_preferences}, Section Preferences: {section_preferences}")

    # Handle menu preferences
    if menu_preferences is None:
        preferences = ["non-vegetarian", "vegetarian", "all"]
        closest_match = process.extractOne(command, preferences, scorer=fuzz.partial_ratio)
        if closest_match and closest_match[1] > 60:
            menu_preferences = closest_match[0]
            if menu_preferences == "non-vegetarian":
                return "You have chosen the Non-Vegetarian menu. Which section would you like? (biryanis, starters, curries, desserts, soft drinks)"
            elif menu_preferences == "vegetarian":
                return "You have chosen the Vegetarian menu. Which section would you like? (biryanis, starters, curries, desserts, soft drinks)"
            elif menu_preferences == "all":
                return "You have chosen the complete menu. Which section would you like? (biryanis, starters, curries, desserts, soft drinks)"
        return "Please specify your preference: Non-Vegetarian, Vegetarian, or All."

    # Handle section preferences
    if section_preferences is None or any(section in command for section in default_sections.keys()):
        for section in default_sections.keys():
            if section in command:
                section_preferences = section
                available_items = [item for item in default_sections[section] if item in menus[menu_preferences]]
                return f"Here are the items in {section}: {', '.join(available_items)}. Say the item name to add to the cart."
        return "Please specify a section: biryanis, starters, curries, desserts, or soft drinks."

    # Handle item addition
    available_items = [item for item in default_sections[section_preferences] if item in menus[menu_preferences]]
    for item in available_items:
        if item in command:
            quantity = extract_quantity(command)
            if quantity is not None:
                for cart_item in cart:
                    if cart_item[0] == item:
                        cart_item[2] += quantity
                        break
                else:
                    cart.append([item, prices[item], quantity])
                cart_summary = ", ".join([f"{i[0]} x{i[2]} (${i[1] * i[2]})" for i in cart])
                return f"Added {quantity} x {item} to your cart. Current cart: {cart_summary}. Would you like to choose another section or finalize your order?"
            return "Please specify a quantity between 1 and 10. Say 'Add 2' or 'Add three' followed by the item name."

    # Handle remove command
    if "remove" in command:
        for item in cart:
            if item[0] in command:
                cart.remove(item)
                cart_summary = ", ".join([f"{i[0]} x{i[2]} (${i[1] * i[2]})" for i in cart])
                return f"Removed {item[0]} from your cart. Current cart: {cart_summary}."
        return "The item you are trying to remove is not in your cart."

    # Handle final order
    if "final order" in command:
        if cart:
            total = sum(i[1] * i[2] for i in cart)
            order_summary = ", ".join([f"{i[0]} x{i[2]} (${i[1] * i[2]})" for i in cart])
            cart.clear()
            menu_preferences = None
            section_preferences = None
            return f"Your final order is: {order_summary}. Total bill: ${total}. Thank you for ordering! You can start a new order by specifying your preference."
        return "Your cart is empty. Please add items to your cart first."


    return "Sorry, I couldn't understand that. Please try again."
def extract_quantity(command):
    """
    Extract quantity from the user command.
    """
    # Map numeric words to digits
    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
    }

    # Normalize command and split into words
    command_words = command.split()

    # Check for numeric words or digits in the command
    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('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 = 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("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)