Spaces:
Running on Zero
Running on Zero
| """LightDiffusion Settings persistence and history store. | |
| This module provides a small JSON-backed settings store used for: | |
| - persisting the last used seed (previously include/last_seed.txt) | |
| - maintaining a short history of saved generation settings (for UI) | |
| - storing server-wide generation preferences that should survive restarts | |
| Design notes: | |
| - Store file defaults to './include/settings_store.json'. Override via | |
| environment variable LD_SETTINGS_STORE_PATH for tests or custom locations. | |
| - migrate_from_last_seed_txt() will import the legacy include/last_seed.txt | |
| if present and then remove the legacy file. | |
| - Reads/writes are atomic (write to temp file then replace). | |
| This is intentionally lightweight (no DB dependency) and robust to failure. | |
| """ | |
| from __future__ import annotations | |
| import json | |
| import os | |
| import time | |
| import tempfile | |
| import uuid | |
| from typing import Any, Dict, List, Optional | |
| def _default_preferences() -> Dict[str, bool]: | |
| return { | |
| "torch_compile": False, | |
| "vae_autotune": False, | |
| } | |
| def _default_store() -> Dict[str, Any]: | |
| return { | |
| "last_seed": None, | |
| "history": [], | |
| "preferences": _default_preferences(), | |
| } | |
| def _get_store_path() -> str: | |
| env = os.environ.get("LD_SETTINGS_STORE_PATH") | |
| if env: | |
| return env | |
| return os.path.join(os.getcwd(), "include", "settings_store.json") | |
| def _read_store() -> Dict[str, Any]: | |
| path = _get_store_path() | |
| try: | |
| if not os.path.exists(path): | |
| return _default_store() | |
| with open(path, "r", encoding="utf-8") as f: | |
| raw = json.load(f) | |
| if not isinstance(raw, dict): | |
| return _default_store() | |
| data = _default_store() | |
| data.update(raw) | |
| history = raw.get("history") | |
| data["history"] = history if isinstance(history, list) else [] | |
| seed = raw.get("last_seed") | |
| data["last_seed"] = int(seed) if seed is not None else None | |
| raw_preferences = raw.get("preferences") | |
| if not isinstance(raw_preferences, dict): | |
| raw_preferences = {} | |
| data["preferences"] = { | |
| "torch_compile": bool(raw_preferences.get("torch_compile", False)), | |
| "vae_autotune": bool(raw_preferences.get("vae_autotune", False)), | |
| } | |
| return data | |
| except Exception: | |
| # Corrupt/invalid file -> return sane default (do not raise) | |
| return _default_store() | |
| def _write_store(data: Dict[str, Any]) -> None: | |
| path = _get_store_path() | |
| ddir = os.path.dirname(path) | |
| os.makedirs(ddir, exist_ok=True) | |
| # atomic write | |
| fd, tmp = tempfile.mkstemp(prefix="settings_store_", dir=ddir) | |
| try: | |
| with os.fdopen(fd, "w", encoding="utf-8") as f: | |
| json.dump(data, f, indent=2, ensure_ascii=False) | |
| os.replace(tmp, path) | |
| finally: | |
| try: | |
| if os.path.exists(tmp): | |
| os.remove(tmp) | |
| except Exception: | |
| pass | |
| # Public API --------------------------------------------------------------- | |
| def get_last_seed() -> Optional[int]: | |
| """Return the persisted last-seed or None if not set.""" | |
| data = _read_store() | |
| seed = data.get("last_seed") | |
| return int(seed) if seed is not None else None | |
| def get_preferences() -> Dict[str, bool]: | |
| """Return persisted server-wide generation preferences.""" | |
| data = _read_store() | |
| prefs = data.get("preferences") or {} | |
| defaults = _default_preferences() | |
| return { | |
| "torch_compile": bool(prefs.get("torch_compile", defaults["torch_compile"])), | |
| "vae_autotune": bool(prefs.get("vae_autotune", defaults["vae_autotune"])), | |
| } | |
| def set_preferences(preferences: Dict[str, Any]) -> Dict[str, bool]: | |
| """Persist server-wide generation preferences and return the stored values.""" | |
| try: | |
| stored = { | |
| "torch_compile": bool(preferences.get("torch_compile", False)), | |
| "vae_autotune": bool(preferences.get("vae_autotune", False)), | |
| } | |
| d = _read_store() | |
| d["preferences"] = stored | |
| _write_store(d) | |
| return stored | |
| except Exception: | |
| return get_preferences() | |
| def set_last_seed(seed: int) -> None: | |
| """Persist the provided seed (int). | |
| Also ensures the store file exists and retains existing history. | |
| """ | |
| try: | |
| d = _read_store() | |
| d["last_seed"] = int(seed) | |
| _write_store(d) | |
| except Exception: | |
| # Best-effort only; never raise for caller convenience | |
| pass | |
| def get_history() -> List[Dict[str, Any]]: | |
| """Return the settings history (most-recent-first).""" | |
| data = _read_store() | |
| hist = data.get("history") or [] | |
| # Ensure sensible return type | |
| return list(reversed(hist)) if hist else [] | |
| def append_snapshot(snapshot: Dict[str, Any], max_len: int = 64) -> Dict[str, Any]: | |
| """Append a snapshot to the history and return the stored entry. | |
| The incoming `snapshot` should be a dict (typically containing a | |
| `settings` key). We enrich it with `id` and `ts` fields. | |
| """ | |
| try: | |
| d = _read_store() | |
| hist = d.get("history") or [] | |
| entry = { | |
| "id": uuid.uuid4().hex[:12], | |
| "ts": int(time.time()), | |
| **snapshot, | |
| } | |
| # Store newest at the end for compact append logic | |
| hist.append(entry) | |
| # Trim oldest if exceeding max_len | |
| if len(hist) > max_len: | |
| hist = hist[-max_len:] | |
| d["history"] = hist | |
| _write_store(d) | |
| return entry | |
| except Exception: | |
| return {"id": uuid.uuid4().hex[:12], "ts": int(time.time()), **snapshot} | |
| def migrate_from_last_seed_txt() -> Optional[int]: | |
| """Migrate legacy `include/last_seed.txt` into the JSON store. | |
| Migration target directory is derived from the store path so tests can | |
| override the store location with LD_SETTINGS_STORE_PATH and provide a | |
| corresponding legacy file next to it. | |
| """ | |
| store_path = _get_store_path() | |
| basedir = os.path.dirname(store_path) | |
| legacy = os.path.join(basedir, "last_seed.txt") | |
| if not os.path.exists(legacy): | |
| return None | |
| try: | |
| with open(legacy, "r", encoding="utf-8") as f: | |
| raw = f.read().strip() | |
| if not raw: | |
| return None | |
| seed = int(raw) | |
| set_last_seed(seed) | |
| try: | |
| os.remove(legacy) | |
| except Exception: | |
| pass | |
| return seed | |
| except Exception: | |
| return None | |