import os import requests import gradio as gr from gtts import gTTS from datetime import datetime from openpyxl import Workbook, load_workbook import re from langchain.memory import ConversationBufferMemory # ========== CONFIGURATION ========== API_KEY = "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" MODEL = "google/gemma-3n-e2b-it:free" MENU = { "Cheeseburger": 5.99, "Fries": 2.99, "Coke": 1.99, "Pizza": 12.99, "Chicken Wings": 7.99, "Salad": 6.99 } memory = ConversationBufferMemory(return_messages=True) order = [] customer_name = "" # ========== Excel Setup ========== EXCEL_FILE = "orders.xlsx" def setup_excel(): if not os.path.exists(EXCEL_FILE): wb = Workbook() ws = wb.active ws.title = "Orders" ws.append(["Order ID", "Date", "Customer", "Items", "Total", "Time"]) wb.save(EXCEL_FILE) setup_excel() def save_to_excel(name, items): wb = load_workbook(EXCEL_FILE) ws = wb.active order_id = f"ORD{ws.max_row:04d}" now = datetime.now() total = sum(qty * MENU[item] for item, qty in items) items_str = ", ".join(f"{qty} x {item}" for item, qty in items) ws.append([order_id, now.strftime("%Y-%m-%d"), name, items_str, f"${total:.2f}", now.strftime("%H:%M:%S")]) wb.save(EXCEL_FILE) return order_id # ========== Text-to-Speech ========== def clean_text(text): text = re.sub(r"\*\*(.*?)\*\*", r"\1", text) text = re.sub(r"Bot\s*:\s*", "", text, flags=re.IGNORECASE) return text.strip() def speak(text, filename="response.mp3"): cleaned = clean_text(text) tts = gTTS(text=cleaned) tts.save(filename) return filename # ========== OpenRouter API ========== def generate_response(user_input): global customer_name, order memory.chat_memory.add_user_message(user_input) menu_description = "\n".join([f"{item}: ${price}" for item, price in MENU.items()]) order_summary = ", ".join([f"{qty} x {item}" for item, qty in order]) if order else "No items yet" context = f""" You are a helpful, kind restaurant assistant at 'PandaAI'. MENU: {menu_description} Customer name: {customer_name} Current order: {order_summary} Instructions: - Ask for name if not known. - Show menu if requested. - Extract item names and quantities from messages. - Say 'Order summary' and ask 'Confirm?' when user is done. - Respond only as the bot, no need to prefix with \"Bot:\". - Keep tone human, natural, and friendly. Conversation: """ for msg in memory.chat_memory.messages: if msg.type == "human": context += f"\nCustomer: {msg.content}" else: context += f"\nBot: {msg.content}" context += f"\nCustomer: {user_input}\nBot:" headers = { "Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json" } payload = { "model": MODEL, "messages": [{"role": "user", "content": context}], } try: response = requests.post("https://openrouter.ai/api/v1/chat/completions", headers=headers, json=payload) response.raise_for_status() reply = response.json()["choices"][0]["message"]["content"] memory.chat_memory.add_ai_message(reply) return reply except Exception as e: return f"āŒ OpenRouter Error: {str(e)}" # ========== Chat Logic ========== def handle_chat(user_input): global customer_name, order bot_reply = generate_response(user_input) if "my name is" in user_input.lower(): customer_name = user_input.split("my name is")[-1].strip().split()[0].title() for item in MENU: if item.lower() in user_input.lower(): qty = 1 for word in user_input.lower().split(): if word.isdigit(): qty = int(word) break order.append((item, qty)) if "confirm" in user_input.lower() or "yes" in user_input.lower(): if customer_name and order: order_id = save_to_excel(customer_name, order) bot_reply += f"\nāœ… Your order ID is {order_id}. Thank you for ordering from Saad's Restaurant!" audio_file = speak(bot_reply) return bot_reply, audio_file # ========== Gradio UI ========== gr.Interface( fn=handle_chat, inputs=gr.Textbox(label="šŸ‘¤ You", placeholder="Type your order..."), outputs=[ gr.Textbox(label="šŸ¤– Bot Response"), gr.Audio(label="šŸ”Š Speaking", autoplay=True) ], title="šŸ” PandaAI", description="Smart restaurant assistant powered by LangChain memory and OpenRouter API.", theme="soft" ).launch(share=True)