Spaces:
Sleeping
Sleeping
Update main.py
Browse files
main.py
CHANGED
|
@@ -969,10 +969,11 @@ KNOWN_VOICE_IDS = {
|
|
| 969 |
|
| 970 |
class ConversationalAIHandler:
|
| 971 |
def __init__(self):
|
| 972 |
-
|
|
|
|
| 973 |
self.api_key = os.getenv("ELEVENLABS_API_KEY")
|
| 974 |
if not self.api_key:
|
| 975 |
-
logger.critical("[
|
| 976 |
raise ValueError("ELEVENLABS_API_KEY environment variable not set.")
|
| 977 |
self.headers = {
|
| 978 |
"xi-api-key": self.api_key,
|
|
@@ -980,41 +981,29 @@ class ConversationalAIHandler:
|
|
| 980 |
}
|
| 981 |
|
| 982 |
def get_british_male_voice_id(self):
|
| 983 |
-
|
|
|
|
| 984 |
return KNOWN_VOICE_IDS.get("Daniel", "onwK4e9ZLuTAKqWW03F9")
|
| 985 |
|
| 986 |
def create_or_get_agent(self, project_id, project_data):
|
|
|
|
| 987 |
try:
|
| 988 |
-
logger.info(f"[
|
| 989 |
|
| 990 |
existing_agent_id = db_ref.child(f'projects/{project_id}/agent_id').get()
|
| 991 |
if existing_agent_id:
|
| 992 |
-
|
| 993 |
-
|
| 994 |
-
|
| 995 |
-
|
| 996 |
-
|
| 997 |
-
|
| 998 |
-
return existing_agent_id
|
| 999 |
-
else:
|
| 1000 |
-
logger.warning(f"[AGENT] Verification failed for agent {existing_agent_id} (Status: {response.status_code}). Creating a new one.")
|
| 1001 |
-
except Exception as e:
|
| 1002 |
-
logger.warning(f"[AGENT] Error verifying agent: {e}. Creating a new one.")
|
| 1003 |
-
|
| 1004 |
-
logger.info(f"[AGENT] Creating new agent as per Convai API documentation.")
|
| 1005 |
voice_id = self.get_british_male_voice_id()
|
| 1006 |
|
| 1007 |
diy_expert_prompt = f"""
|
| 1008 |
-
You are an experienced DIY expert
|
| 1009 |
-
Upcycling & Sustainable Crafts, or DIY Project Creation. You speak in a friendly, knowledgeable British manner and provide
|
| 1010 |
-
practical, actionable advice. You're working on this specific project:
|
| 1011 |
-
Project: {project_data.get('projectTitle', 'DIY Project')}
|
| 1012 |
-
Description: {project_data.get('projectDescription', '')}
|
| 1013 |
-
Initial Plan: {project_data.get('initialPlan', '')}
|
| 1014 |
-
Provide helpful, step-by-step guidance while being encouraging and safety-conscious.
|
| 1015 |
-
Ask clarifying questions when needed and share relevant tips from your experience.
|
| 1016 |
"""
|
| 1017 |
-
|
| 1018 |
agent_payload = {
|
| 1019 |
"conversation_config": {
|
| 1020 |
"name": f"DIY Expert - {project_data.get('projectTitle', project_id)}",
|
|
@@ -1024,83 +1013,53 @@ class ConversationalAIHandler:
|
|
| 1024 |
"prompt": diy_expert_prompt
|
| 1025 |
}
|
| 1026 |
}
|
| 1027 |
-
|
| 1028 |
-
creation_url = f"{self.base_url}/convai/agents/create"
|
| 1029 |
-
logger.info(f"[AGENT] Posting to official endpoint: {creation_url}")
|
| 1030 |
-
|
| 1031 |
response = requests.post(creation_url, headers=self.headers, json=agent_payload, timeout=30)
|
| 1032 |
|
| 1033 |
if response.status_code == 200:
|
| 1034 |
agent_info = response.json()
|
| 1035 |
agent_id = agent_info.get("agent_id")
|
| 1036 |
-
logger.info(f"[
|
| 1037 |
db_ref.child(f'projects/{project_id}').update({'agent_id': agent_id})
|
| 1038 |
return agent_id
|
| 1039 |
else:
|
| 1040 |
-
logger.error(f"[
|
| 1041 |
-
logger.error("[AGENT] ACTION REQUIRED: This error may indicate a subscription issue or malformed payload.")
|
| 1042 |
raise Exception(f"Failed to create agent: HTTP {response.status_code} - {response.text}")
|
| 1043 |
|
| 1044 |
except Exception as e:
|
| 1045 |
-
logger.error(f"[
|
| 1046 |
raise
|
| 1047 |
|
| 1048 |
-
def
|
| 1049 |
"""
|
| 1050 |
-
|
| 1051 |
-
|
| 1052 |
-
a conversation_id for tracking or logging.
|
| 1053 |
"""
|
| 1054 |
try:
|
| 1055 |
-
logger.info(f"[
|
| 1056 |
-
url = f"{self.base_url}/
|
| 1057 |
params = {"agent_id": agent_id}
|
|
|
|
| 1058 |
resp = requests.get(url, headers=self.headers, params=params, timeout=15)
|
| 1059 |
|
| 1060 |
if resp.status_code != 200:
|
| 1061 |
-
logger.error(f"[
|
| 1062 |
raise Exception(f"Could not get signed URL: {resp.text}")
|
| 1063 |
|
| 1064 |
data = resp.json()
|
| 1065 |
signed_url = data.get("signed_url")
|
| 1066 |
if not signed_url:
|
| 1067 |
-
logger.error("[
|
| 1068 |
-
raise Exception("Invalid signed URL response")
|
| 1069 |
-
|
| 1070 |
-
# You can optionally list the conversations to extract the conversation_id:
|
| 1071 |
-
logger.info("[CONVERSATION] Fetching existing conversations list for agent...")
|
| 1072 |
-
list_resp = requests.get(f"{self.base_url}/convai/conversations",
|
| 1073 |
-
headers=self.headers,
|
| 1074 |
-
params={"agent_id": agent_id}, timeout=15)
|
| 1075 |
-
if list_resp.status_code == 200:
|
| 1076 |
-
convs = list_resp.json().get("conversations", [])
|
| 1077 |
-
conv_ids = [c["conversation_id"] for c in convs]
|
| 1078 |
-
logger.info(f"[CONVERSATION] Agent has {len(conv_ids)} existing conversations.")
|
| 1079 |
-
else:
|
| 1080 |
-
logger.warning(f"[CONVERSATION] Couldn't list conversations: HTTP {list_resp.status_code}")
|
| 1081 |
|
| 1082 |
-
|
|
|
|
|
|
|
| 1083 |
except Exception as e:
|
| 1084 |
-
logger.error(f"[
|
| 1085 |
raise
|
| 1086 |
|
| 1087 |
-
|
| 1088 |
-
try:
|
| 1089 |
-
logger.info(f"[MESSAGE] Sending message to conversation: {conversation_id}")
|
| 1090 |
-
message_url = f"{self.base_url}/conversations/{conversation_id}/messages"
|
| 1091 |
-
payload = {"text": message}
|
| 1092 |
-
|
| 1093 |
-
response = requests.post(message_url, headers=self.headers, json=payload, timeout=30)
|
| 1094 |
-
|
| 1095 |
-
if response.status_code == 200:
|
| 1096 |
-
logger.info(f"[MESSAGE] Message sent successfully.")
|
| 1097 |
-
return response.json()
|
| 1098 |
-
else:
|
| 1099 |
-
logger.error(f"[MESSAGE] Failed to send message. Status: {response.status_code}, Response: {response.text}")
|
| 1100 |
-
raise Exception(f"Failed to send message: {response.text}")
|
| 1101 |
-
except Exception as e:
|
| 1102 |
-
logger.error(f"[MESSAGE] An exception occurred in send_message: {e}")
|
| 1103 |
-
raise
|
| 1104 |
|
| 1105 |
def calculate_cost(duration_seconds):
|
| 1106 |
"""Calculate cost based on conversation duration"""
|
|
@@ -1110,88 +1069,87 @@ def calculate_cost(duration_seconds):
|
|
| 1110 |
# Your existing Flask route - no changes needed
|
| 1111 |
@app.route('/api/projects/<project_id>/start-conversation', methods=['POST'])
|
| 1112 |
def start_conversation(project_id):
|
| 1113 |
-
"""
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1114 |
start_time = time.time()
|
| 1115 |
-
logger.info(f"[CONVERSATION] Starting conversation process for project: {project_id}")
|
| 1116 |
|
| 1117 |
try:
|
| 1118 |
-
# Authorization
|
| 1119 |
uid = verify_token(request.headers.get('Authorization'))
|
| 1120 |
if not uid:
|
| 1121 |
-
logger.error(f"[CONVERSATION] ERROR: Unauthorized access
|
| 1122 |
return jsonify({'error': 'Unauthorized'}), 401
|
| 1123 |
|
| 1124 |
-
# Get user data
|
| 1125 |
user_ref = db_ref.child(f'users/{uid}')
|
| 1126 |
user = user_ref.get()
|
| 1127 |
if not user:
|
| 1128 |
logger.error(f"[CONVERSATION] ERROR: User not found for uid: {uid}")
|
| 1129 |
return jsonify({'error': 'User not found'}), 404
|
| 1130 |
|
| 1131 |
-
# Get project data
|
| 1132 |
project = db_ref.child(f'projects/{project_id}').get()
|
| 1133 |
if not project or project.get('uid') != uid:
|
| 1134 |
-
logger.error(f"[CONVERSATION] ERROR: Project not found or access denied")
|
| 1135 |
return jsonify({'error': 'Project not found'}), 404
|
| 1136 |
|
| 1137 |
-
# Parse request data
|
| 1138 |
data = request.get_json()
|
| 1139 |
if data is None:
|
| 1140 |
logger.error(f"[CONVERSATION] ERROR: No JSON data received")
|
| 1141 |
return jsonify({'error': 'No JSON data provided'}), 400
|
| 1142 |
|
|
|
|
| 1143 |
initial_message = data.get('message', 'Hello, I need help with my DIY project.')
|
| 1144 |
-
logger.info(f"[CONVERSATION]
|
| 1145 |
|
| 1146 |
-
# Initialize
|
| 1147 |
ai_handler = ConversationalAIHandler()
|
| 1148 |
-
|
| 1149 |
-
# Create or get agent
|
| 1150 |
agent_id = ai_handler.create_or_get_agent(project_id, project)
|
| 1151 |
logger.info(f"[CONVERSATION] Agent ready: {agent_id}")
|
| 1152 |
|
| 1153 |
-
#
|
| 1154 |
-
|
| 1155 |
-
|
|
|
|
| 1156 |
|
| 1157 |
-
#
|
| 1158 |
-
|
| 1159 |
-
logger.info(f"[CONVERSATION] Message sent successfully")
|
| 1160 |
-
|
| 1161 |
-
# Calculate cost and duration
|
| 1162 |
duration = time.time() - start_time
|
| 1163 |
-
cost = calculate_cost(duration)
|
| 1164 |
|
| 1165 |
-
# Check and deduct credits
|
| 1166 |
user_credits = user.get('credits', 0)
|
| 1167 |
if user_credits < cost:
|
| 1168 |
-
logger.error(f"[CONVERSATION] ERROR: Insufficient credits")
|
| 1169 |
return jsonify({'error': 'Insufficient credits', 'needed': cost}), 402
|
| 1170 |
|
| 1171 |
new_credits = user_credits - cost
|
| 1172 |
user_ref.update({'credits': new_credits})
|
| 1173 |
-
|
| 1174 |
-
|
|
|
|
|
|
|
| 1175 |
conversation_log_id = f"{project_id}_{int(start_time)}"
|
| 1176 |
conversation_data = {
|
| 1177 |
'project_id': project_id,
|
| 1178 |
'uid': uid,
|
| 1179 |
'agent_id': agent_id,
|
| 1180 |
-
'
|
| 1181 |
-
'
|
| 1182 |
-
'
|
| 1183 |
-
'duration_seconds': duration,
|
| 1184 |
'credits_used': cost,
|
| 1185 |
'created_at': int(start_time)
|
| 1186 |
}
|
| 1187 |
db_ref.child(f'conversations/{conversation_log_id}').set(conversation_data)
|
| 1188 |
|
| 1189 |
-
# Return
|
|
|
|
| 1190 |
return jsonify({
|
| 1191 |
-
'
|
| 1192 |
'agent_id': agent_id,
|
| 1193 |
-
'
|
| 1194 |
-
'
|
| 1195 |
'durationSeconds': round(duration, 1),
|
| 1196 |
'creditsDeducted': cost,
|
| 1197 |
'remainingCredits': new_credits
|
|
@@ -1199,9 +1157,9 @@ def start_conversation(project_id):
|
|
| 1199 |
|
| 1200 |
except Exception as e:
|
| 1201 |
total_duration = time.time() - start_time
|
| 1202 |
-
logger.error(f"[CONVERSATION] CRITICAL ERROR: {str(e)}")
|
| 1203 |
logger.error(f"[CONVERSATION] Exception type: {type(e).__name__}")
|
| 1204 |
-
return jsonify({'error': 'Failed to
|
| 1205 |
|
| 1206 |
@app.route('/api/webhook/agent/<project_id>', methods=['POST'])
|
| 1207 |
def agent_webhook(project_id):
|
|
|
|
| 969 |
|
| 970 |
class ConversationalAIHandler:
|
| 971 |
def __init__(self):
|
| 972 |
+
# This is the correct base URL for this specific API
|
| 973 |
+
self.base_url = "https://api.elevenlabs.io/v1/convai"
|
| 974 |
self.api_key = os.getenv("ELEVENLABS_API_KEY")
|
| 975 |
if not self.api_key:
|
| 976 |
+
logger.critical("[CONVAI] ELEVENLABS_API_KEY environment variable not set.")
|
| 977 |
raise ValueError("ELEVENLABS_API_KEY environment variable not set.")
|
| 978 |
self.headers = {
|
| 979 |
"xi-api-key": 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"[CONVAI] Using known voice ID for Daniel.")
|
| 986 |
return KNOWN_VOICE_IDS.get("Daniel", "onwK4e9ZLuTAKqWW03F9")
|
| 987 |
|
| 988 |
def create_or_get_agent(self, project_id, project_data):
|
| 989 |
+
"""Creates or retrieves a Convai agent."""
|
| 990 |
try:
|
| 991 |
+
logger.info(f"[CONVAI] Starting agent creation/retrieval for project: {project_id}")
|
| 992 |
|
| 993 |
existing_agent_id = db_ref.child(f'projects/{project_id}/agent_id').get()
|
| 994 |
if existing_agent_id:
|
| 995 |
+
# The Convai API does not have a simple GET /agents/{id} endpoint for verification.
|
| 996 |
+
# We will trust the ID from our database.
|
| 997 |
+
logger.info(f"[CONVAI] Found existing agent ID in DB: {existing_agent_id}. Assuming active.")
|
| 998 |
+
return existing_agent_id
|
| 999 |
+
|
| 1000 |
+
logger.info(f"[CONVAI] Creating new Convai agent.")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1001 |
voice_id = self.get_british_male_voice_id()
|
| 1002 |
|
| 1003 |
diy_expert_prompt = f"""
|
| 1004 |
+
You are an experienced DIY expert... [Your full prompt here] ...
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1005 |
"""
|
| 1006 |
+
|
| 1007 |
agent_payload = {
|
| 1008 |
"conversation_config": {
|
| 1009 |
"name": f"DIY Expert - {project_data.get('projectTitle', project_id)}",
|
|
|
|
| 1013 |
"prompt": diy_expert_prompt
|
| 1014 |
}
|
| 1015 |
}
|
| 1016 |
+
creation_url = f"{self.base_url}/agents/create"
|
|
|
|
|
|
|
|
|
|
| 1017 |
response = requests.post(creation_url, headers=self.headers, json=agent_payload, timeout=30)
|
| 1018 |
|
| 1019 |
if response.status_code == 200:
|
| 1020 |
agent_info = response.json()
|
| 1021 |
agent_id = agent_info.get("agent_id")
|
| 1022 |
+
logger.info(f"[CONVAI] New agent created successfully: {agent_id}")
|
| 1023 |
db_ref.child(f'projects/{project_id}').update({'agent_id': agent_id})
|
| 1024 |
return agent_id
|
| 1025 |
else:
|
| 1026 |
+
logger.error(f"[CONVAI] Failed to create agent. Status: {response.status_code}, Response: {response.text}")
|
|
|
|
| 1027 |
raise Exception(f"Failed to create agent: HTTP {response.status_code} - {response.text}")
|
| 1028 |
|
| 1029 |
except Exception as e:
|
| 1030 |
+
logger.error(f"[CONVAI] An exception occurred in create_or_get_agent: {e}")
|
| 1031 |
raise
|
| 1032 |
|
| 1033 |
+
def get_conversation_url(self, agent_id):
|
| 1034 |
"""
|
| 1035 |
+
Gets a signed WebSocket URL to start a conversation.
|
| 1036 |
+
This is the only method needed to start the conversation flow.
|
|
|
|
| 1037 |
"""
|
| 1038 |
try:
|
| 1039 |
+
logger.info(f"[CONVAI] Requesting signed WebSocket URL for agent: {agent_id}")
|
| 1040 |
+
url = f"{self.base_url}/conversation/get-signed-url"
|
| 1041 |
params = {"agent_id": agent_id}
|
| 1042 |
+
|
| 1043 |
resp = requests.get(url, headers=self.headers, params=params, timeout=15)
|
| 1044 |
|
| 1045 |
if resp.status_code != 200:
|
| 1046 |
+
logger.error(f"[CONVAI] Signed-URL request failed ({resp.status_code}): {resp.text}")
|
| 1047 |
raise Exception(f"Could not get signed URL: {resp.text}")
|
| 1048 |
|
| 1049 |
data = resp.json()
|
| 1050 |
signed_url = data.get("signed_url")
|
| 1051 |
if not signed_url:
|
| 1052 |
+
logger.error("[CONVAI] Missing 'signed_url' in response.")
|
| 1053 |
+
raise Exception("Invalid signed URL response from ElevenLabs")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1054 |
|
| 1055 |
+
logger.info("[CONVAI] Successfully retrieved WebSocket URL.")
|
| 1056 |
+
return signed_url
|
| 1057 |
+
|
| 1058 |
except Exception as e:
|
| 1059 |
+
logger.error(f"[CONVAI] Exception in get_conversation_url: {e}")
|
| 1060 |
raise
|
| 1061 |
|
| 1062 |
+
# The send_message function is intentionally removed as it is not used in this WebSocket architecture.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1063 |
|
| 1064 |
def calculate_cost(duration_seconds):
|
| 1065 |
"""Calculate cost based on conversation duration"""
|
|
|
|
| 1069 |
# Your existing Flask route - no changes needed
|
| 1070 |
@app.route('/api/projects/<project_id>/start-conversation', methods=['POST'])
|
| 1071 |
def start_conversation(project_id):
|
| 1072 |
+
"""
|
| 1073 |
+
Initiates a conversational AI session by providing the client with a secure WebSocket URL.
|
| 1074 |
+
This backend route authorizes the user, creates/retrieves the agent, deducts an initiation
|
| 1075 |
+
cost, and returns the URL. The client is responsible for the actual conversation.
|
| 1076 |
+
"""
|
| 1077 |
start_time = time.time()
|
| 1078 |
+
logger.info(f"[CONVERSATION] Starting conversation initiation process for project: {project_id}")
|
| 1079 |
|
| 1080 |
try:
|
| 1081 |
+
# Step 1: Authorization and Data Validation (Unchanged)
|
| 1082 |
uid = verify_token(request.headers.get('Authorization'))
|
| 1083 |
if not uid:
|
| 1084 |
+
logger.error(f"[CONVERSATION] ERROR: Unauthorized access for project: {project_id}")
|
| 1085 |
return jsonify({'error': 'Unauthorized'}), 401
|
| 1086 |
|
|
|
|
| 1087 |
user_ref = db_ref.child(f'users/{uid}')
|
| 1088 |
user = user_ref.get()
|
| 1089 |
if not user:
|
| 1090 |
logger.error(f"[CONVERSATION] ERROR: User not found for uid: {uid}")
|
| 1091 |
return jsonify({'error': 'User not found'}), 404
|
| 1092 |
|
|
|
|
| 1093 |
project = db_ref.child(f'projects/{project_id}').get()
|
| 1094 |
if not project or project.get('uid') != uid:
|
| 1095 |
+
logger.error(f"[CONVERSATION] ERROR: Project not found or access denied for user {uid}")
|
| 1096 |
return jsonify({'error': 'Project not found'}), 404
|
| 1097 |
|
|
|
|
| 1098 |
data = request.get_json()
|
| 1099 |
if data is None:
|
| 1100 |
logger.error(f"[CONVERSATION] ERROR: No JSON data received")
|
| 1101 |
return jsonify({'error': 'No JSON data provided'}), 400
|
| 1102 |
|
| 1103 |
+
# The initial message is noted but will be sent by the client, not the backend.
|
| 1104 |
initial_message = data.get('message', 'Hello, I need help with my DIY project.')
|
| 1105 |
+
logger.info(f"[CONVERSATION] User's initial message (to be sent by client): {initial_message[:100]}{'...' if len(initial_message) > 100 else ''}")
|
| 1106 |
|
| 1107 |
+
# Step 2: Initialize Handler and Get Agent (Unchanged)
|
| 1108 |
ai_handler = ConversationalAIHandler()
|
|
|
|
|
|
|
| 1109 |
agent_id = ai_handler.create_or_get_agent(project_id, project)
|
| 1110 |
logger.info(f"[CONVERSATION] Agent ready: {agent_id}")
|
| 1111 |
|
| 1112 |
+
# Step 3: Get the Secure WebSocket URL (This is the new core logic)
|
| 1113 |
+
# This replaces the old start_conversation and send_message calls.
|
| 1114 |
+
websocket_url = ai_handler.get_conversation_url(agent_id)
|
| 1115 |
+
logger.info(f"[CONVERSATION] WebSocket URL received from ElevenLabs.")
|
| 1116 |
|
| 1117 |
+
# Step 4: Calculate Cost and Deduct Credits (Logic adapted for initiation fee)
|
| 1118 |
+
# The cost is now for the service of setting up the session.
|
|
|
|
|
|
|
|
|
|
| 1119 |
duration = time.time() - start_time
|
| 1120 |
+
cost = calculate_cost(duration) # Or a flat fee, e.g., cost = CREDITS_PER_MIN
|
| 1121 |
|
|
|
|
| 1122 |
user_credits = user.get('credits', 0)
|
| 1123 |
if user_credits < cost:
|
| 1124 |
+
logger.error(f"[CONVERSATION] ERROR: Insufficient credits for session initiation.")
|
| 1125 |
return jsonify({'error': 'Insufficient credits', 'needed': cost}), 402
|
| 1126 |
|
| 1127 |
new_credits = user_credits - cost
|
| 1128 |
user_ref.update({'credits': new_credits})
|
| 1129 |
+
logger.info(f"[CONVERSATION] Billed {cost} credits for session initiation. Remaining credits: {new_credits}")
|
| 1130 |
+
|
| 1131 |
+
# Step 5: Log the Session Initiation
|
| 1132 |
+
# We log that a session was started, but not the back-and-forth messages.
|
| 1133 |
conversation_log_id = f"{project_id}_{int(start_time)}"
|
| 1134 |
conversation_data = {
|
| 1135 |
'project_id': project_id,
|
| 1136 |
'uid': uid,
|
| 1137 |
'agent_id': agent_id,
|
| 1138 |
+
'conversation_log_id': conversation_log_id, # Self-reference
|
| 1139 |
+
'initial_message_prompt': initial_message,
|
| 1140 |
+
'duration_seconds_backend': duration, # Log backend processing time
|
|
|
|
| 1141 |
'credits_used': cost,
|
| 1142 |
'created_at': int(start_time)
|
| 1143 |
}
|
| 1144 |
db_ref.child(f'conversations/{conversation_log_id}').set(conversation_data)
|
| 1145 |
|
| 1146 |
+
# Step 6: Return the WebSocket URL to the Client
|
| 1147 |
+
# The client will use this URL to establish a direct connection for the conversation.
|
| 1148 |
return jsonify({
|
| 1149 |
+
'websocket_url': websocket_url,
|
| 1150 |
'agent_id': agent_id,
|
| 1151 |
+
'conversation_log_id': conversation_log_id,
|
| 1152 |
+
'status': 'success',
|
| 1153 |
'durationSeconds': round(duration, 1),
|
| 1154 |
'creditsDeducted': cost,
|
| 1155 |
'remainingCredits': new_credits
|
|
|
|
| 1157 |
|
| 1158 |
except Exception as e:
|
| 1159 |
total_duration = time.time() - start_time
|
| 1160 |
+
logger.error(f"[CONVERSATION] CRITICAL ERROR in start_conversation route: {str(e)}")
|
| 1161 |
logger.error(f"[CONVERSATION] Exception type: {type(e).__name__}")
|
| 1162 |
+
return jsonify({'error': 'Failed to initiate conversation'}), 500
|
| 1163 |
|
| 1164 |
@app.route('/api/webhook/agent/<project_id>', methods=['POST'])
|
| 1165 |
def agent_webhook(project_id):
|