VibecoderMcSwaggins's picture
docs: Clarify config parameter behavior in factory.py
d5b800f
raw
history blame
4.96 kB
"""Factory for creating orchestrators.
Implements the Factory Pattern (GoF) for creating the appropriate
orchestrator based on configuration and available credentials.
Design Principles:
- Open/Closed: Easy to add new orchestrator types without modifying existing code
- Dependency Inversion: Returns protocol-compatible objects, not concrete types
- Single Responsibility: Only handles orchestrator creation logic
"""
from typing import TYPE_CHECKING, Literal
import structlog
from src.config.domain import ResearchDomain
from src.orchestrators.base import (
JudgeHandlerProtocol,
OrchestratorProtocol,
SearchHandlerProtocol,
)
from src.orchestrators.simple import Orchestrator
from src.utils.config import settings
from src.utils.models import OrchestratorConfig
if TYPE_CHECKING:
from src.orchestrators.advanced import AdvancedOrchestrator
logger = structlog.get_logger()
def _get_advanced_orchestrator_class() -> type["AdvancedOrchestrator"]:
"""Import AdvancedOrchestrator lazily to avoid hard dependency.
This allows the simple mode to work without agent-framework-core installed.
Returns:
The AdvancedOrchestrator class
Raises:
ValueError: If agent-framework-core is not installed
"""
try:
from src.orchestrators.advanced import AdvancedOrchestrator
return AdvancedOrchestrator
except ImportError as e:
logger.error("Failed to import AdvancedOrchestrator", error=str(e))
raise ValueError(
"Advanced mode requires agent-framework-core. "
"Install with: pip install agent-framework-core. "
"Or use mode='simple' instead."
) from e
def create_orchestrator(
search_handler: SearchHandlerProtocol | None = None,
judge_handler: JudgeHandlerProtocol | None = None,
config: OrchestratorConfig | None = None,
mode: Literal["simple", "magentic", "advanced", "hierarchical"] | None = None,
api_key: str | None = None,
domain: ResearchDomain | str | None = None,
) -> OrchestratorProtocol:
"""
Create an orchestrator instance.
This factory automatically selects the appropriate orchestrator based on:
1. Explicit mode parameter (if provided)
2. Available API keys (auto-detection)
Args:
search_handler: The search handler (required for simple mode)
judge_handler: The judge handler (required for simple mode)
config: Optional configuration (max_iterations, timeouts, etc.)
Note: This parameter is only used by simple and hierarchical modes.
Advanced mode uses settings.advanced_max_rounds instead.
mode: "simple", "magentic", "advanced", or "hierarchical"
Note: "magentic" is an alias for "advanced" (kept for backwards compatibility)
api_key: Optional API key for advanced mode (OpenAI)
domain: Research domain for customization (default: sexual_health)
Returns:
Orchestrator instance implementing OrchestratorProtocol
Raises:
ValueError: If required handlers are missing for simple mode
ValueError: If advanced mode is requested but dependencies are missing
"""
effective_config = config or OrchestratorConfig()
effective_mode = _determine_mode(mode, api_key)
logger.info("Creating orchestrator", mode=effective_mode, domain=domain)
if effective_mode == "advanced":
orchestrator_cls = _get_advanced_orchestrator_class()
return orchestrator_cls(
max_rounds=settings.advanced_max_rounds,
api_key=api_key,
domain=domain,
)
if effective_mode == "hierarchical":
from src.orchestrators.hierarchical import HierarchicalOrchestrator
return HierarchicalOrchestrator(config=effective_config, domain=domain)
# Simple mode requires handlers
if search_handler is None or judge_handler is None:
raise ValueError("Simple mode requires search_handler and judge_handler")
return Orchestrator(
search_handler=search_handler,
judge_handler=judge_handler,
config=effective_config,
domain=domain,
)
def _determine_mode(explicit_mode: str | None, api_key: str | None) -> str:
"""Determine which mode to use.
Priority:
1. Explicit mode parameter
2. Auto-detect based on available API keys
Args:
explicit_mode: Mode explicitly requested by caller
api_key: API key provided by caller
Returns:
Effective mode string: "simple", "advanced", or "hierarchical"
"""
if explicit_mode:
if explicit_mode in ("magentic", "advanced"):
return "advanced"
if explicit_mode == "hierarchical":
return "hierarchical"
return "simple"
# Auto-detect: advanced if paid API key available
if settings.has_openai_key or (api_key and api_key.startswith("sk-")):
return "advanced"
return "simple"