Clawith/backend/app/config.py

148 lines
4.5 KiB
Python

"""Application configuration."""
from functools import lru_cache
from pathlib import Path
from pydantic_settings import BaseSettings
from app.services.sandbox.config import SandboxConfig, SandboxType
def _running_in_container() -> bool:
"""Best-effort container runtime detection."""
if Path("/.dockerenv").exists() or Path("/run/.containerenv").exists():
return True
cgroup = Path("/proc/1/cgroup")
if not cgroup.exists():
return False
try:
content = cgroup.read_text(encoding="utf-8", errors="ignore")
except OSError:
return False
return any(token in content for token in ("docker", "containerd", "kubepods", "podman"))
def _default_agent_data_dir() -> str:
"""Use Docker path in containers, user-writable path on local hosts."""
if _running_in_container():
return "/data/agents"
return str(Path.home() / ".clawith" / "data" / "agents")
def _default_agent_template_dir() -> str:
"""Locate the agent template directory for both Docker and source deployments.
In a Docker container the backend source is copied to /app, so the template
lives at /app/agent_template. In a source deployment it sits next to the
backend/ package root, i.e. <repo>/backend/agent_template.
"""
if _running_in_container():
return "/app/agent_template"
# Source layout: backend/app/config.py -> ../.. = backend/ -> agent_template
source_path = Path(__file__).resolve().parent.parent / "agent_template"
return str(source_path)
def _read_version() -> str:
"""Read version from local VERSION file, fallback to root."""
for candidate in [Path(__file__).resolve().parent.parent / "VERSION",
Path(__file__).resolve().parent.parent.parent / "VERSION",
Path("/app/VERSION"), Path("/VERSION")]:
try:
return candidate.read_text(encoding="utf-8").strip()
except OSError:
continue
return "0.0.0"
class Settings(BaseSettings):
"""Application settings loaded from environment variables."""
# App
APP_NAME: str = "Clawith"
APP_VERSION: str = _read_version()
DEBUG: bool = False
SECRET_KEY: str = "change-me-in-production"
API_PREFIX: str = "/api"
# Database
DATABASE_URL: str = "postgresql+asyncpg://clawith:clawith@localhost:5432/clawith"
# Redis
REDIS_URL: str = "redis://localhost:6379/0"
# JWT
JWT_SECRET_KEY: str = "change-me-jwt-secret"
JWT_ALGORITHM: str = "HS256"
JWT_ACCESS_TOKEN_EXPIRE_MINUTES: int = 60 * 24 # 24 hours
PASSWORD_RESET_TOKEN_EXPIRE_MINUTES: int = 60
EMAIL_VERIFICATION_TOKEN_EXPIRE_MINUTES: int = 60 # 1 hour
EMAIL_VERIFICATION_REQUIRED: bool = False # Require email verification for login
# File Storage
AGENT_DATA_DIR: str = _default_agent_data_dir()
AGENT_TEMPLATE_DIR: str = _default_agent_template_dir()
# Docker (for Agent containers)
DOCKER_NETWORK: str = "clawith_network"
OPENCLAW_IMAGE: str = "openclaw:local"
OPENCLAW_GATEWAY_PORT: int = 18789
# Feishu OAuth
FEISHU_APP_ID: str = ""
FEISHU_APP_SECRET: str = ""
FEISHU_REDIRECT_URI: str = ""
PUBLIC_BASE_URL: str = ""
# CORS
CORS_ORIGINS: list[str] = ["http://localhost:3000", "http://localhost:5173"]
# Jina AI (Reader + Search APIs)
JINA_API_KEY: str = ""
# Exa AI (Search API)
EXA_API_KEY: str = ""
# Sandbox configuration
SANDBOX_TYPE: SandboxType = SandboxType.SUBPROCESS
SANDBOX_API_KEY: str = ""
SANDBOX_API_URL: str = ""
SANDBOX_CPU_LIMIT: str = "0.5"
SANDBOX_MEMORY_LIMIT: str = "256m"
SANDBOX_ALLOW_NETWORK: bool = False
SANDBOX_DEFAULT_TIMEOUT: int = 30
SANDBOX_MAX_TIMEOUT: int = 60
model_config = {
"env_file": [".env", "../.env"],
"env_file_encoding": "utf-8",
"case_sensitive": True,
"extra": "ignore",
}
@lru_cache
def get_settings() -> Settings:
"""Get cached application settings."""
return Settings()
def get_sandbox_config() -> SandboxConfig:
"""Create SandboxConfig from application settings."""
settings = get_settings()
return SandboxConfig(
type=settings.SANDBOX_TYPE,
enabled=True,
api_key=settings.SANDBOX_API_KEY,
api_url=settings.SANDBOX_API_URL,
cpu_limit=settings.SANDBOX_CPU_LIMIT,
memory_limit=settings.SANDBOX_MEMORY_LIMIT,
allow_network=settings.SANDBOX_ALLOW_NETWORK,
default_timeout=settings.SANDBOX_DEFAULT_TIMEOUT,
max_timeout=settings.SANDBOX_MAX_TIMEOUT,
)