Bot & Automation

AI Assistants

/root/hermes-projects/AI Assistants

apps/whatsapp_control_api/app/core/config.py text
from __future__ import annotations

import os
from dataclasses import dataclass
from functools import lru_cache
from pathlib import Path


def _to_bool(value: str | None, default: bool = False) -> bool:
    if value is None:
        return default
    return value.strip().lower() in {"1", "true", "yes", "on"}


def _resolve_sqlite_path(database_url: str) -> Path:
    prefix = "sqlite:///"
    if not database_url.startswith(prefix):
        raise ValueError("Only sqlite:/// DATABASE_URL is supported in this MVP.")
    raw_path = database_url[len(prefix) :]
    path = Path(raw_path)
    if not path.is_absolute():
        path = Path.cwd() / path
    return path


@dataclass(slots=True)
class Settings:
    app_env: str
    app_host: str
    app_port: int
    log_level: str
    meta_app_id: str
    meta_app_secret: str
    meta_business_id: str
    meta_waba_id: str
    meta_phone_number_id: str
    meta_access_token: str
    meta_verify_token: str
    meta_graph_api_version: str
    whatsapp_webhook_url: str
    webhook_signature_required: bool
    database_url: str
    database_path: Path
    hermes_command: str
    hermes_args: str
    hermes_workspace_root: Path
    hermes_projects_root: Path
    task_logs_dir: Path
    task_artifacts_dir: Path
    gateway_registry_tasks_dir: Path
    hermes_task_timeout_seconds: int
    task_progress_throttle_seconds: int
    whatsapp_allowed_numbers: set[str]
    outbound_rate_limit_per_minute: int
    approval_code_ttl_seconds: int
    max_outbound_text_length: int
    chat_enabled: bool
    chat_provider: str
    chat_base_url: str
    chat_model: str
    chat_api_key: str
    chat_temperature: float
    chat_max_tokens: int
    web_search_enabled: bool
    web_search_provider: str
    web_search_api_key: str
    web_search_base_url: str
    web_search_max_results: int
    web_search_timeout_seconds: float
    dashboard_admin_token: str
    gateway_api_token: str


@lru_cache(maxsize=1)
def get_settings() -> Settings:
    database_url = os.getenv("DATABASE_URL", "sqlite:///./data/control_layer.db")
    database_path = _resolve_sqlite_path(database_url)

    task_logs_dir = Path(os.getenv("TASK_LOGS_DIR", "./data/tasks"))
    task_artifacts_dir = Path(os.getenv("TASK_ARTIFACTS_DIR", "./data/artifacts"))
    gateway_registry_tasks_dir = Path(
        os.getenv("HERMES_GATEWAY_REGISTRY_TASKS_DIR", "/root/hermes-projects/.hermes-registry/tasks")
    )

    if not task_logs_dir.is_absolute():
        task_logs_dir = Path.cwd() / task_logs_dir
    if not task_artifacts_dir.is_absolute():
        task_artifacts_dir = Path.cwd() / task_artifacts_dir
    if not gateway_registry_tasks_dir.is_absolute():
        gateway_registry_tasks_dir = Path.cwd() / gateway_registry_tasks_dir

    allowed_numbers = {
        "".join(ch for ch in item if ch.isdigit())
        for item in os.getenv("WHATSAPP_ALLOWED_NUMBERS", "").split(",")
        if item.strip()
    }

    return Settings(
        app_env=os.getenv("APP_ENV", "production"),
        app_host=os.getenv("APP_HOST", "0.0.0.0"),
        app_port=int(os.getenv("APP_PORT", "8040")),
        log_level=os.getenv("LOG_LEVEL", "INFO").upper(),
        meta_app_id=os.getenv("META_APP_ID", "").strip(),
        meta_app_secret=os.getenv("META_APP_SECRET", "").strip(),
        meta_business_id=os.getenv("META_BUSINESS_ID", "").strip(),
        meta_waba_id=os.getenv("META_WABA_ID", "").strip(),
        meta_phone_number_id=os.getenv("META_PHONE_NUMBER_ID", "").strip(),
        meta_access_token=os.getenv("META_ACCESS_TOKEN", "").strip(),
        meta_verify_token=os.getenv("META_VERIFY_TOKEN", "").strip(),
        meta_graph_api_version=os.getenv("META_GRAPH_API_VERSION", "v23.0").strip(),
        whatsapp_webhook_url=os.getenv("WHATSAPP_WEBHOOK_URL", "").strip(),
        webhook_signature_required=_to_bool(os.getenv("WEBHOOK_SIGNATURE_REQUIRED"), True),
        database_url=database_url,
        database_path=database_path,
        hermes_command=os.getenv("HERMES_COMMAND", "/usr/local/bin/hermes").strip(),
        hermes_args=os.getenv("HERMES_ARGS", "").strip(),
        hermes_workspace_root=Path(os.getenv("HERMES_WORKSPACE_ROOT", "/root")),
        hermes_projects_root=Path(os.getenv("HERMES_PROJECTS_ROOT", "/root/hermes-projects")),
        task_logs_dir=task_logs_dir,
        task_artifacts_dir=task_artifacts_dir,
        gateway_registry_tasks_dir=gateway_registry_tasks_dir,
        hermes_task_timeout_seconds=int(os.getenv("HERMES_TASK_TIMEOUT_SECONDS", "3600")),
        task_progress_throttle_seconds=int(os.getenv("TASK_PROGRESS_THROTTLE_SECONDS", "20")),
        whatsapp_allowed_numbers=allowed_numbers,
        outbound_rate_limit_per_minute=int(os.getenv("OUTBOUND_RATE_LIMIT_PER_MINUTE", "20")),
        approval_code_ttl_seconds=int(os.getenv("APPROVAL_CODE_TTL_SECONDS", "900")),
        max_outbound_text_length=int(os.getenv("MAX_OUTBOUND_TEXT_LENGTH", "4096")),
        chat_enabled=_to_bool(os.getenv("CHAT_ENABLED"), True),
        chat_provider=os.getenv("CHAT_PROVIDER", "local").strip().lower(),
        chat_base_url=os.getenv("CHAT_BASE_URL", "https://openrouter.ai/api/v1").strip().rstrip("/"),
        chat_model=os.getenv("CHAT_MODEL", "").strip(),
        chat_api_key=os.getenv("CHAT_API_KEY", "").strip(),
        chat_temperature=float(os.getenv("CHAT_TEMPERATURE", "0.4")),
        chat_max_tokens=int(os.getenv("CHAT_MAX_TOKENS", "450")),
        web_search_enabled=_to_bool(os.getenv("WEB_SEARCH_ENABLED"), True),
        web_search_provider=os.getenv("WEB_SEARCH_PROVIDER", "bing").strip().lower(),
        web_search_api_key=os.getenv("WEB_SEARCH_API_KEY", "").strip(),
        web_search_base_url=os.getenv("WEB_SEARCH_BASE_URL", "").strip().rstrip("/"),
        web_search_max_results=int(os.getenv("WEB_SEARCH_MAX_RESULTS", "5")),
        web_search_timeout_seconds=float(os.getenv("WEB_SEARCH_TIMEOUT_SECONDS", "12")),
        dashboard_admin_token=os.getenv("DASHBOARD_ADMIN_TOKEN", "").strip(),
        gateway_api_token=os.getenv("HERMES_GATEWAY_API_TOKEN", os.getenv("DASHBOARD_ADMIN_TOKEN", "")).strip(),
    )