feat: rebuild as multi-user web agent
This commit is contained in:
@@ -0,0 +1,117 @@
|
||||
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
|
||||
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", ""),
|
||||
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()
|
||||
Reference in New Issue
Block a user