148 lines
4.5 KiB
Python
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,
|
|
)
|