Peter Michael Gits Claude commited on
Commit
3cb7ae9
Β·
1 Parent(s): 35326c5

feat: Complete fresh restart with clean Streamlit configuration

Browse files

- Replaced complex Gradio app with minimal Streamlit interface
- Updated README.md to use app.py instead of hello_world.py
- Clean configuration to resolve persistent 500 errors
- Testing basic Streamlit functionality in existing space

πŸ€– Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>

Files changed (2) hide show
  1. README.md +1 -1
  2. app.py +12 -426
README.md CHANGED
@@ -4,7 +4,7 @@ emoji: πŸŽ€πŸ“…
4
  colorFrom: blue
5
  colorTo: purple
6
  sdk: streamlit
7
- app_file: hello_world.py
8
  pinned: false
9
  license: mit
10
  short_description: Voice-enabled AI assistant with WebRTC integration
 
4
  colorFrom: blue
5
  colorTo: purple
6
  sdk: streamlit
7
+ app_file: app.py
8
  pinned: false
9
  license: mit
10
  short_description: Voice-enabled AI assistant with WebRTC integration
app.py CHANGED
@@ -1,430 +1,16 @@
1
- #!/usr/bin/env python3
2
- """
3
- ChatCal Voice-Enabled AI Assistant - Hugging Face Gradio Implementation
4
 
5
- A voice-enabled calendar booking assistant with real-time speech-to-text,
6
- text-to-speech responses, and Google Calendar integration.
7
- """
 
 
8
 
9
- import gradio as gr
10
- import os
11
- import asyncio
12
- import json
13
- from typing import Dict, List, Tuple, Optional
14
- from datetime import datetime
15
 
16
- # Hugging Face Spaces GPU support
17
- import spaces
 
18
 
19
- # Core functionality imports
20
- from core.chat_agent import ChatCalAgent
21
- from core.session_manager import SessionManager
22
- from core.mcp_audio_handler import MCPAudioHandler
23
- from core.config import config
24
- from version import get_version_info
25
-
26
- # WebRTC imports - re-enabled for WebRTC-first approach
27
- from webrtc.server.fastapi_integration import create_fastapi_app
28
-
29
- # Required GPU function for Hugging Face Spaces
30
- @spaces.GPU
31
- def initialize_gpu_resources():
32
- """Initialize GPU resources for VoiceCal AI processing."""
33
- # This function satisfies the HF Spaces requirement for @spaces.GPU
34
- # GPU resources are actually used by external STT/TTS services
35
- return "GPU initialized for VoiceCal v0.5.7"
36
-
37
- class ChatCalVoiceApp:
38
- """Main application class for voice-enabled ChatCal."""
39
-
40
- def __init__(self):
41
- self.session_manager = SessionManager()
42
- self.chat_agent = ChatCalAgent()
43
- self.audio_handler = MCPAudioHandler()
44
-
45
- async def process_message(
46
- self,
47
- message: str,
48
- history: List[Tuple[str, str]],
49
- session_id: str
50
- ) -> Tuple[List[Tuple[str, str]], str]:
51
- """Process a chat message and return updated history."""
52
- try:
53
- # Get or create session
54
- session = await self.session_manager.get_session(session_id)
55
-
56
- # Process message through ChatCal agent
57
- response = await self.chat_agent.process_message(message, session)
58
-
59
- # Update conversation history
60
- history.append((message, response))
61
-
62
- return history, ""
63
-
64
- except Exception as e:
65
- error_msg = f"Sorry, I encountered an error: {str(e)}"
66
- history.append((message, error_msg))
67
- return history, ""
68
-
69
- async def process_audio(
70
- self,
71
- audio_data: bytes,
72
- history: List[Tuple[str, str]],
73
- session_id: str
74
- ) -> Tuple[List[Tuple[str, str]], str, bytes]:
75
- """Process audio input and return transcription + response audio."""
76
- try:
77
- # Convert audio to text via STT service
78
- transcription = await self.audio_handler.speech_to_text(audio_data)
79
-
80
- # Process the transcribed message
81
- history, _ = await self.process_message(transcription, history, session_id)
82
-
83
- # Get the latest response for TTS
84
- if history:
85
- latest_response = history[-1][1]
86
- # Convert response to speech
87
- response_audio = await self.audio_handler.text_to_speech(latest_response)
88
- return history, transcription, response_audio
89
-
90
- return history, transcription, None
91
-
92
- except Exception as e:
93
- error_msg = f"Audio processing error: {str(e)}"
94
- history.append(("(Audio input)", error_msg))
95
- return history, "", None
96
-
97
- def create_interface(self) -> gr.Interface:
98
- """Create the main Gradio interface."""
99
-
100
- with gr.Blocks(
101
- theme=gr.themes.Soft(),
102
- title="ChatCal Voice Assistant",
103
- # Note: max_file_size removed for HF Spaces compatibility
104
- css="""
105
- .chat-container {
106
- max-height: 500px;
107
- overflow-y: auto;
108
- }
109
- .voice-controls {
110
- background: linear-gradient(45deg, #667eea 0%, #764ba2 100%);
111
- padding: 10px;
112
- border-radius: 10px;
113
- margin: 10px 0;
114
- }
115
- .status-indicator {
116
- display: inline-block;
117
- width: 12px;
118
- height: 12px;
119
- border-radius: 50%;
120
- margin-right: 8px;
121
- }
122
- .recording { background-color: #ff4444; }
123
- .idle { background-color: #44ff44; }
124
- """
125
- ) as demo:
126
-
127
- # Title and description
128
- gr.Markdown("""
129
- # πŸŽ€πŸ“… ChatCal Voice Assistant
130
-
131
- **Book your Google Calendar appointments with voice or text!**
132
-
133
- - ����️ **Voice Input**: Click record, speak naturally
134
- - πŸ’¬ **Text Input**: Type your message
135
- - πŸ“… **Smart Booking**: AI understands dates, times, and preferences
136
- - πŸŽ₯ **Google Meet**: Automatic video conference setup
137
- """)
138
-
139
- # Session state
140
- session_id = gr.State(value=lambda: f"session_{datetime.now().timestamp()}")
141
-
142
- with gr.Row():
143
- with gr.Column(scale=3):
144
- # Chat history display
145
- chatbot = gr.Chatbot(
146
- label="Chat History",
147
- height=400,
148
- elem_classes=["chat-container"],
149
- type="messages"
150
- )
151
-
152
- with gr.Row(elem_classes=["voice-controls"]):
153
- # Traditional Voice input section
154
- with gr.Column(scale=2):
155
- audio_input = gr.Audio(
156
- type="numpy",
157
- label="🎀 Voice Input (Gradio)",
158
- interactive=True
159
- )
160
- voice_status = gr.HTML(
161
- value='<span class="status-indicator idle"></span>Ready for voice input'
162
- )
163
-
164
- with gr.Column(scale=1):
165
- # Audio output
166
- audio_output = gr.Audio(
167
- label="πŸ”Š AI Response",
168
- type="numpy",
169
- interactive=False
170
- )
171
-
172
- # WebRTC Real-time Voice Section
173
- with gr.Row():
174
- gr.HTML("""
175
- <div style="background: linear-gradient(45deg, #28a745 0%, #20c997 100%);
176
- padding: 15px; border-radius: 10px; margin: 10px 0;">
177
- <h3 style="color: white; margin: 0;">πŸš€ WebRTC Real-time Voice (Beta)</h3>
178
- <p style="color: white; margin: 5px 0;">
179
- Enhanced real-time voice interaction with streaming transcription
180
- </p>
181
- <p style="color: white; margin: 5px 0; font-size: 0.9em;">
182
- πŸ“‘ <strong>WebSocket endpoints:</strong> /ws/webrtc/{client_id} |
183
- πŸ§ͺ <strong>Test page:</strong> <a href="/webrtc/demo" style="color: #fff; text-decoration: underline;">WebRTC Demo</a> |
184
- ⚑ <strong>API Status:</strong> <a href="/webrtc/test" style="color: #fff; text-decoration: underline;">Test Endpoint</a>
185
- </p>
186
- </div>
187
- """)
188
-
189
- # Text input section
190
- with gr.Row():
191
- text_input = gr.Textbox(
192
- label="πŸ’¬ Type your message or see voice transcription",
193
- placeholder="Hi! I'm [Your Name]. Book a 30-minute meeting tomorrow at 2 PM...",
194
- lines=2,
195
- scale=4
196
- )
197
- send_btn = gr.Button("Send", variant="primary", scale=1)
198
-
199
- with gr.Column(scale=1):
200
- # Quick action buttons
201
- gr.Markdown("### πŸš€ Quick Actions")
202
-
203
- quick_meet = gr.Button(
204
- "πŸŽ₯ Google Meet (30m)",
205
- variant="secondary"
206
- )
207
- quick_availability = gr.Button(
208
- "πŸ“… Check Availability",
209
- variant="secondary"
210
- )
211
- quick_cancel = gr.Button(
212
- "❌ Cancel Meeting",
213
- variant="secondary"
214
- )
215
-
216
- # Version info
217
- version_btn = gr.Button(
218
- "ℹ️ Version Info",
219
- variant="secondary"
220
- )
221
- version_display = gr.Textbox(
222
- label="Version Information",
223
- interactive=False,
224
- visible=False
225
- )
226
-
227
- # Voice settings
228
- gr.Markdown("### 🎭 Voice Settings")
229
- voice_enabled = gr.Checkbox(
230
- label="Enable voice responses",
231
- value=True
232
- )
233
- voice_selection = gr.Dropdown(
234
- choices=[
235
- "v2/en_speaker_0",
236
- "v2/en_speaker_1",
237
- "v2/en_speaker_2",
238
- "v2/en_speaker_6",
239
- "v2/en_speaker_9"
240
- ],
241
- value="v2/en_speaker_6",
242
- label="AI Voice"
243
- )
244
-
245
- # Event handlers
246
- def handle_text_submit(message, history, session):
247
- if message.strip():
248
- # Use asyncio to handle the async function
249
- loop = asyncio.new_event_loop()
250
- asyncio.set_event_loop(loop)
251
- try:
252
- result = loop.run_until_complete(
253
- app.process_message(message, history, session)
254
- )
255
- return result
256
- finally:
257
- loop.close()
258
- return history, message
259
-
260
- def handle_audio_submit(audio, history, session):
261
- print(f"🎀 AUDIO DEBUG: Received audio input: {type(audio)}")
262
- print(f"🎀 AUDIO DEBUG: Audio data: {audio}")
263
-
264
- if audio is not None:
265
- print(f"🎀 AUDIO DEBUG: Processing audio...")
266
- # Convert audio data and process
267
- loop = asyncio.new_event_loop()
268
- asyncio.set_event_loop(loop)
269
- try:
270
- # Debug audio format
271
- if isinstance(audio, tuple) and len(audio) >= 2:
272
- sample_rate, audio_array = audio
273
- print(f"🎀 AUDIO DEBUG: Sample rate: {sample_rate}")
274
- print(f"🎀 AUDIO DEBUG: Audio array type: {type(audio_array)}")
275
- print(f"🎀 AUDIO DEBUG: Audio array shape: {audio_array.shape if hasattr(audio_array, 'shape') else 'No shape'}")
276
-
277
- # Use the audio handler's process method instead
278
- transcription = app.audio_handler.process_audio_input(audio)
279
- print(f"🎀 AUDIO DEBUG: Transcription result: {transcription}")
280
-
281
- if transcription and transcription != "No audio received":
282
- # Process the transcription as a message
283
- result = loop.run_until_complete(
284
- app.process_message(transcription, history, session)
285
- )
286
- # Return updated history, transcription in text box, and no audio output for now
287
- return result[0], transcription, None
288
- else:
289
- print(f"🎀 AUDIO DEBUG: No valid transcription received")
290
- return history, "No audio transcription available", None
291
- else:
292
- print(f"🎀 AUDIO DEBUG: Invalid audio format")
293
- return history, "Invalid audio format", None
294
-
295
- except Exception as e:
296
- print(f"🎀 AUDIO ERROR: {str(e)}")
297
- import traceback
298
- traceback.print_exc()
299
- return history, f"Audio processing error: {str(e)}", None
300
- finally:
301
- loop.close()
302
- else:
303
- print(f"🎀 AUDIO DEBUG: No audio received")
304
- return history, "No audio received", None
305
-
306
- def handle_quick_action(action_text, history, session):
307
- """Handle quick action button clicks."""
308
- loop = asyncio.new_event_loop()
309
- asyncio.set_event_loop(loop)
310
- try:
311
- result = loop.run_until_complete(
312
- app.process_message(action_text, history, session)
313
- )
314
- return result[0], "" # Return updated history and clear text input
315
- finally:
316
- loop.close()
317
-
318
- # Wire up the event handlers
319
- send_btn.click(
320
- fn=handle_text_submit,
321
- inputs=[text_input, chatbot, session_id],
322
- outputs=[chatbot, text_input]
323
- )
324
-
325
- text_input.submit(
326
- fn=handle_text_submit,
327
- inputs=[text_input, chatbot, session_id],
328
- outputs=[chatbot, text_input]
329
- )
330
-
331
- audio_input.change(
332
- fn=handle_audio_submit,
333
- inputs=[audio_input, chatbot, session_id],
334
- outputs=[chatbot, text_input, audio_output]
335
- )
336
-
337
- # Quick action handlers
338
- quick_meet.click(
339
- fn=lambda hist, sess: handle_quick_action(
340
- "Book a 30-minute Google Meet with Peter for next available time",
341
- hist, sess
342
- ),
343
- inputs=[chatbot, session_id],
344
- outputs=[chatbot, text_input]
345
- )
346
-
347
- quick_availability.click(
348
- fn=lambda hist, sess: handle_quick_action(
349
- "What is Peter's availability this week?",
350
- hist, sess
351
- ),
352
- inputs=[chatbot, session_id],
353
- outputs=[chatbot, text_input]
354
- )
355
-
356
- quick_cancel.click(
357
- fn=lambda hist, sess: handle_quick_action(
358
- "Cancel my upcoming meeting with Peter",
359
- hist, sess
360
- ),
361
- inputs=[chatbot, session_id],
362
- outputs=[chatbot, text_input]
363
- )
364
-
365
- # Version info handler
366
- def show_version():
367
- info = get_version_info()
368
- version_text = f"Version: {info['version']}\nBuild: {info['build_date']}\nDescription: {info['description']}\nStatus: {info['status']}"
369
- return version_text, gr.update(visible=True)
370
-
371
- version_btn.click(
372
- fn=show_version,
373
- outputs=[version_display, version_display]
374
- )
375
-
376
- return demo
377
-
378
- # Initialize GPU resources for Hugging Face Spaces
379
- gpu_status = initialize_gpu_resources()
380
- print(f"πŸš€ {gpu_status}")
381
-
382
- # Global app instance
383
- app = ChatCalVoiceApp()
384
-
385
- # Create and launch the interface
386
- if __name__ == "__main__":
387
- import uvicorn
388
-
389
- try:
390
- # Create WebRTC-enabled FastAPI app as main app
391
- webrtc_app = create_fastapi_app()
392
-
393
- # Hybrid approach: Mount Gradio on FastAPI for complete functionality
394
- from version import __version__
395
- print(f"πŸš€ VoiceCal - Voice-Enabled AI Scheduling Assistant v{__version__}")
396
- print("πŸ“‘ WebSocket endpoint: /ws/webrtc/{client_id}")
397
- print("πŸ§ͺ WebRTC demo page: /webrtc/demo")
398
- print("⚑ API status: /webrtc/test")
399
- print("🎀 Full Gradio interface with voice integration")
400
-
401
- # Create Gradio interface
402
- demo = app.create_interface()
403
-
404
- # Mount Gradio app on FastAPI for hybrid deployment
405
- webrtc_app.mount("/", demo.app)
406
-
407
- # Launch hybrid app with both WebRTC and Gradio
408
- uvicorn.run(webrtc_app, host="0.0.0.0", port=7860)
409
-
410
- except Exception as e:
411
- print(f"❌ WebRTC integration error: {e}")
412
- print("πŸ“‹ Falling back to Gradio-only deployment")
413
- import traceback
414
- traceback.print_exc()
415
-
416
- # Create stable Gradio interface fallback
417
- demo = app.create_interface()
418
-
419
- from version import __version__
420
- print(f"πŸš€ VoiceCal - Voice-Enabled AI Scheduling Assistant v{__version__} (Fallback)")
421
- print("πŸ“± Traditional voice input available via Gradio Audio component")
422
- print("βš™οΈ WebRTC real-time streaming: Error - using fallback mode")
423
-
424
- # Launch configuration for HF Spaces (stable fallback)
425
- demo.launch(
426
- server_name="0.0.0.0",
427
- server_port=7860,
428
- share=False, # HF handles sharing
429
- show_error=True
430
- )
 
1
+ import streamlit as st
 
 
2
 
3
+ st.set_page_config(
4
+ page_title="VoiceCal Fresh",
5
+ page_icon="🎀",
6
+ layout="centered"
7
+ )
8
 
9
+ st.title("πŸŽ€πŸ“… VoiceCal - Fresh Start")
10
+ st.write("Testing fresh deployment with clean configuration.")
 
 
 
 
11
 
12
+ if st.button("Test Basic Functionality"):
13
+ st.success("βœ… Basic Streamlit functionality working!")
14
+ st.balloons()
15
 
16
+ st.info("If you see this interface, the space infrastructure issues have been resolved.")