hanzo 0.3.30__tar.gz → 0.3.31__tar.gz
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-0.3.30 → hanzo-0.3.31}/PKG-INFO +1 -1
- {hanzo-0.3.30 → hanzo-0.3.31}/pyproject.toml +1 -1
- {hanzo-0.3.30 → hanzo-0.3.31}/src/hanzo/interactive/enhanced_repl.py +45 -23
- {hanzo-0.3.30 → hanzo-0.3.31}/src/hanzo/tools/detector.py +47 -37
- {hanzo-0.3.30 → hanzo-0.3.31}/.gitignore +0 -0
- {hanzo-0.3.30 → hanzo-0.3.31}/README.md +0 -0
- {hanzo-0.3.30 → hanzo-0.3.31}/src/hanzo/__init__.py +0 -0
- {hanzo-0.3.30 → hanzo-0.3.31}/src/hanzo/__main__.py +0 -0
- {hanzo-0.3.30 → hanzo-0.3.31}/src/hanzo/base_agent.py +0 -0
- {hanzo-0.3.30 → hanzo-0.3.31}/src/hanzo/batch_orchestrator.py +0 -0
- {hanzo-0.3.30 → hanzo-0.3.31}/src/hanzo/cli.py +0 -0
- {hanzo-0.3.30 → hanzo-0.3.31}/src/hanzo/commands/__init__.py +0 -0
- {hanzo-0.3.30 → hanzo-0.3.31}/src/hanzo/commands/agent.py +0 -0
- {hanzo-0.3.30 → hanzo-0.3.31}/src/hanzo/commands/auth.py +0 -0
- {hanzo-0.3.30 → hanzo-0.3.31}/src/hanzo/commands/auth_broken.py +0 -0
- {hanzo-0.3.30 → hanzo-0.3.31}/src/hanzo/commands/chat.py +0 -0
- {hanzo-0.3.30 → hanzo-0.3.31}/src/hanzo/commands/config.py +0 -0
- {hanzo-0.3.30 → hanzo-0.3.31}/src/hanzo/commands/mcp.py +0 -0
- {hanzo-0.3.30 → hanzo-0.3.31}/src/hanzo/commands/miner.py +0 -0
- {hanzo-0.3.30 → hanzo-0.3.31}/src/hanzo/commands/network.py +0 -0
- {hanzo-0.3.30 → hanzo-0.3.31}/src/hanzo/commands/node.py +0 -0
- {hanzo-0.3.30 → hanzo-0.3.31}/src/hanzo/commands/repl.py +0 -0
- {hanzo-0.3.30 → hanzo-0.3.31}/src/hanzo/commands/router.py +0 -0
- {hanzo-0.3.30 → hanzo-0.3.31}/src/hanzo/commands/tools.py +0 -0
- {hanzo-0.3.30 → hanzo-0.3.31}/src/hanzo/dev.py +0 -0
- {hanzo-0.3.30 → hanzo-0.3.31}/src/hanzo/fallback_handler.py +0 -0
- {hanzo-0.3.30 → hanzo-0.3.31}/src/hanzo/interactive/__init__.py +0 -0
- {hanzo-0.3.30 → hanzo-0.3.31}/src/hanzo/interactive/dashboard.py +0 -0
- {hanzo-0.3.30 → hanzo-0.3.31}/src/hanzo/interactive/model_selector.py +0 -0
- {hanzo-0.3.30 → hanzo-0.3.31}/src/hanzo/interactive/repl.py +0 -0
- {hanzo-0.3.30 → hanzo-0.3.31}/src/hanzo/interactive/todo_manager.py +0 -0
- {hanzo-0.3.30 → hanzo-0.3.31}/src/hanzo/mcp_server.py +0 -0
- {hanzo-0.3.30 → hanzo-0.3.31}/src/hanzo/memory_manager.py +0 -0
- {hanzo-0.3.30 → hanzo-0.3.31}/src/hanzo/model_registry.py +0 -0
- {hanzo-0.3.30 → hanzo-0.3.31}/src/hanzo/orchestrator_config.py +0 -0
- {hanzo-0.3.30 → hanzo-0.3.31}/src/hanzo/rate_limiter.py +0 -0
- {hanzo-0.3.30 → hanzo-0.3.31}/src/hanzo/repl.py +0 -0
- {hanzo-0.3.30 → hanzo-0.3.31}/src/hanzo/router/__init__.py +0 -0
- {hanzo-0.3.30 → hanzo-0.3.31}/src/hanzo/streaming.py +0 -0
- {hanzo-0.3.30 → hanzo-0.3.31}/src/hanzo/tools/__init__.py +0 -0
- {hanzo-0.3.30 → hanzo-0.3.31}/src/hanzo/ui/__init__.py +0 -0
- {hanzo-0.3.30 → hanzo-0.3.31}/src/hanzo/ui/inline_startup.py +0 -0
- {hanzo-0.3.30 → hanzo-0.3.31}/src/hanzo/ui/startup.py +0 -0
- {hanzo-0.3.30 → hanzo-0.3.31}/src/hanzo/utils/__init__.py +0 -0
- {hanzo-0.3.30 → hanzo-0.3.31}/src/hanzo/utils/config.py +0 -0
- {hanzo-0.3.30 → hanzo-0.3.31}/src/hanzo/utils/net_check.py +0 -0
- {hanzo-0.3.30 → hanzo-0.3.31}/src/hanzo/utils/output.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: hanzo
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.31
|
|
4
4
|
Summary: Hanzo AI - Complete AI Infrastructure Platform with CLI, Router, MCP, and Agent Runtime
|
|
5
5
|
Project-URL: Homepage, https://hanzo.ai
|
|
6
6
|
Project-URL: Repository, https://github.com/hanzoai/python-sdk
|
|
@@ -100,6 +100,7 @@ class EnhancedHanzoREPL:
|
|
|
100
100
|
self.tool_detector = ToolDetector(console) if ToolDetector else None
|
|
101
101
|
self.detected_tools = []
|
|
102
102
|
self.current_tool = None
|
|
103
|
+
self.failed_tools = set() # Track tools that have failed this session
|
|
103
104
|
|
|
104
105
|
# Initialize background task manager
|
|
105
106
|
self.task_manager = BackgroundTaskManager(console) if BackgroundTaskManager else None
|
|
@@ -637,31 +638,52 @@ class EnhancedHanzoREPL:
|
|
|
637
638
|
"""Chat with AI using current model or tool."""
|
|
638
639
|
# Check if using a tool
|
|
639
640
|
if self.current_model.startswith("tool:") and self.current_tool:
|
|
640
|
-
#
|
|
641
|
-
self.
|
|
641
|
+
# Skip if this tool has already failed this session
|
|
642
|
+
if self.current_tool.name in self.failed_tools:
|
|
643
|
+
# Automatically use the first working tool
|
|
644
|
+
for tool in self.detected_tools:
|
|
645
|
+
if tool.name not in self.failed_tools:
|
|
646
|
+
self.current_tool = tool
|
|
647
|
+
self.current_model = f"tool:{tool.name}"
|
|
648
|
+
self.console.print(f"[yellow]Switched to {tool.display_name}[/yellow]")
|
|
649
|
+
break
|
|
642
650
|
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
self.
|
|
647
|
-
else:
|
|
648
|
-
# Show error and try fallback
|
|
649
|
-
self.console.print(f"[red]Error: {output}[/red]")
|
|
650
|
-
|
|
651
|
-
# Try to find next available tool
|
|
652
|
-
if self.tool_detector and self.tool_detector.detected_tools:
|
|
653
|
-
for fallback_tool in self.tool_detector.detected_tools:
|
|
654
|
-
if fallback_tool != self.current_tool:
|
|
655
|
-
self.console.print(f"[yellow]Trying {fallback_tool.display_name}...[/yellow]")
|
|
656
|
-
success, output = self.tool_detector.execute_with_tool(fallback_tool, message)
|
|
657
|
-
if success:
|
|
658
|
-
self.console.print(output)
|
|
659
|
-
# Suggest switching to working tool
|
|
660
|
-
self.console.print(f"\n[dim]Tip: Switch to {fallback_tool.display_name} with /model {fallback_tool.name}[/dim]")
|
|
661
|
-
return
|
|
662
|
-
else:
|
|
663
|
-
self.console.print(f"[red]{fallback_tool.display_name} also failed[/red]")
|
|
651
|
+
# Try the current tool
|
|
652
|
+
if self.current_tool.name not in self.failed_tools:
|
|
653
|
+
self.console.print(f"[dim]Using {self.current_tool.display_name}...[/dim]")
|
|
654
|
+
success, output = self.tool_detector.execute_with_tool(self.current_tool, message)
|
|
664
655
|
|
|
656
|
+
if success:
|
|
657
|
+
self.console.print(output)
|
|
658
|
+
return
|
|
659
|
+
else:
|
|
660
|
+
# Mark this tool as failed for the session
|
|
661
|
+
self.failed_tools.add(self.current_tool.name)
|
|
662
|
+
self.console.print(f"[red]Error: {output}[/red]")
|
|
663
|
+
|
|
664
|
+
# Try to find next available tool
|
|
665
|
+
found_working = False
|
|
666
|
+
if self.tool_detector and self.tool_detector.detected_tools:
|
|
667
|
+
for fallback_tool in self.tool_detector.detected_tools:
|
|
668
|
+
if fallback_tool.name not in self.failed_tools:
|
|
669
|
+
self.console.print(f"[yellow]Trying {fallback_tool.display_name}...[/yellow]")
|
|
670
|
+
success, output = self.tool_detector.execute_with_tool(fallback_tool, message)
|
|
671
|
+
if success:
|
|
672
|
+
self.console.print(output)
|
|
673
|
+
# Automatically switch to this working tool
|
|
674
|
+
self.current_tool = fallback_tool
|
|
675
|
+
self.current_model = f"tool:{fallback_tool.name}"
|
|
676
|
+
self.config["default_model"] = self.current_model
|
|
677
|
+
self.save_config()
|
|
678
|
+
self.console.print(f"\n[green]Switched to {fallback_tool.display_name} (now default)[/green]")
|
|
679
|
+
found_working = True
|
|
680
|
+
return
|
|
681
|
+
else:
|
|
682
|
+
# Mark as failed
|
|
683
|
+
self.failed_tools.add(fallback_tool.name)
|
|
684
|
+
self.console.print(f"[red]{fallback_tool.display_name} also failed[/red]")
|
|
685
|
+
|
|
686
|
+
if not found_working:
|
|
665
687
|
# Final fallback to cloud model
|
|
666
688
|
self.console.print(f"[yellow]Falling back to cloud model...[/yellow]")
|
|
667
689
|
await self.execute_command("ask", f"--cloud --model gpt-3.5-turbo {message}")
|
|
@@ -167,51 +167,56 @@ class ToolDetector:
|
|
|
167
167
|
"""Detect if a specific tool is available."""
|
|
168
168
|
# Check API endpoint first (for services like hanzod)
|
|
169
169
|
if tool.api_endpoint:
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
#
|
|
174
|
-
|
|
170
|
+
# For Hanzo Node, directly test the chat endpoint since health may lie
|
|
171
|
+
if tool.name == "hanzod":
|
|
172
|
+
try:
|
|
173
|
+
# Try both ports in case configuration varies
|
|
174
|
+
for port in [3690, 8000]:
|
|
175
175
|
try:
|
|
176
|
-
# Check if the chat completions endpoint works
|
|
176
|
+
# Check if the chat completions endpoint actually works
|
|
177
177
|
test_response = httpx.post(
|
|
178
|
-
"http://localhost:
|
|
178
|
+
f"http://localhost:{port}/v1/chat/completions",
|
|
179
179
|
json={
|
|
180
180
|
"messages": [{"role": "user", "content": "test"}],
|
|
181
|
-
"model": "
|
|
181
|
+
"model": "default",
|
|
182
182
|
"max_tokens": 1
|
|
183
183
|
},
|
|
184
|
-
timeout=
|
|
184
|
+
timeout=1.0
|
|
185
185
|
)
|
|
186
|
-
# Only
|
|
187
|
-
#
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
186
|
+
# Only accept if we get a proper response (not 404, not connection error)
|
|
187
|
+
if test_response.status_code in [200, 400, 422]: # 400/422 means endpoint exists but params wrong
|
|
188
|
+
tool.detected = True
|
|
189
|
+
tool.version = f"Running (Port {port})"
|
|
190
|
+
tool.api_endpoint = f"http://localhost:{port}/health" # Update port
|
|
191
|
+
|
|
192
|
+
# Try to get model info
|
|
193
|
+
try:
|
|
194
|
+
models_response = httpx.get(f"http://localhost:{port}/v1/models", timeout=0.5)
|
|
195
|
+
if models_response.status_code == 200:
|
|
196
|
+
models = models_response.json().get("data", [])
|
|
197
|
+
if models:
|
|
198
|
+
tool.version = f"Running ({len(models)} models, Port {port})"
|
|
199
|
+
except:
|
|
200
|
+
pass
|
|
201
|
+
|
|
202
|
+
return True
|
|
203
|
+
except (httpx.ConnectError, httpx.TimeoutException):
|
|
204
|
+
# Connection refused or timeout - node not available on this port
|
|
205
|
+
continue
|
|
206
|
+
except:
|
|
207
|
+
pass
|
|
208
|
+
# If we get here, Hanzo Node is not properly available
|
|
209
|
+
return False
|
|
210
|
+
else:
|
|
211
|
+
# For other services, check health endpoint
|
|
212
|
+
try:
|
|
213
|
+
response = httpx.get(tool.api_endpoint, timeout=1.0)
|
|
214
|
+
if response.status_code == 200:
|
|
210
215
|
tool.detected = True
|
|
211
216
|
tool.version = "Running"
|
|
212
217
|
return True
|
|
213
|
-
|
|
214
|
-
|
|
218
|
+
except:
|
|
219
|
+
pass
|
|
215
220
|
|
|
216
221
|
# Check if command exists
|
|
217
222
|
if tool.command:
|
|
@@ -342,10 +347,15 @@ class ToolDetector:
|
|
|
342
347
|
try:
|
|
343
348
|
# Special handling for Hanzo services
|
|
344
349
|
if tool.name == "hanzod":
|
|
345
|
-
# Use the local API directly
|
|
350
|
+
# Use the local API directly - extract port from the detected endpoint
|
|
346
351
|
try:
|
|
352
|
+
# Extract port from api_endpoint (e.g., "http://localhost:3690/health")
|
|
353
|
+
import re
|
|
354
|
+
port_match = re.search(r':(\d+)', tool.api_endpoint)
|
|
355
|
+
port = port_match.group(1) if port_match else "3690"
|
|
356
|
+
|
|
347
357
|
response = httpx.post(
|
|
348
|
-
"http://localhost:
|
|
358
|
+
f"http://localhost:{port}/v1/chat/completions",
|
|
349
359
|
json={
|
|
350
360
|
"messages": [{"role": "user", "content": prompt}],
|
|
351
361
|
"model": "default", # Use default model
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|