mirror of
https://github.com/hanzoai/python-sdk.git
synced 2026-10-27 04:20:09 +00:00
The default surface was 34 tools, 61 KB, ~15,300 tokens of schema in every
prompt of every agent. The mode system already names what a session should
carry, but only the tools register_all_tools enumerates obeyed it: zsh,
curl, playwright and the helpers (ps, npx, uvx, jq, wget, cdp, open) stayed
on in every mode. A mode now decides every discovered tool, the `hanzo`
mode lists what a coding agent calls every few steps (18 tools, 32 KB,
~8,000 tokens), and the rest is a name away: HANZO_MCP_ENABLED_TOOLS=a,b
or =all, `tool enable`, or `mode`.
hanzo-tools-shell 0.6.6: run_shell kept every byte a foreground command
wrote until the command ended. `yes` for thirty seconds is gigabytes. It
keeps the first 16 MiB per stream and drains the rest.
The tests' session-wide patch of asyncio.create_subprocess_exec handed
every shell command a stream that never reached EOF; the first test to run
one read it in a loop until the machine was out of memory (22 GB, killed).
The patch is now opt-in and its process has exited.
bench/latency.py measures the cold start, the tools/list bill and the common
calls through a real MCP client over stdio. Interleaved rounds, wall and CPU
seconds, p50 over 6 cold starts:
cold wall cold CPU tools bytes ~tokens
installed 0.15.18 9.34 s 10.46 s 30 52,601 13,150
main 0.15.20 1.52 s 1.13 s 34 61,287 15,321
this 0.96 s 1.06 s 18 32,222 8,055
The installed environment's 9 s is its hanzoai 8.5.156, whose package init
imports the whole generated cloud client; hanzo-mcp >= 0.15.20 requires
hanzoai >= 8.5.624, which loads it lazily. Per-call latency is spawn-bound
(zsh 17.6 ms, git status 33 ms, fs.read 2.5 ms at p50) and moves with the
machine's load; no call path changed.
130 lines
4.0 KiB
Python
130 lines
4.0 KiB
Python
"""Tool mode loader for dynamic tool configuration."""
|
|
|
|
import os
|
|
from typing import Dict, Optional, Set
|
|
|
|
from hanzo_mcp.tools.common.mode import (
|
|
ModeRegistry,
|
|
activate_mode_from_env,
|
|
register_default_modes,
|
|
)
|
|
|
|
# Essential system tools that are ALWAYS enabled regardless of mode
|
|
# These are core infrastructure tools that should never be disabled
|
|
# NOTE: llm and consensus are NOT essential (heavy llm dependency)
|
|
ESSENTIAL_SYSTEM_TOOLS: Set[str] = {
|
|
"mode", # Mode switching: the way to a wider surface
|
|
"tool", # Unified tool management (install, enable, disable, list)
|
|
"version", # Version info
|
|
"stats", # Statistics
|
|
}
|
|
|
|
# Heavy tools that are disabled by default (opt-in)
|
|
# These require large dependencies like llm
|
|
HEAVY_TOOLS: Set[str] = {
|
|
"consensus", # Requires llm
|
|
}
|
|
|
|
|
|
class ModeLoader:
|
|
"""Loads and manages tool modes for dynamic configuration."""
|
|
|
|
@staticmethod
|
|
def initialize_modes() -> None:
|
|
"""Initialize the mode system with defaults."""
|
|
# Initialize modes
|
|
register_default_modes()
|
|
|
|
# Check for mode from environment
|
|
activate_mode_from_env()
|
|
|
|
# If no mode set, use default
|
|
if not ModeRegistry.get_active():
|
|
default_mode = os.environ.get("HANZO_DEFAULT_MODE", "hanzo")
|
|
if ModeRegistry.get(default_mode):
|
|
ModeRegistry.set_active(default_mode)
|
|
|
|
@staticmethod
|
|
def get_enabled_tools_from_mode(
|
|
base_enabled_tools: Optional[Dict[str, bool]] = None,
|
|
force_mode: Optional[str] = None,
|
|
) -> Dict[str, bool]:
|
|
"""Get enabled tools configuration from active mode.
|
|
|
|
Args:
|
|
base_enabled_tools: Base configuration to merge with
|
|
force_mode: Force a specific mode (overrides active)
|
|
|
|
Returns:
|
|
Dictionary of tool enable states
|
|
"""
|
|
# Initialize if needed
|
|
if not ModeRegistry.list():
|
|
ModeLoader.initialize_modes()
|
|
|
|
# Get mode to use
|
|
tools_list = None
|
|
|
|
if force_mode:
|
|
# Set and get mode
|
|
if ModeRegistry.get(force_mode):
|
|
ModeRegistry.set_active(force_mode)
|
|
mode = ModeRegistry.get_active()
|
|
tools_list = mode.tools if mode else None
|
|
else:
|
|
# Check active mode
|
|
mode = ModeRegistry.get_active()
|
|
if mode:
|
|
tools_list = mode.tools
|
|
|
|
if not tools_list:
|
|
# No active mode, return base config
|
|
return base_enabled_tools or {}
|
|
|
|
# Start with base configuration
|
|
result = base_enabled_tools.copy() if base_enabled_tools else {}
|
|
|
|
# Get all possible tools from registry
|
|
from hanzo_mcp.config.tool_config import TOOL_REGISTRY
|
|
|
|
all_possible_tools = set(TOOL_REGISTRY.keys())
|
|
|
|
# Disable all tools first (clean slate for mode)
|
|
# EXCEPT essential system tools which are always enabled
|
|
for tool in all_possible_tools:
|
|
if tool in ESSENTIAL_SYSTEM_TOOLS:
|
|
result[tool] = True # Essential tools always enabled
|
|
else:
|
|
result[tool] = False
|
|
|
|
# Enable tools from mode
|
|
for tool in tools_list:
|
|
result[tool] = True
|
|
|
|
# Ensure all essential system tools are enabled (in case mode tried to disable them)
|
|
for tool in ESSENTIAL_SYSTEM_TOOLS:
|
|
result[tool] = True
|
|
|
|
return result
|
|
|
|
@staticmethod
|
|
def get_environment_from_mode() -> Dict[str, str]:
|
|
"""Get environment variables from active mode.
|
|
|
|
Returns:
|
|
Dictionary of environment variables
|
|
"""
|
|
# Check mode
|
|
mode = ModeRegistry.get_active()
|
|
if mode and mode.environment:
|
|
return mode.environment.copy()
|
|
|
|
return {}
|
|
|
|
@staticmethod
|
|
def apply_environment_from_mode() -> None:
|
|
"""Apply environment variables from active mode."""
|
|
env_vars = ModeLoader.get_environment_from_mode()
|
|
for key, value in env_vars.items():
|
|
os.environ[key] = value
|