File size: 4,204 Bytes
921d377 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 | """
Runtime settings for the interactive service.
All values read from env at ``load_config()`` time (not import time)
so tests and live reloads pick up overrides without restart churn.
Design rule: every flag has a safe default such that an unconfigured
install behaves identically to the pre-interactive codebase. Turning
``INTERACTIVE_ENABLED=false`` (the default) must keep the router
empty, the schema un-applied, and zero background work running.
"""
from __future__ import annotations
import os
from dataclasses import dataclass, field
from typing import List, Optional
def _env_bool(key: str, default: bool) -> bool:
raw = os.getenv(key, "").strip().lower()
if raw in {"1", "true", "yes", "on"}:
return True
if raw in {"0", "false", "no", "off"}:
return False
return default
def _env_int(key: str, default: int) -> int:
try:
return int(os.getenv(key, "").strip() or default)
except (TypeError, ValueError):
return default
def _env_str(key: str, default: str) -> str:
raw = os.getenv(key, "")
return raw.strip() if raw.strip() else default
def _env_list(key: str, default: Optional[List[str]] = None) -> List[str]:
"""Comma-separated list env var. Empty → default."""
raw = os.getenv(key, "").strip()
if not raw:
return list(default or [])
return [p.strip() for p in raw.split(",") if p.strip()]
@dataclass(frozen=True)
class InteractiveConfig:
"""Resolved runtime settings. Immutable after ``load_config``.
The ``enabled`` flag is the master switch. When false the router
returned by ``build_router()`` has no routes — the service
contributes zero paths to the OpenAPI schema. Nothing else in
this package should ever be executed.
"""
# Master switch. All other settings are irrelevant when False.
enabled: bool = False
# Structural safety caps on the branching graph. Planner refuses
# to emit graphs beyond these bounds; prevents exponential blowup.
max_branches: int = 12
max_depth: int = 6
max_nodes_per_experience: int = 200
# LLM model id used by the planner / script generator. Falls
# through to HomePilot's default Ollama model when unset.
llm_model: str = "llama3:8b"
# Where generated interactive artifacts live on disk. Empty
# string → reuse UPLOAD_DIR (standard HomePilot storage).
storage_root: str = ""
# Default-on chassis guardrails for mature-mode experiences.
# These cannot be disabled per-profile; they're the permanent
# safety net. Setting any to False requires editing source code.
require_consent_for_mature: bool = True
enforce_region_block: bool = True
moderate_mature_narration: bool = True
# Regions hard-blocked for mature content (ISO 3166-1 alpha-2
# codes). Checked BEFORE any profile-level rule. Empty → no
# blocks; admins configure via INTERACTIVE_REGION_BLOCK env.
region_block: List[str] = field(default_factory=list)
# p50 latency target for the runtime loop excluding asset
# generation. Not enforced — used by QA assertions only.
runtime_latency_target_ms: int = 200
def load_config() -> InteractiveConfig:
"""Resolve a fresh config from the current environment.
Called once at mount (see ``main.py``) and by tests that need a
per-test override via monkeypatch.
"""
return InteractiveConfig(
enabled=_env_bool("INTERACTIVE_ENABLED", False),
max_branches=_env_int("INTERACTIVE_MAX_BRANCHES", 12),
max_depth=_env_int("INTERACTIVE_MAX_DEPTH", 6),
max_nodes_per_experience=_env_int("INTERACTIVE_MAX_NODES", 200),
llm_model=_env_str("INTERACTIVE_LLM_MODEL", "llama3:8b"),
storage_root=_env_str("INTERACTIVE_STORAGE_ROOT", ""),
require_consent_for_mature=_env_bool("INTERACTIVE_REQUIRE_MATURE_CONSENT", True),
enforce_region_block=_env_bool("INTERACTIVE_ENFORCE_REGION_BLOCK", True),
moderate_mature_narration=_env_bool("INTERACTIVE_MODERATE_MATURE", True),
region_block=_env_list("INTERACTIVE_REGION_BLOCK", default=[]),
runtime_latency_target_ms=_env_int("INTERACTIVE_LATENCY_TARGET_MS", 200),
)
|