Files
zk-data-agent/agent_platform/config.py
T

120 lines
4.6 KiB
Python

from __future__ import annotations
import os
from dataclasses import dataclass
from functools import lru_cache
def _bool(name: str, default: bool) -> bool:
value = os.getenv(name)
if value is None:
return default
return value.strip().lower() in {"1", "true", "yes", "on"}
def _int(name: str, default: int) -> int:
value = os.getenv(name)
return int(value) if value else default
def _float(name: str, default: float) -> float:
value = os.getenv(name)
return float(value) if value else default
@dataclass(frozen=True, slots=True)
class Settings:
model_api_base_url: str
model_api_key: str
openwebui_forward_jwt_secret: str
internal_provider_key: str
internal_gateway_key: str
database_url: str
redis_url: str
gateway_url: str
execution_provider: str
workspace_image: str
workspace_network_enabled: bool
workspace_memory_limit: str
workspace_cpu_limit: float
workspace_pids_limit: int
workspace_ssh_host: str
workspace_download_max_bytes: int
model_timeout_seconds: int
tool_timeout_seconds: int
max_tool_output_chars: int
@classmethod
def from_env(cls) -> Settings:
return cls(
model_api_base_url=os.getenv("MODEL_API_BASE_URL", "https://api.k1412.top").rstrip("/"),
model_api_key=os.getenv("MODEL_API_KEY", ""),
openwebui_forward_jwt_secret=os.getenv("OPENWEBUI_FORWARD_JWT_SECRET", ""),
internal_provider_key=os.getenv("INTERNAL_PROVIDER_KEY", ""),
internal_gateway_key=os.getenv("INTERNAL_GATEWAY_KEY", ""),
database_url=os.getenv("DATABASE_URL", "sqlite+aiosqlite:///./.runtime/agent.db"),
redis_url=os.getenv("REDIS_URL", "redis://localhost:6379/0"),
gateway_url=os.getenv("GATEWAY_URL", "http://gateway:8001").rstrip("/"),
execution_provider=os.getenv("EXECUTION_PROVIDER", "local-docker"),
workspace_image=os.getenv("WORKSPACE_IMAGE", "k1412-agent-workspace:dev"),
workspace_network_enabled=_bool("WORKSPACE_NETWORK_ENABLED", True),
workspace_memory_limit=os.getenv("WORKSPACE_MEMORY_LIMIT", "2g"),
workspace_cpu_limit=_float("WORKSPACE_CPU_LIMIT", 2.0),
workspace_pids_limit=_int("WORKSPACE_PIDS_LIMIT", 512),
workspace_ssh_host=os.getenv("WORKSPACE_SSH_HOST", ""),
workspace_download_max_bytes=_int("WORKSPACE_DOWNLOAD_MAX_BYTES", 128 * 1024 * 1024),
model_timeout_seconds=_int("MODEL_TIMEOUT_SECONDS", 600),
tool_timeout_seconds=_int("TOOL_TIMEOUT_SECONDS", 120),
max_tool_output_chars=_int("MAX_TOOL_OUTPUT_CHARS", 24_000),
)
def validate_runtime(self) -> None:
missing = [
name
for name, value in (
("MODEL_API_KEY", self.model_api_key),
("OPENWEBUI_FORWARD_JWT_SECRET", self.openwebui_forward_jwt_secret),
("INTERNAL_PROVIDER_KEY", self.internal_provider_key),
("INTERNAL_GATEWAY_KEY", self.internal_gateway_key),
)
if not value
]
if missing:
raise RuntimeError(f"Missing required runtime secrets: {', '.join(missing)}")
self._validate_internal_secret_lengths()
def validate_gateway(self) -> None:
missing = [
name
for name, value in (
("OPENWEBUI_FORWARD_JWT_SECRET", self.openwebui_forward_jwt_secret),
("INTERNAL_GATEWAY_KEY", self.internal_gateway_key),
)
if not value
]
if missing:
raise RuntimeError(f"Missing required gateway secrets: {', '.join(missing)}")
self._validate_internal_secret_lengths()
if self.execution_provider not in {"local-docker", "ssh-docker"}:
raise RuntimeError("EXECUTION_PROVIDER must be local-docker or ssh-docker")
if self.execution_provider == "ssh-docker" and not self.workspace_ssh_host:
raise RuntimeError("WORKSPACE_SSH_HOST is required for ssh-docker")
def _validate_internal_secret_lengths(self) -> None:
weak = [
name
for name, value in (
("OPENWEBUI_FORWARD_JWT_SECRET", self.openwebui_forward_jwt_secret),
("INTERNAL_PROVIDER_KEY", self.internal_provider_key),
("INTERNAL_GATEWAY_KEY", self.internal_gateway_key),
)
if value and len(value.encode("utf-8")) < 32
]
if weak:
raise RuntimeError(f"Internal secrets must be at least 32 bytes: {', '.join(weak)}")
@lru_cache(maxsize=1)
def get_settings() -> Settings:
return Settings.from_env()