hanzo 0.3.10__py3-none-any.whl → 0.3.12__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.
- hanzo/cli.py +148 -9
- hanzo/dev.py +1621 -0
- {hanzo-0.3.10.dist-info → hanzo-0.3.12.dist-info}/METADATA +2 -5
- {hanzo-0.3.10.dist-info → hanzo-0.3.12.dist-info}/RECORD +6 -5
- {hanzo-0.3.10.dist-info → hanzo-0.3.12.dist-info}/WHEEL +0 -0
- {hanzo-0.3.10.dist-info → hanzo-0.3.12.dist-info}/entry_points.txt +0 -0
hanzo/cli.py
CHANGED
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
import sys
|
|
4
4
|
import asyncio
|
|
5
|
+
import signal
|
|
6
|
+
import os
|
|
7
|
+
import subprocess
|
|
5
8
|
from typing import Optional
|
|
6
9
|
|
|
7
10
|
import click
|
|
@@ -115,7 +118,11 @@ def serve(ctx, name: str, port: int):
|
|
|
115
118
|
@click.pass_context
|
|
116
119
|
def net(ctx, name: str, port: int, network: str, models: tuple, max_jobs: int):
|
|
117
120
|
"""Start the Hanzo Network distributed AI compute node."""
|
|
118
|
-
|
|
121
|
+
try:
|
|
122
|
+
asyncio.run(start_compute_node(ctx, name, port, network, models, max_jobs))
|
|
123
|
+
except KeyboardInterrupt:
|
|
124
|
+
# Already handled in start_compute_node
|
|
125
|
+
pass
|
|
119
126
|
|
|
120
127
|
|
|
121
128
|
@cli.command()
|
|
@@ -133,7 +140,73 @@ def net(ctx, name: str, port: int, network: str, models: tuple, max_jobs: int):
|
|
|
133
140
|
@click.pass_context
|
|
134
141
|
def node(ctx, name: str, port: int, network: str, models: tuple, max_jobs: int):
|
|
135
142
|
"""Alias for 'hanzo net' - Start as a compute node for the Hanzo network."""
|
|
136
|
-
|
|
143
|
+
try:
|
|
144
|
+
asyncio.run(start_compute_node(ctx, name, port, network, models, max_jobs))
|
|
145
|
+
except KeyboardInterrupt:
|
|
146
|
+
# Already handled in start_compute_node
|
|
147
|
+
pass
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
@cli.command()
|
|
151
|
+
@click.option("--workspace", default="~/.hanzo/dev", help="Workspace directory")
|
|
152
|
+
@click.option("--orchestrator", default="gpt-5", help="Orchestrator model (e.g., gpt-5, gpt-4, claude-3-5-sonnet, local:llama3.2)")
|
|
153
|
+
@click.option("--claude-path", help="Path to Claude Code executable")
|
|
154
|
+
@click.option("--monitor", is_flag=True, help="Start in monitor mode")
|
|
155
|
+
@click.option("--repl", is_flag=True, help="Start REPL interface (default)")
|
|
156
|
+
@click.option("--instances", type=int, default=2, help="Number of worker agents")
|
|
157
|
+
@click.option("--mcp-tools", is_flag=True, default=True, help="Enable all MCP tools")
|
|
158
|
+
@click.option("--network-mode", is_flag=True, default=True, help="Network agents together")
|
|
159
|
+
@click.option("--guardrails", is_flag=True, default=True, help="Enable code quality guardrails")
|
|
160
|
+
@click.option("--use-network/--no-network", default=True, help="Use hanzo-network if available")
|
|
161
|
+
@click.option("--use-hanzo-net", is_flag=True, help="Use hanzo/net for local AI (auto-enabled with local: models)")
|
|
162
|
+
@click.option("--hanzo-net-port", type=int, default=52415, help="Port for hanzo/net (default: 52415)")
|
|
163
|
+
@click.pass_context
|
|
164
|
+
def dev(ctx, workspace: str, orchestrator: str, claude_path: str, monitor: bool, repl: bool,
|
|
165
|
+
instances: int, mcp_tools: bool, network_mode: bool, guardrails: bool, use_network: bool,
|
|
166
|
+
use_hanzo_net: bool, hanzo_net_port: int):
|
|
167
|
+
"""Start Hanzo Dev - AI Coding OS with configurable orchestrator.
|
|
168
|
+
|
|
169
|
+
This creates a multi-agent system where:
|
|
170
|
+
- Configurable orchestrator (GPT-5, GPT-4, Claude, or LOCAL) manages the network
|
|
171
|
+
- Local AI via hanzo/net for cost-effective orchestration
|
|
172
|
+
- Worker agents (Claude + local) handle code implementation
|
|
173
|
+
- Critic agents review and improve code (System 2 thinking)
|
|
174
|
+
- Cost-optimized routing (local models for simple tasks)
|
|
175
|
+
- All agents can use MCP tools
|
|
176
|
+
- Agents can call each other recursively
|
|
177
|
+
- Guardrails prevent code degradation
|
|
178
|
+
- Auto-recovery from failures
|
|
179
|
+
|
|
180
|
+
Examples:
|
|
181
|
+
hanzo dev # GPT-5 orchestrator (default)
|
|
182
|
+
hanzo dev --orchestrator gpt-4 # GPT-4 orchestrator
|
|
183
|
+
hanzo dev --orchestrator claude-3-5-sonnet # Claude orchestrator
|
|
184
|
+
hanzo dev --orchestrator local:llama3.2 # Local Llama 3.2 via hanzo/net
|
|
185
|
+
hanzo dev --use-hanzo-net # Enable local AI workers
|
|
186
|
+
hanzo dev --instances 4 # More worker agents
|
|
187
|
+
hanzo dev --monitor # Auto-monitor and restart mode
|
|
188
|
+
"""
|
|
189
|
+
from .dev import run_dev_orchestrator
|
|
190
|
+
|
|
191
|
+
# Auto-enable hanzo net if using local orchestrator
|
|
192
|
+
if orchestrator.startswith("local:"):
|
|
193
|
+
use_hanzo_net = True
|
|
194
|
+
|
|
195
|
+
asyncio.run(run_dev_orchestrator(
|
|
196
|
+
workspace=workspace,
|
|
197
|
+
orchestrator_model=orchestrator,
|
|
198
|
+
claude_path=claude_path,
|
|
199
|
+
monitor=monitor,
|
|
200
|
+
repl=repl or not monitor, # Default to REPL if not monitoring
|
|
201
|
+
instances=instances,
|
|
202
|
+
mcp_tools=mcp_tools,
|
|
203
|
+
network_mode=network_mode,
|
|
204
|
+
guardrails=guardrails,
|
|
205
|
+
use_network=use_network,
|
|
206
|
+
use_hanzo_net=use_hanzo_net,
|
|
207
|
+
hanzo_net_port=hanzo_net_port,
|
|
208
|
+
console=ctx.obj.get("console", console)
|
|
209
|
+
))
|
|
137
210
|
|
|
138
211
|
|
|
139
212
|
async def start_compute_node(
|
|
@@ -204,8 +277,40 @@ async def start_compute_node(
|
|
|
204
277
|
console.print("API: http://localhost:52415/v1/chat/completions")
|
|
205
278
|
console.print("\nPress Ctrl+C to stop\n")
|
|
206
279
|
|
|
207
|
-
#
|
|
208
|
-
|
|
280
|
+
# Set up signal handlers for async version
|
|
281
|
+
stop_event = asyncio.Event()
|
|
282
|
+
|
|
283
|
+
def async_signal_handler(signum, frame):
|
|
284
|
+
console.print("\n[yellow]Stopping hanzo net...[/yellow]")
|
|
285
|
+
stop_event.set()
|
|
286
|
+
|
|
287
|
+
signal.signal(signal.SIGINT, async_signal_handler)
|
|
288
|
+
signal.signal(signal.SIGTERM, async_signal_handler)
|
|
289
|
+
|
|
290
|
+
# Run net with proper signal handling
|
|
291
|
+
try:
|
|
292
|
+
net_task = asyncio.create_task(net_run())
|
|
293
|
+
stop_task = asyncio.create_task(stop_event.wait())
|
|
294
|
+
|
|
295
|
+
# Wait for either net to complete or stop signal
|
|
296
|
+
done, pending = await asyncio.wait(
|
|
297
|
+
[net_task, stop_task],
|
|
298
|
+
return_when=asyncio.FIRST_COMPLETED
|
|
299
|
+
)
|
|
300
|
+
|
|
301
|
+
# Cancel pending tasks
|
|
302
|
+
for task in pending:
|
|
303
|
+
task.cancel()
|
|
304
|
+
try:
|
|
305
|
+
await task
|
|
306
|
+
except asyncio.CancelledError:
|
|
307
|
+
pass
|
|
308
|
+
|
|
309
|
+
# Check if we stopped due to signal
|
|
310
|
+
if stop_task in done:
|
|
311
|
+
console.print("[green]✓[/green] Node stopped gracefully")
|
|
312
|
+
except asyncio.CancelledError:
|
|
313
|
+
console.print("[yellow]Cancelled[/yellow]")
|
|
209
314
|
finally:
|
|
210
315
|
sys.argv = original_argv
|
|
211
316
|
else:
|
|
@@ -252,12 +357,46 @@ async def start_compute_node(
|
|
|
252
357
|
if models:
|
|
253
358
|
cmd_args.extend(["--default-model", models[0]])
|
|
254
359
|
|
|
255
|
-
# Run net command with detected python
|
|
256
|
-
process
|
|
257
|
-
|
|
258
|
-
|
|
360
|
+
# Run net command with detected python in a more signal-friendly way
|
|
361
|
+
# Create new process group for better signal handling
|
|
362
|
+
process = subprocess.Popen(
|
|
363
|
+
cmd_args,
|
|
364
|
+
env=env,
|
|
365
|
+
preexec_fn=os.setsid if hasattr(os, 'setsid') else None
|
|
366
|
+
)
|
|
367
|
+
|
|
368
|
+
# Set up signal handlers to forward to subprocess group
|
|
369
|
+
def signal_handler(signum, frame):
|
|
370
|
+
if process.poll() is None: # Process is still running
|
|
371
|
+
console.print("\n[yellow]Stopping hanzo net...[/yellow]")
|
|
372
|
+
try:
|
|
373
|
+
# Send signal to entire process group
|
|
374
|
+
if hasattr(os, 'killpg'):
|
|
375
|
+
os.killpg(os.getpgid(process.pid), signal.SIGTERM)
|
|
376
|
+
else:
|
|
377
|
+
process.terminate()
|
|
378
|
+
process.wait(timeout=5) # Wait up to 5 seconds
|
|
379
|
+
except subprocess.TimeoutExpired:
|
|
380
|
+
console.print("[yellow]Force stopping...[/yellow]")
|
|
381
|
+
if hasattr(os, 'killpg'):
|
|
382
|
+
os.killpg(os.getpgid(process.pid), signal.SIGKILL)
|
|
383
|
+
else:
|
|
384
|
+
process.kill()
|
|
385
|
+
process.wait()
|
|
386
|
+
except ProcessLookupError:
|
|
387
|
+
pass # Process already terminated
|
|
388
|
+
raise KeyboardInterrupt
|
|
389
|
+
|
|
390
|
+
# Register signal handlers
|
|
391
|
+
signal.signal(signal.SIGINT, signal_handler)
|
|
392
|
+
signal.signal(signal.SIGTERM, signal_handler)
|
|
393
|
+
|
|
394
|
+
# Wait for process to complete
|
|
395
|
+
returncode = process.wait()
|
|
396
|
+
|
|
397
|
+
if returncode != 0 and returncode != -2: # -2 is Ctrl+C
|
|
259
398
|
console.print(
|
|
260
|
-
f"[red]Net exited with code {
|
|
399
|
+
f"[red]Net exited with code {returncode}[/red]"
|
|
261
400
|
)
|
|
262
401
|
|
|
263
402
|
finally:
|