hanzo 0.3.21__tar.gz → 0.3.22__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 (34) hide show
  1. {hanzo-0.3.21 → hanzo-0.3.22}/PKG-INFO +1 -1
  2. {hanzo-0.3.21 → hanzo-0.3.22}/pyproject.toml +1 -1
  3. {hanzo-0.3.21 → hanzo-0.3.22}/src/hanzo/cli.py +1 -1
  4. {hanzo-0.3.21 → hanzo-0.3.22}/src/hanzo/dev.py +74 -30
  5. {hanzo-0.3.21 → hanzo-0.3.22}/.gitignore +0 -0
  6. {hanzo-0.3.21 → hanzo-0.3.22}/README.md +0 -0
  7. {hanzo-0.3.21 → hanzo-0.3.22}/src/hanzo/__init__.py +0 -0
  8. {hanzo-0.3.21 → hanzo-0.3.22}/src/hanzo/__main__.py +0 -0
  9. {hanzo-0.3.21 → hanzo-0.3.22}/src/hanzo/commands/__init__.py +0 -0
  10. {hanzo-0.3.21 → hanzo-0.3.22}/src/hanzo/commands/agent.py +0 -0
  11. {hanzo-0.3.21 → hanzo-0.3.22}/src/hanzo/commands/auth.py +0 -0
  12. {hanzo-0.3.21 → hanzo-0.3.22}/src/hanzo/commands/chat.py +0 -0
  13. {hanzo-0.3.21 → hanzo-0.3.22}/src/hanzo/commands/cluster.py +0 -0
  14. {hanzo-0.3.21 → hanzo-0.3.22}/src/hanzo/commands/config.py +0 -0
  15. {hanzo-0.3.21 → hanzo-0.3.22}/src/hanzo/commands/mcp.py +0 -0
  16. {hanzo-0.3.21 → hanzo-0.3.22}/src/hanzo/commands/miner.py +0 -0
  17. {hanzo-0.3.21 → hanzo-0.3.22}/src/hanzo/commands/network.py +0 -0
  18. {hanzo-0.3.21 → hanzo-0.3.22}/src/hanzo/commands/repl.py +0 -0
  19. {hanzo-0.3.21 → hanzo-0.3.22}/src/hanzo/commands/tools.py +0 -0
  20. {hanzo-0.3.21 → hanzo-0.3.22}/src/hanzo/fallback_handler.py +0 -0
  21. {hanzo-0.3.21 → hanzo-0.3.22}/src/hanzo/interactive/__init__.py +0 -0
  22. {hanzo-0.3.21 → hanzo-0.3.22}/src/hanzo/interactive/dashboard.py +0 -0
  23. {hanzo-0.3.21 → hanzo-0.3.22}/src/hanzo/interactive/repl.py +0 -0
  24. {hanzo-0.3.21 → hanzo-0.3.22}/src/hanzo/mcp_server.py +0 -0
  25. {hanzo-0.3.21 → hanzo-0.3.22}/src/hanzo/memory_manager.py +0 -0
  26. {hanzo-0.3.21 → hanzo-0.3.22}/src/hanzo/orchestrator_config.py +0 -0
  27. {hanzo-0.3.21 → hanzo-0.3.22}/src/hanzo/rate_limiter.py +0 -0
  28. {hanzo-0.3.21 → hanzo-0.3.22}/src/hanzo/repl.py +0 -0
  29. {hanzo-0.3.21 → hanzo-0.3.22}/src/hanzo/router/__init__.py +0 -0
  30. {hanzo-0.3.21 → hanzo-0.3.22}/src/hanzo/streaming.py +0 -0
  31. {hanzo-0.3.21 → hanzo-0.3.22}/src/hanzo/utils/__init__.py +0 -0
  32. {hanzo-0.3.21 → hanzo-0.3.22}/src/hanzo/utils/config.py +0 -0
  33. {hanzo-0.3.21 → hanzo-0.3.22}/src/hanzo/utils/net_check.py +0 -0
  34. {hanzo-0.3.21 → hanzo-0.3.22}/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.21
3
+ Version: 0.3.22
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.21"
3
+ version = "0.3.22"
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"},
@@ -26,7 +26,7 @@ from .utils.output import console
26
26
  from .interactive.repl import HanzoREPL
27
27
 
28
28
  # Version
29
- __version__ = "0.3.21"
29
+ __version__ = "0.3.22"
30
30
 
31
31
 
32
32
  @click.group(invoke_without_command=True)
@@ -697,34 +697,35 @@ class HanzoDevREPL:
697
697
  padding=(0, 1)
698
698
  ))
699
699
  console.print()
700
+
701
+ # Check for available API keys and show status
702
+ from .fallback_handler import FallbackHandler
703
+ handler = FallbackHandler()
704
+ if not handler.fallback_order:
705
+ console.print("[yellow]⚠️ No API keys detected[/yellow]")
706
+ console.print("[dim]Set OPENAI_API_KEY or ANTHROPIC_API_KEY to enable AI[/dim]")
707
+ console.print()
708
+ else:
709
+ primary = handler.fallback_order[0][1]
710
+ console.print(f"[green]✅ Using {primary} for AI responses[/green]")
711
+ console.print()
700
712
 
701
713
  while True:
702
714
  try:
703
- # Draw input box border (top)
704
- console.print("[dim white]╭" + "─" * 78 + "╮[/dim white]")
705
-
706
- # Get input with styled prompt inside the box
707
- console.print("[dim white]│[/dim white] ", end="")
708
-
715
+ # Simple prompt without box borders to avoid rendering issues
709
716
  try:
710
- # Get input - using simple input() wrapped in executor for async
711
- # The visual box is drawn by console.print statements
717
+ # Add spacing to prevent UI cutoff at bottom
712
718
  user_input = await asyncio.get_event_loop().run_in_executor(
713
719
  None,
714
720
  input,
715
- '› ' # Using › instead of > for a more modern look
721
+ '› ' # Clean prompt
716
722
  )
717
-
718
- # Draw input box border (bottom)
719
- console.print("[dim white]╰" + "─" * 78 + "╯[/dim white]")
723
+ console.print() # Add spacing after input
720
724
 
721
725
  except EOFError:
722
726
  console.print() # New line before exit
723
- console.print("[dim white]╰" + "─" * 78 + "╯[/dim white]")
724
727
  break
725
728
  except KeyboardInterrupt:
726
- console.print() # Complete the box
727
- console.print("[dim white]╰" + "─" * 78 + "╯[/dim white]")
728
729
  console.print("\n[dim yellow]Use /exit to quit[/dim]")
729
730
  continue
730
731
 
@@ -1044,8 +1045,25 @@ Examples:
1044
1045
  await self._use_local_model(message)
1045
1046
  return
1046
1047
 
1047
- # Try OpenAI first
1048
- if os.getenv("OPENAI_API_KEY"):
1048
+ # Use the fallback handler to intelligently try available options
1049
+ from .fallback_handler import smart_chat
1050
+ response = await smart_chat(message, console=console)
1051
+
1052
+ if response:
1053
+ from rich.panel import Panel
1054
+ console.print()
1055
+ console.print(Panel(
1056
+ response,
1057
+ title="[bold cyan]AI Response[/bold cyan]",
1058
+ title_align="left",
1059
+ border_style="dim cyan",
1060
+ padding=(1, 2)
1061
+ ))
1062
+ return
1063
+
1064
+ # Try OpenAI first explicitly (in case fallback handler missed it)
1065
+ openai_key = os.environ.get("OPENAI_API_KEY") or os.getenv("OPENAI_API_KEY")
1066
+ if openai_key:
1049
1067
  try:
1050
1068
  from openai import AsyncOpenAI
1051
1069
 
@@ -1578,13 +1596,10 @@ async def run_dev_orchestrator(**kwargs):
1578
1596
  console_obj.print("[red]Failed to initialize network[/red]")
1579
1597
  return
1580
1598
  else:
1581
- # Fallback to multi-Claude mode
1582
- console_obj.print(f"[cyan]Mode: Multi-Claude Orchestration (legacy)[/cyan]")
1583
- console_obj.print(
1584
- f"Instances: {instances} (1 primary + {instances-1} critic{'s' if instances > 2 else ''})"
1585
- )
1599
+ # Fallback to API mode
1600
+ console_obj.print(f"[cyan]Mode: AI Chat[/cyan]")
1601
+ console_obj.print(f"Model: {orchestrator_model}")
1586
1602
  console_obj.print(f"MCP Tools: {'Enabled' if mcp_tools else 'Disabled'}")
1587
- console_obj.print(f"Networking: {'Enabled' if network_mode else 'Disabled'}")
1588
1603
  console_obj.print(f"Guardrails: {'Enabled' if guardrails else 'Disabled'}\n")
1589
1604
 
1590
1605
  orchestrator = MultiClaudeOrchestrator(
@@ -2216,6 +2231,21 @@ class MultiClaudeOrchestrator(HanzoDevOrchestrator):
2216
2231
 
2217
2232
  async def initialize(self):
2218
2233
  """Initialize all Claude instances with MCP networking."""
2234
+ # Check if Claude is available first
2235
+ claude_available = False
2236
+ try:
2237
+ import shutil
2238
+ if self.claude_code_path and Path(self.claude_code_path).exists():
2239
+ claude_available = True
2240
+ elif shutil.which("claude"):
2241
+ claude_available = True
2242
+ except:
2243
+ pass
2244
+
2245
+ if not claude_available:
2246
+ # Skip Claude instance initialization - will use API fallback silently
2247
+ return
2248
+
2219
2249
  self.console.print("[cyan]Initializing Claude instances...[/cyan]")
2220
2250
 
2221
2251
  for i in range(self.num_instances):
@@ -2237,7 +2267,8 @@ class MultiClaudeOrchestrator(HanzoDevOrchestrator):
2237
2267
  if success:
2238
2268
  self.console.print(f"[green]✓ Instance {i} started[/green]")
2239
2269
  else:
2240
- self.console.print(f"[red]✗ Failed to start instance {i}[/red]")
2270
+ # Don't show error, just skip silently
2271
+ pass
2241
2272
 
2242
2273
  async def _create_instance_config(self, index: int, role: str) -> Dict:
2243
2274
  """Create configuration for a Claude instance."""
@@ -2377,7 +2408,12 @@ class MultiClaudeOrchestrator(HanzoDevOrchestrator):
2377
2408
 
2378
2409
  # Check if instances are initialized
2379
2410
  if not self.claude_instances:
2380
- # No instances started, use direct API
2411
+ # No instances started, use fallback handler for smart routing
2412
+ from .fallback_handler import smart_chat
2413
+ response = await smart_chat(task, console=self.console)
2414
+ if response:
2415
+ return {"output": response, "success": True}
2416
+ # If smart_chat fails, try direct API as last resort
2381
2417
  return await self._call_api_model(task)
2382
2418
 
2383
2419
  # Step 1: Primary execution
@@ -2530,11 +2566,12 @@ class MultiClaudeOrchestrator(HanzoDevOrchestrator):
2530
2566
  """Call API-based model and return structured response."""
2531
2567
  import os
2532
2568
 
2533
- # Try OpenAI
2534
- if os.getenv("OPENAI_API_KEY"):
2569
+ # Try OpenAI first (check environment variable properly)
2570
+ openai_key = os.environ.get("OPENAI_API_KEY") or os.getenv("OPENAI_API_KEY")
2571
+ if openai_key:
2535
2572
  try:
2536
2573
  from openai import AsyncOpenAI
2537
- client = AsyncOpenAI()
2574
+ client = AsyncOpenAI(api_key=openai_key)
2538
2575
  response = await client.chat.completions.create(
2539
2576
  model="gpt-4",
2540
2577
  messages=[{"role": "user", "content": prompt}],
@@ -2546,10 +2583,11 @@ class MultiClaudeOrchestrator(HanzoDevOrchestrator):
2546
2583
  logger.error(f"OpenAI API error: {e}")
2547
2584
 
2548
2585
  # Try Anthropic
2549
- if os.getenv("ANTHROPIC_API_KEY"):
2586
+ anthropic_key = os.environ.get("ANTHROPIC_API_KEY") or os.getenv("ANTHROPIC_API_KEY")
2587
+ if anthropic_key:
2550
2588
  try:
2551
2589
  from anthropic import AsyncAnthropic
2552
- client = AsyncAnthropic()
2590
+ client = AsyncAnthropic(api_key=anthropic_key)
2553
2591
  response = await client.messages.create(
2554
2592
  model="claude-3-5-sonnet-20241022",
2555
2593
  messages=[{"role": "user", "content": prompt}],
@@ -2560,6 +2598,12 @@ class MultiClaudeOrchestrator(HanzoDevOrchestrator):
2560
2598
  except Exception as e:
2561
2599
  logger.error(f"Anthropic API error: {e}")
2562
2600
 
2601
+ # Try fallback handler as last resort
2602
+ from .fallback_handler import smart_chat
2603
+ response = await smart_chat(prompt, console=None) # No console to avoid duplicate messages
2604
+ if response:
2605
+ return {"output": response, "success": True}
2606
+
2563
2607
  return {"output": "No API keys configured. Set OPENAI_API_KEY or ANTHROPIC_API_KEY", "success": False}
2564
2608
 
2565
2609
  async def _validate_improvement(self, original: Dict, improved: Dict) -> bool:
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