199 lines
6.8 KiB
Python
199 lines
6.8 KiB
Python
from unittest.mock import AsyncMock, MagicMock, patch
|
|
|
|
import pytest
|
|
|
|
from src.utils.model_client import (
|
|
DEFAULT_MAX_TOKENS,
|
|
DEFAULT_MODELS,
|
|
DEFAULT_TEMPERATURE,
|
|
ModelClient,
|
|
ModelProvider,
|
|
)
|
|
|
|
|
|
@pytest.fixture
|
|
def mock_env(monkeypatch):
|
|
monkeypatch.setenv("ANTHROPIC_API_KEY", "mock-anthropic-api-key")
|
|
monkeypatch.setenv("OPENAI_COMPATIBLE_API_KEY", "mock-openai-api-key")
|
|
monkeypatch.setenv("OPENAI_API_KEY", "mock-openai-api-key")
|
|
monkeypatch.setenv("GROQ_API_KEY", "mock-groq-api-key")
|
|
monkeypatch.setenv("CEREBRAS_API_KEY", "mock-cerebras-api-key")
|
|
monkeypatch.setenv("OPENROUTER_API_KEY", "mock-openrouter-api-key")
|
|
|
|
|
|
# Test fixtures
|
|
@pytest.fixture
|
|
def mock_anthropic_response():
|
|
mock_response = MagicMock()
|
|
mock_content = MagicMock()
|
|
mock_content.type = "text"
|
|
mock_content.text = "Test response"
|
|
mock_response.content = [mock_content]
|
|
return mock_response
|
|
|
|
|
|
@pytest.fixture
|
|
def mock_openai_response():
|
|
mock_response = MagicMock()
|
|
mock_message = MagicMock()
|
|
mock_message.content = "Test response"
|
|
mock_choice = MagicMock()
|
|
mock_choice.message = mock_message
|
|
mock_response.choices = [mock_choice]
|
|
return mock_response
|
|
|
|
|
|
@pytest.fixture
|
|
def mock_anthropic_client():
|
|
with patch("src.utils.model_client.AsyncAnthropic") as mock:
|
|
client = MagicMock()
|
|
client.messages.create = AsyncMock()
|
|
client.messages.stream = AsyncMock()
|
|
mock.return_value = client
|
|
yield client
|
|
|
|
|
|
@pytest.fixture
|
|
def mock_openai_client():
|
|
with patch("src.utils.model_client.AsyncOpenAI") as mock:
|
|
client = MagicMock()
|
|
client.chat.completions.create = AsyncMock()
|
|
mock.return_value = client
|
|
yield client
|
|
|
|
|
|
# Initialization Tests
|
|
def test_default_initialization(mock_env):
|
|
"""Test default initialization with Anthropic provider."""
|
|
client = ModelClient()
|
|
assert client.provider == ModelProvider.ANTHROPIC
|
|
assert client.model == DEFAULT_MODELS[ModelProvider.ANTHROPIC]
|
|
assert client.base_url is None
|
|
|
|
|
|
def test_custom_model_initialization():
|
|
"""Test initialization with custom model name."""
|
|
custom_model = "custom-model"
|
|
client = ModelClient(model=custom_model)
|
|
assert client.model == custom_model
|
|
|
|
|
|
def test_custom_api_key_initialization():
|
|
"""Test initialization with custom API key."""
|
|
custom_key = "test-api-key"
|
|
client = ModelClient(api_key=custom_key)
|
|
assert client.api_key == custom_key
|
|
|
|
|
|
def test_custom_base_url_initialization():
|
|
"""Test initialization with custom base URL."""
|
|
custom_url = "https://custom-api.example.com"
|
|
client = ModelClient(base_url=custom_url)
|
|
assert client.base_url == custom_url
|
|
|
|
|
|
def test_unsupported_provider_initialization():
|
|
"""Test initialization with unsupported provider."""
|
|
with pytest.raises(ValueError, match="is not a valid ModelProvider"):
|
|
ModelClient(provider=ModelProvider("unsupported"))
|
|
|
|
|
|
def test_missing_api_key_initialization():
|
|
"""Test initialization without required API key."""
|
|
with (
|
|
patch("src.utils.model_client.settings") as mock_settings,
|
|
pytest.raises(ValueError, match="API key is required"),
|
|
):
|
|
# Mock the settings to return None for all API keys
|
|
mock_settings.LLM.ANTHROPIC_API_KEY = None
|
|
mock_settings.LLM.OPENAI_API_KEY = None
|
|
mock_settings.LLM.OPENAI_COMPATIBLE_API_KEY = None
|
|
mock_settings.LLM.GROQ_API_KEY = None
|
|
mock_settings.LLM.GEMINI_API_KEY = None
|
|
ModelClient()
|
|
|
|
|
|
# Message Creation Tests
|
|
def test_create_message():
|
|
"""Test message creation with different roles."""
|
|
client = ModelClient()
|
|
message = client.create_message("user", "Hello")
|
|
assert message == {"role": "user", "content": "Hello"}
|
|
|
|
|
|
# Generation Tests
|
|
@pytest.mark.asyncio
|
|
async def test_generate_anthropic(mock_anthropic_client, mock_anthropic_response):
|
|
"""Test generation with Anthropic provider."""
|
|
mock_anthropic_client.messages.create.return_value = mock_anthropic_response
|
|
|
|
client = ModelClient(provider=ModelProvider.ANTHROPIC)
|
|
messages = [{"role": "user", "content": "Hello"}]
|
|
response = await client.generate(messages)
|
|
assert response == "Test response"
|
|
|
|
# Verify the API call
|
|
mock_anthropic_client.messages.create.assert_called_once()
|
|
call_args = mock_anthropic_client.messages.create.call_args[1]
|
|
assert call_args["model"] == DEFAULT_MODELS[ModelProvider.ANTHROPIC]
|
|
assert call_args["messages"] == messages
|
|
assert call_args["max_tokens"] == DEFAULT_MAX_TOKENS
|
|
assert call_args["temperature"] == DEFAULT_TEMPERATURE
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_generate_openai(mock_openai_client, mock_openai_response, mock_env):
|
|
"""Test generation with OpenAI provider."""
|
|
mock_openai_client.chat.completions.create.return_value = mock_openai_response
|
|
|
|
client = ModelClient(provider=ModelProvider.OPENAI)
|
|
messages = [{"role": "user", "content": "Hello"}]
|
|
response = await client.generate(messages)
|
|
assert response == "Test response"
|
|
|
|
# Verify the API call
|
|
mock_openai_client.chat.completions.create.assert_called_once()
|
|
call_args = mock_openai_client.chat.completions.create.call_args[1]
|
|
assert call_args["model"] == DEFAULT_MODELS[ModelProvider.OPENAI]
|
|
assert call_args["messages"] == messages
|
|
assert call_args["max_tokens"] == DEFAULT_MAX_TOKENS
|
|
assert call_args["temperature"] == DEFAULT_TEMPERATURE
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_generate_with_system_prompt(
|
|
mock_anthropic_client, mock_anthropic_response
|
|
):
|
|
"""Test generation with system prompt."""
|
|
mock_anthropic_client.messages.create.return_value = mock_anthropic_response
|
|
|
|
client = ModelClient(provider=ModelProvider.ANTHROPIC)
|
|
messages = [{"role": "user", "content": "Hello"}]
|
|
system = "You are a helpful assistant"
|
|
response = await client.generate(messages, system=system)
|
|
assert response == "Test response"
|
|
|
|
# Verify the API call
|
|
mock_anthropic_client.messages.create.assert_called_once()
|
|
call_args = mock_anthropic_client.messages.create.call_args[1]
|
|
assert call_args["system"] == system
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_generate_with_caching(mock_anthropic_client, mock_anthropic_response):
|
|
"""Test generation with caching enabled."""
|
|
mock_anthropic_client.messages.create.return_value = mock_anthropic_response
|
|
|
|
client = ModelClient(provider=ModelProvider.ANTHROPIC)
|
|
messages = [{"role": "user", "content": "Hello"}]
|
|
system = "You are a helpful assistant"
|
|
response = await client.generate(messages, system=system, use_caching=True)
|
|
assert response == "Test response"
|
|
|
|
# Verify the API call
|
|
mock_anthropic_client.messages.create.assert_called_once()
|
|
call_args = mock_anthropic_client.messages.create.call_args[1]
|
|
assert call_args["system"] == [
|
|
{"type": "text", "text": system, "cache_control": {"type": "ephemeral"}}
|
|
]
|