Skip to main content

narratoflow

PyPI version Python versions CI License: Apache 2.0

Tags: llm · prompt-compression · token-optimization · cost-reduction · anthropic · openai · ollama · claude · gpt · pydantic · python · multilingual · spacy · async · rag · narrative-generation · context-window · apache-2.0

Compress huge LLM input context into dense intermediate representations. Pay fewer tokens, keep the meaning.

Docs: https://Mrrobi.github.io/narratoflow/ · PyPI: https://pypi.org/project/narratoflow/ · Source: https://github.com/Mrrobi/narratoflow

narratoflow (PyPI name; import as narrato) is an open-source Python library (Apache-2.0) for shrinking long source text before sending it to an LLM. It targets any workload where the input dwarfs the output and tokens are the dominant cost — RAG retrieval contexts, narrative generation, transcript summarisation, long-document QA.

The library has a generic, language- and domain-neutral core. Common starting points ship as named profiles (rag-en, narrative-no, news-en, …) so you do not have to choose every argument up front.


Highlights

  • 43% token reduction on a real Norwegian narrative sample (gpt-4o-mini extractor → gpt-4o target), with 8/10 quality from an LLM judge
  • 3 providers out of the box — Anthropic, OpenAI, Ollama (local models, no API key). Bring your own via a 3-method Protocol.
  • Async API — Compressor.acompress(...) runs chunked extraction concurrently via asyncio.gather
  • Layered design — pick free deterministic layers, an LLM-backed semantic layer, or both
  • Schema-driven — define a Pydantic model, get a dense JSON payload in return; 5 presets built in (narrative, qa, interview, dialogue, news)
  • Long-document ready — automatic chunked map-reduce extraction with overlap-aware merging
  • Anthropic prompt caching — opt-in via Compressor(cache=True); OpenAI's automatic cache is reported on the response
  • 12 bundled languages — stopwords for en, no, de, fr, es, it, pt, nl, sv, da, fi, pl; auto-detect via source_lang="auto"
  • Optional spaCy preprocessing — POS-aware token stripping that keeps named entities verbatim
  • Typed (PEP 561) — py.typed marker; works directly with mypy / pyright / IDEs
  • Named profiles — Compressor.from_profile("rag-en") for one-line setup; register your own
  • uv ready — pure-PEP-621 hatchling package; uv add narratoflow, uv build, uvx all work

Why

LLM input is priced per token, and a long source document — say a 20-page transcript that feeds a 200-word summary — burns most of the budget before the model has written anything.

narrato lets you trade a tiny bit of fidelity for a large reduction in input tokens by passing your downstream LLM a dense, machine-friendly representation instead of the raw text. The intermediate representation does not need to be human-readable. It just needs to be:

  1. Cheap to produce.
  2. Decodable by the downstream LLM into a faithful output.
  3. Smaller in tokens than the original.

Architecture

                   ┌──────────────────────────────────────────────┐
   raw text  ──▶   │  L1 preprocess        (deterministic, free)   │
                   │  L2 codebook          (deterministic, free)   │
                   │  L3 semantic extract  (small LLM call)        │
                   │  L4 learned encoder   (planned, v0.6+)        │
                   └──────────────────────────────────────────────┘
                                      │
                                      ▼
                          CompressionResult
                          (payload + legend + stats)
                                      │
                                      ▼
                   ┌──────────────────────────────────────────────┐
                   │  Decoder.unpack_prompt()                      │
                   │  → ready-to-send prompt for downstream LLM    │
                   └──────────────────────────────────────────────┘

Pick which layers run for each call. Free layers stack with paid layers.

Install

pip install narratoflow
# or
uv add narratoflow

Optional extras:

pip install "narratoflow[lang]"       # langdetect, for source_lang="auto"
pip install "narratoflow[nlp]"        # spaCy, for POS-aware preprocessing
pip install "narratoflow[dev]"        # pytest, ruff, mypy
pip install "narratoflow[docs]"       # mkdocs-material to build the site
pip install "narratoflow[benchmark]"  # rich + tabulate for the bench CLI

Set credentials for the provider you use:

export ANTHROPIC_API_KEY=sk-ant-...
export OPENAI_API_KEY=sk-...
# Ollama: no key — just `ollama pull <model>` and start the daemon.

Quick start

from narrato import Compressor

c = Compressor.from_profile("rag-en", provider="anthropic")
result = c.compress(long_source_text)

print(result.stats)
# {'input_tokens': 8421, 'output_tokens': 1102, 'ratio': 0.131, ...}

Run narratoflow profiles to list all built-in profiles, or register your own:

from narrato import Compressor, Profile, register_profile

register_profile(Profile(
    name="legal-en",
    description="English legal documents — chunked + cached",
    source_lang="en",
    schema="qa",
    chunked=True,
    chunk_chars=6000,
    extra={"cache": True},
))

c = Compressor.from_profile("legal-en", provider="anthropic")

Async + concurrent chunked extraction

import asyncio
from narrato import Compressor

c = Compressor.from_profile("long-en", provider="openai")
result = asyncio.run(c.acompress(very_long_document, concurrency=8))

Local models via Ollama

from narrato import Compressor

c = Compressor.from_profile(
    "rag-en",
    provider="ollama",
    extractor_model="llama3",
    target_model="llama3",
)
result = c.compress(text)

Auto-detect language

c = Compressor.from_profile("rag-en", provider="anthropic")
c.source_lang = "auto"           # langdetect if installed, heuristic otherwise

result = c.compress(any_language_document)
print(result.stats["resolved_lang"])     # 'en' / 'no' / 'de' / ...

Explicit construction

from narrato import Compressor, Decoder

c = Compressor(
    source_lang="no",
    provider="anthropic",
    extractor_model="claude-haiku-4-5-20251001",
    target_model="claude-opus-4-7",
    layers=["preprocess", "codebook", "extract"],
    schema="narrative",
)

result = c.compress(long_norwegian_text)

prompt = Decoder.unpack_prompt(
    result,
    instruction="Skriv en kort fortelling basert på faktene over.",
)
# Send `prompt` to your target LLM.

CLI

narratoflow profiles
narratoflow schemas
narratoflow compress doc.txt --profile rag-en --out compressed.json
narratoflow eval doc.txt --target-task "Write a 200-word summary." --profile rag-en

The eval command reports tokens_in, tokens_out, ratio, estimated cost savings, and an LLM-judge quality score (skip with --skip-quality).

Layer reference

Layer What it does Cost Loss
preprocess Whitespace/punct normalize, near-duplicate sentence dedupe, stopword or spaCy-POS stripping free tiny
codebook Frequent phrase → short §x code, emit legend; tokenizer-aware savings free none (with legend)
extract Small LLM extracts schema-conformant facts; chunked + concurrent for long docs cheap LLM call lossy by design
learned (v0.6+) Fine-tuned encoder produces dense codes one-time train tunable

Schemas

Schemas tell the extractor what to keep. Built-in presets:

preset for
narrative story / fiction. Characters, setting, ordered events, themes, tone, verbatim quotes.
qa fact extraction / RAG. Summary, entities, dates, claims.
interview interview / transcript. Interviewer + interviewee, ordered turns, key points, sentiment.
dialogue scripted / fictional dialogue. Participants, setting, ordered lines, arc, notable quotes.
news news article. Headline, lede, 5W1H (who/what/when/where/why/how), sources, quotes.

Define your own — any Pydantic v2 BaseModel:

from pydantic import BaseModel, Field
from narrato import Compressor

class MyFacts(BaseModel):
    summary: str
    speakers: list[str] = Field(default_factory=list)
    key_dates: list[str] = Field(default_factory=list)

c = Compressor(schema=MyFacts, provider="anthropic")

Providers

provider sync async JSON mode prompt cache
Anthropic ✅ ✅ tool-use opt-in (cache=True)
OpenAI ✅ ✅ response_format JSON schema automatic (≥1024 tok), reported on cached_input_tokens
Ollama ✅ ✅ format=json + schema reminder n/a
Mock (tests) ✅ ✅ canned payloads n/a

Bring your own — implement complete + complete_json (and optionally acomplete*):

from narrato.providers import Provider, ProviderResponse

class MyProvider:
    name = "myco"

    def complete(self, system, user, model, max_tokens=2048, temperature=0.0):
        text = call_my_api(system, user, model)
        return ProviderResponse(text=text, input_tokens=0, output_tokens=0, model=model)

    def complete_json(self, system, user, model, schema=None, max_tokens=2048, temperature=0.0):
        ...

c = Compressor(provider=MyProvider(), ...)

Documentation

Full docs are at https://Mrrobi.github.io/narratoflow/. Highlights:

Roadmap

  • v0.1 — layered preprocess + codebook + schema extract, Anthropic + OpenAI, CLI, eval harness
  • v0.2 — chunked map-reduce extraction, Anthropic prompt caching, 3 new schema presets, MockProvider, tokenizer-aware codebook
  • v0.3 — generic-core refactor, named profiles, 12-language stopword bundle, CLI --profile
  • v0.4 — Ollama provider, async API, OpenAI prompt-cache reporting, py.typed, uv build verified, expanded docs
  • v0.5 — optional spaCy preprocessing, language auto-detect, mypy CI job
  • v0.6 — learned encoder R&D, HF Spaces demo, multilingual benchmark corpora, more language stopword sets

See CHANGELOG.md for full release notes.

Contributing

PRs welcome. Bring your own benchmark.

Local dev:

git clone https://github.com/Mrrobi/narratoflow.git
cd narratoflow
pip install -e ".[dev]"
pytest -q
mypy narrato
ruff check narrato tests
mkdocs serve     # http://127.0.0.1:8000

License

Apache-2.0. See LICENSE.

Metadata

Release files for narratoflow 0.5.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for narratoflow 0.5.1
File Size Uploaded
narratoflow-0.5.1.tar.gz 80.6 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for narratoflow 0.5.1
File Interpreter ABI Platform
narratoflow-0.5.1-py3-none-any.whl Python 3 none any Details

Total release size: 133.3 kB

Release files / narratoflow-0.5.1.tar.gz

Download URL narratoflow-0.5.1.tar.gz
Size 80.6 kB
Tags Source
SHA-256 checksum
How to use checksums
56725e9c7504942e75e455a654fa7f6f6bb23bc020bde3590eb81e4b247f7b03
BLAKE2b-256 checksum
How to use checksums
5b0d9b44e138d88971b196007d353a0ceabfe22bbf5d54d145cb99d061a7dc23
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on May 25, 2026.

Transparency log

Release files / narratoflow-0.5.1-py3-none-any.whl

Download URL narratoflow-0.5.1-py3-none-any.whl
Size 52.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
fded9fd753e094c3c278cf1035efd513ea176645e0dec91fa94a700c8af40ea5
BLAKE2b-256 checksum
How to use checksums
0b0f14e153ff1d7b8a260f9f0522c47fc1b008ac480561e581371e9baa908a2e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on May 25, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.5.1 This release

2 release files

0.5.0

2 release files

0.4.0

2 release files

0.3.0

2 release files

0.2.0

2 release files

0.1.3

2 release files

0.1.2

2 release files

0.1.1

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page