Skip to content

Context reduction

Fit a document corpus to a token budget. Your application collects; this subpackage reduces. The reasoning and the strategy tradeoffs are in context reduction; the task-oriented walkthrough is fitting a corpus to a budget.

Imported from its own path, like the other optional subsystems:

from anyinfer import context

Documents and results

anyinfer.context.ContextDocument dataclass

ContextDocument(
    path: str,
    content: str,
    sha256: str,
    pinned: bool = False,
    language: str | None = None,
    extract: str = "",
)

One document offered to the reducer.

Attributes:

Name Type Description
path str

POSIX-style relative path. Doubles as identity and as a ranking signal — a query term matching the path outweighs the same term in the body.

content str

The document's text.

sha256 str

Hex digest of content, used for identity in rendered envelopes and for deterministic tie-breaking.

pinned bool

Sorts before every unpinned document and is selected first. This is how an app says "the user explicitly chose this file".

language str | None

Language name for rendering and rollups, detected when omitted.

extract str

A structural summary (signatures, imports, headings) used by the tiered strategy when the whole document does not fit. Empty means none.

bytes_length property

bytes_length: int

UTF-8 byte length of the content.

of classmethod

of(
    path: str,
    content: str,
    *,
    pinned: bool = False,
    language: str | None = None,
    extract: str | None = None,
) -> ContextDocument

Build a document, computing its digest and filling in what was omitted.

Parameters:

Name Type Description Default
path str

POSIX-style relative path.

required
content str

The document's text.

required
pinned bool

Whether the document must be included ahead of ranked candidates.

False
language str | None

Overrides language detection.

None
extract str | None

Overrides extraction. Pass "" to opt out of it entirely; omit it to have one derived from the content.

None

Returns:

Type Description
ContextDocument

The document, with sha256 computed and language/extract derived unless

ContextDocument

they were supplied.

anyinfer.context.Reduction dataclass

Reduction(
    strategy: str,
    representation: str,
    documents: tuple[ContextDocument, ...],
    candidate_count: int,
    text: str,
    estimated_tokens: int,
    max_tokens: int,
    max_bytes: int,
    max_documents: int,
    total_bytes: int,
    binding_constraints: tuple[str, ...] = (),
    tier_metadata: Mapping[str, Any] | None = None,
)

What a reduction produced, and what it cost.

Attributes:

Name Type Description
strategy str

The strategy that was requested. auto stays auto even after dispatch, so the caller can see what they asked for.

representation str

The strategy actually applied — what auto resolved to.

documents tuple[ContextDocument, ...]

Documents represented at full or extract fidelity. In tiered this is the set actually rendered in detail, not the ranked prefix.

candidate_count int

How many documents were offered.

text str

The rendered envelope. Always present — place it in your own message.

estimated_tokens int

Planning-side estimate of text.

max_tokens int

The token budget this reduction was held to.

max_bytes int

The byte ceiling in force.

max_documents int

The document-count ceiling in force.

total_bytes int

UTF-8 byte length of text.

binding_constraints tuple[str, ...]

Which ceilings excluded at least one document, in the fixed order ("document count", "bytes", "tokens"). Empty means everything fit.

tier_metadata Mapping[str, Any] | None

Strategy-specific detail (tier composition, chunk counts).

omitted_count property

omitted_count: int

How many offered documents are not represented in detail.

complete property

complete: bool

Whether every offered document was represented without any ceiling binding.

metadata

metadata() -> dict[str, Any]

The full machine-readable record, for logging or a debug pane.

summary

summary() -> str

A one-line, content-free description of what happened.

Safe to show a user or write to a log: counts and ceilings only, never paths or content — a path name can itself be sensitive.

event

event(*, calls: int = 0) -> ContextReduced

Build the telemetry event describing this reduction.

anyinfer.context.Strategy module-attribute

Strategy = Literal[
    "auto", "whole", "ranked", "tiered", "packed"
]

Names accepted by select's strategy argument.

anyinfer.context.RenderOrder module-attribute

RenderOrder = Literal['path', 'rank']

Whether selected documents render by stable path order or relevance rank.

anyinfer.context.RankCache dataclass

RankCache(
    term_counts: dict[str, Counter[str]] = dict(),
    document_frequency: Counter[str] = Counter(),
    document_lengths: dict[str, int] = dict(),
    total_documents: int = 0,
)

Precomputed term statistics for one corpus.

Ranking a corpus repeatedly — as an interactive app does on every turn — otherwise re-tokenizes every document each time. Build one with anyinfer.context.rank.build_rank_cache() and pass it back in.

Validity is the caller's responsibility: key it on a corpus hash and rebuild when the corpus changes. Passing a cache built from different documents produces undefined ranking rather than an error, which is why the cache is not consulted for document identity.

Attributes:

Name Type Description
term_counts dict[str, Counter[str]]

Per-document term frequencies, keyed by document path.

document_frequency Counter[str]

How many documents contain each term.

document_lengths dict[str, int]

Total token count per document, for length normalization.

total_documents int

Corpus size, for the inverse-document-frequency term.

Selection

anyinfer.context.select

Corpus selection: the strategies, the result type, and the dispatch rule.

Reduction is emulation of a larger context window, and emulation announces itself. Every reduction returns what it kept, what it dropped, which ceiling bound it, and a content-free summary — plus a ContextReduced telemetry event when an observer is supplied. A silent truncation that looks like a complete answer is the failure mode this module exists to prevent.

DEFAULT_MAX_DOCUMENTS module-attribute

DEFAULT_MAX_DOCUMENTS = 200

Ceiling on documents in one envelope; a long tail of tiny files helps nobody.

DEFAULT_MAX_BYTES module-attribute

DEFAULT_MAX_BYTES = 4 * 1024 * 1024

Byte ceiling, enforced independently of tokens because transports cap bytes.

VALID_STRATEGIES module-attribute

VALID_STRATEGIES = (
    "auto",
    "whole",
    "ranked",
    "tiered",
    "packed",
)

Strategy names select() accepts.

Strategy module-attribute

Strategy = Literal[
    "auto", "whole", "ranked", "tiered", "packed"
]

Names accepted by select's strategy argument.

RenderOrder module-attribute

RenderOrder = Literal['path', 'rank']

Whether selected documents render by stable path order or relevance rank.

Reduction dataclass

Reduction(
    strategy: str,
    representation: str,
    documents: tuple[ContextDocument, ...],
    candidate_count: int,
    text: str,
    estimated_tokens: int,
    max_tokens: int,
    max_bytes: int,
    max_documents: int,
    total_bytes: int,
    binding_constraints: tuple[str, ...] = (),
    tier_metadata: Mapping[str, Any] | None = None,
)

What a reduction produced, and what it cost.

Attributes:

Name Type Description
strategy str

The strategy that was requested. auto stays auto even after dispatch, so the caller can see what they asked for.

representation str

The strategy actually applied — what auto resolved to.

documents tuple[ContextDocument, ...]

Documents represented at full or extract fidelity. In tiered this is the set actually rendered in detail, not the ranked prefix.

candidate_count int

How many documents were offered.

text str

The rendered envelope. Always present — place it in your own message.

estimated_tokens int

Planning-side estimate of text.

max_tokens int

The token budget this reduction was held to.

max_bytes int

The byte ceiling in force.

max_documents int

The document-count ceiling in force.

total_bytes int

UTF-8 byte length of text.

binding_constraints tuple[str, ...]

Which ceilings excluded at least one document, in the fixed order ("document count", "bytes", "tokens"). Empty means everything fit.

tier_metadata Mapping[str, Any] | None

Strategy-specific detail (tier composition, chunk counts).

omitted_count property

omitted_count: int

How many offered documents are not represented in detail.

complete property

complete: bool

Whether every offered document was represented without any ceiling binding.

metadata

metadata() -> dict[str, Any]

The full machine-readable record, for logging or a debug pane.

summary

summary() -> str

A one-line, content-free description of what happened.

Safe to show a user or write to a log: counts and ceilings only, never paths or content — a path name can itself be sensitive.

event

event(*, calls: int = 0) -> ContextReduced

Build the telemetry event describing this reduction.

normalize_strategy

normalize_strategy(value: str | None) -> str

Normalize a strategy name.

Parameters:

Name Type Description Default
value str | None

The requested strategy; None or blank means "auto".

required

Returns:

Type Description
str

The lowercase strategy name.

Raises:

Type Description
ValueError

On an unrecognized strategy, listing the valid names.

select

select(
    documents: Iterable[ContextDocument],
    query: str,
    *,
    max_tokens: int,
    strategy: str = "auto",
    max_documents: int = DEFAULT_MAX_DOCUMENTS,
    max_bytes: int = DEFAULT_MAX_BYTES,
    estimator: TokenEstimator | None = None,
    rank_cache: RankCache | None = None,
    module_digests: Mapping[str, str] | None = None,
    render_order: RenderOrder = "path",
    observer: Observer | None = None,
) -> Reduction

Reduce a corpus to fit a token budget.

Parameters:

Name Type Description Default
documents Iterable[ContextDocument]

The corpus the app has already collected and approved.

required
query str

What the request is about, used for relevance ranking.

required
max_tokens int

The token budget. Normally client.budget(messages, target=...).remaining_tokens — an explicit number, because an unknown context window stays unknown rather than being guessed at.

required
strategy str

auto (default), whole, ranked, tiered, or packed.

'auto'
max_documents int

Ceiling on documents represented.

DEFAULT_MAX_DOCUMENTS
max_bytes int

Ceiling on envelope bytes.

DEFAULT_MAX_BYTES
estimator TokenEstimator | None

Token counting strategy; defaults to the byte heuristic.

None
rank_cache RankCache | None

Precomputed corpus statistics, for repeated queries.

None
module_digests Mapping[str, str] | None

App-supplied per-module summaries, rendered by tiered. The library never generates these.

None
render_order RenderOrder

path (default) renders selected documents in path order regardless of rank, so consecutive turns over the same corpus share a stable prompt prefix and provider prompt caches hit. rank renders strongest-first.

'path'
observer Observer | None

Receives a ContextReduced event describing the outcome.

None

Returns:

Type Description
Reduction

The Reduction, whose text is the envelope to place in your own message.

Raises:

Type Description
ValueError

On an unknown strategy or a non-positive budget.

anyinfer.context.normalize_strategy

normalize_strategy(value: str | None) -> str

Normalize a strategy name.

Parameters:

Name Type Description Default
value str | None

The requested strategy; None or blank means "auto".

required

Returns:

Type Description
str

The lowercase strategy name.

Raises:

Type Description
ValueError

On an unrecognized strategy, listing the valid names.

anyinfer.context.VALID_STRATEGIES module-attribute

VALID_STRATEGIES = (
    "auto",
    "whole",
    "ranked",
    "tiered",
    "packed",
)

Strategy names select() accepts.

anyinfer.context.DEFAULT_MAX_DOCUMENTS module-attribute

DEFAULT_MAX_DOCUMENTS = 200

Ceiling on documents in one envelope; a long tail of tiny files helps nobody.

anyinfer.context.DEFAULT_MAX_BYTES module-attribute

DEFAULT_MAX_BYTES = 4 * 1024 * 1024

Byte ceiling, enforced independently of tokens because transports cap bytes.

Ranking

Public so it can be replaced: the shipped ranker is lexical, and an application needing semantic retrieval ranks its own documents and passes the result through.

anyinfer.context.rank

Lexical relevance ranking.

A BM25-style scorer, deliberately lexical and dependency-free: term frequency saturated and length-normalized, weighted by inverse document frequency, plus two signals that matter a great deal in a code or document corpus and nothing in classical IR — a path match outweighs a body match, and well-known anchor files (README, pyproject.toml, ARCHITECTURE) get a small bonus.

What this is not. There are no embeddings and no semantic matching: a query for "authentication" will not find a file that only says "login". That is a deliberate boundary — see the concept documentation — and the reason ranking is exposed as a function you can replace rather than hidden inside selection.

Tokenization is ASCII alphanumeric. Ranking is fully deterministic: ties break on path depth, then path, then digest, so the same corpus and query always produce the same order regardless of the order documents were supplied in.

STOP_WORDS module-attribute

STOP_WORDS = frozenset(
    {
        "a",
        "about",
        "all",
        "an",
        "and",
        "any",
        "are",
        "as",
        "at",
        "be",
        "been",
        "but",
        "by",
        "can",
        "do",
        "does",
        "for",
        "from",
        "get",
        "has",
        "have",
        "how",
        "i",
        "if",
        "in",
        "into",
        "is",
        "it",
        "its",
        "of",
        "on",
        "or",
        "please",
        "should",
        "so",
        "some",
        "than",
        "that",
        "the",
        "their",
        "them",
        "then",
        "there",
        "these",
        "this",
        "to",
        "use",
        "was",
        "were",
        "what",
        "when",
        "where",
        "which",
        "who",
        "why",
        "will",
        "with",
        "would",
        "you",
        "your",
    }
)

Words carrying no retrieval signal, dropped from queries and documents alike.

TERM_SATURATION module-attribute

TERM_SATURATION = 1.2

BM25's k1: how fast repeated occurrences of a term stop adding score.

LENGTH_NORMALIZATION module-attribute

LENGTH_NORMALIZATION = 0.001

Penalty per token of document length, so a long file cannot win on volume alone.

PATH_MATCH_WEIGHT module-attribute

PATH_MATCH_WEIGHT = 4.0

How much more a query term in the path counts than the same term in the body.

Deliberately large: someone asking about credentials almost always means the file named for it, even when a dozen other files mention the word more often.

ANCHOR_SCORE module-attribute

ANCHOR_SCORE = 0.25

Bonus for files that orient a reader regardless of the query.

ANCHOR_NAMES module-attribute

ANCHOR_NAMES = frozenset(
    {
        "architecture",
        "changelog",
        "contributing",
        "design",
        "overview",
        "readme",
        "cargo.toml",
        "go.mod",
        "package.json",
        "pom.xml",
        "pyproject.toml",
    }
)

Filenames (and stems) worth a small unconditional boost.

tokenize

tokenize(text: str) -> list[str]

Split text into lowercase alphanumeric terms, dropping stop words.

ASCII-only by design — the tradeoff is stated in the module documentation.

build_rank_cache

build_rank_cache(
    documents: Iterable[ContextDocument],
) -> RankCache

Precompute term statistics for a corpus.

Pass the result to rank() on subsequent queries over the same corpus. The caller owns invalidation; see RankCache.

score_document

score_document(
    document: ContextDocument,
    query_terms: Counter[str],
    cache: RankCache,
) -> float

Score one document against a query.

Parameters:

Name Type Description Default
document ContextDocument

The candidate.

required
query_terms Counter[str]

Query term frequencies, from Counter(tokenize(query)).

required
cache RankCache

Corpus statistics covering this document.

required

Returns:

Type Description
float

A non-negative relevance score. Zero means nothing matched — which is still a

float

valid candidate, just an unranked one.

rank

rank(
    documents: Sequence[ContextDocument],
    query: str,
    *,
    rank_cache: RankCache | None = None,
) -> list[ContextDocument]

Order documents by relevance, pinned ones first.

Parameters:

Name Type Description Default
documents Sequence[ContextDocument]

The corpus.

required
query str

What the app is asking about. An empty query ranks everything at zero, leaving the deterministic tie-break as the order.

required
rank_cache RankCache | None

Precomputed statistics for this corpus; built on the fly when absent.

None

Returns:

Type Description
list[ContextDocument]

A new list, most relevant first. Ordering is total and deterministic: every

list[ContextDocument]

pinned document precedes every unpinned one, then higher score, then shallower

list[ContextDocument]

path, then path, then digest.

anyinfer.context.build_rank_cache

build_rank_cache(
    documents: Iterable[ContextDocument],
) -> RankCache

Precompute term statistics for a corpus.

Pass the result to rank() on subsequent queries over the same corpus. The caller owns invalidation; see RankCache.

anyinfer.context.tokenize

tokenize(text: str) -> list[str]

Split text into lowercase alphanumeric terms, dropping stop words.

ASCII-only by design — the tradeoff is stated in the module documentation.

Structure and tiers

anyinfer.context.detect_language

detect_language(path: str) -> str | None

Infer a language from a path's suffix, or None when it is ambiguous.

anyinfer.context.structural_extract

structural_extract(
    content: str, *, language: str | None
) -> str

Reduce a document to its declarations, imports, and headings.

Parameters:

Name Type Description Default
content str

The document text.

required
language str | None

The detected language. None yields no extract — guessing patterns for an unknown language produces noise, not a summary.

required

Returns:

Type Description
str

The extract, or "" when none could be produced. Files under

str

SMALL_FILE_VERBATIM_BYTES are returned whole: they are already their own

str

summary.

anyinfer.context.is_generated_path

is_generated_path(path: str) -> bool

Whether a path looks machine-generated or vendored.

Offered as a public helper because the decision belongs at collection time — the library never walks a filesystem, but an app deciding what to collect needs the same heuristic AnyInfer would have applied.

anyinfer.context.module_surfaces

module_surfaces(
    documents: Sequence[ContextDocument], *, depth: int = 2
) -> dict[str, str]

Group a corpus into modules and render each one's surface text.

Offered as a public helper for the app-side digest recipe: generate a summary per module with your own client, cache it keyed on the surface's digest, and hand the results back as module_digests. Deterministic, so the cache key is stable.

Parameters:

Name Type Description Default
documents Sequence[ContextDocument]

The corpus.

required
depth int

Path-prefix depth at which to group.

2

Returns:

Type Description
dict[str, str]

Module path to concatenated extract (or content) text, in path order.

anyinfer.context.DEFAULT_ROLLUP_SHARE module-attribute

DEFAULT_ROLLUP_SHARE = 0.45

Share of the token and byte budget reserved for the module rollup.

Chunking

anyinfer.context.Chunk dataclass

Chunk(
    document: ContextDocument,
    text: str,
    index: int,
    start_line: int,
    end_line: int,
)

One span of a document.

Attributes:

Name Type Description
document ContextDocument

The document this came from.

text str

The span's text.

index int

Position within the document, from zero.

start_line int

First line of the span, 1-based and inclusive.

end_line int

Last line of the span, 1-based and inclusive.

anyinfer.context.split_document

split_document(
    document: ContextDocument,
    *,
    chunk_tokens: int = DEFAULT_CHUNK_TOKENS,
) -> list[Chunk]

Split a document into boundary-aware chunks with line spans.

Prefers the last blank line within budget, falls back to the last line break, and hard-cuts only when neither lands past a quarter of the budget.

Parameters:

Name Type Description Default
document ContextDocument

The document to split.

required
chunk_tokens int

Target chunk size in planning tokens.

DEFAULT_CHUNK_TOKENS

Returns:

Type Description
list[Chunk]

Chunks in document order. A document shorter than one chunk yields exactly one.

anyinfer.context.DEFAULT_CHUNK_TOKENS module-attribute

DEFAULT_CHUNK_TOKENS = 512

Target chunk size. Large enough for a whole function, small enough to pack several.

Distillation

The only reduction that spends inference. See distill a corpus for the cookbook.

anyinfer.context.distill

The distill strategy: map/reduce a corpus that will never fit.

The other strategies decide what to drop. This one reads everything and writes something shorter: each chunk is summarized against the query (the map phase), then the notes are synthesized into one answer (the reduce phase).

It is separated from anyinfer.context.select by construction, because it is the one strategy that spends money. It takes your client, issues real generation calls, and reports the count and aggregate usage so the multiplier is never a surprise.

Two properties distinguish this from a naive map/reduce. Reduction is hierarchical: if the map notes together exceed the target's window, they are reduced in batches and the batch summaries reduced again, rather than being sent in one overflowing request. And a deterministic reducer can replace the reduce call entirely, so an application that merges structurally pays for the map phase only.

Prompts here are mechanical scaffolding — "here is chunk 3 of 9, take notes" — not application prose. You own the question; override map_instructions and reduce_instructions to own the framing too.

DEFAULT_CONCURRENCY module-attribute

DEFAULT_CONCURRENCY = 4

Map calls in flight at once. Bounded because a fan-out is someone's rate limit.

SupportsGenerate

Bases: Protocol

The slice of an async client distill() needs.

A structural protocol rather than an import, so this subpackage never depends on the client — anyinfer.AsyncClient satisfies it as-is.

generate async

generate(
    messages: Any, *, target: str, **kwargs: Any
) -> Generation

Generate one result.

budget

budget(
    messages: Any, *, target: str, **kwargs: Any
) -> ContextBudget

Compute a context budget without issuing a request.

SupportsGenerateSync

Bases: Protocol

The synchronous mirror of SupportsGenerate, satisfied by anyinfer.Client.

generate

generate(
    messages: Any, *, target: str, **kwargs: Any
) -> Generation

Generate one result.

budget

budget(
    messages: Any, *, target: str, **kwargs: Any
) -> ContextBudget

Compute a context budget without issuing a request.

Distillation dataclass

Distillation(
    text: str,
    chunk_count: int,
    calls: int,
    usage: Usage,
    reduce_depth: int = 1,
    notes: tuple[str, ...] = (),
)

What a distillation produced, and what it cost.

Attributes:

Name Type Description
text str

The synthesized answer.

chunk_count int

How many chunks the source was split into.

calls int

Total generation calls spent, map and reduce together. This is the multiplier over a single request.

usage Usage

Merged usage across every call, including cost when providers report it.

reduce_depth int

1 for a single-pass reduce; higher when notes were reduced in batches and the batch summaries reduced again.

notes tuple[str, ...]

The intermediate map outputs. Payload-bearing — excluded from repr and never placed in telemetry.

summary

summary() -> str

A one-line, content-free description of the run.

event

event(*, max_tokens: int) -> ContextReduced

Build the telemetry event describing this distillation.

distill async

distill(
    source: str | Iterable[ContextDocument],
    query: str,
    *,
    client: SupportsGenerate,
    target: str,
    max_output_tokens: int = 1024,
    chunk_tokens: int | None = None,
    concurrency: int = DEFAULT_CONCURRENCY,
    map_instructions: str | None = None,
    reduce_instructions: str | None = None,
    reducer: Callable[[Sequence[str]], str] | None = None,
    observer: Observer | None = None,
) -> Distillation

Summarize a corpus larger than the window by mapping and reducing over it.

Parameters:

Name Type Description Default
source str | Iterable[ContextDocument]

Raw text, or documents. Documents split per document, because a document boundary is a natural chunk boundary.

required
query str

What the summary should answer.

required
client SupportsGenerate

Anything satisfying SupportsGenerate — normally an anyinfer.AsyncClient.

required
target str

Where to send the calls.

required
max_output_tokens int

Ceiling on the final answer.

1024
chunk_tokens int | None

Chunk size. Derived from the target's remaining budget when omitted.

None
concurrency int

Map calls in flight at once.

DEFAULT_CONCURRENCY
map_instructions str | None

Replaces the default note-taking instruction.

None
reduce_instructions str | None

Replaces the default synthesis instruction.

None
reducer Callable[[Sequence[str]], str] | None

Merge the notes deterministically instead of with a reduce call. Saves every reduce call, and makes the merge reproducible.

None
observer Observer | None

Receives a ContextReduced event when the run finishes.

None

Returns:

Type Description
Distillation

The Distillation.

Raises:

Type Description
ConfigError

When chunk_tokens is omitted and the target's context window is unknown. An unknown window stays unknown — the caller chooses the number.

distill_sync

distill_sync(
    source: str | Iterable[ContextDocument],
    query: str,
    *,
    client: SupportsGenerateSync,
    target: str,
    max_output_tokens: int = 1024,
    chunk_tokens: int | None = None,
    map_instructions: str | None = None,
    reduce_instructions: str | None = None,
    reducer: Callable[[Sequence[str]], str] | None = None,
    observer: Observer | None = None,
) -> Distillation

Run distill() sequentially against a synchronous client.

Chunks are processed one at a time: concurrency is the async path's feature, and a sync caller that wants it should use distill() with an anyinfer.AsyncClient.

Args and returns are as distill(), minus concurrency.

Raises:

Type Description
ConfigError

When chunk_tokens is omitted and the window is unknown.

anyinfer.context.distill_sync

distill_sync(
    source: str | Iterable[ContextDocument],
    query: str,
    *,
    client: SupportsGenerateSync,
    target: str,
    max_output_tokens: int = 1024,
    chunk_tokens: int | None = None,
    map_instructions: str | None = None,
    reduce_instructions: str | None = None,
    reducer: Callable[[Sequence[str]], str] | None = None,
    observer: Observer | None = None,
) -> Distillation

Run distill() sequentially against a synchronous client.

Chunks are processed one at a time: concurrency is the async path's feature, and a sync caller that wants it should use distill() with an anyinfer.AsyncClient.

Args and returns are as distill(), minus concurrency.

Raises:

Type Description
ConfigError

When chunk_tokens is omitted and the window is unknown.

anyinfer.context.Distillation dataclass

Distillation(
    text: str,
    chunk_count: int,
    calls: int,
    usage: Usage,
    reduce_depth: int = 1,
    notes: tuple[str, ...] = (),
)

What a distillation produced, and what it cost.

Attributes:

Name Type Description
text str

The synthesized answer.

chunk_count int

How many chunks the source was split into.

calls int

Total generation calls spent, map and reduce together. This is the multiplier over a single request.

usage Usage

Merged usage across every call, including cost when providers report it.

reduce_depth int

1 for a single-pass reduce; higher when notes were reduced in batches and the batch summaries reduced again.

notes tuple[str, ...]

The intermediate map outputs. Payload-bearing — excluded from repr and never placed in telemetry.

summary

summary() -> str

A one-line, content-free description of the run.

event

event(*, max_tokens: int) -> ContextReduced

Build the telemetry event describing this distillation.

anyinfer.context.SupportsGenerate

Bases: Protocol

The slice of an async client distill() needs.

A structural protocol rather than an import, so this subpackage never depends on the client — anyinfer.AsyncClient satisfies it as-is.

generate async

generate(
    messages: Any, *, target: str, **kwargs: Any
) -> Generation

Generate one result.

budget

budget(
    messages: Any, *, target: str, **kwargs: Any
) -> ContextBudget

Compute a context budget without issuing a request.

Rendering

The envelope format, exposed for applications that parse reduced context back out of stored transcripts.

anyinfer.context.render_corpus

render_corpus(blocks: Iterable[str]) -> str

Wrap rendered blocks in the corpus element.

anyinfer.context.render_file_block

render_file_block(document: ContextDocument) -> str

Render a whole document.

anyinfer.context.render_extract_block

render_extract_block(document: ContextDocument) -> str

Render a document's structural extract.

anyinfer.context.render_chunk_block

render_chunk_block(
    document: ContextDocument,
    text: str,
    start_line: int,
    end_line: int,
) -> str

Render one contiguous span of a document, with its line range.