jjmandog's picture
Update app.py
2a42ccf verified
raw
history blame
28.5 kB
#!/usr/bin/env python3
"""
πŸš— JAY'S MOBILE WASH - GRADIO + FASTAPI VERSION
iPhone Forwarding + AI Assistant - HuggingFace Spaces Optimized
"""
import os
import json
import requests
import logging
import threading
import time
from datetime import datetime, timedelta
from collections import defaultdict
from threading import Lock
import gradio as gr
from fastapi import FastAPI, Request, Form
from fastapi.responses import PlainTextResponse
import uvicorn
# Load environment variables
from dotenv import load_dotenv
load_dotenv()
# Configuration
SIGNALWIRE_PROJECT_ID = os.environ.get('SIGNALWIRE_PROJECT_ID', '')
SIGNALWIRE_AUTH_TOKEN = os.environ.get('SIGNALWIRE_AUTH_TOKEN', '')
SIGNALWIRE_SPACE_URL = os.environ.get('SIGNALWIRE_SPACE_URL', '')
DEEPSEEK_API_KEY = os.environ.get('DEEPSEEK_API_KEY', '')
# Business settings
BUSINESS_NAME = "Jay's Mobile Wash"
JAY_PHONE = os.environ.get('JAY_PHONE', '+15622289429')
SIGNALWIRE_PHONE = os.environ.get('SIGNALWIRE_PHONE', '+17149278841')
FORWARDING_ENABLED = True
FORWARDING_DELAY = 20
# Services and pricing
SERVICES = {
'basic_wash': {'price': 25.00, 'name': 'Basic Wash', 'description': 'Exterior wash and dry'},
'premium_wash': {'price': 45.00, 'name': 'Premium Wash', 'description': 'Wash, wax, interior vacuum'},
'full_detail': {'price': 85.00, 'name': 'Full Detail', 'description': 'Complete interior & exterior'},
'ceramic_coating': {'price': 150.00, 'name': 'Ceramic Coating', 'description': 'Premium protection'},
'headlight_restoration': {'price': 35.00, 'name': 'Headlight Restoration', 'description': 'Clear headlight restoration'}
}
# Initialize SignalWire
signalwire_client = None
VoiceResponse = None
MessagingResponse = None
try:
from signalwire.rest import Client
from signalwire.voice_response import VoiceResponse
from signalwire.messaging_response import MessagingResponse
if SIGNALWIRE_PROJECT_ID and SIGNALWIRE_AUTH_TOKEN and SIGNALWIRE_SPACE_URL:
signalwire_client = Client(
SIGNALWIRE_PROJECT_ID,
SIGNALWIRE_AUTH_TOKEN,
signalwire_space_url=SIGNALWIRE_SPACE_URL
)
print("βœ… SignalWire initialized")
else:
print("⚠️ SignalWire credentials not configured")
except ImportError:
print("⚠️ SignalWire library not available")
except Exception as e:
print(f"⚠️ SignalWire error: {e}")
# Logging setup
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
# Thread-safe system state
class SystemState:
def __init__(self):
self._lock = Lock()
self._data = {
'calls_today': 0,
'sms_today': 0,
'calls_forwarded': 0,
'ai_responses': 0,
'revenue_today': 0.0,
'active_calls': {},
'active_sms': {},
'start_time': datetime.now(),
'status': 'healthy',
'call_log': [],
'sms_log': []
}
def get(self, key):
with self._lock:
return self._data.get(key, 0)
def set(self, key, value):
with self._lock:
self._data[key] = value
def increment(self, key):
with self._lock:
self._data[key] = self._data.get(key, 0) + 1
def get_all(self):
with self._lock:
return self._data.copy()
def add_call_log(self, entry):
with self._lock:
self._data['call_log'].insert(0, entry)
if len(self._data['call_log']) > 50:
self._data['call_log'] = self._data['call_log'][:50]
def add_sms_log(self, entry):
with self._lock:
self._data['sms_log'].insert(0, entry)
if len(self._data['sms_log']) > 50:
self._data['sms_log'] = self._data['sms_log'][:50]
# Initialize system state
system_state = SystemState()
# Simple AI processor
class SimpleAI:
def __init__(self):
self.response_cache = {}
def analyze_sentiment(self, text):
"""Simple sentiment analysis"""
try:
from textblob import TextBlob
blob = TextBlob(text)
polarity = blob.sentiment.polarity
return {
'score': polarity,
'label': 'positive' if polarity > 0.1 else 'negative' if polarity < -0.1 else 'neutral'
}
except:
return {'score': 0.0, 'label': 'neutral'}
def detect_intent(self, text):
"""Detect customer intent"""
text_lower = text.lower()
if any(word in text_lower for word in ['price', 'cost', 'how much', 'pricing']):
return 'pricing'
elif any(word in text_lower for word in ['book', 'schedule', 'appointment', 'when']):
return 'booking'
elif any(word in text_lower for word in ['service', 'wash', 'detail', 'clean']):
return 'services'
elif any(word in text_lower for word in ['location', 'where', 'area', 'address']):
return 'location'
elif any(word in text_lower for word in ['urgent', 'emergency', 'asap', 'now']):
return 'urgent'
elif any(word in text_lower for word in ['jay', 'owner', 'human', 'person']):
return 'human_request'
else:
return 'general'
def generate_response(self, user_input, context=None):
"""Generate AI response"""
intent = self.detect_intent(user_input)
sentiment = self.analyze_sentiment(user_input)
# Add forwarding acknowledgment if needed
forwarding_ack = ""
if context and context.get('forwarded'):
forwarding_ack = "Thank you for your patience while the call was connected. "
# Generate response based on intent
if intent == 'pricing':
response = f"{forwarding_ack}I'd be happy to help with pricing! Our basic wash is $25, premium wash is $45, and full detail is $85. Which service interests you most?"
elif intent == 'booking':
response = f"{forwarding_ack}I can help you schedule an appointment! We're available Monday-Saturday 8AM-6PM, Sunday 10AM-4PM. What day works best for you?"
elif intent == 'services':
response = f"{forwarding_ack}We offer mobile car detailing services: Basic wash ($25), Premium wash with wax ($45), Full detail ($85), and ceramic coating ($150). Which would you like to know more about?"
elif intent == 'location':
response = f"{forwarding_ack}We provide mobile service within a 15-mile radius of Los Angeles. We come to your location! Where are you located?"
elif intent == 'urgent':
response = f"{forwarding_ack}I understand this is urgent. Let me connect you with Jay directly right away for immediate assistance."
elif intent == 'human_request':
response = f"{forwarding_ack}Let me connect you with Jay personally. He'll be right with you!"
else:
response = f"{forwarding_ack}Hi! Thanks for contacting Jay's Mobile Wash! I can help with pricing, scheduling, or questions about our mobile car detailing services. How can I assist you today?"
# Try DeepSeek if available for better responses
if DEEPSEEK_API_KEY and intent not in ['urgent', 'human_request']:
try:
deepseek_response = self.get_deepseek_response(user_input, context)
if deepseek_response:
response = deepseek_response
except Exception as e:
logger.warning(f"DeepSeek failed: {e}")
return response
def get_deepseek_response(self, prompt, context=None):
"""Get response from DeepSeek API"""
try:
headers = {
"Authorization": f"Bearer {DEEPSEEK_API_KEY}",
"Content-Type": "application/json"
}
system_prompt = f"""You are Jay's Mobile Wash AI assistant. Be friendly and professional.
BUSINESS INFO:
- Services: Basic wash ($25), Premium wash ($45), Full detail ($85), Ceramic coating ($150), Headlight restoration ($35)
- Hours: Mon-Sat 8AM-6PM, Sun 10AM-4PM
- Service area: 15 mile radius from Los Angeles
- Owner: Jay, Phone: {JAY_PHONE}
{"IMPORTANT: Customer's call was forwarded from Jay's phone. Be empathetic about any wait time." if context and context.get('forwarded') else ""}"""
data = {
"model": "deepseek-chat",
"messages": [
{"role": "system", "content": system_prompt},
{"role": "user", "content": prompt}
],
"max_tokens": 150,
"temperature": 0.7
}
response = requests.post(
"https://api.deepseek.com/v1/chat/completions",
headers=headers,
json=data,
timeout=10
)
if response.status_code == 200:
result = response.json()
return result['choices'][0]['message']['content'].strip()
except Exception as e:
logger.error(f"DeepSeek error: {e}")
return None
# Initialize AI processor
ai = SimpleAI()
# FastAPI app for webhooks
fastapi_app = FastAPI()
@fastapi_app.post("/voice/incoming")
async def handle_voice(request: Request):
"""Handle incoming voice calls"""
try:
form = await request.form()
if not signalwire_client or not VoiceResponse:
return PlainTextResponse("Service unavailable", status_code=503)
call_sid = form.get('CallSid')
from_number = form.get('From')
to_number = form.get('To')
forwarded_from = form.get('ForwardedFrom')
# Detect forwarding
is_forwarded = bool(forwarded_from) or (to_number == SIGNALWIRE_PHONE)
# Log the call
call_entry = {
'time': datetime.now().strftime('%H:%M:%S'),
'from': from_number,
'type': 'Forwarded Call' if is_forwarded else 'Direct Call',
'status': 'Incoming'
}
system_state.add_call_log(call_entry)
# Update stats
system_state.increment('calls_today')
if is_forwarded:
system_state.increment('calls_forwarded')
response = VoiceResponse()
# Greeting
if is_forwarded:
greeting = "Hi! Thanks for calling Jay's Mobile Wash. I'm Jay's AI assistant ready to help while Jay is with another customer. How can I assist you?"
else:
greeting = "Hi! Welcome to Jay's Mobile Wash! I'm your AI assistant ready to help with pricing, scheduling, or any questions. How can I help you?"
response.say(greeting, voice='alice')
# Gather speech
response.gather(
input='speech',
timeout=10,
action=f'/voice/process?call_sid={call_sid}&forwarded={is_forwarded}',
speech_timeout=3
)
# Fallback
response.say("I didn't catch that. Let me connect you with Jay.", voice='alice')
response.dial(JAY_PHONE, timeout=30)
return PlainTextResponse(str(response), media_type="application/xml")
except Exception as e:
logger.error(f"Voice error: {e}")
response = VoiceResponse()
response.say("Technical issue. Connecting you with Jay.", voice='alice')
response.dial(JAY_PHONE, timeout=30)
return PlainTextResponse(str(response), media_type="application/xml")
@fastapi_app.post("/voice/process")
async def process_speech(request: Request):
"""Process customer speech"""
try:
form = await request.form()
query_params = request.query_params
call_sid = query_params.get('call_sid')
is_forwarded = query_params.get('forwarded') == 'True'
speech_result = form.get('SpeechResult', '')
confidence = float(form.get('Confidence', '0.0'))
if not speech_result or confidence < 0.4:
response = VoiceResponse()
response.say("I didn't understand clearly. Let me connect you with Jay.", voice='alice')
response.dial(JAY_PHONE, timeout=30)
return PlainTextResponse(str(response), media_type="application/xml")
# Analyze intent
intent = ai.detect_intent(speech_result)
# Check for escalation
if intent in ['urgent', 'human_request'] or 'jay' in speech_result.lower():
response = VoiceResponse()
response.say("Let me connect you with Jay right away.", voice='alice')
response.dial(JAY_PHONE, timeout=30)
return PlainTextResponse(str(response), media_type="application/xml")
# Generate AI response
context = {'forwarded': is_forwarded, 'call_sid': call_sid}
ai_response = ai.generate_response(speech_result, context)
system_state.increment('ai_responses')
response = VoiceResponse()
response.say(ai_response, voice='alice')
# Continue conversation
response.gather(
input='speech',
timeout=8,
action=f'/voice/process?call_sid={call_sid}&forwarded={is_forwarded}',
speech_timeout=3
)
response.say("Thank you for calling Jay's Mobile Wash! Have a great day!", voice='alice')
return PlainTextResponse(str(response), media_type="application/xml")
except Exception as e:
logger.error(f"Speech processing error: {e}")
response = VoiceResponse()
response.say("Technical issue. Connecting with Jay.", voice='alice')
response.dial(JAY_PHONE, timeout=30)
return PlainTextResponse(str(response), media_type="application/xml")
@fastapi_app.post("/sms/incoming")
async def handle_sms(request: Request):
"""Handle incoming SMS"""
try:
form = await request.form()
message_sid = form.get('MessageSid')
from_number = form.get('From')
message_body = form.get('Body', '').strip()
# Log the SMS
sms_entry = {
'time': datetime.now().strftime('%H:%M:%S'),
'from': from_number,
'message': message_body[:50] + '...' if len(message_body) > 50 else message_body,
'status': 'Received'
}
system_state.add_sms_log(sms_entry)
system_state.increment('sms_today')
if not message_body:
await send_sms("Hi! How can I help you with Jay's Mobile Wash today?", from_number)
return PlainTextResponse(str(MessagingResponse()) if MessagingResponse else "", media_type="application/xml")
# Analyze message
intent = ai.detect_intent(message_body)
sentiment = ai.analyze_sentiment(message_body)
# Check for escalation
if (intent in ['urgent', 'human_request'] or
sentiment['score'] < -0.6 or
'jay' in message_body.lower()):
response_text = f"I'll have Jay respond to you personally. For immediate assistance, call {JAY_PHONE}."
await send_sms(response_text, from_number)
return PlainTextResponse(str(MessagingResponse()) if MessagingResponse else "", media_type="application/xml")
# Generate AI response
ai_response = ai.generate_response(message_body, {'sms': True})
# Optimize for SMS
if len(ai_response) > 320:
ai_response = ai_response[:317] + "..."
system_state.increment('ai_responses')
await send_sms(ai_response, from_number)
return PlainTextResponse(str(MessagingResponse()) if MessagingResponse else "", media_type="application/xml")
except Exception as e:
logger.error(f"SMS error: {e}")
await send_sms("Thanks for your message! Jay will get back to you soon.", from_number)
return PlainTextResponse(str(MessagingResponse()) if MessagingResponse else "", media_type="application/xml")
async def send_sms(message, to_number):
"""Send SMS response"""
try:
if signalwire_client:
signalwire_client.messages.create(
body=message,
from_=SIGNALWIRE_PHONE,
to=to_number
)
# Log sent SMS
sms_entry = {
'time': datetime.now().strftime('%H:%M:%S'),
'from': 'AI Assistant',
'message': message[:50] + '...' if len(message) > 50 else message,
'status': 'Sent'
}
system_state.add_sms_log(sms_entry)
except Exception as e:
logger.error(f"SMS send error: {e}")
@fastapi_app.get("/health")
async def health_check():
"""Health check endpoint"""
health = {
'status': 'healthy',
'signalwire': bool(signalwire_client),
'deepseek': bool(DEEPSEEK_API_KEY),
'uptime': int((datetime.now() - system_state.get_all()['start_time']).total_seconds()),
'timestamp': datetime.now().isoformat()
}
return health
# Gradio Interface Functions
def get_dashboard_data():
"""Get current dashboard data"""
stats = system_state.get_all()
uptime = datetime.now() - stats['start_time']
dashboard_html = f"""
<div style="font-family: Arial, sans-serif; padding: 20px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); border-radius: 15px; color: white;">
<h1 style="text-align: center; margin-bottom: 30px;">πŸš— {BUSINESS_NAME} - AI Dashboard</h1>
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 20px; margin-bottom: 30px;">
<div style="background: rgba(255,255,255,0.15); padding: 20px; border-radius: 10px; text-align: center;">
<h2 style="color: #4facfe; font-size: 2em; margin: 0;">{stats['calls_today']}</h2>
<p style="margin: 5px 0 0 0;">πŸ“ž Calls Today</p>
</div>
<div style="background: rgba(255,255,255,0.15); padding: 20px; border-radius: 10px; text-align: center;">
<h2 style="color: #4facfe; font-size: 2em; margin: 0;">{stats['sms_today']}</h2>
<p style="margin: 5px 0 0 0;">πŸ“± SMS Today</p>
</div>
<div style="background: rgba(255,255,255,0.15); padding: 20px; border-radius: 10px; text-align: center;">
<h2 style="color: #4facfe; font-size: 2em; margin: 0;">{stats['calls_forwarded']}</h2>
<p style="margin: 5px 0 0 0;">πŸ”„ Calls Forwarded</p>
</div>
<div style="background: rgba(255,255,255,0.15); padding: 20px; border-radius: 10px; text-align: center;">
<h2 style="color: #4facfe; font-size: 2em; margin: 0;">{stats['ai_responses']}</h2>
<p style="margin: 5px 0 0 0;">πŸ€– AI Responses</p>
</div>
</div>
<div style="background: rgba(255,255,255,0.1); padding: 20px; border-radius: 10px; margin-bottom: 20px;">
<h3 style="color: #4facfe; margin-bottom: 15px;">πŸ“ž iPhone Forwarding Status</h3>
<p><strong>Jay's iPhone:</strong> {JAY_PHONE}</p>
<p><strong>AI Assistant Number:</strong> {SIGNALWIRE_PHONE}</p>
<p><strong>Forwarding:</strong> {'βœ… Enabled' if FORWARDING_ENABLED else '❌ Disabled'}</p>
<p><strong>Delay:</strong> {FORWARDING_DELAY} seconds</p>
<p><strong>SignalWire:</strong> {'βœ… Connected' if signalwire_client else '❌ Not configured'}</p>
<p><strong>DeepSeek AI:</strong> {'βœ… Connected' if DEEPSEEK_API_KEY else '⚠️ Fallback mode'}</p>
</div>
<div style="background: rgba(255,255,255,0.1); padding: 20px; border-radius: 10px;">
<h3 style="color: #4facfe; margin-bottom: 15px;">πŸ’Ό Services & Pricing</h3>
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 15px;">
"""
for service_id, service in SERVICES.items():
dashboard_html += f"""
<div style="background: rgba(79, 172, 254, 0.2); padding: 15px; border-radius: 8px; text-align: center;">
<h4 style="margin: 0 0 5px 0; color: white;">{service['name']}</h4>
<p style="margin: 0 0 10px 0; font-size: 0.9em; opacity: 0.8;">{service['description']}</p>
<p style="margin: 0; font-size: 1.5em; font-weight: bold; color: #00ff88;">${service['price']:.0f}</p>
</div>
"""
dashboard_html += """
</div>
</div>
</div>
"""
return dashboard_html
def get_call_log():
"""Get recent call log"""
stats = system_state.get_all()
call_log = stats.get('call_log', [])
if not call_log:
return "No calls logged yet."
log_html = """
<div style="font-family: Arial, sans-serif;">
<h3>πŸ“ž Recent Calls</h3>
<div style="max-height: 300px; overflow-y: auto;">
"""
for call in call_log[:10]: # Show last 10 calls
log_html += f"""
<div style="background: #f5f5f5; padding: 10px; margin: 5px 0; border-radius: 5px; border-left: 4px solid #4facfe;">
<strong>{call['time']}</strong> | {call['from']} | {call['type']} | {call['status']}
</div>
"""
log_html += "</div></div>"
return log_html
def get_sms_log():
"""Get recent SMS log"""
stats = system_state.get_all()
sms_log = stats.get('sms_log', [])
if not sms_log:
return "No SMS logged yet."
log_html = """
<div style="font-family: Arial, sans-serif;">
<h3>πŸ“± Recent SMS</h3>
<div style="max-height: 300px; overflow-y: auto;">
"""
for sms in sms_log[:10]: # Show last 10 SMS
log_html += f"""
<div style="background: #f5f5f5; padding: 10px; margin: 5px 0; border-radius: 5px; border-left: 4px solid #00ff88;">
<strong>{sms['time']}</strong> | {sms['from']}<br>
<em>{sms['message']}</em> | {sms['status']}
</div>
"""
log_html += "</div></div>"
return log_html
def test_ai_response(message):
"""Test AI response generation"""
if not message.strip():
return "Please enter a message to test."
try:
intent = ai.detect_intent(message)
sentiment = ai.analyze_sentiment(message)
response = ai.generate_response(message, {'test': True})
result = f"""
<div style="font-family: Arial, sans-serif; padding: 15px; background: #f9f9f9; border-radius: 10px;">
<h3>AI Response Test</h3>
<p><strong>Customer Message:</strong> {message}</p>
<p><strong>Detected Intent:</strong> {intent}</p>
<p><strong>Sentiment:</strong> {sentiment['label']} ({sentiment['score']:.2f})</p>
<div style="background: #e3f2fd; padding: 10px; border-radius: 5px; margin-top: 10px;">
<strong>AI Response:</strong><br>
{response}
</div>
</div>
"""
return result
except Exception as e:
return f"Error testing AI response: {e}"
# Start FastAPI in background thread
def run_fastapi():
uvicorn.run(fastapi_app, host="0.0.0.0", port=8000, log_level="info")
fastapi_thread = threading.Thread(target=run_fastapi, daemon=True)
fastapi_thread.start()
# Create Gradio Interface
with gr.Blocks(
title=f"{BUSINESS_NAME} - AI Dashboard",
theme=gr.themes.Soft(),
css="""
.gradio-container {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
}
"""
) as interface:
gr.Markdown(f"""
# πŸš— {BUSINESS_NAME} - iPhone Forwarding AI System
**Real-time dashboard for your AI assistant with iPhone call forwarding**
πŸ“ž **Jay's iPhone:** {JAY_PHONE}
πŸ€– **AI Assistant:** {SIGNALWIRE_PHONE}
πŸ”„ **Forwarding:** {'βœ… Enabled' if FORWARDING_ENABLED else '❌ Disabled'}
""")
with gr.Tabs():
with gr.Tab("πŸ“Š Dashboard"):
dashboard_display = gr.HTML(value=get_dashboard_data())
refresh_btn = gr.Button("πŸ”„ Refresh Dashboard", variant="primary")
refresh_btn.click(fn=get_dashboard_data, outputs=dashboard_display)
with gr.Tab("πŸ“ž Call Log"):
call_log_display = gr.HTML(value=get_call_log())
refresh_calls_btn = gr.Button("πŸ”„ Refresh Calls", variant="secondary")
refresh_calls_btn.click(fn=get_call_log, outputs=call_log_display)
with gr.Tab("πŸ“± SMS Log"):
sms_log_display = gr.HTML(value=get_sms_log())
refresh_sms_btn = gr.Button("πŸ”„ Refresh SMS", variant="secondary")
refresh_sms_btn.click(fn=get_sms_log, outputs=sms_log_display)
with gr.Tab("πŸ§ͺ Test AI"):
with gr.Row():
with gr.Column():
test_message = gr.Textbox(
label="Test Message",
placeholder="Enter a customer message to test AI response...",
lines=3
)
test_btn = gr.Button("πŸ€– Test AI Response", variant="primary")
with gr.Column():
test_result = gr.HTML(label="AI Response")
test_btn.click(fn=test_ai_response, inputs=test_message, outputs=test_result)
with gr.Tab("ℹ️ Setup Info"):
gr.Markdown(f"""
## πŸ”§ Webhook Configuration
**Set these URLs in your SignalWire dashboard:**
- **Voice Webhook:** `https://YOUR-SPACE-NAME.hf.space/voice/incoming`
- **SMS Webhook:** `https://YOUR-SPACE-NAME.hf.space/sms/incoming`
## πŸ“± iPhone Setup
**On Jay's iPhone:**
1. Go to **Settings** β†’ **Phone** β†’ **Call Forwarding**
2. Turn **ON** Call Forwarding
3. Set forward number to: `{SIGNALWIRE_PHONE}`
4. Choose: Forward when **unanswered** after {FORWARDING_DELAY} seconds
## 🎯 How It Works
1. Customer calls Jay's iPhone: `{JAY_PHONE}`
2. iPhone rings for {FORWARDING_DELAY} seconds
3. If no answer β†’ forwards to AI: `{SIGNALWIRE_PHONE}`
4. AI handles call with forwarding awareness
5. Can escalate back to Jay if needed
## πŸ“Š System Status
- **SignalWire:** {'βœ… Connected' if signalwire_client else '❌ Not configured'}
- **DeepSeek AI:** {'βœ… Connected' if DEEPSEEK_API_KEY else '⚠️ Using fallback responses'}
- **FastAPI Webhooks:** βœ… Running on port 8000
- **Health Check:** [/health](./health)
""")
# Auto-refresh every 30 seconds
interface.load(fn=get_dashboard_data, outputs=dashboard_display, every=30)
interface.load(fn=get_call_log, outputs=call_log_display, every=30)
interface.load(fn=get_sms_log, outputs=sms_log_display, every=30)
# Launch the interface
if __name__ == "__main__":
print("\n" + "="*60)
print("πŸš— JAY'S MOBILE WASH AI SYSTEM")
print("="*60)
print(f"πŸ“ž Jay's Phone: {JAY_PHONE}")
print(f"πŸ€– AI Phone: {SIGNALWIRE_PHONE}")
print(f"πŸ”„ Forwarding: {'βœ… Enabled' if FORWARDING_ENABLED else '❌ Disabled'}")
print(f"🌐 SignalWire: {'βœ… Connected' if signalwire_client else '❌ Not configured'}")
print(f"🧠 DeepSeek: {'βœ… Connected' if DEEPSEEK_API_KEY else '❌ Not configured'}")
print("="*60)
print("πŸš€ Starting Gradio interface on port 7860...")
print("πŸ“ž FastAPI webhooks running on port 8000")
print("="*60)
interface.launch(
server_name="0.0.0.0",
server_port=7860,
share=False,
show_error=True
)