from __future__ import annotations import json from pathlib import Path from typing import Any from pydantic import BaseModel, Field class ModelsConfig(BaseModel): orchestrator_path: str = "models/llama.gguf" coder_path: str = "models/xcoder.gguf" critic_path: str = "models/gemma.gguf" embeddings_path: str = "models/all-MiniLM-L6-v2" inference: dict[str, Any] = Field(default_factory=dict) thinker: dict[str, Any] = Field(default_factory=dict) json_compiler: dict[str, Any] = Field(default_factory=dict) orchestrator: dict[str, Any] = Field(default_factory=dict) coder: dict[str, Any] = Field(default_factory=dict) critic: dict[str, Any] = Field(default_factory=dict) sys_util: dict[str, Any] = Field(default_factory=dict) embeddings: dict[str, Any] = Field(default_factory=dict) class PromptsConfig(BaseModel): orchestration_prompt: str = "" planning_prompt: str = "" coder_prompt: str = "" critic_prompt: str = "" class PermissionsConfig(BaseModel): dangerous_commands: dict[str, str] = Field(default_factory=dict) sensitive_paths: list[str] = Field(default_factory=list) default_approval_behavior: str = "ask_always" class RuntimeConfig(BaseModel): step_timeout_ms: int = 30_000 task_timeout_ms: int = 300_000 planner_retry_limit: int = 2 tool_retry_limit: int = 1 replan_limit: int = 1 max_execution_steps: int = 20 retrieval_top_k: int = 5 max_context_tokens: int = 8192 context_budgets: dict[str, int] = Field(default_factory=lambda: { "system": 512, "task": 512, "memory": 2048, "execution": 2048, "tools": 1024, "safety": 512, }) reserve_for_generation_pct: int = 25 orchestrator_retry_limit: int = 2 intent_classifier: str = "thinker" memory_thresholds: dict[str, float] = Field(default_factory=dict) critic_fallback_policy: str = "continue_without_critic" checkpoint_policy: dict[str, Any] = Field(default_factory=dict) event_retention_policy: dict[str, Any] = Field(default_factory=dict) streaming_settings: dict[str, Any] = Field(default_factory=dict) debug: bool = False debug_orchestrator_log_length: int = 500 json_fix_retry_limit: int = 2 json_fix_use_sys_util: bool = True class AppConfig(BaseModel): models: ModelsConfig prompts: PromptsConfig permissions: PermissionsConfig runtime: RuntimeConfig def _load_json(path: Path) -> dict[str, Any]: with path.open("r", encoding="utf-8") as handle: return json.load(handle) def load_app_config(config_dir: str | Path) -> AppConfig: config_path = Path(config_dir) return AppConfig( models=ModelsConfig.model_validate(_load_json(config_path / "models.json")), prompts=PromptsConfig.model_validate(_load_json(config_path / "prompts.json")), permissions=PermissionsConfig.model_validate(_load_json(config_path / "permissions.json")), runtime=RuntimeConfig.model_validate(_load_json(config_path / "runtime.json")), )