rebrand: rename litellm module to llm — complete purge

All imports, test files, docs, and config references updated.
import litellm → import llm across entire codebase.
This commit is contained in:
2026-02-25 15:13:14 -08:00
parent 6f2c239ea6
commit 394c437277
42 changed files with 168 additions and 168 deletions
+1 -1
View File
@@ -120,7 +120,7 @@ jobs:
# Run all working tests with strict mode
# Note: Some async tests require specific pytest-asyncio configuration
# The core test suite validates CI requirements
python -m pytest tests/test_agent_tools_ci.py tests/test_litellm_warnings.py \
python -m pytest tests/test_agent_tools_ci.py tests/test_llm_warnings.py \
-v \
--strict-markers \
--tb=short \
+1 -1
View File
@@ -71,7 +71,7 @@ jobs:
cd pkg/hanzo-mcp
source .venv/bin/activate
# Run specific working tests, excluding the failing stdio_server test
python -m pytest tests/test_agent_tools_ci.py tests/test_litellm_warnings.py -v
python -m pytest tests/test_agent_tools_ci.py tests/test_llm_warnings.py -v
python -m pytest tests/test_hanzo_mcp_simple.py::test_cli_help tests/test_hanzo_mcp_simple.py::test_cli_version tests/test_hanzo_mcp_simple.py::test_import_tools -v
echo "✓ Test Summary: 15 tests passed (13 passed + 2 skipped), 100% success rate"
+1 -1
View File
@@ -116,7 +116,7 @@ hanzo-aci includes these dependencies:
- **numpy, pandas, scipy** - Data processing
- **networkx** - Graph analysis
- **litellm** - LLM integration
- **llm** - LLM integration
- **gitpython** - Git operations
- **tree-sitter** - AST parsing
- **grep-ast** - Code search
+1 -1
View File
@@ -1,6 +1,6 @@
# LLM Tools
Unified LLM interface via LiteLLM.
Unified LLM interface via LLM.
→ **Full documentation: [../../tools/llm-tools.md](../../tools/llm-tools.md)**
+1 -1
View File
@@ -150,7 +150,7 @@ pip install hanzo-mcp[tools-all]
# Or specific tools
pip install hanzo-mcp[browser] # Playwright
pip install hanzo-mcp[llm] # LiteLLM
pip install hanzo-mcp[llm] # LLM
```
### Logs and Debugging
+3 -3
View File
@@ -8,7 +8,7 @@ Unified LLM interface with multi-model consensus. Access 100+ models through a s
pip install hanzo-tools-llm[full]
```
Note: This package requires `litellm` which is a heavy dependency. It's disabled by default in hanzo-mcp.
Note: This package requires `llm` which is a heavy dependency. It's disabled by default in hanzo-mcp.
## Overview
@@ -173,7 +173,7 @@ llm(action="test", model="gpt-4")
## Supported Providers
The LLM tool uses [LiteLLM](https://github.com/BerriAI/litellm) to support 100+ models:
The LLM tool uses [LLM](https://github.com/BerriAI/llm) to support 100+ models:
| Provider | Models | API Key Env |
|----------|--------|-------------|
@@ -317,5 +317,5 @@ Configure model aliases in `~/.hanzo/llm/aliases.json`:
1. **Set appropriate temperature** - Low for factual, high for creative
2. **Use system prompts** - Provide context for better responses
3. **Validate JSON output** - Even with json_mode, validate responses
4. **Handle rate limits** - LiteLLM handles retries automatically
4. **Handle rate limits** - LLM handles retries automatically
5. **Monitor costs** - Different models have different pricing
+1 -1
View File
@@ -15,7 +15,7 @@ except Exception: # pragma: no cover
import os
import warnings
# Suppress litellm deprecation warnings about event loop
# Suppress llm deprecation warnings about event loop
warnings.filterwarnings(
"ignore", message="There is no current event loop", category=DeprecationWarning
)
+1 -1
View File
@@ -156,7 +156,7 @@ Examples:
agent_group = parser.add_argument_group("Agent Tool Configuration")
agent_group.add_argument(
"--agent-model",
help="Model name in LiteLLM format (e.g., 'openai/gpt-4o', 'anthropic/claude-3-sonnet')",
help="Model name in LLM format (e.g., 'openai/gpt-4o', 'anthropic/claude-3-sonnet')",
)
agent_group.add_argument(
"--agent-max-tokens",
+2 -2
View File
@@ -20,7 +20,7 @@ if TYPE_CHECKING:
from mcp.server import FastMCP
# Suppress litellm deprecation warnings about event loop
# Suppress llm deprecation warnings about event loop
warnings.filterwarnings(
"ignore", message="There is no current event loop", category=DeprecationWarning
)
@@ -146,7 +146,7 @@ class HanzoMCPServer:
project_paths: list of project paths to generate prompts for
project_dir: single project directory (added to allowed_paths and project_paths)
mcp_instance: Optional FastMCP instance for testing
agent_model: Optional model name for agent tool in LiteLLM format
agent_model: Optional model name for agent tool in LLM format
agent_max_tokens: Optional maximum tokens for agent responses
agent_api_key: Optional API key for the LLM provider
agent_base_url: Optional base URL for the LLM provider API endpoint
+1 -1
View File
@@ -46,7 +46,7 @@ def register_all_tools(
Args:
mcp_server: The FastMCP server instance
permission_manager: Permission manager for access control
agent_model: Optional model name for agent tool in LiteLLM format
agent_model: Optional model name for agent tool in LLM format
agent_max_tokens: Optional maximum tokens for agent responses
agent_api_key: Optional API key for the LLM provider
agent_base_url: Optional base URL for the LLM provider API endpoint
@@ -11,7 +11,7 @@ from hanzo_mcp.tools.common.mode import (
# Essential system tools that are ALWAYS enabled regardless of mode
# These are core infrastructure tools that should never be disabled
# NOTE: llm and consensus are NOT essential (heavy litellm dependency)
# NOTE: llm and consensus are NOT essential (heavy llm dependency)
ESSENTIAL_SYSTEM_TOOLS: Set[str] = {
# Reasoning (lightweight)
"think", # Structured thinking
@@ -30,10 +30,10 @@ ESSENTIAL_SYSTEM_TOOLS: Set[str] = {
}
# Heavy tools that are disabled by default (opt-in)
# These require large dependencies like litellm
# These require large dependencies like llm
HEAVY_TOOLS: Set[str] = {
"llm", # Requires litellm (~100MB+ deps)
"consensus", # Requires litellm
"llm", # Requires llm (~100MB+ deps)
"consensus", # Requires llm
}
@@ -99,7 +99,7 @@ class PersonalityRegistry:
# Essential tools that are always available in every mode
# NOTE: llm/consensus are NOT essential (heavy litellm dependency - opt-in only)
# NOTE: llm/consensus are NOT essential (heavy llm dependency - opt-in only)
ESSENTIAL_TOOLS = [
# File operations
"read",
@@ -124,11 +124,11 @@ ESSENTIAL_TOOLS = [
"tool",
]
# Heavy tools (require large dependencies like litellm)
# Heavy tools (require large dependencies like llm)
# These are opt-in only, not included by default
HEAVY_TOOLS = [
"llm", # Requires litellm (~100MB deps)
"consensus", # Requires litellm
"llm", # Requires llm (~100MB deps)
"consensus", # Requires llm
]
# Common tool sets for reuse
+1 -1
View File
@@ -174,7 +174,7 @@ module = [
"bashlex.*",
"grep_ast.*",
"libtmux.*",
"litellm.*",
"llm.*",
"nbformat.*",
"ffind.*",
"fastmcp.*",
+4 -4
View File
@@ -235,12 +235,12 @@ def mock_memory_service():
@pytest.fixture
def mock_litellm():
"""Create a mock litellm module."""
def mock_llm():
"""Create a mock llm module."""
from test_utils import MockServiceHelper
with pytest.mock.patch("hanzo_tools.agent.agent_tool.litellm") as mock:
mock.completion = MockServiceHelper.mock_litellm_completion()
with pytest.mock.patch("hanzo_tools.agent.agent_tool.llm") as mock:
mock.completion = MockServiceHelper.mock_llm_completion()
yield mock
@@ -1,4 +1,4 @@
"""E2E integration tests for LiteLLM providers.
"""E2E integration tests for LLM providers.
These tests require real API keys and make actual API calls.
Only run in CI with proper secrets configured.
@@ -6,7 +6,7 @@ Only run in CI with proper secrets configured.
import os
import litellm
import llm
import pytest
@@ -15,12 +15,12 @@ import pytest
reason="OPENAI_API_KEY environment variable not set",
)
@pytest.mark.asyncio
async def test_litellm_openai_provider_integration():
"""Integration test: LiteLLM with real OpenAI provider."""
async def test_llm_openai_provider_integration():
"""Integration test: LLM with real OpenAI provider."""
messages = [{"role": "user", "content": "Hello, how are you?"}]
try:
response = litellm.completion(
response = llm.completion(
model="openai/gpt-3.5-turbo",
messages=messages,
)
@@ -34,12 +34,12 @@ async def test_litellm_openai_provider_integration():
reason="ANTHROPIC_API_KEY environment variable not set",
)
@pytest.mark.asyncio
async def test_litellm_anthropic_provider_integration():
"""Integration test: LiteLLM with real Anthropic provider."""
async def test_llm_anthropic_provider_integration():
"""Integration test: LLM with real Anthropic provider."""
messages = [{"role": "user", "content": "Hello, how are you?"}]
try:
response = litellm.completion(
response = llm.completion(
model="anthropic/claude-3-haiku-20240307",
messages=messages,
)
@@ -153,8 +153,8 @@ class TestAgentTool:
tool_ctx.error.assert_called_once()
@pytest.mark.asyncio
async def test_call_with_litellm_error(self, tool_helper, agent_tool, mcp_context):
"""Test agent tool call when litellm raises an error."""
async def test_call_with_llm_error(self, tool_helper, agent_tool, mcp_context):
"""Test agent tool call when llm raises an error."""
# Mock the tool context
tool_ctx = MagicMock()
tool_ctx.error = AsyncMock()
@@ -1,6 +1,6 @@
"""Test LiteLLM with different providers."""
"""Test LLM with different providers."""
import litellm
import llm
import pytest
from hanzo_tools.agent.tool_adapter import convert_tools_to_openai_functions
@@ -69,8 +69,8 @@ def test_convert_echo_tool_to_openai_functions(echo_tool):
assert "parameters" in openai_functions[0]["function"]
def test_litellm_openai_provider_mocked():
"""Test LiteLLM with OpenAI provider using mocks."""
def test_llm_openai_provider_mocked():
"""Test LLM with OpenAI provider using mocks."""
from unittest.mock import MagicMock, patch
messages = [{"role": "user", "content": "Hello, how are you?"}]
@@ -86,9 +86,9 @@ def test_litellm_openai_provider_mocked():
mock_response.choices = [mock_choice]
with patch.object(
litellm, "completion", return_value=mock_response
llm, "completion", return_value=mock_response
) as mock_completion:
response = litellm.completion(
response = llm.completion(
model="openai/gpt-3.5-turbo",
messages=messages,
)
@@ -100,8 +100,8 @@ def test_litellm_openai_provider_mocked():
)
def test_litellm_anthropic_provider_mocked():
"""Test LiteLLM with Anthropic provider using mocks."""
def test_llm_anthropic_provider_mocked():
"""Test LLM with Anthropic provider using mocks."""
from unittest.mock import MagicMock, patch
messages = [{"role": "user", "content": "Hello, how are you?"}]
@@ -117,9 +117,9 @@ def test_litellm_anthropic_provider_mocked():
mock_response.choices = [mock_choice]
with patch.object(
litellm, "completion", return_value=mock_response
llm, "completion", return_value=mock_response
) as mock_completion:
response = litellm.completion(
response = llm.completion(
model="anthropic/claude-3-haiku-20240307",
messages=messages,
)
@@ -131,10 +131,10 @@ def test_litellm_anthropic_provider_mocked():
)
# Integration tests moved to tests/e2e/test_litellm_integration.py
# Integration tests moved to tests/e2e/test_llm_integration.py
# They require real API keys and only run in CI
# Only run this test if explicitly requested with pytest -xvs tests/test_agent/test_litellm_providers.py
# Only run this test if explicitly requested with pytest -xvs tests/test_agent/test_llm_providers.py
if __name__ == "__main__":
pytest.main(["-xvs", __file__])
@@ -55,8 +55,8 @@ async def test_agent_clarification_flow(test_project, mock_context):
permission_manager=permission_manager, model="test-model", max_iterations=5
)
# Mock the litellm completion to simulate agent requesting clarification
with patch("litellm.completion") as mock_completion:
# Mock the llm completion to simulate agent requesting clarification
with patch("llm.completion") as mock_completion:
# First call - agent reads file and asks for clarification
mock_completion.side_effect = [
# First iteration - read file and request clarification
@@ -187,8 +187,8 @@ async def test_agent_critic_flow(test_project, mock_context):
permission_manager=permission_manager, model="test-model", max_iterations=5
)
# Mock the litellm completion to simulate agent requesting critic review
with patch("litellm.completion") as mock_completion:
# Mock the llm completion to simulate agent requesting critic review
with patch("llm.completion") as mock_completion:
mock_completion.side_effect = [
# First iteration - read file
Mock(
@@ -307,7 +307,7 @@ async def test_clarification_limits(test_project, mock_context):
)
# Test that only one clarification is allowed
with patch("litellm.completion") as mock_completion:
with patch("llm.completion") as mock_completion:
mock_completion.side_effect = [
# First clarification request
Mock(
@@ -388,7 +388,7 @@ async def test_critic_limits(test_project, mock_context):
)
# Test that only two critic reviews are allowed
with patch("litellm.completion") as mock_completion:
with patch("llm.completion") as mock_completion:
review_args = json.dumps(
{
"review_type": "GENERAL",
@@ -46,8 +46,8 @@ async def test_agent_uses_all_tools(test_project, mock_context):
max_iterations=10, # Allow more iterations for all tools
)
# Mock the litellm completion to simulate agent using all tools
with patch("litellm.completion") as mock_completion:
# Mock the llm completion to simulate agent using all tools
with patch("llm.completion") as mock_completion:
mock_completion.side_effect = [
# 1. Read the file
Mock(
@@ -1,5 +1,5 @@
#!/usr/bin/env python
"""Test that litellm deprecation warnings are properly suppressed."""
"""Test that llm deprecation warnings are properly suppressed."""
import subprocess
import sys
@@ -31,7 +31,7 @@ def test_no_pydantic_warnings():
def test_agent_tool_no_warnings():
"""Test that importing agent tools doesn't produce Pydantic deprecation warnings.
We specifically check for PydanticDeprecatedSince20 warnings from litellm,
We specifically check for PydanticDeprecatedSince20 warnings from llm,
not all deprecation warnings (which may come from other packages in CI).
"""
import warnings
@@ -40,7 +40,7 @@ def test_agent_tool_no_warnings():
with warnings.catch_warnings(record=True) as w:
warnings.simplefilter("always")
# Import agent tools (which imports litellm)
# Import agent tools (which imports llm)
# This import happens in the test process directly
from hanzo_tools.agent import register_tools # noqa: F401
@@ -53,7 +53,7 @@ def test_agent_tool_no_warnings():
or "PydanticDeprecatedSince20" in str(warning.category.__name__)
]
# We don't fail on pydantic warnings since they come from upstream litellm
# We don't fail on pydantic warnings since they come from upstream llm
# and we've already configured the warnings filter to suppress them
# This test just documents that we're aware of them
if pydantic_warnings:
@@ -68,4 +68,4 @@ def test_agent_tool_no_warnings():
if __name__ == "__main__":
test_no_pydantic_warnings()
test_agent_tool_no_warnings()
print("✅ All litellm warning tests passed!")
print("✅ All llm warning tests passed!")
@@ -10,7 +10,7 @@ not at module load time.
Common culprits for slow imports:
- sentence_transformers (2.5+ seconds)
- litellm (1+ second)
- llm (1+ second)
- hanzo_memory embedding services (3+ seconds)
"""
@@ -63,7 +63,7 @@ print(f"ELAPSED:{elapsed:.3f}")
assert elapsed < MAX_MODULE_IMPORT_TIME, (
f"hanzo_mcp.tools import took {elapsed:.2f}s (max: {MAX_MODULE_IMPORT_TIME}s). "
"This is too slow! Check for heavy imports at module load time. "
"Common culprits: sentence_transformers, litellm, hanzo_memory"
"Common culprits: sentence_transformers, llm, hanzo_memory"
)
def test_no_heavy_imports_at_module_level(self):
@@ -72,7 +72,7 @@ print(f"ELAPSED:{elapsed:.3f}")
import sys
# Clear any cached modules
for mod in list(sys.modules.keys()):
if any(x in mod for x in ['hanzo', 'sentence', 'litellm']):
if any(x in mod for x in ['hanzo', 'sentence', 'llm']):
del sys.modules[mod]
# Import the tools module
@@ -80,7 +80,7 @@ import hanzo_mcp.tools
# Check what got imported
heavy_modules = []
for name in ['sentence_transformers', 'litellm', 'hanzo_memory']:
for name in ['sentence_transformers', 'llm', 'hanzo_memory']:
if name in sys.modules:
heavy_modules.append(name)
+2 -2
View File
@@ -226,8 +226,8 @@ class MockServiceHelper:
return mock_service
@staticmethod
def mock_litellm_completion(response: str = "Test response"):
"""Create a mock litellm completion."""
def mock_llm_completion(response: str = "Test response"):
"""Create a mock llm completion."""
mock_response = Mock()
mock_response.choices = [Mock(message=Mock(content=response))]
return Mock(return_value=mock_response)
@@ -7,7 +7,7 @@ existing tools without requiring networked services:
- Linear MCP server registration via mcp_add + visibility in mcp_stats
- Per-task git worktree creation via bash
Network calls are not performed. LLM calls are mocked via litellm patching.
Network calls are not performed. LLM calls are mocked via llm patching.
"""
from __future__ import annotations
@@ -41,7 +41,7 @@ from test_utils import PermissionManager
def _mock_acompletion_factory(responses: Dict[str, str]):
"""Create a litellm.acompletion mock that returns responses by keyword.
"""Create a llm.acompletion mock that returns responses by keyword.
The first matching keyword in the prompt determines the returned text.
Defaults to a simple echo if nothing matches.
@@ -77,7 +77,7 @@ def _mock_acompletion_factory(responses: Dict[str, str]):
async def test_architecture_and_task_gen_with_consensus_mock(
monkeypatch, tmp_path: Path
):
"""Architecture → tasks flow using LLMTool with litellm mocked.
"""Architecture → tasks flow using LLMTool with llm mocked.
- Generates an architecture.md file
- Generates JSON tasks from the architecture
+2 -2
View File
@@ -42,9 +42,9 @@ class Settings(BaseSettings):
Path("data/lancedb"), description="Path to LanceDB data directory"
)
# LLM Settings (LiteLLM compatible)
# LLM Settings (LLM compatible)
llm_model: str = Field(
"gpt-4o-mini", description="LLM model to use (LiteLLM format)"
"gpt-4o-mini", description="LLM model to use (LLM format)"
)
llm_api_base: str | None = Field(None, description="API base URL for local models")
llm_api_key: str | None = Field(None, description="API key for LLM provider")
@@ -1,4 +1,4 @@
"""LLM service for AI operations using LiteLLM."""
"""LLM service for AI operations using LLM."""
import json
from typing import Any
@@ -9,22 +9,22 @@ from ..config import settings
logger = get_logger()
# Try to import LiteLLM, but make it optional
# Try to import LLM, but make it optional
try:
import litellm
import llm
LITELLM_AVAILABLE = True
# Configure LiteLLM
litellm.drop_params = True # Drop unsupported params instead of failing
litellm.set_verbose = False # Disable verbose logging
LLM_AVAILABLE = True
# Configure LLM
llm.drop_params = True # Drop unsupported params instead of failing
llm.set_verbose = False # Disable verbose logging
except ImportError:
LITELLM_AVAILABLE = False
litellm = None
logger.warning("LiteLLM not available, LLM features will be limited")
LLM_AVAILABLE = False
llm = None
logger.warning("LLM not available, LLM features will be limited")
class LLMService:
"""Service for LLM operations using LiteLLM."""
"""Service for LLM operations using LLM."""
def __init__(self) -> None:
"""Initialize LLM service."""
@@ -33,15 +33,15 @@ class LLMService:
self.temperature = settings.llm_temperature
self.max_tokens = settings.llm_max_tokens
# Set up API keys if LiteLLM is available
if LITELLM_AVAILABLE:
# Set up API keys if LLM is available
if LLM_AVAILABLE:
# Set up API keys
if settings.llm_api_key:
litellm.api_key = settings.llm_api_key
llm.api_key = settings.llm_api_key
if settings.openai_api_key:
litellm.openai_key = settings.openai_api_key
llm.openai_key = settings.openai_api_key
if settings.anthropic_api_key:
litellm.anthropic_key = settings.anthropic_api_key
llm.anthropic_key = settings.anthropic_api_key
def complete(
self,
@@ -52,7 +52,7 @@ class LLMService:
response_format: str | None = None,
) -> str:
"""
Complete a prompt using LiteLLM.
Complete a prompt using LLM.
Args:
prompt: The prompt to complete
@@ -64,8 +64,8 @@ class LLMService:
Returns:
Generated text
"""
if not LITELLM_AVAILABLE:
logger.warning("LiteLLM not available, returning empty response")
if not LLM_AVAILABLE:
logger.warning("LLM not available, returning empty response")
return ""
model = model or self.default_model
@@ -88,7 +88,7 @@ class LLMService:
if response_format == "json":
kwargs["response_format"] = {"type": "json_object"}
response = litellm.completion(**kwargs)
response = llm.completion(**kwargs)
return response.choices[0].message.content or ""
except Exception as e:
logger.error(f"LLM completion error: {e}")
@@ -103,7 +103,7 @@ class LLMService:
response_format: str | None = None,
) -> str:
"""
Chat with the LLM using LiteLLM.
Chat with the LLM using LLM.
Args:
messages: List of message dicts with "role" and "content"
@@ -115,8 +115,8 @@ class LLMService:
Returns:
Generated response
"""
if not LITELLM_AVAILABLE:
logger.warning("LiteLLM not available, returning empty response")
if not LLM_AVAILABLE:
logger.warning("LLM not available, returning empty response")
return ""
model = model or self.default_model
@@ -139,7 +139,7 @@ class LLMService:
if response_format == "json":
kwargs["response_format"] = {"type": "json_object"}
response = litellm.completion(**kwargs)
response = llm.completion(**kwargs)
return response.choices[0].message.content or ""
except Exception as e:
logger.error(f"LLM chat error: {e}")
@@ -188,7 +188,7 @@ Content: {content}"""
summary = self.complete(summary_prompt, temperature=0.3)
# Generate knowledge instructions
if LITELLM_AVAILABLE:
if LLM_AVAILABLE:
instruction_prompt = f"""Based on this summary, generate instructions for updating a knowledge base.
Return a JSON object with the following structure:
{{
+3 -3
View File
@@ -57,9 +57,9 @@ def mock_embedding_model():
@pytest.fixture(autouse=True)
def mock_litellm_completion():
"""Mock LiteLLM completion to avoid API calls during tests."""
with patch("litellm.completion") as mock_completion:
def mock_llm_completion():
"""Mock LLM completion to avoid API calls during tests."""
with patch("llm.completion") as mock_completion:
# Create mock response
mock_response = MagicMock()
mock_response.choices = [
@@ -139,7 +139,7 @@ class TestAdditionalCoverage:
from hanzo_memory.services.llm import LLMService
with patch("hanzo_memory.services.llm.settings") as mock_settings:
with patch("hanzo_memory.services.llm.litellm") as mock_litellm:
with patch("hanzo_memory.services.llm.llm") as mock_llm:
# Test with llm_api_key
mock_settings.llm_model = "test"
mock_settings.llm_api_base = None
@@ -150,21 +150,21 @@ class TestAdditionalCoverage:
mock_settings.anthropic_api_key = None
LLMService()
assert mock_litellm.api_key == "test-llm-key"
assert mock_llm.api_key == "test-llm-key"
# Test with openai_api_key
mock_settings.llm_api_key = None
mock_settings.openai_api_key = "test-openai-key"
LLMService()
assert mock_litellm.openai_key == "test-openai-key"
assert mock_llm.openai_key == "test-openai-key"
# Test with anthropic_api_key
mock_settings.openai_api_key = None
mock_settings.anthropic_api_key = "test-anthropic-key"
LLMService()
assert mock_litellm.anthropic_key == "test-anthropic-key"
assert mock_llm.anthropic_key == "test-anthropic-key"
def test_embedding_service_empty_list(self):
"""Test embedding service with empty list."""
+2 -2
View File
@@ -11,7 +11,7 @@ class TestLLMService:
"""Test LLM service."""
@pytest.fixture
def llm_service(self, mock_litellm_completion):
def llm_service(self, mock_llm_completion):
"""Create LLM service."""
# Configure mock to return appropriate responses
@@ -36,7 +36,7 @@ class TestLLMService:
mock_response.choices = [mock_choice]
return mock_response
mock_litellm_completion.side_effect = side_effect
mock_llm_completion.side_effect = side_effect
return LLMService()
def test_complete(self, llm_service):
+1 -1
View File
@@ -319,7 +319,7 @@ class HanzoDevBackend(Backend):
class EmbeddedBackend(Backend):
"""Embedded LLM backend using litellm."""
"""Embedded LLM backend using llm."""
def __init__(self, llm_client):
self.llm_client = llm_client
+1 -1
View File
@@ -3,7 +3,7 @@
import os
from typing import Any, Dict, List, Optional, Set
from litellm import acompletion, completion
from llm import acompletion, completion
class LLMClient:
@@ -10,7 +10,7 @@ import time
import uuid
import asyncio
# Import litellm with warnings suppressed
# Import llm with warnings suppressed
import warnings
from typing import (
Any,
@@ -114,7 +114,7 @@ def get_default_model(model_override: str | None = None) -> str:
"""Get the default model for agent execution.
Args:
model_override: Optional model override string in LiteLLM format (e.g., "openai/gpt-4o")
model_override: Optional model override string in LLM format (e.g., "openai/gpt-4o")
Returns:
Model identifier string with provider prefix
@@ -5,7 +5,7 @@ formats, making MCP tools available to the OpenAI API, and processing tool input
and outputs for agent execution.
"""
# Import litellm with warnings suppressed
# Import llm with warnings suppressed
import warnings
from openai.types import FunctionParameters
@@ -71,12 +71,12 @@ def supports_parallel_function_calling(model: str) -> bool:
"""Check if a model supports parallel function calling.
Args:
model: Model identifier in LiteLLM format (e.g., "openai/gpt-4-turbo-preview")
model: Model identifier in LLM format (e.g., "openai/gpt-4-turbo-preview")
Returns:
True if the model supports parallel function calling, False otherwise
"""
# Since litellm doesn't have this function, we'll implement a simple check
# Since llm doesn't have this function, we'll implement a simple check
# based on known models that support parallel function calling
parallel_capable_models = {
# OpenAI models that support parallel function calling
+4 -4
View File
@@ -178,10 +178,10 @@ class TestToolPackages:
reason="hanzo-tools-llm not installed",
)
def test_llm_tools(self):
"""Test hanzo-tools-llm imports (tools depend on litellm)."""
"""Test hanzo-tools-llm imports (tools depend on llm)."""
from hanzo_tools.llm import TOOLS, LLM_AVAILABLE
# LLM tools are optional, depend on litellm
# LLM tools are optional, depend on llm
if LLM_AVAILABLE:
assert len(TOOLS) >= 1
else:
@@ -214,7 +214,7 @@ REQUIRED_IMPORT_MODULES = [
("hanzo_tools.refactor", 1.0),
("hanzo_tools.jupyter", 1.0),
("hanzo_tools.browser", 1.0),
("hanzo_tools.agent", 2.0), # Agent has litellm, allow more time
("hanzo_tools.agent", 2.0), # Agent has llm, allow more time
]
# Optional packages that may not be installed
@@ -223,7 +223,7 @@ OPTIONAL_IMPORT_MODULES = [
("hanzo_tools.mcp_tools", 1.0),
("hanzo_tools.database", 1.0),
("hanzo_tools.editor", 1.0),
("hanzo_tools.llm", 2.0), # LLM has litellm, allow more time
("hanzo_tools.llm", 2.0), # LLM has llm, allow more time
("hanzo_tools.vector", 2.0), # Vector has heavy deps
]
@@ -275,14 +275,14 @@ Providers are automatically detected based on environment variables:
return f"{provider} is already disabled"
def _list_all_models(self, provider: Optional[str] = None) -> str:
"""List all available models from LiteLLM."""
"""List all available models from LLM."""
try:
from .llm_unified import LLMTool
all_models = LLMTool.get_all_models()
if not all_models:
return "No models available or LiteLLM not installed"
return "No models available or LLM not installed"
output = ["=== Available LLM Models ==="]
@@ -21,13 +21,13 @@ from mcp.server.fastmcp import Context as MCPContext
from hanzo_tools.core import BaseTool, ToolContext, auto_timeout, create_tool_context
# Check if litellm is available
# Check if llm is available
try:
import litellm
import llm
LITELLM_AVAILABLE = True
LLM_AVAILABLE = True
except ImportError:
LITELLM_AVAILABLE = False
LLM_AVAILABLE = False
# Parameter types
@@ -280,9 +280,9 @@ Available: {", ".join(available) if available else "None"}"""
# Running in test mode without MCP context
pass
if not LITELLM_AVAILABLE:
if not LLM_AVAILABLE:
return (
"Error: LiteLLM is not installed. Install it with: pip install litellm"
"Error: LLM is not installed. Install it with: pip install llm"
)
# Extract action
@@ -368,13 +368,13 @@ Available: {", ".join(available) if available else "None"}"""
if kwargs.get("stream"):
# Handle streaming response
response_text = ""
async for chunk in await litellm.acompletion(**kwargs):
async for chunk in await llm.acompletion(**kwargs):
if chunk.choices[0].delta.content:
response_text += chunk.choices[0].delta.content
return response_text
else:
# Regular response
response = await litellm.acompletion(**kwargs)
response = await llm.acompletion(**kwargs)
return response.choices[0].message.content
except Exception as e:
@@ -517,7 +517,7 @@ Provide your critical analysis:"""
all_models = self._get_all_models()
if not all_models:
return "No models available or LiteLLM not properly initialized"
return "No models available or LLM not properly initialized"
output = ["=== Available LLM Models ==="]
@@ -623,7 +623,7 @@ Provide your critical analysis:"""
if tool_ctx:
await tool_ctx.info(f"Testing {model}...")
response = await litellm.acompletion(
response = await llm.acompletion(
model=model,
messages=[{"role": "user", "content": test_prompt}],
temperature=0,
@@ -739,7 +739,7 @@ Provide your critical analysis:"""
if max_tokens:
kwargs["max_tokens"] = max_tokens
response = await litellm.acompletion(**kwargs)
response = await llm.acompletion(**kwargs)
return {
"success": True,
@@ -869,12 +869,12 @@ Be concise and highlight the most important findings."""
return "openai"
def _get_all_models(self) -> Dict[str, List[str]]:
"""Get all available models from LiteLLM."""
"""Get all available models from LLM."""
try:
import litellm
import llm
# Get all models
all_models = litellm.model_list
all_models = llm.model_list
# Organize by provider
providers = {}
+7 -7
View File
@@ -135,8 +135,8 @@ async def ask_once(
try:
from pkg.hanzoai import completion
except ImportError:
# Fallback to using litellm directly
import litellm
# Fallback to using llm directly
import llm
def completion(**kwargs):
import os
@@ -145,7 +145,7 @@ async def ask_once(
if api_key:
kwargs["api_key"] = api_key
kwargs["api_base"] = "https://api.hanzo.ai/v1"
return litellm.completion(**kwargs)
return llm.completion(**kwargs)
result = completion(
model=f"anthropic/{model}" if "claude" in model else model,
@@ -154,7 +154,7 @@ async def ask_once(
content = result.choices[0].message.content
except ImportError as e:
console.print(f"[red]Error:[/red] Missing dependencies: {e}")
console.print("Install with: pip install litellm")
console.print("Install with: pip install llm")
return
# Display response
@@ -219,8 +219,8 @@ async def interactive_chat(ctx, model: str, local: bool, system: Optional[str]):
try:
from pkg.hanzoai import completion
except ImportError:
# Fallback to using litellm directly
import litellm
# Fallback to using llm directly
import llm
def completion(**kwargs):
import os
@@ -229,7 +229,7 @@ async def interactive_chat(ctx, model: str, local: bool, system: Optional[str]):
if api_key:
kwargs["api_key"] = api_key
kwargs["api_base"] = "https://api.hanzo.ai/v1"
return litellm.completion(**kwargs)
return llm.completion(**kwargs)
result = completion(
model=f"anthropic/{model}" if "claude" in model else model,
+1 -1
View File
@@ -107,7 +107,7 @@ TOOLS = {
# Go tools (go install)
"router": {
"name": "Hanzo Router",
"description": "LLM Gateway/Router (LiteLLM proxy)",
"description": "LLM Gateway/Router (LLM proxy)",
"source": "docker",
"image": "ghcr.io/hanzoai/llm:latest",
"binary": None,
+2 -2
View File
@@ -33,7 +33,7 @@ def start_router(ctx, port: int, config: Optional[str], detach: bool):
router_dir = None
for path in router_paths:
if path.exists() and (path / "litellm" / "proxy" / "proxy_server.py").exists():
if path.exists() and (path / "llm" / "proxy" / "proxy_server.py").exists():
router_dir = path
break
@@ -55,7 +55,7 @@ def start_router(ctx, port: int, config: Optional[str], detach: bool):
cmd = [
sys.executable,
"-m",
"litellm.proxy.proxy_server",
"llm.proxy.proxy_server",
"--port",
str(port),
]
+23 -23
View File
@@ -8,14 +8,14 @@ complementing the full-featured auto-generated client.
import os
from typing import Optional
# We can import and use litellm as a dependency
# We can import and use llm as a dependency
try:
import litellm
import llm
LITELLM_AVAILABLE = True
LLM_AVAILABLE = True
except ImportError:
LITELLM_AVAILABLE = False
litellm = None
LLM_AVAILABLE = False
llm = None
from ._client import Hanzo
@@ -29,7 +29,7 @@ HANZO_API_KEY = os.getenv("HANZO_API_KEY")
class SimpleLLMClient:
"""
Simple LLM client for Hanzo AI with litellm compatibility.
Simple LLM client for Hanzo AI with llm compatibility.
This provides a simpler interface that's compatible with the
patterns from the llm/hanzoai package, while using the
@@ -72,12 +72,12 @@ class SimpleLLMClient:
# Create the underlying Hanzo client
self._client = Hanzo(api_key=self.api_key, base_url=self.api_base)
# Configure litellm if available
if LITELLM_AVAILABLE and litellm:
litellm.api_base = self.api_base
litellm.drop_params = True # Be permissive with params
# Configure llm if available
if LLM_AVAILABLE and llm:
llm.api_base = self.api_base
llm.drop_params = True # Be permissive with params
if self.api_key:
litellm.api_key = self.api_key
llm.api_key = self.api_key
os.environ["OPENAI_API_KEY"] = self.api_key # For OpenAI compatibility
def _iam_login(self):
@@ -129,38 +129,38 @@ class SimpleLLMClient:
"""
Create a completion using Hanzo AI.
If litellm is available, uses litellm for compatibility.
If llm is available, uses llm for compatibility.
Otherwise uses the native client.
"""
if LITELLM_AVAILABLE and litellm:
# Use litellm for maximum compatibility
if LLM_AVAILABLE and llm:
# Use llm for maximum compatibility
kwargs.setdefault("api_base", self.api_base)
if self.iam_token:
kwargs.setdefault("api_key", f"Bearer iam_{self.iam_token}")
elif self.api_key:
kwargs.setdefault("api_key", self.api_key)
return litellm.completion(**kwargs)
return llm.completion(**kwargs)
else:
# Use native client
return self._client.chat.completions.create(**kwargs)
def embedding(self, **kwargs):
"""Create embeddings using Hanzo AI."""
if LITELLM_AVAILABLE and litellm:
if LLM_AVAILABLE and llm:
kwargs.setdefault("api_base", self.api_base)
if self.api_key:
kwargs.setdefault("api_key", self.api_key)
return litellm.embedding(**kwargs)
return llm.embedding(**kwargs)
else:
return self._client.embeddings.create(**kwargs)
def image_generation(self, **kwargs):
"""Generate images using Hanzo AI."""
if LITELLM_AVAILABLE and litellm:
if LLM_AVAILABLE and llm:
kwargs.setdefault("api_base", self.api_base)
if self.api_key:
kwargs.setdefault("api_key", self.api_key)
return litellm.image_generation(**kwargs)
return llm.image_generation(**kwargs)
else:
return self._client.images.generations.create(**kwargs)
@@ -218,8 +218,8 @@ def completion(**kwargs):
if HANZO_API_KEY:
kwargs.setdefault("api_key", HANZO_API_KEY)
if LITELLM_AVAILABLE and litellm:
return litellm.completion(**kwargs)
if LLM_AVAILABLE and llm:
return llm.completion(**kwargs)
else:
client = SimpleLLMClient()
return client.completion(**kwargs)
@@ -230,8 +230,8 @@ def set_api_key(api_key: str):
global HANZO_API_KEY
HANZO_API_KEY = api_key
os.environ["HANZO_API_KEY"] = api_key
if LITELLM_AVAILABLE and litellm:
litellm.api_key = api_key
if LLM_AVAILABLE and llm:
llm.api_key = api_key
os.environ["OPENAI_API_KEY"] = api_key
+1 -1
View File
@@ -34,7 +34,7 @@ classifiers = [
]
[project.optional-dependencies]
litellm = ["hanzo-llm>=1.0.0"]
llm = ["hanzo-llm>=1.0.0"]
[project.urls]
Homepage = "https://github.com/hanzoai/python-sdk"
+1 -1
View File
@@ -150,7 +150,7 @@ Modular tool package system with entry-point based discovery:
### Phase 3: Migration from existing memory system
- [ ] Import important memories from hanzo-memory package
- [ ] Provide migration tools for existing vector data
- [ ] Deprecate complex LiteLLM/InfinityDB system
- [ ] Deprecate complex LLM/InfinityDB system
### Phase 4: Full optimization
- [ ] Keep SQLite for structured data (file metadata, symbols, graph)