Update to use toolsets and make them easy to create and configure

This commit is contained in:
Teknium 2025-09-10 00:43:55 -07:00
parent c7fa4447b8
commit 17608c1142
5 changed files with 1210 additions and 742 deletions

121
README.md
View file

@ -1,13 +1,65 @@
# Hermes Agent
An AI agent with advanced tool-calling capabilities, featuring a flexible toolsets system for organizing and managing tools.
## Features
- **Web Tools**: Search, extract content, and crawl websites
- **Terminal Tools**: Execute commands with interactive session support
- **Vision Tools**: Analyze images from URLs
- **Reasoning Tools**: Advanced multi-model reasoning (Mixture of Agents)
- **Creative Tools**: Generate images from text prompts
- **Toolsets System**: Organize tools into logical groups for different scenarios
## Setup ## Setup
``` ```bash
pip install -r requirements.txt pip install -r requirements.txt
git clone git@github.com:NousResearch/hecate.git git clone git@github.com:NousResearch/hecate.git
cd hecate cd hecate
pip install -e . pip install -e .
``` ```
## Run ## Toolsets System
The agent uses a toolsets system for organizing and managing tools. All tools must be part of a toolset to be accessible - individual tool selection is not supported. This ensures consistent and logical grouping of capabilities.
### Key Concepts
- **Toolsets**: Logical groups of tools for specific use cases (e.g., "research", "development", "debugging")
- **Composition**: Toolsets can include other toolsets for powerful combinations
- **Custom Toolsets**: Create your own toolsets at runtime or by editing `toolsets.py`
- **Toolset-Only Access**: Tools are only accessible through toolsets, not individually
### Available Toolsets
See `toolsets.py` for the complete list of predefined toolsets including:
- Basic toolsets (web, terminal, vision, creative, reasoning)
- Composite toolsets (research, development, analysis, etc.)
- Scenario-specific toolsets (debugging, documentation, API testing, etc.)
- Special toolsets (safe mode without terminal, minimal, offline)
### Using Toolsets
```bash
# Use a predefined toolset
python run_agent.py --enabled_toolsets=research --query "Find latest AI papers"
# Combine multiple toolsets
python run_agent.py --enabled_toolsets=web,vision --query "Analyze this website"
# Safe mode (no terminal access)
python run_agent.py --enabled_toolsets=safe --query "Help without running commands"
# List all available toolsets and tools
python run_agent.py --list_tools
``` ```
For detailed documentation on toolsets, see `TOOLSETS_README.md`.
## Basic Usage
### Default (all tools enabled)
```bash
python run_agent.py \ python run_agent.py \
--query "search up the latest docs on jit in python 3.13 and write me basic example that's not in their docs. profile its perf" \ --query "search up the latest docs on jit in python 3.13 and write me basic example that's not in their docs. profile its perf" \
--max_turns 20 \ --max_turns 20 \
@ -15,3 +67,68 @@ python run_agent.py \
--base_url https://api.anthropic.com/v1/ \ --base_url https://api.anthropic.com/v1/ \
--api_key $ANTHROPIC_API_KEY --api_key $ANTHROPIC_API_KEY
``` ```
### With specific toolset
```bash
python run_agent.py \
--query "Debug this Python error" \
--enabled_toolsets=debugging \
--model claude-sonnet-4-20250514 \
--api_key $ANTHROPIC_API_KEY
```
### Python API
```python
from run_agent import AIAgent
# Use a specific toolset
agent = AIAgent(
model="claude-opus-4-20250514",
enabled_toolsets=["research"]
)
response = agent.chat("Find information about quantum computing")
# Create custom toolset at runtime
from toolsets import create_custom_toolset
create_custom_toolset(
name="my_tools",
description="My custom toolkit",
tools=["web_search"],
includes=["terminal", "vision"]
)
agent = AIAgent(enabled_toolsets=["my_tools"])
```
## Command Line Arguments
- `--query`: The question or task for the agent
- `--model`: Model to use (default: claude-opus-4-20250514)
- `--api_key`: API key for authentication
- `--base_url`: API endpoint URL
- `--max_turns`: Maximum number of tool-calling iterations
- `--enabled_toolsets`: Comma-separated list of toolsets to enable
- `--disabled_toolsets`: Comma-separated list of toolsets to disable
- `--list_tools`: List all available toolsets and tools
- `--save_trajectories`: Save conversation trajectories to JSONL files
## Environment Variables
Set these environment variables to enable different tools:
- `FIRECRAWL_API_KEY`: For web tools (search, extract, crawl)
- `MORPH_API_KEY`: For terminal tools
- `NOUS_API_KEY`: For vision and reasoning tools
- `FAL_KEY`: For image generation tools
- `ANTHROPIC_API_KEY`: For the main agent model
## Documentation
- `TOOLSETS_README.md`: Comprehensive guide to the toolsets system
- `toolsets.py`: View and modify available toolsets
- `model_tools.py`: Core tool definitions and handlers
## Examples
See `TOOLSETS_README.md` for extensive examples of using different toolsets for various scenarios.

View file

@ -23,7 +23,7 @@ Usage:
web_tools = get_tool_definitions(enabled_toolsets=['web_tools']) web_tools = get_tool_definitions(enabled_toolsets=['web_tools'])
# Handle function calls from model # Handle function calls from model
result = handle_function_call("web_search", {"query": "Python", "limit": 3}) result = handle_function_call("web_search", {"query": "Python"})
""" """
import json import json
@ -35,6 +35,11 @@ from terminal_tool import terminal_tool, check_hecate_requirements, TERMINAL_TOO
from vision_tools import vision_analyze_tool, check_vision_requirements from vision_tools import vision_analyze_tool, check_vision_requirements
from mixture_of_agents_tool import mixture_of_agents_tool, check_moa_requirements from mixture_of_agents_tool import mixture_of_agents_tool, check_moa_requirements
from image_generation_tool import image_generate_tool, check_image_generation_requirements from image_generation_tool import image_generate_tool, check_image_generation_requirements
from toolsets import (
get_toolset, resolve_toolset, resolve_multiple_toolsets,
get_all_toolsets, get_toolset_names, validate_toolset,
get_toolset_info, print_toolset_tree
)
def get_web_tool_definitions() -> List[Dict[str, Any]]: def get_web_tool_definitions() -> List[Dict[str, Any]]:
""" """
@ -48,20 +53,13 @@ def get_web_tool_definitions() -> List[Dict[str, Any]]:
"type": "function", "type": "function",
"function": { "function": {
"name": "web_search", "name": "web_search",
"description": "Search the web for information on any topic. Returns relevant results with titles and URLs. Uses advanced search depth for comprehensive results.", "description": "Search the web for information on any topic. Returns up to 5 relevant results with titles and URLs. Uses advanced search depth for comprehensive results.",
"parameters": { "parameters": {
"type": "object", "type": "object",
"properties": { "properties": {
"query": { "query": {
"type": "string", "type": "string",
"description": "The search query to look up on the web" "description": "The search query to look up on the web"
},
"limit": {
"type": "integer",
"description": "Maximum number of results to return (default: 5, max: 10)",
"default": 5,
"minimum": 1,
"maximum": 10
} }
}, },
"required": ["query"] "required": ["query"]
@ -308,145 +306,146 @@ def get_toolset_for_tool(tool_name: str) -> str:
def get_tool_definitions( def get_tool_definitions(
enabled_tools: List[str] = None,
disabled_tools: List[str] = None,
enabled_toolsets: List[str] = None, enabled_toolsets: List[str] = None,
disabled_toolsets: List[str] = None disabled_toolsets: List[str] = None
) -> List[Dict[str, Any]]: ) -> List[Dict[str, Any]]:
""" """
Get tool definitions for model API calls with optional filtering. Get tool definitions for model API calls with toolset-based filtering.
This function aggregates tool definitions from all available toolsets This function aggregates tool definitions from available toolsets.
and applies filtering based on the provided parameters. All tools must be part of a toolset to be accessible. Individual tool
selection is not supported - use toolsets to organize and select tools.
Filter Priority (higher priority overrides lower):
1. enabled_tools (highest priority - only these tools, overrides everything)
2. disabled_tools (applied after toolset filtering)
3. enabled_toolsets (only tools from these toolsets)
4. disabled_toolsets (exclude tools from these toolsets)
Args: Args:
enabled_tools (List[str]): Only include these specific tools. If provided, enabled_toolsets (List[str]): Only include tools from these toolsets.
ONLY these tools will be included (overrides all other filters) If None, all available tools are included.
disabled_tools (List[str]): Exclude these specific tools (applied after toolset filtering) disabled_toolsets (List[str]): Exclude tools from these toolsets.
enabled_toolsets (List[str]): Only include tools from these toolsets Applied only if enabled_toolsets is None.
disabled_toolsets (List[str]): Exclude tools from these toolsets
Returns: Returns:
List[Dict]: Filtered list of tool definitions List[Dict]: Filtered list of tool definitions
Examples: Examples:
# Only web tools # Use predefined toolsets
tools = get_tool_definitions(enabled_toolsets=["web_tools"]) tools = get_tool_definitions(enabled_toolsets=["research"])
tools = get_tool_definitions(enabled_toolsets=["development"])
# All tools except terminal # Combine multiple toolsets
tools = get_tool_definitions(disabled_tools=["terminal"]) tools = get_tool_definitions(enabled_toolsets=["web", "vision"])
# Only specific tools (overrides toolset filters) # All tools except those in terminal toolset
tools = get_tool_definitions(enabled_tools=["web_search", "web_extract"]) tools = get_tool_definitions(disabled_toolsets=["terminal"])
# Conflicting filters (enabled_tools wins) # Default - all available tools
tools = get_tool_definitions(enabled_toolsets=["web_tools"], enabled_tools=["terminal"]) tools = get_tool_definitions()
# Result: Only terminal tool (enabled_tools overrides enabled_toolsets)
""" """
# Detect and warn about potential conflicts # Collect all available tool definitions
conflicts_detected = False all_available_tools_map = {}
if enabled_tools and (enabled_toolsets or disabled_toolsets or disabled_tools): # Map tool names to their definitions
print("⚠️ enabled_tools overrides all other filters") if check_firecrawl_api_key():
conflicts_detected = True for tool in get_web_tool_definitions():
all_available_tools_map[tool["function"]["name"]] = tool
if enabled_toolsets and disabled_toolsets: if check_hecate_requirements():
# Check for overlap for tool in get_terminal_tool_definitions():
enabled_set = set(enabled_toolsets) all_available_tools_map[tool["function"]["name"]] = tool
disabled_set = set(disabled_toolsets)
overlap = enabled_set & disabled_set
if overlap:
print(f"⚠️ Conflicting toolsets: {overlap} in both enabled and disabled")
print(f" → enabled_toolsets takes priority")
conflicts_detected = True
if enabled_tools and disabled_tools: if check_vision_requirements():
# Check for overlap for tool in get_vision_tool_definitions():
enabled_set = set(enabled_tools) all_available_tools_map[tool["function"]["name"]] = tool
disabled_set = set(disabled_tools)
overlap = enabled_set & disabled_set
if overlap:
print(f"⚠️ Conflicting tools: {overlap} in both enabled and disabled")
print(f" → enabled_tools takes priority")
conflicts_detected = True
all_tools = [] if check_moa_requirements():
for tool in get_moa_tool_definitions():
all_available_tools_map[tool["function"]["name"]] = tool
# Collect all available tools from each toolset if check_image_generation_requirements():
toolset_tools = { for tool in get_image_tool_definitions():
"web_tools": get_web_tool_definitions() if check_firecrawl_api_key() else [], all_available_tools_map[tool["function"]["name"]] = tool
"terminal_tools": get_terminal_tool_definitions() if check_hecate_requirements() else [],
"vision_tools": get_vision_tool_definitions() if check_vision_requirements() else [],
"moa_tools": get_moa_tool_definitions() if check_moa_requirements() else [],
"image_tools": get_image_tool_definitions() if check_image_generation_requirements() else []
}
# HIGHEST PRIORITY: enabled_tools (overrides everything) # Determine which tools to include based on toolsets
if enabled_tools: tools_to_include = set()
if conflicts_detected:
print(f"🎯 Using only enabled_tools: {enabled_tools}")
# Collect all available tools first
all_available_tools = []
for tools in toolset_tools.values():
all_available_tools.extend(tools)
# Only include specifically enabled tools
tool_names_to_include = set(enabled_tools)
filtered_tools = [
tool for tool in all_available_tools
if tool["function"]["name"] in tool_names_to_include
]
# Warn about requested tools that aren't available
found_tools = {tool["function"]["name"] for tool in filtered_tools}
missing_tools = tool_names_to_include - found_tools
if missing_tools:
print(f"⚠️ Requested tools not available: {missing_tools}")
return filtered_tools
# Apply toolset-level filtering first
if enabled_toolsets: if enabled_toolsets:
# Only include tools from enabled toolsets # Only include tools from enabled toolsets
for toolset_name in enabled_toolsets: for toolset_name in enabled_toolsets:
if toolset_name in toolset_tools: if validate_toolset(toolset_name):
all_tools.extend(toolset_tools[toolset_name]) resolved_tools = resolve_toolset(toolset_name)
tools_to_include.update(resolved_tools)
print(f"✅ Enabled toolset '{toolset_name}': {', '.join(resolved_tools) if resolved_tools else 'no tools'}")
else: else:
print(f"⚠️ Unknown toolset: {toolset_name}") # Try legacy compatibility
if toolset_name in ["web_tools", "terminal_tools", "vision_tools", "moa_tools", "image_tools"]:
# Map legacy names to new system
legacy_map = {
"web_tools": ["web_search", "web_extract", "web_crawl"],
"terminal_tools": ["terminal"],
"vision_tools": ["vision_analyze"],
"moa_tools": ["mixture_of_agents"],
"image_tools": ["image_generate"]
}
legacy_tools = legacy_map.get(toolset_name, [])
tools_to_include.update(legacy_tools)
print(f"✅ Enabled legacy toolset '{toolset_name}': {', '.join(legacy_tools)}")
else:
print(f"⚠️ Unknown toolset: {toolset_name}")
elif disabled_toolsets: elif disabled_toolsets:
# Include all tools except from disabled toolsets # Start with all tools from all toolsets, then remove disabled ones
for toolset_name, tools in toolset_tools.items(): # Note: Only tools that are part of toolsets are accessible
if toolset_name not in disabled_toolsets: # We need to get all tools from all defined toolsets
all_tools.extend(tools) from toolsets import get_all_toolsets
all_toolset_tools = set()
for toolset_name in get_all_toolsets():
resolved_tools = resolve_toolset(toolset_name)
all_toolset_tools.update(resolved_tools)
# Start with all tools from toolsets
tools_to_include = all_toolset_tools
# Remove tools from disabled toolsets
for toolset_name in disabled_toolsets:
if validate_toolset(toolset_name):
resolved_tools = resolve_toolset(toolset_name)
tools_to_include.difference_update(resolved_tools)
print(f"🚫 Disabled toolset '{toolset_name}': {', '.join(resolved_tools) if resolved_tools else 'no tools'}")
else:
# Try legacy compatibility
if toolset_name in ["web_tools", "terminal_tools", "vision_tools", "moa_tools", "image_tools"]:
legacy_map = {
"web_tools": ["web_search", "web_extract", "web_crawl"],
"terminal_tools": ["terminal"],
"vision_tools": ["vision_analyze"],
"moa_tools": ["mixture_of_agents"],
"image_tools": ["image_generate"]
}
legacy_tools = legacy_map.get(toolset_name, [])
tools_to_include.difference_update(legacy_tools)
print(f"🚫 Disabled legacy toolset '{toolset_name}': {', '.join(legacy_tools)}")
else:
print(f"⚠️ Unknown toolset: {toolset_name}")
else: else:
# Include all available tools # No filtering - include all tools from all defined toolsets
for tools in toolset_tools.values(): from toolsets import get_all_toolsets
all_tools.extend(tools) for toolset_name in get_all_toolsets():
resolved_tools = resolve_toolset(toolset_name)
tools_to_include.update(resolved_tools)
# Apply tool-level filtering (disabled_tools) # Build final tool list (only include tools that are available)
if disabled_tools: filtered_tools = []
tool_names_to_exclude = set(disabled_tools) for tool_name in tools_to_include:
original_tools = [tool["function"]["name"] for tool in all_tools] if tool_name in all_available_tools_map:
filtered_tools.append(all_available_tools_map[tool_name])
all_tools = [
tool for tool in all_tools
if tool["function"]["name"] not in tool_names_to_exclude
]
# Show what was actually filtered out
remaining_tools = {tool["function"]["name"] for tool in all_tools}
actually_excluded = set(original_tools) & tool_names_to_exclude
if actually_excluded:
print(f"🚫 Excluded tools: {actually_excluded}")
return all_tools # Sort tools for consistent ordering
filtered_tools.sort(key=lambda t: t["function"]["name"])
if filtered_tools:
tool_names = [t["function"]["name"] for t in filtered_tools]
print(f"🛠️ Final tool selection ({len(filtered_tools)} tools): {', '.join(tool_names)}")
else:
print("🛠️ No tools selected (all filtered out or unavailable)")
return filtered_tools
def handle_web_function_call(function_name: str, function_args: Dict[str, Any]) -> str: def handle_web_function_call(function_name: str, function_args: Dict[str, Any]) -> str:
""" """
@ -461,9 +460,8 @@ def handle_web_function_call(function_name: str, function_args: Dict[str, Any])
""" """
if function_name == "web_search": if function_name == "web_search":
query = function_args.get("query", "") query = function_args.get("query", "")
limit = function_args.get("limit", 5) # Always use fixed limit of 5
# Ensure limit is within bounds limit = 5
limit = max(1, min(10, limit))
return web_search_tool(query, limit) return web_search_tool(query, limit)
elif function_name == "web_extract": elif function_name == "web_extract":

File diff suppressed because it is too large Load diff

View file

@ -17,14 +17,14 @@ export WEB_TOOLS_DEBUG=true
python run_agent.py \ python run_agent.py \
--query "$PROMPT" \ --query "$PROMPT" \
--max_turns 30 \ --max_turns 30 \
# --model claude-sonnet-4-20250514 \ --model claude-sonnet-4-20250514 \
# --base_url https://api.anthropic.com/v1/ \ --base_url https://api.anthropic.com/v1/ \
--model hermes-4-70B \
--base_url http://bore.pub:8292/v1 \
--api_key $ANTHROPIC_API_KEY \ --api_key $ANTHROPIC_API_KEY \
--save_trajectories --save_trajectories \
#--enabled_toolsets=vision_tools --enabled_toolsets=web
# --model claude-sonnet-4-20250514 \
#
#Possible Toolsets: #Possible Toolsets:
#web_tools #web_tools
#vision_tools #vision_tools

326
toolsets.py Normal file
View file

@ -0,0 +1,326 @@
#!/usr/bin/env python3
"""
Toolsets Module
This module provides a flexible system for defining and managing tool aliases/toolsets.
Toolsets allow you to group tools together for specific scenarios and can be composed
from individual tools or other toolsets.
Features:
- Define custom toolsets with specific tools
- Compose toolsets from other toolsets
- Built-in common toolsets for typical use cases
- Easy extension for new toolsets
- Support for dynamic toolset resolution
Usage:
from toolsets import get_toolset, resolve_toolset, get_all_toolsets
# Get tools for a specific toolset
tools = get_toolset("research")
# Resolve a toolset to get all tool names (including from composed toolsets)
all_tools = resolve_toolset("full_stack")
"""
from typing import List, Dict, Any, Set, Optional
import json
# Core toolset definitions
# These can include individual tools or reference other toolsets
TOOLSETS = {
# Basic toolsets - individual tool categories
"web": {
"description": "Web research and content extraction tools",
"tools": ["web_search", "web_extract", "web_crawl"],
"includes": [] # No other toolsets included
},
"vision": {
"description": "Image analysis and vision tools",
"tools": ["vision_analyze"],
"includes": []
},
"image_gen": {
"description": "Creative generation tools (images)",
"tools": ["image_generate"],
"includes": []
},
"terminal": {
"description": "Terminal/command execution tools",
"tools": ["terminal"],
"includes": []
},
"moa": {
"description": "Advanced reasoning and problem-solving tools",
"tools": ["mixture_of_agents"],
"includes": []
},
# Scenario-specific toolsets
"debugging": {
"description": "Debugging and troubleshooting toolkit",
"tools": ["terminal"],
"includes": ["web"] # For searching error messages and solutions
},
"safe": {
"description": "Safe toolkit without terminal access",
"tools": ["mixture_of_agents"],
"includes": ["web", "vision", "creative"]
}
}
def get_toolset(name: str) -> Optional[Dict[str, Any]]:
"""
Get a toolset definition by name.
Args:
name (str): Name of the toolset
Returns:
Dict: Toolset definition with description, tools, and includes
None: If toolset not found
"""
# Return toolset definition
return TOOLSETS.get(name)
def resolve_toolset(name: str, visited: Set[str] = None) -> List[str]:
"""
Recursively resolve a toolset to get all tool names.
This function handles toolset composition by recursively resolving
included toolsets and combining all tools.
Args:
name (str): Name of the toolset to resolve
visited (Set[str]): Set of already visited toolsets (for cycle detection)
Returns:
List[str]: List of all tool names in the toolset
"""
if visited is None:
visited = set()
# Check for cycles
if name in visited:
print(f"⚠️ Circular dependency detected in toolset '{name}'")
return []
visited.add(name)
# Get toolset definition
toolset = TOOLSETS.get(name)
if not toolset:
return []
# Collect direct tools
tools = set(toolset.get("tools", []))
# Recursively resolve included toolsets
for included_name in toolset.get("includes", []):
included_tools = resolve_toolset(included_name, visited.copy())
tools.update(included_tools)
return list(tools)
def resolve_multiple_toolsets(toolset_names: List[str]) -> List[str]:
"""
Resolve multiple toolsets and combine their tools.
Args:
toolset_names (List[str]): List of toolset names to resolve
Returns:
List[str]: Combined list of all tool names (deduplicated)
"""
all_tools = set()
for name in toolset_names:
tools = resolve_toolset(name)
all_tools.update(tools)
return list(all_tools)
def get_all_toolsets() -> Dict[str, Dict[str, Any]]:
"""
Get all available toolsets with their definitions.
Returns:
Dict: All toolset definitions
"""
return TOOLSETS.copy()
def get_toolset_names() -> List[str]:
"""
Get names of all available toolsets (excluding aliases).
Returns:
List[str]: List of toolset names
"""
return list(TOOLSETS.keys())
def validate_toolset(name: str) -> bool:
"""
Check if a toolset name is valid.
Args:
name (str): Toolset name to validate
Returns:
bool: True if valid, False otherwise
"""
return name in TOOLSETS
def create_custom_toolset(
name: str,
description: str,
tools: List[str] = None,
includes: List[str] = None
) -> None:
"""
Create a custom toolset at runtime.
Args:
name (str): Name for the new toolset
description (str): Description of the toolset
tools (List[str]): Direct tools to include
includes (List[str]): Other toolsets to include
"""
TOOLSETS[name] = {
"description": description,
"tools": tools or [],
"includes": includes or []
}
def get_toolset_info(name: str) -> Dict[str, Any]:
"""
Get detailed information about a toolset including resolved tools.
Args:
name (str): Toolset name
Returns:
Dict: Detailed toolset information
"""
toolset = get_toolset(name)
if not toolset:
return None
resolved_tools = resolve_toolset(name)
return {
"name": name,
"description": toolset["description"],
"direct_tools": toolset["tools"],
"includes": toolset["includes"],
"resolved_tools": resolved_tools,
"tool_count": len(resolved_tools),
"is_composite": len(toolset["includes"]) > 0
}
def print_toolset_tree(name: str, indent: int = 0) -> None:
"""
Print a tree view of a toolset and its composition.
Args:
name (str): Toolset name
indent (int): Current indentation level
"""
prefix = " " * indent
toolset = get_toolset(name)
if not toolset:
print(f"{prefix}❌ Unknown toolset: {name}")
return
# Print toolset name and description
print(f"{prefix}📦 {name}: {toolset['description']}")
# Print direct tools
if toolset["tools"]:
print(f"{prefix} 🔧 Tools: {', '.join(toolset['tools'])}")
# Print included toolsets
if toolset["includes"]:
print(f"{prefix} 📂 Includes:")
for included in toolset["includes"]:
print_toolset_tree(included, indent + 2)
if __name__ == "__main__":
"""
Demo and testing of the toolsets system
"""
print("🎯 Toolsets System Demo")
print("=" * 60)
# Show all available toolsets
print("\n📦 Available Toolsets:")
print("-" * 40)
for name, toolset in get_all_toolsets().items():
info = get_toolset_info(name)
composite = "📂" if info["is_composite"] else "🔧"
print(f"{composite} {name:20} - {toolset['description']}")
print(f" Tools: {len(info['resolved_tools'])} total")
# Demo toolset resolution
print("\n🔍 Toolset Resolution Examples:")
print("-" * 40)
examples = ["research", "development", "full_stack", "minimal", "safe"]
for name in examples:
tools = resolve_toolset(name)
print(f"\n{name}:")
print(f" Resolved to {len(tools)} tools: {', '.join(sorted(tools))}")
# Show toolset composition tree
print("\n🌳 Toolset Composition Tree:")
print("-" * 40)
print("\nExample: 'content_creation' toolset:")
print_toolset_tree("content_creation")
print("\nExample: 'full_stack' toolset:")
print_toolset_tree("full_stack")
# Demo multiple toolset resolution
print("\n🔗 Multiple Toolset Resolution:")
print("-" * 40)
combined = resolve_multiple_toolsets(["minimal", "vision", "reasoning"])
print(f"Combining ['minimal', 'vision', 'reasoning']:")
print(f" Result: {', '.join(sorted(combined))}")
# Demo custom toolset creation
print("\n Custom Toolset Creation:")
print("-" * 40)
create_custom_toolset(
name="my_custom",
description="My custom toolset for specific tasks",
tools=["web_search"],
includes=["terminal", "vision"]
)
custom_info = get_toolset_info("my_custom")
print(f"Created 'my_custom' toolset:")
print(f" Description: {custom_info['description']}")
print(f" Resolved tools: {', '.join(custom_info['resolved_tools'])}")