Spaces:
Runtime error
Runtime error
| import os | |
| import gradio as gr | |
| from datetime import datetime | |
| from threading import Lock | |
| import requests | |
| import logging | |
| # Simple configuration | |
| BUSINESS_NAME = "Jay's Mobile Wash" | |
| JAY_PHONE = os.environ.get('JAY_PHONE_NUMBER', '+15622289429') | |
| AI_PHONE = os.environ.get('AI_PHONE_NUMBER', '+17149278841') | |
| DEEPSEEK_KEY = os.environ.get('DEEPSEEK_API_KEY', '') | |
| # Simple state management | |
| class SimpleState: | |
| def __init__(self): | |
| self.lock = Lock() | |
| self.data = { | |
| 'calls': 0, 'sms': 0, 'ai_responses': 0, | |
| 'start_time': datetime.now(), 'log': [] | |
| } | |
| def increment(self, key): | |
| with self.lock: | |
| self.data[key] += 1 | |
| def add_log(self, entry): | |
| with self.lock: | |
| self.data['log'].insert(0, entry) | |
| if len(self.data['log']) > 20: | |
| self.data['log'] = self.data['log'][:20] | |
| def get_all(self): | |
| with self.lock: | |
| return self.data.copy() | |
| state = SimpleState() | |
| # Simple AI | |
| class SimpleAI: | |
| def detect_intent(self, text): | |
| text = text.lower() | |
| if any(word in text for word in ['price', 'cost', 'much']): | |
| return 'pricing' | |
| elif any(word in text for word in ['book', 'schedule', 'appointment']): | |
| return 'booking' | |
| elif any(word in text for word in ['urgent', 'emergency']): | |
| return 'urgent' | |
| elif any(word in text for word in ['jay', 'human', 'person']): | |
| return 'human' | |
| return 'general' | |
| def generate_response(self, text, forwarded=False): | |
| intent = self.detect_intent(text) | |
| prefix = "Thanks for your patience. " if forwarded else "" | |
| responses = { | |
| 'pricing': f"{prefix}Our services: Basic wash $25, Premium $45, Full detail $85. Which interests you?", | |
| 'booking': f"{prefix}We're available Mon-Sat 8AM-6PM, Sun 10AM-4PM. What day works for you?", | |
| 'urgent': f"{prefix}I understand this is urgent. Let me connect you with Jay right away.", | |
| 'human': f"{prefix}Let me connect you with Jay personally.", | |
| 'general': f"{prefix}Hi! I'm Jay's AI assistant. I can help with pricing, scheduling, or questions about our mobile car wash services." | |
| } | |
| response = responses.get(intent, responses['general']) | |
| # Try DeepSeek if available | |
| if DEEPSEEK_KEY and intent not in ['urgent', 'human']: | |
| try: | |
| enhanced = self.get_deepseek_response(text) | |
| if enhanced: | |
| response = enhanced | |
| except: | |
| pass | |
| return response | |
| def get_deepseek_response(self, prompt): | |
| try: | |
| headers = {"Authorization": f"Bearer {DEEPSEEK_KEY}", "Content-Type": "application/json"} | |
| data = { | |
| "model": "deepseek-chat", | |
| "messages": [ | |
| {"role": "system", "content": f"You are {BUSINESS_NAME} AI assistant. Be friendly and professional. Services: Basic wash ($25), Premium ($45), Full detail ($85), Ceramic coating ($150). Hours: Mon-Sat 8AM-6PM, Sun 10AM-4PM. Phone: {JAY_PHONE}"}, | |
| {"role": "user", "content": prompt} | |
| ], | |
| "max_tokens": 150 | |
| } | |
| response = requests.post("https://api.deepseek.com/v1/chat/completions", | |
| headers=headers, json=data, timeout=10) | |
| if response.status_code == 200: | |
| return response.json()['choices'][0]['message']['content'].strip() | |
| except: | |
| pass | |
| return None | |
| ai = SimpleAI() | |
| # Dashboard function | |
| def get_dashboard(): | |
| stats = state.get_all() | |
| uptime = datetime.now() - stats['start_time'] | |
| return f""" | |
| <div style="font-family: Arial; padding: 20px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); border-radius: 15px; color: white;"> | |
| <h1 style="text-align: center;">๐ {BUSINESS_NAME} - AI Dashboard</h1> | |
| <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 15px; margin: 20px 0;"> | |
| <div style="background: rgba(255,255,255,0.15); padding: 15px; border-radius: 10px; text-align: center;"> | |
| <h2 style="color: #4facfe; font-size: 2em; margin: 0;">{stats['calls']}</h2> | |
| <p style="margin: 5px 0;">๐ Calls</p> | |
| </div> | |
| <div style="background: rgba(255,255,255,0.15); padding: 15px; border-radius: 10px; text-align: center;"> | |
| <h2 style="color: #4facfe; font-size: 2em; margin: 0;">{stats['sms']}</h2> | |
| <p style="margin: 5px 0;">๐ฑ SMS</p> | |
| </div> | |
| <div style="background: rgba(255,255,255,0.15); padding: 15px; border-radius: 10px; text-align: center;"> | |
| <h2 style="color: #4facfe; font-size: 2em; margin: 0;">{stats['ai_responses']}</h2> | |
| <p style="margin: 5px 0;">๐ค AI Responses</p> | |
| </div> | |
| </div> | |
| <div style="background: rgba(255,255,255,0.1); padding: 15px; border-radius: 10px; margin: 20px 0;"> | |
| <h3 style="color: #4facfe;">๐ iPhone Forwarding Status</h3> | |
| <p><strong>Jay's iPhone:</strong> {JAY_PHONE}</p> | |
| <p><strong>AI Number:</strong> {AI_PHONE}</p> | |
| <p><strong>DeepSeek AI:</strong> {'โ Connected' if DEEPSEEK_KEY else 'โ ๏ธ Not configured'}</p> | |
| <p><strong>Uptime:</strong> {int(uptime.total_seconds() / 3600)} hours</p> | |
| </div> | |
| <div style="background: rgba(255,255,255,0.1); padding: 15px; border-radius: 10px;"> | |
| <h3 style="color: #4facfe;">๐ผ Services</h3> | |
| <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); gap: 10px;"> | |
| <div style="background: rgba(79,172,254,0.2); padding: 10px; border-radius: 8px; text-align: center;"> | |
| <h4 style="margin: 0; color: white;">Basic Wash</h4> | |
| <p style="margin: 5px 0; color: #00ff88; font-size: 1.2em;">$25</p> | |
| </div> | |
| <div style="background: rgba(79,172,254,0.2); padding: 10px; border-radius: 8px; text-align: center;"> | |
| <h4 style="margin: 0; color: white;">Premium</h4> | |
| <p style="margin: 5px 0; color: #00ff88; font-size: 1.2em;">$45</p> | |
| </div> | |
| <div style="background: rgba(79,172,254,0.2); padding: 10px; border-radius: 8px; text-align: center;"> | |
| <h4 style="margin: 0; color: white;">Full Detail</h4> | |
| <p style="margin: 5px 0; color: #00ff88; font-size: 1.2em;">$85</p> | |
| </div> | |
| <div style="background: rgba(79,172,254,0.2); padding: 10px; border-radius: 8px; text-align: center;"> | |
| <h4 style="margin: 0; color: white;">Ceramic</h4> | |
| <p style="margin: 5px 0; color: #00ff88; font-size: 1.2em;">$150</p> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| """ | |
| # Test AI function | |
| def test_ai(message): | |
| if not message.strip(): | |
| return "Please enter a message to test." | |
| try: | |
| intent = ai.detect_intent(message) | |
| response = ai.generate_response(message) | |
| state.increment('ai_responses') | |
| return f""" | |
| <div style="font-family: Arial; padding: 15px; background: #f9f9f9; border-radius: 10px;"> | |
| <h3>๐ค AI Response Test</h3> | |
| <p><strong>Your Message:</strong> {message}</p> | |
| <p><strong>Detected Intent:</strong> {intent}</p> | |
| <div style="background: #e3f2fd; padding: 10px; border-radius: 5px; margin-top: 10px;"> | |
| <strong>AI Response:</strong><br>{response} | |
| </div> | |
| </div> | |
| """ | |
| except Exception as e: | |
| return f"Error: {e}" | |
| # Simulate functions | |
| def simulate_call(phone, forwarded): | |
| entry = { | |
| 'time': datetime.now().strftime('%H:%M:%S'), | |
| 'type': 'Call', | |
| 'from': phone or '+1555123456', | |
| 'forwarded': forwarded | |
| } | |
| state.add_log(entry) | |
| state.increment('calls') | |
| return f"โ Simulated {'forwarded' if forwarded else 'direct'} call from {entry['from']}" | |
| def simulate_sms(phone, message): | |
| if not message.strip(): | |
| return "Please enter a message." | |
| # Log incoming SMS | |
| entry = { | |
| 'time': datetime.now().strftime('%H:%M:%S'), | |
| 'type': 'SMS', | |
| 'from': phone or '+1555123456', | |
| 'message': message | |
| } | |
| state.add_log(entry) | |
| state.increment('sms') | |
| # Generate AI response | |
| response = ai.generate_response(message) | |
| state.increment('ai_responses') | |
| # Log AI response | |
| response_entry = { | |
| 'time': datetime.now().strftime('%H:%M:%S'), | |
| 'type': 'AI Response', | |
| 'from': 'AI Assistant', | |
| 'message': response | |
| } | |
| state.add_log(response_entry) | |
| return f"โ SMS processed!\n\n**Customer:** {message}\n\n**AI Response:** {response}" | |
| # Activity log | |
| def get_activity(): | |
| stats = state.get_all() | |
| log = stats.get('log', []) | |
| if not log: | |
| return "No activity yet. Try the demo!" | |
| html = "<div style='font-family: Arial;'><h3>๐ Recent Activity</h3>" | |
| for entry in log[:10]: | |
| color = "#4facfe" if entry['type'] == 'Call' else "#00ff88" if entry['type'] == 'SMS' else "#ff6b6b" | |
| html += f""" | |
| <div style="background: #f5f5f5; padding: 10px; margin: 5px 0; border-radius: 5px; border-left: 4px solid {color};"> | |
| <strong>{entry['time']}</strong> | {entry['type']} | {entry['from']}<br> | |
| {entry.get('message', entry.get('forwarded', ''))} | |
| </div> | |
| """ | |
| html += "</div>" | |
| return html | |
| # Create Gradio interface | |
| with gr.Blocks(title=f"{BUSINESS_NAME} - AI Dashboard", theme=gr.themes.Soft()) as demo: | |
| gr.Markdown(f""" | |
| # ๐ {BUSINESS_NAME} - iPhone Forwarding AI System | |
| **Jay's iPhone:** {JAY_PHONE} โ **AI Assistant:** {AI_PHONE} | |
| """) | |
| with gr.Tabs(): | |
| with gr.Tab("๐ Dashboard"): | |
| dashboard = gr.HTML(value=get_dashboard()) | |
| gr.Button("๐ Refresh").click(fn=get_dashboard, outputs=dashboard) | |
| with gr.Tab("๐งช Test AI"): | |
| with gr.Row(): | |
| with gr.Column(): | |
| test_input = gr.Textbox(label="Test Message", placeholder="How much for a car wash?", lines=2) | |
| test_btn = gr.Button("๐ค Test AI", variant="primary") | |
| with gr.Column(): | |
| test_output = gr.HTML() | |
| test_btn.click(fn=test_ai, inputs=test_input, outputs=test_output) | |
| with gr.Tab("๐ฎ Demo"): | |
| with gr.Row(): | |
| with gr.Column(): | |
| gr.Markdown("#### ๐ Simulate Call") | |
| call_phone = gr.Textbox(label="Phone", value="+1555123456") | |
| call_forwarded = gr.Checkbox(label="Forwarded Call", value=True) | |
| call_btn = gr.Button("๐ Simulate") | |
| call_result = gr.Textbox(label |