import os import json import gspread import google.generativeai as genai from flask import Flask, request, jsonify, render_template_string from oauth2client.service_account import ServiceAccountCredentials from vapi import Vapi from datetime import datetime # --- App Initialization --- app = Flask(__name__) # --- Configuration Loading --- def load_config(): config_json_str = os.getenv("APP_CONFIG_JSON") if not config_json_str: raise ValueError("FATAL: The 'APP_CONFIG_JSON' secret is not set in your Space settings!") return json.loads(config_json_str) # --- HTML/CSS/JS Front-End Template --- HTML_TEMPLATE = """ Voice AI Coordinator

🚀 Voice AI Coordinator

Deploy a voice agent to make calls on your behalf.

""" # --- Routes --- @app.route('/', methods=['GET']) def index(): return render_template_string(HTML_TEMPLATE) @app.route('/place-call', methods=['POST']) def place_call_handler(): try: config = load_config() data = request.get_json() name, number, reason = data['name'], data['number'], data['reason'] genai.configure(api_key=config["GEMINI_API_KEY"]) model = genai.GenerativeModel('gemini-1.5-flash') prompt_details = f"User's intent: '{reason}'. Calling: '{name if name else 'N/A'}'." gemini_prompt = f"Generate a single, polite opening sentence for a phone call. {prompt_details} If a name is provided, ask for them. Output ONLY the sentence string." response = model.generate_content(gemini_prompt) first_message = response.text.strip().replace('"', '') # --- FIX 1: Initialize the Vapi client using the `token` keyword argument. --- vapi = Vapi(token=config["VAPI_API_KEY"]) system_prompt = "You are Alex, a polite AI assistant. Your first sentence has been spoken. Now, listen and respond naturally in English or Hindi." # --- FIX 2: Create the call using the `client.calls.create` attribute (plural). --- call_response = vapi.calls.create( assistant_id=config["VAPI_ASSISTANT_ID"], phone_number_id=config["VAPI_PHONE_NUMBER_ID"], customer={"number": number}, assistant={ "serverUrl": config["WEBHOOK_SERVER_URL"], "firstMessage": first_message, "voice": {"provider": "11labs", "voiceId": "pola", "speed": 0.9}, "model": {"provider": "openai", "model": "gpt-4o", "messages": [{"role": "system", "content": system_prompt}]}, "metadata": {"raw_intent": f"{reason} (for {name})"} }) if call_response and call_response.id: return jsonify({"message": "Call deployed successfully!", "call_id": call_response.id}), 200 else: raise Exception("Vapi did not return a valid call object.") except Exception as e: print(f"[ERROR] in /place-call: {e}") return jsonify({"error": str(e)}), 500 @app.route('/webhook', methods=['POST']) def webhook_handler(): try: config = load_config() payload = request.json if payload.get('message', {}).get('type') == 'hang': call_data = payload.get('message', {}).get('call', {}) row = [ call_data.get("id", "N/A"), datetime.now().strftime("%Y-%m-%d %H:%M:%S"), call_data.get("customer", {}).get("number", "N/A"), call_data.get("metadata", {}).get("raw_intent", "N/A"), payload.get('message', {}).get('endedReason', 'N/A'), call_data.get('summary', 'No summary provided.'), "Yes" if "follow-up" in call_data.get('summary', '').lower() else "No", json.dumps(call_data.get('transcript', []), indent=2) ] creds = ServiceAccountCredentials.from_json_keyfile_name("credentials.json", ["https://spreadsheets.google.com/feeds", 'https://www.googleapis.com/auth/spreadsheets', "https://www.googleapis.com/auth/drive.file", "https://www.googleapis.com/auth/drive"]) client = gspread.authorize(creds) sheet = client.open(config["GOOGLE_SHEET_NAME"]).sheet1 sheet.append_row(row) print(f"[Log] Successfully logged call data for {call_data.get('id')}.") except Exception as e: print(f"[ERROR] in /webhook: {e}") return jsonify({'status': 'success'}), 200