mirror of
https://github.com/rookiestar28/ComfyUI-OpenClaw.git
synced 2026-08-14 00:48:07 +00:00
153 lines
4.8 KiB
Python
153 lines
4.8 KiB
Python
"""
|
|
Persisted config storage and tenant-resolution helpers for runtime_config.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import logging
|
|
import os
|
|
from typing import Any, Dict, Optional
|
|
|
|
try:
|
|
from .runtime_guardrails import strip_runtime_only_config_fields
|
|
except ImportError:
|
|
from services.runtime_guardrails import (
|
|
strip_runtime_only_config_fields, # type: ignore
|
|
)
|
|
|
|
try:
|
|
from .tenant_context import (
|
|
DEFAULT_TENANT_ID,
|
|
get_current_tenant_id,
|
|
is_multi_tenant_enabled,
|
|
normalize_tenant_id,
|
|
)
|
|
except ImportError:
|
|
try:
|
|
from services.tenant_context import ( # type: ignore
|
|
DEFAULT_TENANT_ID,
|
|
get_current_tenant_id,
|
|
is_multi_tenant_enabled,
|
|
normalize_tenant_id,
|
|
)
|
|
except ImportError:
|
|
DEFAULT_TENANT_ID = "default"
|
|
|
|
def get_current_tenant_id(): # type: ignore
|
|
return DEFAULT_TENANT_ID
|
|
|
|
def is_multi_tenant_enabled(): # type: ignore
|
|
return False
|
|
|
|
def normalize_tenant_id(value): # type: ignore
|
|
return str(value or DEFAULT_TENANT_ID).strip().lower() or DEFAULT_TENANT_ID
|
|
|
|
|
|
def get_default_config_file() -> str:
|
|
try:
|
|
# CRITICAL: keep path resolution import-safe; do not call get_state_dir()
|
|
# here or plain imports recreate state on disk during bootstrap/tests.
|
|
from .state_dir import peek_state_dir
|
|
|
|
return os.path.join(peek_state_dir(), "config.json")
|
|
except ImportError:
|
|
try:
|
|
from services.state_dir import peek_state_dir # type: ignore
|
|
|
|
return os.path.join(peek_state_dir(), "config.json")
|
|
except ImportError:
|
|
return os.path.join(
|
|
os.path.dirname(os.path.dirname(__file__)),
|
|
"data",
|
|
"config.json",
|
|
)
|
|
|
|
|
|
def resolve_active_tenant_id(tenant_id: Optional[str] = None) -> str:
|
|
if not is_multi_tenant_enabled():
|
|
return DEFAULT_TENANT_ID
|
|
if tenant_id is None:
|
|
tenant_id = get_current_tenant_id()
|
|
try:
|
|
return normalize_tenant_id(tenant_id)
|
|
except Exception:
|
|
return DEFAULT_TENANT_ID
|
|
|
|
|
|
def get_runtime_override_section(tenant_id: Optional[str] = None) -> str:
|
|
resolved = resolve_active_tenant_id(tenant_id)
|
|
if resolved == DEFAULT_TENANT_ID:
|
|
return "llm"
|
|
return f"llm::{resolved}"
|
|
|
|
|
|
def tenant_llm_config_view(
|
|
config_blob: Dict[str, Any], tenant_id: str
|
|
) -> Dict[str, Any]:
|
|
llm_global = config_blob.get("llm", {})
|
|
if tenant_id == DEFAULT_TENANT_ID:
|
|
return llm_global if isinstance(llm_global, dict) else {}
|
|
|
|
tenants = config_blob.get("tenants", {})
|
|
tenant_cfg = {}
|
|
if isinstance(tenants, dict):
|
|
tenant_cfg = tenants.get(tenant_id, {})
|
|
tenant_llm = tenant_cfg.get("llm", {}) if isinstance(tenant_cfg, dict) else {}
|
|
if isinstance(tenant_llm, dict) and tenant_llm:
|
|
return tenant_llm
|
|
if _allow_tenant_config_fallback() and isinstance(llm_global, dict):
|
|
return llm_global
|
|
return {}
|
|
|
|
|
|
def load_file_config(config_file: str, *, logger: logging.Logger) -> Dict[str, Any]:
|
|
if os.path.exists(config_file):
|
|
try:
|
|
with open(config_file, "r", encoding="utf-8") as fh:
|
|
raw = json.load(fh)
|
|
if isinstance(raw, dict):
|
|
sanitized, notices = strip_runtime_only_config_fields(raw)
|
|
if notices:
|
|
logger.warning(
|
|
"S66: Ignoring runtime-only guardrail keys from persisted config (%s)",
|
|
", ".join(n.get("path", "?") for n in notices),
|
|
)
|
|
return sanitized
|
|
return {}
|
|
except (json.JSONDecodeError, OSError) as exc:
|
|
logger.warning("Failed to load config file: %s", exc)
|
|
return {}
|
|
|
|
|
|
def save_file_config(
|
|
config_file: str,
|
|
config: Dict[str, Any],
|
|
*,
|
|
logger: logging.Logger,
|
|
) -> bool:
|
|
try:
|
|
config_to_save, notices = strip_runtime_only_config_fields(config)
|
|
if notices:
|
|
logger.warning(
|
|
"S66: Stripped runtime-only guardrail keys before config save (%s)",
|
|
", ".join(n.get("path", "?") for n in notices),
|
|
)
|
|
os.makedirs(os.path.dirname(config_file), exist_ok=True)
|
|
with open(config_file, "w", encoding="utf-8") as fh:
|
|
json.dump(config_to_save, fh, indent=2)
|
|
logger.info("Saved config to %s", config_file)
|
|
return True
|
|
except OSError as exc:
|
|
logger.error("Failed to save config file: %s", exc)
|
|
return False
|
|
|
|
|
|
def _allow_tenant_config_fallback() -> bool:
|
|
value = (
|
|
os.environ.get("OPENCLAW_MULTI_TENANT_ALLOW_CONFIG_FALLBACK")
|
|
or os.environ.get("MOLTBOT_MULTI_TENANT_ALLOW_CONFIG_FALLBACK")
|
|
or "0"
|
|
)
|
|
return str(value).strip().lower() in ("1", "true", "yes", "on")
|