rairo commited on
Commit
0911208
·
verified ·
1 Parent(s): ff38912

Update main.py

Browse files
Files changed (1) hide show
  1. main.py +170 -151
main.py CHANGED
@@ -952,6 +952,13 @@ def delete_project(project_id):
952
  # AI phone call ElevenLabs
953
  #-------------------------
954
 
 
 
 
 
 
 
 
955
  CREDITS_PER_MIN = 3 # Adjust based on ElevenLabs conversational AI pricing
956
  VOICE_NAME = "Daniel" # British male voice (more reliable than Archer)
957
 
@@ -968,7 +975,8 @@ KNOWN_VOICE_IDS = {
968
  class ConversationalAIHandler:
969
  def __init__(self):
970
  self.client = ElevenLabs(api_key=os.getenv("ELEVENLABS_API_KEY"))
971
- self.base_url = "https://api.elevenlabs.io/v1/convai" # Fixed: Use convai endpoint
 
972
  self.headers = {
973
  "xi-api-key": os.getenv("ELEVENLABS_API_KEY"),
974
  "Content-Type": "application/json"
@@ -983,62 +991,8 @@ class ConversationalAIHandler:
983
  logger.info(f"[VOICE] Using known voice ID for Daniel: {KNOWN_VOICE_IDS['Daniel']}")
984
  return KNOWN_VOICE_IDS["Daniel"]
985
 
986
- # Fallback: Try to get voices from API (if above fails)
987
- try:
988
- logger.info(f"[VOICE] Fallback: Fetching voices from API")
989
- response = self.client.voices.get_all()
990
- voices = response.voices if hasattr(response, 'voices') else response
991
- logger.info(f"[VOICE] Retrieved {len(voices)} voices from API")
992
-
993
- # Preferred British male voices in order
994
- preferred_voices = ["Daniel", "Charlie", "George", "Archer"]
995
-
996
- for voice_name in preferred_voices:
997
- voice = next((v for v in voices if v.name == voice_name), None)
998
- if voice:
999
- logger.info(f"[VOICE] Found preferred voice: {voice_name} - {voice.voice_id}")
1000
- return voice.voice_id
1001
-
1002
- # Fallback to first available voice
1003
- if voices:
1004
- logger.info(f"[VOICE] Using fallback voice: {voices[0].name} - {voices[0].voice_id}")
1005
- return voices[0].voice_id
1006
-
1007
- except Exception as ae:
1008
- logger.warning(f"[VOICE] SDK method failed, trying alternative: {str(ae)}")
1009
-
1010
- # Alternative: Try direct API call
1011
- try:
1012
- response = requests.get(
1013
- "https://api.elevenlabs.io/v1/voices",
1014
- headers={"xi-api-key": os.getenv("ELEVENLABS_API_KEY")}
1015
- )
1016
- if response.status_code == 200:
1017
- voices_data = response.json()
1018
- voices = voices_data.get('voices', [])
1019
- logger.info(f"[VOICE] Retrieved {len(voices)} voices via direct API")
1020
-
1021
- # Look for Daniel first
1022
- for voice in voices:
1023
- if voice.get('name') == 'Daniel':
1024
- logger.info(f"[VOICE] Found Daniel via API: {voice.get('voice_id')}")
1025
- return voice.get('voice_id')
1026
-
1027
- # Fallback to first voice
1028
- if voices:
1029
- logger.info(f"[VOICE] Using first available voice: {voices[0].get('voice_id')}")
1030
- return voices[0].get('voice_id')
1031
-
1032
- except Exception as api_e:
1033
- logger.error(f"[VOICE] Direct API call failed: {str(api_e)}")
1034
-
1035
- # Final fallback: Use known IDs
1036
- logger.warning(f"[VOICE] All API methods failed, using known Daniel ID")
1037
- return KNOWN_VOICE_IDS["Daniel"]
1038
-
1039
  except Exception as e:
1040
  logger.error(f"[VOICE] Error getting voice: {str(e)}")
1041
- logger.error(f"[VOICE] Exception type: {type(e).__name__}")
1042
  logger.error(f"[VOICE] Using fallback Daniel voice ID")
1043
  return KNOWN_VOICE_IDS["Daniel"]
1044
 
@@ -1066,7 +1020,7 @@ class ConversationalAIHandler:
1066
  Ask clarifying questions when needed and share relevant tips from your experience.
1067
  """
1068
 
1069
- # Fixed: Correct ElevenLabs Conversational AI agent structure
1070
  agent_data = {
1071
  "name": f"DIY Expert - {project_data.get('projectTitle', project_data.get('title', 'Project'))}",
1072
  "conversation_config": {
@@ -1097,7 +1051,6 @@ class ConversationalAIHandler:
1097
  logger.info(f"[AGENT] Agent data prepared, checking for existing agent")
1098
 
1099
  # Check if agent already exists for this project
1100
- # Import your database reference
1101
  existing_agent_id = db_ref.child(f'projects/{project_id}/agent_id').get()
1102
 
1103
  if existing_agent_id:
@@ -1106,7 +1059,8 @@ class ConversationalAIHandler:
1106
  try:
1107
  response = requests.get(
1108
  f"{self.base_url}/agents/{existing_agent_id}",
1109
- headers=self.headers
 
1110
  )
1111
  if response.status_code == 200:
1112
  logger.info(f"[AGENT] Existing agent verified and active")
@@ -1116,50 +1070,84 @@ class ConversationalAIHandler:
1116
  except Exception as e:
1117
  logger.warning(f"[AGENT] Error verifying existing agent: {str(e)}, creating new one")
1118
 
1119
- # Create new agent
1120
  logger.info(f"[AGENT] Creating new agent")
1121
- logger.info(f"[AGENT] Request URL: {self.base_url}/agents")
1122
- logger.info(f"[AGENT] Request headers: {self.headers}")
1123
- logger.info(f"[AGENT] Request payload keys: {list(agent_data.keys())}")
1124
 
1125
- response = requests.post(
1126
- f"{self.base_url}/agents",
1127
- headers=self.headers,
1128
- json=agent_data,
1129
- timeout=30
1130
- )
1131
-
1132
- logger.info(f"[AGENT] Agent creation response status: {response.status_code}")
1133
- logger.info(f"[AGENT] Agent creation response headers: {dict(response.headers)}")
1134
-
1135
- if response.status_code == 201:
1136
- agent_info = response.json()
1137
- agent_id = agent_info.get("agent_id")
1138
- logger.info(f"[AGENT] New agent created successfully: {agent_id}")
1139
 
1140
- # Store agent ID in database
1141
- try:
 
 
 
 
 
 
 
 
 
 
 
 
 
1142
  db_ref.child(f'projects/{project_id}').update({
1143
  'agent_id': agent_id,
1144
  'agent_created_at': int(time.time())
1145
  })
1146
  logger.info(f"[AGENT] Agent ID stored in database")
1147
- except Exception as e:
1148
- logger.error(f"[AGENT] Failed to store agent ID in database: {str(e)}")
 
 
 
 
 
 
 
 
1149
 
1150
- return agent_id
1151
- else:
1152
- logger.error(f"[AGENT] Failed to create agent: Status {response.status_code}")
1153
- logger.error(f"[AGENT] Response text: {response.text}")
 
 
1154
 
1155
- # Try to parse error response
1156
- try:
1157
- error_data = response.json()
1158
- logger.error(f"[AGENT] Error details: {error_data}")
1159
- except:
1160
- logger.error(f"[AGENT] Could not parse error response as JSON")
1161
 
1162
- raise Exception(f"Failed to create agent: HTTP {response.status_code} - {response.text}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1163
 
1164
  except Exception as e:
1165
  logger.error(f"[AGENT] Error creating/getting agent: {str(e)}")
@@ -1171,25 +1159,39 @@ class ConversationalAIHandler:
1171
  try:
1172
  logger.info(f"[CONVERSATION] Starting conversation with agent: {agent_id}")
1173
 
1174
- # Start conversation
1175
- response = requests.post(
1176
- f"{self.base_url}/agents/{agent_id}/conversations",
1177
- headers=self.headers,
1178
- json={},
1179
- timeout=30
1180
- )
1181
 
1182
- logger.info(f"[CONVERSATION] Start conversation response status: {response.status_code}")
1183
-
1184
- if response.status_code == 201:
1185
- conversation_info = response.json()
1186
- conversation_id = conversation_info.get("conversation_id")
1187
- logger.info(f"[CONVERSATION] Conversation started successfully: {conversation_id}")
1188
- return conversation_id
1189
- else:
1190
- logger.error(f"[CONVERSATION] Failed to start conversation: Status {response.status_code}")
1191
- logger.error(f"[CONVERSATION] Response: {response.text}")
1192
- raise Exception(f"Failed to start conversation: {response.text}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1193
 
1194
  except Exception as e:
1195
  logger.error(f"[CONVERSATION] Error starting conversation: {str(e)}")
@@ -1202,26 +1204,41 @@ class ConversationalAIHandler:
1202
  logger.info(f"[MESSAGE] Sending message to agent: {agent_id}, conversation: {conversation_id}")
1203
  logger.info(f"[MESSAGE] Message: {message[:100]}{'...' if len(message) > 100 else ''}")
1204
 
1205
- response = requests.post(
1206
- f"{self.base_url}/agents/{agent_id}/conversations/{conversation_id}/messages",
1207
- headers=self.headers,
1208
- json={
1209
- "message": message,
1210
- "message_type": "text"
1211
- },
1212
- timeout=30
1213
- )
1214
-
1215
- logger.info(f"[MESSAGE] Send message response status: {response.status_code}")
1216
 
1217
- if response.status_code == 200:
1218
- message_response = response.json()
1219
- logger.info(f"[MESSAGE] Message sent successfully")
1220
- return message_response
1221
- else:
1222
- logger.error(f"[MESSAGE] Failed to send message: Status {response.status_code}")
1223
- logger.error(f"[MESSAGE] Response: {response.text}")
1224
- raise Exception(f"Failed to send message: {response.text}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1225
 
1226
  except Exception as e:
1227
  logger.error(f"[MESSAGE] Error sending message: {str(e)}")
@@ -1233,7 +1250,7 @@ def calculate_cost(duration_seconds):
1233
  minutes = (duration_seconds + 59) // 60
1234
  return minutes * CREDITS_PER_MIN
1235
 
1236
- # Updated Flask route
1237
  @app.route('/api/projects/<project_id>/start-conversation', methods=['POST'])
1238
  def start_conversation(project_id):
1239
  """Start a conversational AI session for a project"""
@@ -1329,6 +1346,29 @@ def start_conversation(project_id):
1329
  logger.error(f"[CONVERSATION] Exception type: {type(e).__name__}")
1330
  return jsonify({'error': 'Failed to start conversation'}), 500
1331
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1332
  @app.route('/api/projects/<project_id>/continue-conversation', methods=['POST'])
1333
  def continue_conversation(project_id):
1334
  """Continue an existing conversation"""
@@ -1356,28 +1396,7 @@ def continue_conversation(project_id):
1356
  logger.error(f"Error continuing conversation: {str(e)}")
1357
  return jsonify({'error': 'Failed to continue conversation'}), 500
1358
 
1359
- @app.route('/api/webhook/agent/<project_id>', methods=['POST'])
1360
- def agent_webhook(project_id):
1361
- """Webhook to handle agent events"""
1362
- try:
1363
- data = request.get_json()
1364
- event_type = data.get('event_type')
1365
-
1366
- # Log the webhook event
1367
- logger.info(f"Agent webhook for project {project_id}: {event_type}")
1368
-
1369
- # Store webhook data for debugging/monitoring
1370
- db_ref.child(f'webhooks/{project_id}/{int(time.time())}').set({
1371
- 'event_type': event_type,
1372
- 'data': data,
1373
- 'timestamp': int(time.time())
1374
- })
1375
-
1376
- return jsonify({'status': 'received'}), 200
1377
-
1378
- except Exception as e:
1379
- logger.error(f"Webhook error: {str(e)}")
1380
- return jsonify({'error': 'Webhook processing failed'}), 500
1381
  # -----------------------------------------------------------------------------
1382
  # 7. MAIN EXECUTION
1383
  # -----------------------------------------------------------------------------
 
952
  # AI phone call ElevenLabs
953
  #-------------------------
954
 
955
+ import os
956
+ import requests
957
+ import time
958
+ import logging
959
+ from elevenlabs import ElevenLabs
960
+
961
+ # Configuration
962
  CREDITS_PER_MIN = 3 # Adjust based on ElevenLabs conversational AI pricing
963
  VOICE_NAME = "Daniel" # British male voice (more reliable than Archer)
964
 
 
975
  class ConversationalAIHandler:
976
  def __init__(self):
977
  self.client = ElevenLabs(api_key=os.getenv("ELEVENLABS_API_KEY"))
978
+ # Fixed: Try the correct endpoint structure
979
+ self.base_url = "https://api.elevenlabs.io/v1/convai"
980
  self.headers = {
981
  "xi-api-key": os.getenv("ELEVENLABS_API_KEY"),
982
  "Content-Type": "application/json"
 
991
  logger.info(f"[VOICE] Using known voice ID for Daniel: {KNOWN_VOICE_IDS['Daniel']}")
992
  return KNOWN_VOICE_IDS["Daniel"]
993
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
994
  except Exception as e:
995
  logger.error(f"[VOICE] Error getting voice: {str(e)}")
 
996
  logger.error(f"[VOICE] Using fallback Daniel voice ID")
997
  return KNOWN_VOICE_IDS["Daniel"]
998
 
 
1020
  Ask clarifying questions when needed and share relevant tips from your experience.
1021
  """
1022
 
1023
+ # Fixed: Updated agent structure to match ElevenLabs 2025 API
1024
  agent_data = {
1025
  "name": f"DIY Expert - {project_data.get('projectTitle', project_data.get('title', 'Project'))}",
1026
  "conversation_config": {
 
1051
  logger.info(f"[AGENT] Agent data prepared, checking for existing agent")
1052
 
1053
  # Check if agent already exists for this project
 
1054
  existing_agent_id = db_ref.child(f'projects/{project_id}/agent_id').get()
1055
 
1056
  if existing_agent_id:
 
1059
  try:
1060
  response = requests.get(
1061
  f"{self.base_url}/agents/{existing_agent_id}",
1062
+ headers=self.headers,
1063
+ timeout=10
1064
  )
1065
  if response.status_code == 200:
1066
  logger.info(f"[AGENT] Existing agent verified and active")
 
1070
  except Exception as e:
1071
  logger.warning(f"[AGENT] Error verifying existing agent: {str(e)}, creating new one")
1072
 
1073
+ # Create new agent - Fixed: Try correct endpoints in order
1074
  logger.info(f"[AGENT] Creating new agent")
 
 
 
1075
 
1076
+ # Endpoint 1: Try the newer conversational-ai endpoint first
1077
+ try:
1078
+ endpoint = "https://api.elevenlabs.io/v1/conversational-ai/agents"
1079
+ logger.info(f"[AGENT] Trying primary endpoint: {endpoint}")
 
 
 
 
 
 
 
 
 
 
1080
 
1081
+ response = requests.post(
1082
+ endpoint,
1083
+ headers=self.headers,
1084
+ json=agent_data,
1085
+ timeout=30
1086
+ )
1087
+
1088
+ logger.info(f"[AGENT] Primary endpoint response status: {response.status_code}")
1089
+
1090
+ if response.status_code == 201:
1091
+ agent_info = response.json()
1092
+ agent_id = agent_info.get("agent_id")
1093
+ logger.info(f"[AGENT] New agent created successfully with primary endpoint: {agent_id}")
1094
+
1095
+ # Store agent ID in database
1096
  db_ref.child(f'projects/{project_id}').update({
1097
  'agent_id': agent_id,
1098
  'agent_created_at': int(time.time())
1099
  })
1100
  logger.info(f"[AGENT] Agent ID stored in database")
1101
+
1102
+ return agent_id
1103
+
1104
+ except Exception as e:
1105
+ logger.warning(f"[AGENT] Primary endpoint failed: {str(e)}")
1106
+
1107
+ # Endpoint 2: Try the original convai endpoint
1108
+ try:
1109
+ endpoint = f"{self.base_url}/agents"
1110
+ logger.info(f"[AGENT] Trying fallback endpoint: {endpoint}")
1111
 
1112
+ response = requests.post(
1113
+ endpoint,
1114
+ headers=self.headers,
1115
+ json=agent_data,
1116
+ timeout=30
1117
+ )
1118
 
1119
+ logger.info(f"[AGENT] Fallback endpoint response status: {response.status_code}")
1120
+ logger.info(f"[AGENT] Response headers: {dict(response.headers)}")
 
 
 
 
1121
 
1122
+ if response.status_code == 201:
1123
+ agent_info = response.json()
1124
+ agent_id = agent_info.get("agent_id")
1125
+ logger.info(f"[AGENT] New agent created successfully with fallback endpoint: {agent_id}")
1126
+
1127
+ # Store agent ID in database
1128
+ db_ref.child(f'projects/{project_id}').update({
1129
+ 'agent_id': agent_id,
1130
+ 'agent_created_at': int(time.time())
1131
+ })
1132
+ logger.info(f"[AGENT] Agent ID stored in database")
1133
+
1134
+ return agent_id
1135
+ else:
1136
+ logger.error(f"[AGENT] Failed to create agent: Status {response.status_code}")
1137
+ logger.error(f"[AGENT] Response text: {response.text}")
1138
+
1139
+ # Try to parse error response
1140
+ try:
1141
+ error_data = response.json()
1142
+ logger.error(f"[AGENT] Error details: {error_data}")
1143
+ except:
1144
+ logger.error(f"[AGENT] Could not parse error response as JSON")
1145
+
1146
+ raise Exception(f"Failed to create agent: HTTP {response.status_code} - {response.text}")
1147
+
1148
+ except Exception as e:
1149
+ logger.error(f"[AGENT] All endpoints failed: {str(e)}")
1150
+ raise
1151
 
1152
  except Exception as e:
1153
  logger.error(f"[AGENT] Error creating/getting agent: {str(e)}")
 
1159
  try:
1160
  logger.info(f"[CONVERSATION] Starting conversation with agent: {agent_id}")
1161
 
1162
+ # Fixed: Try the correct conversation endpoints
1163
+ endpoints = [
1164
+ f"https://api.elevenlabs.io/v1/conversational-ai/agents/{agent_id}/conversations",
1165
+ f"{self.base_url}/agents/{agent_id}/conversations"
1166
+ ]
 
 
1167
 
1168
+ for endpoint in endpoints:
1169
+ try:
1170
+ logger.info(f"[CONVERSATION] Trying endpoint: {endpoint}")
1171
+
1172
+ response = requests.post(
1173
+ endpoint,
1174
+ headers=self.headers,
1175
+ json={},
1176
+ timeout=30
1177
+ )
1178
+
1179
+ logger.info(f"[CONVERSATION] Response status: {response.status_code}")
1180
+
1181
+ if response.status_code == 201:
1182
+ conversation_info = response.json()
1183
+ conversation_id = conversation_info.get("conversation_id")
1184
+ logger.info(f"[CONVERSATION] Conversation started successfully: {conversation_id}")
1185
+ return conversation_id
1186
+ else:
1187
+ logger.warning(f"[CONVERSATION] Endpoint failed with status {response.status_code}: {response.text}")
1188
+ if endpoint == endpoints[-1]: # Last endpoint
1189
+ raise Exception(f"Failed to start conversation: {response.text}")
1190
+
1191
+ except requests.exceptions.RequestException as req_e:
1192
+ logger.warning(f"[CONVERSATION] Request failed for {endpoint}: {str(req_e)}")
1193
+ if endpoint == endpoints[-1]: # Last endpoint
1194
+ raise Exception(f"All conversation endpoints failed: {str(req_e)}")
1195
 
1196
  except Exception as e:
1197
  logger.error(f"[CONVERSATION] Error starting conversation: {str(e)}")
 
1204
  logger.info(f"[MESSAGE] Sending message to agent: {agent_id}, conversation: {conversation_id}")
1205
  logger.info(f"[MESSAGE] Message: {message[:100]}{'...' if len(message) > 100 else ''}")
1206
 
1207
+ # Fixed: Try the correct message endpoints
1208
+ endpoints = [
1209
+ f"https://api.elevenlabs.io/v1/conversational-ai/agents/{agent_id}/conversations/{conversation_id}/messages",
1210
+ f"{self.base_url}/agents/{agent_id}/conversations/{conversation_id}/messages"
1211
+ ]
 
 
 
 
 
 
1212
 
1213
+ for endpoint in endpoints:
1214
+ try:
1215
+ logger.info(f"[MESSAGE] Trying endpoint: {endpoint}")
1216
+
1217
+ response = requests.post(
1218
+ endpoint,
1219
+ headers=self.headers,
1220
+ json={
1221
+ "message": message,
1222
+ "message_type": "text"
1223
+ },
1224
+ timeout=30
1225
+ )
1226
+
1227
+ logger.info(f"[MESSAGE] Response status: {response.status_code}")
1228
+
1229
+ if response.status_code == 200:
1230
+ message_response = response.json()
1231
+ logger.info(f"[MESSAGE] Message sent successfully")
1232
+ return message_response
1233
+ else:
1234
+ logger.warning(f"[MESSAGE] Endpoint failed with status {response.status_code}: {response.text}")
1235
+ if endpoint == endpoints[-1]: # Last endpoint
1236
+ raise Exception(f"Failed to send message: {response.text}")
1237
+
1238
+ except requests.exceptions.RequestException as req_e:
1239
+ logger.warning(f"[MESSAGE] Request failed for {endpoint}: {str(req_e)}")
1240
+ if endpoint == endpoints[-1]: # Last endpoint
1241
+ raise Exception(f"All message endpoints failed: {str(req_e)}")
1242
 
1243
  except Exception as e:
1244
  logger.error(f"[MESSAGE] Error sending message: {str(e)}")
 
1250
  minutes = (duration_seconds + 59) // 60
1251
  return minutes * CREDITS_PER_MIN
1252
 
1253
+ # Your existing Flask route - no changes needed
1254
  @app.route('/api/projects/<project_id>/start-conversation', methods=['POST'])
1255
  def start_conversation(project_id):
1256
  """Start a conversational AI session for a project"""
 
1346
  logger.error(f"[CONVERSATION] Exception type: {type(e).__name__}")
1347
  return jsonify({'error': 'Failed to start conversation'}), 500
1348
 
1349
+ @app.route('/api/webhook/agent/<project_id>', methods=['POST'])
1350
+ def agent_webhook(project_id):
1351
+ """Webhook to handle agent events"""
1352
+ try:
1353
+ data = request.get_json()
1354
+ event_type = data.get('event_type')
1355
+
1356
+ # Log the webhook event
1357
+ logger.info(f"Agent webhook for project {project_id}: {event_type}")
1358
+
1359
+ # Store webhook data for debugging/monitoring
1360
+ db_ref.child(f'webhooks/{project_id}/{int(time.time())}').set({
1361
+ 'event_type': event_type,
1362
+ 'data': data,
1363
+ 'timestamp': int(time.time())
1364
+ })
1365
+
1366
+ return jsonify({'status': 'received'}), 200
1367
+
1368
+ except Exception as e:
1369
+ logger.error(f"Webhook error: {str(e)}")
1370
+ return jsonify({'error': 'Webhook processing failed'}), 500
1371
+
1372
  @app.route('/api/projects/<project_id>/continue-conversation', methods=['POST'])
1373
  def continue_conversation(project_id):
1374
  """Continue an existing conversation"""
 
1396
  logger.error(f"Error continuing conversation: {str(e)}")
1397
  return jsonify({'error': 'Failed to continue conversation'}), 500
1398
 
1399
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1400
  # -----------------------------------------------------------------------------
1401
  # 7. MAIN EXECUTION
1402
  # -----------------------------------------------------------------------------