"""Narrator backend selector — ONE seam, two implementations. `get_narrator()` returns a client implementing the same prose-only interface the rest of the app already depends on (`.chat`, `.chat_with_tools`, `.wait_until_ready`, `.model_id`). It chooses the backend from the `HER_BACKEND` env var: * `llama` (default) — the local llama.cpp `llama-server` client (unchanged local app; trace content never leaves the machine). * `hf` — an in-process transformers model running on a Hugging Face ZeroGPU GPU via `@spaces.GPU` (the Space deployment). This is the ONLY place that knows which backend is live, so server/app.py stays backend-agnostic and the local product is untouched. Still prose-only at the seam: neither backend reads JSONL or computes a number. """ from __future__ import annotations import os def get_narrator(): """Return the active narrator client for this process (see module docstring).""" backend = os.environ.get("HER_BACKEND", "llama").lower() if backend == "hf": # The HF backend loads its model at MODULE import (ZeroGPU requirement), so # return the already-loaded singleton rather than constructing per call. from narrator.hf_narrator import NARRATOR return NARRATOR from narrator.client import NarratorClient return NarratorClient()