|
|
"""Centralized LLM client factory. |
|
|
|
|
|
This module provides factory functions for creating LLM clients, |
|
|
ensuring consistent configuration and clear error messages. |
|
|
|
|
|
Why Magentic requires OpenAI: |
|
|
- Magentic agents use the @ai_function decorator for tool calling |
|
|
- This requires structured function calling protocol (tools, tool_choice) |
|
|
- OpenAI's API supports this natively |
|
|
- Anthropic/HuggingFace Inference APIs are text-in/text-out only |
|
|
""" |
|
|
|
|
|
from typing import TYPE_CHECKING, Any |
|
|
|
|
|
from src.utils.config import settings |
|
|
from src.utils.exceptions import ConfigurationError |
|
|
|
|
|
if TYPE_CHECKING: |
|
|
from agent_framework.openai import OpenAIChatClient |
|
|
|
|
|
|
|
|
def get_magentic_client() -> "OpenAIChatClient": |
|
|
""" |
|
|
Get the OpenAI client for Magentic agents. |
|
|
|
|
|
Magentic requires OpenAI because it uses function calling protocol: |
|
|
- @ai_function decorators define callable tools |
|
|
- LLM returns structured tool calls (not just text) |
|
|
- Requires OpenAI's tools/function_call API support |
|
|
|
|
|
Raises: |
|
|
ConfigurationError: If OPENAI_API_KEY is not set |
|
|
|
|
|
Returns: |
|
|
Configured OpenAIChatClient for Magentic agents |
|
|
""" |
|
|
|
|
|
from agent_framework.openai import OpenAIChatClient |
|
|
|
|
|
api_key = settings.get_openai_api_key() |
|
|
|
|
|
return OpenAIChatClient( |
|
|
model_id=settings.openai_model, |
|
|
api_key=api_key, |
|
|
) |
|
|
|
|
|
|
|
|
def get_pydantic_ai_model() -> Any: |
|
|
""" |
|
|
Get the appropriate model for pydantic-ai based on configuration. |
|
|
|
|
|
Uses the configured LLM_PROVIDER to select between OpenAI and Anthropic. |
|
|
This is used by simple mode components (JudgeHandler, etc.) |
|
|
|
|
|
Returns: |
|
|
Configured pydantic-ai model |
|
|
""" |
|
|
from pydantic_ai.models.anthropic import AnthropicModel |
|
|
from pydantic_ai.models.openai import OpenAIChatModel |
|
|
from pydantic_ai.providers.anthropic import AnthropicProvider |
|
|
from pydantic_ai.providers.openai import OpenAIProvider |
|
|
|
|
|
if settings.llm_provider == "openai": |
|
|
if not settings.openai_api_key: |
|
|
raise ConfigurationError("OPENAI_API_KEY not set for pydantic-ai") |
|
|
provider = OpenAIProvider(api_key=settings.openai_api_key) |
|
|
return OpenAIChatModel(settings.openai_model, provider=provider) |
|
|
|
|
|
if settings.llm_provider == "anthropic": |
|
|
if not settings.anthropic_api_key: |
|
|
raise ConfigurationError("ANTHROPIC_API_KEY not set for pydantic-ai") |
|
|
anthropic_provider = AnthropicProvider(api_key=settings.anthropic_api_key) |
|
|
return AnthropicModel(settings.anthropic_model, provider=anthropic_provider) |
|
|
|
|
|
raise ConfigurationError(f"Unknown LLM provider: {settings.llm_provider}") |
|
|
|
|
|
|
|
|
def check_magentic_requirements() -> None: |
|
|
""" |
|
|
Check if Magentic mode requirements are met. |
|
|
|
|
|
Raises: |
|
|
ConfigurationError: If requirements not met |
|
|
""" |
|
|
if not settings.has_openai_key: |
|
|
raise ConfigurationError( |
|
|
"Magentic mode requires OPENAI_API_KEY for function calling support. " |
|
|
"Anthropic and HuggingFace Inference do not support the structured " |
|
|
"function calling protocol that Magentic agents require. " |
|
|
"Use mode='simple' for other LLM providers." |
|
|
) |
|
|
|
|
|
|
|
|
def check_simple_mode_requirements() -> None: |
|
|
""" |
|
|
Check if simple mode requirements are met. |
|
|
|
|
|
Simple mode supports both OpenAI and Anthropic. |
|
|
|
|
|
Raises: |
|
|
ConfigurationError: If no LLM API key is configured |
|
|
""" |
|
|
if not settings.has_any_llm_key: |
|
|
raise ConfigurationError( |
|
|
"No LLM API key configured. Set OPENAI_API_KEY or ANTHROPIC_API_KEY." |
|
|
) |
|
|
|