rairo commited on
Commit
45f8ce2
·
verified ·
1 Parent(s): d160019

Update main.py

Browse files
Files changed (1) hide show
  1. main.py +134 -33
main.py CHANGED
@@ -947,10 +947,24 @@ def delete_project(project_id):
947
  blob.delete()
948
  return jsonify({"success": True, "message": f"Project {project_id} deleted."})
949
 
950
- # AI call ElevenLabs
 
 
 
 
951
  CREDITS_PER_MIN = 3 # Adjust based on ElevenLabs conversational AI pricing
952
  VOICE_NAME = "Daniel" # British male voice (more reliable than Archer)
953
 
 
 
 
 
 
 
 
 
 
 
954
  class ConversationalAIHandler:
955
  def __init__(self):
956
  self.client = ElevenLabs(api_key=os.getenv("ELEVENLABS_API_KEY"))
@@ -963,44 +977,98 @@ class ConversationalAIHandler:
963
  def get_british_male_voice_id(self):
964
  """Get British male voice ID"""
965
  try:
966
- voices = self.client.voices.list().voices
967
- # Preferred British male voices in order
968
- preferred_voices = ["Daniel", "Charlie", "Archer", "George"]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
969
 
970
- for voice_name in preferred_voices:
971
- voice = next((v for v in voices if v.name == voice_name), None)
972
- if voice:
973
- return voice.voice_id
974
 
975
- # Fallback to first available voice
976
- return voices[0].voice_id if voices else None
977
  except Exception as e:
978
- logger.error(f"Error getting voice: {str(e)}")
979
- return None
 
 
980
 
981
  def create_or_get_agent(self, project_id, project_data):
982
  """Create or retrieve existing agent for the project"""
983
  try:
 
 
984
  voice_id = self.get_british_male_voice_id()
985
  if not voice_id:
986
- raise Exception("No suitable voice available")
 
 
 
987
 
988
  # DIY Expert persona context
989
  diy_expert_prompt = f"""
990
  You are an experienced DIY expert with years of hands-on experience in Home Appliance Repair, Automotive Maintenance, Gardening & Urban Farming,
991
- Upcycling & Sustainable Crafts, or DIY Project Creation.. You speak in a friendly, knowledgeable British manner and provide
992
- practical, actionable advice. You're working on this specific project:
993
-
994
- Project: {project_data.get('title', 'DIY Project')}
995
  Description: {project_data.get('projectDescription', '')}
996
  Initial Plan: {project_data.get('initialPlan', '')}
997
-
998
  Provide helpful, step-by-step guidance while being encouraging and safety-conscious.
999
  Ask clarifying questions when needed and share relevant tips from your experience.
1000
  """
1001
 
1002
  agent_data = {
1003
- "name": f"DIY Expert - {project_data.get('title', 'Project')}",
1004
  "prompt": diy_expert_prompt,
1005
  "voice_id": voice_id,
1006
  "language": "en",
@@ -1016,46 +1084,69 @@ class ConversationalAIHandler:
1016
  "max_duration_seconds": 1800 # 30 minutes max
1017
  }
1018
 
 
 
1019
  # Check if agent already exists for this project
1020
  existing_agent_id = db_ref.child(f'projects/{project_id}/agent_id').get()
1021
 
1022
  if existing_agent_id:
 
1023
  # Verify agent still exists
1024
- response = requests.get(
1025
- f"{self.base_url}/agents/{existing_agent_id}",
1026
- headers=self.headers
1027
- )
1028
- if response.status_code == 200:
1029
- return existing_agent_id
 
 
 
 
 
 
1030
 
1031
  # Create new agent
 
1032
  response = requests.post(
1033
  f"{self.base_url}/agents",
1034
  headers=self.headers,
1035
  json=agent_data
1036
  )
1037
 
 
 
1038
  if response.status_code == 201:
1039
  agent_info = response.json()
1040
  agent_id = agent_info["id"]
 
1041
 
1042
  # Store agent ID in database
1043
- db_ref.child(f'projects/{project_id}').update({
1044
- 'agent_id': agent_id,
1045
- 'agent_created_at': int(time.time())
1046
- })
 
 
 
 
1047
 
1048
  return agent_id
1049
  else:
 
 
1050
  raise Exception(f"Failed to create agent: {response.text}")
1051
 
1052
  except Exception as e:
1053
- logger.error(f"Error creating/getting agent: {str(e)}")
 
1054
  raise
1055
 
1056
  def simulate_conversation(self, agent_id, user_message):
1057
  """Simulate a conversation with the agent"""
1058
  try:
 
 
 
1059
  simulation_data = {
1060
  "simulation_specification": {
1061
  "user_message": user_message,
@@ -1064,19 +1155,29 @@ class ConversationalAIHandler:
1064
  }
1065
  }
1066
 
 
1067
  response = requests.post(
1068
  f"{self.base_url}/agents/{agent_id}/simulate-conversation",
1069
  headers=self.headers,
1070
- json=simulation_data
 
1071
  )
1072
 
 
 
1073
  if response.status_code == 200:
1074
- return response.json()
 
 
 
1075
  else:
 
 
1076
  raise Exception(f"Simulation failed: {response.text}")
1077
 
1078
  except Exception as e:
1079
- logger.error(f"Error simulating conversation: {str(e)}")
 
1080
  raise
1081
 
1082
  def calculate_cost(duration_seconds):
 
947
  blob.delete()
948
  return jsonify({"success": True, "message": f"Project {project_id} deleted."})
949
 
950
+
951
+ #------------------------
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
 
958
+ # Known ElevenLabs voice IDs for common voices
959
+ KNOWN_VOICE_IDS = {
960
+ "Daniel": "onwK4e9ZLuTAKqWW03F9", # British male
961
+ "Charlie": "IKne3meq5aSn9XLyUdCD", # British male
962
+ "George": "JBFqnCBsd6RMkjVDRZzb", # British male
963
+ "Archer": "D38z5RcWu1voky8WS1ja", # British male
964
+ "Adam": "pNInz6obpgDQGcFmaJgB", # American male (fallback)
965
+ "Antoni": "ErXwobaYiN019PkySvjV", # American male (fallback)
966
+ }
967
+
968
  class ConversationalAIHandler:
969
  def __init__(self):
970
  self.client = ElevenLabs(api_key=os.getenv("ELEVENLABS_API_KEY"))
 
977
  def get_british_male_voice_id(self):
978
  """Get British male voice ID"""
979
  try:
980
+ logger.info(f"[VOICE] Attempting to get voices using SDK method")
981
+
982
+ # First try with known voice IDs (fastest approach)
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
+ # Note: Using the correct method based on current SDK
988
+ try:
989
+ logger.info(f"[VOICE] Fallback: Fetching voices from API")
990
+ response = self.client.voices.get_all() # Try get_all method
991
+ voices = response.voices if hasattr(response, 'voices') else response
992
+ logger.info(f"[VOICE] Retrieved {len(voices)} voices from API")
993
+
994
+ # Preferred British male voices in order
995
+ preferred_voices = ["Daniel", "Charlie", "George", "Archer"]
996
+
997
+ for voice_name in preferred_voices:
998
+ voice = next((v for v in voices if v.name == voice_name), None)
999
+ if voice:
1000
+ logger.info(f"[VOICE] Found preferred voice: {voice_name} - {voice.voice_id}")
1001
+ return voice.voice_id
1002
+
1003
+ # Fallback to first available voice
1004
+ if voices:
1005
+ logger.info(f"[VOICE] Using fallback voice: {voices[0].name} - {voices[0].voice_id}")
1006
+ return voices[0].voice_id
1007
+
1008
+ except AttributeError as ae:
1009
+ logger.warning(f"[VOICE] SDK method failed, trying alternative: {str(ae)}")
1010
+
1011
+ # Alternative: Try direct API call
1012
+ try:
1013
+ response = requests.get(
1014
+ f"{self.base_url}/voices",
1015
+ headers={"xi-api-key": os.getenv("ELEVENLABS_API_KEY")}
1016
+ )
1017
+ if response.status_code == 200:
1018
+ voices_data = response.json()
1019
+ voices = voices_data.get('voices', [])
1020
+ logger.info(f"[VOICE] Retrieved {len(voices)} voices via direct API")
1021
+
1022
+ # Look for Daniel first
1023
+ for voice in voices:
1024
+ if voice.get('name') == 'Daniel':
1025
+ logger.info(f"[VOICE] Found Daniel via API: {voice.get('voice_id')}")
1026
+ return voice.get('voice_id')
1027
+
1028
+ # Fallback to first voice
1029
+ if voices:
1030
+ logger.info(f"[VOICE] Using first available voice: {voices[0].get('voice_id')}")
1031
+ return voices[0].get('voice_id')
1032
+
1033
+ except Exception as api_e:
1034
+ logger.error(f"[VOICE] Direct API call failed: {str(api_e)}")
1035
 
1036
+ # Final fallback: Use known IDs
1037
+ logger.warning(f"[VOICE] All API methods failed, using known Daniel ID")
1038
+ return KNOWN_VOICE_IDS["Daniel"]
 
1039
 
 
 
1040
  except Exception as e:
1041
+ logger.error(f"[VOICE] Error getting voice: {str(e)}")
1042
+ logger.error(f"[VOICE] Exception type: {type(e).__name__}")
1043
+ logger.error(f"[VOICE] Using fallback Daniel voice ID")
1044
+ return KNOWN_VOICE_IDS["Daniel"]
1045
 
1046
  def create_or_get_agent(self, project_id, project_data):
1047
  """Create or retrieve existing agent for the project"""
1048
  try:
1049
+ logger.info(f"[AGENT] Starting agent creation/retrieval for project: {project_id}")
1050
+
1051
  voice_id = self.get_british_male_voice_id()
1052
  if not voice_id:
1053
+ logger.error(f"[AGENT] No voice ID available, using Daniel fallback")
1054
+ voice_id = KNOWN_VOICE_IDS["Daniel"]
1055
+
1056
+ logger.info(f"[AGENT] Using voice ID: {voice_id}")
1057
 
1058
  # DIY Expert persona context
1059
  diy_expert_prompt = f"""
1060
  You are an experienced DIY expert with years of hands-on experience in Home Appliance Repair, Automotive Maintenance, Gardening & Urban Farming,
1061
+ Upcycling & Sustainable Crafts, or DIY Project Creation. You speak in a friendly, knowledgeable British manner and provide
1062
+ practical, actionable advice. You're working on this specific project:
1063
+ Project: {project_data.get('projectTitle', project_data.get('title', 'DIY Project'))}
 
1064
  Description: {project_data.get('projectDescription', '')}
1065
  Initial Plan: {project_data.get('initialPlan', '')}
 
1066
  Provide helpful, step-by-step guidance while being encouraging and safety-conscious.
1067
  Ask clarifying questions when needed and share relevant tips from your experience.
1068
  """
1069
 
1070
  agent_data = {
1071
+ "name": f"DIY Expert - {project_data.get('projectTitle', project_data.get('title', 'Project'))}",
1072
  "prompt": diy_expert_prompt,
1073
  "voice_id": voice_id,
1074
  "language": "en",
 
1084
  "max_duration_seconds": 1800 # 30 minutes max
1085
  }
1086
 
1087
+ logger.info(f"[AGENT] Agent data prepared, checking for existing agent")
1088
+
1089
  # Check if agent already exists for this project
1090
  existing_agent_id = db_ref.child(f'projects/{project_id}/agent_id').get()
1091
 
1092
  if existing_agent_id:
1093
+ logger.info(f"[AGENT] Found existing agent ID: {existing_agent_id}")
1094
  # Verify agent still exists
1095
+ try:
1096
+ response = requests.get(
1097
+ f"{self.base_url}/agents/{existing_agent_id}",
1098
+ headers=self.headers
1099
+ )
1100
+ if response.status_code == 200:
1101
+ logger.info(f"[AGENT] Existing agent verified and active")
1102
+ return existing_agent_id
1103
+ else:
1104
+ logger.warning(f"[AGENT] Existing agent not found (status {response.status_code}), creating new one")
1105
+ except Exception as e:
1106
+ logger.warning(f"[AGENT] Error verifying existing agent: {str(e)}, creating new one")
1107
 
1108
  # Create new agent
1109
+ logger.info(f"[AGENT] Creating new agent")
1110
  response = requests.post(
1111
  f"{self.base_url}/agents",
1112
  headers=self.headers,
1113
  json=agent_data
1114
  )
1115
 
1116
+ logger.info(f"[AGENT] Agent creation response status: {response.status_code}")
1117
+
1118
  if response.status_code == 201:
1119
  agent_info = response.json()
1120
  agent_id = agent_info["id"]
1121
+ logger.info(f"[AGENT] New agent created successfully: {agent_id}")
1122
 
1123
  # Store agent ID in database
1124
+ try:
1125
+ db_ref.child(f'projects/{project_id}').update({
1126
+ 'agent_id': agent_id,
1127
+ 'agent_created_at': int(time.time())
1128
+ })
1129
+ logger.info(f"[AGENT] Agent ID stored in database")
1130
+ except Exception as e:
1131
+ logger.error(f"[AGENT] Failed to store agent ID in database: {str(e)}")
1132
 
1133
  return agent_id
1134
  else:
1135
+ logger.error(f"[AGENT] Failed to create agent: Status {response.status_code}")
1136
+ logger.error(f"[AGENT] Response: {response.text}")
1137
  raise Exception(f"Failed to create agent: {response.text}")
1138
 
1139
  except Exception as e:
1140
+ logger.error(f"[AGENT] Error creating/getting agent: {str(e)}")
1141
+ logger.error(f"[AGENT] Exception type: {type(e).__name__}")
1142
  raise
1143
 
1144
  def simulate_conversation(self, agent_id, user_message):
1145
  """Simulate a conversation with the agent"""
1146
  try:
1147
+ logger.info(f"[SIMULATION] Starting conversation simulation with agent: {agent_id}")
1148
+ logger.info(f"[SIMULATION] User message: {user_message[:100]}{'...' if len(user_message) > 100 else ''}")
1149
+
1150
  simulation_data = {
1151
  "simulation_specification": {
1152
  "user_message": user_message,
 
1155
  }
1156
  }
1157
 
1158
+ logger.info(f"[SIMULATION] Sending simulation request")
1159
  response = requests.post(
1160
  f"{self.base_url}/agents/{agent_id}/simulate-conversation",
1161
  headers=self.headers,
1162
+ json=simulation_data,
1163
+ timeout=30 # Add timeout
1164
  )
1165
 
1166
+ logger.info(f"[SIMULATION] Simulation response status: {response.status_code}")
1167
+
1168
  if response.status_code == 200:
1169
+ result = response.json()
1170
+ logger.info(f"[SIMULATION] Simulation completed successfully")
1171
+ logger.info(f"[SIMULATION] Response keys: {list(result.keys()) if isinstance(result, dict) else 'Not a dict'}")
1172
+ return result
1173
  else:
1174
+ logger.error(f"[SIMULATION] Simulation failed: Status {response.status_code}")
1175
+ logger.error(f"[SIMULATION] Response: {response.text}")
1176
  raise Exception(f"Simulation failed: {response.text}")
1177
 
1178
  except Exception as e:
1179
+ logger.error(f"[SIMULATION] Error simulating conversation: {str(e)}")
1180
+ logger.error(f"[SIMULATION] Exception type: {type(e).__name__}")
1181
  raise
1182
 
1183
  def calculate_cost(duration_seconds):