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/commands/auth.py
CHANGED
|
@@ -1,15 +1,12 @@
|
|
|
1
1
|
"""Authentication commands."""
|
|
2
2
|
|
|
3
|
-
import asyncio
|
|
4
3
|
import os
|
|
5
4
|
from pathlib import Path
|
|
6
|
-
from typing import Optional
|
|
7
5
|
|
|
8
6
|
import click
|
|
9
|
-
import httpx
|
|
10
7
|
from rich.prompt import Prompt
|
|
11
8
|
|
|
12
|
-
from ..utils.output import console
|
|
9
|
+
from ..utils.output import console
|
|
13
10
|
|
|
14
11
|
|
|
15
12
|
@click.group(name="auth")
|
|
@@ -32,9 +29,9 @@ async def login(ctx, email: str, password: str, api_key: str, sso: bool):
|
|
|
32
29
|
console.print("[red]Error:[/red] hanzoai not installed")
|
|
33
30
|
console.print("Install with: pip install hanzo")
|
|
34
31
|
return
|
|
35
|
-
|
|
32
|
+
|
|
36
33
|
auth = HanzoAuth()
|
|
37
|
-
|
|
34
|
+
|
|
38
35
|
try:
|
|
39
36
|
if api_key:
|
|
40
37
|
# Direct API key authentication
|
|
@@ -51,33 +48,33 @@ async def login(ctx, email: str, password: str, api_key: str, sso: bool):
|
|
|
51
48
|
email = Prompt.ask("Email")
|
|
52
49
|
if not password:
|
|
53
50
|
password = Prompt.ask("Password", password=True)
|
|
54
|
-
|
|
51
|
+
|
|
55
52
|
console.print("Authenticating...")
|
|
56
53
|
result = await auth.login(email, password)
|
|
57
|
-
|
|
54
|
+
|
|
58
55
|
# Save credentials
|
|
59
56
|
config_dir = Path.home() / ".hanzo"
|
|
60
57
|
config_dir.mkdir(exist_ok=True)
|
|
61
|
-
|
|
58
|
+
|
|
62
59
|
config_file = config_dir / "auth.json"
|
|
63
60
|
await auth.save_credentials(config_file)
|
|
64
|
-
|
|
61
|
+
|
|
65
62
|
# Also set environment variable if API key is available
|
|
66
63
|
if api_key or result.get("api_key"):
|
|
67
64
|
key = api_key or result.get("api_key")
|
|
68
65
|
console.print(f"\n[dim]To use in environment:[/dim]")
|
|
69
66
|
console.print(f"export HANZO_API_KEY={key}")
|
|
70
|
-
|
|
67
|
+
|
|
71
68
|
console.print(f"[green]✓[/green] Logged in as {result.get('email', 'user')}")
|
|
72
|
-
|
|
69
|
+
|
|
73
70
|
# Check organization
|
|
74
71
|
if org := result.get("organization"):
|
|
75
72
|
console.print(f" Organization: {org}")
|
|
76
|
-
|
|
73
|
+
|
|
77
74
|
# Check permissions
|
|
78
75
|
if permissions := result.get("permissions"):
|
|
79
76
|
console.print(f" Permissions: {', '.join(permissions)}")
|
|
80
|
-
|
|
77
|
+
|
|
81
78
|
except Exception as e:
|
|
82
79
|
console.print(f"[red]Login failed: {e}[/red]")
|
|
83
80
|
|
|
@@ -91,19 +88,19 @@ async def logout(ctx):
|
|
|
91
88
|
except ImportError:
|
|
92
89
|
console.print("[red]Error:[/red] hanzoai not installed")
|
|
93
90
|
return
|
|
94
|
-
|
|
91
|
+
|
|
95
92
|
auth = HanzoAuth()
|
|
96
|
-
|
|
93
|
+
|
|
97
94
|
try:
|
|
98
95
|
await auth.logout()
|
|
99
|
-
|
|
96
|
+
|
|
100
97
|
# Remove saved credentials
|
|
101
98
|
config_file = Path.home() / ".hanzo" / "auth.json"
|
|
102
99
|
if config_file.exists():
|
|
103
100
|
config_file.unlink()
|
|
104
|
-
|
|
101
|
+
|
|
105
102
|
console.print("[green]✓[/green] Logged out successfully")
|
|
106
|
-
|
|
103
|
+
|
|
107
104
|
except Exception as e:
|
|
108
105
|
console.print(f"[red]Logout failed: {e}[/red]")
|
|
109
106
|
|
|
@@ -116,38 +113,41 @@ async def status(ctx):
|
|
|
116
113
|
if api_key := os.environ.get("HANZO_API_KEY"):
|
|
117
114
|
console.print("[green]✓[/green] HANZO_API_KEY environment variable set")
|
|
118
115
|
console.print(f" Key: {api_key[:8]}...{api_key[-4:]}")
|
|
119
|
-
|
|
116
|
+
|
|
120
117
|
# Check saved credentials
|
|
121
118
|
config_file = Path.home() / ".hanzo" / "auth.json"
|
|
122
119
|
if config_file.exists():
|
|
123
120
|
try:
|
|
124
121
|
from hanzoai.auth import HanzoAuth
|
|
122
|
+
|
|
125
123
|
auth = HanzoAuth()
|
|
126
124
|
creds = await auth.load_credentials(config_file)
|
|
127
|
-
|
|
125
|
+
|
|
128
126
|
console.print("[green]✓[/green] Saved credentials found")
|
|
129
127
|
if email := creds.get("email"):
|
|
130
128
|
console.print(f" Email: {email}")
|
|
131
129
|
if org := creds.get("organization"):
|
|
132
130
|
console.print(f" Organization: {org}")
|
|
133
|
-
|
|
131
|
+
|
|
134
132
|
# Verify credentials are still valid
|
|
135
133
|
with console.status("Verifying credentials..."):
|
|
136
134
|
try:
|
|
137
135
|
user_info = await auth.get_user_info()
|
|
138
136
|
console.print("[green]✓[/green] Credentials are valid")
|
|
139
|
-
|
|
137
|
+
|
|
140
138
|
# Show usage stats if available
|
|
141
139
|
if usage := user_info.get("usage"):
|
|
142
140
|
console.print("\n[cyan]Usage:[/cyan]")
|
|
143
141
|
console.print(f" API calls: {usage.get('api_calls', 0)}")
|
|
144
142
|
console.print(f" Tokens: {usage.get('tokens', 0)}")
|
|
145
|
-
if quota := usage.get(
|
|
146
|
-
console.print(
|
|
143
|
+
if quota := usage.get("quota"):
|
|
144
|
+
console.print(
|
|
145
|
+
f" Quota: {usage.get('tokens', 0)} / {quota}"
|
|
146
|
+
)
|
|
147
147
|
except:
|
|
148
148
|
console.print("[yellow]![/yellow] Credentials may be expired")
|
|
149
149
|
console.print("Run 'hanzo auth login' to refresh")
|
|
150
|
-
|
|
150
|
+
|
|
151
151
|
except Exception as e:
|
|
152
152
|
console.print(f"[red]Error reading credentials: {e}[/red]")
|
|
153
153
|
else:
|
|
@@ -157,7 +157,9 @@ async def status(ctx):
|
|
|
157
157
|
|
|
158
158
|
@auth_group.command()
|
|
159
159
|
@click.option("--name", "-n", required=True, help="API key name")
|
|
160
|
-
@click.option(
|
|
160
|
+
@click.option(
|
|
161
|
+
"--permissions", "-p", multiple=True, help="Permissions (e.g., read, write, admin)"
|
|
162
|
+
)
|
|
161
163
|
@click.option("--expires", "-e", help="Expiration (e.g., 30d, 1y, never)")
|
|
162
164
|
@click.pass_context
|
|
163
165
|
async def create_key(ctx, name: str, permissions: tuple, expires: str):
|
|
@@ -167,30 +169,32 @@ async def create_key(ctx, name: str, permissions: tuple, expires: str):
|
|
|
167
169
|
except ImportError:
|
|
168
170
|
console.print("[red]Error:[/red] hanzoai not installed")
|
|
169
171
|
return
|
|
170
|
-
|
|
172
|
+
|
|
171
173
|
auth = HanzoAuth()
|
|
172
|
-
|
|
174
|
+
|
|
173
175
|
# Ensure authenticated
|
|
174
176
|
if not await auth.is_authenticated():
|
|
175
177
|
console.print("[red]Error:[/red] Not authenticated")
|
|
176
178
|
console.print("Run 'hanzo auth login' first")
|
|
177
179
|
return
|
|
178
|
-
|
|
180
|
+
|
|
179
181
|
with console.status(f"Creating API key '{name}'..."):
|
|
180
182
|
try:
|
|
181
183
|
result = await auth.create_api_key(
|
|
182
184
|
name=name,
|
|
183
185
|
permissions=list(permissions) if permissions else None,
|
|
184
|
-
expires=expires
|
|
186
|
+
expires=expires,
|
|
185
187
|
)
|
|
186
|
-
|
|
188
|
+
|
|
187
189
|
key = result.get("key")
|
|
188
190
|
console.print(f"[green]✓[/green] Created API key: {name}")
|
|
189
|
-
console.print(
|
|
191
|
+
console.print(
|
|
192
|
+
f"\n[yellow]Save this key - it won't be shown again:[/yellow]"
|
|
193
|
+
)
|
|
190
194
|
console.print(f"{key}")
|
|
191
195
|
console.print(f"\nTo use:")
|
|
192
196
|
console.print(f"export HANZO_API_KEY={key}")
|
|
193
|
-
|
|
197
|
+
|
|
194
198
|
except Exception as e:
|
|
195
199
|
console.print(f"[red]Failed to create key: {e}[/red]")
|
|
196
200
|
|
|
@@ -204,43 +208,43 @@ async def list_keys(ctx):
|
|
|
204
208
|
except ImportError:
|
|
205
209
|
console.print("[red]Error:[/red] hanzoai not installed")
|
|
206
210
|
return
|
|
207
|
-
|
|
211
|
+
|
|
208
212
|
auth = HanzoAuth()
|
|
209
|
-
|
|
213
|
+
|
|
210
214
|
# Ensure authenticated
|
|
211
215
|
if not await auth.is_authenticated():
|
|
212
216
|
console.print("[red]Error:[/red] Not authenticated")
|
|
213
217
|
console.print("Run 'hanzo auth login' first")
|
|
214
218
|
return
|
|
215
|
-
|
|
219
|
+
|
|
216
220
|
with console.status("Loading API keys..."):
|
|
217
221
|
try:
|
|
218
222
|
keys = await auth.list_api_keys()
|
|
219
|
-
|
|
223
|
+
|
|
220
224
|
if keys:
|
|
221
225
|
from rich.table import Table
|
|
222
|
-
|
|
226
|
+
|
|
223
227
|
table = Table(title="API Keys")
|
|
224
228
|
table.add_column("Name", style="cyan")
|
|
225
229
|
table.add_column("Created", style="green")
|
|
226
230
|
table.add_column("Last Used", style="yellow")
|
|
227
231
|
table.add_column("Permissions", style="blue")
|
|
228
232
|
table.add_column("Status", style="magenta")
|
|
229
|
-
|
|
233
|
+
|
|
230
234
|
for key in keys:
|
|
231
235
|
table.add_row(
|
|
232
236
|
key.get("name", "unknown"),
|
|
233
237
|
key.get("created_at", "unknown"),
|
|
234
238
|
key.get("last_used", "never"),
|
|
235
239
|
", ".join(key.get("permissions", [])),
|
|
236
|
-
key.get("status", "active")
|
|
240
|
+
key.get("status", "active"),
|
|
237
241
|
)
|
|
238
|
-
|
|
242
|
+
|
|
239
243
|
console.print(table)
|
|
240
244
|
else:
|
|
241
245
|
console.print("[yellow]No API keys found[/yellow]")
|
|
242
246
|
console.print("Create one with: hanzo auth create-key")
|
|
243
|
-
|
|
247
|
+
|
|
244
248
|
except Exception as e:
|
|
245
249
|
console.print(f"[red]Failed to list keys: {e}[/red]")
|
|
246
250
|
|
|
@@ -255,15 +259,15 @@ async def revoke_key(ctx, name: str):
|
|
|
255
259
|
except ImportError:
|
|
256
260
|
console.print("[red]Error:[/red] hanzoai not installed")
|
|
257
261
|
return
|
|
258
|
-
|
|
262
|
+
|
|
259
263
|
auth = HanzoAuth()
|
|
260
|
-
|
|
264
|
+
|
|
261
265
|
# Ensure authenticated
|
|
262
266
|
if not await auth.is_authenticated():
|
|
263
267
|
console.print("[red]Error:[/red] Not authenticated")
|
|
264
268
|
console.print("Run 'hanzo auth login' first")
|
|
265
269
|
return
|
|
266
|
-
|
|
270
|
+
|
|
267
271
|
if click.confirm(f"Revoke API key '{name}'?"):
|
|
268
272
|
with console.status(f"Revoking key '{name}'..."):
|
|
269
273
|
try:
|
|
@@ -282,43 +286,43 @@ async def whoami(ctx):
|
|
|
282
286
|
except ImportError:
|
|
283
287
|
console.print("[red]Error:[/red] hanzoai not installed")
|
|
284
288
|
return
|
|
285
|
-
|
|
289
|
+
|
|
286
290
|
auth = HanzoAuth()
|
|
287
|
-
|
|
291
|
+
|
|
288
292
|
# Check if authenticated
|
|
289
293
|
if not await auth.is_authenticated():
|
|
290
294
|
console.print("[yellow]Not authenticated[/yellow]")
|
|
291
|
-
|
|
295
|
+
|
|
292
296
|
# Check if API key is set
|
|
293
297
|
if os.environ.get("HANZO_API_KEY"):
|
|
294
298
|
console.print("HANZO_API_KEY is set but may be invalid")
|
|
295
299
|
else:
|
|
296
300
|
console.print("Run 'hanzo auth login' to authenticate")
|
|
297
301
|
return
|
|
298
|
-
|
|
302
|
+
|
|
299
303
|
with console.status("Loading user information..."):
|
|
300
304
|
try:
|
|
301
305
|
user = await auth.get_user_info()
|
|
302
|
-
|
|
306
|
+
|
|
303
307
|
console.print(f"[cyan]User Information:[/cyan]")
|
|
304
308
|
console.print(f" ID: {user.get('id', 'unknown')}")
|
|
305
309
|
console.print(f" Email: {user.get('email', 'unknown')}")
|
|
306
310
|
console.print(f" Name: {user.get('name', 'unknown')}")
|
|
307
|
-
|
|
308
|
-
if org := user.get(
|
|
311
|
+
|
|
312
|
+
if org := user.get("organization"):
|
|
309
313
|
console.print(f"\n[cyan]Organization:[/cyan]")
|
|
310
314
|
console.print(f" Name: {org.get('name', 'unknown')}")
|
|
311
315
|
console.print(f" Role: {org.get('role', 'member')}")
|
|
312
|
-
|
|
313
|
-
if teams := user.get(
|
|
316
|
+
|
|
317
|
+
if teams := user.get("teams"):
|
|
314
318
|
console.print(f"\n[cyan]Teams:[/cyan]")
|
|
315
319
|
for team in teams:
|
|
316
320
|
console.print(f" • {team}")
|
|
317
|
-
|
|
318
|
-
if perms := user.get(
|
|
321
|
+
|
|
322
|
+
if perms := user.get("permissions"):
|
|
319
323
|
console.print(f"\n[cyan]Permissions:[/cyan]")
|
|
320
324
|
for perm in sorted(perms):
|
|
321
325
|
console.print(f" • {perm}")
|
|
322
|
-
|
|
326
|
+
|
|
323
327
|
except Exception as e:
|
|
324
|
-
console.print(f"[red]Error: {e}[/red]")
|
|
328
|
+
console.print(f"[red]Error: {e}[/red]")
|
hanzo/commands/chat.py
CHANGED
|
@@ -7,7 +7,7 @@ import click
|
|
|
7
7
|
import httpx
|
|
8
8
|
from rich.markdown import Markdown
|
|
9
9
|
|
|
10
|
-
from ..utils.output import console
|
|
10
|
+
from ..utils.output import console
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
@click.command(name="chat")
|
|
@@ -15,19 +15,31 @@ from ..utils.output import console, handle_errors
|
|
|
15
15
|
@click.option("--local/--cloud", default=True, help="Use local or cloud model")
|
|
16
16
|
@click.option("--once", is_flag=True, help="Single question mode")
|
|
17
17
|
@click.option("--system", "-s", help="System prompt")
|
|
18
|
-
@click.option(
|
|
18
|
+
@click.option(
|
|
19
|
+
"--repl", is_flag=True, help="Start full REPL interface (like Claude Code)"
|
|
20
|
+
)
|
|
19
21
|
@click.option("--ipython", is_flag=True, help="Use IPython REPL interface")
|
|
20
22
|
@click.option("--tui", is_flag=True, help="Use beautiful TUI interface")
|
|
21
23
|
@click.argument("prompt", nargs=-1)
|
|
22
24
|
@click.pass_context
|
|
23
|
-
def chat_command(
|
|
25
|
+
def chat_command(
|
|
26
|
+
ctx,
|
|
27
|
+
model: str,
|
|
28
|
+
local: bool,
|
|
29
|
+
once: bool,
|
|
30
|
+
system: Optional[str],
|
|
31
|
+
repl: bool,
|
|
32
|
+
ipython: bool,
|
|
33
|
+
tui: bool,
|
|
34
|
+
prompt: tuple,
|
|
35
|
+
):
|
|
24
36
|
"""Interactive AI chat."""
|
|
25
37
|
# Check if REPL mode requested
|
|
26
38
|
if repl or ipython or tui:
|
|
27
39
|
try:
|
|
28
|
-
import sys
|
|
29
40
|
import os
|
|
30
|
-
|
|
41
|
+
import sys
|
|
42
|
+
|
|
31
43
|
# Set up environment
|
|
32
44
|
if model:
|
|
33
45
|
os.environ["HANZO_DEFAULT_MODEL"] = model
|
|
@@ -35,15 +47,18 @@ def chat_command(ctx, model: str, local: bool, once: bool, system: Optional[str]
|
|
|
35
47
|
os.environ["HANZO_USE_LOCAL"] = "true"
|
|
36
48
|
if system:
|
|
37
49
|
os.environ["HANZO_SYSTEM_PROMPT"] = system
|
|
38
|
-
|
|
50
|
+
|
|
39
51
|
if ipython:
|
|
40
52
|
from hanzo_repl.ipython_repl import main
|
|
53
|
+
|
|
41
54
|
sys.exit(main())
|
|
42
55
|
elif tui:
|
|
43
56
|
from hanzo_repl.textual_repl import main
|
|
57
|
+
|
|
44
58
|
sys.exit(main())
|
|
45
59
|
else:
|
|
46
60
|
from hanzo_repl.cli import main
|
|
61
|
+
|
|
47
62
|
sys.exit(main())
|
|
48
63
|
except ImportError:
|
|
49
64
|
console.print("[red]Error:[/red] hanzo-repl not installed")
|
|
@@ -51,9 +66,9 @@ def chat_command(ctx, model: str, local: bool, once: bool, system: Optional[str]
|
|
|
51
66
|
console.print("\nAlternatively:")
|
|
52
67
|
console.print(" pip install hanzo-repl")
|
|
53
68
|
return
|
|
54
|
-
|
|
69
|
+
|
|
55
70
|
prompt_text = " ".join(prompt) if prompt else None
|
|
56
|
-
|
|
71
|
+
|
|
57
72
|
if once or prompt_text:
|
|
58
73
|
# Single question mode
|
|
59
74
|
asyncio.run(ask_once(ctx, prompt_text or "Hello", model, local, system))
|
|
@@ -62,35 +77,35 @@ def chat_command(ctx, model: str, local: bool, once: bool, system: Optional[str]
|
|
|
62
77
|
asyncio.run(interactive_chat(ctx, model, local, system))
|
|
63
78
|
|
|
64
79
|
|
|
65
|
-
async def ask_once(
|
|
80
|
+
async def ask_once(
|
|
81
|
+
ctx, prompt: str, model: str, local: bool, system: Optional[str] = None
|
|
82
|
+
):
|
|
66
83
|
"""Ask a single question."""
|
|
67
84
|
messages = []
|
|
68
85
|
if system:
|
|
69
86
|
messages.append({"role": "system", "content": system})
|
|
70
87
|
messages.append({"role": "user", "content": prompt})
|
|
71
|
-
|
|
88
|
+
|
|
72
89
|
try:
|
|
73
90
|
if local:
|
|
74
91
|
# Use local cluster
|
|
75
92
|
base_url = "http://localhost:8000"
|
|
76
|
-
|
|
93
|
+
|
|
77
94
|
# Check if cluster is running
|
|
78
95
|
try:
|
|
79
96
|
async with httpx.AsyncClient() as client:
|
|
80
97
|
await client.get(f"{base_url}/health")
|
|
81
98
|
except httpx.ConnectError:
|
|
82
|
-
console.print(
|
|
99
|
+
console.print(
|
|
100
|
+
"[yellow]Local cluster not running. Start with: hanzo serve[/yellow]"
|
|
101
|
+
)
|
|
83
102
|
return
|
|
84
|
-
|
|
103
|
+
|
|
85
104
|
# Make request to local cluster
|
|
86
105
|
async with httpx.AsyncClient() as client:
|
|
87
106
|
response = await client.post(
|
|
88
107
|
f"{base_url}/v1/chat/completions",
|
|
89
|
-
json={
|
|
90
|
-
"model": model,
|
|
91
|
-
"messages": messages,
|
|
92
|
-
"stream": False
|
|
93
|
-
}
|
|
108
|
+
json={"model": model, "messages": messages, "stream": False},
|
|
94
109
|
)
|
|
95
110
|
response.raise_for_status()
|
|
96
111
|
result = response.json()
|
|
@@ -99,22 +114,23 @@ async def ask_once(ctx, prompt: str, model: str, local: bool, system: Optional[s
|
|
|
99
114
|
# Use cloud API
|
|
100
115
|
try:
|
|
101
116
|
from hanzoai import completion
|
|
117
|
+
|
|
102
118
|
result = completion(
|
|
103
119
|
model=f"anthropic/{model}" if "claude" in model else model,
|
|
104
|
-
messages=messages
|
|
120
|
+
messages=messages,
|
|
105
121
|
)
|
|
106
122
|
content = result.choices[0].message.content
|
|
107
123
|
except ImportError:
|
|
108
124
|
console.print("[red]Error:[/red] hanzoai not installed")
|
|
109
125
|
console.print("Install with: pip install hanzo[all]")
|
|
110
126
|
return
|
|
111
|
-
|
|
127
|
+
|
|
112
128
|
# Display response
|
|
113
129
|
if ctx.obj.get("json"):
|
|
114
130
|
console.print_json(data={"response": content})
|
|
115
131
|
else:
|
|
116
132
|
console.print(Markdown(content))
|
|
117
|
-
|
|
133
|
+
|
|
118
134
|
except Exception as e:
|
|
119
135
|
console.print(f"[red]Error: {e}[/red]")
|
|
120
136
|
|
|
@@ -123,27 +139,29 @@ async def interactive_chat(ctx, model: str, local: bool, system: Optional[str]):
|
|
|
123
139
|
"""Run interactive chat session."""
|
|
124
140
|
from prompt_toolkit import PromptSession
|
|
125
141
|
from prompt_toolkit.history import FileHistory
|
|
126
|
-
|
|
127
|
-
console.print(
|
|
142
|
+
|
|
143
|
+
console.print(
|
|
144
|
+
f"[cyan]Chat session started[/cyan] (model: {model}, mode: {'local' if local else 'cloud'})"
|
|
145
|
+
)
|
|
128
146
|
console.print("Type 'exit' or Ctrl+D to quit\n")
|
|
129
|
-
|
|
147
|
+
|
|
130
148
|
session = PromptSession(history=FileHistory(".hanzo_chat_history"))
|
|
131
149
|
messages = []
|
|
132
|
-
|
|
150
|
+
|
|
133
151
|
if system:
|
|
134
152
|
messages.append({"role": "system", "content": system})
|
|
135
|
-
|
|
153
|
+
|
|
136
154
|
while True:
|
|
137
155
|
try:
|
|
138
156
|
# Get user input
|
|
139
157
|
user_input = await session.prompt_async("You: ")
|
|
140
|
-
|
|
158
|
+
|
|
141
159
|
if user_input.lower() in ["exit", "quit"]:
|
|
142
160
|
break
|
|
143
|
-
|
|
161
|
+
|
|
144
162
|
# Add to messages
|
|
145
163
|
messages.append({"role": "user", "content": user_input})
|
|
146
|
-
|
|
164
|
+
|
|
147
165
|
# Get response
|
|
148
166
|
console.print("AI: ", end="")
|
|
149
167
|
with console.status(""):
|
|
@@ -155,8 +173,8 @@ async def interactive_chat(ctx, model: str, local: bool, system: Optional[str]):
|
|
|
155
173
|
json={
|
|
156
174
|
"model": model,
|
|
157
175
|
"messages": messages,
|
|
158
|
-
"stream": False
|
|
159
|
-
}
|
|
176
|
+
"stream": False,
|
|
177
|
+
},
|
|
160
178
|
)
|
|
161
179
|
response.raise_for_status()
|
|
162
180
|
result = response.json()
|
|
@@ -164,20 +182,21 @@ async def interactive_chat(ctx, model: str, local: bool, system: Optional[str]):
|
|
|
164
182
|
else:
|
|
165
183
|
# Use cloud API
|
|
166
184
|
from hanzoai import completion
|
|
185
|
+
|
|
167
186
|
result = completion(
|
|
168
187
|
model=f"anthropic/{model}" if "claude" in model else model,
|
|
169
|
-
messages=messages
|
|
188
|
+
messages=messages,
|
|
170
189
|
)
|
|
171
190
|
content = result.choices[0].message.content
|
|
172
|
-
|
|
191
|
+
|
|
173
192
|
# Display and save response
|
|
174
193
|
console.print(Markdown(content))
|
|
175
194
|
messages.append({"role": "assistant", "content": content})
|
|
176
195
|
console.print()
|
|
177
|
-
|
|
196
|
+
|
|
178
197
|
except KeyboardInterrupt:
|
|
179
198
|
continue
|
|
180
199
|
except EOFError:
|
|
181
200
|
break
|
|
182
201
|
except Exception as e:
|
|
183
|
-
console.print(f"\n[red]Error: {e}[/red]\n")
|
|
202
|
+
console.print(f"\n[red]Error: {e}[/red]\n")
|