import yaml from pathlib import Path from typing import Type, TypeVar, Any from dataclasses import dataclass, asdict, field ConfigInst = TypeVar('ConfigInst', bound='Config') OpenAIConfigInst = TypeVar('OpenAIConfigInst', bound='OpenAIConfig') @dataclass class AIConfig: """ The base class of all AI configurations. """ ai_type: str name: str @dataclass class OpenAIConfig(AIConfig): """ The OpenAI section of the configuration file. """ # all members have default values, so we can easily create # a default configuration ai_type: str = 'openai' name: str = 'openai_1' system: str = 'You are an assistant' api_key: str = '0123456789' model: str = 'gpt-3.5' temperature: float = 1.0 max_tokens: int = 4000 top_p: float = 1.0 frequency_penalty: float = 0.0 presence_penalty: float = 0.0 @classmethod def from_dict(cls: Type[OpenAIConfigInst], source: dict[str, Any]) -> OpenAIConfigInst: """ Create OpenAIConfig from a dict. """ return cls( ai_type='openai', name=str(source['name']), system=str(source['system']), api_key=str(source['api_key']), model=str(source['model']), max_tokens=int(source['max_tokens']), temperature=float(source['temperature']), top_p=float(source['top_p']), frequency_penalty=float(source['frequency_penalty']), presence_penalty=float(source['presence_penalty']) ) def as_dict(self) -> dict[str, Any]: return asdict(self) def create_default_ai_configs() -> dict[str, AIConfig]: openai_conf = OpenAIConfig() return {openai_conf.name: openai_conf} @dataclass class Config: """ The configuration file structure. """ # all members have default values, so we can easily create # a default configuration db: str = './db/' ais: dict[str, AIConfig] = field(default_factory=create_default_ai_configs) @classmethod def from_dict(cls: Type[ConfigInst], source: dict[str, Any]) -> ConfigInst: """ Create Config from a dict. """ return cls( db=str(source['db']), ais=source['ais'] # FIXME: call correct constructors ) @classmethod def create_default(self, file_path: Path) -> None: """ Creates a default Config in the given file. """ conf = Config() conf.to_file(file_path) @classmethod def from_file(cls: Type[ConfigInst], path: str) -> ConfigInst: with open(path, 'r') as f: source = yaml.load(f, Loader=yaml.FullLoader) # add the AI name to the config (for easy internal access) for ai_name, ai_conf in source['ais'].items(): ai_conf['name'] = ai_name return cls.from_dict(source) def to_file(self, file_path: Path) -> None: # remove the AI name from the config (for a cleaner format) data = self.as_dict() for ai_name, ai_conf in data['ais'].items(): del (ai_conf['name']) with open(file_path, 'w') as f: yaml.dump(data, f, sort_keys=False) def as_dict(self) -> dict[str, Any]: return asdict(self)