hanzo 0.3.10__tar.gz → 0.3.12__tar.gz

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.

Files changed (29) hide show
  1. {hanzo-0.3.10 → hanzo-0.3.12}/PKG-INFO +2 -5
  2. {hanzo-0.3.10 → hanzo-0.3.12}/pyproject.toml +2 -5
  3. {hanzo-0.3.10 → hanzo-0.3.12}/src/hanzo/cli.py +148 -9
  4. hanzo-0.3.12/src/hanzo/dev.py +1621 -0
  5. {hanzo-0.3.10 → hanzo-0.3.12}/.gitignore +0 -0
  6. {hanzo-0.3.10 → hanzo-0.3.12}/README.md +0 -0
  7. {hanzo-0.3.10 → hanzo-0.3.12}/src/hanzo/__init__.py +0 -0
  8. {hanzo-0.3.10 → hanzo-0.3.12}/src/hanzo/__main__.py +0 -0
  9. {hanzo-0.3.10 → hanzo-0.3.12}/src/hanzo/commands/__init__.py +0 -0
  10. {hanzo-0.3.10 → hanzo-0.3.12}/src/hanzo/commands/agent.py +0 -0
  11. {hanzo-0.3.10 → hanzo-0.3.12}/src/hanzo/commands/auth.py +0 -0
  12. {hanzo-0.3.10 → hanzo-0.3.12}/src/hanzo/commands/chat.py +0 -0
  13. {hanzo-0.3.10 → hanzo-0.3.12}/src/hanzo/commands/cluster.py +0 -0
  14. {hanzo-0.3.10 → hanzo-0.3.12}/src/hanzo/commands/config.py +0 -0
  15. {hanzo-0.3.10 → hanzo-0.3.12}/src/hanzo/commands/mcp.py +0 -0
  16. {hanzo-0.3.10 → hanzo-0.3.12}/src/hanzo/commands/miner.py +0 -0
  17. {hanzo-0.3.10 → hanzo-0.3.12}/src/hanzo/commands/network.py +0 -0
  18. {hanzo-0.3.10 → hanzo-0.3.12}/src/hanzo/commands/repl.py +0 -0
  19. {hanzo-0.3.10 → hanzo-0.3.12}/src/hanzo/commands/tools.py +0 -0
  20. {hanzo-0.3.10 → hanzo-0.3.12}/src/hanzo/interactive/__init__.py +0 -0
  21. {hanzo-0.3.10 → hanzo-0.3.12}/src/hanzo/interactive/dashboard.py +0 -0
  22. {hanzo-0.3.10 → hanzo-0.3.12}/src/hanzo/interactive/repl.py +0 -0
  23. {hanzo-0.3.10 → hanzo-0.3.12}/src/hanzo/mcp_server.py +0 -0
  24. {hanzo-0.3.10 → hanzo-0.3.12}/src/hanzo/repl.py +0 -0
  25. {hanzo-0.3.10 → hanzo-0.3.12}/src/hanzo/router/__init__.py +0 -0
  26. {hanzo-0.3.10 → hanzo-0.3.12}/src/hanzo/utils/__init__.py +0 -0
  27. {hanzo-0.3.10 → hanzo-0.3.12}/src/hanzo/utils/config.py +0 -0
  28. {hanzo-0.3.10 → hanzo-0.3.12}/src/hanzo/utils/net_check.py +0 -0
  29. {hanzo-0.3.10 → hanzo-0.3.12}/src/hanzo/utils/output.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hanzo
3
- Version: 0.3.10
3
+ Version: 0.3.12
4
4
  Summary: Hanzo AI - Complete AI Infrastructure Platform with CLI, Router, MCP, and Agent Runtime
5
5
  Project-URL: Homepage, https://hanzo.ai
6
6
  Project-URL: Repository, https://github.com/hanzoai/python-sdk
@@ -14,16 +14,13 @@ Classifier: Intended Audience :: Developers
14
14
  Classifier: License :: OSI Approved :: Apache Software License
15
15
  Classifier: Operating System :: OS Independent
16
16
  Classifier: Programming Language :: Python :: 3
17
- Classifier: Programming Language :: Python :: 3.8
18
- Classifier: Programming Language :: Python :: 3.9
19
17
  Classifier: Programming Language :: Python :: 3.10
20
18
  Classifier: Programming Language :: Python :: 3.11
21
19
  Classifier: Programming Language :: Python :: 3.12
22
20
  Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
23
21
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
24
- Requires-Python: >=3.8
22
+ Requires-Python: >=3.10
25
23
  Requires-Dist: click>=8.1.0
26
- Requires-Dist: hanzo-net>=0.1.12
27
24
  Requires-Dist: httpx>=0.23.0
28
25
  Requires-Dist: prompt-toolkit>=3.0.0
29
26
  Requires-Dist: pydantic>=2.0.0
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "hanzo"
3
- version = "0.3.10"
3
+ version = "0.3.12"
4
4
  description = "Hanzo AI - Complete AI Infrastructure Platform with CLI, Router, MCP, and Agent Runtime"
5
5
  authors = [
6
6
  {name = "Hanzo AI", email = "dev@hanzo.ai"},
@@ -13,10 +13,9 @@ dependencies = [
13
13
  "httpx>=0.23.0",
14
14
  "pydantic>=2.0.0",
15
15
  "pyyaml>=6.0",
16
- "hanzo-net>=0.1.12",
17
16
  ]
18
17
  readme = "README.md"
19
- requires-python = ">= 3.8"
18
+ requires-python = ">= 3.10"
20
19
  keywords = ["ai", "cli", "hanzo", "agents", "llm", "mcp", "local-ai", "private-ai"]
21
20
  classifiers = [
22
21
  "Development Status :: 4 - Beta",
@@ -25,8 +24,6 @@ classifiers = [
25
24
  "License :: OSI Approved :: Apache Software License",
26
25
  "Operating System :: OS Independent",
27
26
  "Programming Language :: Python :: 3",
28
- "Programming Language :: Python :: 3.8",
29
- "Programming Language :: Python :: 3.9",
30
27
  "Programming Language :: Python :: 3.10",
31
28
  "Programming Language :: Python :: 3.11",
32
29
  "Programming Language :: Python :: 3.12",
@@ -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
- asyncio.run(start_compute_node(ctx, name, port, network, models, max_jobs))
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
- asyncio.run(start_compute_node(ctx, name, port, network, models, max_jobs))
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
- # Run net
208
- await net_run()
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 = subprocess.run(cmd_args, env=env, check=False)
257
-
258
- if process.returncode != 0 and process.returncode != -2: # -2 is Ctrl+C
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 {process.returncode}[/red]"
399
+ f"[red]Net exited with code {returncode}[/red]"
261
400
  )
262
401
 
263
402
  finally: