Repository navigation
Expand file tree
/
Copy pathmain.py
More file actions
183 lines (149 loc) · 5.83 KB
/
Copy pathmain.py
File metadata and controls
183 lines (149 loc) · 5.83 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
from fastapi import FastAPI, HTTPException, Request, Form
from fastapi.responses import Response
from twilio.twiml.voice_response import VoiceResponse
from twilio.rest import Client
import uvicorn
import os
from datetime import datetime
import logging
from dotenv import load_dotenv
# Load environment variables
load_dotenv()
from database import Database
from speech_services import SpeechServices
from ai_agent import SwiftLogisticsAgent
from config import Config
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
app = FastAPI(title="SwiftLogistics Voice AI", version="1.0.0")
# Initialize services
config = Config()
db = Database(config.database_url)
speech_services = SpeechServices(config.google_credentials_path)
ai_agent = SwiftLogisticsAgent(db)
twilio_client = Client(config.twilio_account_sid, config.twilio_auth_token)
# Store active calls
active_calls = {}
#Addd layers of security to the code
@app.post("/webhook/voice")
async def handle_voice_call(request: Request):
"""Handle incoming voice calls from Twilio"""
form = await request.form()
call_sid = form.get("CallSid")
from_number = form.get("From")
logger.info(f"Incoming call from {from_number}, SID: {call_sid}")
response = VoiceResponse()
# Start recording and gather speech
response.say("Hello! Welcome to AeroLogistics. I'm your AeroLogistics AI agent. How can I help you today?")
response.record(
action=f"/webhook/process-speech/{call_sid}",
method="POST",
max_length=30,
finish_on_key="#",
play_beep=True
)
# Initialize call session
active_calls[call_sid] = {
"from_number": from_number,
"conversation_state": "greeting",
"context": {}
}
return Response(content=str(response), media_type="application/xml")
@app.post("/webhook/process-speech/{call_sid}")
async def process_speech(call_sid: str, request: Request):
"""Process recorded speech and generate AI response"""
form = await request.form()
recording_url = form.get("RecordingUrl")
if call_sid not in active_calls:
logger.error(f"Call SID {call_sid} not found in active calls")
return Response(content="<Response></Response>", media_type="application/xml")
try:
# Convert speech to text
user_text = await speech_services.speech_to_text(recording_url)
logger.info(f"User said: {user_text}")
# Process with AI agent
call_context = active_calls[call_sid]
ai_response = await ai_agent.process_message(
user_text,
call_context["conversation_state"],
call_context["context"]
)
# Update call context
active_calls[call_sid]["conversation_state"] = ai_response["next_state"]
active_calls[call_sid]["context"].update(ai_response["context"])
# Convert response to speech
audio_url = await speech_services.text_to_speech(ai_response["message"])
# Create TwiML response
response = VoiceResponse()
response.play(audio_url)
# Continue conversation if needed
if ai_response["continue_conversation"]:
response.record(
action=f"/webhook/process-speech/{call_sid}",
method="POST",
max_length=30,
finish_on_key="#",
play_beep=True
)
else:
response.say("Thank you for calling AeroLogistics. Have a great day!")
response.hangup()
# Clean up call session
if call_sid in active_calls:
del active_calls[call_sid]
return Response(content=str(response), media_type="application/xml")
except Exception as e:
logger.error(f"Error processing speech: {str(e)}")
response = VoiceResponse()
response.say("Sorry, I didn't get that. Can you please speak again?")
response.record(
action=f"/webhook/process-speech/{call_sid}",
method="POST",
max_length=30,
finish_on_key="#",
play_beep=True
)
return Response(content=str(response), media_type="application/xml")
@app.post("/webhook/transfer-human/{call_sid}")
async def transfer_to_human(call_sid: str):
"""Transfer call to human agent"""
response = VoiceResponse()
response.say("Please hold while I transfer you to a human agent.")
response.dial(config.human_agent_number)
# Clean up call session
if call_sid in active_calls:
del active_calls[call_sid]
return Response(content=str(response), media_type="application/xml")
@app.get("/audio/{filename}")
async def serve_audio_file(filename: str):
"""Serve temporary audio files"""
from fastapi.responses import FileResponse
import os
# Use the static/audio directory
audio_dir = os.path.join(os.getcwd(), 'static', 'audio')
file_path = os.path.join(audio_dir, filename)
if os.path.exists(file_path):
return FileResponse(file_path, media_type="audio/mpeg")
else:
raise HTTPException(status_code=404, detail="Audio file not found")
@app.get("/health")
async def health_check():
"""Health check endpoint"""
return {"status": "healthy", "timestamp": datetime.now().isoformat()}
@app.on_event("startup")
async def startup_event():
"""Initialize database and services on startup"""
await db.initialize()
logger.info("SwiftLogistics Voice AI started successfully")
@app.on_event("shutdown")
async def shutdown_event():
"""Clean up resources on shutdown"""
await db.close()
logger.info("SwiftLogistics Voice AI shut down")
if __name__ == "__main__":
uvicorn.run(
"main:app",
host="127.0.0.1",
port=int(os.getenv("PORT", 8000)),
reload=True
)