Skip to content

API reference

Generated from docstrings via mkdocstrings.

narrato.pipeline

narrato.pipeline

High-level orchestrator: Compressor chains layers and returns a CompressionResult. Decoder builds a ready-to-send prompt from the result.

Compressor dataclass

End-to-end context compressor.

Parameters

source_lang: ISO code for the source text language. Drives stopword selection. provider: "anthropic" or "openai" — used for the extractor LLM call. May also be a pre-constructed provider instance (useful for testing / injecting :class:narrato.providers.MockProvider). extractor_model: Cheap model used for L3 extraction. e.g. claude-haiku-4-5-20251001 or gpt-4o-mini. target_model: Model the downstream prompt will be sent to. Used only for token accounting in stats. layers: Subset of ("preprocess", "codebook", "extract") to run, in order. schema: Preset name or Pydantic model class. Only used if extract is in layers. preprocess_config / codebook_config: Override default configs for L1 / L2. cache: Enable provider-side prompt caching when supported (Anthropic only as of v0.2). Reduces cost on repeated calls within the cache TTL. chunked: Split very long source text into chunks for the extract layer and map-reduce the results. Triggers automatically when len(text) > chunk_chars even if this flag is False — set explicitly to force chunked mode for testing. chunk_chars / overlap_chars: Chunk size and overlap for chunked extraction.

from_profile classmethod

from_profile(profile: str, *, provider: str | Provider | None = None, extractor_model: str | None = None, target_model: str | None = None, **overrides: Any) -> Compressor

Construct a Compressor from a named profile.

Profile fields become defaults; provider, extractor_model, target_model and any keyword overrides are applied on top.

Compressor.from_profile("rag-en", provider="openai")

acompress async

acompress(text: str, *, concurrency: int = 4) -> CompressionResult

Async equivalent of :meth:compress.

Free layers run synchronously (they are pure CPU and fast); the extract layer uses the provider's async methods. When chunked mode is active the chunks are extracted concurrently with the given concurrency. Requires the configured provider to implement :class:narrato.providers.AsyncProvider.

CompressionResult dataclass

payload instance-attribute

payload: str

The compressed payload, ready to be embedded in a downstream prompt.

For extract runs this is a JSON object as a string. For runs without extract this is the rewritten text.

legend class-attribute instance-attribute

legend: dict[str, str] = field(default_factory=dict)

Codebook legend; empty when codebook layer is not used.

format class-attribute instance-attribute

format: str = 'text'

"text" or "json".

Decoder

Builds the final prompt to send to the downstream LLM.

unpack_prompt staticmethod

unpack_prompt(result: CompressionResult, instruction: str, *, target_lang: str | None = None) -> str

Return a single-string prompt embedding payload, legend, and instruction.

For Anthropic, this is suitable as the user message. The caller is free to split the legend onto a cached system prompt instead — see unpack_messages.

unpack_messages staticmethod

unpack_messages(result: CompressionResult, instruction: str, *, target_lang: str | None = None) -> dict[str, Any]

Return a {system, user} split so the legend can be cached server-side.

narrato.preprocess

narrato.preprocess

Layer 1 — deterministic preprocessing.

No LLM calls. Cheap, language-aware text cleanup that shaves tokens before any downstream stage runs.

Operations (toggleable): - whitespace and punctuation normalization - near-duplicate sentence dedupe (Jaccard over token shingles) - stopword stripping (optional, lang-aware)

Stopword stripping is OFF by default for the narrative preset because narrative extraction benefits from grammatical context. Turn it on for fact/RAG flows.

PreprocessConfig dataclass

spacy_model class-attribute instance-attribute

spacy_model: str | None = None

When set, use spaCy for sentence splitting and POS-aware token stripping.

May be a full model name (en_core_web_sm) or a short ISO code (en — maps to the language's default small model). Requires the nlp extra: pip install 'narratoflow[nlp]' and the model itself (python -m spacy download <model>). If spaCy is not installed, this field is silently ignored and the regex-based default is used.

spacy_strip_pos class-attribute instance-attribute

spacy_strip_pos: bool = False

When True (and spacy_model is set), drop function-word tokens by POS instead of dropping stopwords from the bundled word list. Named entities are preserved.

PreprocessResult dataclass

preprocess

preprocess(text: str, cfg: PreprocessConfig | None = None) -> PreprocessResult

narrato.codebook

narrato.codebook

Layer 2 — reference compression and codebook substitution.

Builds a per-document codebook of frequent multi-word phrases and assigns each a short code (e.g. §a, §b). The document is rewritten using the codes, and the legend is sent alongside so the downstream LLM can decode.

Codes only "win" when the phrase is long enough and frequent enough that the code + one legend entry costs fewer tokens than the original occurrences.

Two savings estimators are supported
  1. "chars" (default, free) — uses character length as a fast proxy.
  2. "tokens" — measures real token counts via a :class:Tokenizer. More accurate, costs a handful of tokenizer calls per candidate. Pass a tokenizer to :func:build to enable.

Codes use § followed by base-36 digits. § is rare enough in source text that collisions are unlikely; we strip it from the input first to be safe.

CodebookConfig dataclass

estimator class-attribute instance-attribute

estimator: str = 'chars'

Either "chars" (default, free) or "tokens" (real-token measurement).

CodebookResult dataclass

legend_string

legend_string() -> str

Render legend as compact text block for prepending to prompts.

build

build(text: str, cfg: CodebookConfig | None = None, *, tokenizer: _CountableTokenizer | None = None) -> CodebookResult

Build a codebook from text.

Parameters

text: Source text. cfg: Codebook configuration (thresholds, max entries, estimator choice). tokenizer: Optional tokenizer. When provided and cfg.estimator == "tokens", savings are computed using real token counts; otherwise char-length is used as a fast proxy.

decode

decode(text: str, legend: dict[str, str]) -> str

Expand codes back to phrases (for testing/debugging round trips).

narrato.extractors

narrato.extractors

Layer 3 — semantic extraction via a small/cheap LLM.

The extractor takes the (already preprocessed + codebook'd) text and asks a cheap model to fill a Pydantic schema. Output is a compact JSON payload — the densest representation narrato can produce without learned compression.

For sources too long for a single call, use :func:extract_chunked to split the text, extract each chunk independently, then merge the partial payloads back into a single schema-conformant object.

Async variants (:func:extract_async, :func:extract_chunked_async) run chunks concurrently via asyncio.gather and require the provider to implement :class:narrato.providers.AsyncProvider.

ExtractResult dataclass

extract

extract(text: str, schema: type[BaseModel], provider: Provider, model: str, *, legend: dict[str, str] | None = None, source_lang: str = 'no', max_tokens: int = 4096, temperature: float = 0.0) -> ExtractResult

narrato.schemas

narrato.schemas

Pydantic schemas that drive the semantic extractor.

A schema describes what to keep from the source text. The extractor LLM is asked to fill it; the filled object is the dense payload sent downstream.

Custom schemas: pass any BaseModel subclass to Compressor(schema=...).

NarrativeFacts

Bases: BaseModel

Dense fact bundle suitable for narrative generation.

QAFacts

Bases: BaseModel

Schema for QA / fact-extraction flows.

get_schema

get_schema(name_or_class: str | type[BaseModel]) -> type[BaseModel]

schema_to_json_schema

schema_to_json_schema(model: type[BaseModel]) -> dict[str, Any]

Convert a Pydantic model to a JSON Schema dict for provider JSON modes.

narrato.profiles

narrato.profiles

Named compression profiles — opinionated configs on top of the generic core.

A :class:Profile is a frozen bundle of Compressor arguments. Profiles let users start with one line:

.. code-block:: python

from narrato import Compressor
c = Compressor.from_profile("rag-en")

instead of choosing language, schema, layers, chunk size, etc.

Profiles do not change the engine — they are pure convenience wrappers. The generic core stays language- and domain-neutral; profiles sit on top.

Register your own profiles via :func:register_profile.

Profile dataclass

A named bundle of Compressor defaults.

extra class-attribute instance-attribute

extra: dict[str, Any] = field(default_factory=dict)

Free-form extra kwargs forwarded to Compressor().

as_compressor_kwargs

as_compressor_kwargs() -> dict[str, Any]

Materialise as a Compressor() kwargs dict (without provider/model).

get_profile

get_profile(name: str) -> Profile

Look up a profile by name. Raises KeyError if unknown.

list_profiles

list_profiles() -> list[Profile]

Return all registered profiles.

register_profile

register_profile(profile: Profile, *, overwrite: bool = False) -> None

Register a new profile (or overwrite an existing one).

unregister_profile

unregister_profile(name: str) -> None

Remove a profile from the registry.

narrato.providers

narrato.providers.base

Provider abstraction. Concrete impls live in sibling modules.

Provider

Bases: Protocol

AsyncProvider

Bases: Protocol

Structural type for providers exposing async methods.

All built-in providers implement both Provider and AsyncProvider. Code that wants to require async support can type-hint against this.

ProviderResponse dataclass

cached_input_tokens class-attribute instance-attribute

cached_input_tokens: int = 0

Tokens that were served from the provider-side prompt cache, when reported.

get_provider

get_provider(name: str, *, cache: bool = False) -> Provider

Factory by provider name.

Parameters

name: "anthropic", "openai", or "ollama". cache: Enable provider-side prompt caching when supported. Currently honored by the Anthropic provider (marks the system prompt as an ephemeral cache breakpoint). Ignored by providers without explicit cache opt-in (OpenAI's caching is automatic above 1024 tokens; Ollama has none).

narrato.providers.ollama

Ollama provider — local LLMs via the Ollama HTTP API.

Tested against Ollama 0.x. The provider talks to /api/chat and uses Ollama's format option for structured JSON output. Models that lack tool or JSON-mode training (e.g. small base models) may produce loose JSON; the extractor wraps the response and the pipeline reports validation errors without crashing.

Install Ollama: https://ollama.com — then ollama pull llama3.

This provider has no separate Python SDK requirement: it uses httpx which is already pulled in as a transitive dependency of anthropic and openai. Set OLLAMA_HOST (default: http://localhost:11434).

OllamaProvider

narrato.providers.mock

Mock provider for offline tests and demos.

Use with Compressor(provider=MockProvider(payload=...)) after constructing the compressor — or pass MockProvider instances directly to extract and run_benchmark.

MockProvider dataclass

Returns canned responses; counts how many times each method ran.

Parameters

payload: Default JSON payload returned by complete_json. May be a dict or a callable taking the user prompt and returning a dict. text: Default text returned by complete. May be a string or a callable. sequence: Optional list of canned ProviderResponse objects returned in order on successive calls (round-robin if exhausted). Overrides payload and text when set. input_tokens / output_tokens: Token counts reported on each response.

narrato.tokenizers

narrato.tokenizers

Tokenizer adapters. Unified count(text) across providers.

Anthropic does not ship a public offline tokenizer for Claude 4.x; we fall back to the SDK's client.messages.count_tokens when available, otherwise to a character-heuristic estimate. OpenAI uses tiktoken directly.

AnthropicTokenizer dataclass

Uses Anthropic SDK count_tokens when available; falls back to estimate.

The fallback uses a conservative chars-per-token ratio for Latin-script languages. For Norwegian text it tends to slightly under-estimate, which is acceptable for ratio comparisons.

OpenAITokenizer dataclass

Uses tiktoken. Works fully offline.

get_tokenizer

get_tokenizer(provider: str, model: str | None = None) -> Tokenizer

Factory by provider name.

Unknown provider names fall back to the OpenAI tokenizer (BPE via tiktoken) so Ollama / mock / custom providers still get a working token counter.

narrato.language

narrato.language

Lightweight language detection utilities.

Used when callers pass source_lang="auto" to the compressor. Detection is done by the optional langdetect library if installed; otherwise a small heuristic that scores text against bundled stopword lists is used.

The heuristic is good enough to distinguish the 12 bundled languages on most inputs longer than ~100 characters. For short or mixed-language input, install langdetect (pip install 'narratoflow[lang]') for better accuracy.

detect

detect(text: str, *, default: str = 'en') -> str

Detect the ISO 639-1 language code of text.

Order:

  1. If langdetect is installed, use it.
  2. Otherwise score against bundled stopword lists.
  3. If text is too short / no signal, return default.

supported

supported() -> tuple[str, ...]

Return ISO codes that the bundled detector recognises.

narrato.spacy_pipeline

narrato.spacy_pipeline

Optional spaCy integration for higher-quality preprocessing.

This module is imported lazily by :mod:narrato.preprocess. spaCy is not a hard dependency of narratoflow; install it via the nlp extra:

.. code-block:: bash

pip install "narratoflow[nlp]"
python -m spacy download en_core_web_sm   # or your language model

When :class:~narrato.preprocess.PreprocessConfig has spacy_model set and spaCy is importable, the preprocess layer uses spaCy for sentence splitting and (optionally) for stripping non-content tokens by POS or by lemma. If spaCy is not installed, the preprocess layer falls back to the regex-based default with no error.

Public functions:

  • :func:load_model — cached loader; raises a clear error when missing.
  • :func:spacy_sentences — sentence-split using spaCy.
  • :func:spacy_strip — drop tokens that match a POS filter, preserving order.

Performance: spaCy is heavier than the regex fallback (~10× slower on small docs). It pays off on longer documents and in languages where naïve regex sentence splitting breaks down (German compound words, Finnish, etc.).

load_model cached

load_model(name_or_lang: str) -> Any

Load and cache a spaCy Language object.

name_or_lang may be either a full model name (en_core_web_sm) or a short ISO code (en, no). Raises a :class:RuntimeError with a helpful message if spaCy or the model is not installed.

spacy_sentences

spacy_sentences(text: str, *, model: str) -> list[str]

Sentence-split text using spaCy. Falls through with empty list on empty input.

spacy_strip

spacy_strip(text: str, *, model: str, drop_pos: frozenset[str] | set[str] | None = None, keep_entities: bool = True) -> tuple[str, int]

Strip function-word tokens (by POS) while preserving content.

Returns the rewritten text and the count of dropped tokens. Named entities are kept verbatim regardless of POS when keep_entities is true.

model_for_lang

model_for_lang(lang: str) -> str

Best-effort mapping from ISO code to a default small spaCy model.

is_available

is_available() -> bool

Return True if spaCy is installed and importable.

narrato.benchmark

narrato.benchmark

Round-trip benchmark: measure tokens-in, tokens-out, and downstream quality.

Quality is scored by an LLM judge that compares two narratives generated from (a) the full source vs (b) the compressed payload. A score from 1-10 is returned along with a short rationale.

Cost numbers are estimates using the price table below; update as providers change pricing.

BenchmarkReport dataclass

run_benchmark

run_benchmark(source_text: str, *, instruction: str, compressor: Compressor, target_model: str | None = None, judge_model: str | None = None, skip_quality: bool = False) -> BenchmarkReport

Run an end-to-end benchmark on one document.

judge

judge(reference_narrative: str, candidate_narrative: str, *, provider: Provider, model: str) -> dict

cost

cost(model: str, input_tokens: int, output_tokens: int) -> float