hanzo 0.3.6__py3-none-any.whl → 0.3.8__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/commands/cluster.py CHANGED
@@ -1,14 +1,12 @@
1
1
  """Cluster management commands."""
2
2
 
3
- import asyncio
4
3
  from typing import List, Optional
5
4
 
6
5
  import click
7
- import httpx
8
- from rich.progress import Progress, SpinnerColumn, TextColumn
9
6
  from rich.table import Table
7
+ from rich.progress import Progress, TextColumn, SpinnerColumn
10
8
 
11
- from ..utils.output import console, handle_errors
9
+ from ..utils.output import console
12
10
 
13
11
 
14
12
  @click.group(name="cluster")
@@ -21,14 +19,21 @@ def cluster_group():
21
19
  @click.option("--name", "-n", default="hanzo-local", help="Cluster name")
22
20
  @click.option("--port", "-p", default=8000, type=int, help="API port")
23
21
  @click.option("--models", "-m", multiple=True, help="Models to load")
24
- @click.option("--device", type=click.Choice(["cpu", "gpu", "auto"]), default="auto", help="Device to use")
22
+ @click.option(
23
+ "--device",
24
+ type=click.Choice(["cpu", "gpu", "auto"]),
25
+ default="auto",
26
+ help="Device to use",
27
+ )
25
28
  @click.pass_context
26
29
  async def start(ctx, name: str, port: int, models: tuple, device: str):
27
30
  """Start local AI cluster."""
28
31
  await start_cluster(ctx, name, port, list(models) if models else None, device)
29
32
 
30
33
 
31
- async def start_cluster(ctx, name: str, port: int, models: Optional[List[str]] = None, device: str = "auto"):
34
+ async def start_cluster(
35
+ ctx, name: str, port: int, models: Optional[List[str]] = None, device: str = "auto"
36
+ ):
32
37
  """Start a local cluster via hanzo-cluster."""
33
38
  try:
34
39
  from hanzo_cluster import HanzoCluster
@@ -36,16 +41,16 @@ async def start_cluster(ctx, name: str, port: int, models: Optional[List[str]] =
36
41
  console.print("[red]Error:[/red] hanzo-cluster not installed")
37
42
  console.print("Install with: pip install hanzo[cluster]")
38
43
  return
39
-
44
+
40
45
  cluster = HanzoCluster(name=name, port=port, device=device)
41
-
46
+
42
47
  with Progress(
43
48
  SpinnerColumn(),
44
49
  TextColumn("[progress.description]{task.description}"),
45
50
  console=console,
46
51
  ) as progress:
47
52
  task = progress.add_task("Starting cluster...", total=None)
48
-
53
+
49
54
  try:
50
55
  await cluster.start(models=models)
51
56
  progress.update(task, completed=True)
@@ -53,10 +58,10 @@ async def start_cluster(ctx, name: str, port: int, models: Optional[List[str]] =
53
58
  progress.stop()
54
59
  console.print(f"[red]Failed to start cluster: {e}[/red]")
55
60
  return
56
-
61
+
57
62
  console.print(f"[green]✓[/green] Cluster started at http://localhost:{port}")
58
63
  console.print("Press Ctrl+C to stop\n")
59
-
64
+
60
65
  # Show cluster info
61
66
  info = await cluster.info()
62
67
  console.print("[cyan]Cluster Information:[/cyan]")
@@ -64,11 +69,11 @@ async def start_cluster(ctx, name: str, port: int, models: Optional[List[str]] =
64
69
  console.print(f" Port: {info.get('port', port)}")
65
70
  console.print(f" Device: {info.get('device', device)}")
66
71
  console.print(f" Nodes: {info.get('nodes', 1)}")
67
- if models := info.get('models', models):
72
+ if models := info.get("models", models):
68
73
  console.print(f" Models: {', '.join(models)}")
69
-
74
+
70
75
  console.print("\n[dim]Logs:[/dim]")
71
-
76
+
72
77
  try:
73
78
  # Stream logs
74
79
  async for log in cluster.stream_logs():
@@ -89,9 +94,9 @@ async def stop(ctx, name: str):
89
94
  except ImportError:
90
95
  console.print("[red]Error:[/red] hanzo-cluster not installed")
91
96
  return
92
-
97
+
93
98
  cluster = HanzoCluster(name=name)
94
-
99
+
95
100
  console.print("[yellow]Stopping cluster...[/yellow]")
96
101
  try:
97
102
  await cluster.stop()
@@ -110,38 +115,40 @@ async def status(ctx, name: str):
110
115
  except ImportError:
111
116
  console.print("[red]Error:[/red] hanzo-cluster not installed")
112
117
  return
113
-
118
+
114
119
  cluster = HanzoCluster(name=name)
115
-
120
+
116
121
  try:
117
122
  status = await cluster.status()
118
-
119
- if status.get('running'):
123
+
124
+ if status.get("running"):
120
125
  console.print("[green]✓[/green] Cluster is running")
121
-
126
+
122
127
  # Show cluster info
123
128
  console.print("\n[cyan]Cluster Information:[/cyan]")
124
129
  console.print(f" Name: {status.get('name', name)}")
125
130
  console.print(f" Nodes: {status.get('nodes', 0)}")
126
131
  console.print(f" Status: {status.get('state', 'unknown')}")
127
-
132
+
128
133
  # Show models
129
- if models := status.get('models', []):
134
+ if models := status.get("models", []):
130
135
  console.print("\n[cyan]Available Models:[/cyan]")
131
136
  for model in models:
132
137
  console.print(f" • {model}")
133
-
138
+
134
139
  # Show nodes
135
- if nodes := status.get('node_details', []):
140
+ if nodes := status.get("node_details", []):
136
141
  console.print("\n[cyan]Nodes:[/cyan]")
137
142
  for node in nodes:
138
- console.print(f" • {node.get('name', 'unknown')} ({node.get('state', 'unknown')})")
139
- if device := node.get('device'):
143
+ console.print(
144
+ f" • {node.get('name', 'unknown')} ({node.get('state', 'unknown')})"
145
+ )
146
+ if device := node.get("device"):
140
147
  console.print(f" Device: {device}")
141
148
  else:
142
149
  console.print("[yellow]![/yellow] Cluster is not running")
143
150
  console.print("Start with: hanzo cluster start")
144
-
151
+
145
152
  except Exception as e:
146
153
  console.print(f"[red]Error checking status: {e}[/red]")
147
154
 
@@ -156,32 +163,32 @@ async def models(ctx, name: str):
156
163
  except ImportError:
157
164
  console.print("[red]Error:[/red] hanzo-cluster not installed")
158
165
  return
159
-
166
+
160
167
  cluster = HanzoCluster(name=name)
161
-
168
+
162
169
  try:
163
170
  models = await cluster.list_models()
164
-
171
+
165
172
  if models:
166
173
  table = Table(title="Available Models")
167
174
  table.add_column("Model ID", style="cyan")
168
175
  table.add_column("Type", style="green")
169
176
  table.add_column("Status", style="yellow")
170
177
  table.add_column("Node", style="blue")
171
-
178
+
172
179
  for model in models:
173
180
  table.add_row(
174
181
  model.get("id", "unknown"),
175
182
  model.get("type", "model"),
176
183
  model.get("status", "unknown"),
177
- model.get("node", "local")
184
+ model.get("node", "local"),
178
185
  )
179
-
186
+
180
187
  console.print(table)
181
188
  else:
182
189
  console.print("[yellow]No models loaded[/yellow]")
183
190
  console.print("Load models with: hanzo cluster load <model>")
184
-
191
+
185
192
  except Exception as e:
186
193
  console.print(f"[red]Error: {e}[/red]")
187
194
 
@@ -198,21 +205,21 @@ async def load(ctx, model: str, name: str, node: str = None):
198
205
  except ImportError:
199
206
  console.print("[red]Error:[/red] hanzo-cluster not installed")
200
207
  return
201
-
208
+
202
209
  cluster = HanzoCluster(name=name)
203
-
210
+
204
211
  with console.status(f"Loading model '{model}'..."):
205
212
  try:
206
213
  result = await cluster.load_model(model, node=node)
207
214
  console.print(f"[green]✓[/green] Loaded model: {model}")
208
- if node_name := result.get('node'):
215
+ if node_name := result.get("node"):
209
216
  console.print(f" Node: {node_name}")
210
217
  except Exception as e:
211
218
  console.print(f"[red]Failed to load model: {e}[/red]")
212
219
 
213
220
 
214
221
  @cluster_group.command()
215
- @click.argument("model")
222
+ @click.argument("model")
216
223
  @click.option("--name", "-n", default="hanzo-local", help="Cluster name")
217
224
  @click.pass_context
218
225
  async def unload(ctx, model: str, name: str):
@@ -222,9 +229,9 @@ async def unload(ctx, model: str, name: str):
222
229
  except ImportError:
223
230
  console.print("[red]Error:[/red] hanzo-cluster not installed")
224
231
  return
225
-
232
+
226
233
  cluster = HanzoCluster(name=name)
227
-
234
+
228
235
  if click.confirm(f"Unload model '{model}'?"):
229
236
  with console.status(f"Unloading model '{model}'..."):
230
237
  try:
@@ -243,35 +250,51 @@ def node_group():
243
250
  @node_group.command(name="start")
244
251
  @click.option("--name", "-n", default="node-1", help="Node name")
245
252
  @click.option("--cluster", "-c", default="hanzo-local", help="Cluster to join")
246
- @click.option("--device", type=click.Choice(["cpu", "gpu", "auto"]), default="auto", help="Device to use")
247
- @click.option("--port", "-p", type=int, help="Node port (auto-assigned if not specified)")
253
+ @click.option(
254
+ "--device",
255
+ type=click.Choice(["cpu", "gpu", "auto"]),
256
+ default="auto",
257
+ help="Device to use",
258
+ )
259
+ @click.option(
260
+ "--port", "-p", type=int, help="Node port (auto-assigned if not specified)"
261
+ )
248
262
  @click.option("--blockchain", is_flag=True, help="Enable blockchain features")
249
263
  @click.option("--network", is_flag=True, help="Enable network discovery")
250
264
  @click.pass_context
251
- async def node_start(ctx, name: str, cluster: str, device: str, port: int, blockchain: bool, network: bool):
265
+ async def node_start(
266
+ ctx,
267
+ name: str,
268
+ cluster: str,
269
+ device: str,
270
+ port: int,
271
+ blockchain: bool,
272
+ network: bool,
273
+ ):
252
274
  """Start this machine as a node in the cluster."""
253
275
  try:
254
276
  from hanzo_cluster import HanzoNode
277
+
255
278
  if blockchain or network:
256
279
  from hanzo_network import HanzoNetwork
257
280
  except ImportError:
258
281
  console.print("[red]Error:[/red] Required packages not installed")
259
282
  console.print("Install with: pip install hanzo[cluster,network]")
260
283
  return
261
-
284
+
262
285
  node = HanzoNode(name=name, device=device, port=port)
263
-
286
+
264
287
  with Progress(
265
288
  SpinnerColumn(),
266
289
  TextColumn("[progress.description]{task.description}"),
267
290
  console=console,
268
291
  ) as progress:
269
292
  task = progress.add_task(f"Starting node '{name}'...", total=None)
270
-
293
+
271
294
  try:
272
295
  # Start the node
273
296
  await node.start(cluster=cluster)
274
-
297
+
275
298
  # Enable blockchain/network features if requested
276
299
  if blockchain or network:
277
300
  network_mgr = HanzoNetwork(node=node)
@@ -279,13 +302,13 @@ async def node_start(ctx, name: str, cluster: str, device: str, port: int, block
279
302
  await network_mgr.enable_blockchain()
280
303
  if network:
281
304
  await network_mgr.enable_discovery()
282
-
305
+
283
306
  progress.update(task, completed=True)
284
307
  except Exception as e:
285
308
  progress.stop()
286
309
  console.print(f"[red]Failed to start node: {e}[/red]")
287
310
  return
288
-
311
+
289
312
  console.print(f"[green]✓[/green] Node '{name}' started")
290
313
  console.print(f" Cluster: {cluster}")
291
314
  console.print(f" Device: {device}")
@@ -295,10 +318,10 @@ async def node_start(ctx, name: str, cluster: str, device: str, port: int, block
295
318
  console.print(" [cyan]Blockchain enabled[/cyan]")
296
319
  if network:
297
320
  console.print(" [cyan]Network discovery enabled[/cyan]")
298
-
321
+
299
322
  console.print("\nPress Ctrl+C to stop\n")
300
323
  console.print("[dim]Logs:[/dim]")
301
-
324
+
302
325
  try:
303
326
  # Stream logs
304
327
  async for log in node.stream_logs():
@@ -320,7 +343,7 @@ async def node_stop(ctx, name: str, all: bool):
320
343
  except ImportError:
321
344
  console.print("[red]Error:[/red] hanzo-cluster not installed")
322
345
  return
323
-
346
+
324
347
  if all:
325
348
  if click.confirm("Stop all nodes?"):
326
349
  console.print("[yellow]Stopping all nodes...[/yellow]")
@@ -351,10 +374,10 @@ async def node_list(ctx, cluster: str):
351
374
  except ImportError:
352
375
  console.print("[red]Error:[/red] hanzo-cluster not installed")
353
376
  return
354
-
377
+
355
378
  try:
356
379
  nodes = await HanzoNode.list_nodes(cluster=cluster)
357
-
380
+
358
381
  if nodes:
359
382
  table = Table(title="Cluster Nodes")
360
383
  table.add_column("Name", style="cyan")
@@ -362,21 +385,21 @@ async def node_list(ctx, cluster: str):
362
385
  table.add_column("Device", style="yellow")
363
386
  table.add_column("Status", style="blue")
364
387
  table.add_column("Models", style="magenta")
365
-
388
+
366
389
  for node in nodes:
367
390
  table.add_row(
368
391
  node.get("name", "unknown"),
369
392
  node.get("cluster", "unknown"),
370
393
  node.get("device", "unknown"),
371
394
  node.get("status", "unknown"),
372
- str(len(node.get("models", [])))
395
+ str(len(node.get("models", []))),
373
396
  )
374
-
397
+
375
398
  console.print(table)
376
399
  else:
377
400
  console.print("[yellow]No nodes found[/yellow]")
378
401
  console.print("Start a node with: hanzo cluster node start")
379
-
402
+
380
403
  except Exception as e:
381
404
  console.print(f"[red]Error: {e}[/red]")
382
405
 
@@ -391,38 +414,46 @@ async def node_info(ctx, name: str):
391
414
  except ImportError:
392
415
  console.print("[red]Error:[/red] hanzo-cluster not installed")
393
416
  return
394
-
417
+
395
418
  node = HanzoNode(name=name)
396
-
419
+
397
420
  try:
398
421
  info = await node.info()
399
-
422
+
400
423
  console.print(f"[cyan]Node: {name}[/cyan]")
401
424
  console.print(f" Cluster: {info.get('cluster', 'unknown')}")
402
425
  console.print(f" Status: {info.get('status', 'unknown')}")
403
426
  console.print(f" Device: {info.get('device', 'unknown')}")
404
-
405
- if uptime := info.get('uptime'):
427
+
428
+ if uptime := info.get("uptime"):
406
429
  console.print(f" Uptime: {uptime}")
407
-
408
- if resources := info.get('resources'):
430
+
431
+ if resources := info.get("resources"):
409
432
  console.print("\n[cyan]Resources:[/cyan]")
410
433
  console.print(f" CPU: {resources.get('cpu_percent', 'N/A')}%")
411
- console.print(f" Memory: {resources.get('memory_used', 'N/A')} / {resources.get('memory_total', 'N/A')}")
412
- if gpu := resources.get('gpu'):
413
- console.print(f" GPU: {gpu.get('name', 'N/A')} ({gpu.get('memory_used', 'N/A')} / {gpu.get('memory_total', 'N/A')})")
414
-
415
- if models := info.get('models'):
434
+ console.print(
435
+ f" Memory: {resources.get('memory_used', 'N/A')} / {resources.get('memory_total', 'N/A')}"
436
+ )
437
+ if gpu := resources.get("gpu"):
438
+ console.print(
439
+ f" GPU: {gpu.get('name', 'N/A')} ({gpu.get('memory_used', 'N/A')} / {gpu.get('memory_total', 'N/A')})"
440
+ )
441
+
442
+ if models := info.get("models"):
416
443
  console.print("\n[cyan]Loaded Models:[/cyan]")
417
444
  for model in models:
418
445
  console.print(f" • {model}")
419
-
420
- if network := info.get('network'):
446
+
447
+ if network := info.get("network"):
421
448
  console.print("\n[cyan]Network:[/cyan]")
422
- console.print(f" Blockchain: {'enabled' if network.get('blockchain') else 'disabled'}")
423
- console.print(f" Discovery: {'enabled' if network.get('discovery') else 'disabled'}")
424
- if peers := network.get('peers'):
449
+ console.print(
450
+ f" Blockchain: {'enabled' if network.get('blockchain') else 'disabled'}"
451
+ )
452
+ console.print(
453
+ f" Discovery: {'enabled' if network.get('discovery') else 'disabled'}"
454
+ )
455
+ if peers := network.get("peers"):
425
456
  console.print(f" Peers: {len(peers)}")
426
-
457
+
427
458
  except Exception as e:
428
- console.print(f"[red]Error: {e}[/red]")
459
+ console.print(f"[red]Error: {e}[/red]")
hanzo/commands/config.py CHANGED
@@ -1,14 +1,12 @@
1
1
  """Configuration management commands."""
2
2
 
3
3
  import json
4
- from pathlib import Path
5
- from typing import Any, Optional
6
4
 
7
- import click
8
5
  import yaml
6
+ import click
9
7
  from rich.syntax import Syntax
10
8
 
11
- from ..utils.output import console, handle_errors
9
+ from ..utils.output import console
12
10
 
13
11
 
14
12
  @click.group(name="config")
@@ -25,37 +23,37 @@ def config_group():
25
23
  def show(ctx, is_global: bool, is_local: bool, system: bool):
26
24
  """Show configuration."""
27
25
  from ..utils.config import load_config, get_config_paths
28
-
26
+
29
27
  # Determine which configs to show
30
28
  show_all = not (is_global or is_local or system)
31
-
29
+
32
30
  configs = {}
33
31
  paths = get_config_paths()
34
-
32
+
35
33
  if show_all or system:
36
34
  if paths["system"].exists():
37
35
  configs["System"] = (paths["system"], load_config(paths["system"]))
38
-
36
+
39
37
  if show_all or is_global:
40
38
  if paths["global"].exists():
41
39
  configs["Global"] = (paths["global"], load_config(paths["global"]))
42
-
40
+
43
41
  if show_all or is_local:
44
42
  if paths["local"].exists():
45
43
  configs["Local"] = (paths["local"], load_config(paths["local"]))
46
-
44
+
47
45
  # Merge and show
48
46
  if configs:
49
47
  for name, (path, config) in configs.items():
50
48
  console.print(f"[cyan]{name} Config:[/cyan] {path}")
51
-
49
+
52
50
  # Pretty print config
53
51
  if config:
54
52
  syntax = Syntax(
55
53
  yaml.dump(config, default_flow_style=False),
56
54
  "yaml",
57
55
  theme="monokai",
58
- line_numbers=False
56
+ line_numbers=False,
59
57
  )
60
58
  console.print(syntax)
61
59
  else:
@@ -75,20 +73,20 @@ def show(ctx, is_global: bool, is_local: bool, system: bool):
75
73
  def set(ctx, key: str, value: str, is_global: bool, is_local: bool):
76
74
  """Set configuration value."""
77
75
  from ..utils.config import load_config, save_config, get_config_paths
78
-
76
+
79
77
  # Determine target config
80
78
  paths = get_config_paths()
81
-
79
+
82
80
  if is_local:
83
81
  config_path = paths["local"]
84
82
  config_name = "local"
85
83
  else: # Default to global
86
84
  config_path = paths["global"]
87
85
  config_name = "global"
88
-
86
+
89
87
  # Load existing config
90
88
  config = load_config(config_path) if config_path.exists() else {}
91
-
89
+
92
90
  # Parse value
93
91
  try:
94
92
  # Try to parse as JSON first
@@ -102,22 +100,24 @@ def set(ctx, key: str, value: str, is_global: bool, is_local: bool):
102
100
  else:
103
101
  # Keep as string
104
102
  parsed_value = value
105
-
103
+
106
104
  # Set value (support nested keys with dot notation)
107
105
  keys = key.split(".")
108
106
  current = config
109
-
107
+
110
108
  for k in keys[:-1]:
111
109
  if k not in current:
112
110
  current[k] = {}
113
111
  current = current[k]
114
-
112
+
115
113
  current[keys[-1]] = parsed_value
116
-
114
+
117
115
  # Save config
118
116
  save_config(config_path, config)
119
-
120
- console.print(f"[green]✓[/green] Set {key} = {parsed_value} in {config_name} config")
117
+
118
+ console.print(
119
+ f"[green]✓[/green] Set {key} = {parsed_value} in {config_name} config"
120
+ )
121
121
 
122
122
 
123
123
  @config_group.command()
@@ -128,10 +128,10 @@ def set(ctx, key: str, value: str, is_global: bool, is_local: bool):
128
128
  def get(ctx, key: str, is_global: bool, is_local: bool):
129
129
  """Get configuration value."""
130
130
  from ..utils.config import get_config_value
131
-
131
+
132
132
  scope = "local" if is_local else ("global" if is_global else None)
133
133
  value = get_config_value(key, scope=scope)
134
-
134
+
135
135
  if value is not None:
136
136
  if isinstance(value, (dict, list)):
137
137
  console.print_json(data=value)
@@ -149,32 +149,32 @@ def get(ctx, key: str, is_global: bool, is_local: bool):
149
149
  def unset(ctx, key: str, is_global: bool, is_local: bool):
150
150
  """Unset configuration value."""
151
151
  from ..utils.config import load_config, save_config, get_config_paths
152
-
152
+
153
153
  # Determine target config
154
154
  paths = get_config_paths()
155
-
155
+
156
156
  if is_local:
157
157
  config_path = paths["local"]
158
158
  config_name = "local"
159
159
  else: # Default to global
160
160
  config_path = paths["global"]
161
161
  config_name = "global"
162
-
162
+
163
163
  if not config_path.exists():
164
164
  console.print(f"[yellow]No {config_name} config found[/yellow]")
165
165
  return
166
-
166
+
167
167
  # Load config
168
168
  config = load_config(config_path)
169
-
169
+
170
170
  # Remove value (support nested keys)
171
171
  keys = key.split(".")
172
172
  current = config
173
-
173
+
174
174
  try:
175
175
  for k in keys[:-1]:
176
176
  current = current[k]
177
-
177
+
178
178
  if keys[-1] in current:
179
179
  del current[keys[-1]]
180
180
  save_config(config_path, config)
@@ -194,26 +194,27 @@ def edit(ctx, system: bool, is_global: bool, is_local: bool):
194
194
  """Edit configuration file in editor."""
195
195
  import os
196
196
  import subprocess
197
+
197
198
  from ..utils.config import get_config_paths
198
-
199
+
199
200
  # Determine which config to edit
200
201
  paths = get_config_paths()
201
-
202
+
202
203
  if system:
203
204
  config_path = paths["system"]
204
205
  elif is_local:
205
206
  config_path = paths["local"]
206
207
  else: # Default to global
207
208
  config_path = paths["global"]
208
-
209
+
209
210
  # Ensure file exists
210
211
  if not config_path.exists():
211
212
  config_path.parent.mkdir(parents=True, exist_ok=True)
212
213
  config_path.write_text("# Hanzo configuration\n")
213
-
214
+
214
215
  # Get editor
215
216
  editor = os.environ.get("EDITOR", "vi")
216
-
217
+
217
218
  # Open in editor
218
219
  try:
219
220
  subprocess.run([editor, str(config_path)], check=True)
@@ -230,11 +231,11 @@ def edit(ctx, system: bool, is_global: bool, is_local: bool):
230
231
  def init(ctx):
231
232
  """Initialize configuration."""
232
233
  from ..utils.config import init_config
233
-
234
+
234
235
  try:
235
236
  paths = init_config()
236
237
  console.print("[green]✓[/green] Initialized configuration")
237
238
  console.print(f" Global: {paths['global']}")
238
239
  console.print(f" Local: {paths.get('local', 'Not in project')}")
239
240
  except Exception as e:
240
- console.print(f"[red]Failed to initialize config: {e}[/red]")
241
+ console.print(f"[red]Failed to initialize config: {e}[/red]")