Spaces:
Sleeping
Sleeping
Update main.py
Browse files
main.py
CHANGED
|
@@ -966,17 +966,10 @@ KNOWN_VOICE_IDS = {
|
|
| 966 |
"Antoni": "ErXwobaYiN019PkySvjV", # American male (fallback)
|
| 967 |
}
|
| 968 |
|
| 969 |
-
import os
|
| 970 |
-
import time
|
| 971 |
-
import requests
|
| 972 |
-
import logging
|
| 973 |
-
|
| 974 |
-
# Assumes 'db_ref', 'logger', and 'KNOWN_VOICE_IDS' are initialized elsewhere in your application.
|
| 975 |
-
# from elevenlabs import ElevenLabs # This is no longer needed for the handler's logic.
|
| 976 |
|
| 977 |
class ConversationalAIHandler:
|
| 978 |
def __init__(self):
|
| 979 |
-
#
|
| 980 |
self.base_url = "https://api.elevenlabs.io/v1"
|
| 981 |
self.api_key = os.getenv("ELEVENLABS_API_KEY")
|
| 982 |
if not self.api_key:
|
|
@@ -988,19 +981,16 @@ class ConversationalAIHandler:
|
|
| 988 |
}
|
| 989 |
|
| 990 |
def get_british_male_voice_id(self):
|
| 991 |
-
"""
|
| 992 |
-
|
| 993 |
-
|
| 994 |
-
return KNOWN_VOICE_IDS["Daniel"]
|
| 995 |
-
except KeyError:
|
| 996 |
-
logger.error("[VOICE] Voice ID for 'Daniel' not found. Using hardcoded fallback.")
|
| 997 |
-
return "onwK4e9ZLuTAKqWW03F9"
|
| 998 |
|
| 999 |
def create_or_get_agent(self, project_id, project_data):
|
| 1000 |
-
"""
|
| 1001 |
try:
|
| 1002 |
logger.info(f"[AGENT] Starting agent creation/retrieval for project: {project_id}")
|
| 1003 |
|
|
|
|
| 1004 |
existing_agent_id = db_ref.child(f'projects/{project_id}/agent_id').get()
|
| 1005 |
if existing_agent_id:
|
| 1006 |
logger.info(f"[AGENT] Found existing agent ID in DB: {existing_agent_id}. Verifying...")
|
|
@@ -1015,30 +1005,34 @@ class ConversationalAIHandler:
|
|
| 1015 |
except Exception as e:
|
| 1016 |
logger.warning(f"[AGENT] Error verifying agent: {e}. Creating a new one.")
|
| 1017 |
|
| 1018 |
-
logger.info(f"[AGENT] Creating new agent.")
|
| 1019 |
voice_id = self.get_british_male_voice_id()
|
| 1020 |
|
| 1021 |
diy_expert_prompt = f"""
|
| 1022 |
-
You are an experienced DIY expert
|
|
|
|
|
|
|
| 1023 |
Project: {project_data.get('projectTitle', 'DIY Project')}
|
| 1024 |
Description: {project_data.get('projectDescription', '')}
|
| 1025 |
Initial Plan: {project_data.get('initialPlan', '')}
|
| 1026 |
-
|
|
|
|
| 1027 |
"""
|
| 1028 |
|
| 1029 |
-
#
|
|
|
|
| 1030 |
agent_payload = {
|
| 1031 |
"name": f"DIY Expert - {project_data.get('projectTitle', project_id)}",
|
| 1032 |
"prompt": diy_expert_prompt,
|
| 1033 |
"voice_id": voice_id,
|
| 1034 |
-
"initial_message": "Hello! I'm your Sozo DIY assistant. I'm here to help you with your project. What would you like to work on today?",
|
| 1035 |
"webhook_url": f"{os.getenv('BASE_URL', 'https://rairo-neofix-api.hf.space')}/api/webhook/agent/{project_id}",
|
| 1036 |
"model": "eleven_turbo_v2"
|
| 1037 |
}
|
| 1038 |
|
| 1039 |
-
#
|
| 1040 |
creation_url = f"{self.base_url}/agents"
|
| 1041 |
-
logger.info(f"[AGENT] Posting to
|
| 1042 |
|
| 1043 |
response = requests.post(creation_url, headers=self.headers, json=agent_payload, timeout=30)
|
| 1044 |
|
|
@@ -1048,14 +1042,12 @@ class ConversationalAIHandler:
|
|
| 1048 |
agent_id = agent_info.get("agent_id")
|
| 1049 |
logger.info(f"[AGENT] New agent created successfully: {agent_id}")
|
| 1050 |
|
| 1051 |
-
db_ref.child(f'projects/{project_id}').update({
|
| 1052 |
-
'agent_id': agent_id,
|
| 1053 |
-
'agent_created_at': int(time.time())
|
| 1054 |
-
})
|
| 1055 |
-
logger.info(f"[AGENT] Agent ID stored in database.")
|
| 1056 |
return agent_id
|
| 1057 |
else:
|
|
|
|
| 1058 |
logger.error(f"[AGENT] Failed to create agent. Status: {response.status_code}, Response: {response.text}")
|
|
|
|
| 1059 |
raise Exception(f"Failed to create agent: HTTP {response.status_code} - {response.text}")
|
| 1060 |
|
| 1061 |
except Exception as e:
|
|
@@ -1063,15 +1055,13 @@ class ConversationalAIHandler:
|
|
| 1063 |
raise
|
| 1064 |
|
| 1065 |
def start_conversation(self, agent_id):
|
| 1066 |
-
"""
|
| 1067 |
try:
|
| 1068 |
logger.info(f"[CONVERSATION] Starting conversation with agent: {agent_id}")
|
| 1069 |
-
# CORRECT ENDPOINT: Follows the /agents/{id}/conversations pattern.
|
| 1070 |
conversation_url = f"{self.base_url}/agents/{agent_id}/conversations"
|
| 1071 |
|
| 1072 |
response = requests.post(conversation_url, headers=self.headers, json={}, timeout=30)
|
| 1073 |
|
| 1074 |
-
# A successful start returns HTTP 200 OK.
|
| 1075 |
if response.status_code == 200:
|
| 1076 |
conversation_info = response.json()
|
| 1077 |
conversation_id = conversation_info.get("conversation_id")
|
|
@@ -1080,19 +1070,16 @@ class ConversationalAIHandler:
|
|
| 1080 |
else:
|
| 1081 |
logger.error(f"[CONVERSATION] Failed to start conversation. Status: {response.status_code}, Response: {response.text}")
|
| 1082 |
raise Exception(f"Failed to start conversation: {response.text}")
|
| 1083 |
-
|
| 1084 |
except Exception as e:
|
| 1085 |
logger.error(f"[CONVERSATION] An exception occurred in start_conversation: {e}")
|
| 1086 |
raise
|
| 1087 |
|
| 1088 |
def send_message(self, agent_id, conversation_id, message):
|
| 1089 |
-
"""
|
| 1090 |
try:
|
|
|
|
| 1091 |
logger.info(f"[MESSAGE] Sending message to conversation: {conversation_id}")
|
| 1092 |
-
# CORRECT ENDPOINT: Uses the /conversations/{id}/messages pattern.
|
| 1093 |
message_url = f"{self.base_url}/conversations/{conversation_id}/messages"
|
| 1094 |
-
|
| 1095 |
-
# CORRECT PAYLOAD: The payload requires the "text" of the message.
|
| 1096 |
payload = {"text": message}
|
| 1097 |
|
| 1098 |
response = requests.post(message_url, headers=self.headers, json=payload, timeout=30)
|
|
@@ -1103,7 +1090,6 @@ class ConversationalAIHandler:
|
|
| 1103 |
else:
|
| 1104 |
logger.error(f"[MESSAGE] Failed to send message. Status: {response.status_code}, Response: {response.text}")
|
| 1105 |
raise Exception(f"Failed to send message: {response.text}")
|
| 1106 |
-
|
| 1107 |
except Exception as e:
|
| 1108 |
logger.error(f"[MESSAGE] An exception occurred in send_message: {e}")
|
| 1109 |
raise
|
|
|
|
| 966 |
"Antoni": "ErXwobaYiN019PkySvjV", # American male (fallback)
|
| 967 |
}
|
| 968 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 969 |
|
| 970 |
class ConversationalAIHandler:
|
| 971 |
def __init__(self):
|
| 972 |
+
# Per documentation, the base URL for all v1 API calls.
|
| 973 |
self.base_url = "https://api.elevenlabs.io/v1"
|
| 974 |
self.api_key = os.getenv("ELEVENLABS_API_KEY")
|
| 975 |
if not self.api_key:
|
|
|
|
| 981 |
}
|
| 982 |
|
| 983 |
def get_british_male_voice_id(self):
|
| 984 |
+
"""Gets a known British male voice ID."""
|
| 985 |
+
logger.info(f"[VOICE] Using known voice ID for Daniel.")
|
| 986 |
+
return KNOWN_VOICE_IDS.get("Daniel", "onwK4e9ZLuTAKqWW03F9") # Default fallback
|
|
|
|
|
|
|
|
|
|
|
|
|
| 987 |
|
| 988 |
def create_or_get_agent(self, project_id, project_data):
|
| 989 |
+
"""Creates or retrieves an agent, strictly following the /v1/agents documentation."""
|
| 990 |
try:
|
| 991 |
logger.info(f"[AGENT] Starting agent creation/retrieval for project: {project_id}")
|
| 992 |
|
| 993 |
+
# First, check for and verify an existing agent in the database.
|
| 994 |
existing_agent_id = db_ref.child(f'projects/{project_id}/agent_id').get()
|
| 995 |
if existing_agent_id:
|
| 996 |
logger.info(f"[AGENT] Found existing agent ID in DB: {existing_agent_id}. Verifying...")
|
|
|
|
| 1005 |
except Exception as e:
|
| 1006 |
logger.warning(f"[AGENT] Error verifying agent: {e}. Creating a new one.")
|
| 1007 |
|
| 1008 |
+
logger.info(f"[AGENT] Creating new agent as per API documentation.")
|
| 1009 |
voice_id = self.get_british_male_voice_id()
|
| 1010 |
|
| 1011 |
diy_expert_prompt = f"""
|
| 1012 |
+
You are an experienced DIY expert with years of hands-on experience in Home Appliance Repair, Automotive Maintenance, Gardening & Urban Farming,
|
| 1013 |
+
Upcycling & Sustainable Crafts, or DIY Project Creation. You speak in a friendly, knowledgeable British manner and provide
|
| 1014 |
+
practical, actionable advice. You're working on this specific project:
|
| 1015 |
Project: {project_data.get('projectTitle', 'DIY Project')}
|
| 1016 |
Description: {project_data.get('projectDescription', '')}
|
| 1017 |
Initial Plan: {project_data.get('initialPlan', '')}
|
| 1018 |
+
Provide helpful, step-by-step guidance while being encouraging and safety-conscious.
|
| 1019 |
+
Ask clarifying questions when needed and share relevant tips from your experience.
|
| 1020 |
"""
|
| 1021 |
|
| 1022 |
+
# This payload EXACTLY matches the fields in the /v1/agents/create documentation.
|
| 1023 |
+
# The complex 'conversation_config' is NOT part of this documented endpoint.
|
| 1024 |
agent_payload = {
|
| 1025 |
"name": f"DIY Expert - {project_data.get('projectTitle', project_id)}",
|
| 1026 |
"prompt": diy_expert_prompt,
|
| 1027 |
"voice_id": voice_id,
|
| 1028 |
+
"initial_message": "Hello! I'm your Sozo DIY expert assistant. I'm here to help you with your project. What would you like to work on today?",
|
| 1029 |
"webhook_url": f"{os.getenv('BASE_URL', 'https://rairo-neofix-api.hf.space')}/api/webhook/agent/{project_id}",
|
| 1030 |
"model": "eleven_turbo_v2"
|
| 1031 |
}
|
| 1032 |
|
| 1033 |
+
# This is the exact endpoint from the documentation.
|
| 1034 |
creation_url = f"{self.base_url}/agents"
|
| 1035 |
+
logger.info(f"[AGENT] Posting to official endpoint: {creation_url}")
|
| 1036 |
|
| 1037 |
response = requests.post(creation_url, headers=self.headers, json=agent_payload, timeout=30)
|
| 1038 |
|
|
|
|
| 1042 |
agent_id = agent_info.get("agent_id")
|
| 1043 |
logger.info(f"[AGENT] New agent created successfully: {agent_id}")
|
| 1044 |
|
| 1045 |
+
db_ref.child(f'projects/{project_id}').update({'agent_id': agent_id})
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1046 |
return agent_id
|
| 1047 |
else:
|
| 1048 |
+
# If this 404 error persists, the issue is not in the code but in the environment/account.
|
| 1049 |
logger.error(f"[AGENT] Failed to create agent. Status: {response.status_code}, Response: {response.text}")
|
| 1050 |
+
logger.error("[AGENT] ACTION REQUIRED: This 404 error indicates the API endpoint was not found for your account. Please verify your ElevenLabs subscription includes the Agents API.")
|
| 1051 |
raise Exception(f"Failed to create agent: HTTP {response.status_code} - {response.text}")
|
| 1052 |
|
| 1053 |
except Exception as e:
|
|
|
|
| 1055 |
raise
|
| 1056 |
|
| 1057 |
def start_conversation(self, agent_id):
|
| 1058 |
+
"""Starts a conversation with the created agent."""
|
| 1059 |
try:
|
| 1060 |
logger.info(f"[CONVERSATION] Starting conversation with agent: {agent_id}")
|
|
|
|
| 1061 |
conversation_url = f"{self.base_url}/agents/{agent_id}/conversations"
|
| 1062 |
|
| 1063 |
response = requests.post(conversation_url, headers=self.headers, json={}, timeout=30)
|
| 1064 |
|
|
|
|
| 1065 |
if response.status_code == 200:
|
| 1066 |
conversation_info = response.json()
|
| 1067 |
conversation_id = conversation_info.get("conversation_id")
|
|
|
|
| 1070 |
else:
|
| 1071 |
logger.error(f"[CONVERSATION] Failed to start conversation. Status: {response.status_code}, Response: {response.text}")
|
| 1072 |
raise Exception(f"Failed to start conversation: {response.text}")
|
|
|
|
| 1073 |
except Exception as e:
|
| 1074 |
logger.error(f"[CONVERSATION] An exception occurred in start_conversation: {e}")
|
| 1075 |
raise
|
| 1076 |
|
| 1077 |
def send_message(self, agent_id, conversation_id, message):
|
| 1078 |
+
"""Sends a message to an active conversation."""
|
| 1079 |
try:
|
| 1080 |
+
# Note: agent_id is not used in the URL but kept for consistent function signature.
|
| 1081 |
logger.info(f"[MESSAGE] Sending message to conversation: {conversation_id}")
|
|
|
|
| 1082 |
message_url = f"{self.base_url}/conversations/{conversation_id}/messages"
|
|
|
|
|
|
|
| 1083 |
payload = {"text": message}
|
| 1084 |
|
| 1085 |
response = requests.post(message_url, headers=self.headers, json=payload, timeout=30)
|
|
|
|
| 1090 |
else:
|
| 1091 |
logger.error(f"[MESSAGE] Failed to send message. Status: {response.status_code}, Response: {response.text}")
|
| 1092 |
raise Exception(f"Failed to send message: {response.text}")
|
|
|
|
| 1093 |
except Exception as e:
|
| 1094 |
logger.error(f"[MESSAGE] An exception occurred in send_message: {e}")
|
| 1095 |
raise
|