Files
2026-05-14 19:12:14 +08:00

78 lines
2.8 KiB
Python

"""System-wide runtime settings editable from the admin UI.
Backed by a single JSON file under the DeerFlow runtime home (default
``.deer-flow/system_settings.json``). Unlike :mod:`extensions_config` this
file is **not** part of the project source tree — it is per-deployment
state, and it stays writable at runtime.
The only feature stored here at the moment is the **prompt prefix toggle**
shown in the chat input: an administrator can configure a label and a
prefix string, and any user can flip the toggle on/off; when enabled, the
prefix is prepended to the first human message of a new thread.
"""
from __future__ import annotations
import json
import logging
import threading
from pathlib import Path
from pydantic import BaseModel, ConfigDict, Field
from deerflow.config.runtime_paths import runtime_home
logger = logging.getLogger(__name__)
_SETTINGS_FILE = "system_settings.json"
_lock = threading.Lock()
class PromptPrefixSettings(BaseModel):
"""Admin-configurable first-message prompt prefix."""
enabled: bool = Field(default=False, description="Whether the prefix toggle is offered to users at all")
switch_label: str = Field(default="加入立场前缀", description="Toggle label shown in the chat input")
prompt_prefix: str = Field(default="", description="Text prepended to the first user message when enabled")
model_config = ConfigDict(extra="ignore")
class SystemSettings(BaseModel):
"""Top-level system settings document."""
prompt_prefix: PromptPrefixSettings = Field(default_factory=PromptPrefixSettings)
model_config = ConfigDict(extra="ignore")
def _settings_path() -> Path:
home = runtime_home()
home.mkdir(parents=True, exist_ok=True)
return home / _SETTINGS_FILE
def load_system_settings() -> SystemSettings:
"""Read the on-disk system settings, returning defaults when missing."""
path = _settings_path()
if not path.is_file():
return SystemSettings()
try:
raw = json.loads(path.read_text(encoding="utf-8"))
if not isinstance(raw, dict):
logger.warning("System settings file %s is not a JSON object; ignoring", path)
return SystemSettings()
return SystemSettings.model_validate(raw)
except Exception as exc:
logger.warning("Failed to read system settings from %s: %s", path, exc)
return SystemSettings()
def save_system_settings(settings: SystemSettings) -> SystemSettings:
"""Persist the settings document atomically, returning the saved value."""
path = _settings_path()
payload = settings.model_dump(mode="json")
with _lock:
tmp = path.with_suffix(path.suffix + ".tmp")
tmp.write_text(json.dumps(payload, ensure_ascii=False, indent=2), encoding="utf-8")
tmp.replace(path)
return settings