From 7e5974b05d0d677846ec185dc3e705113f13bb47 Mon Sep 17 00:00:00 2001 From: Kye Gomez Date: Fri, 14 Nov 2025 13:53:38 -0800 Subject: [PATCH] [x402 agent discovery docs] [HiearchicalSwarm Improvements] [Fix][Majority Voting issue] --- README.md | 11 + docs/examples/x402_discovery_query.md | 231 ++++++++++++++++++ docs/mkdocs.yml | 1 + .../agent_integration/x402_agent_buying.py | 50 ++++ .../agent_integration/x402_discovery_query.py | 231 ++++++++++++++++++ pyproject.toml | 2 +- swarms/prompts/visual_cot.py | 5 + swarms/structs/hiearchical_swarm.py | 3 +- swarms/structs/swarm_router.py | 5 +- 9 files changed, 534 insertions(+), 5 deletions(-) create mode 100644 docs/examples/x402_discovery_query.md create mode 100644 examples/guides/x402_examples/agent_integration/x402_agent_buying.py create mode 100644 examples/guides/x402_examples/agent_integration/x402_discovery_query.py diff --git a/README.md b/README.md index 29944494..fa70d1dd 100644 --- a/README.md +++ b/README.md @@ -58,6 +58,17 @@ Swarms delivers a comprehensive, enterprise-grade multi-agent infrastructure pla | 🛠️ **Developer Experience** | • Intuitive Enterprise API
• Comprehensive Documentation
• Active Enterprise Community
• CLI & SDK Tools
• IDE Integration Support
• Code Generation Templates | • Accelerated Development Cycles
• Reduced Learning Curve
• Expert Community Support
• Rapid Deployment Capabilities
• Enhanced Developer Productivity
• Standardized Development Patterns | +## 🔌 Supported Protocols & Integrations + +Swarms seamlessly integrates with industry-standard protocols, enabling powerful capabilities for tool integration, payment processing, and distributed agent orchestration. + +| Protocol | Description | Use Cases | Documentation | +|----------|-------------|-----------|---------------| +| **[MCP (Model Context Protocol)](https://docs.swarms.world/en/latest/swarms/examples/multi_mcp_agent/)** | Standardized protocol for AI agents to interact with external tools and services through MCP servers. Enables dynamic tool discovery and execution. | • Tool integration
• Multi-server connections
• External API access
• Database connectivity | [MCP Integration Guide](https://docs.swarms.world/en/latest/swarms/examples/multi_mcp_agent/) | +| **[X402](https://docs.swarms.world/en/latest/examples/x402_payment_integration/)** | Cryptocurrency payment protocol for API endpoints. Enables monetization of agents with pay-per-use models. | • Agent monetization
• Payment gate protection
• Crypto payments
• Pay-per-use services | [X402 Quickstart](https://docs.swarms.world/en/latest/examples/x402_payment_integration/) | +| **[AOP (Agent Orchestration Protocol)](https://docs.swarms.world/en/latest/examples/aop_medical/)** | Framework for deploying and managing agents as distributed services. Enables agent discovery, management, and execution through standardized protocols. | • Distributed agent deployment
• Agent discovery
• Service orchestration
• Scalable multi-agent systems | [AOP Reference](https://docs.swarms.world/en/latest/swarms/structs/aop/) | + + ## Install 💻 ### Using pip diff --git a/docs/examples/x402_discovery_query.md b/docs/examples/x402_discovery_query.md new file mode 100644 index 00000000..f6e4abd9 --- /dev/null +++ b/docs/examples/x402_discovery_query.md @@ -0,0 +1,231 @@ +# X402 Discovery Query Agent + +This example demonstrates how to create a Swarms agent that can search and query services from the X402 bazaar using the Coinbase CDP API. The agent can discover available services, filter them by price, and provide summaries of the results. + +## Overview + +The X402 Discovery Query Agent enables you to: + +| Feature | Description | +|---------|-------------| +| Query X402 services | Search the X402 bazaar for available services | +| Filter by price | Find services within your budget | +| Summarize results | Get AI-powered summaries of discovered services | +| Pagination support | Handle large result sets efficiently | + +## Prerequisites + +Before you begin, ensure you have: + +- Python 3.10 or higher +- API keys for your AI model provider (e.g., Anthropic Claude) +- `httpx` library for async HTTP requests + +## Installation + +Install the required dependencies: + +```bash +pip install swarms httpx +``` + +## Code Example + +Here's the complete implementation of the X402 Discovery Query Agent: + +```python +import asyncio +from typing import List, Optional, Dict, Any +from swarms import Agent +import httpx + + +async def query_x402_services( + limit: Optional[int] = None, + max_price: Optional[int] = None, + offset: int = 0, + base_url: str = "https://api.cdp.coinbase.com", +) -> Dict[str, Any]: + """ + Query x402 discovery services from the Coinbase CDP API. + + Args: + limit: Optional maximum number of services to return. If None, returns all available. + max_price: Optional maximum price in atomic units to filter by. Only services with + maxAmountRequired <= max_price will be included. + offset: Pagination offset for the API request. Defaults to 0. + base_url: Base URL for the API. Defaults to Coinbase CDP API. + + Returns: + Dict containing the API response with 'items' list and pagination info. + + Raises: + httpx.HTTPError: If the HTTP request fails. + httpx.RequestError: If there's a network error. + """ + url = f"{base_url}/platform/v2/x402/discovery/resources" + params = {"offset": offset} + + # If both limit and max_price are specified, fetch more services to account for filtering + api_limit = limit + if limit is not None and max_price is not None: + # Fetch 5x the limit to account for services that might be filtered out + api_limit = limit * 5 + + if api_limit is not None: + params["limit"] = api_limit + + async with httpx.AsyncClient(timeout=30.0) as client: + response = await client.get(url, params=params) + response.raise_for_status() + data = response.json() + + # Filter by price if max_price is specified + if max_price is not None and "items" in data: + filtered_items = [] + for item in data.get("items", []): + # Check if any payment option in 'accepts' has maxAmountRequired <= max_price + accepts = item.get("accepts", []) + for accept in accepts: + max_amount_str = accept.get("maxAmountRequired", "") + if max_amount_str: + try: + max_amount = int(max_amount_str) + if max_amount <= max_price: + filtered_items.append(item) + break # Only add item once if any payment option matches + except (ValueError, TypeError): + continue + + # Apply limit to filtered results if specified + if limit is not None: + filtered_items = filtered_items[:limit] + + data["items"] = filtered_items + # Update pagination total if we filtered + if "pagination" in data: + data["pagination"]["total"] = len(filtered_items) + + return data + + +def get_x402_services_sync( + limit: Optional[int] = None, + max_price: Optional[int] = None, + offset: int = 0, +) -> str: + """ + Synchronous wrapper for get_x402_services that returns a formatted string. + + Args: + limit: Optional maximum number of services to return. + max_price: Optional maximum price in atomic units to filter by. + offset: Pagination offset for the API request. Defaults to 0. + + Returns: + JSON-formatted string of service dictionaries matching the criteria. + """ + async def get_x402_services(): + result = await query_x402_services( + limit=limit, max_price=max_price, offset=offset + ) + return result.get("items", []) + + services = asyncio.run(get_x402_services()) + return str(services) + + +# Initialize the agent with the discovery tool +agent = Agent( + agent_name="X402-Discovery-Agent", + agent_description="A agent that queries the x402 discovery services from the Coinbase CDP API.", + model_name="claude-haiku-4-5", + dynamic_temperature_enabled=True, + max_loops=1, + dynamic_context_window=True, + tools=[get_x402_services_sync], + top_p=None, + temperature=None, + tool_call_summary=True, +) + +if __name__ == "__main__": + # Run the agent + out = agent.run( + task="Summarize the first 10 services under 100000 atomic units (e.g., $0.10 USDC)" + ) + print(out) +``` + +## Usage + +### Basic Query + +Query all available services: + +```python +result = await query_x402_services() +print(f"Found {len(result['items'])} services") +``` + +### Filtered Query + +Get services within a specific price range: + +```python +# Get first 10 services under 100000 atomic units ($0.10 USDC with 6 decimals) +services = await get_x402_services(limit=10, max_price=100000) +for service in services: + print(service["resource"]) +``` + +### Using the Agent + +Run the agent to get AI-powered summaries: + +```python +# The agent will automatically call the tool and provide a summary +out = agent.run( + task="Find and summarize 5 affordable services under 50000 atomic units" +) +print(out) +``` + +## Understanding Price Units + +X402 services use atomic units for pricing. For example: + +- **USDC** typically uses 6 decimals +- 100,000 atomic units = $0.10 USDC +- 1,000,000 atomic units = $1.00 USDC + +Always check the `accepts` array in each service to understand the payment options and their price requirements. + +## API Response Structure + +Each service in the response contains: + +- `resource`: The service endpoint or resource identifier +- `accepts`: Array of payment options with `maxAmountRequired` values +- Additional metadata about the service + +## Error Handling + +The functions handle various error cases: + +- Network errors are raised as `httpx.RequestError` +- HTTP errors are raised as `httpx.HTTPError` +- Invalid price values are silently skipped during filtering + +## Next Steps + +1. Customize the agent's system prompt for specific use cases +2. Add additional filtering criteria (e.g., by service type) +3. Implement caching for frequently accessed services +4. Create a web interface for browsing services +5. Integrate with payment processing to actually use discovered services + +## Related Documentation + +- [X402 Payment Integration](x402_payment_integration.md) - Learn how to monetize your agents with X402 +- [Agent Tools Reference](../swarms/tools/tools_examples.md) - Understand how to create and use tools with agents diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index b2b95c8d..f5f2c81c 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -437,6 +437,7 @@ nav: - X402: - x402 Quickstart Example: "examples/x402_payment_integration.md" + - X402 Discovery Query Agent: "examples/x402_discovery_query.md" - Swarms Cloud API: diff --git a/examples/guides/x402_examples/agent_integration/x402_agent_buying.py b/examples/guides/x402_examples/agent_integration/x402_agent_buying.py new file mode 100644 index 00000000..e61f4466 --- /dev/null +++ b/examples/guides/x402_examples/agent_integration/x402_agent_buying.py @@ -0,0 +1,50 @@ +from x402.client import X402Client +from eth_account import Account +from x402.clients.httpx import x402HttpxClient + + +import os +from dotenv import load_dotenv + +load_dotenv() + + +async def buy_x402_service( + base_url: str = None, + endpoint: str = None +): + """ + Purchase a service from the X402 bazaar using the provided affordable_service details. + + This function sets up an X402 client with the user's private key, connects to the service provider, + and executes a GET request to the service's endpoint as part of the buying process. + + Args: + affordable_service (dict): A dictionary containing information about the target service. + base_url (str, optional): The base URL of the service provider. Defaults to None. + endpoint (str, optional): The specific API endpoint to interact with. Defaults to None. + + Returns: + response (httpx.Response): The response object returned by the GET request to the service endpoint. + + Example: + ```python + affordable_service = {"id": "service123", "price": 90000} + response = await buy_x402_service( + affordable_service, + base_url="https://api.cdp.coinbase.com", + endpoint="/x402/v1/bazaar/services/service123" + ) + print(await response.aread()) + ``` + """ + key = os.getenv('X402_PRIVATE_KEY') + + # Set up your payment account from private key + account = Account.from_key(key) + + async with x402HttpxClient(account=account, base_url=base_url) as client: + response = await client.get(endpoint) + print(await response.aread()) + + return response \ No newline at end of file diff --git a/examples/guides/x402_examples/agent_integration/x402_discovery_query.py b/examples/guides/x402_examples/agent_integration/x402_discovery_query.py new file mode 100644 index 00000000..c9424172 --- /dev/null +++ b/examples/guides/x402_examples/agent_integration/x402_discovery_query.py @@ -0,0 +1,231 @@ +import asyncio +from typing import List, Optional, Dict, Any +from swarms import Agent +import httpx + + + +async def query_x402_services( + limit: Optional[int] = None, + max_price: Optional[int] = None, + offset: int = 0, + base_url: str = "https://api.cdp.coinbase.com", +) -> Dict[str, Any]: + """ + Query x402 discovery services from the Coinbase CDP API. + + Args: + limit: Optional maximum number of services to return. If None, returns all available. + max_price: Optional maximum price in atomic units to filter by. Only services with + maxAmountRequired <= max_price will be included. + offset: Pagination offset for the API request. Defaults to 0. + base_url: Base URL for the API. Defaults to Coinbase CDP API. + + Returns: + Dict containing the API response with 'items' list and pagination info. + + Raises: + httpx.HTTPError: If the HTTP request fails. + httpx.RequestError: If there's a network error. + + Example: + ```python + # Get all services + result = await query_x402_services() + print(f"Found {len(result['items'])} services") + + # Get first 10 services under 100000 atomic units + result = await query_x402_services(limit=10, max_price=100000) + ``` + """ + url = f"{base_url}/platform/v2/x402/discovery/resources" + params = {"offset": offset} + + # If both limit and max_price are specified, fetch more services to account for filtering + # This ensures we can return the requested number after filtering by price + api_limit = limit + if limit is not None and max_price is not None: + # Fetch 5x the limit to account for services that might be filtered out + api_limit = limit * 5 + + if api_limit is not None: + params["limit"] = api_limit + + async with httpx.AsyncClient(timeout=30.0) as client: + response = await client.get(url, params=params) + response.raise_for_status() + data = response.json() + + # Filter by price if max_price is specified + if max_price is not None and "items" in data: + filtered_items = [] + for item in data.get("items", []): + # Check if any payment option in 'accepts' has maxAmountRequired <= max_price + accepts = item.get("accepts", []) + for accept in accepts: + max_amount_str = accept.get("maxAmountRequired", "") + if max_amount_str: + try: + max_amount = int(max_amount_str) + if max_amount <= max_price: + filtered_items.append(item) + break # Only add item once if any payment option matches + except (ValueError, TypeError): + continue + + # Apply limit to filtered results if specified + if limit is not None: + filtered_items = filtered_items[:limit] + + data["items"] = filtered_items + # Update pagination total if we filtered + if "pagination" in data: + data["pagination"]["total"] = len(filtered_items) + + return data + + +def filter_services_by_price( + services: List[Dict[str, Any]], max_price: int +) -> List[Dict[str, Any]]: + """ + Filter services by maximum price in atomic units. + + Args: + services: List of service dictionaries from the API. + max_price: Maximum price in atomic units. Only services with at least one + payment option where maxAmountRequired <= max_price will be included. + + Returns: + List of filtered service dictionaries. + + Example: + ```python + all_services = result["items"] + affordable = filter_services_by_price(all_services, max_price=100000) + ``` + """ + filtered = [] + for item in services: + accepts = item.get("accepts", []) + for accept in accepts: + max_amount_str = accept.get("maxAmountRequired", "") + if max_amount_str: + try: + max_amount = int(max_amount_str) + if max_amount <= max_price: + filtered.append(item) + break # Only add item once if any payment option matches + except (ValueError, TypeError): + continue + return filtered + + +def limit_services( + services: List[Dict[str, Any]], max_count: int +) -> List[Dict[str, Any]]: + """ + Limit the number of services returned. + + Args: + services: List of service dictionaries. + max_count: Maximum number of services to return. + + Returns: + List containing at most max_count services. + + Example: + ```python + all_services = result["items"] + limited = limit_services(all_services, max_count=10) + ``` + """ + return services[:max_count] + + +async def get_x402_services( + limit: Optional[int] = None, + max_price: Optional[int] = None, + offset: int = 0, +) -> List[Dict[str, Any]]: + """ + Get x402 services with optional filtering by count and price. + + This is a convenience function that queries the API and applies filters. + + Args: + limit: Optional maximum number of services to return. + max_price: Optional maximum price in atomic units to filter by. + offset: Pagination offset for the API request. Defaults to 0. + + Returns: + List of service dictionaries matching the criteria. + + Example: + ```python + # Get first 10 services under $0.10 USDC (100000 atomic units with 6 decimals) + services = await get_x402_services(limit=10, max_price=100000) + for service in services: + print(service["resource"]) + ``` + """ + result = await query_x402_services( + limit=limit, max_price=max_price, offset=offset + ) + + return result.get("items", []) + + +def get_x402_services_sync( + limit: Optional[int] = None, + max_price: Optional[int] = None, + offset: int = 0, +) -> str: + """ + Synchronous wrapper for get_x402_services that returns a formatted string. + + Args: + limit: Optional maximum number of services to return. + max_price: Optional maximum price in atomic units to filter by. + offset: Pagination offset for the API request. Defaults to 0. + + Returns: + JSON-formatted string of service dictionaries matching the criteria. + + Example: + ```python + # Get first 10 services under $0.10 USDC + services_str = get_x402_services_sync(limit=10, max_price=100000) + print(services_str) + ``` + """ + services = asyncio.run( + get_x402_services( + limit=limit, max_price=max_price, offset=offset + ) + ) + return str(services) + + + +agent = Agent( + agent_name="X402-Discovery-Agent", + agent_description="A agent that queries the x402 discovery services from the Coinbase CDP API.", + model_name="claude-haiku-4-5", + dynamic_temperature_enabled=True, + max_loops=1, + dynamic_context_window=True, + tools=[get_x402_services_sync], + top_p=None, + # temperature=0.0, + temperature=None, + tool_call_summary=True, +) + +if __name__ == "__main__": + + # Run the agent + out = agent.run( + task="Summarize the first 10 services under 100000 atomic units (e.g., $0.10 USDC)" + ) + print(out) \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index 25cd5911..c9f3627a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ build-backend = "poetry.core.masonry.api" [tool.poetry] name = "swarms" -version = "8.6.0" +version = "8.6.1" description = "Swarms - TGSC" license = "MIT" authors = ["Kye Gomez "] diff --git a/swarms/prompts/visual_cot.py b/swarms/prompts/visual_cot.py index f33c72e1..e6701642 100644 --- a/swarms/prompts/visual_cot.py +++ b/swarms/prompts/visual_cot.py @@ -1,3 +1,8 @@ +""" +A structured prompt template that guides models through step-by-step visual analysis, from observation to reflection. +Provides a systematic chain-of-thought approach for analyzing images, graphs, and visual puzzles with detailed reasoning and visual references. +""" + VISUAL_CHAIN_OF_THOUGHT = """ You, as the model, are presented with a visual problem. This could be an image containing various elements that you need to analyze, a graph that requires interpretation, or a visual puzzle. Your task is to examine the visual information carefully and describe your process of understanding and solving the problem. diff --git a/swarms/structs/hiearchical_swarm.py b/swarms/structs/hiearchical_swarm.py index 66edb444..1501ccb6 100644 --- a/swarms/structs/hiearchical_swarm.py +++ b/swarms/structs/hiearchical_swarm.py @@ -944,7 +944,8 @@ class HierarchicalSwarm: if self.planning_enabled is True: self.director.tools_list_dictionary = None out = self.setup_director_with_planning( - task=self.conversation.get_str(), img=img + task=f"History: {self.conversation.get_str()} \n\n Task: {task}", + img=img, ) self.conversation.add( role=self.director.agent_name, content=out diff --git a/swarms/structs/swarm_router.py b/swarms/structs/swarm_router.py index 84256d8f..b5f3fd2c 100644 --- a/swarms/structs/swarm_router.py +++ b/swarms/structs/swarm_router.py @@ -423,7 +423,6 @@ class SwarmRouter: max_loops=self.max_loops, flow=self.rearrange_flow, output_type=self.output_type, - return_entire_history=self.return_entire_history, *args, **kwargs, ) @@ -474,7 +473,6 @@ class SwarmRouter: description=self.description, agents=self.agents, max_loops=self.max_loops, - return_all_history=self.return_entire_history, output_type=self.output_type, *args, **kwargs, @@ -499,7 +497,8 @@ class SwarmRouter: name=self.name, description=self.description, agents=self.agents, - consensus_agent=self.agents[-1], + max_loops=self.max_loops, + output_type=self.output_type, *args, **kwargs, )