@fanalis/narrative
v0.1.0
Published
Local LLM narrative orchestrator. Auto-detects Ollama, falls back to transformers.js (Qwen ONNX) if installed.
Readme
@fanalis/narrative
local LLM narrative orchestrator. v1: ollama only. no cloud fallback.
what it does
detectLLM()— probesOLLAMA_HOST(defaulthttp://127.0.0.1:11434) within 600 ms. if alive, returns the highest-priority installed model from a preference list (qwen2.5:1.5b → 3b → 7b → llama3-instruct → mistral-instruct → phi).createLLMRunner({backend, model})→LLMRunner— currently only the ollama branch is wired. transformers-js detection succeeds atdetectLLMlevel but the runner factory returns null (post-v1).narrateJson<T>(llm, systemHint, userPrompt, schemaDescription, opts)— JSON-only prompt with code-fence stripping; parses + returns typed T or null on parse failure (callers fall back to deterministic output).narrateText(llm, systemHint, userPrompt, opts)— plain-narrative prompt.
how it's used
the CLI's audit command calls prepareLLM(flags, tier) at T≥3 and threads the resulting LLMRunner into the orchestrator via runAudit({llm}). pillars that want LLM input read opts.llm in their runAtTier body.
current LLM users:
@fanalis/pillar-aiseo— callsnarrateJson<GapVerdict>for the summarization-gap check (asks the model "what would a user expect to learn here vs what's actually answered?").@fanalis/pillar-conversion— callsnarrateTextfor the per-route conversion narrative (FCRS stage commentary).@fanalis/cli— callssummarizeAudit(llm, {target, composite, top})at the end of an audit for a 1-paragraph diagnosis.
prompt discipline
narrateJson's system hint always tells the model: "Output JSON only, conforming to this shape: ... Do not include code fences, prose, or explanation." cleaning step strips ``````json fences anyway because models still sometimes wrap.
narrateText keeps the system hint terse and uses maxTokens: 320, temperature: 0.4 by default. callers override per use.
what we removed
- gemini backend (v0 had
@google/genai). v1 ships zero paid-api deps; ifGEMINI_API_KEYis set, narrative just doesn't fire. - the "transformers-js fallback" hand-wave. detect still surfaces transformers-js as available if
@huggingface/transformersis installed, butcreateLLMRunnerreturns null for that backend until a real runner ships post-v1.
not in scope
- vision LLM (LLaVA-Critic etc) — registered as post-v1 in
@fanalis/models/registry.ts. - streaming completions —
llm.complete()is request/response. - multi-turn conversation — every call is fresh, stateless.
