hanzo 0.2.3__py3-none-any.whl → 0.2.6__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.

@@ -0,0 +1,335 @@
1
+ """Tools management commands."""
2
+
3
+ import asyncio
4
+ from typing import List, Optional
5
+
6
+ import click
7
+ from rich.table import Table
8
+ from rich.syntax import Syntax
9
+
10
+ from ..utils.output import console, handle_errors
11
+
12
+
13
+ @click.group(name="tools")
14
+ def tools_group():
15
+ """Manage Hanzo tools and plugins."""
16
+ pass
17
+
18
+
19
+ @tools_group.command(name="list")
20
+ @click.option("--category", "-c", help="Filter by category")
21
+ @click.option("--installed", is_flag=True, help="Show only installed tools")
22
+ @click.pass_context
23
+ async def list_tools(ctx, category: str, installed: bool):
24
+ """List available tools."""
25
+ try:
26
+ from hanzo_tools import get_tool_registry
27
+ except ImportError:
28
+ console.print("[red]Error:[/red] hanzo-tools not installed")
29
+ console.print("Install with: pip install hanzo[tools]")
30
+ return
31
+
32
+ registry = get_tool_registry()
33
+
34
+ with console.status("Loading tools..."):
35
+ try:
36
+ tools = await registry.list_tools(
37
+ category=category,
38
+ installed_only=installed
39
+ )
40
+ except Exception as e:
41
+ console.print(f"[red]Failed to load tools: {e}[/red]")
42
+ return
43
+
44
+ if not tools:
45
+ console.print("[yellow]No tools found[/yellow]")
46
+ return
47
+
48
+ # Group by category
49
+ categories = {}
50
+ for tool in tools:
51
+ cat = tool.get("category", "uncategorized")
52
+ if cat not in categories:
53
+ categories[cat] = []
54
+ categories[cat].append(tool)
55
+
56
+ # Display tools
57
+ for cat, cat_tools in sorted(categories.items()):
58
+ table = Table(title=f"{cat.title()} Tools")
59
+ table.add_column("Name", style="cyan")
60
+ table.add_column("Version", style="green")
61
+ table.add_column("Description", style="white")
62
+ table.add_column("Status", style="yellow")
63
+
64
+ for tool in sorted(cat_tools, key=lambda t: t["name"]):
65
+ table.add_row(
66
+ tool["name"],
67
+ tool.get("version", "latest"),
68
+ tool.get("description", ""),
69
+ "installed" if tool.get("installed") else "available"
70
+ )
71
+
72
+ console.print(table)
73
+ if len(categories) > 1:
74
+ console.print()
75
+
76
+
77
+ @tools_group.command()
78
+ @click.argument("tool_name")
79
+ @click.option("--version", "-v", help="Specific version to install")
80
+ @click.pass_context
81
+ async def install(ctx, tool_name: str, version: str):
82
+ """Install a tool."""
83
+ try:
84
+ from hanzo_tools import get_tool_registry
85
+ except ImportError:
86
+ console.print("[red]Error:[/red] hanzo-tools not installed")
87
+ return
88
+
89
+ registry = get_tool_registry()
90
+
91
+ with console.status(f"Installing {tool_name}..."):
92
+ try:
93
+ result = await registry.install_tool(
94
+ name=tool_name,
95
+ version=version
96
+ )
97
+
98
+ console.print(f"[green]✓[/green] Installed {tool_name} v{result['version']}")
99
+
100
+ if deps := result.get("dependencies_installed"):
101
+ console.print(f" Dependencies: {', '.join(deps)}")
102
+
103
+ if config := result.get("post_install_message"):
104
+ console.print(f"\n[yellow]Configuration:[/yellow]")
105
+ console.print(config)
106
+
107
+ except Exception as e:
108
+ console.print(f"[red]Failed to install {tool_name}: {e}[/red]")
109
+
110
+
111
+ @tools_group.command()
112
+ @click.argument("tool_name")
113
+ @click.pass_context
114
+ async def uninstall(ctx, tool_name: str):
115
+ """Uninstall a tool."""
116
+ try:
117
+ from hanzo_tools import get_tool_registry
118
+ except ImportError:
119
+ console.print("[red]Error:[/red] hanzo-tools not installed")
120
+ return
121
+
122
+ registry = get_tool_registry()
123
+
124
+ if click.confirm(f"Uninstall {tool_name}?"):
125
+ with console.status(f"Uninstalling {tool_name}..."):
126
+ try:
127
+ await registry.uninstall_tool(tool_name)
128
+ console.print(f"[green]✓[/green] Uninstalled {tool_name}")
129
+ except Exception as e:
130
+ console.print(f"[red]Failed to uninstall {tool_name}: {e}[/red]")
131
+
132
+
133
+ @tools_group.command()
134
+ @click.argument("tool_name")
135
+ @click.option("--version", "-v", help="Target version")
136
+ @click.pass_context
137
+ async def update(ctx, tool_name: str, version: str):
138
+ """Update a tool."""
139
+ try:
140
+ from hanzo_tools import get_tool_registry
141
+ except ImportError:
142
+ console.print("[red]Error:[/red] hanzo-tools not installed")
143
+ return
144
+
145
+ registry = get_tool_registry()
146
+
147
+ with console.status(f"Updating {tool_name}..."):
148
+ try:
149
+ result = await registry.update_tool(
150
+ name=tool_name,
151
+ version=version
152
+ )
153
+
154
+ console.print(f"[green]✓[/green] Updated {tool_name}")
155
+ console.print(f" Previous: v{result['previous_version']}")
156
+ console.print(f" Current: v{result['current_version']}")
157
+
158
+ except Exception as e:
159
+ console.print(f"[red]Failed to update {tool_name}: {e}[/red]")
160
+
161
+
162
+ @tools_group.command()
163
+ @click.argument("tool_name")
164
+ @click.pass_context
165
+ async def info(ctx, tool_name: str):
166
+ """Show tool information."""
167
+ try:
168
+ from hanzo_tools import get_tool_registry
169
+ except ImportError:
170
+ console.print("[red]Error:[/red] hanzo-tools not installed")
171
+ return
172
+
173
+ registry = get_tool_registry()
174
+
175
+ with console.status(f"Loading {tool_name} info..."):
176
+ try:
177
+ info = await registry.get_tool_info(tool_name)
178
+ except Exception as e:
179
+ console.print(f"[red]Failed to get info: {e}[/red]")
180
+ return
181
+
182
+ console.print(f"[cyan]{info['name']}[/cyan]")
183
+ console.print(f" Version: {info['version']}")
184
+ console.print(f" Category: {info['category']}")
185
+ console.print(f" Author: {info.get('author', 'Unknown')}")
186
+ console.print(f" License: {info.get('license', 'Unknown')}")
187
+
188
+ if desc := info.get("description"):
189
+ console.print(f"\n{desc}")
190
+
191
+ if features := info.get("features"):
192
+ console.print("\n[cyan]Features:[/cyan]")
193
+ for feature in features:
194
+ console.print(f" • {feature}")
195
+
196
+ if deps := info.get("dependencies"):
197
+ console.print("\n[cyan]Dependencies:[/cyan]")
198
+ for dep in deps:
199
+ console.print(f" • {dep}")
200
+
201
+ if usage := info.get("usage_example"):
202
+ console.print("\n[cyan]Usage Example:[/cyan]")
203
+ syntax = Syntax(usage, "python", theme="monokai", line_numbers=False)
204
+ console.print(syntax)
205
+
206
+
207
+ @tools_group.command()
208
+ @click.argument("tool_name")
209
+ @click.argument("args", nargs=-1)
210
+ @click.option("--json", "-j", is_flag=True, help="Output as JSON")
211
+ @click.pass_context
212
+ async def run(ctx, tool_name: str, args: tuple, json: bool):
213
+ """Run a tool directly."""
214
+ try:
215
+ from hanzo_tools import run_tool
216
+ except ImportError:
217
+ console.print("[red]Error:[/red] hanzo-tools not installed")
218
+ return
219
+
220
+ # Parse arguments
221
+ tool_args = {}
222
+ for arg in args:
223
+ if "=" in arg:
224
+ key, value = arg.split("=", 1)
225
+ tool_args[key] = value
226
+ else:
227
+ console.print(f"[red]Invalid argument format: {arg}[/red]")
228
+ console.print("Use: key=value")
229
+ return
230
+
231
+ with console.status(f"Running {tool_name}..."):
232
+ try:
233
+ result = await run_tool(
234
+ name=tool_name,
235
+ args=tool_args
236
+ )
237
+
238
+ if json:
239
+ import json as json_lib
240
+ console.print_json(data=result)
241
+ else:
242
+ if isinstance(result, str):
243
+ console.print(result)
244
+ elif isinstance(result, dict):
245
+ for key, value in result.items():
246
+ console.print(f"{key}: {value}")
247
+ else:
248
+ console.print(result)
249
+
250
+ except Exception as e:
251
+ console.print(f"[red]Tool execution failed: {e}[/red]")
252
+
253
+
254
+ @tools_group.command()
255
+ @click.option("--check", is_flag=True, help="Check for updates only")
256
+ @click.pass_context
257
+ async def upgrade(ctx, check: bool):
258
+ """Upgrade all tools."""
259
+ try:
260
+ from hanzo_tools import get_tool_registry
261
+ except ImportError:
262
+ console.print("[red]Error:[/red] hanzo-tools not installed")
263
+ return
264
+
265
+ registry = get_tool_registry()
266
+
267
+ with console.status("Checking for updates..."):
268
+ try:
269
+ updates = await registry.check_updates()
270
+ except Exception as e:
271
+ console.print(f"[red]Failed to check updates: {e}[/red]")
272
+ return
273
+
274
+ if not updates:
275
+ console.print("[green]✓[/green] All tools are up to date")
276
+ return
277
+
278
+ # Show available updates
279
+ table = Table(title="Available Updates")
280
+ table.add_column("Tool", style="cyan")
281
+ table.add_column("Current", style="yellow")
282
+ table.add_column("Latest", style="green")
283
+ table.add_column("Changes", style="white")
284
+
285
+ for update in updates:
286
+ table.add_row(
287
+ update["name"],
288
+ update["current_version"],
289
+ update["latest_version"],
290
+ update.get("changelog_summary", "")
291
+ )
292
+
293
+ console.print(table)
294
+
295
+ if check:
296
+ return
297
+
298
+ # Perform updates
299
+ if click.confirm(f"Update {len(updates)} tools?"):
300
+ for update in updates:
301
+ with console.status(f"Updating {update['name']}..."):
302
+ try:
303
+ await registry.update_tool(update["name"])
304
+ console.print(f"[green]✓[/green] Updated {update['name']}")
305
+ except Exception as e:
306
+ console.print(f"[red]Failed to update {update['name']}: {e}[/red]")
307
+
308
+
309
+ @tools_group.command()
310
+ @click.argument("name")
311
+ @click.option("--template", "-t", help="Tool template to use")
312
+ @click.pass_context
313
+ async def create(ctx, name: str, template: str):
314
+ """Create a new custom tool."""
315
+ try:
316
+ from hanzo_tools import create_tool_template
317
+ except ImportError:
318
+ console.print("[red]Error:[/red] hanzo-tools not installed")
319
+ return
320
+
321
+ with console.status(f"Creating tool '{name}'..."):
322
+ try:
323
+ path = await create_tool_template(
324
+ name=name,
325
+ template=template or "basic"
326
+ )
327
+
328
+ console.print(f"[green]✓[/green] Created tool template at: {path}")
329
+ console.print("\nNext steps:")
330
+ console.print("1. Edit the tool implementation")
331
+ console.print("2. Test with: hanzo tools run {name}")
332
+ console.print("3. Package with: hanzo tools package {name}")
333
+
334
+ except Exception as e:
335
+ console.print(f"[red]Failed to create tool: {e}[/red]")
@@ -0,0 +1,3 @@
1
+ """Interactive modules for Hanzo CLI."""
2
+
3
+ __all__ = ["repl", "dashboard"]
@@ -0,0 +1,125 @@
1
+ """Dashboard interface for Hanzo CLI."""
2
+
3
+ from typing import Optional
4
+
5
+ from rich.console import Console
6
+ from rich.layout import Layout
7
+ from rich.panel import Panel
8
+ from rich.table import Table
9
+ from rich.live import Live
10
+ from rich.text import Text
11
+
12
+
13
+ def run_dashboard(refresh_rate: float = 1.0):
14
+ """Run the interactive dashboard."""
15
+ console = Console()
16
+
17
+ layout = Layout()
18
+ layout.split_column(
19
+ Layout(name="header", size=3),
20
+ Layout(name="body"),
21
+ Layout(name="footer", size=3)
22
+ )
23
+
24
+ layout["header"].update(
25
+ Panel(
26
+ Text("Hanzo AI Dashboard", style="bold cyan", justify="center"),
27
+ border_style="cyan"
28
+ )
29
+ )
30
+
31
+ layout["body"].split_row(
32
+ Layout(name="left"),
33
+ Layout(name="right")
34
+ )
35
+
36
+ layout["left"].split_column(
37
+ Layout(name="cluster", size=10),
38
+ Layout(name="agents")
39
+ )
40
+
41
+ layout["right"].split_column(
42
+ Layout(name="jobs", size=15),
43
+ Layout(name="logs")
44
+ )
45
+
46
+ def get_cluster_panel() -> Panel:
47
+ """Get cluster status panel."""
48
+ table = Table(show_header=False, box=None)
49
+ table.add_column("Key", style="cyan")
50
+ table.add_column("Value", style="white")
51
+
52
+ # Mock data - would be real in production
53
+ table.add_row("Status", "[green]Running[/green]")
54
+ table.add_row("Nodes", "3")
55
+ table.add_row("Models", "llama-3.2-3b, gpt-4")
56
+ table.add_row("Port", "8000")
57
+
58
+ return Panel(table, title="Cluster", border_style="green")
59
+
60
+ def get_agents_panel() -> Panel:
61
+ """Get agents panel."""
62
+ table = Table()
63
+ table.add_column("ID", style="cyan")
64
+ table.add_column("Name", style="green")
65
+ table.add_column("Status", style="yellow")
66
+ table.add_column("Jobs", style="magenta")
67
+
68
+ # Mock data
69
+ table.add_row("a1b2", "researcher", "idle", "42")
70
+ table.add_row("c3d4", "coder", "busy", "17")
71
+ table.add_row("e5f6", "analyst", "idle", "23")
72
+
73
+ return Panel(table, title="Agents", border_style="blue")
74
+
75
+ def get_jobs_panel() -> Panel:
76
+ """Get jobs panel."""
77
+ table = Table()
78
+ table.add_column("ID", style="cyan", width=8)
79
+ table.add_column("Type", style="green")
80
+ table.add_column("Status", style="yellow")
81
+
82
+ # Mock data
83
+ table.add_row("j001", "chat", "complete")
84
+ table.add_row("j002", "analysis", "running")
85
+ table.add_row("j003", "search", "queued")
86
+
87
+ return Panel(table, title="Recent Jobs", border_style="yellow")
88
+
89
+ def get_logs_panel() -> Panel:
90
+ """Get logs panel."""
91
+ logs = """[dim]2024-01-20 10:15:23[/dim] Agent started: researcher
92
+ [dim]2024-01-20 10:15:24[/dim] Job j002 assigned to coder
93
+ [dim]2024-01-20 10:15:25[/dim] Model loaded: llama-3.2-3b
94
+ [dim]2024-01-20 10:15:26[/dim] Network peer connected: node-2
95
+ [dim]2024-01-20 10:15:27[/dim] Job j001 completed (2.3s)"""
96
+
97
+ return Panel(logs, title="Logs", border_style="dim")
98
+
99
+ layout["footer"].update(
100
+ Panel(
101
+ "[bold]Q[/bold] Quit [bold]R[/bold] Refresh [bold]C[/bold] Clear",
102
+ border_style="dim"
103
+ )
104
+ )
105
+
106
+ # Update panels
107
+ layout["cluster"].update(get_cluster_panel())
108
+ layout["agents"].update(get_agents_panel())
109
+ layout["jobs"].update(get_jobs_panel())
110
+ layout["logs"].update(get_logs_panel())
111
+
112
+ try:
113
+ with Live(layout, refresh_per_second=1/refresh_rate, screen=True):
114
+ while True:
115
+ import time
116
+ time.sleep(refresh_rate)
117
+
118
+ # Update dynamic panels
119
+ layout["cluster"].update(get_cluster_panel())
120
+ layout["agents"].update(get_agents_panel())
121
+ layout["jobs"].update(get_jobs_panel())
122
+ layout["logs"].update(get_logs_panel())
123
+
124
+ except KeyboardInterrupt:
125
+ console.print("\n[yellow]Dashboard closed[/yellow]")
@@ -0,0 +1,184 @@
1
+ """Interactive REPL for Hanzo CLI."""
2
+
3
+ import asyncio
4
+ from pathlib import Path
5
+ from typing import Dict, Any, Optional
6
+
7
+ from prompt_toolkit import PromptSession
8
+ from prompt_toolkit.history import FileHistory
9
+ from prompt_toolkit.auto_suggest import AutoSuggestFromHistory
10
+ from prompt_toolkit.completion import WordCompleter
11
+ from rich.console import Console
12
+ from rich.markdown import Markdown
13
+
14
+
15
+ class HanzoREPL:
16
+ """Interactive REPL for Hanzo CLI."""
17
+
18
+ def __init__(self, console: Optional[Console] = None):
19
+ self.console = console or Console()
20
+ self.session = PromptSession(
21
+ history=FileHistory(".hanzo_repl_history"),
22
+ auto_suggest=AutoSuggestFromHistory(),
23
+ )
24
+ self.commands = {
25
+ "help": self.show_help,
26
+ "exit": self.exit_repl,
27
+ "quit": self.exit_repl,
28
+ "clear": self.clear_screen,
29
+ "status": self.show_status,
30
+ }
31
+ self.running = False
32
+
33
+ async def run(self):
34
+ """Run the REPL."""
35
+ self.running = True
36
+ # Don't print welcome message here since it's already printed in cli.py
37
+
38
+ # Set up command completer
39
+ completer = WordCompleter(
40
+ list(self.commands.keys()) + ["chat", "agent", "cluster", "mcp", "network"],
41
+ ignore_case=True
42
+ )
43
+
44
+ while self.running:
45
+ try:
46
+ # Get input
47
+ command = await self.session.prompt_async(
48
+ "hanzo> ",
49
+ completer=completer
50
+ )
51
+
52
+ if not command.strip():
53
+ continue
54
+
55
+ # Parse command
56
+ parts = command.strip().split(maxsplit=1)
57
+ cmd = parts[0].lower()
58
+ args = parts[1] if len(parts) > 1 else ""
59
+
60
+ # Execute command
61
+ if cmd in self.commands:
62
+ await self.commands[cmd](args)
63
+ else:
64
+ await self.execute_command(cmd, args)
65
+
66
+ except KeyboardInterrupt:
67
+ continue
68
+ except EOFError:
69
+ break
70
+ except Exception as e:
71
+ self.console.print(f"[red]Error: {e}[/red]")
72
+
73
+ async def show_help(self, args: str = ""):
74
+ """Show help message."""
75
+ help_text = """
76
+ # Hanzo Interactive Mode
77
+
78
+ ## Built-in Commands:
79
+ - `help` - Show this help message
80
+ - `exit/quit` - Exit interactive mode
81
+ - `clear` - Clear the screen
82
+ - `status` - Show system status
83
+
84
+ ## CLI Commands:
85
+ All Hanzo CLI commands are available:
86
+ - `chat <message>` - Chat with AI
87
+ - `agent start` - Start an agent
88
+ - `cluster status` - Check cluster status
89
+ - `mcp tools` - List MCP tools
90
+ - `network agents` - List network agents
91
+
92
+ ## Examples:
93
+ ```
94
+ hanzo> chat How do I create a Python web server?
95
+ hanzo> agent list
96
+ hanzo> cluster start --models llama-3.2-3b
97
+ hanzo> mcp run read_file --arg path=README.md
98
+ ```
99
+
100
+ ## Tips:
101
+ - Use Tab for command completion
102
+ - Use ↑/↓ for command history
103
+ - Use Ctrl+R for reverse search
104
+ """
105
+ self.console.print(Markdown(help_text))
106
+
107
+ def exit_repl(self, args: str = ""):
108
+ """Exit the REPL."""
109
+ self.running = False
110
+ self.console.print("\n[yellow]Goodbye![/yellow]")
111
+
112
+ def clear_screen(self, args: str = ""):
113
+ """Clear the screen."""
114
+ self.console.clear()
115
+
116
+ async def show_status(self, args: str = ""):
117
+ """Show system status."""
118
+ status = {
119
+ "cluster": await self.check_cluster_status(),
120
+ "agents": await self.count_agents(),
121
+ "auth": self.check_auth_status(),
122
+ }
123
+
124
+ self.console.print("[cyan]System Status:[/cyan]")
125
+ self.console.print(f" Cluster: {status['cluster']}")
126
+ self.console.print(f" Agents: {status['agents']}")
127
+ self.console.print(f" Auth: {status['auth']}")
128
+
129
+ async def execute_command(self, cmd: str, args: str):
130
+ """Execute a CLI command."""
131
+ # Import here to avoid circular imports
132
+ from .. import cli
133
+ import click
134
+ import sys
135
+
136
+ # Build command line
137
+ argv = [cmd]
138
+ if args:
139
+ import shlex
140
+ argv.extend(shlex.split(args))
141
+
142
+ # Create a new context
143
+ try:
144
+ # Save original argv
145
+ orig_argv = sys.argv
146
+ sys.argv = ["hanzo"] + argv
147
+
148
+ # Execute command
149
+ ctx = click.Context(cli.cli)
150
+ cli.cli.invoke(ctx)
151
+
152
+ except SystemExit:
153
+ # Catch exit from commands
154
+ pass
155
+ except Exception as e:
156
+ self.console.print(f"[red]Command error: {e}[/red]")
157
+ finally:
158
+ # Restore argv
159
+ sys.argv = orig_argv
160
+
161
+ async def check_cluster_status(self) -> str:
162
+ """Check if cluster is running."""
163
+ try:
164
+ import httpx
165
+ async with httpx.AsyncClient() as client:
166
+ response = await client.get("http://localhost:8000/health", timeout=1.0)
167
+ return "running" if response.status_code == 200 else "not responding"
168
+ except:
169
+ return "not running"
170
+
171
+ async def count_agents(self) -> int:
172
+ """Count running agents."""
173
+ # This would check actual agent status
174
+ return 0
175
+
176
+ def check_auth_status(self) -> str:
177
+ """Check authentication status."""
178
+ import os
179
+ if os.environ.get("HANZO_API_KEY"):
180
+ return "authenticated (API key)"
181
+ elif (Path.home() / ".hanzo" / "auth.json").exists():
182
+ return "authenticated (saved)"
183
+ else:
184
+ return "not authenticated"
hanzo/router/__init__.py CHANGED
@@ -1,19 +1,25 @@
1
- """Router module - re-exports from hanzo-router package."""
1
+ """Router module - re-exports from router package."""
2
2
 
3
3
  try:
4
- # Import all exports from hanzo-router (which internally uses 'router' package name)
5
- from router import *
4
+ # Import directly from the installed router package
5
+ import router
6
6
  from router import Router, completion, acompletion, embedding, aembedding
7
7
 
8
- # Ensure these are explicitly available
9
- __all__ = ["Router", "completion", "acompletion", "embedding", "aembedding"]
8
+ # Re-export the entire router module
9
+ __all__ = ["router", "Router", "completion", "acompletion", "embedding", "aembedding"]
10
+
11
+ # Make router available as a submodule
12
+ import sys
13
+ sys.modules['hanzo.router'] = router
14
+
10
15
  except ImportError as e:
11
- # If hanzo-router is not installed, provide helpful error
16
+ # If router is not installed, provide helpful error
12
17
  import sys
13
18
  print(f"Error importing router: {e}", file=sys.stderr)
14
- print("Please install hanzo-router: pip install hanzo[router] or pip install hanzo[all]", file=sys.stderr)
19
+ print("Please install router from the main repository: pip install -e /Users/z/work/hanzo/router", file=sys.stderr)
15
20
 
16
21
  # Define placeholders to avoid complete failure
22
+ router = None
17
23
  Router = None
18
24
  completion = None
19
25
  acompletion = None
@@ -0,0 +1,3 @@
1
+ """Utility modules for Hanzo CLI."""
2
+
3
+ __all__ = ["config", "output"]