Context Reduction¶
Fit a document corpus to a token budget. The 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 |
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
|
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 |
None
|
Returns:
| Type | Description |
|---|---|
ContextDocument
|
The document, with |
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, ...] = (),
collapsed_exact: int = 0,
collapsed_near: int = 0,
compacted_count: int = 0,
partial_count: int = 0,
carried_over: int = 0,
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. |
representation |
str
|
The strategy actually applied — what |
documents |
tuple[ContextDocument, ...]
|
Documents represented at full, compact, or extract fidelity. In
|
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 |
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 |
binding_constraints |
tuple[str, ...]
|
Which ceilings excluded at least one document, in the fixed
order |
collapsed_exact |
int
|
Documents rendered as a pointer because a byte-identical copy was sent. Lossless. |
collapsed_near |
int
|
Documents rendered as a pointer because a similar copy was sent. Their differences are not in the envelope. |
compacted_count |
int
|
Documents sent with commentary removed because they would not fit whole. |
partial_count |
int
|
Documents represented by only part of their content — the spans
|
carried_over |
int
|
Documents this reduction kept because the previous one had them,
when |
tier_metadata |
Mapping[str, Any] | None
|
Strategy-specific detail (tier composition, chunk counts). |
collapsed_count
property
¶
collapsed_count: int
How many documents were represented by a pointer to another.
omitted_count
property
¶
omitted_count: int
How many offered documents are not represented at all.
Collapsed duplicates are not omitted: their content reached the model under another path, and the envelope says so.
complete
property
¶
complete: bool
Whether every offered document reached the model at full fidelity.
Exact collapse preserves completeness — the same bytes were sent, once. Near collapse, compaction, and chunk-level selection do not: each drops content that was offered, and a strategy that sends fragments must not report the same confidence as one that sends files.
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.ReductionState
dataclass
¶
ReductionState(
entries: tuple[tuple[str, str], ...] = (),
representation: str = "",
)
What an earlier reduction sent, so the next one can send the same thing.
Selection is deterministic given the same inputs, but a corpus changes between turns
and a re-ranked selection can churn for no reason — swapping one file for an equally
ranked other, moving the whole prompt prefix and missing the provider's cache. Hand
the previous state back through select(previous=...) and unchanged documents get a
rank bonus, so the set stays put unless something real moved it.
Attributes:
| Name | Type | Description |
|---|---|---|
entries |
tuple[tuple[str, str], ...]
|
|
representation |
str
|
The strategy that produced it, for diagnostics. |
of
classmethod
¶
of(reduction: Reduction) -> ReductionState
Capture the state of a completed reduction.
unchanged ¶
unchanged(
documents: Sequence[ContextDocument],
) -> frozenset[str]
Paths present here and still byte-identical in documents.
A path whose content changed is deliberately excluded: carrying it over would move the prompt prefix anyway, so there is nothing to preserve.
metadata ¶
metadata() -> dict[str, Any]
The machine-readable record, content-free apart from paths the caller owns.
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,
split_identifiers: bool = False,
)
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. |
split_identifiers |
bool
|
Which tokenization produced these counts. Ranking checks it and rebuilds rather than scoring a query tokenized one way against statistics gathered the other — a mismatch there produces plausible, wrong ordering. |
Advanced Settings¶
One record carries every algorithmic choice. The same field names are the context block
of the configuration file and the --context-* flags of
anyinfer context. Every setting that changes what gets sent is
off by default; ContextTuning.recommended() enables the set worth having for a
source-code corpus.
| Setting | Default | What it changes |
|---|---|---|
collapse_duplicates |
True |
Render byte-identical documents once |
near_duplicate_threshold |
0.0 |
Collapse merely similar documents too |
selection_order |
"rank" |
"density" admits by score per token |
diversity |
0.0 |
Penalize candidates resembling what is already chosen |
split_identifiers |
False |
Tokenize compound identifiers into their parts |
query_expansion |
False |
Pseudo-relevance feedback before ranking |
salience_weight |
0.0 |
Blend in import-graph centrality |
compact_fallback |
False |
Shorten a document rather than drop it |
carry_over_bonus |
0.0 |
Keep the previous turn's selection, with previous= |
chunk_tokens |
512 |
Chunk size for packed and distill |
rollup_share |
0.45 |
Budget share tiered reserves for its rollup |
Two orderings deserve a note. "rank" admits documents strongest-first; "density"
admits them by score divided by token cost, which packs measurably more relevance into a
fixed budget (the classic knapsack result) at the risk of preferring two good small files
over one great large one. diversity penalizes each candidate by how much it resembles
what is already selected (multiplicatively, value * (1 - diversity * similarity),
because the two value scales differ by orders of magnitude), so a budget is not spent on
eight files that say the same thing.
anyinfer.context.ContextTuning
dataclass
¶
ContextTuning(
collapse_duplicates: bool = True,
near_duplicate_threshold: float = 0.0,
shingle_size: int = 5,
selection_order: SelectionOrder = "rank",
diversity: float = 0.0,
split_identifiers: bool = False,
query_expansion: bool = False,
expansion_terms: int = 8,
feedback_documents: int = 5,
expansion_weight: float = 0.4,
salience_weight: float = 0.0,
salience_damping: float = 0.85,
salience_iterations: int = 20,
compact_fallback: bool = False,
chunk_tokens: int = DEFAULT_CHUNK_TOKENS,
rollup_share: float = 0.45,
carry_over_bonus: float = 0.0,
)
Advanced settings for corpus reduction.
Every field defaults to the behaviour AnyInfer has always had, except
collapse_duplicates. Construct one, or start from recommended() and override.
Attributes:
| Name | Type | Description |
|---|---|---|
collapse_duplicates |
bool
|
Render byte-identical documents once, with the rest as pointer elements. Lossless — the content is still present, and on by default, since sending the same bytes twice helps nobody. |
near_duplicate_threshold |
float
|
Jaccard similarity at or above which two documents are
treated as duplicates of each other. |
shingle_size |
int
|
Word count per shingle for near-duplicate comparison. Larger is stricter. |
selection_order |
SelectionOrder
|
|
diversity |
float
|
Similarity penalty applied to candidates resembling what is already
selected, between |
split_identifiers |
bool
|
Tokenize |
query_expansion |
bool
|
Rank once, harvest distinctive terms from the strongest documents, then re-rank with the expanded query. The lexical answer to vocabulary mismatch: it finds "login" from "authentication" whenever the two co-occur anywhere in the corpus. Costs a second ranking pass, no inference. |
expansion_terms |
int
|
How many harvested terms to add. |
feedback_documents |
int
|
How many top-ranked documents to harvest them from. |
expansion_weight |
float
|
Weight of an expansion term relative to an original query term. |
salience_weight |
float
|
How much a document's centrality in the corpus's own import
graph contributes to its score. The signal is query-independent, so this is
what orders a corpus when the query is weak or absent; |
salience_damping |
float
|
Random-restart probability complement for the centrality
iteration. The conventional |
salience_iterations |
int
|
Fixed iteration count. Fixed rather than convergence-tested, because determinism outranks the last decimal place. |
compact_fallback |
bool
|
When a document will not fit whole, send it with comments, docstrings, and blank runs removed before giving up on it. Elisions are counted in the rendered element, never silent. |
chunk_tokens |
int
|
Target chunk size for |
rollup_share |
float
|
Share of the budget |
carry_over_bonus |
float
|
Rank bonus applied to documents an earlier reduction already
sent unchanged, when |
ranking_is_default
property
¶
ranking_is_default: bool
Whether ranking behaves exactly as the unconfigured ranker does.
Selection consults this to skip the expansion and centrality passes entirely rather than running them with neutral parameters.
__post_init__ ¶
__post_init__() -> None
Reject settings that cannot produce a usable reduction.
Raises:
| Type | Description |
|---|---|
ValueError
|
On an out-of-range or non-finite value, naming the field. |
recommended
classmethod
¶
recommended() -> ContextTuning
The settings worth turning on for a typical source-code corpus.
Near-duplicate collapse at a strict threshold, density-ordered selection with a mild diversity penalty, identifier splitting and query expansion, a light centrality signal, and compact fallback instead of dropping a file outright. Every one of these changes what gets sent, which is why they are a named preset rather than the default.
from_mapping
classmethod
¶
from_mapping(values: Mapping[str, Any]) -> ContextTuning
Build settings from a JSON-shaped mapping.
Used by the shared configuration loader and the CLI, so a context block in a
config file and a --context-* flag mean exactly the same thing.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
values
|
Mapping[str, Any]
|
Field names to values. Unknown names are an error rather than being ignored, so a typo does not silently do nothing. |
required |
Returns:
| Type | Description |
|---|---|
ContextTuning
|
The settings. |
Raises:
| Type | Description |
|---|---|
ValueError
|
On an unknown key, a wrong type, or an out-of-range value. |
merged ¶
merged(**overrides: Any) -> ContextTuning
Return a copy with overrides applied, dropping any that are None.
The shape command-line parsing wants: unspecified flags arrive as None and
must leave the configured value alone.
to_mapping ¶
to_mapping() -> dict[str, Any]
The settings as a JSON-shaped mapping, for logging or round-tripping.
anyinfer.context.SelectionOrder
module-attribute
¶
SelectionOrder = Literal['rank', 'density']
How the greedy selector orders candidates.
anyinfer.context.SELECTION_ORDERS
module-attribute
¶
SELECTION_ORDERS = ('rank', 'density')
Accepted ContextTuning.selection_order values.
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, what it collapsed, 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.
Everything algorithmic is a setting rather than a constant. ContextTuning decides how
duplicates collapse, whether candidates are ordered by relevance or relevance per token,
whether near-identical documents are penalized against each other, and what a document
degrades to instead of being dropped. The defaults reproduce the plain behaviour exactly,
so turning nothing on changes nothing.
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.
ReductionState
dataclass
¶
ReductionState(
entries: tuple[tuple[str, str], ...] = (),
representation: str = "",
)
What an earlier reduction sent, so the next one can send the same thing.
Selection is deterministic given the same inputs, but a corpus changes between turns
and a re-ranked selection can churn for no reason — swapping one file for an equally
ranked other, moving the whole prompt prefix and missing the provider's cache. Hand
the previous state back through select(previous=...) and unchanged documents get a
rank bonus, so the set stays put unless something real moved it.
Attributes:
| Name | Type | Description |
|---|---|---|
entries |
tuple[tuple[str, str], ...]
|
|
representation |
str
|
The strategy that produced it, for diagnostics. |
of
classmethod
¶
of(reduction: Reduction) -> ReductionState
Capture the state of a completed reduction.
unchanged ¶
unchanged(
documents: Sequence[ContextDocument],
) -> frozenset[str]
Paths present here and still byte-identical in documents.
A path whose content changed is deliberately excluded: carrying it over would move the prompt prefix anyway, so there is nothing to preserve.
metadata ¶
metadata() -> dict[str, Any]
The machine-readable record, content-free apart from paths the caller owns.
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, ...] = (),
collapsed_exact: int = 0,
collapsed_near: int = 0,
compacted_count: int = 0,
partial_count: int = 0,
carried_over: int = 0,
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. |
representation |
str
|
The strategy actually applied — what |
documents |
tuple[ContextDocument, ...]
|
Documents represented at full, compact, or extract fidelity. In
|
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 |
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 |
binding_constraints |
tuple[str, ...]
|
Which ceilings excluded at least one document, in the fixed
order |
collapsed_exact |
int
|
Documents rendered as a pointer because a byte-identical copy was sent. Lossless. |
collapsed_near |
int
|
Documents rendered as a pointer because a similar copy was sent. Their differences are not in the envelope. |
compacted_count |
int
|
Documents sent with commentary removed because they would not fit whole. |
partial_count |
int
|
Documents represented by only part of their content — the spans
|
carried_over |
int
|
Documents this reduction kept because the previous one had them,
when |
tier_metadata |
Mapping[str, Any] | None
|
Strategy-specific detail (tier composition, chunk counts). |
collapsed_count
property
¶
collapsed_count: int
How many documents were represented by a pointer to another.
omitted_count
property
¶
omitted_count: int
How many offered documents are not represented at all.
Collapsed duplicates are not omitted: their content reached the model under another path, and the envelope says so.
complete
property
¶
complete: bool
Whether every offered document reached the model at full fidelity.
Exact collapse preserves completeness — the same bytes were sent, once. Near collapse, compaction, and chunk-level selection do not: each drops content that was offered, and a strategy that sends fragments must not report the same confidence as one that sends files.
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.
StrategyOutlook
dataclass
¶
StrategyOutlook(
strategy: str,
representation: str,
selected_count: int,
omitted_count: int,
collapsed_count: int,
compacted_count: int,
partial_count: int,
estimated_tokens: int,
total_bytes: int,
binding_constraints: tuple[str, ...],
complete: bool,
)
What one strategy would produce, costed exactly rather than modelled.
Attributes:
| Name | Type | Description |
|---|---|---|
strategy |
str
|
The strategy this describes. |
representation |
str
|
What it resolved to — identical to |
selected_count |
int
|
Documents it would represent at detail fidelity. |
omitted_count |
int
|
Documents it would not represent at all. |
collapsed_count |
int
|
Documents it would render as a pointer to another. |
compacted_count |
int
|
Documents it would shorten rather than drop. |
partial_count |
int
|
Documents it would represent by fragments rather than whole. |
estimated_tokens |
int
|
Planning-side estimate of the envelope it would render. |
total_bytes |
int
|
Byte length of that envelope. |
binding_constraints |
tuple[str, ...]
|
Which ceilings would bind. |
complete |
bool
|
Whether it would send everything at full fidelity. |
ReductionPlan
dataclass
¶
ReductionPlan(
candidate_count: int,
max_tokens: int,
options: tuple[StrategyOutlook, ...],
distill_chunks: int,
distill_calls: int,
)
What every strategy would do with this corpus and this budget.
A dry run for context preparation, in the same spirit as the pre-dispatch request preflight: choose a strategy from measured outcomes instead of guessing and finding out in the transcript. Costing it spends no inference and touches no network — the deterministic strategies are actually executed and their envelopes measured, then discarded, so the numbers are exact rather than modelled.
Attributes:
| Name | Type | Description |
|---|---|---|
candidate_count |
int
|
Documents offered. |
max_tokens |
int
|
The budget every option was held to. |
options |
tuple[StrategyOutlook, ...]
|
One outlook per deterministic strategy, in a fixed order. |
distill_chunks |
int
|
How many chunks |
distill_calls |
int
|
The floor on generation calls distillation would spend — one per chunk plus a single reduce. A corpus whose notes do not fit at once reduces hierarchically and spends more. |
option ¶
option(strategy: str) -> StrategyOutlook | None
The outlook for one strategy, or None if it was not costed.
best ¶
best() -> StrategyOutlook | None
The option that gets the most of the corpus to the model.
Prefers an option that sends everything at full fidelity; failing that, the one
representing the most documents at detail fidelity; and among equals, the one that
represents them most faithfully — a whole file beats a summary beats a fragment.
It is a recommendation, not a decision: an app that would rather have twelve whole
files than four hundred summarized ones should read options and pick for
itself.
normalize_strategy ¶
normalize_strategy(value: str | None) -> str
Normalize a strategy name.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
value
|
str | None
|
The requested strategy; |
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",
tuning: ContextTuning | None = None,
previous: ReductionState | None = None,
observer: Observer | None = None,
ranker: SemanticRanker | 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
|
required |
strategy
|
str
|
|
'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 |
None
|
render_order
|
RenderOrder
|
|
'path'
|
tuning
|
ContextTuning | None
|
Advanced settings — duplicate collapse, selection order, diversity, query expansion, centrality, compact fallback. Defaults reproduce the plain behaviour exactly. |
None
|
previous
|
ReductionState | None
|
The state of the last reduction over this corpus. Unchanged documents
get |
None
|
observer
|
Observer | None
|
Receives a |
None
|
ranker
|
SemanticRanker | None
|
Caller-supplied semantic scoring ( |
None
|
Returns:
| Type | Description |
|---|---|
Reduction
|
The |
Raises:
| Type | Description |
|---|---|
ValueError
|
On an unknown strategy or a non-positive budget. |
plan ¶
plan(
documents: Iterable[ContextDocument],
query: str,
*,
max_tokens: int,
max_documents: int = DEFAULT_MAX_DOCUMENTS,
max_bytes: int = DEFAULT_MAX_BYTES,
estimator: TokenEstimator | None = None,
module_digests: Mapping[str, str] | None = None,
tuning: ContextTuning | None = None,
) -> ReductionPlan
Cost every strategy against this corpus without committing to one.
Spends no inference and performs no I/O: each deterministic strategy is run, its envelope measured, and the text discarded. The distillation figures are projections — that is the only strategy whose cost cannot be known without paying it.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
documents
|
Iterable[ContextDocument]
|
The corpus. |
required |
query
|
str
|
What the request is about. |
required |
max_tokens
|
int
|
The budget to hold every option to. |
required |
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
|
module_digests
|
Mapping[str, str] | None
|
App-supplied module summaries, costed into the |
None
|
tuning
|
ContextTuning | None
|
Advanced settings, applied to every option so the comparison is fair. |
None
|
Returns:
| Type | Description |
|---|---|
ReductionPlan
|
The |
Raises:
| Type | Description |
|---|---|
ValueError
|
On 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; |
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.
Planning¶
Cost every strategy before committing to one. Spends no inference and performs no I/O.
anyinfer.context.plan ¶
plan(
documents: Iterable[ContextDocument],
query: str,
*,
max_tokens: int,
max_documents: int = DEFAULT_MAX_DOCUMENTS,
max_bytes: int = DEFAULT_MAX_BYTES,
estimator: TokenEstimator | None = None,
module_digests: Mapping[str, str] | None = None,
tuning: ContextTuning | None = None,
) -> ReductionPlan
Cost every strategy against this corpus without committing to one.
Spends no inference and performs no I/O: each deterministic strategy is run, its envelope measured, and the text discarded. The distillation figures are projections — that is the only strategy whose cost cannot be known without paying it.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
documents
|
Iterable[ContextDocument]
|
The corpus. |
required |
query
|
str
|
What the request is about. |
required |
max_tokens
|
int
|
The budget to hold every option to. |
required |
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
|
module_digests
|
Mapping[str, str] | None
|
App-supplied module summaries, costed into the |
None
|
tuning
|
ContextTuning | None
|
Advanced settings, applied to every option so the comparison is fair. |
None
|
Returns:
| Type | Description |
|---|---|
ReductionPlan
|
The |
Raises:
| Type | Description |
|---|---|
ValueError
|
On a non-positive budget. |
anyinfer.context.ReductionPlan
dataclass
¶
ReductionPlan(
candidate_count: int,
max_tokens: int,
options: tuple[StrategyOutlook, ...],
distill_chunks: int,
distill_calls: int,
)
What every strategy would do with this corpus and this budget.
A dry run for context preparation, in the same spirit as the pre-dispatch request preflight: choose a strategy from measured outcomes instead of guessing and finding out in the transcript. Costing it spends no inference and touches no network — the deterministic strategies are actually executed and their envelopes measured, then discarded, so the numbers are exact rather than modelled.
Attributes:
| Name | Type | Description |
|---|---|---|
candidate_count |
int
|
Documents offered. |
max_tokens |
int
|
The budget every option was held to. |
options |
tuple[StrategyOutlook, ...]
|
One outlook per deterministic strategy, in a fixed order. |
distill_chunks |
int
|
How many chunks |
distill_calls |
int
|
The floor on generation calls distillation would spend — one per chunk plus a single reduce. A corpus whose notes do not fit at once reduces hierarchically and spends more. |
option ¶
option(strategy: str) -> StrategyOutlook | None
The outlook for one strategy, or None if it was not costed.
best ¶
best() -> StrategyOutlook | None
The option that gets the most of the corpus to the model.
Prefers an option that sends everything at full fidelity; failing that, the one
representing the most documents at detail fidelity; and among equals, the one that
represents them most faithfully — a whole file beats a summary beats a fragment.
It is a recommendation, not a decision: an app that would rather have twelve whole
files than four hundred summarized ones should read options and pick for
itself.
anyinfer.context.StrategyOutlook
dataclass
¶
StrategyOutlook(
strategy: str,
representation: str,
selected_count: int,
omitted_count: int,
collapsed_count: int,
compacted_count: int,
partial_count: int,
estimated_tokens: int,
total_bytes: int,
binding_constraints: tuple[str, ...],
complete: bool,
)
What one strategy would produce, costed exactly rather than modelled.
Attributes:
| Name | Type | Description |
|---|---|---|
strategy |
str
|
The strategy this describes. |
representation |
str
|
What it resolved to — identical to |
selected_count |
int
|
Documents it would represent at detail fidelity. |
omitted_count |
int
|
Documents it would not represent at all. |
collapsed_count |
int
|
Documents it would render as a pointer to another. |
compacted_count |
int
|
Documents it would shorten rather than drop. |
partial_count |
int
|
Documents it would represent by fragments rather than whole. |
estimated_tokens |
int
|
Planning-side estimate of the envelope it would render. |
total_bytes |
int
|
Byte length of that envelope. |
binding_constraints |
tuple[str, ...]
|
Which ceilings would bind. |
complete |
bool
|
Whether it would send everything at full fidelity. |
Duplicate Collapse¶
anyinfer.context.find_duplicates ¶
find_duplicates(
documents: Sequence[ContextDocument],
*,
tuning: ContextTuning = DEFAULT_TUNING,
) -> DuplicateMap
Group duplicate documents and choose one representative for each group.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
documents
|
Sequence[ContextDocument]
|
The corpus. |
required |
tuning
|
ContextTuning
|
Supplies |
DEFAULT_TUNING
|
Returns:
| Type | Description |
|---|---|
DuplicateMap
|
The |
DuplicateMap
|
fewer than two documents, or when nothing repeated. |
anyinfer.context.DuplicateMap
dataclass
¶
DuplicateMap(
canonical: Mapping[str, str] = dict(),
exact: frozenset[str] = frozenset(),
)
Which documents were collapsed into which.
Attributes:
| Name | Type | Description |
|---|---|---|
canonical |
Mapping[str, str]
|
Duplicate path to the path that represents it. A path absent from this mapping is itself canonical. |
exact |
frozenset[str]
|
Paths collapsed because they were byte-identical, as opposed to merely similar. Rendering distinguishes the two, because one is lossless and the other is not. |
members ¶
members(canonical_path: str) -> tuple[str, ...]
Paths collapsed into canonical_path, in path order.
History Compaction¶
Reduce a conversation rather than a corpus, without breaking tool-call pairing. Call it
directly, or hand anyinfer.HistoryPolicy to a client and let every frontend built on that
client apply the same rules on the request path.
anyinfer.context.compact_history ¶
compact_history(
messages: Iterable[Message],
*,
max_tokens: int,
estimator: TokenEstimator | None = None,
keep_recent: int = DEFAULT_KEEP_RECENT,
keep_system: bool = True,
observer: Observer | None = None,
) -> HistoryCompaction
Shrink a conversation to fit a token budget without invalidating it.
Three passes over the unprotected middle, cheapest loss first: tool-result payloads are elided, then long text payloads, then plain messages are dropped outright. Each pass stops the moment the conversation fits, so a transcript that needs one large tool result elided loses exactly that and nothing else.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
messages
|
Iterable[Message]
|
The conversation so far. |
required |
max_tokens
|
int
|
The budget. Normally |
required |
estimator
|
TokenEstimator | None
|
Token counting strategy; defaults to the byte heuristic. |
None
|
keep_recent
|
int
|
Trailing messages held at full fidelity. |
DEFAULT_KEEP_RECENT
|
keep_system
|
bool
|
Whether system messages are protected wherever they appear. Leave this on unless the application's system prompt is genuinely disposable. |
True
|
observer
|
Observer | None
|
Receives a |
None
|
Returns:
| Type | Description |
|---|---|
HistoryCompaction
|
The |
HistoryCompaction
|
alone exceed the budget comes back unchanged and honest rather than mutilated. |
Raises:
| Type | Description |
|---|---|
ValueError
|
On a non-positive budget or a negative |
anyinfer.context.HistoryCompaction
dataclass
¶
HistoryCompaction(
messages: tuple[Message, ...],
original_count: int,
dropped_count: int,
elided_results: int,
elided_texts: int,
estimated_tokens: int,
original_tokens: int,
max_tokens: int,
fits: bool,
)
What compaction produced, and what it cost.
Attributes:
| Name | Type | Description |
|---|---|---|
messages |
tuple[Message, ...]
|
The compacted conversation, ready to send. |
original_count |
int
|
Messages offered. |
dropped_count |
int
|
Messages removed entirely. |
elided_results |
int
|
Tool results whose payload was replaced by a marker. |
elided_texts |
int
|
Text parts whose payload was replaced by a marker. |
estimated_tokens |
int
|
Planning-side estimate of the compacted conversation. |
original_tokens |
int
|
The same estimate before compaction. |
max_tokens |
int
|
The budget it was held to. |
fits |
bool
|
Whether the result is within that budget. |
anyinfer.context.DEFAULT_KEEP_RECENT
module-attribute
¶
DEFAULT_KEEP_RECENT = 6
Trailing messages held at full fidelity.
Six covers a tool call, its result, and the exchange around them — enough that the model is never asked to continue from a turn it cannot see.
anyinfer.HistoryPolicy
dataclass
¶
HistoryPolicy(
enabled: bool = True,
mode: HistoryMode = "last_resort",
keep_recent: int = 6,
keep_system: bool = True,
)
Opt-in conversation compaction, applied by the client on the request path.
A prompt that outgrows the window has two possible answers: send it somewhere with a
bigger window, or make it smaller. The router has always owned the first
(Route.context_window_targets). This owns the second, at the same layer, so the
Python API, the command line, and the OpenAI-compatible frontend all behave the same
way, because all three are the same client wearing different skins.
Compaction is never silent: it emits a ContextReduced telemetry event, and it is
off unless a policy is supplied.
Attributes:
| Name | Type | Description |
|---|---|---|
enabled |
bool
|
Whether to compact at all. Present so a configuration file can turn the policy off without deleting how it was tuned. |
mode |
HistoryMode
|
|
keep_recent |
int
|
Trailing messages held at full fidelity. |
keep_system |
bool
|
Whether system messages are protected wherever they appear. |
See anyinfer.context.compact_history for the rules compaction follows and for the
same behaviour as a function you call yourself.
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. 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.
Three optional settings narrow the gap without an index or a model. Identifier
splitting tokenizes resolve_credentials as its parts as well as the whole, so a query
phrased in words matches an identifier phrased in code. Query expansion ranks once,
harvests distinctive terms from the strongest documents, and re-ranks, which does find
"login" from "authentication" whenever the two co-occur anywhere in the corpus. And
centrality scores a document by its position in the corpus's own import graph, a
query-independent signal that is what orders a corpus when the query is weak or absent.
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.
SemanticRanker ¶
Bases: Protocol
Caller-supplied relevance scoring for context reduction.
The default ranking is lexical and offline on purpose; this protocol is the opt-in
seam for a semantic ranker backed by a rerank model. Scores are keyed by
ContextDocument.path — a document absent from the mapping scores 0.0. Scores are
only compared against each other within one call, never persisted.
Implementations live outside this package (context reduction is a leaf consumer
and never imports the client); anyinfer.semantic_ranker builds one from a client
and a rerank target.
scores ¶
scores(
documents: Sequence[ContextDocument], query: str
) -> Mapping[str, float]
Score every document's relevance to query, keyed by document path.
tokenize ¶
tokenize(
text: str, *, split_identifiers: bool = False
) -> list[str]
Split text into lowercase alphanumeric terms, dropping stop words.
ASCII-only by design — the tradeoff is stated in the module documentation.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
text
|
str
|
What to tokenize. |
required |
split_identifiers
|
bool
|
Also emit the parts of compound identifiers.
|
False
|
Returns:
| Type | Description |
|---|---|
list[str]
|
The terms, in order of appearance. |
build_rank_cache ¶
build_rank_cache(
documents: Iterable[ContextDocument],
*,
split_identifiers: bool = False,
) -> 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.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
documents
|
Iterable[ContextDocument]
|
The corpus. |
required |
split_identifiers
|
bool
|
Tokenize compound identifiers into their parts as well. Must
match the setting ranking will use; |
False
|
Returns:
| Type | Description |
|---|---|
RankCache
|
The statistics. |
score_document ¶
score_document(
document: ContextDocument,
query_terms: Mapping[str, float],
cache: RankCache,
) -> float
Score one document against a query.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
document
|
ContextDocument
|
The candidate. |
required |
query_terms
|
Mapping[str, float]
|
Query terms mapped to their weights. |
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. |
query_terms ¶
query_terms(
query: str, *, tuning: ContextTuning = DEFAULT_TUNING
) -> dict[str, float]
Tokenize a query into weighted terms, without expansion.
The base a expand_query call starts from, and what score_document wants when no
expansion is configured.
expand_query ¶
expand_query(
query: str,
documents: Sequence[ContextDocument],
*,
cache: RankCache,
tuning: ContextTuning = DEFAULT_TUNING,
) -> dict[str, float]
Add distinctive terms from the strongest documents to the query.
Pseudo-relevance feedback: rank once against the query as written, take the top documents on faith, and harvest the terms that make them distinctive — high frequency within that set, low frequency across the corpus. Those terms join the query at a reduced weight and everything is ranked again.
This is the lexical answer to vocabulary mismatch. It has no index and no model, and
it finds a file that says "login" from a query that says "authentication" whenever
some document in the corpus uses both. It also inherits the classic failure mode: if
the top documents are wrong, expansion makes them wronger, which is why
expansion_weight defaults well below one.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
query
|
str
|
The query as written. |
required |
documents
|
Sequence[ContextDocument]
|
The corpus. |
required |
cache
|
RankCache
|
Statistics for that corpus. |
required |
tuning
|
ContextTuning
|
Supplies |
DEFAULT_TUNING
|
Returns:
| Type | Description |
|---|---|
dict[str, float]
|
Term weights, with the original query terms at full weight. Returns the |
dict[str, float]
|
unexpanded terms when expansion is disabled, the query is empty, or the corpus |
dict[str, float]
|
has nothing to harvest. |
salience ¶
salience(
documents: Sequence[ContextDocument],
*,
tuning: ContextTuning = DEFAULT_TUNING,
) -> dict[str, float]
Score documents by their centrality in the corpus's own import graph.
An edge runs from a document to every document whose filename stem it imports. The stationary distribution of a damped random walk over those edges answers "what does this corpus depend on?", which is query-independent, and therefore what orders a corpus when the query is weak or missing entirely. Ranking with an empty query otherwise falls through to the path tie-break, which is arbitrary.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
documents
|
Sequence[ContextDocument]
|
The corpus. |
required |
tuning
|
ContextTuning
|
Supplies |
DEFAULT_TUNING
|
Returns:
| Type | Description |
|---|---|
dict[str, float]
|
Path to a score in |
dict[str, float]
|
one. An empty mapping when the corpus has no resolvable edges at all, so callers |
dict[str, float]
|
can skip the blend entirely. |
rank ¶
rank(
documents: Sequence[ContextDocument],
query: str,
*,
rank_cache: RankCache | None = None,
tuning: ContextTuning = DEFAULT_TUNING,
carry_over: Iterable[str] = (),
) -> 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 — unless
|
required |
rank_cache
|
RankCache | None
|
Precomputed statistics for this corpus; built on the fly when absent,
and rebuilt when its tokenization disagrees with |
None
|
tuning
|
ContextTuning
|
Advanced settings. Defaults reproduce the plain lexical ranker. |
DEFAULT_TUNING
|
carry_over
|
Iterable[str]
|
Paths an earlier reduction already sent unchanged. Each receives
|
()
|
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. |
scores_for ¶
scores_for(
documents: Sequence[ContextDocument],
query: str,
*,
cache: RankCache,
tuning: ContextTuning = DEFAULT_TUNING,
) -> dict[str, float]
Relevance scores keyed by path, for selectors that need magnitudes not order.
rank() returns an ordering, which is all a greedy fill needs. Density ordering and
the diversity penalty need the numbers themselves.
anyinfer.context.SemanticRanker ¶
Bases: Protocol
Caller-supplied relevance scoring for context reduction.
The default ranking is lexical and offline on purpose; this protocol is the opt-in
seam for a semantic ranker backed by a rerank model. Scores are keyed by
ContextDocument.path — a document absent from the mapping scores 0.0. Scores are
only compared against each other within one call, never persisted.
Implementations live outside this package (context reduction is a leaf consumer
and never imports the client); anyinfer.semantic_ranker builds one from a client
and a rerank target.
scores ¶
scores(
documents: Sequence[ContextDocument], query: str
) -> Mapping[str, float]
Score every document's relevance to query, keyed by document path.
anyinfer.semantic_ranker ¶
semantic_ranker(
client: Client,
target: Target,
*,
batch: BatchPolicy | None = None,
) -> SemanticRanker
Build a SemanticRanker for anyinfer.context.select from a rerank target.
One rerank call per reduction, spending real provider usage — which is exactly why
context reduction does not do this by default. Reduction is synchronous, so this
takes the synchronous Client.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
client
|
Client
|
An open synchronous client configured with the target's provider. |
required |
target
|
Target
|
A rerank-capable target, e.g. |
required |
batch
|
BatchPolicy | None
|
Batching policy for corpora larger than the provider's document limit.
Splitting a rerank produces chunk-local scores, so enabling
|
None
|
Returns:
| Type | Description |
|---|---|
SemanticRanker
|
An object satisfying the |
anyinfer.context.build_rank_cache ¶
build_rank_cache(
documents: Iterable[ContextDocument],
*,
split_identifiers: bool = False,
) -> 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.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
documents
|
Iterable[ContextDocument]
|
The corpus. |
required |
split_identifiers
|
bool
|
Tokenize compound identifiers into their parts as well. Must
match the setting ranking will use; |
False
|
Returns:
| Type | Description |
|---|---|
RankCache
|
The statistics. |
anyinfer.context.tokenize ¶
tokenize(
text: str, *, split_identifiers: bool = False
) -> list[str]
Split text into lowercase alphanumeric terms, dropping stop words.
ASCII-only by design — the tradeoff is stated in the module documentation.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
text
|
str
|
What to tokenize. |
required |
split_identifiers
|
bool
|
Also emit the parts of compound identifiers.
|
False
|
Returns:
| Type | Description |
|---|---|
list[str]
|
The terms, in order of appearance. |
anyinfer.context.expand_query ¶
expand_query(
query: str,
documents: Sequence[ContextDocument],
*,
cache: RankCache,
tuning: ContextTuning = DEFAULT_TUNING,
) -> dict[str, float]
Add distinctive terms from the strongest documents to the query.
Pseudo-relevance feedback: rank once against the query as written, take the top documents on faith, and harvest the terms that make them distinctive — high frequency within that set, low frequency across the corpus. Those terms join the query at a reduced weight and everything is ranked again.
This is the lexical answer to vocabulary mismatch. It has no index and no model, and
it finds a file that says "login" from a query that says "authentication" whenever
some document in the corpus uses both. It also inherits the classic failure mode: if
the top documents are wrong, expansion makes them wronger, which is why
expansion_weight defaults well below one.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
query
|
str
|
The query as written. |
required |
documents
|
Sequence[ContextDocument]
|
The corpus. |
required |
cache
|
RankCache
|
Statistics for that corpus. |
required |
tuning
|
ContextTuning
|
Supplies |
DEFAULT_TUNING
|
Returns:
| Type | Description |
|---|---|
dict[str, float]
|
Term weights, with the original query terms at full weight. Returns the |
dict[str, float]
|
unexpanded terms when expansion is disabled, the query is empty, or the corpus |
dict[str, float]
|
has nothing to harvest. |
anyinfer.context.salience ¶
salience(
documents: Sequence[ContextDocument],
*,
tuning: ContextTuning = DEFAULT_TUNING,
) -> dict[str, float]
Score documents by their centrality in the corpus's own import graph.
An edge runs from a document to every document whose filename stem it imports. The stationary distribution of a damped random walk over those edges answers "what does this corpus depend on?", which is query-independent, and therefore what orders a corpus when the query is weak or missing entirely. Ranking with an empty query otherwise falls through to the path tie-break, which is arbitrary.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
documents
|
Sequence[ContextDocument]
|
The corpus. |
required |
tuning
|
ContextTuning
|
Supplies |
DEFAULT_TUNING
|
Returns:
| Type | Description |
|---|---|
dict[str, float]
|
Path to a score in |
dict[str, float]
|
one. An empty mapping when the corpus has no resolvable edges at all, so callers |
dict[str, float]
|
can skip the blend entirely. |
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. |
required |
Returns:
| Type | Description |
|---|---|
str
|
The extract, or |
str
|
|
str
|
summary. |
anyinfer.context.imported_names ¶
imported_names(
content: str, *, language: str | None = None
) -> tuple[str, ...]
Names a document imports, as bare identifiers.
The corpus's own dependency graph is derivable from this: a name here that matches another document's filename stem is an edge. Used for query-independent centrality ranking, where "which files does everything else depend on?" is the whole question.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
content
|
str
|
The document text. |
required |
language
|
str | None
|
Accepted for symmetry with the rest of this module and to allow future per-language refinement; the current extraction is language-agnostic. |
None
|
Returns:
| Type | Description |
|---|---|
str
|
Distinct identifiers, in first-appearance order. Path separators, dots, and |
...
|
|
tuple[str, ...]
|
|
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.
The default for ContextTuning.rollup_share, which is what actually applies.
Compaction¶
The fidelity between a structural extract and a whole file.
anyinfer.context.compact_source ¶
compact_source(
content: str,
*,
language: str | None = None,
path: str | None = None,
) -> CompactSource
Remove commentary and blank runs from a document.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
content
|
str
|
The document text. |
required |
language
|
str | None
|
The language, from |
None
|
path
|
str | None
|
Used to infer the language when one was not supplied. |
None
|
Returns:
| Type | Description |
|---|---|
CompactSource
|
The |
CompactSource
|
yields the input with blank runs collapsed and nothing else touched — a safe |
CompactSource
|
no-op is always better than a guess at foreign syntax. |
anyinfer.context.CompactSource
dataclass
¶
CompactSource(
text: str, original_lines: int, elided_lines: int
)
A document with its commentary removed.
Attributes:
| Name | Type | Description |
|---|---|---|
text |
str
|
The compacted content. |
original_lines |
int
|
Line count before compaction. |
elided_lines |
int
|
How many lines were removed. Zero means compaction found nothing
to drop, and |
anyinfer.context.supports_compaction ¶
supports_compaction(language: str | None) -> bool
Whether compaction knows how to shorten this language.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
language
|
str | None
|
A language name from |
required |
Returns:
| Type | Description |
|---|---|
bool
|
Whether |
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 for sub-document splitting, shared by packed and distill.
Large enough for a whole function, small enough to pack several. The canonical home for
this default; anyinfer.context.pack imports it rather than redefining it, since a chunk
size chosen for packed's splitter is the same number ContextTuning.chunk_tokens
means for distill.
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.
SupportsGenerateSync ¶
Bases: Protocol
The synchronous mirror of SupportsGenerate, satisfied by anyinfer.Client.
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 |
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 |
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 |
None
|
Returns:
| Type | Description |
|---|---|
Distillation
|
The |
Raises:
| Type | Description |
|---|---|
ConfigError
|
When |
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 |
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 |
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 |
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.
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.
anyinfer.context.render_compact_block ¶
render_compact_block(
document: ContextDocument,
text: str,
*,
elided_lines: int,
) -> str
Render a document with its commentary removed, saying how much was removed.
A distinct element from render_file_block on purpose: a reader must be able to tell
a whole file from a shortened one, and elided_lines makes the shortening a number
rather than an impression.
anyinfer.context.render_duplicate_block ¶
render_duplicate_block(
path: str, canonical: str, *, identical: bool
) -> str
Render a pointer from a collapsed document to the one that represents it.
identical="true" means byte-for-byte, and nothing was lost. "false" means the
documents were merely similar above the configured threshold, and this one's
differences are not in the envelope — a real loss of fidelity, stated rather than
implied.
anyinfer.context.ENVELOPE_FORMAT
module-attribute
¶
ENVELOPE_FORMAT = 1
Version stamped on every rendered wrapper.
Bumped when an existing element's meaning changes, not when a new one is added: a reader that ignores unknown elements keeps working across additions, which is the point of declaring the version at all.