hanzo 0.3.6__py3-none-any.whl → 0.3.7__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.7.dist-info/METADATA +138 -0
- hanzo-0.3.7.dist-info/RECORD +28 -0
- {hanzo-0.3.6.dist-info → hanzo-0.3.7.dist-info}/WHEEL +1 -2
- hanzo-0.3.7.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/repl.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"""REPL and AI chat entry points for Hanzo."""
|
|
2
2
|
|
|
3
3
|
import sys
|
|
4
|
+
|
|
4
5
|
import click
|
|
5
6
|
|
|
6
7
|
|
|
@@ -9,11 +10,15 @@ def ai_chat():
|
|
|
9
10
|
try:
|
|
10
11
|
# Use hanzo-router instead of litellm for AI routing
|
|
11
12
|
from hanzo_repl.cli import main as repl_main
|
|
13
|
+
|
|
12
14
|
# Pass AI chat mode
|
|
13
15
|
sys.argv = [sys.argv[0], "--mode", "ai"]
|
|
14
16
|
repl_main()
|
|
15
17
|
except ImportError:
|
|
16
|
-
click.echo(
|
|
18
|
+
click.echo(
|
|
19
|
+
"Error: hanzo-repl or hanzo-router is not installed. Please run: pip install hanzo[ai] or pip install hanzo[all]",
|
|
20
|
+
err=True,
|
|
21
|
+
)
|
|
17
22
|
sys.exit(1)
|
|
18
23
|
|
|
19
24
|
|
|
@@ -21,15 +26,20 @@ def repl_main():
|
|
|
21
26
|
"""Start the Hanzo REPL interface (hanzo/repl)."""
|
|
22
27
|
try:
|
|
23
28
|
from hanzo_repl.cli import main as repl_cli_main
|
|
29
|
+
|
|
24
30
|
repl_cli_main()
|
|
25
31
|
except ImportError:
|
|
26
|
-
click.echo(
|
|
32
|
+
click.echo(
|
|
33
|
+
"Error: hanzo-repl is not installed. Please run: pip install hanzo[all]",
|
|
34
|
+
err=True,
|
|
35
|
+
)
|
|
27
36
|
sys.exit(1)
|
|
28
37
|
|
|
29
38
|
|
|
30
39
|
if __name__ == "__main__":
|
|
31
40
|
import os
|
|
41
|
+
|
|
32
42
|
if os.path.basename(sys.argv[0]) in ["ai", "chat"]:
|
|
33
43
|
ai_chat()
|
|
34
44
|
else:
|
|
35
|
-
repl_main()
|
|
45
|
+
repl_main()
|
hanzo/router/__init__.py
CHANGED
|
@@ -3,25 +3,37 @@
|
|
|
3
3
|
try:
|
|
4
4
|
# Import directly from the installed router package
|
|
5
5
|
import router
|
|
6
|
-
from router import Router,
|
|
7
|
-
|
|
6
|
+
from router import Router, embedding, aembedding, completion, acompletion
|
|
7
|
+
|
|
8
8
|
# Re-export the entire router module
|
|
9
|
-
__all__ = [
|
|
10
|
-
|
|
9
|
+
__all__ = [
|
|
10
|
+
"router",
|
|
11
|
+
"Router",
|
|
12
|
+
"completion",
|
|
13
|
+
"acompletion",
|
|
14
|
+
"embedding",
|
|
15
|
+
"aembedding",
|
|
16
|
+
]
|
|
17
|
+
|
|
11
18
|
# Make router available as a submodule
|
|
12
19
|
import sys
|
|
13
|
-
|
|
14
|
-
|
|
20
|
+
|
|
21
|
+
sys.modules["hanzo.router"] = router
|
|
22
|
+
|
|
15
23
|
except ImportError as e:
|
|
16
24
|
# If router is not installed, provide helpful error
|
|
17
25
|
import sys
|
|
26
|
+
|
|
18
27
|
print(f"Error importing router: {e}", file=sys.stderr)
|
|
19
|
-
print(
|
|
20
|
-
|
|
28
|
+
print(
|
|
29
|
+
"Please install router from the main repository: pip install -e /Users/z/work/hanzo/router",
|
|
30
|
+
file=sys.stderr,
|
|
31
|
+
)
|
|
32
|
+
|
|
21
33
|
# Define placeholders to avoid complete failure
|
|
22
34
|
router = None
|
|
23
35
|
Router = None
|
|
24
36
|
completion = None
|
|
25
37
|
acompletion = None
|
|
26
38
|
embedding = None
|
|
27
|
-
aembedding = None
|
|
39
|
+
aembedding = None
|
hanzo/utils/__init__.py
CHANGED
hanzo/utils/config.py
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
"""Configuration utilities for Hanzo CLI."""
|
|
2
2
|
|
|
3
|
-
import json
|
|
4
3
|
import os
|
|
5
|
-
|
|
4
|
+
import json
|
|
6
5
|
from typing import Any, Dict, Optional
|
|
6
|
+
from pathlib import Path
|
|
7
7
|
|
|
8
8
|
import yaml
|
|
9
9
|
|
|
@@ -11,17 +11,21 @@ import yaml
|
|
|
11
11
|
def get_config_paths() -> Dict[str, Path]:
|
|
12
12
|
"""Get configuration file paths."""
|
|
13
13
|
paths = {}
|
|
14
|
-
|
|
14
|
+
|
|
15
15
|
# System config
|
|
16
16
|
if os.name == "nt": # Windows
|
|
17
|
-
paths["system"] =
|
|
17
|
+
paths["system"] = (
|
|
18
|
+
Path(os.environ.get("PROGRAMDATA", "C:\\ProgramData"))
|
|
19
|
+
/ "hanzo"
|
|
20
|
+
/ "config.yaml"
|
|
21
|
+
)
|
|
18
22
|
else: # Unix-like
|
|
19
23
|
paths["system"] = Path("/etc/hanzo/config.yaml")
|
|
20
|
-
|
|
24
|
+
|
|
21
25
|
# Global config (user)
|
|
22
26
|
config_home = Path(os.environ.get("XDG_CONFIG_HOME", Path.home() / ".config"))
|
|
23
27
|
paths["global"] = config_home / "hanzo" / "config.yaml"
|
|
24
|
-
|
|
28
|
+
|
|
25
29
|
# Local config (project)
|
|
26
30
|
cwd = Path.cwd()
|
|
27
31
|
for parent in [cwd] + list(cwd.parents):
|
|
@@ -32,7 +36,7 @@ def get_config_paths() -> Dict[str, Path]:
|
|
|
32
36
|
else:
|
|
33
37
|
# Default local path even if it doesn't exist
|
|
34
38
|
paths["local"] = cwd / ".hanzo" / "config.yaml"
|
|
35
|
-
|
|
39
|
+
|
|
36
40
|
return paths
|
|
37
41
|
|
|
38
42
|
|
|
@@ -40,7 +44,7 @@ def load_config(path: Path) -> Dict[str, Any]:
|
|
|
40
44
|
"""Load configuration from file."""
|
|
41
45
|
if not path.exists():
|
|
42
46
|
return {}
|
|
43
|
-
|
|
47
|
+
|
|
44
48
|
try:
|
|
45
49
|
with open(path, "r") as f:
|
|
46
50
|
if path.suffix == ".json":
|
|
@@ -54,7 +58,7 @@ def load_config(path: Path) -> Dict[str, Any]:
|
|
|
54
58
|
def save_config(path: Path, config: Dict[str, Any]):
|
|
55
59
|
"""Save configuration to file."""
|
|
56
60
|
path.parent.mkdir(parents=True, exist_ok=True)
|
|
57
|
-
|
|
61
|
+
|
|
58
62
|
with open(path, "w") as f:
|
|
59
63
|
if path.suffix == ".json":
|
|
60
64
|
json.dump(config, f, indent=2)
|
|
@@ -65,31 +69,31 @@ def save_config(path: Path, config: Dict[str, Any]):
|
|
|
65
69
|
def get_config_value(key: str, default: Any = None, scope: Optional[str] = None) -> Any:
|
|
66
70
|
"""Get configuration value from merged configs."""
|
|
67
71
|
paths = get_config_paths()
|
|
68
|
-
|
|
72
|
+
|
|
69
73
|
# Load configs in priority order (local > global > system)
|
|
70
74
|
configs = []
|
|
71
|
-
|
|
75
|
+
|
|
72
76
|
if scope == "system" or scope is None:
|
|
73
77
|
if paths["system"].exists():
|
|
74
78
|
configs.append(load_config(paths["system"]))
|
|
75
|
-
|
|
79
|
+
|
|
76
80
|
if scope == "global" or scope is None:
|
|
77
81
|
if paths["global"].exists():
|
|
78
82
|
configs.append(load_config(paths["global"]))
|
|
79
|
-
|
|
83
|
+
|
|
80
84
|
if scope == "local" or scope is None:
|
|
81
85
|
if paths.get("local") and paths["local"].exists():
|
|
82
86
|
configs.append(load_config(paths["local"]))
|
|
83
|
-
|
|
87
|
+
|
|
84
88
|
# Merge configs (later ones override earlier)
|
|
85
89
|
merged = {}
|
|
86
90
|
for config in configs:
|
|
87
91
|
merged.update(config)
|
|
88
|
-
|
|
92
|
+
|
|
89
93
|
# Get nested key
|
|
90
94
|
keys = key.split(".")
|
|
91
95
|
current = merged
|
|
92
|
-
|
|
96
|
+
|
|
93
97
|
try:
|
|
94
98
|
for k in keys:
|
|
95
99
|
current = current[k]
|
|
@@ -102,49 +106,45 @@ def set_config_value(key: str, value: Any, scope: str = "global"):
|
|
|
102
106
|
"""Set configuration value."""
|
|
103
107
|
paths = get_config_paths()
|
|
104
108
|
path = paths.get(scope, paths["global"])
|
|
105
|
-
|
|
109
|
+
|
|
106
110
|
config = load_config(path) if path.exists() else {}
|
|
107
|
-
|
|
111
|
+
|
|
108
112
|
# Set nested key
|
|
109
113
|
keys = key.split(".")
|
|
110
114
|
current = config
|
|
111
|
-
|
|
115
|
+
|
|
112
116
|
for k in keys[:-1]:
|
|
113
117
|
if k not in current:
|
|
114
118
|
current[k] = {}
|
|
115
119
|
current = current[k]
|
|
116
|
-
|
|
120
|
+
|
|
117
121
|
current[keys[-1]] = value
|
|
118
|
-
|
|
122
|
+
|
|
119
123
|
save_config(path, config)
|
|
120
124
|
|
|
121
125
|
|
|
122
126
|
def init_config() -> Dict[str, Path]:
|
|
123
127
|
"""Initialize configuration structure."""
|
|
124
128
|
paths = get_config_paths()
|
|
125
|
-
|
|
129
|
+
|
|
126
130
|
# Create global config if it doesn't exist
|
|
127
131
|
if not paths["global"].exists():
|
|
128
132
|
default_config = {
|
|
129
133
|
"default_model": "llama-3.2-3b",
|
|
130
134
|
"default_provider": "local",
|
|
131
|
-
"mcp": {
|
|
132
|
-
|
|
133
|
-
"enable_all_tools": True
|
|
134
|
-
},
|
|
135
|
-
"cluster": {
|
|
136
|
-
"default_name": "hanzo-local",
|
|
137
|
-
"default_port": 8000
|
|
138
|
-
}
|
|
135
|
+
"mcp": {"allowed_paths": [str(Path.home())], "enable_all_tools": True},
|
|
136
|
+
"cluster": {"default_name": "hanzo-local", "default_port": 8000},
|
|
139
137
|
}
|
|
140
138
|
save_config(paths["global"], default_config)
|
|
141
|
-
|
|
139
|
+
|
|
142
140
|
return paths
|
|
143
141
|
|
|
144
142
|
|
|
145
143
|
def get_default_model() -> str:
|
|
146
144
|
"""Get default model from config or environment."""
|
|
147
|
-
return os.environ.get("HANZO_DEFAULT_MODEL") or get_config_value(
|
|
145
|
+
return os.environ.get("HANZO_DEFAULT_MODEL") or get_config_value(
|
|
146
|
+
"default_model", "llama-3.2-3b"
|
|
147
|
+
)
|
|
148
148
|
|
|
149
149
|
|
|
150
150
|
def get_api_key(provider: str) -> Optional[str]:
|
|
@@ -156,15 +156,17 @@ def get_api_key(provider: str) -> Optional[str]:
|
|
|
156
156
|
"hanzo": "HANZO_API_KEY",
|
|
157
157
|
"groq": "GROQ_API_KEY",
|
|
158
158
|
}
|
|
159
|
-
|
|
159
|
+
|
|
160
160
|
if env_key := env_map.get(provider.lower()):
|
|
161
161
|
if key := os.environ.get(env_key):
|
|
162
162
|
return key
|
|
163
|
-
|
|
163
|
+
|
|
164
164
|
# Check config
|
|
165
165
|
return get_config_value(f"api_keys.{provider}")
|
|
166
166
|
|
|
167
167
|
|
|
168
168
|
def is_local_preferred() -> bool:
|
|
169
169
|
"""Check if local execution is preferred."""
|
|
170
|
-
return os.environ.get("HANZO_USE_LOCAL", "").lower() == "true" or get_config_value(
|
|
170
|
+
return os.environ.get("HANZO_USE_LOCAL", "").lower() == "true" or get_config_value(
|
|
171
|
+
"prefer_local", False
|
|
172
|
+
)
|
hanzo/utils/net_check.py
CHANGED
|
@@ -1,33 +1,33 @@
|
|
|
1
1
|
"""Utilities for checking hanzo/net availability and dependencies."""
|
|
2
2
|
|
|
3
|
-
import os
|
|
4
|
-
import subprocess
|
|
5
3
|
import sys
|
|
6
|
-
|
|
4
|
+
import subprocess
|
|
5
|
+
from typing import Tuple, Optional
|
|
7
6
|
from pathlib import Path
|
|
8
7
|
|
|
9
8
|
|
|
10
9
|
def check_net_installation() -> Tuple[bool, Optional[str], Optional[str]]:
|
|
11
10
|
"""Check if hanzo/net is available and properly configured.
|
|
12
|
-
|
|
11
|
+
|
|
13
12
|
Returns:
|
|
14
13
|
Tuple of (is_available, net_path, python_exe)
|
|
15
14
|
"""
|
|
16
15
|
# First try to import as PyPI package (hanzo-net)
|
|
17
16
|
try:
|
|
18
17
|
import net
|
|
18
|
+
|
|
19
19
|
return True, None, sys.executable
|
|
20
20
|
except ImportError:
|
|
21
21
|
pass
|
|
22
|
-
|
|
22
|
+
|
|
23
23
|
# For development: check for hanzo/net in standard location
|
|
24
24
|
net_path = Path.home() / "work" / "hanzo" / "net"
|
|
25
25
|
if not net_path.exists():
|
|
26
26
|
net_path = Path("/Users/z/work/hanzo/net")
|
|
27
|
-
|
|
27
|
+
|
|
28
28
|
if not net_path.exists():
|
|
29
29
|
return False, None, None
|
|
30
|
-
|
|
30
|
+
|
|
31
31
|
# Check for venv
|
|
32
32
|
venv_python = net_path / ".venv" / "bin" / "python"
|
|
33
33
|
if venv_python.exists():
|
|
@@ -35,21 +35,19 @@ def check_net_installation() -> Tuple[bool, Optional[str], Optional[str]]:
|
|
|
35
35
|
result = subprocess.run(
|
|
36
36
|
[str(venv_python), "-c", "import net, scapy, mlx, transformers"],
|
|
37
37
|
capture_output=True,
|
|
38
|
-
text=True
|
|
38
|
+
text=True,
|
|
39
39
|
)
|
|
40
40
|
if result.returncode == 0:
|
|
41
41
|
return True, str(net_path), str(venv_python)
|
|
42
42
|
else:
|
|
43
43
|
# Venv exists but missing dependencies
|
|
44
44
|
return False, str(net_path), str(venv_python)
|
|
45
|
-
|
|
45
|
+
|
|
46
46
|
# No venv, check system Python
|
|
47
47
|
result = subprocess.run(
|
|
48
|
-
[sys.executable, "-c", "import scapy"],
|
|
49
|
-
capture_output=True,
|
|
50
|
-
text=True
|
|
48
|
+
[sys.executable, "-c", "import scapy"], capture_output=True, text=True
|
|
51
49
|
)
|
|
52
|
-
|
|
50
|
+
|
|
53
51
|
if result.returncode == 0:
|
|
54
52
|
return True, str(net_path), sys.executable
|
|
55
53
|
else:
|
|
@@ -58,52 +56,58 @@ def check_net_installation() -> Tuple[bool, Optional[str], Optional[str]]:
|
|
|
58
56
|
|
|
59
57
|
def install_net_dependencies(net_path: str, python_exe: str = None) -> bool:
|
|
60
58
|
"""Install hanzo/net dependencies.
|
|
61
|
-
|
|
59
|
+
|
|
62
60
|
Args:
|
|
63
61
|
net_path: Path to hanzo/net directory
|
|
64
62
|
python_exe: Python executable to use (optional)
|
|
65
|
-
|
|
63
|
+
|
|
66
64
|
Returns:
|
|
67
65
|
True if installation successful
|
|
68
66
|
"""
|
|
69
67
|
if python_exe is None:
|
|
70
68
|
python_exe = sys.executable
|
|
71
|
-
|
|
69
|
+
|
|
72
70
|
# Install dependencies
|
|
73
71
|
result = subprocess.run(
|
|
74
72
|
[python_exe, "-m", "pip", "install", "-e", net_path],
|
|
75
73
|
capture_output=True,
|
|
76
|
-
text=True
|
|
74
|
+
text=True,
|
|
77
75
|
)
|
|
78
|
-
|
|
76
|
+
|
|
79
77
|
return result.returncode == 0
|
|
80
78
|
|
|
81
79
|
|
|
82
80
|
def get_missing_dependencies(python_exe: str = None) -> list:
|
|
83
81
|
"""Check which dependencies are missing for hanzo/net.
|
|
84
|
-
|
|
82
|
+
|
|
85
83
|
Args:
|
|
86
84
|
python_exe: Python executable to check (default: sys.executable)
|
|
87
|
-
|
|
85
|
+
|
|
88
86
|
Returns:
|
|
89
87
|
List of missing package names
|
|
90
88
|
"""
|
|
91
89
|
if python_exe is None:
|
|
92
90
|
python_exe = sys.executable
|
|
93
|
-
|
|
91
|
+
|
|
94
92
|
required_packages = [
|
|
95
|
-
"scapy",
|
|
96
|
-
"
|
|
93
|
+
"scapy",
|
|
94
|
+
"mlx",
|
|
95
|
+
"mlx_lm",
|
|
96
|
+
"transformers",
|
|
97
|
+
"tinygrad",
|
|
98
|
+
"aiohttp",
|
|
99
|
+
"grpcio",
|
|
100
|
+
"pydantic",
|
|
101
|
+
"rich",
|
|
102
|
+
"tqdm",
|
|
97
103
|
]
|
|
98
|
-
|
|
104
|
+
|
|
99
105
|
missing = []
|
|
100
106
|
for package in required_packages:
|
|
101
107
|
result = subprocess.run(
|
|
102
|
-
[python_exe, "-c", f"import {package}"],
|
|
103
|
-
capture_output=True,
|
|
104
|
-
text=True
|
|
108
|
+
[python_exe, "-c", f"import {package}"], capture_output=True, text=True
|
|
105
109
|
)
|
|
106
110
|
if result.returncode != 0:
|
|
107
111
|
missing.append(package)
|
|
108
|
-
|
|
109
|
-
return missing
|
|
112
|
+
|
|
113
|
+
return missing
|
hanzo/utils/output.py
CHANGED
|
@@ -1,20 +1,22 @@
|
|
|
1
1
|
"""Output utilities for Hanzo CLI."""
|
|
2
2
|
|
|
3
|
-
from functools import wraps
|
|
4
3
|
from typing import Any, Callable
|
|
4
|
+
from functools import wraps
|
|
5
5
|
|
|
6
|
-
from rich.console import Console
|
|
7
6
|
from rich.theme import Theme
|
|
7
|
+
from rich.console import Console
|
|
8
8
|
|
|
9
9
|
# Custom theme
|
|
10
|
-
hanzo_theme = Theme(
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
10
|
+
hanzo_theme = Theme(
|
|
11
|
+
{
|
|
12
|
+
"info": "cyan",
|
|
13
|
+
"warning": "yellow",
|
|
14
|
+
"error": "red",
|
|
15
|
+
"success": "green",
|
|
16
|
+
"dim": "dim white",
|
|
17
|
+
"highlight": "bold cyan",
|
|
18
|
+
}
|
|
19
|
+
)
|
|
18
20
|
|
|
19
21
|
# Global console instance
|
|
20
22
|
console = Console(theme=hanzo_theme)
|
|
@@ -22,6 +24,7 @@ console = Console(theme=hanzo_theme)
|
|
|
22
24
|
|
|
23
25
|
def handle_errors(func: Callable) -> Callable:
|
|
24
26
|
"""Decorator to handle errors in CLI commands."""
|
|
27
|
+
|
|
25
28
|
@wraps(func)
|
|
26
29
|
async def async_wrapper(*args, **kwargs):
|
|
27
30
|
try:
|
|
@@ -32,7 +35,7 @@ def handle_errors(func: Callable) -> Callable:
|
|
|
32
35
|
console.print(f"[error]Error: {e}[/error]")
|
|
33
36
|
if console.is_debug:
|
|
34
37
|
console.print_exception()
|
|
35
|
-
|
|
38
|
+
|
|
36
39
|
@wraps(func)
|
|
37
40
|
def sync_wrapper(*args, **kwargs):
|
|
38
41
|
try:
|
|
@@ -43,7 +46,7 @@ def handle_errors(func: Callable) -> Callable:
|
|
|
43
46
|
console.print(f"[error]Error: {e}[/error]")
|
|
44
47
|
if console.is_debug:
|
|
45
48
|
console.print_exception()
|
|
46
|
-
|
|
49
|
+
|
|
47
50
|
if asyncio.iscoroutinefunction(func):
|
|
48
51
|
return async_wrapper
|
|
49
52
|
return sync_wrapper
|
|
@@ -59,31 +62,33 @@ def print_table(data: list[dict], title: str = None):
|
|
|
59
62
|
if not data:
|
|
60
63
|
console.print("[dim]No data[/dim]")
|
|
61
64
|
return
|
|
62
|
-
|
|
65
|
+
|
|
63
66
|
from rich.table import Table
|
|
64
|
-
|
|
67
|
+
|
|
65
68
|
table = Table(title=title)
|
|
66
|
-
|
|
69
|
+
|
|
67
70
|
# Add columns from first row
|
|
68
71
|
for key in data[0].keys():
|
|
69
72
|
table.add_column(key.replace("_", " ").title())
|
|
70
|
-
|
|
73
|
+
|
|
71
74
|
# Add rows
|
|
72
75
|
for row in data:
|
|
73
76
|
table.add_row(*[str(v) for v in row.values()])
|
|
74
|
-
|
|
77
|
+
|
|
75
78
|
console.print(table)
|
|
76
79
|
|
|
77
80
|
|
|
78
81
|
def confirm(message: str, default: bool = False) -> bool:
|
|
79
82
|
"""Ask for confirmation."""
|
|
80
83
|
from rich.prompt import Confirm
|
|
84
|
+
|
|
81
85
|
return Confirm.ask(message, default=default)
|
|
82
86
|
|
|
83
87
|
|
|
84
88
|
def prompt(message: str, default: str = None, password: bool = False) -> str:
|
|
85
89
|
"""Prompt for input."""
|
|
86
90
|
from rich.prompt import Prompt
|
|
91
|
+
|
|
87
92
|
return Prompt.ask(message, default=default, password=password)
|
|
88
93
|
|
|
89
94
|
|
|
@@ -100,4 +105,4 @@ clear = console.clear
|
|
|
100
105
|
|
|
101
106
|
|
|
102
107
|
# Import asyncio for the decorator
|
|
103
|
-
import asyncio
|
|
108
|
+
import asyncio
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: hanzo
|
|
3
|
+
Version: 0.3.7
|
|
4
|
+
Summary: Hanzo AI - Complete AI Infrastructure Platform with CLI, Router, MCP, and Agent Runtime
|
|
5
|
+
Project-URL: Homepage, https://hanzo.ai
|
|
6
|
+
Project-URL: Repository, https://github.com/hanzoai/python-sdk
|
|
7
|
+
Project-URL: Documentation, https://docs.hanzo.ai/cli
|
|
8
|
+
Project-URL: Bug Tracker, https://github.com/hanzoai/python-sdk/issues
|
|
9
|
+
Author-email: Hanzo AI <dev@hanzo.ai>
|
|
10
|
+
Keywords: agents,ai,cli,hanzo,llm,local-ai,mcp,private-ai
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Environment :: Console
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
22
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
23
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
24
|
+
Requires-Python: >=3.8
|
|
25
|
+
Requires-Dist: click>=8.1.0
|
|
26
|
+
Requires-Dist: hanzo-net>=0.1.12
|
|
27
|
+
Requires-Dist: httpx>=0.23.0
|
|
28
|
+
Requires-Dist: prompt-toolkit>=3.0.0
|
|
29
|
+
Requires-Dist: pydantic>=2.0.0
|
|
30
|
+
Requires-Dist: pyyaml>=6.0
|
|
31
|
+
Requires-Dist: rich>=13.0.0
|
|
32
|
+
Requires-Dist: typer>=0.9.0
|
|
33
|
+
Provides-Extra: agents
|
|
34
|
+
Requires-Dist: hanzo-agents>=0.1.0; extra == 'agents'
|
|
35
|
+
Requires-Dist: hanzo-network>=0.1.2; extra == 'agents'
|
|
36
|
+
Provides-Extra: ai
|
|
37
|
+
Requires-Dist: hanzoai>=1.0.0; extra == 'ai'
|
|
38
|
+
Provides-Extra: all
|
|
39
|
+
Requires-Dist: hanzo-aci>=0.2.8; extra == 'all'
|
|
40
|
+
Requires-Dist: hanzo-agents>=0.1.0; extra == 'all'
|
|
41
|
+
Requires-Dist: hanzo-mcp>=0.7.0; extra == 'all'
|
|
42
|
+
Requires-Dist: hanzo-memory>=1.0.0; extra == 'all'
|
|
43
|
+
Requires-Dist: hanzo-network>=0.1.2; extra == 'all'
|
|
44
|
+
Requires-Dist: hanzo-repl>=0.1.0; extra == 'all'
|
|
45
|
+
Requires-Dist: hanzoai>=1.0.0; extra == 'all'
|
|
46
|
+
Provides-Extra: dev
|
|
47
|
+
Requires-Dist: hanzo-aci>=0.2.8; extra == 'dev'
|
|
48
|
+
Provides-Extra: mcp
|
|
49
|
+
Requires-Dist: hanzo-mcp>=0.7.0; extra == 'mcp'
|
|
50
|
+
Provides-Extra: repl
|
|
51
|
+
Requires-Dist: hanzo-repl>=0.1.0; extra == 'repl'
|
|
52
|
+
Provides-Extra: router
|
|
53
|
+
Description-Content-Type: text/markdown
|
|
54
|
+
|
|
55
|
+
# Hanzo AI - Complete AI Infrastructure Platform
|
|
56
|
+
|
|
57
|
+
The main SDK for the Hanzo AI ecosystem, providing unified access to all Hanzo tools and services.
|
|
58
|
+
|
|
59
|
+
## Installation
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
# Install base package with CLI
|
|
63
|
+
pip install hanzo
|
|
64
|
+
|
|
65
|
+
# Install with all components
|
|
66
|
+
pip install hanzo[all]
|
|
67
|
+
|
|
68
|
+
# Install specific components
|
|
69
|
+
pip install hanzo[ai] # AI SDK (same as standalone hanzoai package)
|
|
70
|
+
pip install hanzo[router] # LLM gateway router (replaces litellm)
|
|
71
|
+
pip install hanzo[mcp] # Model Context Protocol server
|
|
72
|
+
pip install hanzo[agents] # Agent runtime and orchestration
|
|
73
|
+
pip install hanzo[repl] # Interactive REPL with AI chat
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Features
|
|
77
|
+
|
|
78
|
+
- **Unified LLM Gateway**: Use `hanzo.router` instead of litellm for 100+ LLM providers
|
|
79
|
+
- **MCP Integration**: Full Model Context Protocol support for AI tools
|
|
80
|
+
- **Agent Runtime**: Build and deploy AI agents with the agent framework
|
|
81
|
+
- **Interactive REPL**: Chat with AI models directly from the command line
|
|
82
|
+
- **Complete SDK**: Import all Hanzo components from a single package
|
|
83
|
+
|
|
84
|
+
## Quick Start
|
|
85
|
+
|
|
86
|
+
### Command Line
|
|
87
|
+
```bash
|
|
88
|
+
# Main CLI
|
|
89
|
+
hanzo --help
|
|
90
|
+
|
|
91
|
+
# Start MCP server
|
|
92
|
+
hanzo-mcp
|
|
93
|
+
|
|
94
|
+
# Interactive AI chat
|
|
95
|
+
hanzo-ai
|
|
96
|
+
hanzo-chat
|
|
97
|
+
|
|
98
|
+
# REPL interface
|
|
99
|
+
hanzo-repl
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### Python SDK
|
|
103
|
+
```python
|
|
104
|
+
import hanzo
|
|
105
|
+
|
|
106
|
+
# Use router for LLM calls (replaces litellm)
|
|
107
|
+
from hanzo import router
|
|
108
|
+
response = router.completion(
|
|
109
|
+
model="gpt-4",
|
|
110
|
+
messages=[{"role": "user", "content": "Hello!"}]
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
# Use agents
|
|
114
|
+
from hanzo import Agent, Network
|
|
115
|
+
agent = Agent(name="assistant")
|
|
116
|
+
|
|
117
|
+
# Use MCP tools
|
|
118
|
+
from hanzo import Tool, MCPServer
|
|
119
|
+
|
|
120
|
+
# Access AI SDK
|
|
121
|
+
from hanzo import Client
|
|
122
|
+
client = Client(api_key="...")
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## Components
|
|
126
|
+
|
|
127
|
+
- **hanzo.router**: Unified LLM gateway (replaces litellm)
|
|
128
|
+
- **hanzo.mcp**: Model Context Protocol server and tools
|
|
129
|
+
- **hanzo.agents**: Agent runtime and orchestration
|
|
130
|
+
- **hanzo.memory**: Memory systems for agents
|
|
131
|
+
- **hanzo.Client**: Main AI SDK client
|
|
132
|
+
|
|
133
|
+
## Documentation
|
|
134
|
+
|
|
135
|
+
- [Hanzo AI Docs](https://docs.hanzo.ai)
|
|
136
|
+
- [Router Documentation](https://docs.hanzo.ai/router)
|
|
137
|
+
- [MCP Documentation](https://docs.hanzo.ai/mcp)
|
|
138
|
+
- [Agent Documentation](https://docs.hanzo.ai/agents)
|