From 2770b8c7bfbf6f3c6054f0313a6a358823bcc661 Mon Sep 17 00:00:00 2001 From: Kye Gomez Date: Tue, 24 Jun 2025 23:27:44 -0700 Subject: [PATCH] docs fix for the swarmns api fix --- burning_image.jpg | Bin 0 -> 43016 bytes docs/swarms_cloud/swarms_api.md | 1371 +++++++++++++++++-------------- multiple_image_processing.py | 9 +- swarms/structs/agent.py | 113 ++- vision_tools.py | 68 ++ 5 files changed, 913 insertions(+), 648 deletions(-) create mode 100644 burning_image.jpg create mode 100644 vision_tools.py diff --git a/burning_image.jpg b/burning_image.jpg new file mode 100644 index 0000000000000000000000000000000000000000..7d260cf3dcd30f405d410d18f8b0ffffa2b8ae52 GIT binary patch literal 43016 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 literal 0 HcmV?d00001 diff --git a/docs/swarms_cloud/swarms_api.md b/docs/swarms_cloud/swarms_api.md index 9da9ebce..f09c6eae 100644 --- a/docs/swarms_cloud/swarms_api.md +++ b/docs/swarms_cloud/swarms_api.md @@ -18,8 +18,6 @@ Key capabilities include: - **Multiple Swarm Architectures**: Choose from various swarm patterns to match your specific workflow needs -- **Scheduled Execution**: Set up automated, scheduled swarm executions - - **Comprehensive Logging**: Track and analyze all API interactions - **Cost Management**: Predictable, transparent pricing with optimized resource utilization @@ -47,9 +45,6 @@ API keys can be obtained and managed at [https://swarms.world/platform/api-keys] | `/health` | GET | Simple health check endpoint | | `/v1/swarm/completions` | POST | Run a swarm with specified configuration | | `/v1/swarm/batch/completions` | POST | Run multiple swarms in batch mode | -| `/v1/swarm/schedule` | POST | Schedule a swarm to run at a specific time | -| `/v1/swarm/schedule` | GET | Get all scheduled swarm jobs | -| `/v1/swarm/schedule/{job_id}` | DELETE | Cancel a scheduled swarm job | | `/v1/swarm/logs` | GET | Retrieve API request logs | | `/v1/swarms/available` | GET | Get all available swarms as a list of strings | | `/v1/models/available` | GET | Get all available models as a list of strings | @@ -96,7 +91,6 @@ The `SwarmSpec` model defines the configuration of a swarm. | img | string | Optional image URL for the swarm | No | | return_history | boolean | Whether to return execution history | No | | rules | string | Guidelines for swarm behavior | No | -| schedule | ScheduleSpec | Scheduling information | No | | service_tier | string | Service tier for processing ("standard" or "flex") | No | ### AgentSpec @@ -117,16 +111,6 @@ The `AgentSpec` model defines the configuration of an individual agent. *Required if agents are manually specified; not required if using auto-generated agents -### ScheduleSpec - -The `ScheduleSpec` model defines when a swarm should be executed. - -| Field | Type | Description | Required | -|-------|------|-------------|----------| -| scheduled_time | datetime | Time when the swarm should run | Yes | -| timezone | string | Timezone for the scheduled time | No (defaults to "UTC") | - - ### Endpoint Details @@ -138,11 +122,58 @@ Check if the API service is available and functioning correctly. **Method**: GET **Rate Limit**: 100 requests per 60 seconds -**Example Request**: -```bash -curl -X GET "https://api.swarms.world/health" \ - -H "x-api-key: your_api_key_here" -``` +=== "Shell (curl)" + ```bash + curl -X GET "https://api.swarms.world/health" \ + -H "x-api-key: your_api_key_here" + ``` + +=== "Python (requests)" + ```python + import requests + + API_BASE_URL = "https://api.swarms.world" + API_KEY = "your_api_key_here" + + headers = { + "x-api-key": API_KEY + } + + response = requests.get(f"{API_BASE_URL}/health", headers=headers) + + if response.status_code == 200: + print("API is healthy:", response.json()) + else: + print(f"Error: {response.status_code}") + ``` + +=== "TypeScript (fetch)" + ```typescript + const API_BASE_URL = "https://api.swarms.world"; + const API_KEY = "your_api_key_here"; + + async function checkHealth(): Promise { + try { + const response = await fetch(`${API_BASE_URL}/health`, { + method: 'GET', + headers: { + 'x-api-key': API_KEY + } + }); + + if (response.ok) { + const data = await response.json(); + console.log("API is healthy:", data); + } else { + console.error(`Error: ${response.status}`); + } + } catch (error) { + console.error("Request failed:", error); + } + } + + checkHealth(); + ``` **Example Response**: ```json @@ -173,49 +204,193 @@ Run a swarm with the specified configuration to complete a task. | img | string | Optional image URL for the swarm | No | | return_history | boolean | Whether to return execution history | No | | rules | string | Guidelines for swarm behavior | No | -| schedule | ScheduleSpec | Scheduling information | No | - -**Example Request**: -```bash - -# Run single swarm -curl -X POST "https://api.swarms.world/v1/swarm/completions" \ - -H "x-api-key: $SWARMS_API_KEY" \ - -H "Content-Type: application/json" \ - -d '{ - "name": "Financial Analysis Swarm", - "description": "Market analysis swarm", - "agents": [ - { - "agent_name": "Market Analyst", - "description": "Analyzes market trends", - "system_prompt": "You are a financial analyst expert.", - "model_name": "openai/gpt-4o", - "role": "worker", + +=== "Shell (curl)" + ```bash + curl -X POST "https://api.swarms.world/v1/swarm/completions" \ + -H "x-api-key: $SWARMS_API_KEY" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "Financial Analysis Swarm", + "description": "Market analysis swarm", + "agents": [ + { + "agent_name": "Market Analyst", + "description": "Analyzes market trends", + "system_prompt": "You are a financial analyst expert.", + "model_name": "openai/gpt-4o", + "role": "worker", + "max_loops": 1, + "max_tokens": 8192, + "temperature": 0.5, + "auto_generate_prompt": false + }, + { + "agent_name": "Economic Forecaster", + "description": "Predicts economic trends", + "system_prompt": "You are an expert in economic forecasting.", + "model_name": "gpt-4o", + "role": "worker", + "max_loops": 1, + "max_tokens": 8192, + "temperature": 0.5, + "auto_generate_prompt": false + } + ], "max_loops": 1, - "max_tokens": 8192, - "temperature": 0.5, - "auto_generate_prompt": false - }, - { - "agent_name": "Economic Forecaster", - "description": "Predicts economic trends", - "system_prompt": "You are an expert in economic forecasting.", - "model_name": "gpt-4o", - "role": "worker", + "swarm_type": "ConcurrentWorkflow", + "task": "What are the best etfs and index funds for ai and tech?", + "output_type": "dict" + }' + ``` + +=== "Python (requests)" + ```python + import requests + import json + + API_BASE_URL = "https://api.swarms.world" + API_KEY = "your_api_key_here" + + headers = { + "x-api-key": API_KEY, + "Content-Type": "application/json" + } + + swarm_config = { + "name": "Financial Analysis Swarm", + "description": "Market analysis swarm", + "agents": [ + { + "agent_name": "Market Analyst", + "description": "Analyzes market trends", + "system_prompt": "You are a financial analyst expert.", + "model_name": "openai/gpt-4o", + "role": "worker", + "max_loops": 1, + "max_tokens": 8192, + "temperature": 0.5, + "auto_generate_prompt": False + }, + { + "agent_name": "Economic Forecaster", + "description": "Predicts economic trends", + "system_prompt": "You are an expert in economic forecasting.", + "model_name": "gpt-4o", + "role": "worker", + "max_loops": 1, + "max_tokens": 8192, + "temperature": 0.5, + "auto_generate_prompt": False + } + ], "max_loops": 1, - "max_tokens": 8192, - "temperature": 0.5, - "auto_generate_prompt": false - } - ], - "max_loops": 1, - "swarm_type": "ConcurrentWorkflow", - "task": "What are the best etfs and index funds for ai and tech?", - "output_type": "dict" - }' + "swarm_type": "ConcurrentWorkflow", + "task": "What are the best etfs and index funds for ai and tech?", + "output_type": "dict" + } + + response = requests.post( + f"{API_BASE_URL}/v1/swarm/completions", + headers=headers, + json=swarm_config + ) + + if response.status_code == 200: + result = response.json() + print("Swarm completed successfully!") + print(f"Cost: ${result['metadata']['billing_info']['total_cost']}") + print(f"Execution time: {result['metadata']['execution_time_seconds']} seconds") + else: + print(f"Error: {response.status_code} - {response.text}") + ``` + +=== "TypeScript (fetch)" + ```typescript + interface AgentSpec { + agent_name: string; + description: string; + system_prompt: string; + model_name: string; + role: string; + max_loops: number; + max_tokens: number; + temperature: number; + auto_generate_prompt: boolean; + } -``` + interface SwarmConfig { + name: string; + description: string; + agents: AgentSpec[]; + max_loops: number; + swarm_type: string; + task: string; + output_type: string; + } + + const API_BASE_URL = "https://api.swarms.world"; + const API_KEY = "your_api_key_here"; + + async function runSwarm(): Promise { + const swarmConfig: SwarmConfig = { + name: "Financial Analysis Swarm", + description: "Market analysis swarm", + agents: [ + { + agent_name: "Market Analyst", + description: "Analyzes market trends", + system_prompt: "You are a financial analyst expert.", + model_name: "openai/gpt-4o", + role: "worker", + max_loops: 1, + max_tokens: 8192, + temperature: 0.5, + auto_generate_prompt: false + }, + { + agent_name: "Economic Forecaster", + description: "Predicts economic trends", + system_prompt: "You are an expert in economic forecasting.", + model_name: "gpt-4o", + role: "worker", + max_loops: 1, + max_tokens: 8192, + temperature: 0.5, + auto_generate_prompt: false + } + ], + max_loops: 1, + swarm_type: "ConcurrentWorkflow", + task: "What are the best etfs and index funds for ai and tech?", + output_type: "dict" + }; + + try { + const response = await fetch(`${API_BASE_URL}/v1/swarm/completions`, { + method: 'POST', + headers: { + 'x-api-key': API_KEY, + 'Content-Type': 'application/json' + }, + body: JSON.stringify(swarmConfig) + }); + + if (response.ok) { + const result = await response.json(); + console.log("Swarm completed successfully!"); + console.log(`Cost: $${result.metadata.billing_info.total_cost}`); + console.log(`Execution time: ${result.metadata.execution_time_seconds} seconds`); + } else { + console.error(`Error: ${response.status} - ${await response.text()}`); + } + } catch (error) { + console.error("Request failed:", error); + } + } + + runSwarm(); + ``` **Example Response**: ```json @@ -271,65 +446,249 @@ Run multiple swarms as a batch operation. |-------|------|-------------|----------| | swarms | Array | List of swarm specifications | Yes | -**Example Request**: -```bash -# Batch swarm completions -curl -X POST "https://api.swarms.world/v1/swarm/batch/completions" \ - -H "x-api-key: $SWARMS_API_KEY" \ - -H "Content-Type: application/json" \ - -d '[ - { - "name": "Batch Swarm 1", - "description": "First swarm in the batch", - "agents": [ +=== "Shell (curl)" + ```bash + curl -X POST "https://api.swarms.world/v1/swarm/batch/completions" \ + -H "x-api-key: $SWARMS_API_KEY" \ + -H "Content-Type: application/json" \ + -d '[ { - "agent_name": "Research Agent", - "description": "Conducts research", - "system_prompt": "You are a research assistant.", - "model_name": "gpt-4o", - "role": "worker", - "max_loops": 1 + "name": "Batch Swarm 1", + "description": "First swarm in the batch", + "agents": [ + { + "agent_name": "Research Agent", + "description": "Conducts research", + "system_prompt": "You are a research assistant.", + "model_name": "gpt-4o", + "role": "worker", + "max_loops": 1 + }, + { + "agent_name": "Analysis Agent", + "description": "Analyzes data", + "system_prompt": "You are a data analyst.", + "model_name": "gpt-4o", + "role": "worker", + "max_loops": 1 + } + ], + "max_loops": 1, + "swarm_type": "SequentialWorkflow", + "task": "Research AI advancements." }, { - "agent_name": "Analysis Agent", - "description": "Analyzes data", - "system_prompt": "You are a data analyst.", - "model_name": "gpt-4o", - "role": "worker", - "max_loops": 1 + "name": "Batch Swarm 2", + "description": "Second swarm in the batch", + "agents": [ + { + "agent_name": "Writing Agent", + "description": "Writes content", + "system_prompt": "You are a content writer.", + "model_name": "gpt-4o", + "role": "worker", + "max_loops": 1 + }, + { + "agent_name": "Editing Agent", + "description": "Edits content", + "system_prompt": "You are an editor.", + "model_name": "gpt-4o", + "role": "worker", + "max_loops": 1 + } + ], + "max_loops": 1, + "swarm_type": "SequentialWorkflow", + "task": "Write a summary of AI research." } - ], - "max_loops": 1, - "swarm_type": "SequentialWorkflow", - "task": "Research AI advancements." - }, - { - "name": "Batch Swarm 2", - "description": "Second swarm in the batch", - "agents": [ + ]' + ``` + +=== "Python (requests)" + ```python + import requests + import json + + API_BASE_URL = "https://api.swarms.world" + API_KEY = "your_api_key_here" + + headers = { + "x-api-key": API_KEY, + "Content-Type": "application/json" + } + + batch_swarms = [ { - "agent_name": "Writing Agent", - "description": "Writes content", - "system_prompt": "You are a content writer.", - "model_name": "gpt-4o", - "role": "worker", - "max_loops": 1 + "name": "Batch Swarm 1", + "description": "First swarm in the batch", + "agents": [ + { + "agent_name": "Research Agent", + "description": "Conducts research", + "system_prompt": "You are a research assistant.", + "model_name": "gpt-4o", + "role": "worker", + "max_loops": 1 + }, + { + "agent_name": "Analysis Agent", + "description": "Analyzes data", + "system_prompt": "You are a data analyst.", + "model_name": "gpt-4o", + "role": "worker", + "max_loops": 1 + } + ], + "max_loops": 1, + "swarm_type": "SequentialWorkflow", + "task": "Research AI advancements." }, { - "agent_name": "Editing Agent", - "description": "Edits content", - "system_prompt": "You are an editor.", - "model_name": "gpt-4o", - "role": "worker", - "max_loops": 1 + "name": "Batch Swarm 2", + "description": "Second swarm in the batch", + "agents": [ + { + "agent_name": "Writing Agent", + "description": "Writes content", + "system_prompt": "You are a content writer.", + "model_name": "gpt-4o", + "role": "worker", + "max_loops": 1 + }, + { + "agent_name": "Editing Agent", + "description": "Edits content", + "system_prompt": "You are an editor.", + "model_name": "gpt-4o", + "role": "worker", + "max_loops": 1 + } + ], + "max_loops": 1, + "swarm_type": "SequentialWorkflow", + "task": "Write a summary of AI research." + } + ] + + response = requests.post( + f"{API_BASE_URL}/v1/swarm/batch/completions", + headers=headers, + json=batch_swarms + ) + + if response.status_code == 200: + results = response.json() + print(f"Batch completed with {len(results)} swarms") + for i, result in enumerate(results): + print(f"Swarm {i+1}: {result['swarm_name']} - {result['status']}") + else: + print(f"Error: {response.status_code} - {response.text}") + ``` + +=== "TypeScript (fetch)" + ```typescript + interface AgentSpec { + agent_name: string; + description: string; + system_prompt: string; + model_name: string; + role: string; + max_loops: number; + } + + interface SwarmSpec { + name: string; + description: string; + agents: AgentSpec[]; + max_loops: number; + swarm_type: string; + task: string; + } + + const API_BASE_URL = "https://api.swarms.world"; + const API_KEY = "your_api_key_here"; + + async function runBatchSwarms(): Promise { + const batchSwarms: SwarmSpec[] = [ + { + name: "Batch Swarm 1", + description: "First swarm in the batch", + agents: [ + { + agent_name: "Research Agent", + description: "Conducts research", + system_prompt: "You are a research assistant.", + model_name: "gpt-4o", + role: "worker", + max_loops: 1 + }, + { + agent_name: "Analysis Agent", + description: "Analyzes data", + system_prompt: "You are a data analyst.", + model_name: "gpt-4o", + role: "worker", + max_loops: 1 + } + ], + max_loops: 1, + swarm_type: "SequentialWorkflow", + task: "Research AI advancements." + }, + { + name: "Batch Swarm 2", + description: "Second swarm in the batch", + agents: [ + { + agent_name: "Writing Agent", + description: "Writes content", + system_prompt: "You are a content writer.", + model_name: "gpt-4o", + role: "worker", + max_loops: 1 + }, + { + agent_name: "Editing Agent", + description: "Edits content", + system_prompt: "You are an editor.", + model_name: "gpt-4o", + role: "worker", + max_loops: 1 + } + ], + max_loops: 1, + swarm_type: "SequentialWorkflow", + task: "Write a summary of AI research." + } + ]; + + try { + const response = await fetch(`${API_BASE_URL}/v1/swarm/batch/completions`, { + method: 'POST', + headers: { + 'x-api-key': API_KEY, + 'Content-Type': 'application/json' + }, + body: JSON.stringify(batchSwarms) + }); + + if (response.ok) { + const results = await response.json(); + console.log(`Batch completed with ${results.length} swarms`); + results.forEach((result: any, index: number) => { + console.log(`Swarm ${index + 1}: ${result.swarm_name} - ${result.status}`); + }); + } else { + console.error(`Error: ${response.status} - ${await response.text()}`); + } + } catch (error) { + console.error("Request failed:", error); } - ], - "max_loops": 1, - "swarm_type": "SequentialWorkflow", - "task": "Write a summary of AI research." } - ]' -``` + + runBatchSwarms(); + ``` **Example Response**: ```json @@ -351,10 +710,7 @@ curl -X POST "https://api.swarms.world/v1/swarm/batch/completions" \ ] ``` -------- - - - +## Individual Agent Endpoints ### Run Single Agent @@ -371,24 +727,125 @@ Run a single agent with the specified configuration. | agent_config | AgentSpec | Configuration for the agent | Yes | | task | string | The task to be completed by the agent | Yes | -**Example Request**: -```bash -curl -X POST "https://api.swarms.world/v1/agent/completions" \ - -H "x-api-key: your_api_key_here" \ - -H "Content-Type: application/json" \ - -d '{ - "agent_config": { - "agent_name": "Research Assistant", - "description": "Helps with research tasks", - "system_prompt": "You are a research assistant expert.", - "model_name": "gpt-4o", - "max_loops": 1, - "max_tokens": 8192, - "temperature": 0.5 - }, - "task": "Research the latest developments in quantum computing." - }' -``` +=== "Shell (curl)" + ```bash + curl -X POST "https://api.swarms.world/v1/agent/completions" \ + -H "x-api-key: your_api_key_here" \ + -H "Content-Type: application/json" \ + -d '{ + "agent_config": { + "agent_name": "Research Assistant", + "description": "Helps with research tasks", + "system_prompt": "You are a research assistant expert.", + "model_name": "gpt-4o", + "max_loops": 1, + "max_tokens": 8192, + "temperature": 0.5 + }, + "task": "Research the latest developments in quantum computing." + }' + ``` + +=== "Python (requests)" + ```python + import requests + import json + + API_BASE_URL = "https://api.swarms.world" + API_KEY = "your_api_key_here" + + headers = { + "x-api-key": API_KEY, + "Content-Type": "application/json" + } + + agent_request = { + "agent_config": { + "agent_name": "Research Assistant", + "description": "Helps with research tasks", + "system_prompt": "You are a research assistant expert.", + "model_name": "gpt-4o", + "max_loops": 1, + "max_tokens": 8192, + "temperature": 0.5 + }, + "task": "Research the latest developments in quantum computing." + } + + response = requests.post( + f"{API_BASE_URL}/v1/agent/completions", + headers=headers, + json=agent_request + ) + + if response.status_code == 200: + result = response.json() + print(f"Agent {result['name']} completed successfully!") + print(f"Usage: {result['usage']['total_tokens']} tokens") + print(f"Output: {result['outputs']}") + else: + print(f"Error: {response.status_code} - {response.text}") + ``` + +=== "TypeScript (fetch)" + ```typescript + interface AgentConfig { + agent_name: string; + description: string; + system_prompt: string; + model_name: string; + max_loops: number; + max_tokens: number; + temperature: number; + } + + interface AgentRequest { + agent_config: AgentConfig; + task: string; + } + + const API_BASE_URL = "https://api.swarms.world"; + const API_KEY = "your_api_key_here"; + + async function runSingleAgent(): Promise { + const agentRequest: AgentRequest = { + agent_config: { + agent_name: "Research Assistant", + description: "Helps with research tasks", + system_prompt: "You are a research assistant expert.", + model_name: "gpt-4o", + max_loops: 1, + max_tokens: 8192, + temperature: 0.5 + }, + task: "Research the latest developments in quantum computing." + }; + + try { + const response = await fetch(`${API_BASE_URL}/v1/agent/completions`, { + method: 'POST', + headers: { + 'x-api-key': API_KEY, + 'Content-Type': 'application/json' + }, + body: JSON.stringify(agentRequest) + }); + + if (response.ok) { + const result = await response.json(); + console.log(`Agent ${result.name} completed successfully!`); + console.log(`Usage: ${result.usage.total_tokens} tokens`); + console.log(`Output:`, result.outputs); + } else { + console.error(`Error: ${response.status} - ${await response.text()}`); + } + } catch (error) { + console.error("Request failed:", error); + } + } + + runSingleAgent(); + ``` **Example Response**: ```json @@ -408,92 +865,6 @@ curl -X POST "https://api.swarms.world/v1/agent/completions" \ } ``` - - -### Get Models - -#### Get Available Models - -Get all available models as a list of strings. - -**Endpoint**: `/v1/models/available` -**Method**: GET - -**Example Request**: -```bash -curl -X GET "https://api.swarms.world/v1/models/available" \ - -H "x-api-key: your_api_key_here" -``` - - ------- - - -### Get Swarms Available - -Get all available swarms as a list of strings. - -**Endpoint**: `/v1/swarms/available` -**Method**: GET - -**Example Request**: -```bash -curl -X GET "https://api.swarms.world/v1/swarms/available" \ - -H "x-api-key: your_api_key_here" -``` - -**Example Response**: -```json -{ - "status": "success", - "swarms": ["financial-analysis-swarm", "market-sentiment-swarm"] -} -``` - -------- - - -#### Get API Logs - -Retrieve logs of API requests made with your API key. - -**Endpoint**: `/v1/swarm/logs` -**Method**: GET -**Rate Limit**: 100 requests per 60 seconds - -**Example Request**: -```bash -curl -X GET "https://api.swarms.world/v1/swarm/logs" \ - -H "x-api-key: your_api_key_here" -``` - -**Example Response**: -```json -{ - "status": "success", - "count": 25, - "logs": [ - { - "id": "log_id_12345", - "api_key": "api_key_redacted", - "data": { - "action": "run_swarm", - "swarm_name": "financial-analysis-swarm", - "task": "Analyze quarterly financials...", - "timestamp": "2025-03-04T14:22:45Z" - } - }, - ... - ] -} -``` - - - -## Individual Agent Endpoints - -### Run Single Agent - ### AgentCompletion Model The `AgentCompletion` model defines the configuration for running a single agent task. @@ -596,31 +967,158 @@ Execute multiple agent tasks in parallel. **Maximum Batch Size**: 10 requests **Input** A list of `AgentCompeletion` inputs -**Request Body**: -```json -[ - { - "agent_config": { - "agent_name": "Market Analyst", - "description": "Expert in market analysis", - "system_prompt": "You are a financial market analyst.", - "model_name": "gpt-4o", - "temperature": 0.3 - }, - "task": "Analyze the current market trends in AI technology sector" - }, - { - "agent_config": { - "agent_name": "Technical Writer", - "description": "Specialized in technical documentation", - "system_prompt": "You are a technical documentation expert.", - "model_name": "gpt-4o", - "temperature": 0.7 - }, - "task": "Create a technical guide for implementing OAuth2 authentication" - } -] -``` +=== "Shell (curl)" + ```bash + curl -X POST "https://api.swarms.world/v1/agent/batch/completions" \ + -H "x-api-key: your_api_key_here" \ + -H "Content-Type: application/json" \ + -d '[ + { + "agent_config": { + "agent_name": "Market Analyst", + "description": "Expert in market analysis", + "system_prompt": "You are a financial market analyst.", + "model_name": "gpt-4o", + "temperature": 0.3 + }, + "task": "Analyze the current market trends in AI technology sector" + }, + { + "agent_config": { + "agent_name": "Technical Writer", + "description": "Specialized in technical documentation", + "system_prompt": "You are a technical documentation expert.", + "model_name": "gpt-4o", + "temperature": 0.7 + }, + "task": "Create a technical guide for implementing OAuth2 authentication" + } + ]' + ``` + +=== "Python (requests)" + ```python + import requests + import json + + API_BASE_URL = "https://api.swarms.world" + API_KEY = "your_api_key_here" + + headers = { + "x-api-key": API_KEY, + "Content-Type": "application/json" + } + + batch_agents = [ + { + "agent_config": { + "agent_name": "Market Analyst", + "description": "Expert in market analysis", + "system_prompt": "You are a financial market analyst.", + "model_name": "gpt-4o", + "temperature": 0.3 + }, + "task": "Analyze the current market trends in AI technology sector" + }, + { + "agent_config": { + "agent_name": "Technical Writer", + "description": "Specialized in technical documentation", + "system_prompt": "You are a technical documentation expert.", + "model_name": "gpt-4o", + "temperature": 0.7 + }, + "task": "Create a technical guide for implementing OAuth2 authentication" + } + ] + + response = requests.post( + f"{API_BASE_URL}/v1/agent/batch/completions", + headers=headers, + json=batch_agents + ) + + if response.status_code == 200: + result = response.json() + print(f"Batch completed with {result['total_requests']} agents") + print(f"Execution time: {result['execution_time']} seconds") + print("\nResults:") + for i, agent_result in enumerate(result['results']): + print(f" Agent {i+1}: {agent_result['name']} - {agent_result['success']}") + else: + print(f"Error: {response.status_code} - {response.text}") + ``` + +=== "TypeScript (fetch)" + ```typescript + interface AgentConfig { + agent_name: string; + description: string; + system_prompt: string; + model_name: string; + temperature: number; + } + + interface AgentCompletion { + agent_config: AgentConfig; + task: string; + } + + const API_BASE_URL = "https://api.swarms.world"; + const API_KEY = "your_api_key_here"; + + async function runBatchAgents(): Promise { + const batchAgents: AgentCompletion[] = [ + { + agent_config: { + agent_name: "Market Analyst", + description: "Expert in market analysis", + system_prompt: "You are a financial market analyst.", + model_name: "gpt-4o", + temperature: 0.3 + }, + task: "Analyze the current market trends in AI technology sector" + }, + { + agent_config: { + agent_name: "Technical Writer", + description: "Specialized in technical documentation", + system_prompt: "You are a technical documentation expert.", + model_name: "gpt-4o", + temperature: 0.7 + }, + task: "Create a technical guide for implementing OAuth2 authentication" + } + ]; + + try { + const response = await fetch(`${API_BASE_URL}/v1/agent/batch/completions`, { + method: 'POST', + headers: { + 'x-api-key': API_KEY, + 'Content-Type': 'application/json' + }, + body: JSON.stringify(batchAgents) + }); + + if (response.ok) { + const result = await response.json(); + console.log(`Batch completed with ${result.total_requests} agents`); + console.log(`Execution time: ${result.execution_time} seconds`); + console.log("\nResults:"); + result.results.forEach((agentResult: any, index: number) => { + console.log(` Agent ${index + 1}: ${agentResult.name} - ${agentResult.success}`); + }); + } else { + console.error(`Error: ${response.status} - ${await response.text()}`); + } + } catch (error) { + console.error("Request failed:", error); + } + } + + runBatchAgents(); + ``` **Response**: ```json @@ -660,394 +1158,10 @@ Execute multiple agent tasks in parallel. } ``` - ----- ## Production Examples -### Python Examples - -#### Financial Risk Assessment (Python) - -This example demonstrates creating a swarm for comprehensive financial risk assessment. - -```python -import requests -import json -from datetime import datetime, timedelta - -# API Configuration -API_BASE_URL = "https://api.swarms.world" -API_KEY = "your_api_key_here" -HEADERS = { - "x-api-key": API_KEY, - "Content-Type": "application/json" -} - -def financial_risk_assessment(company_data, market_conditions, risk_tolerance): - """ - Creates and runs a swarm to perform comprehensive financial risk assessment. - - Args: - company_data (str): Description or data about the company - market_conditions (str): Current market conditions - risk_tolerance (str): Risk tolerance level (e.g., "conservative", "moderate", "aggressive") - - Returns: - dict: Risk assessment results - """ - # Prepare the task description with all relevant information - task = f""" - Perform a comprehensive financial risk assessment with the following data: - - COMPANY DATA: - {company_data} - - MARKET CONDITIONS: - {market_conditions} - - RISK TOLERANCE: - {risk_tolerance} - - Analyze all potential risk factors including market risks, credit risks, - operational risks, and regulatory compliance risks. Quantify each risk factor - on a scale of 1-10 and provide specific mitigation strategies. - - Return a detailed report with executive summary, risk scores, detailed analysis, - and actionable recommendations. - """ - - # Define specialized financial agents - financial_analysts = [ - { - "agent_name": "MarketAnalyst", - "description": "Specialist in market risk assessment and forecasting", - "system_prompt": "You are an expert market analyst with deep expertise in financial markets. Analyze market conditions, trends, and external factors that could impact financial performance. Provide quantitative and qualitative analysis of market-related risks.", - "model_name": "gpt-4o", - "temperature": 0.3, - "role": "analyst", - "max_loops": 1 - }, - { - "agent_name": "CreditRiskAnalyst", - "description": "Expert in assessing credit and counterparty risks", - "system_prompt": "You are a specialist in credit risk analysis with experience in banking and financial institutions. Evaluate creditworthiness, default probabilities, and counterparty exposures. Provide detailed analysis of credit-related risks and recommended safeguards.", - "model_name": "gpt-4o", - "temperature": 0.2, - "role": "analyst", - "max_loops": 1 - }, - { - "agent_name": "RegulatoryExpert", - "description": "Expert in financial regulations and compliance", - "system_prompt": "You are a regulatory compliance expert with deep knowledge of financial regulations. Identify potential regulatory risks, compliance issues, and governance concerns. Recommend compliance measures and risk mitigation strategies.", - "model_name": "gpt-4o", - "temperature": 0.2, - "role": "analyst", - "max_loops": 1 - }, - { - "agent_name": "RiskSynthesizer", - "description": "Integrates all risk factors into comprehensive assessment", - "system_prompt": "You are a senior risk management professional responsible for synthesizing multiple risk analyses into a coherent, comprehensive risk assessment. Integrate analyses from various domains, resolve conflicting assessments, and provide a holistic view of risk exposure with prioritized recommendations.", - "model_name": "gpt-4o", - "temperature": 0.4, - "role": "manager", - "max_loops": 1 - } - ] - - # Create the swarm specification - swarm_spec = { - "name": "financial-risk-assessment", - "description": "Comprehensive financial risk assessment swarm", - "agents": financial_analysts, - "max_loops": 2, - "swarm_type": "HiearchicalSwarm", - "task": task, - "return_history": True - } - - # Execute the swarm - response = requests.post( - f"{API_BASE_URL}/v1/swarm/completions", - headers=HEADERS, - json=swarm_spec - ) - - if response.status_code == 200: - result = response.json() - print(f"Risk assessment completed. Cost: ${result['metadata']['billing_info']['total_cost']}") - return result["output"] - else: - print(f"Error: {response.status_code} - {response.text}") - return None - -# Usage example -if __name__ == "__main__": - company_data = """ - XYZ Financial Services - Annual Revenue: $125M - Current Debt: $45M - Credit Rating: BBB+ - Primary Markets: North America, Europe - Key Products: Asset management, retirement planning, commercial lending - Recent Events: Expanding into Asian markets, New CEO appointed 6 months ago - """ - - market_conditions = """ - Current interest rates rising (Federal Reserve increased rates by 0.25% last month) - Inflation at 3.2% (12-month outlook projects 3.5-4.0%) - Market volatility index (VIX) at 22.4 (elevated) - Regulatory environment: New financial reporting requirements taking effect next quarter - Sector performance: Financial services sector underperforming broader market by 2.7% - """ - - risk_tolerance = "moderate" - - result = financial_risk_assessment(company_data, market_conditions, risk_tolerance) - - if result: - # Process and use the risk assessment - print(json.dumps(result, indent=2)) - - # Optionally, schedule a follow-up assessment - tomorrow = datetime.utcnow() + timedelta(days=30) - schedule_spec = { - "name": "monthly-risk-update", - "description": "Monthly update to risk assessment", - "task": f"Update the risk assessment for XYZ Financial Services based on current market conditions. Previous assessment: {json.dumps(result)}", - "schedule": { - "scheduled_time": tomorrow.isoformat() + "Z", - "timezone": "UTC" - } - } - - schedule_response = requests.post( - f"{API_BASE_URL}/v1/swarm/schedule", - headers=HEADERS, - json=schedule_spec - ) - - if schedule_response.status_code == 200: - print("Follow-up assessment scheduled successfully") - print(schedule_response.json()) -``` - -#### Healthcare Patient Data Analysis (Python) - -This example demonstrates creating a swarm for analyzing patient health data and generating insights. - -```python -import requests -import json -import os -from datetime import datetime - -# API Configuration -API_BASE_URL = "https://api.swarms.world" -API_KEY = os.environ.get("SWARMS_API_KEY") -HEADERS = { - "x-api-key": API_KEY, - "Content-Type": "application/json" -} - -def analyze_patient_health_data(patient_data, medical_history, lab_results, treatment_goals): - """ - Creates and runs a swarm to analyze patient health data and generate insights. - - Args: - patient_data (str): Basic patient information - medical_history (str): Patient's medical history - lab_results (str): Recent laboratory results - treatment_goals (str): Treatment objectives - - Returns: - dict: Comprehensive health analysis and recommendations - """ - # Prepare the detailed task description - task = f""" - Perform a comprehensive analysis of the following patient health data: - - PATIENT INFORMATION: - {patient_data} - - MEDICAL HISTORY: - {medical_history} - - LABORATORY RESULTS: - {lab_results} - - TREATMENT GOALS: - {treatment_goals} - - Analyze all aspects of the patient's health status, identify potential concerns, - evaluate treatment effectiveness, and provide evidence-based recommendations for - optimizing care. Consider medication interactions, lifestyle factors, and preventive measures. - - Return a detailed clinical report with key findings, risk stratification, - prioritized recommendations, and suggested follow-up timeline. - """ - - # Create the swarm specification with auto-generated agents - # (letting the system create specialized medical experts) - swarm_spec = { - "name": "patient-health-analysis", - "description": "Comprehensive patient health data analysis", - "swarm_type": "AutoSwarmBuilder", - "task": task, - "max_loops": 3, - "return_history": True - } - - # Execute the swarm - try: - response = requests.post( - f"{API_BASE_URL}/v1/swarm/completions", - headers=HEADERS, - json=swarm_spec - ) - - response.raise_for_status() - result = response.json() - - # Log the execution metadata - execution_time = result["metadata"]["execution_time_seconds"] - cost = result["metadata"]["billing_info"]["total_cost"] - num_agents = result["metadata"]["num_agents"] - - print(f"Analysis completed in {execution_time:.2f} seconds") - print(f"Used {num_agents} specialized medical agents") - print(f"Total cost: ${cost:.4f}") - - # Return just the analysis results - return result["output"] - - except requests.exceptions.RequestException as e: - print(f"API request failed: {str(e)}") - if hasattr(e, 'response') and e.response: - print(f"Response: {e.response.text}") - return None - except Exception as e: - print(f"Error: {str(e)}") - return None - -# Usage example -if __name__ == "__main__": - # Sample patient data (would typically come from EHR system) - patient_data = """ - ID: PT-28456 - Age: 67 - Gender: Female - Height: 162 cm - Weight: 78 kg - Vitals: - - Blood Pressure: 142/88 mmHg - - Heart Rate: 76 bpm - - Respiratory Rate: 16/min - - Temperature: 37.1°C - - Oxygen Saturation: 97% - """ - - medical_history = """ - Diagnoses: - - Type 2 Diabetes Mellitus (diagnosed 12 years ago) - - Hypertension (diagnosed 8 years ago) - - Osteoarthritis (knees, diagnosed 5 years ago) - - Hyperlipidemia - - Surgical History: - - Cholecystectomy (15 years ago) - - Right knee arthroscopy (3 years ago) - - Medications: - - Metformin 1000mg BID - - Lisinopril 20mg daily - - Atorvastatin 40mg daily - - Aspirin 81mg daily - - Acetaminophen 500mg PRN for joint pain - - Allergies: - - Penicillin (rash) - - Sulfa drugs (hives) - - Family History: - - Father: MI at age 70, died at 76 - - Mother: Breast cancer at 68, Type 2 Diabetes, died at 82 - - Sister: Type 2 Diabetes, Hypertension - """ - - lab_results = """ - CBC (2 days ago): - - WBC: 7.2 x10^9/L (normal) - - RBC: 4.1 x10^12/L (low-normal) - - Hemoglobin: 12.8 g/dL (low-normal) - - Hematocrit: 38% (low-normal) - - Platelets: 245 x10^9/L (normal) - - Comprehensive Metabolic Panel: - - Glucose (fasting): 142 mg/dL (elevated) - - HbA1c: 7.8% (elevated) - - BUN: 22 mg/dL (normal) - - Creatinine: 1.1 mg/dL (normal) - - eGFR: 62 mL/min/1.73m² (mildly reduced) - - Sodium: 138 mEq/L (normal) - - Potassium: 4.2 mEq/L (normal) - - Chloride: 101 mEq/L (normal) - - Calcium: 9.4 mg/dL (normal) - - ALT: 32 U/L (normal) - - AST: 28 U/L (normal) - - Lipid Panel: - - Total Cholesterol: 198 mg/dL - - Triglycerides: 172 mg/dL (elevated) - - HDL: 42 mg/dL (low) - - LDL: 122 mg/dL (borderline elevated) - - Urinalysis: - - Microalbumin/Creatinine ratio: 45 mg/g (elevated) - """ - - treatment_goals = """ - Primary Goals: - - Improve glycemic control (target HbA1c < 7.0%) - - Blood pressure control (target < 130/80 mmHg) - - Lipid management (target LDL < 100 mg/dL) - - Renal protection (reduce microalbuminuria) - - Weight management (target BMI < 27) - - Pain management for osteoarthritis - - Maintain functional independence - - Patient Preferences: - - Prefers to minimize medication changes if possible - - Interested in dietary approaches - - Concerned about memory changes - - Limited exercise tolerance due to knee pain - """ - - result = analyze_patient_health_data(patient_data, medical_history, lab_results, treatment_goals) - - if result: - # Write the analysis to a report file - timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") - with open(f"patient_analysis_{timestamp}.json", "w") as f: - json.dump(result, f, indent=2) - - print(f"Analysis saved to patient_analysis_{timestamp}.json") - - # Display key findings - if "key_findings" in result: - print("\nKEY FINDINGS:") - for i, finding in enumerate(result["key_findings"]): - print(f" {i+1}. {finding}") - - # Display recommendations - if "recommendations" in result: - print("\nRECOMMENDATIONS:") - for i, rec in enumerate(result["recommendations"]): - print(f" {i+1}. {rec}") -``` - ## Error Handling The Swarms API follows standard HTTP status codes for error responses: @@ -1112,7 +1226,6 @@ Error responses include a detailed message explaining the issue: | Error Handling | Implement robust error handling and retries | | Logging | Log API responses for debugging and auditing | | Cost Monitoring | Monitor costs closely during development and testing | -| Scheduling | Use scheduled jobs for recurring tasks instead of polling | ### Cost Optimization diff --git a/multiple_image_processing.py b/multiple_image_processing.py index febb29fe..3d90f612 100644 --- a/multiple_image_processing.py +++ b/multiple_image_processing.py @@ -1,7 +1,4 @@ from swarms import Agent -from swarms.prompts.logistics import ( - Quality_Control_Agent_Prompt, -) # Image for analysis @@ -12,8 +9,8 @@ quality_control_agent = Agent( agent_name="Quality Control Agent", agent_description="A quality control agent that analyzes images and provides a detailed report on the quality of the product in the image.", model_name="claude-3-5-sonnet-20240620", - system_prompt=Quality_Control_Agent_Prompt, - multi_modal=True, + # system_prompt=Quality_Control_Agent_Prompt, + # multi_modal=True, max_loops=1, output_type="str-all-except-first", summarize_multiple_images=True, @@ -22,7 +19,7 @@ quality_control_agent = Agent( response = quality_control_agent.run( task="what is in the image?", - imgs=[factory_image, factory_image], + imgs=[factory_image, "burning_image.jpg"], ) print(response) diff --git a/swarms/structs/agent.py b/swarms/structs/agent.py index b138cef2..12d2306a 100644 --- a/swarms/structs/agent.py +++ b/swarms/structs/agent.py @@ -985,7 +985,7 @@ class Agent: self.short_memory.add(role=self.user_name, content=task) - if self.plan_enabled or self.planning_prompt is not None: + if self.plan_enabled is True: self.plan(task) # Set the loop count @@ -1360,10 +1360,15 @@ class Agent: # Get the current conversation history history = self.short_memory.get_str() + plan_prompt = f"Create a comprehensive step-by-step plan to complete the following task: \n\n {task}" + # Construct the planning prompt by combining history, planning prompt, and task - planning_prompt = ( - f"{history}\n\n{self.planning_prompt}\n\nTask: {task}" - ) + if exists(self.planning_prompt): + planning_prompt = f"{history}\n\n{self.planning_prompt}\n\nTask: {task}" + else: + planning_prompt = ( + f"{history}\n\n{plan_prompt}\n\nTask: {task}" + ) # Generate the plan using the LLM plan = self.llm.run(task=planning_prompt, *args, **kwargs) @@ -1371,9 +1376,6 @@ class Agent: # Store the generated plan in short-term memory self.short_memory.add(role=self.agent_name, content=plan) - logger.info( - f"Successfully created plan for task: {task[:50]}..." - ) return None except Exception as error: @@ -2501,6 +2503,7 @@ class Agent: task: Optional[Union[str, Any]] = None, img: Optional[str] = None, imgs: Optional[List[str]] = None, + correct_answer: Optional[str] = None, *args, **kwargs, ) -> Any: @@ -2534,6 +2537,14 @@ class Agent: output = self.run_multiple_images( task=task, imgs=imgs, *args, **kwargs ) + elif exists(correct_answer): + output = self.continuous_run_with_answer( + task=task, + img=img, + correct_answer=correct_answer, + *args, + **kwargs, + ) else: output = self._run( task=task, @@ -2909,7 +2920,7 @@ class Agent: self, task: str, imgs: List[str], *args, **kwargs ): """ - Run the agent with multiple images. + Run the agent with multiple images using concurrent processing. Args: task (str): The task to be performed on each image. @@ -2932,12 +2943,33 @@ class Agent: Raises: Exception: If an error occurs while processing any of the images. """ - outputs = [] - for img in imgs: - output = self.run(task=task, img=img, *args, **kwargs) - outputs.append(output) + # Calculate number of workers as 95% of available CPU cores + cpu_count = os.cpu_count() + max_workers = max(1, int(cpu_count * 0.95)) + + # Use ThreadPoolExecutor for concurrent processing + with ThreadPoolExecutor(max_workers=max_workers) as executor: + # Submit all image processing tasks + future_to_img = { + executor.submit( + self.run, task=task, img=img, *args, **kwargs + ): img + for img in imgs + } + + # Collect results in order + outputs = [] + for future in future_to_img: + try: + output = future.result() + outputs.append(output) + except Exception as e: + logger.error(f"Error processing image: {e}") + outputs.append( + None + ) # or raise the exception based on your preference - # Combine the outputs into a single string + # Combine the outputs into a single string if summarization is enabled if self.summarize_multiple_images is True: output = "\n".join(outputs) @@ -2959,3 +2991,58 @@ class Agent: outputs = self.run(task=prompt, *args, **kwargs) return outputs + + def continuous_run_with_answer( + self, + task: str, + img: Optional[str] = None, + correct_answer: str = None, + max_attempts: int = 10, + ): + """ + Run the agent with the task until the correct answer is provided. + + Args: + task (str): The task to be performed + correct_answer (str): The correct answer that must be found in the response + max_attempts (int): Maximum number of attempts before giving up (default: 10) + + Returns: + str: The response containing the correct answer + + Raises: + Exception: If max_attempts is reached without finding the correct answer + """ + attempts = 0 + + while attempts < max_attempts: + attempts += 1 + + if self.verbose: + logger.info( + f"Attempt {attempts}/{max_attempts} to find correct answer" + ) + + response = self._run(task=task, img=img) + + # Check if the correct answer is in the response (case-insensitive) + if correct_answer.lower() in response.lower(): + if self.verbose: + logger.info( + f"Correct answer found on attempt {attempts}" + ) + return response + else: + # Add feedback to help guide the agent + feedback = "Your previous response was incorrect. Think carefully about the question and ensure your response directly addresses what was asked." + self.short_memory.add(role="User", content=feedback) + + if self.verbose: + logger.info( + f"Correct answer not found. Expected: '{correct_answer}'" + ) + + # If we reach here, we've exceeded max_attempts + raise Exception( + f"Failed to find correct answer '{correct_answer}' after {max_attempts} attempts" + ) diff --git a/vision_tools.py b/vision_tools.py new file mode 100644 index 00000000..f0ec102e --- /dev/null +++ b/vision_tools.py @@ -0,0 +1,68 @@ +from swarms.structs import Agent +from swarms.prompts.logistics import ( + Quality_Control_Agent_Prompt, +) + + +# Image for analysis +factory_image = "image.jpg" + + +def security_analysis(danger_level: str) -> str: + """ + Analyzes the security danger level and returns an appropriate response. + + Args: + danger_level (str, optional): The level of danger to analyze. + Can be "low", "medium", "high", or None. Defaults to None. + + Returns: + str: A string describing the danger level assessment. + - "No danger level provided" if danger_level is None + - "No danger" if danger_level is "low" + - "Medium danger" if danger_level is "medium" + - "High danger" if danger_level is "high" + - "Unknown danger level" for any other value + """ + if danger_level is None: + return "No danger level provided" + + if danger_level == "low": + return "No danger" + + if danger_level == "medium": + return "Medium danger" + + if danger_level == "high": + return "High danger" + + return "Unknown danger level" + + +custom_system_prompt = f""" +{Quality_Control_Agent_Prompt} + +You have access to tools that can help you with your analysis. When you need to perform a security analysis, you MUST use the security_analysis function with an appropriate danger level (low, medium, or high) based on your observations. + +Always use the available tools when they are relevant to the task. If you determine there is any level of danger or security concern, call the security_analysis function with the appropriate danger level. +""" + +# Quality control agent +quality_control_agent = Agent( + agent_name="Quality Control Agent", + agent_description="A quality control agent that analyzes images and provides a detailed report on the quality of the product in the image.", + # model_name="anthropic/claude-3-opus-20240229", + model_name="gpt-4o-mini", + system_prompt=custom_system_prompt, + multi_modal=True, + max_loops=1, + output_type="str-all-except-first", + # tools_list_dictionary=[schema], + tools=[security_analysis], +) + + +response = quality_control_agent.run( + task="Analyze the image and then perform a security analysis. Based on what you see in the image, determine if there is a low, medium, or high danger level and call the security_analysis function with that danger level", + img=factory_image, +)