import gradio as gr from fastapi import FastAPI, Request from fastapi.responses import JSONResponse import uvicorn import os import json from datetime import datetime from calendar_integration import create_calendar_event, initialize_calendar # Initialize FastAPI app = FastAPI() # Store conversation logs conversation_logs = [] @app.post("/webhook") async def elevenlabs_webhook(request: Request): """ Webhook endpoint that ElevenLabs agent calls to create calendar events """ try: data = await request.json() print(f"Received webhook data: {json.dumps(data, indent=2)}") # ElevenLabs sends function parameters in different formats # Handle both direct parameters and nested format if "parameters" in data: params = data["parameters"] elif "tool_calls" in data: params = data["tool_calls"][0]["parameters"] else: params = data # Extract booking details name = params.get("name", "Guest") date = params.get("date") time = params.get("time") title = params.get("title", "Meeting") print(f"Extracted: name={name}, date={date}, time={time}, title={title}") # Validate required fields if not date or not time: error_msg = "Missing required date or time information" print(f"{error_msg}") return JSONResponse({ "success": False, "message": error_msg }) # Create the calendar event event_result = create_calendar_event( name=name, date=date, time=time, title=title ) if event_result["success"]: # Log successful booking log_entry = { "timestamp": datetime.now().isoformat(), "name": name, "date": date, "time": time, "title": title, "event_link": event_result["event_link"] } conversation_logs.append(log_entry) print(f"Event created successfully!") return JSONResponse({ "success": True, "message": f"Perfect! I've created '{title}' for {date} at {time}. Check your Google Calendar!", "event_link": event_result["event_link"] }) else: error_msg = event_result.get("error", "Unknown error") print(f"❌ Failed to create event: {error_msg}") return JSONResponse({ "success": False, "message": f"Sorry, I couldn't create the event. Error: {error_msg}" }) except Exception as e: print(f"Webhook error: {str(e)}") import traceback traceback.print_exc() return JSONResponse({ "success": False, "message": f"An error occurred: {str(e)}" }) @app.get("/") async def root(): """Health check endpoint""" return { "status": "Voice Scheduling Agent is running!", "endpoints": { "webhook": "/webhook", "logs": "/logs", "health": "/" } } @app.get("/logs") async def get_logs(): """Get recent conversation logs""" return { "total_bookings": len(conversation_logs), "recent_logs": conversation_logs[-10:] # Last 10 bookings } @app.get("/health") async def health_check(): """Detailed health check""" return { "status": "healthy", "calendar_initialized": True, "total_bookings": len(conversation_logs), "environment": "HuggingFace Spaces" } # Create Gradio Interface def create_gradio_interface(): """Create the Gradio UI with embedded ElevenLabs widget""" # Get agent ID from environment (HuggingFace Secrets) agent_id = os.getenv("ELEVENLABS_AGENT_ID", "") # Get the Space URL dynamically space_name = os.getenv("SPACE_ID", "") if space_name: webhook_url = f"https://{space_name}.hf.space/webhook" else: webhook_url = "https://YOUR_SPACE_URL/webhook" with gr.Blocks( theme=gr.themes.Soft(primary_hue="blue", secondary_hue="purple"), title="🎙️ Voice Scheduling Agent", css=""" .gradio-container { max-width: 1200px !important; } .widget-container { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); border-radius: 20px; padding: 40px; box-shadow: 0 10px 30px rgba(0,0,0,0.2); } """ ) as demo: gr.Markdown(""" # 🎙️ Voice Scheduling Agent ### Schedule meetings using natural voice conversation with AI """) with gr.Row(): # Left column - Instructions with gr.Column(scale=1): gr.Markdown(""" ## 📝 How to Use 1. **Click the microphone** button on the right 2. **Speak naturally** - just talk to the AI! 3. **Provide details** when asked: - Your name - Meeting date - Meeting time - Meeting title (optional) 4. **Confirm** when the AI repeats your details 5. **Done!** Your event is created automatically ### 📅 Date Format Examples: - "December 25, 2024" - "25th December 2024" - "2024-12-25" ### 🕐 Time Format Examples: - "2:30 PM" - "14:30" - "two thirty in the afternoon" ### 💡 Tips: - Speak clearly and naturally - Wait for the AI to finish before responding - You can interrupt if needed - Say "yes" or "correct" to confirm details """) # Show webhook URL and status gr.Markdown(f""" --- ### 🔧 Configuration **Webhook URL:** `{webhook_url}` **Status:** {"✅ Configured" if agent_id else "⚠️ Agent ID not set"} """) # Recent bookings section gr.Markdown("### 📊 Recent Bookings") logs_display = gr.JSON(label="Latest Events", value=[]) def get_recent_logs(): """Get the 5 most recent bookings""" recent = conversation_logs[-5:] if conversation_logs else [] return recent[::-1] # Reverse to show newest first refresh_btn = gr.Button("🔄 Refresh Bookings", size="sm") refresh_btn.click(get_recent_logs, outputs=logs_display) # Right column - Voice Widget with gr.Column(scale=1): if agent_id: gr.HTML(f"""

🎤 Voice Assistant

Click the button below to start talking

🔊 Make sure your microphone is enabled

""") else: gr.Markdown(""" ### ⚠️ Configuration Required Please add your ElevenLabs Agent ID to the HuggingFace Space secrets: 1. Go to Space Settings 2. Navigate to "Repository secrets" 3. Add `ELEVENLABS_AGENT_ID` with your agent ID 4. Restart the Space **Note:** Make sure to also configure the webhook URL in your ElevenLabs agent settings. """) # Footer gr.Markdown(""" --- ### 🛠️ Tech Stack - **Voice AI:** ElevenLabs Conversational AI - **Calendar:** Google Calendar API - **Backend:** FastAPI + Python - **Frontend:** Gradio - **Deployment:** HuggingFace Spaces ### 📝 Setup Instructions **Required Secrets (Add in Space Settings):** - `ELEVENLABS_AGENT_ID` - Your ElevenLabs agent ID - `ELEVENLABS_API_KEY` - Your ElevenLabs API key (optional) - `GOOGLE_CREDENTIALS_BASE64` - Your Google service account credentials (base64 encoded) **Or upload `credentials.json` directly to the Space** --- Built for Vikara.ai Assessment | [View on GitHub](#) """) return demo # Mount Gradio to FastAPI gradio_app = create_gradio_interface() app = gr.mount_gradio_app(app, gradio_app, path="/") if __name__ == "__main__": print("=" * 60) print("Starting Voice Scheduling Agent") print("=" * 60) # Initialize calendar service print("\nInitializing Google Calendar...") if initialize_calendar(): print("Calendar service ready!") else: print("Calendar service initialization failed") print("Make sure credentials.json exists or GOOGLE_CREDENTIALS_BASE64 is set") # Show configuration print("\nConfiguration:") print(f" Agent ID: {'Set' if os.getenv('ELEVENLABS_AGENT_ID') else 'Not set'}") print(f" Google Credentials: {'Found' if os.path.exists('credentials.json') or os.getenv('GOOGLE_CREDENTIALS_BASE64') else 'Not found'}") print("\n" + "=" * 60) print("Server starting...") print(" Local: http://localhost:7860") print(" Webhook: http://localhost:7860/webhook") print("=" * 60 + "\n") # Run the server uvicorn.run(app, host="0.0.0.0", port=7860)