CORVO-AI/claude_code
1
1from flask import Flask, request, jsonify2import requests3import random4import string5import time6import json7 8app = Flask(__name__)9 10# Global variables to store workspace and bot IDs11GLOBAL_WORKSPACE_ID = None12GLOBAL_BOT_ID = None13 14 15# Authorization value used in requests (should be updated with a valid Authorization),16TOKEN = "Bearer bp_pat_HsN5DOU9RjX2uWVwFWop76dwHDv0lHl2CKlZ"17# -------------------------------------------------------------------18# Helper functions for random bot/workspace names19# -------------------------------------------------------------------20def generate_random_name(length=5):21 """Generate a random name for workspace or bot"""22 return ''.join(random.choices(string.ascii_letters, k=length))23 24# -------------------------------------------------------------------25# Functions to create/delete workspaces and bots26# -------------------------------------------------------------------27def create_workspace():28 """Create a new workspace and return its ID"""29 ws_url = "https://api.botpress.cloud/v1/admin/workspaces"30 headers = {31 "Content-Type": "application/json",32 "Authorization": TOKEN33 }34 payload = {"name": generate_random_name()}35 36 try:37 response = requests.post(ws_url, headers=headers, json=payload)38 if response.status_code == 200:39 response_json = response.json()40 workspace_id = response_json.get('id')41 print(f"Successfully created workspace: {workspace_id}")42 return workspace_id43 else:44 print(f"Workspace creation failed with: {response.status_code}, {response.text}")45 return None46 except Exception as e:47 print(f"Error creating workspace: {str(e)}")48 return None49 50 51def create_bot(workspace_id):52 """Create a new bot in the specified workspace and return its ID"""53 if not workspace_id:54 print("Cannot create bot: No workspace ID provided")55 return None56 57 bot_url = "https://api.botpress.cloud/v1/admin/bots"58 headers = {59 "x-workspace-id": workspace_id,60 "Authorization": TOKEN,61 "Content-Type": "application/json"62 }63 payload = {"name": generate_random_name()}64 65 try:66 response = requests.post(bot_url, headers=headers, json=payload)67 if response.status_code == 200:68 response_json = response.json()69 bot_id = response_json.get("bot", {}).get("id")70 if not bot_id:71 print("Bot ID not found in the response.")72 return None73 74 print(f"Successfully created bot: {bot_id} in workspace: {workspace_id}")75 76 # Install integration for the new bot77 integration_success = install_bot_integration(bot_id, workspace_id)78 if integration_success:79 print(f"Successfully installed integration for bot {bot_id}")80 return bot_id81 else:82 print(f"Failed to install integration for bot {bot_id}")83 return bot_id # Still return the bot ID even if integration fails84 else:85 print(f"Bot creation failed with: {response.status_code}, {response.text}")86 return None87 except Exception as e:88 print(f"Error creating bot: {str(e)}")89 return None90 91 92def install_bot_integration(bot_id, workspace_id):93 """Install required integration for the bot to function properly"""94 if not bot_id or not workspace_id:95 print("Cannot install integration: Missing bot ID or workspace ID")96 return False97 98 url = f"https://api.botpress.cloud/v1/admin/bots/{bot_id}"99 headers = {100 "Authorization": TOKEN,101 "Content-Type": "application/json",102 "x-bot-id": bot_id,103 "x-workspace-id": workspace_id104 }105 # Integration payload106 payload = {107 "integrations": {108 "intver_01KQABNSQ436DE5BD8Y2908R60": {109 "enabled": True110 }111 }112 }113 114 try:115 response = requests.put(url, headers=headers, json=payload)116 if response.status_code == 200:117 print(f"Successfully installed integration for bot {bot_id}")118 return True119 else:120 print(f"Failed to install integration: {response.status_code}, {response.text}")121 return False122 except Exception as e:123 print(f"Error installing integration: {str(e)}")124 return False125 126 127def try_delete_bot(bot_id, workspace_id):128 """Attempt to delete a bot from the specified workspace but continue if it fails"""129 if not bot_id or not workspace_id:130 print("Cannot delete bot: Missing bot ID or workspace ID")131 return False132 133 url = f"https://api.botpress.cloud/v1/admin/bots/{bot_id}"134 headers = {135 "x-workspace-id": workspace_id,136 "Authorization": TOKEN137 }138 139 try:140 response = requests.delete(url, headers=headers)141 if response.status_code in [200, 204]:142 print(f"Successfully deleted bot: {bot_id}")143 return True144 else:145 print(f"Failed to delete bot: {response.status_code}, {response.text}")146 return False147 except Exception as e:148 print(f"Error deleting bot: {str(e)}")149 return False150 151 152def try_delete_workspace(workspace_id):153 """Attempt to delete a workspace but continue if it fails"""154 if not workspace_id:155 print("Cannot delete workspace: No workspace ID provided")156 return False157 158 url = f"https://api.botpress.cloud/v1/admin/workspaces/{workspace_id}"159 headers = {160 "Authorization": TOKEN161 }162 163 try:164 response = requests.delete(url, headers=headers)165 if response.status_code in [200, 204]:166 print(f"Successfully deleted workspace: {workspace_id}")167 return True168 else:169 print(f"Failed to delete workspace: {response.status_code}, {response.text}")170 return False171 except Exception as e:172 print(f"Error deleting workspace: {str(e)}")173 return False174 175 176# -------------------------------------------------------------------177# Main function that calls the Botpress API endpoint178# -------------------------------------------------------------------179def chat_with_assistant(user_input, chat_history, bot_id, workspace_id, temperature=0.9, top_p=0.95, max_tokens=None):180 """181 Sends the user input and chat history to the Botpress API endpoint,182 returns the assistant's response and (possibly updated) bot/workspace IDs.183 """184 # Prepare the headers185 headers = {186 "x-bot-id": bot_id,187 "Content-Type": "application/json",188 "Authorization": TOKEN189 }190 191 # Process chat history into the format expected by the API192 messages = []193 system_prompt = ""194 195 for msg in chat_history:196 if msg["role"] == "system":197 system_prompt = msg["content"]198 elif msg["role"] in ["user", "assistant"]:199 # Pass multipart messages directly without modifying their structure200 if "type" in msg and msg["type"] == "multipart" and "content" in msg:201 messages.append(msg) # Keep the original multipart structure202 # Handle regular text messages203 else:204 messages.append({205 "role": msg["role"],206 "content": msg["content"]207 })208 209 # Add the latest user input if not already in chat history210 if user_input and isinstance(user_input, str) and (not messages or messages[-1]["role"] != "user" or messages[-1]["content"] != user_input):211 messages.append({212 "role": "user",213 "content": user_input214 })215 216 # Prepare the payload for the API217 payload = {218 "type": "anthropic:generateContent",219 "input": {220 "model": {221 "id": "claude-opus-4-7"222 },223 "systemPrompt": system_prompt,224 "messages": messages,225 "debug": False,226 }227 }228 229 # Add maxTokens to the payload if provided230 if max_tokens is not None:231 payload["input"]["maxTokens"] = max_tokens232 233 botpress_url = "https://api.botpress.cloud/v1/chat/actions"234 max_retries = 3235 timeout = 120 # Increased timeout for long messages236 237 # For debugging238 print("Payload being sent to Botpress:")239 print(json.dumps(payload, indent=2))240 241 # Attempt to send the request242 for attempt in range(max_retries):243 try:244 print(f"Attempt {attempt+1}: Sending request to Botpress API with bot_id={bot_id}, workspace_id={workspace_id}")245 response = requests.post(botpress_url, json=payload, headers=headers, timeout=timeout)246 247 # If successful (200)248 if response.status_code == 200:249 data = response.json()250 assistant_content = data.get('output', {}).get('choices', [{}])[0].get('content', '')251 print(f"Successfully received response from Botpress API")252 return assistant_content, bot_id, workspace_id253 254 # Check for authentication or permission errors (401, 403)255 elif response.status_code in [401, 403]:256 error_message = "Authentication error"257 try:258 error_data = response.json()259 error_message = error_data.get('message', 'Authentication error')260 except:261 pass262 263 print(f"Authentication error detected: {error_message}")264 265 # We need to create new resources immediately266 print("Creating new workspace and bot...")267 new_workspace_id = create_workspace()268 if not new_workspace_id:269 print("Failed to create a new workspace")270 if attempt < max_retries - 1:271 time.sleep(3)272 continue273 else:274 return "Unable to create new resources. Please try again later.", bot_id, workspace_id275 276 new_bot_id = create_bot(new_workspace_id)277 if not new_bot_id:278 print("Failed to create a new bot")279 if attempt < max_retries - 1:280 time.sleep(3)281 continue282 else:283 return "Unable to create new bot. Please try again later.", new_workspace_id, workspace_id284 285 print(f"Created new workspace: {new_workspace_id} and bot: {new_bot_id}")286 287 # Try again with new IDs288 headers["x-bot-id"] = new_bot_id289 try:290 print(f"Retrying with new bot_id={new_bot_id}")291 retry_response = requests.post(botpress_url, json=payload, headers=headers, timeout=timeout)292 293 if retry_response.status_code == 200:294 data = retry_response.json()295 assistant_content = data.get('output', {}).get('choices', [{}])[0].get('content', '')296 print(f"Successfully received response with new IDs")297 298 # Try to clean up old resources in the background, but don't wait for result299 if bot_id and workspace_id:300 print(f"Attempting to clean up old resources in the background")301 try_delete_bot(bot_id, workspace_id)302 try_delete_workspace(workspace_id)303 304 return assistant_content, new_bot_id, new_workspace_id305 else:306 print(f"Failed with new IDs: {retry_response.status_code}")307 if attempt < max_retries - 1:308 time.sleep(2)309 continue310 else:311 return f"Unable to get a response even with new credentials.", new_bot_id, new_workspace_id312 313 except Exception as e:314 print(f"Error with new IDs: {str(e)}")315 if attempt < max_retries - 1:316 time.sleep(2)317 continue318 else:319 return f"Error with new credentials: {str(e)}", new_bot_id, new_workspace_id320 321 # Handle network errors or timeouts (just retry)322 elif response.status_code in [404, 408, 502, 503, 504]:323 print(f"Received error {response.status_code}. Retrying...")324 time.sleep(3) # Wait before retrying325 continue326 327 # Any other error status code328 else:329 print(f"Received unexpected error: {response.status_code}, {response.text}")330 if attempt < max_retries - 1:331 time.sleep(2)332 continue333 else:334 return f"Unable to get a response from the assistant (Error {response.status_code}).", bot_id, workspace_id335 336 except requests.exceptions.Timeout:337 print(f"Request timed out. Retrying...")338 if attempt < max_retries - 1:339 time.sleep(2)340 continue341 else:342 return "The assistant is taking too long to respond. Please try again with a shorter message.", bot_id, workspace_id343 344 except Exception as e:345 print(f"Error during request: {str(e)}")346 if attempt < max_retries - 1:347 time.sleep(2)348 continue349 else:350 return f"Unable to get a response from the assistant: {str(e)}", bot_id, workspace_id351 352 # Should not reach here due to the handling in the loop353 return "Unable to get a response from the assistant.", bot_id, workspace_id354 355 356# -------------------------------------------------------------------357# Flask Endpoint358# -------------------------------------------------------------------359@app.route("/chat", methods=["POST"])360def chat_endpoint():361 """362 Expects JSON with:363 {364 "user_input": "string", // Can be null if multipart message is in chat_history365 "chat_history": [366 {"role": "system", "content": "..."},367 {"role": "user", "content": "..."},368 // Or for images:369 {"role": "user", "type": "multipart", "content": [370 {"type": "image", "url": "https://example.com/image.jpg"},371 {"type": "text", "text": "What's in this image?"}372 ]},373 ...374 ],375 "temperature": 0.9, // Optional, defaults to 0.9376 "top_p": 0.95, // Optional, defaults to 0.95377 "max_tokens": 1000 // Optional, defaults to null (no limit)378 }379 Returns JSON with:380 {381 "assistant_response": "string"382 }383 """384 global GLOBAL_WORKSPACE_ID, GLOBAL_BOT_ID385 386 # Parse JSON from request387 data = request.get_json(force=True)388 user_input = data.get("user_input", "")389 chat_history = data.get("chat_history", [])390 391 # Get temperature, top_p, and max_tokens from request, or use defaults392 temperature = data.get("temperature", 0.9)393 top_p = data.get("top_p", 0.95)394 max_tokens = data.get("max_tokens", None)395 396 # Validate temperature and top_p values397 try:398 temperature = float(temperature)399 if not 0 <= temperature <= 2:400 temperature = 0.9401 print(f"Invalid temperature value. Using default: {temperature}")402 except (ValueError, TypeError):403 temperature = 0.9404 print(f"Invalid temperature format. Using default: {temperature}")405 406 try:407 top_p = float(top_p)408 if not 0 <= top_p <= 1:409 top_p = 0.95410 print(f"Invalid top_p value. Using default: {top_p}")411 except (ValueError, TypeError):412 top_p = 0.95413 print(f"Invalid top_p format. Using default: {top_p}")414 415 # Validate max_tokens if provided416 if max_tokens is not None:417 try:418 max_tokens = int(max_tokens)419 if max_tokens <= 0:420 print("Invalid max_tokens value (must be positive). Not using max_tokens.")421 max_tokens = None422 except (ValueError, TypeError):423 print("Invalid max_tokens format. Not using max_tokens.")424 max_tokens = None425 426 # If we don't yet have a workspace or bot, create them427 if not GLOBAL_WORKSPACE_ID or not GLOBAL_BOT_ID:428 print("No existing IDs found. Creating new workspace and bot...")429 GLOBAL_WORKSPACE_ID = create_workspace()430 if GLOBAL_WORKSPACE_ID:431 GLOBAL_BOT_ID = create_bot(GLOBAL_WORKSPACE_ID)432 433 # If creation failed434 if not GLOBAL_WORKSPACE_ID or not GLOBAL_BOT_ID:435 return jsonify({"assistant_response": "I'm currently unavailable. Please try again later."}), 500436 437 # Call our function that interacts with Botpress API438 print(f"Sending chat request with existing bot_id={GLOBAL_BOT_ID}, workspace_id={GLOBAL_WORKSPACE_ID}")439 print(f"Using temperature={temperature}, top_p={top_p}, max_tokens={max_tokens}")440 441 assistant_response, updated_bot_id, updated_workspace_id = chat_with_assistant(442 user_input,443 chat_history,444 GLOBAL_BOT_ID,445 GLOBAL_WORKSPACE_ID,446 temperature,447 top_p,448 max_tokens449 )450 451 # Update global IDs if they changed452 if updated_bot_id != GLOBAL_BOT_ID or updated_workspace_id != GLOBAL_WORKSPACE_ID:453 print(f"Updating global IDs: bot_id={updated_bot_id}, workspace_id={updated_workspace_id}")454 GLOBAL_BOT_ID = updated_bot_id455 GLOBAL_WORKSPACE_ID = updated_workspace_id456 457 return jsonify({"assistant_response": assistant_response})458 459 460# -------------------------------------------------------------------461# Run the Flask app462# -------------------------------------------------------------------463 464if __name__ == "__main__":465 app.run(host="0.0.0.0", port=7860, debug=True)