4a19d70af1
Lint with Ruff / ruff (push) Has been cancelled
MCP Server Tests / live-mcp-tests (push) Has been cancelled
Tests / unit-tests (push) Has been cancelled
Tests / database-integration-tests (push) Has been cancelled
CodeQL Advanced / Analyze (actions) (push) Has been cancelled
CodeQL Advanced / Analyze (python) (push) Has been cancelled
Server Tests / live-server-tests (push) Has been cancelled
Pyright Type Check / pyright (push) Has been cancelled
155 lines
5.1 KiB
Python
155 lines
5.1 KiB
Python
#!/usr/bin/env python3
|
|
"""Unit tests for service factory provider detection and client routing."""
|
|
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
# Add the src directory to the path (mirrors the other factory tests)
|
|
sys.path.insert(0, str(Path(__file__).parent.parent / 'src'))
|
|
|
|
from graphiti_core.llm_client import OpenAIClient
|
|
from graphiti_core.llm_client.azure_openai_client import AzureOpenAILLMClient
|
|
from graphiti_core.llm_client.openai_generic_client import OpenAIGenericClient
|
|
|
|
from config.schema import (
|
|
AzureOpenAIProviderConfig,
|
|
LLMConfig,
|
|
LLMProvidersConfig,
|
|
OpenAIProviderConfig,
|
|
)
|
|
from services.factories import (
|
|
LLMClientFactory,
|
|
is_non_openai_provider,
|
|
reasoning_effort_for_model,
|
|
)
|
|
|
|
|
|
class TestIsNonOpenAIProvider:
|
|
"""Tests for the base_url-based provider detection."""
|
|
|
|
@pytest.mark.parametrize(
|
|
'base_url',
|
|
[
|
|
None,
|
|
'',
|
|
'https://api.openai.com/v1',
|
|
'https://api.openai.com',
|
|
'https://my-resource.openai.azure.com',
|
|
],
|
|
)
|
|
def test_official_or_unset_is_openai(self, base_url):
|
|
"""Unset, empty, or official OpenAI/Azure endpoints are treated as OpenAI."""
|
|
assert is_non_openai_provider(base_url) is False
|
|
|
|
@pytest.mark.parametrize(
|
|
'base_url',
|
|
[
|
|
'http://localhost:11434/v1', # Ollama
|
|
'http://localhost:1234/v1', # LM Studio
|
|
'http://localhost:8000/v1', # vLLM
|
|
'https://my-proxy.internal/v1',
|
|
],
|
|
)
|
|
def test_compatible_providers_are_non_openai(self, base_url):
|
|
"""OpenAI-compatible third-party endpoints are detected as non-OpenAI."""
|
|
assert is_non_openai_provider(base_url) is True
|
|
|
|
|
|
class TestLLMClientFactoryRouting:
|
|
"""Tests that the factory selects the right client based on base_url."""
|
|
|
|
@staticmethod
|
|
def _config(api_url: str) -> LLMConfig:
|
|
return LLMConfig(
|
|
provider='openai',
|
|
model='gpt-5.5',
|
|
providers=LLMProvidersConfig(
|
|
openai=OpenAIProviderConfig(api_key='test-key', api_url=api_url)
|
|
),
|
|
)
|
|
|
|
def test_official_openai_uses_openai_client(self):
|
|
client = LLMClientFactory.create(self._config('https://api.openai.com/v1'))
|
|
assert isinstance(client, OpenAIClient)
|
|
assert not isinstance(client, OpenAIGenericClient)
|
|
|
|
def test_ollama_uses_generic_client(self):
|
|
client = LLMClientFactory.create(self._config('http://localhost:11434/v1'))
|
|
assert isinstance(client, OpenAIGenericClient)
|
|
|
|
|
|
class TestLLMClientReasoningEffort:
|
|
"""The OpenAI factory selects reasoning effort by model family."""
|
|
|
|
@staticmethod
|
|
def _config(model: str) -> LLMConfig:
|
|
return LLMConfig(
|
|
provider='openai',
|
|
model=model,
|
|
providers=LLMProvidersConfig(
|
|
openai=OpenAIProviderConfig(api_key='test-key', api_url='https://api.openai.com/v1')
|
|
),
|
|
)
|
|
|
|
def test_gpt_5_5_uses_reasoning_none(self):
|
|
"""gpt-5.5 (the default) runs with reasoning off."""
|
|
client = LLMClientFactory.create(self._config('gpt-5.5'))
|
|
assert isinstance(client, OpenAIClient)
|
|
assert client.reasoning == 'none'
|
|
|
|
def test_earlier_reasoning_model_uses_minimal(self):
|
|
"""Earlier gpt-5 reasoning models keep the historical 'minimal' floor."""
|
|
client = LLMClientFactory.create(self._config('gpt-5'))
|
|
assert isinstance(client, OpenAIClient)
|
|
assert client.reasoning == 'minimal'
|
|
|
|
|
|
class TestReasoningEffortForModel:
|
|
"""The shared effort selector used by both the OpenAI and Azure branches."""
|
|
|
|
@pytest.mark.parametrize(
|
|
('model', 'expected'),
|
|
[
|
|
('gpt-5.5', 'none'),
|
|
('gpt-5.5-2026-04-23', 'none'),
|
|
('gpt-5', 'minimal'),
|
|
('gpt-5-mini', 'minimal'),
|
|
('gpt-5.4-mini', 'minimal'),
|
|
('o1', 'minimal'),
|
|
('o3-mini', 'minimal'),
|
|
('gpt-4.1', None),
|
|
('gpt-4o-mini', None),
|
|
],
|
|
)
|
|
def test_effort_selection(self, model, expected):
|
|
assert reasoning_effort_for_model(model) == expected
|
|
|
|
|
|
class TestAzureReasoningEffort:
|
|
"""The Azure OpenAI branch applies the same model-tied reasoning effort."""
|
|
|
|
@staticmethod
|
|
def _config(model: str) -> LLMConfig:
|
|
return LLMConfig(
|
|
provider='azure_openai',
|
|
model=model,
|
|
providers=LLMProvidersConfig(
|
|
azure_openai=AzureOpenAIProviderConfig(
|
|
api_key='test-key',
|
|
api_url='https://example.openai.azure.com',
|
|
)
|
|
),
|
|
)
|
|
|
|
def test_azure_gpt_5_5_uses_reasoning_none(self):
|
|
client = LLMClientFactory.create(self._config('gpt-5.5'))
|
|
assert isinstance(client, AzureOpenAILLMClient)
|
|
assert client.reasoning == 'none'
|
|
|
|
def test_azure_non_reasoning_model_sends_no_effort(self):
|
|
client = LLMClientFactory.create(self._config('gpt-4.1'))
|
|
assert isinstance(client, AzureOpenAILLMClient)
|
|
assert client.reasoning is None
|