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.

Files changed (47) hide show
  1. {hanzo-0.3.30 → hanzo-0.3.31}/PKG-INFO +1 -1
  2. {hanzo-0.3.30 → hanzo-0.3.31}/pyproject.toml +1 -1
  3. {hanzo-0.3.30 → hanzo-0.3.31}/src/hanzo/interactive/enhanced_repl.py +45 -23
  4. {hanzo-0.3.30 → hanzo-0.3.31}/src/hanzo/tools/detector.py +47 -37
  5. {hanzo-0.3.30 → hanzo-0.3.31}/.gitignore +0 -0
  6. {hanzo-0.3.30 → hanzo-0.3.31}/README.md +0 -0
  7. {hanzo-0.3.30 → hanzo-0.3.31}/src/hanzo/__init__.py +0 -0
  8. {hanzo-0.3.30 → hanzo-0.3.31}/src/hanzo/__main__.py +0 -0
  9. {hanzo-0.3.30 → hanzo-0.3.31}/src/hanzo/base_agent.py +0 -0
  10. {hanzo-0.3.30 → hanzo-0.3.31}/src/hanzo/batch_orchestrator.py +0 -0
  11. {hanzo-0.3.30 → hanzo-0.3.31}/src/hanzo/cli.py +0 -0
  12. {hanzo-0.3.30 → hanzo-0.3.31}/src/hanzo/commands/__init__.py +0 -0
  13. {hanzo-0.3.30 → hanzo-0.3.31}/src/hanzo/commands/agent.py +0 -0
  14. {hanzo-0.3.30 → hanzo-0.3.31}/src/hanzo/commands/auth.py +0 -0
  15. {hanzo-0.3.30 → hanzo-0.3.31}/src/hanzo/commands/auth_broken.py +0 -0
  16. {hanzo-0.3.30 → hanzo-0.3.31}/src/hanzo/commands/chat.py +0 -0
  17. {hanzo-0.3.30 → hanzo-0.3.31}/src/hanzo/commands/config.py +0 -0
  18. {hanzo-0.3.30 → hanzo-0.3.31}/src/hanzo/commands/mcp.py +0 -0
  19. {hanzo-0.3.30 → hanzo-0.3.31}/src/hanzo/commands/miner.py +0 -0
  20. {hanzo-0.3.30 → hanzo-0.3.31}/src/hanzo/commands/network.py +0 -0
  21. {hanzo-0.3.30 → hanzo-0.3.31}/src/hanzo/commands/node.py +0 -0
  22. {hanzo-0.3.30 → hanzo-0.3.31}/src/hanzo/commands/repl.py +0 -0
  23. {hanzo-0.3.30 → hanzo-0.3.31}/src/hanzo/commands/router.py +0 -0
  24. {hanzo-0.3.30 → hanzo-0.3.31}/src/hanzo/commands/tools.py +0 -0
  25. {hanzo-0.3.30 → hanzo-0.3.31}/src/hanzo/dev.py +0 -0
  26. {hanzo-0.3.30 → hanzo-0.3.31}/src/hanzo/fallback_handler.py +0 -0
  27. {hanzo-0.3.30 → hanzo-0.3.31}/src/hanzo/interactive/__init__.py +0 -0
  28. {hanzo-0.3.30 → hanzo-0.3.31}/src/hanzo/interactive/dashboard.py +0 -0
  29. {hanzo-0.3.30 → hanzo-0.3.31}/src/hanzo/interactive/model_selector.py +0 -0
  30. {hanzo-0.3.30 → hanzo-0.3.31}/src/hanzo/interactive/repl.py +0 -0
  31. {hanzo-0.3.30 → hanzo-0.3.31}/src/hanzo/interactive/todo_manager.py +0 -0
  32. {hanzo-0.3.30 → hanzo-0.3.31}/src/hanzo/mcp_server.py +0 -0
  33. {hanzo-0.3.30 → hanzo-0.3.31}/src/hanzo/memory_manager.py +0 -0
  34. {hanzo-0.3.30 → hanzo-0.3.31}/src/hanzo/model_registry.py +0 -0
  35. {hanzo-0.3.30 → hanzo-0.3.31}/src/hanzo/orchestrator_config.py +0 -0
  36. {hanzo-0.3.30 → hanzo-0.3.31}/src/hanzo/rate_limiter.py +0 -0
  37. {hanzo-0.3.30 → hanzo-0.3.31}/src/hanzo/repl.py +0 -0
  38. {hanzo-0.3.30 → hanzo-0.3.31}/src/hanzo/router/__init__.py +0 -0
  39. {hanzo-0.3.30 → hanzo-0.3.31}/src/hanzo/streaming.py +0 -0
  40. {hanzo-0.3.30 → hanzo-0.3.31}/src/hanzo/tools/__init__.py +0 -0
  41. {hanzo-0.3.30 → hanzo-0.3.31}/src/hanzo/ui/__init__.py +0 -0
  42. {hanzo-0.3.30 → hanzo-0.3.31}/src/hanzo/ui/inline_startup.py +0 -0
  43. {hanzo-0.3.30 → hanzo-0.3.31}/src/hanzo/ui/startup.py +0 -0
  44. {hanzo-0.3.30 → hanzo-0.3.31}/src/hanzo/utils/__init__.py +0 -0
  45. {hanzo-0.3.30 → hanzo-0.3.31}/src/hanzo/utils/config.py +0 -0
  46. {hanzo-0.3.30 → hanzo-0.3.31}/src/hanzo/utils/net_check.py +0 -0
  47. {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.30
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
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "hanzo"
3
- version = "0.3.30"
3
+ version = "0.3.31"
4
4
  description = "Hanzo AI - Complete AI Infrastructure Platform with CLI, Router, MCP, and Agent Runtime"
5
5
  authors = [
6
6
  {name = "Hanzo AI", email = "dev@hanzo.ai"},
@@ -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
- # Use the detected tool directly
641
- self.console.print(f"[dim]Using {self.current_tool.display_name}...[/dim]")
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
- success, output = self.tool_detector.execute_with_tool(self.current_tool, message)
644
-
645
- if success:
646
- self.console.print(output)
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
- try:
171
- response = httpx.get(tool.api_endpoint, timeout=1.0)
172
- if response.status_code == 200:
173
- # For Hanzo Node, verify it can actually handle chat completions
174
- if tool.name == "hanzod":
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:3690/v1/chat/completions",
178
+ f"http://localhost:{port}/v1/chat/completions",
179
179
  json={
180
180
  "messages": [{"role": "user", "content": "test"}],
181
- "model": "test",
181
+ "model": "default",
182
182
  "max_tokens": 1
183
183
  },
184
- timeout=2.0
184
+ timeout=1.0
185
185
  )
186
- # Only mark as detected if we get a valid response or specific error
187
- # 404 means the endpoint doesn't exist
188
- if test_response.status_code == 404:
189
- return False
190
-
191
- tool.detected = True
192
- tool.version = "Running (Local AI)"
193
-
194
- # Try to get model info
195
- try:
196
- models_response = httpx.get("http://localhost:3690/v1/models", timeout=1.0)
197
- if models_response.status_code == 200:
198
- models = models_response.json().get("data", [])
199
- if models:
200
- tool.version = f"Running ({len(models)} models)"
201
- except:
202
- pass
203
-
204
- return True
205
- except:
206
- # If chat endpoint doesn't work, node isn't useful
207
- return False
208
- else:
209
- # For other services, just check health endpoint
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
- except:
214
- pass
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 with correct endpoint (port 3690)
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:3690/v1/chat/completions",
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