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/mcp.py CHANGED
@@ -1,14 +1,11 @@
1
1
  """MCP (Model Context Protocol) commands."""
2
2
 
3
- import asyncio
4
3
  import json
5
- from typing import Dict, Any
6
4
 
7
5
  import click
8
6
  from rich.table import Table
9
- from rich.syntax import Syntax
10
7
 
11
- from ..utils.output import console, handle_errors
8
+ from ..utils.output import console
12
9
 
13
10
 
14
11
  @click.group(name="mcp")
@@ -19,13 +16,27 @@ def mcp_group():
19
16
 
20
17
  @mcp_group.command()
21
18
  @click.option("--name", "-n", default="hanzo-mcp", help="Server name")
22
- @click.option("--transport", "-t", type=click.Choice(["stdio", "sse"]), default="stdio", help="Transport protocol")
19
+ @click.option(
20
+ "--transport",
21
+ "-t",
22
+ type=click.Choice(["stdio", "sse"]),
23
+ default="stdio",
24
+ help="Transport protocol",
25
+ )
23
26
  @click.option("--allow-path", "-p", multiple=True, help="Allowed paths")
24
27
  @click.option("--enable-agent", is_flag=True, help="Enable agent tools")
25
28
  @click.option("--host", default="127.0.0.1", help="Host for SSE transport")
26
29
  @click.option("--port", default=3000, type=int, help="Port for SSE transport")
27
30
  @click.pass_context
28
- def serve(ctx, name: str, transport: str, allow_path: tuple, enable_agent: bool, host: str, port: int):
31
+ def serve(
32
+ ctx,
33
+ name: str,
34
+ transport: str,
35
+ allow_path: tuple,
36
+ enable_agent: bool,
37
+ host: str,
38
+ port: int,
39
+ ):
29
40
  """Start MCP server."""
30
41
  try:
31
42
  from hanzoai.mcp import run_mcp_server
@@ -33,17 +44,17 @@ def serve(ctx, name: str, transport: str, allow_path: tuple, enable_agent: bool,
33
44
  console.print("[red]Error:[/red] hanzo-mcp not installed")
34
45
  console.print("Install with: pip install hanzo[mcp]")
35
46
  return
36
-
47
+
37
48
  allowed_paths = list(allow_path) if allow_path else ["."]
38
-
49
+
39
50
  console.print(f"[cyan]Starting MCP server[/cyan]")
40
51
  console.print(f" Name: {name}")
41
52
  console.print(f" Transport: {transport}")
42
53
  console.print(f" Allowed paths: {', '.join(allowed_paths)}")
43
-
54
+
44
55
  if transport == "sse":
45
56
  console.print(f" Endpoint: http://{host}:{port}")
46
-
57
+
47
58
  try:
48
59
  run_mcp_server(
49
60
  name=name,
@@ -51,7 +62,7 @@ def serve(ctx, name: str, transport: str, allow_path: tuple, enable_agent: bool,
51
62
  allowed_paths=allowed_paths,
52
63
  enable_agent_tool=enable_agent,
53
64
  host=host,
54
- port=port
65
+ port=port,
55
66
  )
56
67
  except KeyboardInterrupt:
57
68
  console.print("\n[yellow]Server stopped[/yellow]")
@@ -68,30 +79,32 @@ async def tools(ctx, category: str):
68
79
  console.print("[red]Error:[/red] hanzo-mcp not installed")
69
80
  console.print("Install with: pip install hanzo[mcp]")
70
81
  return
71
-
82
+
72
83
  with console.status("Loading tools..."):
73
84
  server = create_server(enable_all_tools=True)
74
85
  tools_list = await server.mcp.list_tools()
75
-
86
+
76
87
  # Group by category if available
77
88
  categories = {}
78
89
  for tool in tools_list:
79
- cat = getattr(tool, 'category', 'general')
90
+ cat = getattr(tool, "category", "general")
80
91
  if category and cat != category:
81
92
  continue
82
93
  if cat not in categories:
83
94
  categories[cat] = []
84
95
  categories[cat].append(tool)
85
-
96
+
86
97
  # Display tools
87
98
  for cat, tools in sorted(categories.items()):
88
- table = Table(title=f"{cat.title()} Tools" if len(categories) > 1 else "MCP Tools")
99
+ table = Table(
100
+ title=f"{cat.title()} Tools" if len(categories) > 1 else "MCP Tools"
101
+ )
89
102
  table.add_column("Name", style="cyan", no_wrap=True)
90
103
  table.add_column("Description")
91
-
104
+
92
105
  for tool in sorted(tools, key=lambda t: t.name):
93
106
  table.add_row(tool.name, tool.description)
94
-
107
+
95
108
  console.print(table)
96
109
  if len(categories) > 1:
97
110
  console.print()
@@ -110,10 +123,10 @@ async def run(ctx, tool: str, arg: tuple, json_args: str):
110
123
  console.print("[red]Error:[/red] hanzo-mcp not installed")
111
124
  console.print("Install with: pip install hanzo[mcp]")
112
125
  return
113
-
126
+
114
127
  # Parse arguments
115
128
  args = {}
116
-
129
+
117
130
  if json_args:
118
131
  try:
119
132
  args = json.loads(json_args)
@@ -127,17 +140,17 @@ async def run(ctx, tool: str, arg: tuple, json_args: str):
127
140
  console.print("Use: --arg key=value")
128
141
  return
129
142
  key, value = a.split("=", 1)
130
-
143
+
131
144
  # Try to parse value as JSON first
132
145
  try:
133
146
  args[key] = json.loads(value)
134
147
  except:
135
148
  args[key] = value
136
-
149
+
137
150
  # Create server and run tool
138
151
  with console.status(f"Running tool '{tool}'..."):
139
152
  server = create_server(enable_all_tools=True)
140
-
153
+
141
154
  # Find tool
142
155
  tools_list = await server.mcp.list_tools()
143
156
  tool_obj = None
@@ -145,18 +158,19 @@ async def run(ctx, tool: str, arg: tuple, json_args: str):
145
158
  if t.name == tool:
146
159
  tool_obj = t
147
160
  break
148
-
161
+
149
162
  if not tool_obj:
150
163
  console.print(f"[red]Tool not found: {tool}[/red]")
151
164
  console.print("Use 'hanzo mcp tools' to list available tools")
152
165
  return
153
-
166
+
154
167
  # Run tool
155
168
  try:
156
169
  # Mock context for now
157
170
  from mcp.server.fastmcp import Context
171
+
158
172
  context = Context()
159
-
173
+
160
174
  # Get tool function
161
175
  tool_func = server.mcp._tool_map.get(tool)
162
176
  if tool_func:
@@ -164,11 +178,11 @@ async def run(ctx, tool: str, arg: tuple, json_args: str):
164
178
  else:
165
179
  console.print(f"[red]Tool function not found: {tool}[/red]")
166
180
  return
167
-
181
+
168
182
  except Exception as e:
169
183
  console.print(f"[red]Tool error: {e}[/red]")
170
184
  return
171
-
185
+
172
186
  # Display result
173
187
  if isinstance(result, str):
174
188
  try:
@@ -183,53 +197,60 @@ async def run(ctx, tool: str, arg: tuple, json_args: str):
183
197
 
184
198
 
185
199
  @mcp_group.command()
186
- @click.option("--path", "-p", default="~/.config/claude/claude_desktop_config.json", help="Config file path")
200
+ @click.option(
201
+ "--path",
202
+ "-p",
203
+ default="~/.config/claude/claude_desktop_config.json",
204
+ help="Config file path",
205
+ )
187
206
  @click.pass_context
188
207
  def install(ctx, path: str):
189
208
  """Install MCP server in Claude Desktop."""
190
209
  try:
191
210
  from hanzoai.mcp import create_server
192
211
  except ImportError:
193
- console.print("[red]Error:[/red] hanzo-mcp not installed")
212
+ console.print("[red]Error:[/red] hanzo-mcp not installed")
194
213
  console.print("Install with: pip install hanzo[mcp]")
195
214
  return
196
-
215
+
197
216
  import os
198
217
  import json
199
218
  from pathlib import Path
200
-
219
+
201
220
  config_path = Path(os.path.expanduser(path))
202
-
221
+
203
222
  # Create config
204
223
  config = {
205
224
  "mcpServers": {
206
225
  "hanzo-mcp": {
207
226
  "command": "hanzo",
208
- "args": ["mcp", "serve", "--transport", "stdio"]
227
+ "args": ["mcp", "serve", "--transport", "stdio"],
209
228
  }
210
229
  }
211
230
  }
212
-
231
+
213
232
  # Check if file exists
214
233
  if config_path.exists():
215
234
  try:
216
235
  with open(config_path, "r") as f:
217
236
  existing = json.load(f)
218
-
237
+
219
238
  if "mcpServers" not in existing:
220
239
  existing["mcpServers"] = {}
221
-
240
+
222
241
  existing["mcpServers"]["hanzo-mcp"] = config["mcpServers"]["hanzo-mcp"]
223
242
  config = existing
224
243
  except Exception as e:
225
- console.print(f"[yellow]Warning: Could not read existing config: {e}[/yellow]")
226
-
244
+ console.print(
245
+ f"[yellow]Warning: Could not read existing config: {e}[/yellow]"
246
+ )
247
+
227
248
  # Write config
228
249
  config_path.parent.mkdir(parents=True, exist_ok=True)
229
-
250
+
230
251
  with open(config_path, "w") as f:
231
252
  json.dump(config, f, indent=2)
232
-
253
+
233
254
  console.print(f"[green]✓[/green] Installed hanzo-mcp in Claude Desktop")
234
255
  console.print(f" Config: {config_path}")
235
- console.print("\nRestart Claude Desktop for changes to take effect")
256
+ console.print("\nRestart Claude Desktop for changes to take effect")
hanzo/commands/miner.py CHANGED
@@ -1,13 +1,10 @@
1
1
  """Mining commands for distributed AI compute."""
2
2
 
3
- import asyncio
4
- from typing import Optional, List
5
-
6
3
  import click
7
- from rich.progress import Progress, SpinnerColumn, TextColumn
8
4
  from rich.table import Table
5
+ from rich.progress import Progress, TextColumn, SpinnerColumn
9
6
 
10
- from ..utils.output import console, handle_errors
7
+ from ..utils.output import console
11
8
 
12
9
 
13
10
  @click.group(name="miner")
@@ -20,11 +17,24 @@ def miner_group():
20
17
  @click.option("--name", "-n", help="Miner name (auto-generated if not provided)")
21
18
  @click.option("--wallet", "-w", help="Wallet address for rewards")
22
19
  @click.option("--models", "-m", multiple=True, help="Models to support")
23
- @click.option("--device", type=click.Choice(["cpu", "gpu", "auto"]), default="auto", help="Device to use")
20
+ @click.option(
21
+ "--device",
22
+ type=click.Choice(["cpu", "gpu", "auto"]),
23
+ default="auto",
24
+ help="Device to use",
25
+ )
24
26
  @click.option("--network", default="mainnet", help="Network to join")
25
27
  @click.option("--min-stake", type=float, help="Minimum stake requirement")
26
28
  @click.pass_context
27
- async def start(ctx, name: str, wallet: str, models: tuple, device: str, network: str, min_stake: float):
29
+ async def start(
30
+ ctx,
31
+ name: str,
32
+ wallet: str,
33
+ models: tuple,
34
+ device: str,
35
+ network: str,
36
+ min_stake: float,
37
+ ):
28
38
  """Start mining (contribute compute to network)."""
29
39
  try:
30
40
  from hanzo_miner import HanzoMiner
@@ -32,29 +42,25 @@ async def start(ctx, name: str, wallet: str, models: tuple, device: str, network
32
42
  console.print("[red]Error:[/red] hanzo-miner not installed")
33
43
  console.print("Install with: pip install hanzo[miner]")
34
44
  return
35
-
45
+
36
46
  # Check wallet
37
47
  if not wallet:
38
48
  console.print("[yellow]Warning:[/yellow] No wallet address provided")
39
49
  console.print("You won't earn rewards without a wallet")
40
50
  if not click.confirm("Continue without wallet?"):
41
51
  return
42
-
52
+
43
53
  miner = HanzoMiner(
44
- name=name,
45
- wallet=wallet,
46
- device=device,
47
- network=network,
48
- min_stake=min_stake
54
+ name=name, wallet=wallet, device=device, network=network, min_stake=min_stake
49
55
  )
50
-
56
+
51
57
  with Progress(
52
58
  SpinnerColumn(),
53
59
  TextColumn("[progress.description]{task.description}"),
54
60
  console=console,
55
61
  ) as progress:
56
62
  task = progress.add_task("Starting miner...", total=None)
57
-
63
+
58
64
  try:
59
65
  # Start miner
60
66
  await miner.start(models=list(models) if models else None)
@@ -63,14 +69,14 @@ async def start(ctx, name: str, wallet: str, models: tuple, device: str, network
63
69
  progress.stop()
64
70
  console.print(f"[red]Failed to start miner: {e}[/red]")
65
71
  return
66
-
72
+
67
73
  console.print(f"[green]✓[/green] Miner started")
68
74
  console.print(f" Name: {miner.name}")
69
75
  console.print(f" Network: {network}")
70
76
  console.print(f" Device: {device}")
71
77
  if wallet:
72
78
  console.print(f" Wallet: {wallet[:8]}...{wallet[-4:]}")
73
-
79
+
74
80
  # Show initial stats
75
81
  stats = await miner.get_stats()
76
82
  if stats:
@@ -78,31 +84,35 @@ async def start(ctx, name: str, wallet: str, models: tuple, device: str, network
78
84
  console.print(f" Jobs completed: {stats.get('jobs_completed', 0)}")
79
85
  console.print(f" Tokens earned: {stats.get('tokens_earned', 0)}")
80
86
  console.print(f" Uptime: {stats.get('uptime', '0s')}")
81
-
87
+
82
88
  console.print("\nPress Ctrl+C to stop mining\n")
83
89
  console.print("[dim]Logs:[/dim]")
84
-
90
+
85
91
  try:
86
92
  # Stream logs and stats
87
93
  async for event in miner.stream_events():
88
94
  if event["type"] == "log":
89
95
  console.print(event["message"], end="")
90
96
  elif event["type"] == "job":
91
- console.print(f"[green]Job completed:[/green] {event['job_id']} (+{event['tokens']} tokens)")
97
+ console.print(
98
+ f"[green]Job completed:[/green] {event['job_id']} (+{event['tokens']} tokens)"
99
+ )
92
100
  elif event["type"] == "error":
93
101
  console.print(f"[red]Error:[/red] {event['message']}")
94
102
  except KeyboardInterrupt:
95
103
  console.print("\n[yellow]Stopping miner...[/yellow]")
96
104
  await miner.stop()
97
-
105
+
98
106
  # Show final stats
99
107
  final_stats = await miner.get_stats()
100
108
  if final_stats:
101
109
  console.print("\n[cyan]Session Summary:[/cyan]")
102
110
  console.print(f" Jobs completed: {final_stats.get('jobs_completed', 0)}")
103
111
  console.print(f" Tokens earned: {final_stats.get('tokens_earned', 0)}")
104
- console.print(f" Average job time: {final_stats.get('avg_job_time', 'N/A')}")
105
-
112
+ console.print(
113
+ f" Average job time: {final_stats.get('avg_job_time', 'N/A')}"
114
+ )
115
+
106
116
  console.print("[green]✓[/green] Miner stopped")
107
117
 
108
118
 
@@ -116,7 +126,7 @@ async def stop(ctx, name: str):
116
126
  except ImportError:
117
127
  console.print("[red]Error:[/red] hanzo-miner not installed")
118
128
  return
119
-
129
+
120
130
  if name:
121
131
  miner = HanzoMiner.get_by_name(name)
122
132
  if miner:
@@ -148,24 +158,24 @@ async def status(ctx, name: str, detailed: bool):
148
158
  except ImportError:
149
159
  console.print("[red]Error:[/red] hanzo-miner not installed")
150
160
  return
151
-
161
+
152
162
  if name:
153
163
  # Show specific miner
154
164
  miner = HanzoMiner.get_by_name(name)
155
165
  if not miner:
156
166
  console.print(f"[red]Miner not found: {name}[/red]")
157
167
  return
158
-
168
+
159
169
  miners = [miner]
160
170
  else:
161
171
  # Show all miners
162
172
  miners = HanzoMiner.get_all()
163
-
173
+
164
174
  if not miners:
165
175
  console.print("[yellow]No miners running[/yellow]")
166
176
  console.print("Start mining with: hanzo miner start")
167
177
  return
168
-
178
+
169
179
  # Create table
170
180
  table = Table(title="Active Miners")
171
181
  table.add_column("Name", style="cyan")
@@ -174,7 +184,7 @@ async def status(ctx, name: str, detailed: bool):
174
184
  table.add_column("Jobs", style="blue")
175
185
  table.add_column("Tokens", style="magenta")
176
186
  table.add_column("Uptime", style="white")
177
-
187
+
178
188
  for miner in miners:
179
189
  stats = await miner.get_stats()
180
190
  table.add_row(
@@ -183,26 +193,30 @@ async def status(ctx, name: str, detailed: bool):
183
193
  stats.get("device", "unknown"),
184
194
  str(stats.get("jobs_completed", 0)),
185
195
  f"{stats.get('tokens_earned', 0):.2f}",
186
- stats.get("uptime", "0s")
196
+ stats.get("uptime", "0s"),
187
197
  )
188
-
198
+
189
199
  console.print(table)
190
-
200
+
191
201
  if detailed and len(miners) == 1:
192
202
  # Show detailed stats for single miner
193
203
  miner = miners[0]
194
204
  stats = await miner.get_stats()
195
-
205
+
196
206
  console.print("\n[cyan]Detailed Statistics:[/cyan]")
197
207
  console.print(f" Network: {stats.get('network', 'unknown')}")
198
208
  console.print(f" Wallet: {stats.get('wallet', 'Not set')}")
199
209
  console.print(f" Models: {', '.join(stats.get('models', []))}")
200
- console.print(f" Memory: {stats.get('memory_used', 0)} / {stats.get('memory_total', 0)} MB")
210
+ console.print(
211
+ f" Memory: {stats.get('memory_used', 0)} / {stats.get('memory_total', 0)} MB"
212
+ )
201
213
  console.print(f" CPU: {stats.get('cpu_percent', 0)}%")
202
-
214
+
203
215
  if gpu := stats.get("gpu"):
204
- console.print(f" GPU: {gpu['name']} ({gpu['memory_used']} / {gpu['memory_total']} MB)")
205
-
216
+ console.print(
217
+ f" GPU: {gpu['name']} ({gpu['memory_used']} / {gpu['memory_total']} MB)"
218
+ )
219
+
206
220
  console.print(f"\n[cyan]Performance:[/cyan]")
207
221
  console.print(f" Average job time: {stats.get('avg_job_time', 'N/A')}")
208
222
  console.print(f" Success rate: {stats.get('success_rate', 0)}%")
@@ -219,34 +233,34 @@ async def leaderboard(ctx, network: str):
219
233
  except ImportError:
220
234
  console.print("[red]Error:[/red] hanzo-miner not installed")
221
235
  return
222
-
236
+
223
237
  with console.status("Loading leaderboard..."):
224
238
  try:
225
239
  leaders = await get_leaderboard(network=network)
226
240
  except Exception as e:
227
241
  console.print(f"[red]Failed to load leaderboard: {e}[/red]")
228
242
  return
229
-
243
+
230
244
  if not leaders:
231
245
  console.print("[yellow]No data available[/yellow]")
232
246
  return
233
-
247
+
234
248
  table = Table(title=f"Mining Leaderboard - {network}")
235
249
  table.add_column("Rank", style="cyan")
236
250
  table.add_column("Miner", style="green")
237
251
  table.add_column("Jobs", style="yellow")
238
252
  table.add_column("Tokens", style="magenta")
239
253
  table.add_column("Success Rate", style="blue")
240
-
254
+
241
255
  for i, leader in enumerate(leaders[:20], 1):
242
256
  table.add_row(
243
257
  str(i),
244
258
  leader["name"],
245
259
  str(leader["jobs"]),
246
260
  f"{leader['tokens']:.2f}",
247
- f"{leader['success_rate']}%"
261
+ f"{leader['success_rate']}%",
248
262
  )
249
-
263
+
250
264
  console.print(table)
251
265
 
252
266
 
@@ -261,24 +275,26 @@ async def earnings(ctx, wallet: str, network: str):
261
275
  except ImportError:
262
276
  console.print("[red]Error:[/red] hanzo-miner not installed")
263
277
  return
264
-
278
+
265
279
  with console.status("Checking earnings..."):
266
280
  try:
267
281
  data = await check_earnings(wallet=wallet, network=network)
268
282
  except Exception as e:
269
283
  console.print(f"[red]Failed to check earnings: {e}[/red]")
270
284
  return
271
-
285
+
272
286
  console.print(f"[cyan]Earnings for {wallet[:8]}...{wallet[-4:]}[/cyan]")
273
287
  console.print(f" Network: {network}")
274
288
  console.print(f" Total earned: {data.get('total_earned', 0):.2f} tokens")
275
289
  console.print(f" Available: {data.get('available', 0):.2f} tokens")
276
290
  console.print(f" Pending: {data.get('pending', 0):.2f} tokens")
277
-
291
+
278
292
  if history := data.get("recent_jobs"):
279
293
  console.print("\n[cyan]Recent Jobs:[/cyan]")
280
294
  for job in history[:5]:
281
- console.print(f" • {job['timestamp']}: +{job['tokens']} tokens ({job['model']})")
295
+ console.print(
296
+ f" • {job['timestamp']}: +{job['tokens']} tokens ({job['model']})"
297
+ )
282
298
 
283
299
 
284
300
  @miner_group.command()
@@ -294,30 +310,27 @@ async def withdraw(ctx, amount: float, wallet: str, to: str, network: str):
294
310
  except ImportError:
295
311
  console.print("[red]Error:[/red] hanzo-miner not installed")
296
312
  return
297
-
313
+
298
314
  # Confirm withdrawal
299
315
  console.print(f"[yellow]Withdrawal Request:[/yellow]")
300
316
  console.print(f" Amount: {amount} tokens")
301
317
  console.print(f" From: {wallet[:8]}...{wallet[-4:]}")
302
318
  console.print(f" To: {to[:8]}...{to[-4:]}")
303
319
  console.print(f" Network: {network}")
304
-
320
+
305
321
  if not click.confirm("Proceed with withdrawal?"):
306
322
  return
307
-
323
+
308
324
  with console.status("Processing withdrawal..."):
309
325
  try:
310
326
  result = await withdraw_earnings(
311
- wallet=wallet,
312
- amount=amount,
313
- destination=to,
314
- network=network
327
+ wallet=wallet, amount=amount, destination=to, network=network
315
328
  )
316
-
329
+
317
330
  console.print(f"[green]✓[/green] Withdrawal successful")
318
331
  console.print(f" Transaction: {result['tx_hash']}")
319
332
  console.print(f" Amount: {result['amount']} tokens")
320
333
  console.print(f" Fee: {result['fee']} tokens")
321
-
334
+
322
335
  except Exception as e:
323
- console.print(f"[red]Withdrawal failed: {e}[/red]")
336
+ console.print(f"[red]Withdrawal failed: {e}[/red]")