/ toolsets.py
toolsets.py
1 #!/usr/bin/env python3 2 """ 3 Toolsets Module 4 5 This module provides a flexible system for defining and managing tool aliases/toolsets. 6 Toolsets allow you to group tools together for specific scenarios and can be composed 7 from individual tools or other toolsets. 8 9 Features: 10 - Define custom toolsets with specific tools 11 - Compose toolsets from other toolsets 12 - Built-in common toolsets for typical use cases 13 - Easy extension for new toolsets 14 - Support for dynamic toolset resolution 15 16 Usage: 17 from toolsets import get_toolset, resolve_toolset, get_all_toolsets 18 19 # Get tools for a specific toolset 20 tools = get_toolset("research") 21 22 # Resolve a toolset to get all tool names (including from composed toolsets) 23 all_tools = resolve_toolset("full_stack") 24 """ 25 26 from typing import List, Dict, Any, Set, Optional 27 28 29 # Shared tool list for CLI and all messaging platform toolsets. 30 # Edit this once to update all platforms simultaneously. 31 _HERMES_CORE_TOOLS = [ 32 # Web 33 "web_search", "web_extract", 34 # Terminal + process management 35 "terminal", "process", 36 # File manipulation 37 "read_file", "write_file", "patch", "search_files", 38 # Vision + image generation 39 "vision_analyze", "image_generate", 40 # Skills 41 "skills_list", "skill_view", "skill_manage", 42 # Browser automation 43 "browser_navigate", "browser_snapshot", "browser_click", 44 "browser_type", "browser_scroll", "browser_back", 45 "browser_press", "browser_get_images", 46 "browser_vision", "browser_console", "browser_cdp", "browser_dialog", 47 # Text-to-speech 48 "text_to_speech", 49 # Planning & memory 50 "todo", "memory", 51 # Session history search 52 "session_search", 53 # Clarifying questions 54 "clarify", 55 # Code execution + delegation 56 "execute_code", "delegate_task", 57 # Cronjob management 58 "cronjob", 59 # Cross-platform messaging (gated on gateway running via check_fn) 60 "send_message", 61 # Home Assistant smart home control (gated on HASS_TOKEN via check_fn) 62 "ha_list_entities", "ha_get_state", "ha_list_services", "ha_call_service", 63 # Kanban multi-agent coordination — only in schema when the agent is 64 # spawned as a kanban worker (HERMES_KANBAN_TASK env set), otherwise 65 # zero schema footprint. Gated via check_fn in tools/kanban_tools.py. 66 "kanban_show", "kanban_complete", "kanban_block", "kanban_heartbeat", 67 "kanban_comment", "kanban_create", "kanban_link", 68 ] 69 70 71 # Core toolset definitions 72 # These can include individual tools or reference other toolsets 73 TOOLSETS = { 74 # Basic toolsets - individual tool categories 75 "web": { 76 "description": "Web research and content extraction tools", 77 "tools": ["web_search", "web_extract"], 78 "includes": [] # No other toolsets included 79 }, 80 81 "search": { 82 "description": "Web search only (no content extraction/scraping)", 83 "tools": ["web_search"], 84 "includes": [] 85 }, 86 87 "vision": { 88 "description": "Image analysis and vision tools", 89 "tools": ["vision_analyze"], 90 "includes": [] 91 }, 92 93 "video": { 94 "description": "Video analysis and understanding tools (opt-in, not in default toolset)", 95 "tools": ["video_analyze"], 96 "includes": [] 97 }, 98 99 "image_gen": { 100 "description": "Creative generation tools (images)", 101 "tools": ["image_generate"], 102 "includes": [] 103 }, 104 105 "terminal": { 106 "description": "Terminal/command execution and process management tools", 107 "tools": ["terminal", "process"], 108 "includes": [] 109 }, 110 111 "moa": { 112 "description": "Advanced reasoning and problem-solving tools", 113 "tools": ["mixture_of_agents"], 114 "includes": [] 115 }, 116 117 "skills": { 118 "description": "Access, create, edit, and manage skill documents with specialized instructions and knowledge", 119 "tools": ["skills_list", "skill_view", "skill_manage"], 120 "includes": [] 121 }, 122 123 "browser": { 124 "description": "Browser automation for web interaction (navigate, click, type, scroll, iframes, hold-click) with web search for finding URLs", 125 "tools": [ 126 "browser_navigate", "browser_snapshot", "browser_click", 127 "browser_type", "browser_scroll", "browser_back", 128 "browser_press", "browser_get_images", 129 "browser_vision", "browser_console", "browser_cdp", 130 "browser_dialog", "web_search" 131 ], 132 "includes": [] 133 }, 134 135 "cronjob": { 136 "description": "Cronjob management tool - create, list, update, pause, resume, remove, and trigger scheduled tasks", 137 "tools": ["cronjob"], 138 "includes": [] 139 }, 140 141 "messaging": { 142 "description": "Cross-platform messaging: send messages to Telegram, Discord, Slack, SMS, etc.", 143 "tools": ["send_message"], 144 "includes": [] 145 }, 146 147 "rl": { 148 "description": "RL training tools for running reinforcement learning on Tinker-Atropos", 149 "tools": [ 150 "rl_list_environments", "rl_select_environment", 151 "rl_get_current_config", "rl_edit_config", 152 "rl_start_training", "rl_check_status", 153 "rl_stop_training", "rl_get_results", 154 "rl_list_runs", "rl_test_inference" 155 ], 156 "includes": [] 157 }, 158 159 "file": { 160 "description": "File manipulation tools: read, write, patch (with fuzzy matching), and search (content + files)", 161 "tools": ["read_file", "write_file", "patch", "search_files"], 162 "includes": [] 163 }, 164 165 "tts": { 166 "description": "Text-to-speech: convert text to audio with Edge TTS (free), ElevenLabs, OpenAI, or xAI", 167 "tools": ["text_to_speech"], 168 "includes": [] 169 }, 170 171 "todo": { 172 "description": "Task planning and tracking for multi-step work", 173 "tools": ["todo"], 174 "includes": [] 175 }, 176 177 "memory": { 178 "description": "Persistent memory across sessions (personal notes + user profile)", 179 "tools": ["memory"], 180 "includes": [] 181 }, 182 183 "session_search": { 184 "description": "Search and recall past conversations with summarization", 185 "tools": ["session_search"], 186 "includes": [] 187 }, 188 189 "clarify": { 190 "description": "Ask the user clarifying questions (multiple-choice or open-ended)", 191 "tools": ["clarify"], 192 "includes": [] 193 }, 194 195 "code_execution": { 196 "description": "Run Python scripts that call tools programmatically (reduces LLM round trips)", 197 "tools": ["execute_code"], 198 "includes": [] 199 }, 200 201 "delegation": { 202 "description": "Spawn subagents with isolated context for complex subtasks", 203 "tools": ["delegate_task"], 204 "includes": [] 205 }, 206 207 # "honcho" toolset removed — Honcho is now a memory provider plugin. 208 # Tools are injected via MemoryManager, not the toolset system. 209 210 "homeassistant": { 211 "description": "Home Assistant smart home control and monitoring", 212 "tools": ["ha_list_entities", "ha_get_state", "ha_list_services", "ha_call_service"], 213 "includes": [] 214 }, 215 216 "kanban": { 217 "description": ( 218 "Kanban multi-agent coordination — only active when the agent " 219 "is spawned by the kanban dispatcher (HERMES_KANBAN_TASK env " 220 "set). The dispatcher runs inside the gateway by default; see " 221 "`kanban.dispatch_in_gateway` in config.yaml. Lets workers mark " 222 "tasks done with structured handoffs, block for human input, " 223 "heartbeat during long ops, comment on threads, and (for " 224 "orchestrators) fan out into child tasks." 225 ), 226 "tools": [ 227 "kanban_show", "kanban_complete", "kanban_block", 228 "kanban_heartbeat", "kanban_comment", 229 "kanban_create", "kanban_link", 230 ], 231 "includes": [], 232 }, 233 234 "discord": { 235 "description": "Discord read and participate tools (fetch messages, search members, create threads)", 236 "tools": ["discord"], 237 "includes": [], 238 }, 239 240 "discord_admin": { 241 "description": "Discord server management (list channels/roles, pin messages, assign roles)", 242 "tools": ["discord_admin"], 243 "includes": [], 244 }, 245 246 "yuanbao": { 247 "description": "Yuanbao platform tools - group info, member queries, DM, stickers", 248 "tools": [ 249 "yb_query_group_info", 250 "yb_query_group_members", 251 "yb_send_dm", 252 "yb_search_sticker", 253 "yb_send_sticker", 254 ], 255 "includes": [] 256 }, 257 258 "feishu_doc": { 259 "description": "Read Feishu/Lark document content", 260 "tools": ["feishu_doc_read"], 261 "includes": [] 262 }, 263 264 "feishu_drive": { 265 "description": "Feishu/Lark document comment operations (list, reply, add)", 266 "tools": [ 267 "feishu_drive_list_comments", "feishu_drive_list_comment_replies", 268 "feishu_drive_reply_comment", "feishu_drive_add_comment", 269 ], 270 "includes": [] 271 }, 272 273 "spotify": { 274 "description": "Native Spotify playback, search, playlist, album, and library tools", 275 "tools": [ 276 "spotify_playback", "spotify_devices", "spotify_queue", "spotify_search", 277 "spotify_playlists", "spotify_albums", "spotify_library", 278 ], 279 "includes": [] 280 }, 281 282 283 # Scenario-specific toolsets 284 285 "debugging": { 286 "description": "Debugging and troubleshooting toolkit", 287 "tools": ["terminal", "process"], 288 "includes": ["web", "file"] # For searching error messages and solutions, and file operations 289 }, 290 291 "safe": { 292 "description": "Safe toolkit without terminal access", 293 "tools": [], 294 "includes": ["web", "vision", "image_gen"] 295 }, 296 297 # ========================================================================== 298 # Full Hermes toolsets (CLI + messaging platforms) 299 # 300 # All platforms share the same core tools (including send_message, 301 # which is gated on gateway running via its check_fn). 302 # ========================================================================== 303 304 "hermes-acp": { 305 "description": "Editor integration (VS Code, Zed, JetBrains) — coding-focused tools without messaging, audio, or clarify UI", 306 "tools": [ 307 "web_search", "web_extract", 308 "terminal", "process", 309 "read_file", "write_file", "patch", "search_files", 310 "vision_analyze", 311 "skills_list", "skill_view", "skill_manage", 312 "browser_navigate", "browser_snapshot", "browser_click", 313 "browser_type", "browser_scroll", "browser_back", 314 "browser_press", "browser_get_images", 315 "browser_vision", "browser_console", "browser_cdp", "browser_dialog", 316 "todo", "memory", 317 "session_search", 318 "execute_code", "delegate_task", 319 ], 320 "includes": [] 321 }, 322 323 "hermes-api-server": { 324 "description": "OpenAI-compatible API server — full agent tools accessible via HTTP (no interactive UI tools like clarify or send_message)", 325 "tools": [ 326 # Web 327 "web_search", "web_extract", 328 # Terminal + process management 329 "terminal", "process", 330 # File manipulation 331 "read_file", "write_file", "patch", "search_files", 332 # Vision + image generation 333 "vision_analyze", "image_generate", 334 # Skills 335 "skills_list", "skill_view", "skill_manage", 336 # Browser automation 337 "browser_navigate", "browser_snapshot", "browser_click", 338 "browser_type", "browser_scroll", "browser_back", 339 "browser_press", "browser_get_images", 340 "browser_vision", "browser_console", "browser_cdp", "browser_dialog", 341 # Planning & memory 342 "todo", "memory", 343 # Session history search 344 "session_search", 345 # Code execution + delegation 346 "execute_code", "delegate_task", 347 # Cronjob management 348 "cronjob", 349 # Home Assistant smart home control (gated on HASS_TOKEN via check_fn) 350 "ha_list_entities", "ha_get_state", "ha_list_services", "ha_call_service", 351 352 ], 353 "includes": [] 354 }, 355 356 "hermes-cli": { 357 "description": "Full interactive CLI toolset - all default tools plus cronjob management", 358 "tools": _HERMES_CORE_TOOLS, 359 "includes": [] 360 }, 361 362 "hermes-cron": { 363 # Mirrors hermes-cli so cron's "default" toolset is the same set of 364 # core tools users see interactively — then `hermes tools` filters 365 # them down per the platform config. _DEFAULT_OFF_TOOLSETS (moa, 366 # homeassistant, rl) are excluded by _get_platform_tools() unless 367 # the user explicitly enables them. 368 "description": "Default cron toolset - same core tools as hermes-cli; gated by `hermes tools`", 369 "tools": _HERMES_CORE_TOOLS, 370 "includes": [] 371 }, 372 373 "hermes-telegram": { 374 "description": "Telegram bot toolset - full access for personal use (terminal has safety checks)", 375 "tools": _HERMES_CORE_TOOLS, 376 "includes": [] 377 }, 378 379 "hermes-discord": { 380 "description": "Discord bot toolset - full access (terminal has safety checks via dangerous command approval)", 381 "tools": _HERMES_CORE_TOOLS + [ 382 "discord", 383 "discord_admin", 384 ], 385 "includes": [] 386 }, 387 388 "hermes-whatsapp": { 389 "description": "WhatsApp bot toolset - similar to Telegram (personal messaging, more trusted)", 390 "tools": _HERMES_CORE_TOOLS, 391 "includes": [] 392 }, 393 394 "hermes-slack": { 395 "description": "Slack bot toolset - full access for workspace use (terminal has safety checks)", 396 "tools": _HERMES_CORE_TOOLS, 397 "includes": [] 398 }, 399 400 "hermes-signal": { 401 "description": "Signal bot toolset - encrypted messaging platform (full access)", 402 "tools": _HERMES_CORE_TOOLS, 403 "includes": [] 404 }, 405 406 "hermes-bluebubbles": { 407 "description": "BlueBubbles iMessage bot toolset - Apple iMessage via local BlueBubbles server", 408 "tools": _HERMES_CORE_TOOLS, 409 "includes": [] 410 }, 411 412 "hermes-homeassistant": { 413 "description": "Home Assistant bot toolset - smart home event monitoring and control", 414 "tools": _HERMES_CORE_TOOLS, 415 "includes": [] 416 }, 417 418 "hermes-email": { 419 "description": "Email bot toolset - interact with Hermes via email (IMAP/SMTP)", 420 "tools": _HERMES_CORE_TOOLS, 421 "includes": [] 422 }, 423 424 "hermes-mattermost": { 425 "description": "Mattermost bot toolset - self-hosted team messaging (full access)", 426 "tools": _HERMES_CORE_TOOLS, 427 "includes": [] 428 }, 429 430 "hermes-matrix": { 431 "description": "Matrix bot toolset - decentralized encrypted messaging (full access)", 432 "tools": _HERMES_CORE_TOOLS, 433 "includes": [] 434 }, 435 436 "hermes-dingtalk": { 437 "description": "DingTalk bot toolset - enterprise messaging platform (full access)", 438 "tools": _HERMES_CORE_TOOLS, 439 "includes": [] 440 }, 441 442 "hermes-feishu": { 443 "description": "Feishu/Lark bot toolset - enterprise messaging via Feishu/Lark (full access)", 444 "tools": _HERMES_CORE_TOOLS + [ 445 "feishu_doc_read", 446 "feishu_drive_list_comments", 447 "feishu_drive_list_comment_replies", 448 "feishu_drive_reply_comment", 449 "feishu_drive_add_comment", 450 ], 451 "includes": [] 452 }, 453 454 "hermes-weixin": { 455 "description": "Weixin bot toolset - personal WeChat messaging via iLink (full access)", 456 "tools": _HERMES_CORE_TOOLS, 457 "includes": [] 458 }, 459 460 "hermes-qqbot": { 461 "description": "QQBot toolset - QQ messaging via Official Bot API v2 (full access)", 462 "tools": _HERMES_CORE_TOOLS, 463 "includes": [] 464 }, 465 466 "hermes-wecom": { 467 "description": "WeCom bot toolset - enterprise WeChat messaging (full access)", 468 "tools": _HERMES_CORE_TOOLS, 469 "includes": [] 470 }, 471 472 "hermes-wecom-callback": { 473 "description": "WeCom callback toolset - enterprise self-built app messaging (full access)", 474 "tools": _HERMES_CORE_TOOLS, 475 "includes": [] 476 }, 477 478 "hermes-yuanbao": { 479 "description": "Yuanbao Bot 元宝消息平台工具集 - 群信息、成员查询、私聊、贴纸表情", 480 "tools": _HERMES_CORE_TOOLS + [ 481 "yb_query_group_info", 482 "yb_query_group_members", 483 "yb_send_dm", 484 "yb_search_sticker", 485 "yb_send_sticker", 486 ], 487 "module": "tools.yuanbao_tools", 488 "includes": [] 489 }, 490 491 "hermes-sms": { 492 "description": "SMS bot toolset - interact with Hermes via SMS (Twilio)", 493 "tools": _HERMES_CORE_TOOLS, 494 "includes": [] 495 }, 496 497 "hermes-webhook": { 498 "description": "Webhook toolset - receive and process external webhook events", 499 "tools": _HERMES_CORE_TOOLS, 500 "includes": [] 501 }, 502 503 "hermes-gateway": { 504 "description": "Gateway toolset - union of all messaging platform tools", 505 "tools": [], 506 "includes": ["hermes-telegram", "hermes-discord", "hermes-whatsapp", "hermes-slack", "hermes-signal", "hermes-bluebubbles", "hermes-homeassistant", "hermes-email", "hermes-sms", "hermes-mattermost", "hermes-matrix", "hermes-dingtalk", "hermes-feishu", "hermes-wecom", "hermes-wecom-callback", "hermes-weixin", "hermes-qqbot", "hermes-webhook", "hermes-yuanbao"] 507 } 508 } 509 510 511 512 def get_toolset(name: str) -> Optional[Dict[str, Any]]: 513 """ 514 Get a toolset definition by name. 515 516 Args: 517 name (str): Name of the toolset 518 519 Returns: 520 Dict: Toolset definition with description, tools, and includes 521 None: If toolset not found 522 """ 523 toolset = TOOLSETS.get(name) 524 if toolset: 525 return toolset 526 527 try: 528 from tools.registry import registry 529 except Exception: 530 return None 531 532 registry_toolset = name 533 description = f"Plugin toolset: {name}" 534 alias_target = registry.get_toolset_alias_target(name) 535 536 if name not in _get_plugin_toolset_names(): 537 registry_toolset = alias_target 538 if not registry_toolset: 539 return None 540 description = f"MCP server '{name}' tools" 541 else: 542 reverse_aliases = { 543 canonical: alias 544 for alias, canonical in _get_registry_toolset_aliases().items() 545 if alias not in TOOLSETS 546 } 547 alias = reverse_aliases.get(name) 548 if alias: 549 description = f"MCP server '{alias}' tools" 550 551 return { 552 "description": description, 553 "tools": registry.get_tool_names_for_toolset(registry_toolset), 554 "includes": [], 555 } 556 557 558 def resolve_toolset(name: str, visited: Set[str] = None) -> List[str]: 559 """ 560 Recursively resolve a toolset to get all tool names. 561 562 This function handles toolset composition by recursively resolving 563 included toolsets and combining all tools. 564 565 Args: 566 name (str): Name of the toolset to resolve 567 visited (Set[str]): Set of already visited toolsets (for cycle detection) 568 569 Returns: 570 List[str]: List of all tool names in the toolset 571 """ 572 if visited is None: 573 visited = set() 574 575 # Special aliases that represent all tools across every toolset 576 # This ensures future toolsets are automatically included without changes. 577 if name in {"all", "*"}: 578 all_tools: Set[str] = set() 579 for toolset_name in get_toolset_names(): 580 # Use a fresh visited set per branch to avoid cross-branch contamination 581 resolved = resolve_toolset(toolset_name, visited.copy()) 582 all_tools.update(resolved) 583 return sorted(all_tools) 584 585 # Check for cycles / already-resolved (diamond deps). 586 # Silently return [] — either this is a diamond (not a bug, tools already 587 # collected via another path) or a genuine cycle (safe to skip). 588 if name in visited: 589 return [] 590 591 visited.add(name) 592 593 # Get toolset definition 594 toolset = get_toolset(name) 595 if not toolset: 596 # Auto-generate a toolset for plugin platforms (hermes-<name>). 597 # Gives them _HERMES_CORE_TOOLS plus any tools the plugin registered 598 # into a toolset matching the platform name. 599 if name.startswith("hermes-"): 600 platform_name = name[len("hermes-"):] 601 try: 602 from gateway.platform_registry import platform_registry 603 if platform_registry.is_registered(platform_name): 604 plugin_tools = set(_HERMES_CORE_TOOLS) 605 try: 606 from tools.registry import registry 607 plugin_tools.update( 608 e.name for e in registry._tools.values() 609 if e.toolset == platform_name 610 ) 611 except Exception: 612 pass 613 return list(plugin_tools) 614 except Exception: 615 pass 616 617 return [] 618 619 # Collect direct tools 620 tools = set(toolset.get("tools", [])) 621 622 # Recursively resolve included toolsets, sharing the visited set across 623 # sibling includes so diamond dependencies are only resolved once and 624 # cycle warnings don't fire multiple times for the same cycle. 625 for included_name in toolset.get("includes", []): 626 included_tools = resolve_toolset(included_name, visited) 627 tools.update(included_tools) 628 629 return sorted(tools) 630 631 632 def resolve_multiple_toolsets(toolset_names: List[str]) -> List[str]: 633 """ 634 Resolve multiple toolsets and combine their tools. 635 636 Args: 637 toolset_names (List[str]): List of toolset names to resolve 638 639 Returns: 640 List[str]: Combined list of all tool names (deduplicated) 641 """ 642 all_tools = set() 643 644 for name in toolset_names: 645 tools = resolve_toolset(name) 646 all_tools.update(tools) 647 648 return sorted(all_tools) 649 650 651 def _get_plugin_toolset_names() -> Set[str]: 652 """Return toolset names registered by plugins (from the tool registry). 653 654 These are toolsets that exist in the registry but not in the static 655 ``TOOLSETS`` dict — i.e. they were added by plugins at load time. 656 """ 657 try: 658 from tools.registry import registry 659 return { 660 toolset_name 661 for toolset_name in registry.get_registered_toolset_names() 662 if toolset_name not in TOOLSETS 663 } 664 except Exception: 665 return set() 666 667 668 def _get_registry_toolset_aliases() -> Dict[str, str]: 669 """Return explicit toolset aliases registered in the live registry.""" 670 try: 671 from tools.registry import registry 672 return registry.get_registered_toolset_aliases() 673 except Exception: 674 return {} 675 676 677 def get_all_toolsets() -> Dict[str, Dict[str, Any]]: 678 """ 679 Get all available toolsets with their definitions. 680 681 Includes both statically-defined toolsets and plugin-registered ones. 682 683 Returns: 684 Dict: All toolset definitions 685 """ 686 result = dict(TOOLSETS) 687 aliases = _get_registry_toolset_aliases() 688 for ts_name in _get_plugin_toolset_names(): 689 display_name = ts_name 690 for alias, canonical in aliases.items(): 691 if canonical == ts_name and alias not in TOOLSETS: 692 display_name = alias 693 break 694 if display_name in result: 695 continue 696 toolset = get_toolset(display_name) 697 if toolset: 698 result[display_name] = toolset 699 return result 700 701 702 def get_toolset_names() -> List[str]: 703 """ 704 Get names of all available toolsets (excluding aliases). 705 706 Includes plugin-registered toolset names. 707 708 Returns: 709 List[str]: List of toolset names 710 """ 711 names = set(TOOLSETS.keys()) 712 aliases = _get_registry_toolset_aliases() 713 for ts_name in _get_plugin_toolset_names(): 714 for alias, canonical in aliases.items(): 715 if canonical == ts_name and alias not in TOOLSETS: 716 names.add(alias) 717 break 718 else: 719 names.add(ts_name) 720 return sorted(names) 721 722 723 724 725 def validate_toolset(name: str) -> bool: 726 """ 727 Check if a toolset name is valid. 728 729 Args: 730 name (str): Toolset name to validate 731 732 Returns: 733 bool: True if valid, False otherwise 734 """ 735 # Accept special alias names for convenience 736 if name in {"all", "*"}: 737 return True 738 if name in TOOLSETS: 739 return True 740 if name in _get_plugin_toolset_names(): 741 return True 742 return name in _get_registry_toolset_aliases() 743 744 745 def create_custom_toolset( 746 name: str, 747 description: str, 748 tools: List[str] = None, 749 includes: List[str] = None 750 ) -> None: 751 """ 752 Create a custom toolset at runtime. 753 754 Args: 755 name (str): Name for the new toolset 756 description (str): Description of the toolset 757 tools (List[str]): Direct tools to include 758 includes (List[str]): Other toolsets to include 759 """ 760 TOOLSETS[name] = { 761 "description": description, 762 "tools": tools or [], 763 "includes": includes or [] 764 } 765 766 767 768 769 def get_toolset_info(name: str) -> Dict[str, Any]: 770 """ 771 Get detailed information about a toolset including resolved tools. 772 773 Args: 774 name (str): Toolset name 775 776 Returns: 777 Dict: Detailed toolset information 778 """ 779 toolset = get_toolset(name) 780 if not toolset: 781 return None 782 783 resolved_tools = resolve_toolset(name) 784 785 return { 786 "name": name, 787 "description": toolset["description"], 788 "direct_tools": toolset["tools"], 789 "includes": toolset["includes"], 790 "resolved_tools": resolved_tools, 791 "tool_count": len(resolved_tools), 792 "is_composite": bool(toolset["includes"]) 793 } 794 795 796 797 798 if __name__ == "__main__": 799 print("Toolsets System Demo") 800 print("=" * 60) 801 802 print("\nAvailable Toolsets:") 803 print("-" * 40) 804 for name, toolset in get_all_toolsets().items(): 805 info = get_toolset_info(name) 806 composite = "[composite]" if info["is_composite"] else "[leaf]" 807 print(f" {composite} {name:20} - {toolset['description']}") 808 print(f" Tools: {len(info['resolved_tools'])} total") 809 810 print("\nToolset Resolution Examples:") 811 print("-" * 40) 812 for name in ["web", "terminal", "safe", "debugging"]: 813 tools = resolve_toolset(name) 814 print(f"\n {name}:") 815 print(f" Resolved to {len(tools)} tools: {', '.join(sorted(tools))}") 816 817 print("\nMultiple Toolset Resolution:") 818 print("-" * 40) 819 combined = resolve_multiple_toolsets(["web", "vision", "terminal"]) 820 print(" Combining ['web', 'vision', 'terminal']:") 821 print(f" Result: {', '.join(sorted(combined))}") 822 823 print("\nCustom Toolset Creation:") 824 print("-" * 40) 825 create_custom_toolset( 826 name="my_custom", 827 description="My custom toolset for specific tasks", 828 tools=["web_search"], 829 includes=["terminal", "vision"] 830 ) 831 custom_info = get_toolset_info("my_custom") 832 print(" Created 'my_custom' toolset:") 833 print(f" Description: {custom_info['description']}") 834 print(f" Resolved tools: {', '.join(custom_info['resolved_tools'])}")