ducklm/app/core/config.py

94 lines
3.2 KiB
Python

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
shell_command_timeout_ms: int = 3_600_000
shell_idle_timeout_ms: int = 600_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"
recall_model: str = "sys_util"
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
recall_model: str = "json_compiler"
critic_retry_limit: int = 2
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")),
)