Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions claudecode/claude_api_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

from claudecode.constants import (
DEFAULT_CLAUDE_MODEL, DEFAULT_TIMEOUT_SECONDS, DEFAULT_MAX_RETRIES,
RATE_LIMIT_BACKOFF_MAX, PROMPT_TOKEN_LIMIT,
RATE_LIMIT_BACKOFF_MAX, PROMPT_TOKEN_LIMIT, VALIDATION_MODEL,
)
from claudecode.json_parser import parse_json_with_fallbacks
from claudecode.logger import get_logger
Expand Down Expand Up @@ -59,7 +59,7 @@ def validate_api_access(self) -> Tuple[bool, str]:
try:
# Simple test call to verify API access
self.client.messages.create(
model="claude-3-5-haiku-20241022",
model=VALIDATION_MODEL,
max_tokens=10,
messages=[{"role": "user", "content": "Hello"}],
timeout=10
Expand Down
1 change: 1 addition & 0 deletions claudecode/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

# API Configuration
DEFAULT_CLAUDE_MODEL = os.environ.get('CLAUDE_MODEL') or 'claude-opus-4-1-20250805'
VALIDATION_MODEL = 'claude-haiku-4-5'
DEFAULT_TIMEOUT_SECONDS = 180 # 3 minutes
DEFAULT_MAX_RETRIES = 3
RATE_LIMIT_BACKOFF_MAX = 30 # Maximum backoff time for rate limits
Expand Down
44 changes: 44 additions & 0 deletions claudecode/test_claude_api_client.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#!/usr/bin/env python3
"""
Pytest tests for Claude API client.
"""

from unittest.mock import patch, MagicMock

from claudecode.claude_api_client import ClaudeAPIClient
from claudecode.constants import VALIDATION_MODEL


class TestValidateApiAccess:
"""Test validate_api_access uses VALIDATION_MODEL for cheap validation pings."""

def test_validate_api_access_uses_validation_model(self):
"""Ensure validate_api_access uses VALIDATION_MODEL, not self.model."""
custom_model = "claude-sonnet-4-20250514"
client = ClaudeAPIClient(model=custom_model, api_key="fake-key")

mock_response = MagicMock()
with patch.object(client.client.messages, "create", return_value=mock_response) as mock_create:
success, error = client.validate_api_access()

mock_create.assert_called_once()
call_kwargs = mock_create.call_args[1]
assert call_kwargs["model"] == VALIDATION_MODEL, (
f"Expected VALIDATION_MODEL '{VALIDATION_MODEL}', got '{call_kwargs['model']}'"
)
assert success is True
assert error == ""

def test_validate_api_access_does_not_use_instance_model(self):
"""Ensure validate_api_access does not pass self.model (expensive) to the validation call."""
expensive_model = "claude-opus-4-1-20250805"
client = ClaudeAPIClient(model=expensive_model, api_key="fake-key")

mock_response = MagicMock()
with patch.object(client.client.messages, "create", return_value=mock_response) as mock_create:
success, _ = client.validate_api_access()

call_kwargs = mock_create.call_args[1]
assert call_kwargs["model"] == VALIDATION_MODEL
assert call_kwargs["model"] != expensive_model
assert success is True