hanzo 0.3.23__py3-none-any.whl → 0.3.24__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of hanzo might be problematic. Click here for more details.

@@ -0,0 +1,152 @@
1
+ """Router command for starting Hanzo router proxy."""
2
+
3
+ import os
4
+ import sys
5
+ import subprocess
6
+ from typing import Optional
7
+ from pathlib import Path
8
+
9
+ import click
10
+
11
+ from ..utils.output import console
12
+
13
+
14
+ @click.group(name="router")
15
+ def router_group():
16
+ """Manage Hanzo router (LLM proxy)."""
17
+ pass
18
+
19
+
20
+ @router_group.command(name="start")
21
+ @click.option("--port", "-p", default=4000, help="Port to run router on")
22
+ @click.option("--config", "-c", help="Config file path")
23
+ @click.option("--detach", "-d", is_flag=True, help="Run in background")
24
+ @click.pass_context
25
+ def start_router(ctx, port: int, config: Optional[str], detach: bool):
26
+ """Start the Hanzo router proxy server."""
27
+ # Find router directory
28
+ router_paths = [
29
+ Path.home() / "work" / "hanzo" / "router",
30
+ Path.home() / "hanzo" / "router",
31
+ Path.cwd().parent / "router",
32
+ ]
33
+
34
+ router_dir = None
35
+ for path in router_paths:
36
+ if path.exists() and (path / "litellm" / "proxy" / "proxy_server.py").exists():
37
+ router_dir = path
38
+ break
39
+
40
+ if not router_dir:
41
+ console.print("[red]Error:[/red] Hanzo router not found")
42
+ console.print("\nPlease clone the router:")
43
+ console.print(" git clone https://github.com/hanzoai/router.git ~/work/hanzo/router")
44
+ return
45
+
46
+ console.print(f"[green]✓[/green] Found router at {router_dir}")
47
+
48
+ # Prepare environment
49
+ env = os.environ.copy()
50
+ env["PYTHONPATH"] = str(router_dir) + ":" + env.get("PYTHONPATH", "")
51
+
52
+ # Build command
53
+ cmd = [
54
+ sys.executable,
55
+ "-m", "litellm.proxy.proxy_server",
56
+ "--port", str(port),
57
+ ]
58
+
59
+ if config:
60
+ # Use provided config
61
+ config_path = Path(config)
62
+ if not config_path.exists():
63
+ console.print(f"[red]Error:[/red] Config file not found: {config}")
64
+ return
65
+ cmd.extend(["--config", str(config_path)])
66
+ else:
67
+ # Check for default config
68
+ default_config = router_dir / "config.yaml"
69
+ if default_config.exists():
70
+ cmd.extend(["--config", str(default_config)])
71
+ console.print(f"[dim]Using config: {default_config}[/dim]")
72
+
73
+ console.print(f"\n[bold cyan]Starting Hanzo Router on port {port}[/bold cyan]")
74
+ console.print(f"API endpoint: http://localhost:{port}/v1")
75
+ console.print("\nPress Ctrl+C to stop\n")
76
+
77
+ try:
78
+ # Change to router directory and run
79
+ os.chdir(router_dir)
80
+
81
+ if detach:
82
+ # Run in background
83
+ process = subprocess.Popen(
84
+ cmd,
85
+ env=env,
86
+ stdout=subprocess.DEVNULL,
87
+ stderr=subprocess.DEVNULL,
88
+ start_new_session=True
89
+ )
90
+ console.print(f"[green]✓[/green] Router started in background (PID: {process.pid})")
91
+ console.print(f"Check status: curl http://localhost:{port}/health")
92
+ else:
93
+ # Run in foreground
94
+ subprocess.run(cmd, env=env)
95
+ except KeyboardInterrupt:
96
+ console.print("\n[yellow]Router stopped[/yellow]")
97
+ except Exception as e:
98
+ console.print(f"[red]Error starting router: {e}[/red]")
99
+
100
+
101
+ @router_group.command(name="stop")
102
+ @click.option("--port", "-p", default=4000, help="Port router is running on")
103
+ def stop_router(port: int):
104
+ """Stop the router."""
105
+ import signal
106
+
107
+ import psutil
108
+
109
+ found = False
110
+ for proc in psutil.process_iter(['pid', 'cmdline']):
111
+ try:
112
+ cmdline = proc.info['cmdline']
113
+ if cmdline and 'proxy_server' in ' '.join(cmdline) and str(port) in ' '.join(cmdline):
114
+ console.print(f"[yellow]Stopping router (PID: {proc.info['pid']})[/yellow]")
115
+ proc.send_signal(signal.SIGTERM)
116
+ proc.wait(timeout=5)
117
+ found = True
118
+ console.print("[green]✓[/green] Router stopped")
119
+ except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.TimeoutExpired):
120
+ continue
121
+
122
+ if not found:
123
+ console.print(f"[yellow]No router found on port {port}[/yellow]")
124
+
125
+
126
+ @router_group.command(name="status")
127
+ @click.option("--port", "-p", default=4000, help="Port to check")
128
+ def router_status(port: int):
129
+ """Check router status."""
130
+ import httpx
131
+
132
+ try:
133
+ response = httpx.get(f"http://localhost:{port}/health", timeout=2.0)
134
+ if response.status_code == 200:
135
+ console.print(f"[green]✓[/green] Router is running on port {port}")
136
+
137
+ # Try to get models
138
+ try:
139
+ models_response = httpx.get(f"http://localhost:{port}/models", timeout=2.0)
140
+ if models_response.status_code == 200:
141
+ data = models_response.json()
142
+ if "data" in data:
143
+ console.print(f"Available models: {len(data['data'])}")
144
+ except Exception:
145
+ pass
146
+ else:
147
+ console.print(f"[yellow]Router responding but unhealthy (status: {response.status_code})[/yellow]")
148
+ except httpx.ConnectError:
149
+ console.print(f"[red]Router not running on port {port}[/red]")
150
+ console.print("\nStart with: hanzo router start")
151
+ except Exception as e:
152
+ console.print(f"[red]Error checking router: {e}[/red]")
hanzo/dev.py CHANGED
@@ -2359,7 +2359,7 @@ class MultiClaudeOrchestrator(HanzoDevOrchestrator):
2359
2359
  claude_available = True
2360
2360
  elif shutil.which("claude"):
2361
2361
  claude_available = True
2362
- except:
2362
+ except Exception:
2363
2363
  pass
2364
2364
 
2365
2365
  if not claude_available:
hanzo/fallback_handler.py CHANGED
@@ -44,7 +44,7 @@ class FallbackHandler:
44
44
  if response.status_code == 200:
45
45
  data = response.json()
46
46
  return len(data.get("models", [])) > 0
47
- except:
47
+ except Exception:
48
48
  pass
49
49
  return False
50
50
 
hanzo/interactive/repl.py CHANGED
@@ -35,8 +35,11 @@ class HanzoREPL:
35
35
  # Don't print welcome message here since it's already printed in cli.py
36
36
 
37
37
  # Set up command completer
38
+ cli_commands = ["chat", "ask", "agent", "node", "mcp", "network",
39
+ "auth", "config", "tools", "miner", "serve", "net",
40
+ "dev", "router"]
38
41
  completer = WordCompleter(
39
- list(self.commands.keys()) + ["chat", "agent", "cluster", "mcp", "network"],
42
+ list(self.commands.keys()) + cli_commands,
40
43
  ignore_case=True,
41
44
  )
42
45
 
@@ -58,8 +61,12 @@ class HanzoREPL:
58
61
  # Execute command
59
62
  if cmd in self.commands:
60
63
  await self.commands[cmd](args)
61
- else:
64
+ elif cmd in ["chat", "ask", "agent", "node", "mcp", "network", "auth", "config", "tools", "miner", "serve", "net", "dev", "router"]:
65
+ # Execute known CLI commands
62
66
  await self.execute_command(cmd, args)
67
+ else:
68
+ # Treat as chat message if not a known command
69
+ await self.chat_with_ai(command)
63
70
 
64
71
  except KeyboardInterrupt:
65
72
  continue
@@ -83,7 +90,7 @@ class HanzoREPL:
83
90
  All Hanzo CLI commands are available:
84
91
  - `chat <message>` - Chat with AI
85
92
  - `agent start` - Start an agent
86
- - `cluster status` - Check cluster status
93
+ - `node status` - Check node status
87
94
  - `mcp tools` - List MCP tools
88
95
  - `network agents` - List network agents
89
96
 
@@ -91,7 +98,7 @@ All Hanzo CLI commands are available:
91
98
  ```
92
99
  hanzo> chat How do I create a Python web server?
93
100
  hanzo> agent list
94
- hanzo> cluster start --models llama-3.2-3b
101
+ hanzo> node start --models llama-3.2-3b
95
102
  hanzo> mcp run read_file --arg path=README.md
96
103
  ```
97
104
 
@@ -114,53 +121,60 @@ hanzo> mcp run read_file --arg path=README.md
114
121
  async def show_status(self, args: str = ""):
115
122
  """Show system status."""
116
123
  status = {
117
- "cluster": await self.check_cluster_status(),
124
+ "node": await self.check_node_status(),
118
125
  "agents": await self.count_agents(),
119
126
  "auth": self.check_auth_status(),
120
127
  }
121
128
 
122
129
  self.console.print("[cyan]System Status:[/cyan]")
123
- self.console.print(f" Cluster: {status['cluster']}")
130
+ self.console.print(f" Node: {status['node']}")
124
131
  self.console.print(f" Agents: {status['agents']}")
125
132
  self.console.print(f" Auth: {status['auth']}")
126
133
 
127
134
  async def execute_command(self, cmd: str, args: str):
128
135
  """Execute a CLI command."""
129
- # Import here to avoid circular imports
136
+ import os
130
137
  import sys
131
-
132
- import click
133
-
134
- from .. import cli
135
-
136
- # Build command line
137
- argv = [cmd]
138
+ import shutil
139
+ import subprocess
140
+
141
+ # Find hanzo executable
142
+ hanzo_cmd = shutil.which("hanzo")
143
+ if not hanzo_cmd:
144
+ # Try using Python module directly
145
+ hanzo_cmd = sys.executable
146
+ argv = [hanzo_cmd, "-m", "hanzo", cmd]
147
+ else:
148
+ argv = [hanzo_cmd, cmd]
149
+
138
150
  if args:
139
151
  import shlex
140
-
141
152
  argv.extend(shlex.split(args))
142
153
 
143
- # Create a new context
154
+ # Execute as subprocess to avoid context issues
144
155
  try:
145
- # Save original argv
146
- orig_argv = sys.argv
147
- sys.argv = ["hanzo"] + argv
148
-
149
- # Execute command
150
- ctx = click.Context(cli.cli)
151
- cli.cli.invoke(ctx)
152
-
153
- except SystemExit:
154
- # Catch exit from commands
155
- pass
156
+ result = subprocess.run(
157
+ argv,
158
+ capture_output=True,
159
+ text=True,
160
+ timeout=30,
161
+ env=os.environ.copy() # Pass environment variables
162
+ )
163
+
164
+ if result.stdout:
165
+ self.console.print(result.stdout.rstrip())
166
+ if result.stderr and result.returncode != 0:
167
+ self.console.print(f"[red]{result.stderr.rstrip()}[/red]")
168
+
169
+ except subprocess.TimeoutExpired:
170
+ self.console.print("[red]Command timed out[/red]")
171
+ except FileNotFoundError:
172
+ self.console.print("[red]Command not found. Make sure 'hanzo' is installed.[/red]")
156
173
  except Exception as e:
157
174
  self.console.print(f"[red]Command error: {e}[/red]")
158
- finally:
159
- # Restore argv
160
- sys.argv = orig_argv
161
175
 
162
- async def check_cluster_status(self) -> str:
163
- """Check if cluster is running."""
176
+ async def check_node_status(self) -> str:
177
+ """Check if node is running."""
164
178
  try:
165
179
  import httpx
166
180
 
@@ -185,3 +199,9 @@ hanzo> mcp run read_file --arg path=README.md
185
199
  return "authenticated (saved)"
186
200
  else:
187
201
  return "not authenticated"
202
+
203
+ async def chat_with_ai(self, message: str):
204
+ """Chat with AI when user types natural language."""
205
+ # For natural language input, try to use it as a chat message
206
+ # Default to cloud mode to avoid needing local server
207
+ await self.execute_command("ask", f"--cloud {message}")
hanzo/mcp_server.py CHANGED
@@ -6,11 +6,16 @@ import click
6
6
 
7
7
 
8
8
  def main():
9
- """Start the Hanzo MCP server."""
9
+ """Start the Hanzo MCP server.
10
+
11
+ This wrapper defers to hanzo_mcp.cli:main so that the CLI can parse
12
+ transport flags and configure logging BEFORE importing any heavy modules,
13
+ preventing stdio protocol corruption.
14
+ """
10
15
  try:
11
- from hanzo_mcp.server import main as mcp_main
16
+ from hanzo_mcp.cli import main as cli_main
12
17
 
13
- mcp_main()
18
+ cli_main()
14
19
  except ImportError:
15
20
  click.echo(
16
21
  "Error: hanzo-mcp is not installed. Please run: pip install hanzo[mcp] or pip install hanzo[all]",
hanzo/memory_manager.py CHANGED
@@ -116,7 +116,7 @@ class MemoryManager:
116
116
  try:
117
117
  with open(self.session_file, "r") as f:
118
118
  self.session_context = json.load(f)
119
- except:
119
+ except Exception:
120
120
  self.session_context = {}
121
121
  else:
122
122
  self.session_context = {
hanzo/model_registry.py CHANGED
@@ -6,9 +6,9 @@ eliminating duplication and ensuring consistency across the codebase.
6
6
 
7
7
  from __future__ import annotations
8
8
 
9
- from dataclasses import dataclass, field
10
- from typing import Dict, List, Optional, Set
11
9
  from enum import Enum
10
+ from typing import Set, Dict, List, Optional
11
+ from dataclasses import field, dataclass
12
12
 
13
13
 
14
14
  class ModelProvider(Enum):
hanzo/rate_limiter.py CHANGED
@@ -218,7 +218,7 @@ class ErrorRecovery:
218
218
  if isinstance(e, error_type):
219
219
  try:
220
220
  return await handler(*args, **kwargs)
221
- except:
221
+ except Exception:
222
222
  pass # Fallback failed, continue with retry
223
223
 
224
224
  # Calculate backoff
hanzo/streaming.py CHANGED
@@ -273,7 +273,7 @@ async def stream_with_fallback(message: str, console: Console = None) -> Optiona
273
273
  # Try Ollama streaming
274
274
  try:
275
275
  return await handler.stream_ollama(message)
276
- except:
276
+ except Exception:
277
277
  pass
278
278
 
279
279
  # Fallback to non-streaming with simulated effect
@@ -0,0 +1,276 @@
1
+ Metadata-Version: 2.4
2
+ Name: hanzo
3
+ Version: 0.3.24
4
+ Summary: Hanzo AI - Complete AI Infrastructure Platform with CLI, Router, MCP, and Agent Runtime
5
+ Project-URL: Homepage, https://hanzo.ai
6
+ Project-URL: Repository, https://github.com/hanzoai/python-sdk
7
+ Project-URL: Documentation, https://docs.hanzo.ai/cli
8
+ Project-URL: Bug Tracker, https://github.com/hanzoai/python-sdk/issues
9
+ Author-email: Hanzo AI <dev@hanzo.ai>
10
+ Keywords: agents,ai,cli,hanzo,llm,local-ai,mcp,private-ai
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Environment :: Console
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: License :: OSI Approved :: Apache Software License
15
+ Classifier: Operating System :: OS Independent
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
21
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
22
+ Requires-Python: >=3.10
23
+ Requires-Dist: anthropic>=0.25.0
24
+ Requires-Dist: click>=8.1.0
25
+ Requires-Dist: httpx>=0.23.0
26
+ Requires-Dist: openai>=1.0.0
27
+ Requires-Dist: prompt-toolkit>=3.0.0
28
+ Requires-Dist: pydantic>=2.0.0
29
+ Requires-Dist: pyyaml>=6.0
30
+ Requires-Dist: rich>=13.0.0
31
+ Requires-Dist: typer>=0.9.0
32
+ Provides-Extra: agents
33
+ Requires-Dist: hanzo-agents>=0.1.0; extra == 'agents'
34
+ Requires-Dist: hanzo-network>=0.1.3; extra == 'agents'
35
+ Provides-Extra: ai
36
+ Requires-Dist: hanzoai>=1.0.0; extra == 'ai'
37
+ Provides-Extra: all
38
+ Requires-Dist: hanzo-aci>=0.2.8; extra == 'all'
39
+ Requires-Dist: hanzo-agents>=0.1.0; extra == 'all'
40
+ Requires-Dist: hanzo-mcp>=0.7.0; extra == 'all'
41
+ Requires-Dist: hanzo-memory>=1.0.0; extra == 'all'
42
+ Requires-Dist: hanzo-network>=0.1.3; extra == 'all'
43
+ Requires-Dist: hanzo-repl>=0.1.0; extra == 'all'
44
+ Requires-Dist: hanzoai>=1.0.0; extra == 'all'
45
+ Provides-Extra: dev
46
+ Requires-Dist: hanzo-aci>=0.2.8; extra == 'dev'
47
+ Provides-Extra: mcp
48
+ Requires-Dist: hanzo-mcp>=0.7.0; extra == 'mcp'
49
+ Provides-Extra: repl
50
+ Requires-Dist: hanzo-repl>=0.1.0; extra == 'repl'
51
+ Provides-Extra: router
52
+ Description-Content-Type: text/markdown
53
+
54
+ # Hanzo CLI and Orchestration Tools
55
+
56
+ [![PyPI](https://img.shields.io/pypi/v/hanzo.svg)](https://pypi.org/project/hanzo/)
57
+ [![Python Version](https://img.shields.io/pypi/pyversions/hanzo.svg)](https://pypi.org/project/hanzo/)
58
+
59
+ Core CLI and orchestration tools for the Hanzo AI platform.
60
+
61
+ ## Installation
62
+
63
+ ```bash
64
+ pip install hanzo
65
+ ```
66
+
67
+ ## Features
68
+
69
+ - **Interactive Chat**: Chat with AI models through CLI
70
+ - **Node Management**: Run local AI inference nodes
71
+ - **Router Control**: Manage LLM proxy router
72
+ - **REPL Interface**: Interactive Python REPL with AI
73
+ - **Batch Orchestration**: Orchestrate multiple AI tasks
74
+ - **Memory Management**: Persistent conversation memory
75
+
76
+ ## Usage
77
+
78
+ ### CLI Commands
79
+
80
+ ```bash
81
+ # Interactive chat
82
+ hanzo chat
83
+
84
+ # Use specific model
85
+ hanzo chat --model gpt-4
86
+
87
+ # Use router (local proxy)
88
+ hanzo chat --router
89
+
90
+ # Use cloud API
91
+ hanzo chat --cloud
92
+ ```
93
+
94
+ ### Node Management
95
+
96
+ ```bash
97
+ # Start local node
98
+ hanzo node start
99
+
100
+ # Check status
101
+ hanzo node status
102
+
103
+ # List available models
104
+ hanzo node models
105
+
106
+ # Load specific model
107
+ hanzo node load llama2:7b
108
+
109
+ # Stop node
110
+ hanzo node stop
111
+ ```
112
+
113
+ ### Router Management
114
+
115
+ ```bash
116
+ # Start router proxy
117
+ hanzo router start
118
+
119
+ # Check router status
120
+ hanzo router status
121
+
122
+ # List available models
123
+ hanzo router models
124
+
125
+ # View configuration
126
+ hanzo router config
127
+
128
+ # Stop router
129
+ hanzo router stop
130
+ ```
131
+
132
+ ### Interactive REPL
133
+
134
+ ```bash
135
+ # Start REPL
136
+ hanzo repl
137
+
138
+ # In REPL:
139
+ > /help # Show help
140
+ > /models # List models
141
+ > /model gpt-4 # Switch model
142
+ > /clear # Clear context
143
+ > What is Python? # Ask questions
144
+ ```
145
+
146
+ ## Python API
147
+
148
+ ### Batch Orchestration
149
+
150
+ ```python
151
+ from hanzo.batch_orchestrator import BatchOrchestrator
152
+
153
+ orchestrator = BatchOrchestrator()
154
+ results = await orchestrator.run_batch([
155
+ "Summarize quantum computing",
156
+ "Explain machine learning",
157
+ "Define artificial intelligence"
158
+ ])
159
+ ```
160
+
161
+ ### Memory Management
162
+
163
+ ```python
164
+ from hanzo.memory_manager import MemoryManager
165
+
166
+ memory = MemoryManager()
167
+ memory.add_to_context("user", "What is Python?")
168
+ memory.add_to_context("assistant", "Python is...")
169
+ context = memory.get_context()
170
+ ```
171
+
172
+ ### Fallback Handling
173
+
174
+ ```python
175
+ from hanzo.fallback_handler import FallbackHandler
176
+
177
+ handler = FallbackHandler()
178
+ result = await handler.handle_with_fallback(
179
+ primary_fn=api_call,
180
+ fallback_fn=local_inference
181
+ )
182
+ ```
183
+
184
+ ## Configuration
185
+
186
+ ### Environment Variables
187
+
188
+ ```bash
189
+ # API settings
190
+ HANZO_API_KEY=your-api-key
191
+ HANZO_BASE_URL=https://api.hanzo.ai
192
+
193
+ # Router settings
194
+ HANZO_ROUTER_URL=http://localhost:4000/v1
195
+
196
+ # Node settings
197
+ HANZO_NODE_URL=http://localhost:8000/v1
198
+ HANZO_NODE_WORKERS=4
199
+
200
+ # Model preferences
201
+ HANZO_DEFAULT_MODEL=gpt-4
202
+ HANZO_FALLBACK_MODEL=llama2:7b
203
+ ```
204
+
205
+ ### Configuration File
206
+
207
+ Create `~/.hanzo/config.yaml`:
208
+
209
+ ```yaml
210
+ api:
211
+ key: your-api-key
212
+ base_url: https://api.hanzo.ai
213
+
214
+ router:
215
+ url: http://localhost:4000/v1
216
+ auto_start: true
217
+
218
+ node:
219
+ url: http://localhost:8000/v1
220
+ workers: 4
221
+ models:
222
+ - llama2:7b
223
+ - mistral:7b
224
+
225
+ models:
226
+ default: gpt-4
227
+ fallback: llama2:7b
228
+ ```
229
+
230
+ ## Architecture
231
+
232
+ ### Components
233
+
234
+ - **CLI**: Command-line interface (`cli.py`)
235
+ - **Chat**: Interactive chat interface (`commands/chat.py`)
236
+ - **Node**: Local AI node management (`commands/node.py`)
237
+ - **Router**: LLM proxy management (`commands/router.py`)
238
+ - **REPL**: Interactive Python REPL (`interactive/repl.py`)
239
+ - **Orchestrator**: Batch task orchestration (`batch_orchestrator.py`)
240
+ - **Memory**: Conversation memory (`memory_manager.py`)
241
+ - **Fallback**: Resilient API handling (`fallback_handler.py`)
242
+
243
+ ### Port Allocation
244
+
245
+ - **4000**: Router (LLM proxy)
246
+ - **8000**: Node (local AI)
247
+ - **9550-9553**: Desktop app integration
248
+
249
+ ## Development
250
+
251
+ ### Setup
252
+
253
+ ```bash
254
+ cd pkg/hanzo
255
+ uv sync --all-extras
256
+ ```
257
+
258
+ ### Testing
259
+
260
+ ```bash
261
+ # Run tests
262
+ pytest tests/
263
+
264
+ # With coverage
265
+ pytest tests/ --cov=hanzo
266
+ ```
267
+
268
+ ### Building
269
+
270
+ ```bash
271
+ uv build
272
+ ```
273
+
274
+ ## License
275
+
276
+ Apache License 2.0