graphiti/tests/config/test_factory.py
supmo668 74a422369c feat: Add enhanced configuration system with multi-provider LLM support
This commit introduces a comprehensive configuration system that makes
Graphiti more flexible and easier to configure across different
providers and deployment environments.

## New Features

- **Unified Configuration**: New GraphitiConfig class with Pydantic validation
- **YAML Support**: Load configuration from .graphiti.yaml files
- **Multi-Provider Support**: Easy switching between OpenAI, Azure, Anthropic,
  Gemini, Groq, and LiteLLM
- **LiteLLM Integration**: Unified access to 100+ LLM providers
- **Factory Functions**: Automatic client creation from configuration
- **Full Backward Compatibility**: Existing code continues to work

## Configuration System

- graphiti_core/config/settings.py: Pydantic configuration classes
- graphiti_core/config/providers.py: Provider enumerations and defaults
- graphiti_core/config/factory.py: Factory functions for client creation

## LiteLLM Client

- graphiti_core/llm_client/litellm_client.py: New unified LLM client
- Support for Azure OpenAI, AWS Bedrock, Vertex AI, Ollama, vLLM, etc.
- Automatic structured output detection

## Documentation

- docs/CONFIGURATION.md: Comprehensive configuration guide
- examples/graphiti_config_example.yaml: Example configurations
- DOMAIN_AGNOSTIC_IMPROVEMENT_PLAN.md: Future improvement roadmap

## Tests

- tests/config/test_settings.py: 22 tests for configuration
- tests/config/test_factory.py: 12 tests for factories
- 33/34 tests passing (97%)

## Issues Addressed

- #1004: Azure OpenAI support
- #1006: Azure OpenAI reranker support
- #1007: vLLM/OpenAI-compatible provider stability
- #1074: Ollama embeddings support
- #995: Docker Azure OpenAI support

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-30 23:47:38 -08:00

189 lines
6.6 KiB
Python

"""
Copyright 2024, Zep Software, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
"""
import pytest
from graphiti_core.config import (
DatabaseConfig,
EmbedderConfig,
EmbedderProvider,
LLMProvider,
LLMProviderConfig,
RerankerConfig,
)
from graphiti_core.config.factory import (
create_database_driver,
create_embedder,
create_llm_client,
create_reranker,
)
from graphiti_core.config.providers import DatabaseProvider, RerankerProvider
from graphiti_core.cross_encoder.openai_reranker_client import OpenAIRerankerClient
from graphiti_core.embedder.openai import OpenAIEmbedder
from graphiti_core.llm_client.openai_client import OpenAIClient
class TestCreateLLMClient:
def test_create_openai_client(self, monkeypatch):
"""Test creating OpenAI LLM client."""
monkeypatch.setenv('OPENAI_API_KEY', 'test-key')
config = LLMProviderConfig(provider=LLMProvider.OPENAI)
client = create_llm_client(config)
assert isinstance(client, OpenAIClient)
assert client.model == 'gpt-4.1-mini'
assert client.small_model == 'gpt-4.1-nano'
def test_create_azure_openai_client(self, monkeypatch):
"""Test creating Azure OpenAI LLM client."""
from graphiti_core.llm_client.azure_openai_client import AzureOpenAILLMClient
monkeypatch.setenv('AZURE_OPENAI_API_KEY', 'test-key')
config = LLMProviderConfig(
provider=LLMProvider.AZURE_OPENAI,
base_url='https://my-resource.openai.azure.com',
azure_deployment='gpt-4-deployment',
)
client = create_llm_client(config)
assert isinstance(client, AzureOpenAILLMClient)
def test_create_anthropic_client_missing_dep(self):
"""Test creating Anthropic client without dependency raises error."""
config = LLMProviderConfig(provider=LLMProvider.ANTHROPIC)
# This test will pass if anthropic is installed (in dev env)
# or raise ImportError if not installed
try:
client = create_llm_client(config)
from graphiti_core.llm_client.anthropic_client import AnthropicClient
assert isinstance(client, AnthropicClient)
except ImportError as e:
assert 'anthropic' in str(e).lower()
def test_create_litellm_client_missing_dep(self):
"""Test creating LiteLLM client without dependency raises error."""
config = LLMProviderConfig(
provider=LLMProvider.LITELLM,
litellm_model='gpt-4',
)
# This will raise ImportError if litellm is not installed
try:
client = create_llm_client(config)
from graphiti_core.llm_client.litellm_client import LiteLLMClient
assert isinstance(client, LiteLLMClient)
except ImportError as e:
assert 'litellm' in str(e).lower()
def test_unsupported_provider_raises_error(self):
"""Test unsupported provider raises ValueError."""
# We need to bypass pydantic validation to test this
config = LLMProviderConfig(provider=LLMProvider.OPENAI)
config.provider = 'unsupported' # type: ignore
with pytest.raises(ValueError, match='Unsupported LLM provider'):
create_llm_client(config)
class TestCreateEmbedder:
def test_create_openai_embedder(self, monkeypatch):
"""Test creating OpenAI embedder."""
monkeypatch.setenv('OPENAI_API_KEY', 'test-key')
config = EmbedderConfig(provider=EmbedderProvider.OPENAI)
embedder = create_embedder(config)
assert isinstance(embedder, OpenAIEmbedder)
assert embedder.config.embedding_model == 'text-embedding-3-small'
def test_create_voyage_embedder_missing_dep(self):
"""Test creating Voyage embedder without dependency."""
config = EmbedderConfig(provider=EmbedderProvider.VOYAGE)
try:
embedder = create_embedder(config)
from graphiti_core.embedder.voyage import VoyageEmbedder
assert isinstance(embedder, VoyageEmbedder)
except ImportError as e:
assert 'voyageai' in str(e).lower()
def test_create_azure_openai_embedder(self, monkeypatch):
"""Test creating Azure OpenAI embedder."""
from graphiti_core.embedder.azure_openai import AzureOpenAIEmbedderClient
monkeypatch.setenv('AZURE_OPENAI_API_KEY', 'test-key')
config = EmbedderConfig(
provider=EmbedderProvider.AZURE_OPENAI,
base_url='https://my-resource.openai.azure.com',
azure_deployment='embedding-deployment',
)
embedder = create_embedder(config)
assert isinstance(embedder, AzureOpenAIEmbedderClient)
class TestCreateReranker:
def test_create_openai_reranker(self):
"""Test creating OpenAI reranker."""
config = RerankerConfig(provider=RerankerProvider.OPENAI)
reranker = create_reranker(config)
assert isinstance(reranker, OpenAIRerankerClient)
class TestCreateDatabaseDriver:
def test_create_neo4j_driver(self):
"""Test creating Neo4j driver."""
from graphiti_core.driver.neo4j_driver import Neo4jDriver
config = DatabaseConfig(
provider=DatabaseProvider.NEO4J,
uri='bolt://localhost:7687',
user='neo4j',
password='password',
)
driver = create_database_driver(config)
assert isinstance(driver, Neo4jDriver)
def test_neo4j_driver_missing_uri(self):
"""Test Neo4j driver without URI raises error."""
config = DatabaseConfig(provider=DatabaseProvider.NEO4J)
with pytest.raises(ValueError, match='uri is required'):
create_database_driver(config)
def test_create_falkordb_driver_missing_dep(self):
"""Test creating FalkorDB driver without dependency."""
config = DatabaseConfig(
provider=DatabaseProvider.FALKORDB,
uri='redis://localhost:6379',
)
try:
driver = create_database_driver(config)
from graphiti_core.driver.falkor_driver import FalkorDriver
assert isinstance(driver, FalkorDriver)
except ImportError as e:
assert 'falkordb' in str(e).lower()