jjmandog commited on
Commit
e5d8b01
ยท
verified ยท
1 Parent(s): 0834e98

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +180 -506
app.py CHANGED
@@ -1,599 +1,273 @@
1
- #!/usr/bin/env python3
2
- """
3
- ๐Ÿš— JAY'S MOBILE WASH - PURE GRADIO VERSION
4
- iPhone Forwarding AI System - HuggingFace Spaces Optimized
5
- """
6
-
7
  import os
8
- import json
 
 
9
  import requests
10
  import logging
11
- import threading
12
- import time
13
- from datetime import datetime, timedelta
14
- from collections import defaultdict
15
- from threading import Lock
16
- import gradio as gr
17
-
18
- # Load environment variables
19
- from dotenv import load_dotenv
20
- load_dotenv()
21
 
22
- # Configuration - FIXED VARIABLE NAMES TO AVOID COLLISIONS
23
- SIGNALWIRE_PROJECT_ID = os.environ.get('SIGNALWIRE_PROJECT_ID', '')
24
- SIGNALWIRE_AUTH_TOKEN = os.environ.get('SIGNALWIRE_AUTH_TOKEN', '')
25
- SIGNALWIRE_SPACE_URL = os.environ.get('SIGNALWIRE_SPACE_URL', '')
26
- DEEPSEEK_API_KEY = os.environ.get('DEEPSEEK_API_KEY', '')
27
-
28
- # Business settings - FIXED NAMES
29
  BUSINESS_NAME = "Jay's Mobile Wash"
30
  JAY_PHONE = os.environ.get('JAY_PHONE_NUMBER', '+15622289429')
31
- SIGNALWIRE_PHONE = os.environ.get('AI_PHONE_NUMBER', '+17149278841')
32
- FORWARDING_ENABLED = True
33
- FORWARDING_DELAY = 20
34
-
35
- # Services and pricing
36
- SERVICES = {
37
- 'basic_wash': {'price': 25.00, 'name': 'Basic Wash', 'description': 'Exterior wash and dry'},
38
- 'premium_wash': {'price': 45.00, 'name': 'Premium Wash', 'description': 'Wash, wax, interior vacuum'},
39
- 'full_detail': {'price': 85.00, 'name': 'Full Detail', 'description': 'Complete interior & exterior'},
40
- 'ceramic_coating': {'price': 150.00, 'name': 'Ceramic Coating', 'description': 'Premium protection'},
41
- 'headlight_restoration': {'price': 35.00, 'name': 'Headlight Restoration', 'description': 'Clear headlight restoration'}
42
- }
43
 
44
- # Initialize SignalWire
45
- signalwire_client = None
46
- try:
47
- from signalwire.rest import Client
48
- if SIGNALWIRE_PROJECT_ID and SIGNALWIRE_AUTH_TOKEN and SIGNALWIRE_SPACE_URL:
49
- signalwire_client = Client(
50
- SIGNALWIRE_PROJECT_ID,
51
- SIGNALWIRE_AUTH_TOKEN,
52
- signalwire_space_url=SIGNALWIRE_SPACE_URL
53
- )
54
- print("โœ… SignalWire initialized")
55
- else:
56
- print("โš ๏ธ SignalWire credentials not configured")
57
- except Exception as e:
58
- print(f"โš ๏ธ SignalWire error: {e}")
59
-
60
- # Logging setup
61
- logging.basicConfig(level=logging.INFO)
62
- logger = logging.getLogger(__name__)
63
-
64
- # Thread-safe system state
65
- class SystemState:
66
  def __init__(self):
67
- self._lock = Lock()
68
- self._data = {
69
- 'calls_today': 0,
70
- 'sms_today': 0,
71
- 'calls_forwarded': 0,
72
- 'ai_responses': 0,
73
- 'revenue_today': 0.0,
74
- 'start_time': datetime.now(),
75
- 'status': 'healthy',
76
- 'call_log': [],
77
- 'sms_log': [],
78
- 'test_responses': []
79
  }
80
 
81
- def get(self, key):
82
- with self._lock:
83
- return self._data.get(key, 0)
84
-
85
- def set(self, key, value):
86
- with self._lock:
87
- self._data[key] = value
88
-
89
  def increment(self, key):
90
- with self._lock:
91
- self._data[key] = self._data.get(key, 0) + 1
92
 
93
- def get_all(self):
94
- with self._lock:
95
- return self._data.copy()
96
-
97
- def add_call_log(self, entry):
98
- with self._lock:
99
- self._data['call_log'].insert(0, entry)
100
- if len(self._data['call_log']) > 50:
101
- self._data['call_log'] = self._data['call_log'][:50]
102
 
103
- def add_sms_log(self, entry):
104
- with self._lock:
105
- self._data['sms_log'].insert(0, entry)
106
- if len(self._data['sms_log']) > 50:
107
- self._data['sms_log'] = self._data['sms_log'][:50]
108
 
109
- # Initialize system state
110
- system_state = SystemState()
111
 
112
- # Simple AI processor
113
  class SimpleAI:
114
- def __init__(self):
115
- self.response_cache = {}
116
-
117
- def analyze_sentiment(self, text):
118
- """Simple sentiment analysis"""
119
- try:
120
- from textblob import TextBlob
121
- blob = TextBlob(text)
122
- polarity = blob.sentiment.polarity
123
- return {
124
- 'score': polarity,
125
- 'label': 'positive' if polarity > 0.1 else 'negative' if polarity < -0.1 else 'neutral'
126
- }
127
- except:
128
- return {'score': 0.0, 'label': 'neutral'}
129
-
130
  def detect_intent(self, text):
131
- """Detect customer intent"""
132
- text_lower = text.lower()
133
-
134
- if any(word in text_lower for word in ['price', 'cost', 'how much', 'pricing']):
135
  return 'pricing'
136
- elif any(word in text_lower for word in ['book', 'schedule', 'appointment', 'when']):
137
  return 'booking'
138
- elif any(word in text_lower for word in ['service', 'wash', 'detail', 'clean']):
139
- return 'services'
140
- elif any(word in text_lower for word in ['location', 'where', 'area', 'address']):
141
- return 'location'
142
- elif any(word in text_lower for word in ['urgent', 'emergency', 'asap', 'now']):
143
  return 'urgent'
144
- elif any(word in text_lower for word in ['jay', 'owner', 'human', 'person']):
145
- return 'human_request'
146
- else:
147
- return 'general'
148
 
149
- def generate_response(self, user_input, context=None):
150
- """Generate AI response"""
151
- intent = self.detect_intent(user_input)
152
- sentiment = self.analyze_sentiment(user_input)
153
 
154
- # Add forwarding acknowledgment if needed
155
- forwarding_ack = ""
156
- if context and context.get('forwarded'):
157
- forwarding_ack = "Thank you for your patience while the call was connected. "
158
 
159
- # Generate response based on intent
160
- if intent == 'pricing':
161
- 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?"
162
-
163
- elif intent == 'booking':
164
- 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?"
165
-
166
- elif intent == 'services':
167
- 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?"
168
-
169
- elif intent == 'location':
170
- 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?"
171
-
172
- elif intent == 'urgent':
173
- response = f"{forwarding_ack}I understand this is urgent. Let me connect you with Jay directly right away for immediate assistance."
174
-
175
- elif intent == 'human_request':
176
- response = f"{forwarding_ack}Let me connect you with Jay personally. He'll be right with you!"
177
 
178
- else:
179
- 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?"
180
 
181
- # Try DeepSeek if available for better responses
182
- if DEEPSEEK_API_KEY and intent not in ['urgent', 'human_request']:
183
  try:
184
- deepseek_response = self.get_deepseek_response(user_input, context)
185
- if deepseek_response:
186
- response = deepseek_response
187
- except Exception as e:
188
- logger.warning(f"DeepSeek failed: {e}")
189
 
190
  return response
191
 
192
- def get_deepseek_response(self, prompt, context=None):
193
- """Get response from DeepSeek API"""
194
  try:
195
- headers = {
196
- "Authorization": f"Bearer {DEEPSEEK_API_KEY}",
197
- "Content-Type": "application/json"
198
- }
199
-
200
- system_prompt = f"""You are Jay's Mobile Wash AI assistant. Be friendly and professional.
201
-
202
- BUSINESS INFO:
203
- - Services: Basic wash ($25), Premium wash ($45), Full detail ($85), Ceramic coating ($150), Headlight restoration ($35)
204
- - Hours: Mon-Sat 8AM-6PM, Sun 10AM-4PM
205
- - Service area: 15 mile radius from Los Angeles
206
- - Owner: Jay, Phone: {JAY_PHONE}
207
-
208
- {"IMPORTANT: Customer's call was forwarded from Jay's phone. Be empathetic about any wait time." if context and context.get('forwarded') else ""}"""
209
-
210
  data = {
211
  "model": "deepseek-chat",
212
  "messages": [
213
- {"role": "system", "content": system_prompt},
214
  {"role": "user", "content": prompt}
215
  ],
216
- "max_tokens": 150,
217
- "temperature": 0.7
218
  }
219
 
220
- response = requests.post(
221
- "https://api.deepseek.com/v1/chat/completions",
222
- headers=headers,
223
- json=data,
224
- timeout=10
225
- )
226
 
227
  if response.status_code == 200:
228
- result = response.json()
229
- return result['choices'][0]['message']['content'].strip()
230
-
231
- except Exception as e:
232
- logger.error(f"DeepSeek error: {e}")
233
-
234
  return None
235
 
236
- # Initialize AI processor
237
  ai = SimpleAI()
238
 
239
- # Gradio Interface Functions
240
- def get_dashboard_data():
241
- """Get current dashboard data"""
242
- stats = system_state.get_all()
243
  uptime = datetime.now() - stats['start_time']
244
 
245
- dashboard_html = f"""
246
- <div style="font-family: Arial, sans-serif; padding: 20px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); border-radius: 15px; color: white;">
247
- <h1 style="text-align: center; margin-bottom: 30px;">๐Ÿš— {BUSINESS_NAME} - AI Dashboard</h1>
248
 
249
- <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 20px; margin-bottom: 30px;">
250
- <div style="background: rgba(255,255,255,0.15); padding: 20px; border-radius: 10px; text-align: center;">
251
- <h2 style="color: #4facfe; font-size: 2em; margin: 0;">{stats['calls_today']}</h2>
252
- <p style="margin: 5px 0 0 0;">๐Ÿ“ž Calls Today</p>
253
  </div>
254
- <div style="background: rgba(255,255,255,0.15); padding: 20px; border-radius: 10px; text-align: center;">
255
- <h2 style="color: #4facfe; font-size: 2em; margin: 0;">{stats['sms_today']}</h2>
256
- <p style="margin: 5px 0 0 0;">๐Ÿ“ฑ SMS Today</p>
257
  </div>
258
- <div style="background: rgba(255,255,255,0.15); padding: 20px; border-radius: 10px; text-align: center;">
259
- <h2 style="color: #4facfe; font-size: 2em; margin: 0;">{stats['calls_forwarded']}</h2>
260
- <p style="margin: 5px 0 0 0;">๐Ÿ”„ Calls Forwarded</p>
261
- </div>
262
- <div style="background: rgba(255,255,255,0.15); padding: 20px; border-radius: 10px; text-align: center;">
263
  <h2 style="color: #4facfe; font-size: 2em; margin: 0;">{stats['ai_responses']}</h2>
264
- <p style="margin: 5px 0 0 0;">๐Ÿค– AI Responses</p>
265
  </div>
266
  </div>
267
 
268
- <div style="background: rgba(255,255,255,0.1); padding: 20px; border-radius: 10px; margin-bottom: 20px;">
269
- <h3 style="color: #4facfe; margin-bottom: 15px;">๐Ÿ“ž iPhone Forwarding Configuration</h3>
270
  <p><strong>Jay's iPhone:</strong> {JAY_PHONE}</p>
271
- <p><strong>AI Assistant Number:</strong> {SIGNALWIRE_PHONE}</p>
272
- <p><strong>Forwarding Status:</strong> {'โœ… Enabled' if FORWARDING_ENABLED else 'โŒ Disabled'}</p>
273
- <p><strong>Forwarding Delay:</strong> {FORWARDING_DELAY} seconds</p>
274
- <p><strong>SignalWire:</strong> {'โœ… Connected' if signalwire_client else 'โŒ Not configured'}</p>
275
- <p><strong>DeepSeek AI:</strong> {'โœ… Connected' if DEEPSEEK_API_KEY else 'โš ๏ธ Fallback mode'}</p>
276
- <p><strong>System Uptime:</strong> {int(uptime.total_seconds() / 3600)} hours</p>
277
  </div>
278
 
279
- <div style="background: rgba(255,255,255,0.1); padding: 20px; border-radius: 10px;">
280
- <h3 style="color: #4facfe; margin-bottom: 15px;">๐Ÿ’ผ Services & Pricing</h3>
281
- <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 15px;">
282
- """
283
-
284
- for service_id, service in SERVICES.items():
285
- dashboard_html += f"""
286
- <div style="background: rgba(79, 172, 254, 0.2); padding: 15px; border-radius: 8px; text-align: center;">
287
- <h4 style="margin: 0 0 5px 0; color: white;">{service['name']}</h4>
288
- <p style="margin: 0 0 10px 0; font-size: 0.9em; opacity: 0.8;">{service['description']}</p>
289
- <p style="margin: 0; font-size: 1.5em; font-weight: bold; color: #00ff88;">${service['price']:.0f}</p>
 
 
 
 
 
 
 
290
  </div>
291
- """
292
-
293
- dashboard_html += """
294
  </div>
295
  </div>
296
  </div>
297
  """
298
-
299
- return dashboard_html
300
-
301
- def simulate_call(from_number, is_forwarded):
302
- """Simulate a call for demo purposes"""
303
- try:
304
- # Add to call log
305
- call_entry = {
306
- 'time': datetime.now().strftime('%H:%M:%S'),
307
- 'from': from_number if from_number else '+1555123456',
308
- 'type': 'Forwarded Call' if is_forwarded else 'Direct Call',
309
- 'status': 'Simulated'
310
- }
311
- system_state.add_call_log(call_entry)
312
-
313
- # Update stats
314
- system_state.increment('calls_today')
315
- if is_forwarded:
316
- system_state.increment('calls_forwarded')
317
-
318
- return f"โœ… Simulated {'forwarded' if is_forwarded else 'direct'} call from {call_entry['from']}"
319
-
320
- except Exception as e:
321
- return f"โŒ Error simulating call: {e}"
322
-
323
- def simulate_sms(from_number, message):
324
- """Simulate an SMS for demo purposes"""
325
- try:
326
- # Add to SMS log
327
- sms_entry = {
328
- 'time': datetime.now().strftime('%H:%M:%S'),
329
- 'from': from_number if from_number else '+1555123456',
330
- 'message': message[:50] + '...' if len(message) > 50 else message,
331
- 'status': 'Simulated'
332
- }
333
- system_state.add_sms_log(sms_entry)
334
-
335
- # Update stats
336
- system_state.increment('sms_today')
337
-
338
- # Generate AI response
339
- ai_response = ai.generate_response(message, {'sms': True})
340
- system_state.increment('ai_responses')
341
-
342
- # Add AI response to log
343
- response_entry = {
344
- 'time': datetime.now().strftime('%H:%M:%S'),
345
- 'from': 'AI Assistant',
346
- 'message': ai_response[:50] + '...' if len(ai_response) > 50 else ai_response,
347
- 'status': 'AI Response'
348
- }
349
- system_state.add_sms_log(response_entry)
350
-
351
- return f"โœ… SMS simulated and AI responded!\n\n**Customer:** {message}\n\n**AI Response:** {ai_response}"
352
-
353
- except Exception as e:
354
- return f"โŒ Error simulating SMS: {e}"
355
-
356
- def get_activity_log():
357
- """Get combined activity log"""
358
- stats = system_state.get_all()
359
- call_log = stats.get('call_log', [])
360
- sms_log = stats.get('sms_log', [])
361
-
362
- # Combine and sort by time
363
- all_activity = []
364
-
365
- for call in call_log:
366
- all_activity.append({
367
- 'time': call['time'],
368
- 'type': '๐Ÿ“ž Call',
369
- 'details': f"{call['from']} | {call['type']} | {call['status']}",
370
- 'timestamp': call['time']
371
- })
372
-
373
- for sms in sms_log:
374
- all_activity.append({
375
- 'time': sms['time'],
376
- 'type': '๐Ÿ“ฑ SMS',
377
- 'details': f"{sms['from']} | {sms['message']} | {sms['status']}",
378
- 'timestamp': sms['time']
379
- })
380
-
381
- # Sort by time (newest first)
382
- all_activity.sort(key=lambda x: x['timestamp'], reverse=True)
383
-
384
- if not all_activity:
385
- return "No activity logged yet. Use the Demo tab to simulate calls and SMS!"
386
-
387
- log_html = """
388
- <div style="font-family: Arial, sans-serif;">
389
- <h3>๐Ÿ”„ Recent Activity</h3>
390
- <div style="max-height: 400px; overflow-y: auto;">
391
- """
392
-
393
- for activity in all_activity[:20]: # Show last 20 activities
394
- color = "#4facfe" if activity['type'] == '๐Ÿ“ž Call' else "#00ff88"
395
- log_html += f"""
396
- <div style="background: #f5f5f5; padding: 10px; margin: 5px 0; border-radius: 5px; border-left: 4px solid {color};">
397
- <strong>{activity['time']}</strong> | {activity['type']}<br>
398
- <span style="color: #666;">{activity['details']}</span>
399
- </div>
400
- """
401
-
402
- log_html += "</div></div>"
403
- return log_html
404
 
405
- def test_ai_response(message):
406
- """Test AI response generation"""
407
  if not message.strip():
408
  return "Please enter a message to test."
409
 
410
  try:
411
  intent = ai.detect_intent(message)
412
- sentiment = ai.analyze_sentiment(message)
413
- response = ai.generate_response(message, {'test': True})
414
 
415
- # Increment AI responses counter
416
- system_state.increment('ai_responses')
417
-
418
- result = f"""
419
- <div style="font-family: Arial, sans-serif; padding: 15px; background: #f9f9f9; border-radius: 10px;">
420
  <h3>๐Ÿค– AI Response Test</h3>
421
- <p><strong>Customer Message:</strong> {message}</p>
422
  <p><strong>Detected Intent:</strong> {intent}</p>
423
- <p><strong>Sentiment:</strong> {sentiment['label']} ({sentiment['score']:.2f})</p>
424
  <div style="background: #e3f2fd; padding: 10px; border-radius: 5px; margin-top: 10px;">
425
- <strong>AI Response:</strong><br>
426
- {response}
427
  </div>
428
  </div>
429
  """
430
- return result
431
  except Exception as e:
432
- return f"Error testing AI response: {e}"
433
 
434
- # Create Gradio Interface
435
- with gr.Blocks(
436
- title=f"{BUSINESS_NAME} - AI Dashboard",
437
- theme=gr.themes.Soft(),
438
- css="""
439
- .gradio-container {
440
- background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
441
  }
442
- """
443
- ) as interface:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
444
 
445
  gr.Markdown(f"""
446
  # ๐Ÿš— {BUSINESS_NAME} - iPhone Forwarding AI System
447
 
448
- **Real-time dashboard for your AI assistant with iPhone call forwarding**
449
-
450
- ๐Ÿ“ž **Jay's iPhone:** {JAY_PHONE}
451
- ๐Ÿค– **AI Assistant:** {SIGNALWIRE_PHONE}
452
- ๐Ÿ”„ **Forwarding:** {'โœ… Enabled' if FORWARDING_ENABLED else 'โŒ Disabled'}
453
- ๐Ÿง  **DeepSeek AI:** {'โœ… Connected' if DEEPSEEK_API_KEY else 'โš ๏ธ Fallback Mode'}
454
  """)
455
 
456
  with gr.Tabs():
457
  with gr.Tab("๐Ÿ“Š Dashboard"):
458
- dashboard_display = gr.HTML(value=get_dashboard_data())
459
- with gr.Row():
460
- refresh_btn = gr.Button("๐Ÿ”„ Refresh Dashboard", variant="primary")
461
- refresh_btn.click(fn=get_dashboard_data, outputs=dashboard_display)
462
-
463
- with gr.Tab("๐Ÿ“‹ Activity Log"):
464
- activity_log_display = gr.HTML(value=get_activity_log())
465
- refresh_activity_btn = gr.Button("๐Ÿ”„ Refresh Activity", variant="secondary")
466
- refresh_activity_btn.click(fn=get_activity_log, outputs=activity_log_display)
467
 
468
  with gr.Tab("๐Ÿงช Test AI"):
469
- gr.Markdown("### Test AI Response Generation")
470
  with gr.Row():
471
  with gr.Column():
472
- test_message = gr.Textbox(
473
- label="Customer Message",
474
- placeholder="Enter a customer message to test AI response...",
475
- lines=3
476
- )
477
- test_btn = gr.Button("๐Ÿค– Test AI Response", variant="primary")
478
-
479
  with gr.Column():
480
- test_result = gr.HTML(label="AI Response Result")
481
-
482
- test_btn.click(fn=test_ai_response, inputs=test_message, outputs=test_result)
483
 
484
- with gr.Tab("๐ŸŽฎ Demo Mode"):
485
- gr.Markdown("### Simulate Calls and SMS for Testing")
486
-
487
  with gr.Row():
488
  with gr.Column():
489
- gr.Markdown("#### ๐Ÿ“ž Simulate Phone Call")
490
- call_from = gr.Textbox(label="From Number", value="+1555123456")
491
  call_forwarded = gr.Checkbox(label="Forwarded Call", value=True)
492
- call_btn = gr.Button("๐Ÿ“ž Simulate Call", variant="primary")
493
- call_result = gr.Textbox(label="Call Result", interactive=False)
494
-
495
- with gr.Column():
496
- gr.Markdown("#### ๐Ÿ“ฑ Simulate SMS")
497
- sms_from = gr.Textbox(label="From Number", value="+1555987654")
498
- sms_message = gr.Textbox(label="SMS Message", placeholder="How much for a car wash?", lines=2)
499
- sms_btn = gr.Button("๐Ÿ“ฑ Simulate SMS", variant="primary")
500
- sms_result = gr.Textbox(label="SMS Result", interactive=False, lines=4)
501
-
502
- call_btn.click(fn=simulate_call, inputs=[call_from, call_forwarded], outputs=call_result)
503
- sms_btn.click(fn=simulate_sms, inputs=[sms_from, sms_message], outputs=sms_result)
504
-
505
- with gr.Tab("โ„น๏ธ Setup Guide"):
506
- gr.Markdown(f"""
507
- ## ๐Ÿ”ง Environment Variables Configuration
508
-
509
- **Add these in HuggingFace Spaces Settings โ†’ Variables:**
510
-
511
- ```
512
- DEEPSEEK_API_KEY=your-deepseek-key
513
- SIGNALWIRE_PROJECT_ID=your-project-id
514
- SIGNALWIRE_AUTH_TOKEN=your-auth-token
515
- SIGNALWIRE_SPACE_URL=your-space.signalwire.com
516
- JAY_PHONE_NUMBER=+15622289429
517
- AI_PHONE_NUMBER=+17149278841
518
- ```
519
-
520
- ## ๐Ÿ“ก SignalWire Webhook Configuration
521
-
522
- **Set these URLs in your SignalWire dashboard:**
523
-
524
- - **Voice Webhook:** `https://YOUR-SPACE-NAME.hf.space/voice/incoming`
525
- - **SMS Webhook:** `https://YOUR-SPACE-NAME.hf.space/sms/incoming`
526
-
527
- โš ๏ธ **Note:** Full webhook functionality requires additional server setup.
528
-
529
- ## ๐Ÿ“ฑ iPhone Call Forwarding Setup
530
-
531
- **On Jay's iPhone:**
532
- 1. Go to **Settings** โ†’ **Phone** โ†’ **Call Forwarding**
533
- 2. Turn **ON** Call Forwarding
534
- 3. Set forward number to: `{SIGNALWIRE_PHONE}`
535
- 4. Choose: Forward when **unanswered** after {FORWARDING_DELAY} seconds
536
-
537
- ## ๐ŸŽฏ How iPhone Forwarding Works
538
-
539
- ```
540
- Customer calls Jay's iPhone: {JAY_PHONE}
541
- โ†“
542
- iPhone rings for {FORWARDING_DELAY} seconds
543
- โ†“
544
- โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
545
- โ”‚ ๐Ÿ“ž JAY ANSWERS โ”‚ โฐ NO ANSWER โ”‚
546
- โ”‚ โ”‚ โ”‚
547
- โ”‚ โœ… Normal โ”‚ ๐Ÿ”„ AUTO FORWARD โ”‚
548
- โ”‚ call โ”‚ to AI โ”‚
549
- โ”‚ โ”‚ โ”‚
550
- โ”‚ โ”‚ โ†“ โ”‚
551
- โ”‚ โ”‚ ๐Ÿค– AI handles โ”‚
552
- โ”‚ โ”‚ the call โ”‚
553
- โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
554
- ```
555
-
556
- ## ๐Ÿ“Š System Status
557
-
558
- - **SignalWire:** {'โœ… Connected' if signalwire_client else 'โŒ Not configured'}
559
- - **DeepSeek AI:** {'โœ… Connected' if DEEPSEEK_API_KEY else 'โš ๏ธ Using fallback responses'}
560
- - **Dashboard:** โœ… Active
561
- - **Demo Mode:** โœ… Available
562
-
563
- ## ๐ŸŽฎ Try the Demo
564
-
565
- Use the **Demo Mode** tab to simulate calls and SMS to see how the AI responds!
566
-
567
- ### Example Test Messages:
568
- - "How much for a car wash?"
569
- - "I want to book an appointment"
570
- - "Where do you provide service?"
571
- - "This is urgent!"
572
- - "I want to speak to Jay"
573
- """)
574
-
575
- # Auto-refresh every 30 seconds
576
- interface.load(fn=get_dashboard_data, outputs=dashboard_display, every=30)
577
- interface.load(fn=get_activity_log, outputs=activity_log_display, every=30)
578
-
579
- # Launch the interface
580
- if __name__ == "__main__":
581
- print("\n" + "="*60)
582
- print("๐Ÿš— JAY'S MOBILE WASH AI SYSTEM - PURE GRADIO")
583
- print("="*60)
584
- print(f"๐Ÿ“ž Jay's Phone: {JAY_PHONE}")
585
- print(f"๐Ÿค– AI Phone: {SIGNALWIRE_PHONE}")
586
- print(f"๐Ÿ”„ Forwarding: {'โœ… Enabled' if FORWARDING_ENABLED else 'โŒ Disabled'}")
587
- print(f"๐ŸŒ SignalWire: {'โœ… Connected' if signalwire_client else 'โŒ Not configured'}")
588
- print(f"๐Ÿง  DeepSeek: {'โœ… Connected' if DEEPSEEK_API_KEY else 'โŒ Not configured'}")
589
- print("="*60)
590
- print("๐Ÿš€ Starting Pure Gradio interface...")
591
- print("๐Ÿ“Š Dashboard with demo capabilities")
592
- print("="*60)
593
-
594
- interface.launch(
595
- server_name="0.0.0.0",
596
- server_port=7860,
597
- share=False,
598
- show_error=True
599
- )
 
 
 
 
 
 
 
1
  import os
2
+ import gradio as gr
3
+ from datetime import datetime
4
+ from threading import Lock
5
  import requests
6
  import logging
 
 
 
 
 
 
 
 
 
 
7
 
8
+ # Simple configuration
 
 
 
 
 
 
9
  BUSINESS_NAME = "Jay's Mobile Wash"
10
  JAY_PHONE = os.environ.get('JAY_PHONE_NUMBER', '+15622289429')
11
+ AI_PHONE = os.environ.get('AI_PHONE_NUMBER', '+17149278841')
12
+ DEEPSEEK_KEY = os.environ.get('DEEPSEEK_API_KEY', '')
 
 
 
 
 
 
 
 
 
 
13
 
14
+ # Simple state management
15
+ class SimpleState:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
16
  def __init__(self):
17
+ self.lock = Lock()
18
+ self.data = {
19
+ 'calls': 0, 'sms': 0, 'ai_responses': 0,
20
+ 'start_time': datetime.now(), 'log': []
 
 
 
 
 
 
 
 
21
  }
22
 
 
 
 
 
 
 
 
 
23
  def increment(self, key):
24
+ with self.lock:
25
+ self.data[key] += 1
26
 
27
+ def add_log(self, entry):
28
+ with self.lock:
29
+ self.data['log'].insert(0, entry)
30
+ if len(self.data['log']) > 20:
31
+ self.data['log'] = self.data['log'][:20]
 
 
 
 
32
 
33
+ def get_all(self):
34
+ with self.lock:
35
+ return self.data.copy()
 
 
36
 
37
+ state = SimpleState()
 
38
 
39
+ # Simple AI
40
  class SimpleAI:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
41
  def detect_intent(self, text):
42
+ text = text.lower()
43
+ if any(word in text for word in ['price', 'cost', 'much']):
 
 
44
  return 'pricing'
45
+ elif any(word in text for word in ['book', 'schedule', 'appointment']):
46
  return 'booking'
47
+ elif any(word in text for word in ['urgent', 'emergency']):
 
 
 
 
48
  return 'urgent'
49
+ elif any(word in text for word in ['jay', 'human', 'person']):
50
+ return 'human'
51
+ return 'general'
 
52
 
53
+ def generate_response(self, text, forwarded=False):
54
+ intent = self.detect_intent(text)
 
 
55
 
56
+ prefix = "Thanks for your patience. " if forwarded else ""
 
 
 
57
 
58
+ responses = {
59
+ 'pricing': f"{prefix}Our services: Basic wash $25, Premium $45, Full detail $85. Which interests you?",
60
+ 'booking': f"{prefix}We're available Mon-Sat 8AM-6PM, Sun 10AM-4PM. What day works for you?",
61
+ 'urgent': f"{prefix}I understand this is urgent. Let me connect you with Jay right away.",
62
+ 'human': f"{prefix}Let me connect you with Jay personally.",
63
+ 'general': f"{prefix}Hi! I'm Jay's AI assistant. I can help with pricing, scheduling, or questions about our mobile car wash services."
64
+ }
 
 
 
 
 
 
 
 
 
 
 
65
 
66
+ response = responses.get(intent, responses['general'])
 
67
 
68
+ # Try DeepSeek if available
69
+ if DEEPSEEK_KEY and intent not in ['urgent', 'human']:
70
  try:
71
+ enhanced = self.get_deepseek_response(text)
72
+ if enhanced:
73
+ response = enhanced
74
+ except:
75
+ pass
76
 
77
  return response
78
 
79
+ def get_deepseek_response(self, prompt):
 
80
  try:
81
+ headers = {"Authorization": f"Bearer {DEEPSEEK_KEY}", "Content-Type": "application/json"}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
82
  data = {
83
  "model": "deepseek-chat",
84
  "messages": [
85
+ {"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}"},
86
  {"role": "user", "content": prompt}
87
  ],
88
+ "max_tokens": 150
 
89
  }
90
 
91
+ response = requests.post("https://api.deepseek.com/v1/chat/completions",
92
+ headers=headers, json=data, timeout=10)
 
 
 
 
93
 
94
  if response.status_code == 200:
95
+ return response.json()['choices'][0]['message']['content'].strip()
96
+ except:
97
+ pass
 
 
 
98
  return None
99
 
 
100
  ai = SimpleAI()
101
 
102
+ # Dashboard function
103
+ def get_dashboard():
104
+ stats = state.get_all()
 
105
  uptime = datetime.now() - stats['start_time']
106
 
107
+ return f"""
108
+ <div style="font-family: Arial; padding: 20px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); border-radius: 15px; color: white;">
109
+ <h1 style="text-align: center;">๐Ÿš— {BUSINESS_NAME} - AI Dashboard</h1>
110
 
111
+ <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 15px; margin: 20px 0;">
112
+ <div style="background: rgba(255,255,255,0.15); padding: 15px; border-radius: 10px; text-align: center;">
113
+ <h2 style="color: #4facfe; font-size: 2em; margin: 0;">{stats['calls']}</h2>
114
+ <p style="margin: 5px 0;">๐Ÿ“ž Calls</p>
115
  </div>
116
+ <div style="background: rgba(255,255,255,0.15); padding: 15px; border-radius: 10px; text-align: center;">
117
+ <h2 style="color: #4facfe; font-size: 2em; margin: 0;">{stats['sms']}</h2>
118
+ <p style="margin: 5px 0;">๐Ÿ“ฑ SMS</p>
119
  </div>
120
+ <div style="background: rgba(255,255,255,0.15); padding: 15px; border-radius: 10px; text-align: center;">
 
 
 
 
121
  <h2 style="color: #4facfe; font-size: 2em; margin: 0;">{stats['ai_responses']}</h2>
122
+ <p style="margin: 5px 0;">๐Ÿค– AI Responses</p>
123
  </div>
124
  </div>
125
 
126
+ <div style="background: rgba(255,255,255,0.1); padding: 15px; border-radius: 10px; margin: 20px 0;">
127
+ <h3 style="color: #4facfe;">๐Ÿ“ž iPhone Forwarding Status</h3>
128
  <p><strong>Jay's iPhone:</strong> {JAY_PHONE}</p>
129
+ <p><strong>AI Number:</strong> {AI_PHONE}</p>
130
+ <p><strong>DeepSeek AI:</strong> {'โœ… Connected' if DEEPSEEK_KEY else 'โš ๏ธ Not configured'}</p>
131
+ <p><strong>Uptime:</strong> {int(uptime.total_seconds() / 3600)} hours</p>
 
 
 
132
  </div>
133
 
134
+ <div style="background: rgba(255,255,255,0.1); padding: 15px; border-radius: 10px;">
135
+ <h3 style="color: #4facfe;">๐Ÿ’ผ Services</h3>
136
+ <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); gap: 10px;">
137
+ <div style="background: rgba(79,172,254,0.2); padding: 10px; border-radius: 8px; text-align: center;">
138
+ <h4 style="margin: 0; color: white;">Basic Wash</h4>
139
+ <p style="margin: 5px 0; color: #00ff88; font-size: 1.2em;">$25</p>
140
+ </div>
141
+ <div style="background: rgba(79,172,254,0.2); padding: 10px; border-radius: 8px; text-align: center;">
142
+ <h4 style="margin: 0; color: white;">Premium</h4>
143
+ <p style="margin: 5px 0; color: #00ff88; font-size: 1.2em;">$45</p>
144
+ </div>
145
+ <div style="background: rgba(79,172,254,0.2); padding: 10px; border-radius: 8px; text-align: center;">
146
+ <h4 style="margin: 0; color: white;">Full Detail</h4>
147
+ <p style="margin: 5px 0; color: #00ff88; font-size: 1.2em;">$85</p>
148
+ </div>
149
+ <div style="background: rgba(79,172,254,0.2); padding: 10px; border-radius: 8px; text-align: center;">
150
+ <h4 style="margin: 0; color: white;">Ceramic</h4>
151
+ <p style="margin: 5px 0; color: #00ff88; font-size: 1.2em;">$150</p>
152
  </div>
 
 
 
153
  </div>
154
  </div>
155
  </div>
156
  """
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
157
 
158
+ # Test AI function
159
+ def test_ai(message):
160
  if not message.strip():
161
  return "Please enter a message to test."
162
 
163
  try:
164
  intent = ai.detect_intent(message)
165
+ response = ai.generate_response(message)
166
+ state.increment('ai_responses')
167
 
168
+ return f"""
169
+ <div style="font-family: Arial; padding: 15px; background: #f9f9f9; border-radius: 10px;">
 
 
 
170
  <h3>๐Ÿค– AI Response Test</h3>
171
+ <p><strong>Your Message:</strong> {message}</p>
172
  <p><strong>Detected Intent:</strong> {intent}</p>
 
173
  <div style="background: #e3f2fd; padding: 10px; border-radius: 5px; margin-top: 10px;">
174
+ <strong>AI Response:</strong><br>{response}
 
175
  </div>
176
  </div>
177
  """
 
178
  except Exception as e:
179
+ return f"Error: {e}"
180
 
181
+ # Simulate functions
182
+ def simulate_call(phone, forwarded):
183
+ entry = {
184
+ 'time': datetime.now().strftime('%H:%M:%S'),
185
+ 'type': 'Call',
186
+ 'from': phone or '+1555123456',
187
+ 'forwarded': forwarded
188
  }
189
+ state.add_log(entry)
190
+ state.increment('calls')
191
+
192
+ return f"โœ… Simulated {'forwarded' if forwarded else 'direct'} call from {entry['from']}"
193
+
194
+ def simulate_sms(phone, message):
195
+ if not message.strip():
196
+ return "Please enter a message."
197
+
198
+ # Log incoming SMS
199
+ entry = {
200
+ 'time': datetime.now().strftime('%H:%M:%S'),
201
+ 'type': 'SMS',
202
+ 'from': phone or '+1555123456',
203
+ 'message': message
204
+ }
205
+ state.add_log(entry)
206
+ state.increment('sms')
207
+
208
+ # Generate AI response
209
+ response = ai.generate_response(message)
210
+ state.increment('ai_responses')
211
+
212
+ # Log AI response
213
+ response_entry = {
214
+ 'time': datetime.now().strftime('%H:%M:%S'),
215
+ 'type': 'AI Response',
216
+ 'from': 'AI Assistant',
217
+ 'message': response
218
+ }
219
+ state.add_log(response_entry)
220
+
221
+ return f"โœ… SMS processed!\n\n**Customer:** {message}\n\n**AI Response:** {response}"
222
+
223
+ # Activity log
224
+ def get_activity():
225
+ stats = state.get_all()
226
+ log = stats.get('log', [])
227
+
228
+ if not log:
229
+ return "No activity yet. Try the demo!"
230
+
231
+ html = "<div style='font-family: Arial;'><h3>๐Ÿ“‹ Recent Activity</h3>"
232
+ for entry in log[:10]:
233
+ color = "#4facfe" if entry['type'] == 'Call' else "#00ff88" if entry['type'] == 'SMS' else "#ff6b6b"
234
+ html += f"""
235
+ <div style="background: #f5f5f5; padding: 10px; margin: 5px 0; border-radius: 5px; border-left: 4px solid {color};">
236
+ <strong>{entry['time']}</strong> | {entry['type']} | {entry['from']}<br>
237
+ {entry.get('message', entry.get('forwarded', ''))}
238
+ </div>
239
+ """
240
+ html += "</div>"
241
+ return html
242
+
243
+ # Create Gradio interface
244
+ with gr.Blocks(title=f"{BUSINESS_NAME} - AI Dashboard", theme=gr.themes.Soft()) as demo:
245
 
246
  gr.Markdown(f"""
247
  # ๐Ÿš— {BUSINESS_NAME} - iPhone Forwarding AI System
248
 
249
+ **Jay's iPhone:** {JAY_PHONE} โ†’ **AI Assistant:** {AI_PHONE}
 
 
 
 
 
250
  """)
251
 
252
  with gr.Tabs():
253
  with gr.Tab("๐Ÿ“Š Dashboard"):
254
+ dashboard = gr.HTML(value=get_dashboard())
255
+ gr.Button("๐Ÿ”„ Refresh").click(fn=get_dashboard, outputs=dashboard)
 
 
 
 
 
 
 
256
 
257
  with gr.Tab("๐Ÿงช Test AI"):
 
258
  with gr.Row():
259
  with gr.Column():
260
+ test_input = gr.Textbox(label="Test Message", placeholder="How much for a car wash?", lines=2)
261
+ test_btn = gr.Button("๐Ÿค– Test AI", variant="primary")
 
 
 
 
 
262
  with gr.Column():
263
+ test_output = gr.HTML()
264
+ test_btn.click(fn=test_ai, inputs=test_input, outputs=test_output)
 
265
 
266
+ with gr.Tab("๐ŸŽฎ Demo"):
 
 
267
  with gr.Row():
268
  with gr.Column():
269
+ gr.Markdown("#### ๐Ÿ“ž Simulate Call")
270
+ call_phone = gr.Textbox(label="Phone", value="+1555123456")
271
  call_forwarded = gr.Checkbox(label="Forwarded Call", value=True)
272
+ call_btn = gr.Button("๐Ÿ“ž Simulate")
273
+ call_result = gr.Textbox(label