File size: 2,932 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
"""
FastAPI router for the interactive service.

Batch 7/8 — the router composes the authoring + planner + play
sub-routers from the ``routes`` subpackage. When
``InteractiveConfig.enabled=False`` the router is deliberately
empty so the mount line in ``app/main.py`` is safe regardless of
the runtime flag.

Contract with ``app/main.py``: a single call to
``build_router(load_config())`` returns an ``APIRouter`` ready to
include. The router is safe to include unconditionally — when
disabled it simply has no paths.
"""
from __future__ import annotations

from fastapi import APIRouter

from .config import InteractiveConfig
from .errors import InteractiveError
from .routes import build_all


def build_router(cfg: InteractiveConfig) -> APIRouter:
    """Build the interactive service router.

    ``cfg.enabled=False`` → empty router (no paths, no OpenAPI
    surface). ``cfg.enabled=True`` → health ping + authoring +
    planner + play endpoints.
    """
    router = APIRouter(prefix="/v1/interactive", tags=["interactive"])

    if not cfg.enabled:
        # Deliberately empty. Keeps the mount line in ``main.py``
        # idempotent regardless of the runtime flag.
        return router

    @router.get("/health")
    def health() -> dict:
        """Liveness + configuration readout for the interactive service.

        Includes the playback feature flags so the wizard can honestly
        surface "render is disabled" warnings instead of showing a
        fake full progress bar while every scene silently skips.
        """
        from .playback.playback_config import load_playback_config
        pcfg = load_playback_config()
        return {
            "ok": True,
            "service": "interactive",
            "enabled": True,
            "limits": {
                "max_branches": cfg.max_branches,
                "max_depth": cfg.max_depth,
                "max_nodes_per_experience": cfg.max_nodes_per_experience,
            },
            "chassis_guardrails": {
                "require_consent_for_mature": cfg.require_consent_for_mature,
                "enforce_region_block": cfg.enforce_region_block,
                "moderate_mature_narration": cfg.moderate_mature_narration,
                "blocked_regions": len(cfg.region_block),
            },
            "runtime_latency_target_ms": cfg.runtime_latency_target_ms,
            # Playback flags — surfaced so the wizard + editor can
            # show accurate "rendering is off" hints instead of
            # faking progress when the scene-render pipeline is
            # disabled by env config.
            "playback": {
                "llm_enabled": pcfg.llm_enabled,
                "render_enabled": pcfg.render_enabled,
                "render_workflow": pcfg.render_workflow,
                "image_workflow": pcfg.image_workflow,
            },
        }

    router.include_router(build_all(cfg))
    return router