"""Base configuration classes for configurable games.
from typing import Any This module provides the foundation for creating flexible game
configurations that support multiple LLM providers and configuration modes.
"""
from abc import ABC, abstractmethod
from enum import Enum
from typing import Any
from haive.core.engine.agent.agent import AgentConfig
from haive.core.models.llm.base import OpenAILLMConfig
from pydantic import BaseModel, Field, model_validator
from haive.games.core.agent.player_agent import PlayerAgentConfig
[docs]
def create_llm_config(model: str, **kwargs):
"""Placeholder function until core factory is available."""
return OpenAILLMConfig(model=model, **kwargs)
[docs]
class ConfigMode(str, Enum):
"""Configuration mode for game setup."""
LEGACY = "legacy"
SIMPLE = "simple"
EXAMPLE = "example"
ADVANCED = "advanced"
AUTO = "auto"
[docs]
class GamePlayerRole(BaseModel):
"""Definition of a player role in a game."""
name: str = Field(description="Internal name for the role (e.g., 'white_player')")
display_name: str = Field(description="Display name for the role (e.g., 'White')")
is_analyzer: bool = Field(
default=False, description="Whether this is an analyzer role"
)
default_model: str = Field(
default="gpt-3.5-turbo", description="Default model for this role"
)
[docs]
class BaseGameConfig(AgentConfig, ABC):
"""Base configuration for all configurable games.
This class provides a unified configuration system that supports:
- Legacy hardcoded engines (backward compatibility)
- Simple model string configuration
- Example-based configuration
- Advanced PlayerAgentConfig configuration
Games should extend this class and implement the required abstract methods.
"""
config_mode: ConfigMode = Field(
default=ConfigMode.AUTO, description="Configuration mode to use"
)
use_legacy_engines: bool = Field(
default=False, description="Use hardcoded engines for backward compatibility"
)
player1_model: str | None = Field(default=None, description="Model for player 1")
player2_model: str | None = Field(default=None, description="Model for player 2")
example_config: str | None = Field(
default=None, description="Name of predefined example configuration"
)
player_configs: dict[str, PlayerAgentConfig] | None = Field(
default=None, description="Detailed player configurations"
)
temperature: float = Field(
default=0.7, description="Temperature for LLM generation"
)
enable_analysis: bool = Field(default=True, description="Enable position analysis")
recursion_limit: int = Field(default=500, description="Python recursion limit")
[docs]
@abstractmethod
def get_role_definitions(self) -> dict[str, GamePlayerRole]:
"""Define the player roles for this game.
Returns:
Dictionary mapping role names to GamePlayerRole definitions
Examples:
{
"white_player": GamePlayerRole(name="white_player", display_name="White"),
"black_player": GamePlayerRole(name="black_player", display_name="Black"),
"white_analyzer": GamePlayerRole(name="white_analyzer", display_name="White Analyst", is_analyzer=True),
"black_analyzer": GamePlayerRole(name="black_analyzer", display_name="Black Analyst", is_analyzer=True),
}
"""
[docs]
@abstractmethod
def get_example_configs(self) -> dict[str, dict[str, Any]]:
"""Define available example configurations.
Returns:
Dictionary mapping example names to configuration parameters
Examples:
{
"gpt_vs_claude": {
"player1_model": "gpt-4",
"player2_model": "claude-3-opus",
"temperature": 0.7
},
"budget": {
"player1_model": "gpt-3.5-turbo",
"player2_model": "gpt-3.5-turbo",
"temperature": 0.5
}
}
"""
[docs]
@abstractmethod
def build_legacy_engines(self) -> list[Any]:
"""Build legacy hardcoded engines for backward compatibility.
Returns:
List of game engines using hardcoded LLM configurations
"""
[docs]
@abstractmethod
def create_engines_from_player_configs(
self, player_configs: dict[str, PlayerAgentConfig]
) -> list[Any]:
"""Create engines from detailed player configurations.
Args:
player_configs: Dictionary mapping role names to PlayerAgentConfig
Returns:
List of configured game engines
"""
[docs]
def determine_config_mode(self) -> ConfigMode:
"""Automatically determine configuration mode based on provided fields."""
if self.config_mode != ConfigMode.AUTO:
return self.config_mode
if self.use_legacy_engines:
return ConfigMode.LEGACY
if self.player_configs:
return ConfigMode.ADVANCED
if self.example_config:
return ConfigMode.EXAMPLE
return ConfigMode.SIMPLE
[docs]
def create_simple_player_configs(self) -> dict[str, PlayerAgentConfig]:
"""Create player configs from simple model strings.
This method should be overridden by games that use different field names (e.g.,
white_model/black_model instead of player1_model/player2_model).
"""
roles = self.get_role_definitions()
configs = {}
for role_name, role_def in roles.items():
if "player1" in role_name and self.player1_model:
model = self.player1_model
elif "player2" in role_name and self.player2_model:
model = self.player2_model
else:
model = role_def.default_model
configs[role_name] = PlayerAgentConfig(
llm_config=model,
temperature=self.temperature,
player_name=role_def.display_name,
)
return configs
[docs]
def create_example_player_configs(
self, example_name: str
) -> dict[str, PlayerAgentConfig]:
"""Create player configs from example configuration."""
examples = self.get_example_configs()
if example_name not in examples:
available = ", ".join(examples.keys())
raise ValueError(
f"Unknown example '{example_name}'. Available: {available}"
)
example = examples[example_name]
for key, value in example.items():
if hasattr(self, key):
setattr(self, key, value)
return self.create_simple_player_configs()
[docs]
def get_player_names(self) -> dict[str, str]:
"""Get display names for all players."""
roles = self.get_role_definitions()
return {
role_name: role_def.display_name
for role_name, role_def in roles.items()
if not role_def.is_analyzer
}
[docs]
def create_simple_config(
config_class: type[BaseGameConfig], player1_model: str, player2_model: str, **kwargs
) -> BaseGameConfig:
"""Create a simple game configuration with model strings.
Args:
config_class: The game's configuration class
player1_model: Model for player 1
player2_model: Model for player 2
**kwargs: Additional configuration parameters
Returns:
Configured game instance
"""
return config_class(
config_mode=ConfigMode.SIMPLE,
player1_model=player1_model,
player2_model=player2_model,
**kwargs,
)
[docs]
def create_example_config(
config_class: type[BaseGameConfig], example_name: str, **kwargs
) -> BaseGameConfig:
"""Create a game configuration from a predefined example.
Args:
config_class: The game's configuration class
example_name: Name of the example configuration
**kwargs: Additional configuration parameters to override
Returns:
Configured game instance
"""
return config_class(
config_mode=ConfigMode.EXAMPLE, example_config=example_name, **kwargs
)
[docs]
def create_advanced_config(
config_class: type[BaseGameConfig],
player_configs: dict[str, PlayerAgentConfig],
**kwargs,
) -> BaseGameConfig:
"""Create a game configuration with detailed player configs.
Args:
config_class: The game's configuration class
player_configs: Dictionary mapping role names to PlayerAgentConfig
**kwargs: Additional configuration parameters
Returns:
Configured game instance
"""
return config_class(
config_mode=ConfigMode.ADVANCED, player_configs=player_configs, **kwargs
)