Spaces:
Runtime error
Runtime error
| from __future__ import annotations | |
| import json | |
| import os | |
| from dataclasses import dataclass, field | |
| from pathlib import Path | |
| from typing import Any, Literal, cast | |
| TerrainKind = Literal["plain_green"] | |
| ConnectorKind = Literal["deterministic", "openai_compatible"] | |
| OverseerMode = Literal["off", "advisor", "autopilot"] | |
| FORCE_DETERMINISTIC_ENV = "WORLD_SIMULATOR_FORCE_DETERMINISTIC" | |
| class WorldConfig: | |
| width: int | |
| depth: int | |
| terrain: TerrainKind | |
| seed: int | |
| survival: bool = False | |
| class NpcConfig: | |
| count: int | |
| class SimulationConfig: | |
| tick_ms: int | |
| class ServerConfig: | |
| host: str | |
| port: int | |
| class ConnectorConfig: | |
| type: ConnectorKind | |
| base_url: str | None = None | |
| api_url: str | None = None | |
| model: str | None = None | |
| api_key_env: str | None = None | |
| timeout_seconds: int = 60 | |
| max_tokens: int = 1400 | |
| temperature: float = 0.6 | |
| top_p: float = 0.95 | |
| tool_choice: str | dict[str, Any] | None = None | |
| max_parallel_npc_calls: int = 5 | |
| fallback_to_deterministic: bool = True | |
| extra_body: dict[str, Any] | None = None | |
| class OverseerConfig: | |
| mode: OverseerMode = "off" | |
| cycle_ticks: int = 8 | |
| max_directives: int = 3 | |
| connector: ConnectorConfig = field( | |
| default_factory=lambda: ConnectorConfig(type="deterministic") | |
| ) | |
| class GameConfig: | |
| world: WorldConfig | |
| npcs: NpcConfig | |
| simulation: SimulationConfig | |
| server: ServerConfig | |
| connector: ConnectorConfig = field( | |
| default_factory=lambda: ConnectorConfig(type="deterministic") | |
| ) | |
| god_console: ConnectorConfig | None = None | |
| overseer: OverseerConfig = field(default_factory=OverseerConfig) | |
| secondary_connectors: dict[str, ConnectorConfig] = field(default_factory=dict) | |
| def load_game_config(path: Path) -> GameConfig: | |
| _load_dotenv_for_config(path) | |
| with path.open("r", encoding="utf-8") as config_file: | |
| raw = json.load(config_file) | |
| return parse_game_config(raw) | |
| def apply_runtime_env_overrides(config: GameConfig) -> GameConfig: | |
| if not _truthy_env(os.getenv(FORCE_DETERMINISTIC_ENV)): | |
| return config | |
| deterministic_connector = ConnectorConfig(type="deterministic") | |
| return GameConfig( | |
| world=config.world, | |
| npcs=config.npcs, | |
| simulation=config.simulation, | |
| server=config.server, | |
| connector=deterministic_connector, | |
| god_console=None, | |
| overseer=OverseerConfig( | |
| mode=config.overseer.mode, | |
| cycle_ticks=config.overseer.cycle_ticks, | |
| max_directives=config.overseer.max_directives, | |
| connector=deterministic_connector, | |
| ), | |
| ) | |
| def parse_game_config(raw: dict[str, Any]) -> GameConfig: | |
| world = _required_mapping(raw, "world") | |
| npcs = _required_mapping(raw, "npcs") | |
| simulation = _required_mapping(raw, "simulation") | |
| server = _required_mapping(raw, "server") | |
| connector = _optional_mapping(raw, "connector") or {"type": "deterministic"} | |
| god_console = _optional_mapping(raw, "god_console") | |
| overseer = _optional_mapping(raw, "overseer") | |
| terrain_raw = _required_str(world, "terrain") | |
| if terrain_raw != "plain_green": | |
| raise ValueError(f"Unsupported terrain kind: {terrain_raw}") | |
| terrain: TerrainKind = "plain_green" | |
| secondary_raw = raw.get("secondary_connectors", {}) | |
| if not isinstance(secondary_raw, dict): | |
| raise ValueError("'secondary_connectors' must be an object.") | |
| secondary_connectors = { | |
| cid: _parse_connector_config(cfg) | |
| for cid, cfg in secondary_raw.items() | |
| if isinstance(cfg, dict) | |
| } | |
| return GameConfig( | |
| world=WorldConfig( | |
| width=_required_positive_int(world, "width"), | |
| depth=_required_positive_int(world, "depth"), | |
| terrain=terrain, | |
| seed=_required_int(world, "seed"), | |
| survival=_optional_bool(world, "survival", default=False), | |
| ), | |
| npcs=NpcConfig(count=_required_non_negative_int(npcs, "count")), | |
| simulation=SimulationConfig(tick_ms=_required_positive_int(simulation, "tick_ms")), | |
| server=ServerConfig( | |
| host=_required_str(server, "host"), | |
| port=_required_positive_int(server, "port"), | |
| ), | |
| connector=_parse_connector_config(connector), | |
| god_console=_parse_connector_config(god_console) if god_console else None, | |
| overseer=_parse_overseer_config(overseer), | |
| secondary_connectors=secondary_connectors, | |
| ) | |
| def _parse_overseer_config(raw: dict[str, Any] | None) -> OverseerConfig: | |
| if raw is None: | |
| return OverseerConfig() | |
| mode_raw = _optional_str(raw, "mode") or "off" | |
| if mode_raw not in ("off", "advisor", "autopilot"): | |
| raise ValueError(f"Unsupported overseer mode: {mode_raw}") | |
| connector_raw = _optional_mapping(raw, "connector") | |
| if connector_raw is None: | |
| connector_keys = { | |
| "type", | |
| "base_url", | |
| "api_url", | |
| "model", | |
| "api_key_env", | |
| "timeout_seconds", | |
| "max_tokens", | |
| "temperature", | |
| "top_p", | |
| "tool_choice", | |
| "max_parallel_npc_calls", | |
| "fallback_to_deterministic", | |
| "extra_body", | |
| "base_url_env", | |
| "api_url_env", | |
| "model_env", | |
| } | |
| connector_raw = { | |
| key: value | |
| for key, value in raw.items() | |
| if key in connector_keys | |
| } or {"type": "deterministic"} | |
| return OverseerConfig( | |
| mode=cast(OverseerMode, mode_raw), | |
| cycle_ticks=_optional_positive_int(raw, "cycle_ticks") or 8, | |
| max_directives=_optional_positive_int(raw, "max_directives") or 3, | |
| connector=_parse_connector_config(connector_raw), | |
| ) | |
| def _parse_connector_config(raw: dict[str, Any]) -> ConnectorConfig: | |
| connector_type_raw = _optional_str(raw, "type") or "deterministic" | |
| if connector_type_raw not in ("deterministic", "openai_compatible"): | |
| raise ValueError(f"Unsupported connector type: {connector_type_raw}") | |
| connector_type = cast(ConnectorKind, connector_type_raw) | |
| base_url = _optional_str(raw, "base_url") | |
| api_url = _optional_str(raw, "api_url") | |
| model = _optional_str(raw, "model") | |
| base_url_env = _optional_str(raw, "base_url_env") | |
| api_url_env = _optional_str(raw, "api_url_env") | |
| model_env = _optional_str(raw, "model_env") | |
| env_base_url = os.getenv(base_url_env) if base_url_env else None | |
| env_api_url = os.getenv(api_url_env) if api_url_env else None | |
| if env_base_url: | |
| base_url = env_base_url | |
| elif env_api_url: | |
| api_url = env_api_url | |
| base_url = None | |
| if model_env: | |
| model = os.getenv(model_env) or model | |
| temperature = _optional_non_negative_float(raw, "temperature") | |
| top_p = _optional_probability(raw, "top_p") | |
| return ConnectorConfig( | |
| type=connector_type, | |
| base_url=base_url, | |
| api_url=api_url, | |
| model=model, | |
| api_key_env=_optional_str(raw, "api_key_env"), | |
| timeout_seconds=_optional_positive_int(raw, "timeout_seconds") or 60, | |
| max_tokens=_optional_positive_int(raw, "max_tokens") or 1400, | |
| temperature=0.6 if temperature is None else temperature, | |
| top_p=0.95 if top_p is None else top_p, | |
| tool_choice=_optional_tool_choice(raw, "tool_choice"), | |
| max_parallel_npc_calls=_optional_positive_int(raw, "max_parallel_npc_calls") or 5, | |
| fallback_to_deterministic=_optional_bool(raw, "fallback_to_deterministic", default=True), | |
| extra_body=_optional_mapping(raw, "extra_body"), | |
| ) | |
| def _required_mapping(raw: dict[str, Any], key: str) -> dict[str, Any]: | |
| value = raw.get(key) | |
| if not isinstance(value, dict): | |
| raise ValueError(f"Expected '{key}' to be an object.") | |
| return value | |
| def _optional_mapping(raw: dict[str, Any], key: str) -> dict[str, Any] | None: | |
| value = raw.get(key) | |
| if value is None: | |
| return None | |
| if not isinstance(value, dict): | |
| raise ValueError(f"Expected '{key}' to be an object.") | |
| return value | |
| def _required_int(raw: dict[str, Any], key: str) -> int: | |
| value = raw.get(key) | |
| if not isinstance(value, int) or isinstance(value, bool): | |
| raise ValueError(f"Expected '{key}' to be an integer.") | |
| return value | |
| def _required_positive_int(raw: dict[str, Any], key: str) -> int: | |
| value = _required_int(raw, key) | |
| if value <= 0: | |
| raise ValueError(f"Expected '{key}' to be positive.") | |
| return value | |
| def _required_non_negative_int(raw: dict[str, Any], key: str) -> int: | |
| value = _required_int(raw, key) | |
| if value < 0: | |
| raise ValueError(f"Expected '{key}' to be non-negative.") | |
| return value | |
| def _optional_positive_int(raw: dict[str, Any], key: str) -> int | None: | |
| value = raw.get(key) | |
| if value is None: | |
| return None | |
| if not isinstance(value, int) or isinstance(value, bool): | |
| raise ValueError(f"Expected '{key}' to be an integer.") | |
| if value <= 0: | |
| raise ValueError(f"Expected '{key}' to be positive.") | |
| return value | |
| def _optional_non_negative_float(raw: dict[str, Any], key: str) -> float | None: | |
| value = raw.get(key) | |
| if value is None: | |
| return None | |
| if not isinstance(value, int | float) or isinstance(value, bool): | |
| raise ValueError(f"Expected '{key}' to be a number.") | |
| if value < 0: | |
| raise ValueError(f"Expected '{key}' to be non-negative.") | |
| return float(value) | |
| def _optional_probability(raw: dict[str, Any], key: str) -> float | None: | |
| value = raw.get(key) | |
| if value is None: | |
| return None | |
| if not isinstance(value, int | float) or isinstance(value, bool): | |
| raise ValueError(f"Expected '{key}' to be a number.") | |
| if value <= 0 or value > 1: | |
| raise ValueError(f"Expected '{key}' to be between 0 and 1.") | |
| return float(value) | |
| def _required_str(raw: dict[str, Any], key: str) -> str: | |
| value = raw.get(key) | |
| if not isinstance(value, str) or not value: | |
| raise ValueError(f"Expected '{key}' to be a non-empty string.") | |
| return value | |
| def _optional_str(raw: dict[str, Any], key: str) -> str | None: | |
| value = raw.get(key) | |
| if value is None: | |
| return None | |
| if not isinstance(value, str) or not value: | |
| raise ValueError(f"Expected '{key}' to be a non-empty string.") | |
| return value | |
| def _optional_bool(raw: dict[str, Any], key: str, *, default: bool) -> bool: | |
| value = raw.get(key) | |
| if value is None: | |
| return default | |
| if not isinstance(value, bool): | |
| raise ValueError(f"Expected '{key}' to be a boolean.") | |
| return value | |
| def _optional_tool_choice(raw: dict[str, Any], key: str) -> str | dict[str, Any] | None: | |
| value = raw.get(key) | |
| if value is None: | |
| return None | |
| if isinstance(value, str): | |
| if not value: | |
| raise ValueError(f"Expected '{key}' to be a non-empty string or object.") | |
| return value | |
| if isinstance(value, dict): | |
| return value | |
| raise ValueError(f"Expected '{key}' to be a string or object.") | |
| def _load_dotenv_for_config(config_path: Path) -> None: | |
| candidates = [ | |
| Path.cwd() / ".env", | |
| config_path.resolve().parent / ".env", | |
| config_path.resolve().parent.parent / ".env", | |
| ] | |
| seen: set[Path] = set() | |
| for candidate in candidates: | |
| if candidate in seen or not candidate.is_file(): | |
| continue | |
| seen.add(candidate) | |
| _load_dotenv(candidate) | |
| def _load_dotenv(path: Path) -> None: | |
| with path.open("r", encoding="utf-8") as dotenv_file: | |
| for raw_line in dotenv_file: | |
| line = raw_line.strip() | |
| if not line or line.startswith("#"): | |
| continue | |
| if line.startswith("export "): | |
| line = line.removeprefix("export ").strip() | |
| key, separator, value = line.partition("=") | |
| if not separator: | |
| continue | |
| key = key.strip() | |
| if not key: | |
| continue | |
| os.environ.setdefault(key, _dotenv_value(value)) | |
| def _dotenv_value(raw: str) -> str: | |
| value = raw.strip() | |
| if len(value) >= 2 and value[0] == value[-1] and value[0] in ("'", '"'): | |
| return value[1:-1] | |
| return value | |
| def _truthy_env(value: str | None) -> bool: | |
| return value is not None and value.strip().lower() in ("1", "true", "yes", "on") | |