11"""Configuration management for BalatroLLM."""
22
33import json
4- from dataclasses import asdict , dataclass , field
4+ from dataclasses import asdict , dataclass
55from pathlib import Path
66from typing import Any
77
@@ -62,12 +62,91 @@ def load_model_config(
6262 return final_config
6363
6464
65+ @dataclass
66+ class StrategyManifest :
67+ """Strategy metadata from manifest.json.
68+
69+ Stores all metadata for a game strategy including name, description,
70+ author, version, and tags.
71+
72+ Attributes:
73+ name: Human-readable strategy name (e.g., 'Default', 'Aggressive').
74+ description: Strategy description.
75+ author: Strategy author identifier.
76+ version: Strategy version (distinct from BalatroLLM version).
77+ tags: List of tags for categorization.
78+ """
79+
80+ name : str
81+ description : str
82+ author : str
83+ version : str
84+ tags : list [str ]
85+
86+ @classmethod
87+ def from_manifest_file (
88+ cls , strategy : str , strategies_dir : Path = Path ("src/balatrollm/strategies" )
89+ ) -> "StrategyManifest" :
90+ """Load strategy metadata from manifest.json.
91+
92+ Reads the manifest.json file for a given strategy to retrieve metadata.
93+
94+ Args:
95+ strategy: Strategy name (e.g., 'default', 'aggressive')
96+ strategies_dir: Base directory containing strategy folders
97+
98+ Returns:
99+ StrategyManifest instance loaded from the JSON file.
100+
101+ Raises:
102+ FileNotFoundError: If manifest.json doesn't exist for the strategy
103+ json.JSONDecodeError: If manifest.json is malformed
104+ ValueError: If manifest.json is missing required fields
105+ """
106+ manifest_path = strategies_dir / strategy / "manifest.json"
107+ if not manifest_path .exists ():
108+ raise FileNotFoundError (
109+ f"Manifest not found for strategy '{ strategy } ': { manifest_path } "
110+ )
111+
112+ with manifest_path .open () as f :
113+ data = json .load (f )
114+
115+ # Validate required fields
116+ required_fields = {"name" , "description" , "author" , "version" , "tags" }
117+ missing_fields = required_fields - set (data .keys ())
118+ if missing_fields :
119+ raise ValueError (
120+ f"Manifest for strategy '{ strategy } ' missing required fields: { missing_fields } "
121+ )
122+
123+ return cls (** data )
124+
125+ def to_strategy_file (self , strategy_path : Path ) -> None :
126+ """Write strategy metadata to a JSON file.
127+
128+ Saves the current strategy manifest to a JSON file with proper
129+ formatting.
130+
131+ Args:
132+ strategy_path: Path to the strategy file to write.
133+ """
134+ # Convert dataclass to dictionary
135+ strategy_data = asdict (self )
136+
137+ # Create parent directory if it doesn't exist
138+ strategy_path .parent .mkdir (parents = True , exist_ok = True )
139+
140+ with strategy_path .open ("w" ) as f :
141+ json .dump (strategy_data , f , indent = 2 )
142+
143+
65144@dataclass
66145class Config :
67146 """Configuration for LLMBot.
68147
69148 Stores all configuration parameters for bot execution including
70- model settings, game parameters, and metadata .
149+ model settings and game parameters.
71150
72151 Attributes:
73152 model: LLM model identifier (e.g., 'openai/gpt-oss-20b').
@@ -78,11 +157,7 @@ class Config:
78157 challenge: Optional challenge mode identifier.
79158 take_screenshots: Whether to take screenshots during gameplay (default: True).
80159 use_default_paths: Whether to use BalatroBot's default storage paths (default: False).
81- version: Software version string.
82- name: Human-readable configuration name.
83- description: Configuration description.
84- author: Configuration author identifier.
85- tags: List of tags for categorization.
160+ version: BalatroLLM version string (separate from strategy version).
86161 """
87162
88163 model : str
@@ -94,10 +169,6 @@ class Config:
94169 take_screenshots : bool = True
95170 use_default_paths : bool = False
96171 version : str = __version__
97- name : str = "Unknown Name"
98- description : str = "Unknown Description"
99- author : str = "BalatroBench"
100- tags : list [str ] = field (default_factory = list )
101172
102173 @classmethod
103174 def from_defaults (cls ) -> "Config" :
@@ -115,11 +186,6 @@ def from_defaults(cls) -> "Config":
115186 challenge = None ,
116187 take_screenshots = True ,
117188 use_default_paths = False ,
118- version = "" ,
119- name = "Unknown Name" ,
120- description = "Unknown Description" ,
121- author = "BalatroBench" ,
122- tags = [],
123189 )
124190
125191 @classmethod
0 commit comments