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/__init__.py +1 -1
- hanzo/__main__.py +1 -1
- hanzo/cli.py +86 -52
- hanzo/commands/__init__.py +12 -1
- hanzo/commands/agent.py +18 -21
- hanzo/commands/auth.py +63 -59
- hanzo/commands/chat.py +54 -35
- hanzo/commands/cluster.py +109 -78
- hanzo/commands/config.py +39 -38
- hanzo/commands/mcp.py +63 -42
- hanzo/commands/miner.py +71 -58
- hanzo/commands/network.py +64 -55
- hanzo/commands/repl.py +37 -30
- hanzo/commands/tools.py +52 -67
- hanzo/interactive/__init__.py +1 -1
- hanzo/interactive/dashboard.py +34 -44
- hanzo/interactive/repl.py +35 -32
- hanzo/mcp_server.py +7 -2
- hanzo/repl.py +13 -3
- hanzo/router/__init__.py +21 -9
- hanzo/utils/__init__.py +1 -1
- hanzo/utils/config.py +37 -35
- hanzo/utils/net_check.py +33 -29
- hanzo/utils/output.py +23 -18
- hanzo-0.3.8.dist-info/METADATA +138 -0
- hanzo-0.3.8.dist-info/RECORD +28 -0
- {hanzo-0.3.6.dist-info → hanzo-0.3.8.dist-info}/WHEEL +1 -2
- hanzo-0.3.8.dist-info/entry_points.txt +6 -0
- hanzo-0.3.6.dist-info/METADATA +0 -76
- hanzo-0.3.6.dist-info/RECORD +0 -29
- hanzo-0.3.6.dist-info/entry_points.txt +0 -2
- hanzo-0.3.6.dist-info/top_level.txt +0 -1
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
|
|
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(
|
|
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(
|
|
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,
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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]")
|