hanzo 0.2.3__py3-none-any.whl → 0.2.5__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 +4 -93
- hanzo/__main__.py +6 -0
- hanzo/cli.py +232 -8
- hanzo/commands/__init__.py +3 -0
- hanzo/commands/agent.py +112 -0
- hanzo/commands/auth.py +324 -0
- hanzo/commands/chat.py +183 -0
- hanzo/commands/cluster.py +428 -0
- hanzo/commands/config.py +240 -0
- hanzo/commands/mcp.py +235 -0
- hanzo/commands/miner.py +323 -0
- hanzo/commands/network.py +333 -0
- hanzo/commands/repl.py +186 -0
- hanzo/commands/tools.py +335 -0
- hanzo/interactive/__init__.py +3 -0
- hanzo/interactive/dashboard.py +125 -0
- hanzo/interactive/repl.py +184 -0
- hanzo/router/__init__.py +13 -7
- hanzo/utils/__init__.py +3 -0
- hanzo/utils/config.py +170 -0
- hanzo/utils/output.py +103 -0
- {hanzo-0.2.3.dist-info → hanzo-0.2.5.dist-info}/METADATA +6 -3
- hanzo-0.2.5.dist-info/RECORD +27 -0
- hanzo-0.2.3.dist-info/RECORD +0 -9
- {hanzo-0.2.3.dist-info → hanzo-0.2.5.dist-info}/WHEEL +0 -0
- {hanzo-0.2.3.dist-info → hanzo-0.2.5.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,333 @@
|
|
|
1
|
+
"""Network commands for agent networks."""
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
from typing import List, Optional, Dict, Any
|
|
5
|
+
|
|
6
|
+
import click
|
|
7
|
+
from rich.table import Table
|
|
8
|
+
from rich.progress import Progress, SpinnerColumn, TextColumn
|
|
9
|
+
|
|
10
|
+
from ..utils.output import console, handle_errors
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@click.group(name="network")
|
|
14
|
+
def network_group():
|
|
15
|
+
"""Manage agent networks."""
|
|
16
|
+
pass
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@network_group.command()
|
|
20
|
+
@click.argument("prompt")
|
|
21
|
+
@click.option("--agents", "-a", type=int, default=3, help="Number of agents")
|
|
22
|
+
@click.option("--model", "-m", help="Model to use")
|
|
23
|
+
@click.option("--mode", type=click.Choice(["local", "distributed", "hybrid"]), default="hybrid", help="Execution mode")
|
|
24
|
+
@click.option("--consensus", is_flag=True, help="Require consensus")
|
|
25
|
+
@click.option("--timeout", "-t", type=int, default=300, help="Timeout in seconds")
|
|
26
|
+
@click.pass_context
|
|
27
|
+
async def dispatch(ctx, prompt: str, agents: int, model: str, mode: str, consensus: bool, timeout: int):
|
|
28
|
+
"""Dispatch work to agent network."""
|
|
29
|
+
try:
|
|
30
|
+
from hanzo_network import NetworkDispatcher
|
|
31
|
+
except ImportError:
|
|
32
|
+
console.print("[red]Error:[/red] hanzo-network not installed")
|
|
33
|
+
console.print("Install with: pip install hanzo[network]")
|
|
34
|
+
return
|
|
35
|
+
|
|
36
|
+
dispatcher = NetworkDispatcher(mode=mode)
|
|
37
|
+
|
|
38
|
+
with Progress(
|
|
39
|
+
SpinnerColumn(),
|
|
40
|
+
TextColumn("[progress.description]{task.description}"),
|
|
41
|
+
console=console,
|
|
42
|
+
) as progress:
|
|
43
|
+
task = progress.add_task("Dispatching to network...", total=None)
|
|
44
|
+
|
|
45
|
+
try:
|
|
46
|
+
# Create job
|
|
47
|
+
job = await dispatcher.create_job(
|
|
48
|
+
prompt=prompt,
|
|
49
|
+
num_agents=agents,
|
|
50
|
+
model=model,
|
|
51
|
+
consensus=consensus,
|
|
52
|
+
timeout=timeout
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
progress.update(task, description=f"Job {job['id']} - Finding agents...")
|
|
56
|
+
|
|
57
|
+
# Execute job
|
|
58
|
+
result = await dispatcher.execute_job(job)
|
|
59
|
+
|
|
60
|
+
progress.update(task, completed=True)
|
|
61
|
+
|
|
62
|
+
except Exception as e:
|
|
63
|
+
progress.stop()
|
|
64
|
+
console.print(f"[red]Dispatch failed: {e}[/red]")
|
|
65
|
+
return
|
|
66
|
+
|
|
67
|
+
# Show results
|
|
68
|
+
console.print(f"\n[green]✓[/green] Job completed")
|
|
69
|
+
console.print(f" ID: {result['job_id']}")
|
|
70
|
+
console.print(f" Agents: {result['num_agents']}")
|
|
71
|
+
console.print(f" Duration: {result['duration']}s")
|
|
72
|
+
|
|
73
|
+
if consensus:
|
|
74
|
+
console.print(f" Consensus: {result.get('consensus_reached', False)}")
|
|
75
|
+
|
|
76
|
+
console.print("\n[cyan]Results:[/cyan]")
|
|
77
|
+
|
|
78
|
+
if consensus and result.get("consensus_result"):
|
|
79
|
+
console.print(result["consensus_result"])
|
|
80
|
+
else:
|
|
81
|
+
for i, agent_result in enumerate(result["agent_results"], 1):
|
|
82
|
+
console.print(f"\n[yellow]Agent {i} ({agent_result['agent_id']}):[/yellow]")
|
|
83
|
+
console.print(agent_result["result"])
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
@network_group.command()
|
|
87
|
+
@click.option("--mode", type=click.Choice(["local", "distributed", "all"]), default="all", help="Network mode")
|
|
88
|
+
@click.pass_context
|
|
89
|
+
async def agents(ctx, mode: str):
|
|
90
|
+
"""List available agents in network."""
|
|
91
|
+
try:
|
|
92
|
+
from hanzo_network import get_network_agents
|
|
93
|
+
except ImportError:
|
|
94
|
+
console.print("[red]Error:[/red] hanzo-network not installed")
|
|
95
|
+
return
|
|
96
|
+
|
|
97
|
+
with console.status("Discovering agents..."):
|
|
98
|
+
try:
|
|
99
|
+
agents = await get_network_agents(mode=mode)
|
|
100
|
+
except Exception as e:
|
|
101
|
+
console.print(f"[red]Failed to discover agents: {e}[/red]")
|
|
102
|
+
return
|
|
103
|
+
|
|
104
|
+
if not agents:
|
|
105
|
+
console.print("[yellow]No agents found[/yellow]")
|
|
106
|
+
if mode == "local":
|
|
107
|
+
console.print("Start local agents with: hanzo agent start")
|
|
108
|
+
return
|
|
109
|
+
|
|
110
|
+
# Group by type
|
|
111
|
+
local_agents = [a for a in agents if a["type"] == "local"]
|
|
112
|
+
network_agents = [a for a in agents if a["type"] == "network"]
|
|
113
|
+
|
|
114
|
+
if local_agents:
|
|
115
|
+
table = Table(title="Local Agents")
|
|
116
|
+
table.add_column("ID", style="cyan")
|
|
117
|
+
table.add_column("Name", style="green")
|
|
118
|
+
table.add_column("Model", style="yellow")
|
|
119
|
+
table.add_column("Status", style="blue")
|
|
120
|
+
table.add_column("Jobs", style="magenta")
|
|
121
|
+
|
|
122
|
+
for agent in local_agents:
|
|
123
|
+
table.add_row(
|
|
124
|
+
agent["id"][:8],
|
|
125
|
+
agent["name"],
|
|
126
|
+
agent.get("model", "default"),
|
|
127
|
+
agent["status"],
|
|
128
|
+
str(agent.get("jobs_completed", 0))
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
console.print(table)
|
|
132
|
+
|
|
133
|
+
if network_agents:
|
|
134
|
+
table = Table(title="Network Agents")
|
|
135
|
+
table.add_column("ID", style="cyan")
|
|
136
|
+
table.add_column("Location", style="green")
|
|
137
|
+
table.add_column("Model", style="yellow")
|
|
138
|
+
table.add_column("Latency", style="blue")
|
|
139
|
+
table.add_column("Cost", style="magenta")
|
|
140
|
+
|
|
141
|
+
for agent in network_agents:
|
|
142
|
+
table.add_row(
|
|
143
|
+
agent["id"][:8],
|
|
144
|
+
agent.get("location", "unknown"),
|
|
145
|
+
agent.get("model", "various"),
|
|
146
|
+
f"{agent.get('latency', 0)}ms",
|
|
147
|
+
f"${agent.get('cost_per_token', 0):.4f}"
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
console.print(table)
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
@network_group.command()
|
|
154
|
+
@click.option("--active", is_flag=True, help="Show only active jobs")
|
|
155
|
+
@click.option("--limit", "-n", type=int, default=10, help="Number of jobs to show")
|
|
156
|
+
@click.pass_context
|
|
157
|
+
async def jobs(ctx, active: bool, limit: int):
|
|
158
|
+
"""List network jobs."""
|
|
159
|
+
try:
|
|
160
|
+
from hanzo_network import get_network_jobs
|
|
161
|
+
except ImportError:
|
|
162
|
+
console.print("[red]Error:[/red] hanzo-network not installed")
|
|
163
|
+
return
|
|
164
|
+
|
|
165
|
+
with console.status("Loading jobs..."):
|
|
166
|
+
try:
|
|
167
|
+
jobs = await get_network_jobs(active_only=active, limit=limit)
|
|
168
|
+
except Exception as e:
|
|
169
|
+
console.print(f"[red]Failed to load jobs: {e}[/red]")
|
|
170
|
+
return
|
|
171
|
+
|
|
172
|
+
if not jobs:
|
|
173
|
+
console.print("[yellow]No jobs found[/yellow]")
|
|
174
|
+
return
|
|
175
|
+
|
|
176
|
+
table = Table(title="Network Jobs")
|
|
177
|
+
table.add_column("ID", style="cyan")
|
|
178
|
+
table.add_column("Status", style="green")
|
|
179
|
+
table.add_column("Agents", style="yellow")
|
|
180
|
+
table.add_column("Created", style="blue")
|
|
181
|
+
table.add_column("Duration", style="magenta")
|
|
182
|
+
|
|
183
|
+
for job in jobs:
|
|
184
|
+
table.add_row(
|
|
185
|
+
job["id"][:8],
|
|
186
|
+
job["status"],
|
|
187
|
+
str(job["num_agents"]),
|
|
188
|
+
job["created_at"],
|
|
189
|
+
f"{job.get('duration', 0)}s" if job.get("duration") else "-"
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
console.print(table)
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
@network_group.command()
|
|
196
|
+
@click.argument("job_id")
|
|
197
|
+
@click.pass_context
|
|
198
|
+
async def job(ctx, job_id: str):
|
|
199
|
+
"""Show job details."""
|
|
200
|
+
try:
|
|
201
|
+
from hanzo_network import get_job_details
|
|
202
|
+
except ImportError:
|
|
203
|
+
console.print("[red]Error:[/red] hanzo-network not installed")
|
|
204
|
+
return
|
|
205
|
+
|
|
206
|
+
with console.status("Loading job details..."):
|
|
207
|
+
try:
|
|
208
|
+
job = await get_job_details(job_id)
|
|
209
|
+
except Exception as e:
|
|
210
|
+
console.print(f"[red]Failed to load job: {e}[/red]")
|
|
211
|
+
return
|
|
212
|
+
|
|
213
|
+
console.print(f"[cyan]Job {job_id}[/cyan]")
|
|
214
|
+
console.print(f" Status: {job['status']}")
|
|
215
|
+
console.print(f" Created: {job['created_at']}")
|
|
216
|
+
console.print(f" Agents: {job['num_agents']}")
|
|
217
|
+
console.print(f" Mode: {job['mode']}")
|
|
218
|
+
|
|
219
|
+
if job["status"] == "completed":
|
|
220
|
+
console.print(f" Duration: {job['duration']}s")
|
|
221
|
+
console.print(f" Cost: ${job.get('total_cost', 0):.4f}")
|
|
222
|
+
|
|
223
|
+
console.print(f"\n[cyan]Prompt:[/cyan]")
|
|
224
|
+
console.print(job["prompt"])
|
|
225
|
+
|
|
226
|
+
if job["status"] == "completed" and job.get("results"):
|
|
227
|
+
console.print("\n[cyan]Results:[/cyan]")
|
|
228
|
+
for i, result in enumerate(job["results"], 1):
|
|
229
|
+
console.print(f"\n[yellow]Agent {i}:[/yellow]")
|
|
230
|
+
console.print(result["content"])
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
@network_group.command()
|
|
234
|
+
@click.option("--name", "-n", default="default", help="Swarm name")
|
|
235
|
+
@click.option("--agents", "-a", type=int, default=5, help="Number of agents")
|
|
236
|
+
@click.option("--model", "-m", help="Model to use")
|
|
237
|
+
@click.pass_context
|
|
238
|
+
async def swarm(ctx, name: str, agents: int, model: str):
|
|
239
|
+
"""Start a local agent swarm."""
|
|
240
|
+
try:
|
|
241
|
+
from hanzo_network import LocalSwarm
|
|
242
|
+
except ImportError:
|
|
243
|
+
console.print("[red]Error:[/red] hanzo-network not installed")
|
|
244
|
+
return
|
|
245
|
+
|
|
246
|
+
swarm = LocalSwarm(name=name, size=agents, model=model)
|
|
247
|
+
|
|
248
|
+
with Progress(
|
|
249
|
+
SpinnerColumn(),
|
|
250
|
+
TextColumn("[progress.description]{task.description}"),
|
|
251
|
+
console=console,
|
|
252
|
+
) as progress:
|
|
253
|
+
task = progress.add_task("Starting swarm...", total=None)
|
|
254
|
+
|
|
255
|
+
try:
|
|
256
|
+
await swarm.start()
|
|
257
|
+
progress.update(task, completed=True)
|
|
258
|
+
except Exception as e:
|
|
259
|
+
progress.stop()
|
|
260
|
+
console.print(f"[red]Failed to start swarm: {e}[/red]")
|
|
261
|
+
return
|
|
262
|
+
|
|
263
|
+
console.print(f"[green]✓[/green] Swarm '{name}' started with {agents} agents")
|
|
264
|
+
console.print("Use 'hanzo network dispatch --mode local' to send work to swarm")
|
|
265
|
+
console.print("\nPress Ctrl+C to stop swarm")
|
|
266
|
+
|
|
267
|
+
try:
|
|
268
|
+
# Keep swarm running
|
|
269
|
+
await swarm.run_forever()
|
|
270
|
+
except KeyboardInterrupt:
|
|
271
|
+
console.print("\n[yellow]Stopping swarm...[/yellow]")
|
|
272
|
+
await swarm.stop()
|
|
273
|
+
console.print("[green]✓[/green] Swarm stopped")
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
@network_group.command()
|
|
277
|
+
@click.pass_context
|
|
278
|
+
async def stats(ctx):
|
|
279
|
+
"""Show network statistics."""
|
|
280
|
+
try:
|
|
281
|
+
from hanzo_network import get_network_stats
|
|
282
|
+
except ImportError:
|
|
283
|
+
console.print("[red]Error:[/red] hanzo-network not installed")
|
|
284
|
+
return
|
|
285
|
+
|
|
286
|
+
with console.status("Loading network stats..."):
|
|
287
|
+
try:
|
|
288
|
+
stats = await get_network_stats()
|
|
289
|
+
except Exception as e:
|
|
290
|
+
console.print(f"[red]Failed to load stats: {e}[/red]")
|
|
291
|
+
return
|
|
292
|
+
|
|
293
|
+
console.print("[cyan]Network Statistics[/cyan]")
|
|
294
|
+
console.print(f" Total agents: {stats['total_agents']}")
|
|
295
|
+
console.print(f" Active agents: {stats['active_agents']}")
|
|
296
|
+
console.print(f" Total jobs: {stats['total_jobs']}")
|
|
297
|
+
console.print(f" Active jobs: {stats['active_jobs']}")
|
|
298
|
+
console.print(f" Success rate: {stats['success_rate']}%")
|
|
299
|
+
|
|
300
|
+
console.print(f"\n[cyan]Performance:[/cyan]")
|
|
301
|
+
console.print(f" Average latency: {stats['avg_latency']}ms")
|
|
302
|
+
console.print(f" Average job time: {stats['avg_job_time']}s")
|
|
303
|
+
console.print(f" Throughput: {stats['throughput']} jobs/min")
|
|
304
|
+
|
|
305
|
+
console.print(f"\n[cyan]Economics:[/cyan]")
|
|
306
|
+
console.print(f" Total tokens: {stats['total_tokens']:,}")
|
|
307
|
+
console.print(f" Average cost: ${stats['avg_cost']:.4f}/job")
|
|
308
|
+
console.print(f" Total cost: ${stats['total_cost']:.2f}")
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
@network_group.command()
|
|
312
|
+
@click.option("--enable/--disable", default=True, help="Enable or disable discovery")
|
|
313
|
+
@click.pass_context
|
|
314
|
+
async def discovery(ctx, enable: bool):
|
|
315
|
+
"""Configure network discovery."""
|
|
316
|
+
try:
|
|
317
|
+
from hanzo_network import configure_discovery
|
|
318
|
+
except ImportError:
|
|
319
|
+
console.print("[red]Error:[/red] hanzo-network not installed")
|
|
320
|
+
return
|
|
321
|
+
|
|
322
|
+
try:
|
|
323
|
+
await configure_discovery(enabled=enable)
|
|
324
|
+
|
|
325
|
+
if enable:
|
|
326
|
+
console.print("[green]✓[/green] Network discovery enabled")
|
|
327
|
+
console.print("Your agents will be discoverable by the network")
|
|
328
|
+
else:
|
|
329
|
+
console.print("[green]✓[/green] Network discovery disabled")
|
|
330
|
+
console.print("Your agents will only be accessible locally")
|
|
331
|
+
|
|
332
|
+
except Exception as e:
|
|
333
|
+
console.print(f"[red]Failed to configure discovery: {e}[/red]")
|
hanzo/commands/repl.py
ADDED
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
"""REPL command for interactive AI sessions."""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import sys
|
|
5
|
+
from typing import Optional
|
|
6
|
+
|
|
7
|
+
import click
|
|
8
|
+
|
|
9
|
+
from ..utils.output import console
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@click.group(name="repl")
|
|
13
|
+
def repl_group():
|
|
14
|
+
"""Interactive REPL for AI and MCP tools."""
|
|
15
|
+
pass
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@repl_group.command()
|
|
19
|
+
@click.option("--model", "-m", help="Default model to use")
|
|
20
|
+
@click.option("--local/--cloud", default=False, help="Use local cluster")
|
|
21
|
+
@click.option("--ipython", is_flag=True, help="Use IPython interface")
|
|
22
|
+
@click.option("--tui", is_flag=True, help="Use TUI interface")
|
|
23
|
+
@click.option("--voice", is_flag=True, help="Enable voice mode")
|
|
24
|
+
@click.pass_context
|
|
25
|
+
def start(ctx, model: str, local: bool, ipython: bool, tui: bool, voice: bool):
|
|
26
|
+
"""Start interactive REPL (like Claude Code in terminal)."""
|
|
27
|
+
try:
|
|
28
|
+
# Set up environment
|
|
29
|
+
if model:
|
|
30
|
+
os.environ["HANZO_DEFAULT_MODEL"] = model
|
|
31
|
+
if local:
|
|
32
|
+
os.environ["HANZO_USE_LOCAL"] = "true"
|
|
33
|
+
if voice:
|
|
34
|
+
os.environ["HANZO_ENABLE_VOICE"] = "true"
|
|
35
|
+
|
|
36
|
+
if ipython:
|
|
37
|
+
from hanzo_repl.ipython_repl import main
|
|
38
|
+
elif tui:
|
|
39
|
+
from hanzo_repl.textual_repl import main
|
|
40
|
+
else:
|
|
41
|
+
from hanzo_repl.cli import main
|
|
42
|
+
|
|
43
|
+
console.print("[cyan]Starting Hanzo REPL...[/cyan]")
|
|
44
|
+
console.print("All MCP tools available. Type 'help' for commands.\n")
|
|
45
|
+
|
|
46
|
+
sys.exit(main())
|
|
47
|
+
|
|
48
|
+
except ImportError:
|
|
49
|
+
console.print("[red]Error:[/red] hanzo-repl not installed")
|
|
50
|
+
console.print("Install with: pip install hanzo[repl]")
|
|
51
|
+
console.print("\nFeatures:")
|
|
52
|
+
console.print(" • Direct access to 70+ MCP tools")
|
|
53
|
+
console.print(" • Chat with AI that can use tools")
|
|
54
|
+
console.print(" • IPython magic commands")
|
|
55
|
+
console.print(" • Beautiful TUI interface")
|
|
56
|
+
console.print(" • Voice mode (optional)")
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
@repl_group.command()
|
|
60
|
+
@click.pass_context
|
|
61
|
+
def info(ctx):
|
|
62
|
+
"""Show REPL information and status."""
|
|
63
|
+
try:
|
|
64
|
+
from hanzo_repl import __version__
|
|
65
|
+
import hanzo_mcp
|
|
66
|
+
|
|
67
|
+
console.print("[cyan]Hanzo REPL[/cyan]")
|
|
68
|
+
console.print(f" Version: {__version__}")
|
|
69
|
+
console.print(f" MCP Tools: {len(hanzo_mcp.get_all_tools())}")
|
|
70
|
+
|
|
71
|
+
# Check available interfaces
|
|
72
|
+
interfaces = []
|
|
73
|
+
try:
|
|
74
|
+
import IPython
|
|
75
|
+
interfaces.append("IPython")
|
|
76
|
+
except ImportError:
|
|
77
|
+
pass
|
|
78
|
+
|
|
79
|
+
try:
|
|
80
|
+
import textual
|
|
81
|
+
interfaces.append("TUI")
|
|
82
|
+
except ImportError:
|
|
83
|
+
pass
|
|
84
|
+
|
|
85
|
+
try:
|
|
86
|
+
import speech_recognition
|
|
87
|
+
interfaces.append("Voice")
|
|
88
|
+
except ImportError:
|
|
89
|
+
pass
|
|
90
|
+
|
|
91
|
+
console.print(f" Interfaces: {', '.join(interfaces) or 'Basic'}")
|
|
92
|
+
|
|
93
|
+
# Check LLM providers
|
|
94
|
+
providers = []
|
|
95
|
+
if os.environ.get("OPENAI_API_KEY"):
|
|
96
|
+
providers.append("OpenAI")
|
|
97
|
+
if os.environ.get("ANTHROPIC_API_KEY"):
|
|
98
|
+
providers.append("Anthropic")
|
|
99
|
+
if os.environ.get("HANZO_API_KEY"):
|
|
100
|
+
providers.append("Hanzo AI")
|
|
101
|
+
|
|
102
|
+
console.print(f" Providers: {', '.join(providers) or 'None configured'}")
|
|
103
|
+
|
|
104
|
+
if not providers:
|
|
105
|
+
console.print("\n[yellow]No LLM providers configured[/yellow]")
|
|
106
|
+
console.print("Set one of: OPENAI_API_KEY, ANTHROPIC_API_KEY, HANZO_API_KEY")
|
|
107
|
+
|
|
108
|
+
except ImportError:
|
|
109
|
+
console.print("[red]Error:[/red] hanzo-repl not installed")
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
@repl_group.command()
|
|
113
|
+
@click.option("--interface", type=click.Choice(["all", "ipython", "tui", "voice"]), default="all")
|
|
114
|
+
@click.pass_context
|
|
115
|
+
def install_extras(ctx, interface: str):
|
|
116
|
+
"""Install optional REPL components."""
|
|
117
|
+
import subprocess
|
|
118
|
+
|
|
119
|
+
packages = {
|
|
120
|
+
"ipython": ["ipython>=8.0.0", "jupyter>=1.0.0"],
|
|
121
|
+
"tui": ["textual>=0.41.0", "textual-dev>=1.2.0"],
|
|
122
|
+
"voice": ["speechrecognition>=3.10.0", "pyttsx3>=2.90", "pyaudio>=0.2.11"]
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if interface == "all":
|
|
126
|
+
to_install = []
|
|
127
|
+
for pkgs in packages.values():
|
|
128
|
+
to_install.extend(pkgs)
|
|
129
|
+
else:
|
|
130
|
+
to_install = packages.get(interface, [])
|
|
131
|
+
|
|
132
|
+
if to_install:
|
|
133
|
+
console.print(f"[cyan]Installing {interface} components...[/cyan]")
|
|
134
|
+
cmd = [sys.executable, "-m", "pip", "install"] + to_install
|
|
135
|
+
|
|
136
|
+
try:
|
|
137
|
+
subprocess.run(cmd, check=True)
|
|
138
|
+
console.print(f"[green]✓[/green] Installed {interface} components")
|
|
139
|
+
except subprocess.CalledProcessError:
|
|
140
|
+
console.print(f"[red]Failed to install components[/red]")
|
|
141
|
+
console.print("Try manually: pip install hanzo-repl[voice]")
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
@repl_group.command()
|
|
145
|
+
@click.argument("command", nargs=-1, required=True)
|
|
146
|
+
@click.option("--model", "-m", help="Model to use")
|
|
147
|
+
@click.pass_context
|
|
148
|
+
def exec(ctx, command: tuple, model: str):
|
|
149
|
+
"""Execute a command in REPL and exit."""
|
|
150
|
+
try:
|
|
151
|
+
from hanzo_repl import create_repl
|
|
152
|
+
import asyncio
|
|
153
|
+
|
|
154
|
+
repl = create_repl(model=model)
|
|
155
|
+
command_str = " ".join(command)
|
|
156
|
+
|
|
157
|
+
async def run():
|
|
158
|
+
result = await repl.execute(command_str)
|
|
159
|
+
console.print(result)
|
|
160
|
+
|
|
161
|
+
asyncio.run(run())
|
|
162
|
+
|
|
163
|
+
except ImportError:
|
|
164
|
+
console.print("[red]Error:[/red] hanzo-repl not installed")
|
|
165
|
+
except Exception as e:
|
|
166
|
+
console.print(f"[red]Error: {e}[/red]")
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
@repl_group.command()
|
|
170
|
+
@click.pass_context
|
|
171
|
+
def demo(ctx):
|
|
172
|
+
"""Run REPL demo showcasing features."""
|
|
173
|
+
try:
|
|
174
|
+
from hanzo_repl.demos import run_demo
|
|
175
|
+
|
|
176
|
+
console.print("[cyan]Running Hanzo REPL demo...[/cyan]\n")
|
|
177
|
+
run_demo()
|
|
178
|
+
|
|
179
|
+
except ImportError:
|
|
180
|
+
console.print("[red]Error:[/red] hanzo-repl not installed")
|
|
181
|
+
console.print("\nThe demo would show:")
|
|
182
|
+
console.print(" • File operations with MCP tools")
|
|
183
|
+
console.print(" • Code search and analysis")
|
|
184
|
+
console.print(" • AI chat with tool usage")
|
|
185
|
+
console.print(" • IPython magic commands")
|
|
186
|
+
console.print(" • Voice interaction (if available)")
|