Capabilities¶
Provenance-tagged model metadata: every value knows whether it was cataloged, discovered, probed, or defaulted. The reasoning is in capabilities and provenance; token estimation and the budget calculator are explained in token estimation and context budgets.
anyinfer.ModelCapabilities
dataclass
¶
ModelCapabilities(
context_window: Sourced[int] | None = None,
max_output_tokens: Sourced[int] | None = None,
features: Sourced[Feature] = Sourced(
Feature(0), "default"
),
pricing: Sourced[Pricing] | None = None,
default_temperature: Sourced[float] | None = None,
default_top_p: Sourced[float] | None = None,
local: LocalModelInfo | None = None,
operations: Sourced[frozenset[InferenceOperation]]
| None = None,
embedding: EmbeddingCapabilities | None = None,
)
What a model can do, as far as we know.
Attributes:
| Name | Type | Description |
|---|---|---|
context_window |
Sourced[int] | None
|
Maximum tokens of input context, with provenance; |
max_output_tokens |
Sourced[int] | None
|
Maximum tokens one response may contain, with provenance;
|
features |
Sourced[Feature]
|
Which |
pricing |
Sourced[Pricing] | None
|
Per-million-token pricing, when known. |
default_temperature |
Sourced[float] | None
|
The temperature this provider applies when a request sends
none, with provenance; |
default_top_p |
Sourced[float] | None
|
The nucleus-sampling cutoff this provider applies when a request
sends none, with provenance; |
local |
LocalModelInfo | None
|
Facts about the local artifact, for locally-run models only. |
operations |
Sourced[frozenset[InferenceOperation]] | None
|
Which inference operations this model serves, with provenance;
|
embedding |
EmbeddingCapabilities | None
|
Vector facts this model states about itself — the dimensions and input
ceiling a listing or a pinned catalog row declares. |
The two sampling defaults exist so an application can say what "provider default"
means instead of only that it is one. They are populated from a provider's own
documentation and nowhere else; never probed, never inferred from a sibling
provider, never carried over from a model family. A provider whose documentation
states no default keeps None indefinitely, and that is the correct final state for
it rather than a gap waiting to be filled: an invented number presented beside a
provenance tag is precisely the estimate-as-authority this type exists to prevent.
overlay ¶
overlay(other: ModelCapabilities) -> ModelCapabilities
Layer other on top of this, field by field, stronger provenance winning.
This is the assembly rule: later layers override earlier ones, but a weaker-provenance value never displaces a stronger one.
anyinfer.Pricing
dataclass
¶
Pricing(
input_per_1m: Decimal,
output_per_1m: Decimal,
cache_read_per_1m: Decimal | None = None,
cache_write_per_1m: Decimal | None = None,
currency: str = "USD",
per_search_unit: Decimal | None = None,
per_server_tool_use: Mapping[str, Decimal] = dict(),
)
Per-million-token pricing used to compute cost_usd.
Cache rates are optional and default to unknown rather than to the input rate. A provider that discounts cached prompt tokens but whose discount we have not recorded must not be billed as though the discount were zero or as though it were free — an unknown rate leaves cached tokens priced as ordinary input, which is the same answer this library gave before cache accounting existed, and is wrong in only one direction that a caller can reason about.
Attributes:
| Name | Type | Description |
|---|---|---|
input_per_1m |
Decimal
|
Price per one million prompt tokens. |
output_per_1m |
Decimal
|
Price per one million generated tokens. |
cache_read_per_1m |
Decimal | None
|
Price per one million prompt tokens served from the provider's
cache, or |
cache_write_per_1m |
Decimal | None
|
Price per one million prompt tokens written into the cache, or
|
currency |
str
|
Currency code the prices are quoted in. |
per_search_unit |
Decimal | None
|
Price per one billed search unit, for rerank providers that bill
by searches rather than tokens. |
per_server_tool_use |
Mapping[str, Decimal]
|
Price per invocation of a provider-run tool, keyed by kind.
Web search is billed per search rather than per token, so a generation that
searched costs more than its token counts say — and a caller who set
|
anyinfer.Feature ¶
Bases: Flag
Capabilities a model may support.
Structured-output mechanism selection reads these in the order
GRAMMAR > JSON_SCHEMA > JSON_MODE > prompt injection.
CACHE_USAGE and CACHE_PLACEMENT are deliberately separate facts: reporting what
the prompt cache did is not the same as accepting instructions about where it should
apply, and a provider may do either without the other.
LOGPROBS is a model fact rather than a provider one: several dialects accept the
field on their completion models and reject it on their reasoning models, so it is
carried here beside the input modalities instead of on the descriptor.
anyinfer.Mechanism
module-attribute
¶
Mechanism = Literal[
"grammar", "json_schema", "json_mode", "prompt"
]
How structured output was requested of the provider.
anyinfer.Sourced
dataclass
¶
Sourced(value: _T, provenance: Provenance = 'default')
Bases: Generic[_T]
A capability value paired with its provenance.
outranks ¶
outranks(other: Sourced[_T] | None) -> bool
Whether this value's provenance is at least as strong as other's.
anyinfer.Provenance
module-attribute
¶
Provenance = Literal[
"catalog", "discovered", "probed", "default", "override"
]
Where a capability value came from, weakest (default) to strongest (override).
anyinfer.Health
dataclass
¶
Health(ok: bool, detail: str = '')
Result of a provider's cheap readiness probe.
Attributes:
| Name | Type | Description |
|---|---|---|
ok |
bool
|
Whether the provider answered its readiness probe successfully. |
detail |
str
|
Short human-readable explanation, most useful when |
anyinfer.DiscoveredModel
dataclass
¶
DiscoveredModel(
id: str, capabilities: ModelCapabilities | None = None
)
A model reported by a provider's listing endpoint.
capabilities carries only fields the provider actually reported; the capability
assembler tags them "discovered".
Attributes:
| Name | Type | Description |
|---|---|---|
id |
str
|
The model identifier exactly as the provider lists it. |
capabilities |
ModelCapabilities | None
|
Capability fields the listing reported; |
anyinfer.LocalModelInfo
dataclass
¶
LocalModelInfo(
artifact_size_bytes: int | None = None,
parameter_size: str | None = None,
quantization: str | None = None,
est_ram_bytes: int | None = None,
est_vram_bytes: int | None = None,
observed_vram_bytes: int | None = None,
)
Facts about a local model artifact, used for tuning and recommendation.
Attributes:
| Name | Type | Description |
|---|---|---|
artifact_size_bytes |
int | None
|
On-disk size of the model weights. |
parameter_size |
str | None
|
Parameter count as the runtime reports it (e.g. |
quantization |
str | None
|
Quantization scheme of the artifact (e.g. |
est_ram_bytes |
int | None
|
Estimated system memory needed to run the model. |
est_vram_bytes |
int | None
|
Estimated GPU memory needed to run the model. |
observed_vram_bytes |
int | None
|
GPU memory actually measured in use while the model was loaded, when the runtime reports it. |
anyinfer.ContextBudget
dataclass
¶
ContextBudget(
context_window: Sourced[int] | None,
estimate: RequestEstimate,
output_reserve_tokens: int,
headroom_tokens: int,
pricing: Sourced[Pricing] | None = None,
)
A request's estimated size held against a model's known capacity.
The verdict is tri-state: fits is True/False when the context window
is known, and None when it is not — an unknown capacity is reported as unknown,
never guessed.
Attributes:
| Name | Type | Description |
|---|---|---|
context_window |
Sourced[int] | None
|
The model's context window with its provenance, or |
estimate |
RequestEstimate
|
The per-component input-token estimate. |
output_reserve_tokens |
int
|
Tokens reserved for the response. |
headroom_tokens |
int
|
Safety margin against estimation error. |
pricing |
Sourced[Pricing] | None
|
The model's per-token rates with their provenance, when known. |
input_allowance_tokens
property
¶
input_allowance_tokens: int | None
Tokens the input may spend, or None when the window is unknown.
remaining_tokens
property
¶
remaining_tokens: int | None
Allowance left after the estimated input; negative when over budget.
This is the number an app packs context against: keep adding material while it stays positive.
fits
property
¶
fits: bool | None
Whether the estimated request fits the allowance; None when unknowable.
estimated_cost
property
¶
estimated_cost: CostEstimate | None
A preflight cost range, or None when no trustworthy pricing exists.
Estimated money never mixes with reported money:
cost_usd is only ever computed from
provider-reported usage, and this range is only ever computed from the estimate.
anyinfer.TokenEstimate
dataclass
¶
TokenEstimate(tokens: int, floor: int)
A token count carried as a planning estimate and a defensible lower bound.
Attributes:
| Name | Type | Description |
|---|---|---|
tokens |
int
|
The planning figure, deliberately conservative-high. |
floor |
int
|
A lower bound the true count is not realistically below. An exact
tokenizer sets |
anyinfer.TokenEstimator ¶
Bases: Protocol
Pluggable token counting.
Implementations may be heuristic (the shipped default) or exact (tiktoken, a
provider's tokenize endpoint). Exact implementations should return
TokenEstimate(n, n) so the gate can act on their counts with full force.
anyinfer.HeuristicTokenEstimator
dataclass
¶
HeuristicTokenEstimator(multiplier: float = 1.0)
The dependency-free default: token counts from UTF-8 byte counts.
Attributes:
| Name | Type | Description |
|---|---|---|
multiplier |
float
|
Calibration factor applied to the planning estimate, for providers whose transport envelope inflates reported prompt tokens beyond the serialized bytes. The floor is never inflated — envelope overhead is not something a lower bound may claim. |
anyinfer.TiktokenEstimator ¶
TiktokenEstimator(encoding: str | None = None)
Exact token counts via tiktoken, for models whose encoding it publishes.
Exact for OpenAI's own models and for the many open-weight families that adopted the
same encodings. Not exact for Anthropic, Gemini, or Cohere, whose tokenizers are
not published — those fall back to DEFAULT_ENCODING, which is a better guess than
counting bytes but is still a guess, and is reported as one.
Instances cache their encodings process-wide, so for_model may be called per request.
The first load of a given encoding is not cheap: tiktoken fetches the vocabulary
over the network unless it is already in its cache (TIKTOKEN_CACHE_DIR). That
happens at construction rather than at first count, deliberately — a server should
discover a missing vocabulary while starting up, not part-way through a request.
Deployments that must not reach the network at run time should pre-warm the cache in
their image build.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
encoding
|
str | None
|
Pin one encoding for every model instead of selecting per model. Use when serving a known open-weight family through an OpenAI-compatible endpoint, where the model id tells tiktoken nothing. |
None
|
Raises:
| Type | Description |
|---|---|
ConfigError
|
If |
tokenizer_kind
class-attribute
¶
tokenizer_kind: TokenizerKind = 'tiktoken'
Which exact-counting strategy this implements, matched against a provider's
declared TokenCalibration.tokenizer before its counts are trusted as exact.
for_model ¶
for_model(
provider_id: str, model: str
) -> TiktokenEstimator
Return an estimator using the encoding this model actually uses.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
provider_id
|
str
|
The resolved provider; unused today and accepted because the mapping from model id to encoding is a provider's fact, and a future provider-specific table belongs here rather than at the call site. |
required |
model
|
str
|
The resolved model id. |
required |
Returns:
| Type | Description |
|---|---|
TiktokenEstimator
|
|
TiktokenEstimator
|
encoding tiktoken names for this model, or the default when it names none. |
estimate ¶
estimate(text: str) -> TokenEstimate
Count text with this instance's encoding.
Returns:
| Type | Description |
|---|---|
TokenEstimate
|
|
TokenEstimate
|
pre-dispatch gate can act on the floor with full force. Otherwise the count is |
TokenEstimate
|
the planning figure and the floor is held slightly below it, because a |
TokenEstimate
|
substituted encoding can over-count and a floor that over-claims would refuse |
TokenEstimate
|
requests that fit. |
anyinfer.AnthropicCountTokensEstimator ¶
AnthropicCountTokensEstimator(
*,
api_key: str,
model: str = "claude-sonnet-4-5",
base_url: str = "https://api.anthropic.com",
base: TokenEstimator | None = None,
transport: Any = None,
max_entries: int = DEFAULT_CACHE_ENTRIES,
)
Bases: _RemoteCountingEstimator
Exact counts from Anthropic's own POST /v1/messages/count_tokens.
Anthropic does not publish its tokenizer, so this endpoint is the only exact count
available for Claude models — TiktokenEstimator falls back to an OpenAI encoding
for them, which is a better guess than counting bytes but is still a guess.
The endpoint counts a whole message list, not a string, so each text is counted as a
single user turn. That includes Anthropic's own per-message framing, which makes the
count marginally high for a text that will travel as part of a longer conversation —
high is the safe direction for a planning figure, and the framing is exactly what
estimate_request would otherwise add by heuristic.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
api_key
|
str
|
The credential to count with. Counting is free but authenticated. |
required |
model
|
str
|
The model whose tokenizer to count with. Anthropic requires one, and the count is model-specific. |
'claude-sonnet-4-5'
|
base_url
|
str
|
Override the API root, for a proxy or a test transport. |
'https://api.anthropic.com'
|
base
|
TokenEstimator | None
|
Estimator for texts that were not prewarmed. |
None
|
transport
|
Any
|
An HTTP transport to use instead of the default. |
None
|
max_entries
|
int
|
Counted texts to remember. |
DEFAULT_CACHE_ENTRIES
|
tokenizer_kind
class-attribute
¶
tokenizer_kind: TokenizerKind = 'anthropic_count_tokens'
Matched against a provider's declared TokenCalibration.tokenizer before these
counts are trusted as exact.
prewarm
async
¶
prewarm(texts: Sequence[str]) -> None
Count each unknown text, tolerating a service that will not answer.
anyinfer.LlamaServerTokenizeEstimator ¶
LlamaServerTokenizeEstimator(
*,
base_url: str = "http://127.0.0.1:8080",
api_key: str | None = None,
base: TokenEstimator | None = None,
transport: Any = None,
max_entries: int = DEFAULT_CACHE_ENTRIES,
)
Bases: _RemoteCountingEstimator
Exact counts from a running llama-server's POST /tokenize.
The tokenizer here is the one loaded with the weights, so the count is exact for whatever GGUF the server is serving — including quantized community models whose vocabulary is not published anywhere a local tokenizer could find it.
Unlike the hosted case this is a loopback call, but the reasoning is the same: the estimator protocol is synchronous and the event loop is shared, so the fetch happens ahead of the counting rather than inside it.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
base_url
|
str
|
Where the server is listening. |
'http://127.0.0.1:8080'
|
api_key
|
str | None
|
A credential, for a server started with |
None
|
base
|
TokenEstimator | None
|
Estimator for texts that were not prewarmed. |
None
|
transport
|
Any
|
An HTTP transport to use instead of the default. |
None
|
max_entries
|
int
|
Counted texts to remember. |
DEFAULT_CACHE_ENTRIES
|
tokenizer_kind
class-attribute
¶
tokenizer_kind: TokenizerKind = 'llama_server_tokenize'
Matched against a provider's declared TokenCalibration.tokenizer before these
counts are trusted as exact.
prewarm
async
¶
prewarm(texts: Sequence[str]) -> None
Tokenize each unknown text, tolerating a server that is not up.
anyinfer.PrewarmsCounts ¶
Bases: Protocol
An estimator whose exact counts come from a service rather than from local code.
The optional async half of TokenEstimator. A client that
knows it is about to size a request awaits prewarm once, and the synchronous
estimate calls that follow read what it fetched. An estimator that does not
implement this is used exactly as before — this is an extension, not a requirement.
prewarm
async
¶
prewarm(texts: Sequence[str]) -> None
Fetch exact counts for these texts, ignoring ones already known.
Never raises for a service problem: an estimator that cannot reach its counting endpoint degrades to its base estimator, because a request that could have been sized approximately should not fail outright for want of an exact number.
anyinfer.TargetAwareTokenEstimator ¶
Bases: Protocol
An estimator that can specialize itself for one resolved target.
An optional extension to TokenEstimator, not a replacement:
the tokenizer a count needs depends on the model, and TokenEstimator.estimate sees
only text. An estimator implementing this is asked for a specialized instance once per
target; one that does not is used as-is, which is why the shipped heuristic needs no
change.
for_model ¶
for_model(provider_id: str, model: str) -> TokenEstimator
Return the estimator to use for one provider and model.
anyinfer.RequestEstimate
dataclass
¶
RequestEstimate(
messages: TokenEstimate,
tools: TokenEstimate,
schema: TokenEstimate,
envelope: TokenEstimate = TokenEstimate(0, 0),
unpriced_parts: int = 0,
)
Content-free size accounting for one request, by component.
The breakdown follows the typed request itself: what the caller said, what tools were offered, and what schema was attached — the three things that occupy input tokens on any provider.
Attributes:
| Name | Type | Description |
|---|---|---|
messages |
TokenEstimate
|
The conversation, including per-message wire-framing overhead. |
tools |
TokenEstimate
|
Serialized tool specifications, when any were offered. |
schema |
TokenEstimate
|
The structured-output schema, when one was requested. Counted whether the wire carries it natively or the core injects it into the prompt — either way it occupies input tokens. |
envelope |
TokenEstimate
|
What the provider's own transport adds around all of the above, from its
declared |
anyinfer.TokenCalibration
dataclass
¶
TokenCalibration(
multiplier: float = 1.0,
overhead_tokens: int = 0,
tokenizer: TokenizerKind | None = None,
tokenizer_provenance: Provenance = "default",
)
How much a provider's own envelope inflates the prompt it is sent.
Serialized request bytes are not what every provider counts. Some wrap the caller's messages in a transport of their own before the model ever sees them — a session API that prepends its harness, a tool scaffold, a service-side system preamble, and then bill (and window-check) the inflated total. Estimating such a provider from message bytes alone under-counts every request, and the under-count is systematic rather than noise, so budgets stay optimistic right up to the overflow.
A provider therefore declares its own correction, and only the planning figure moves:
multiplierscales content that grows with the prompt.overhead_tokensadds what the envelope costs regardless of prompt size.
Neither touches the estimate's floor. The floor exists to refuse requests before dispatch, and a lower bound may only claim tokens the provider certainly charges — envelope overhead is a correction we believe, not one we can prove.
A provider also declares how its tokens can be counted exactly, when they can be. That belongs here rather than in a table beside it because it is the same fact from the same source: what this provider's numbers mean and where an exact one comes from.
Attributes:
| Name | Type | Description |
|---|---|---|
multiplier |
float
|
Factor applied to the planning estimate of prompt-proportional
content. |
overhead_tokens |
int
|
Flat tokens the envelope adds per request, counted once. |
tokenizer |
TokenizerKind | None
|
Which exact-counting strategy this provider's models can use, or
|
tokenizer_provenance |
Provenance
|
How the |
__post_init__ ¶
__post_init__() -> None
Reject calibrations that would corrupt every estimate downstream.
Raises:
| Type | Description |
|---|---|
ValueError
|
If the multiplier is not a positive finite number, or the overhead is negative. |
anyinfer.TokenizerKind
module-attribute
¶
TokenizerKind = Literal[
"tiktoken",
"anthropic_count_tokens",
"llama_server_tokenize",
]
Exact-counting strategies AnyInfer knows how to drive.
Each names a real endpoint or library, not a family of them: tiktoken is the published
OpenAI-family vocabulary, anthropic_count_tokens is Anthropic's own counting endpoint,
and llama_server_tokenize is a supervised llama-server's /tokenize. A provider whose
tokenizer is neither published nor exposed declares nothing.
anyinfer.build_context_budget ¶
build_context_budget(
request: GenerationRequest,
capabilities: ModelCapabilities | None,
*,
estimator: TokenEstimator | None = None,
calibration: TokenCalibration | None = None,
output_reserve_tokens: int | None = None,
headroom_tokens: int | None = None,
) -> ContextBudget
Compute the context budget for one request against one model's capabilities.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
request
|
GenerationRequest
|
The request to size. |
required |
capabilities
|
ModelCapabilities | None
|
Assembled capabilities supplying the context window and maximum
output size. |
required |
estimator
|
TokenEstimator | None
|
Token counting strategy; defaults to the byte heuristic. |
None
|
calibration
|
TokenCalibration | None
|
The target provider's declared envelope correction, from its
descriptor. |
None
|
output_reserve_tokens
|
int | None
|
Overrides the derived output reserve. |
None
|
headroom_tokens
|
int | None
|
Overrides the default clamped headroom. |
None
|
Returns:
| Type | Description |
|---|---|
ContextBudget
|
The computed |
Raises:
| Type | Description |
|---|---|
ValueError
|
If an explicit reserve or headroom is negative. |
anyinfer.estimate_request ¶
estimate_request(
request: GenerationRequest,
*,
estimator: TokenEstimator | None = None,
calibration: TokenCalibration | None = None,
) -> RequestEstimate
Estimate the input tokens a request will occupy.
Derived from the typed request rather than hand-fed strings: messages (every content part, plus per-message framing overhead), offered tools, and the schema.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
request
|
GenerationRequest
|
The request to size. |
required |
estimator
|
TokenEstimator | None
|
Token counting strategy; defaults to the byte heuristic. |
None
|
calibration
|
TokenCalibration | None
|
The target provider's declared envelope correction. Applied to the
planning figure only, and reported as its own component so the breakdown still
adds up. |
None
|
Returns:
| Type | Description |
|---|---|
RequestEstimate
|
The per-component estimate. |
anyinfer.check_context_fit ¶
check_context_fit(
request: GenerationRequest,
capabilities: ModelCapabilities | None,
*,
estimator: TokenEstimator | None = None,
calibration: TokenCalibration | None = None,
output_reserve_tokens: int | None = None,
provider: str | None = None,
model: str | None = None,
) -> ContextBudget
Build the budget for a request and raise if it provably cannot fit.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
request
|
GenerationRequest
|
The |
required |
capabilities
|
ModelCapabilities | None
|
The target's assembled capabilities. |
required |
estimator
|
TokenEstimator | None
|
Token counting strategy; defaults to the byte heuristic. |
None
|
calibration
|
TokenCalibration | None
|
The target provider's declared envelope correction. It never affects
the gate's decision — the gate reads the floor, which no calibration moves —
but it keeps the returned budget consistent with the one |
None
|
output_reserve_tokens
|
int | None
|
Overrides the derived output reserve. |
None
|
provider
|
str | None
|
Provider id, for the error's structured fields. |
None
|
model
|
str | None
|
Model id, for the error message. |
None
|
Returns:
| Type | Description |
|---|---|
ContextBudget
|
The computed |
ContextBudget
|
request may proceed. |
Raises:
| Type | Description |
|---|---|
ContextLengthError
|
When the estimate's floor exceeds a trusted-provenance context window. |
anyinfer.ProbeReport
dataclass
¶
ProbeReport(
target: ResolvedTarget,
probes: tuple[FeatureProbe, ...] = (),
capabilities: ModelCapabilities | None = None,
requests: int = 0,
usage: Usage = Usage(),
)
Everything one probing run learned, and what it cost.
Attributes:
| Name | Type | Description |
|---|---|---|
target |
ResolvedTarget
|
The target that was probed. |
probes |
tuple[FeatureProbe, ...]
|
One result per feature tested, in the order tested. |
capabilities |
ModelCapabilities | None
|
What to record at |
requests |
int
|
Round trips spent. |
usage |
Usage
|
Tokens spent, summed across the probes. |
anyinfer.FeatureProbe
dataclass
¶
FeatureProbe(
feature: Feature,
outcome: ProbeOutcome,
detail: str = "",
)
The result of testing one feature against one target.
Attributes:
| Name | Type | Description |
|---|---|---|
feature |
Feature
|
The feature that was tested. |
outcome |
ProbeOutcome
|
What the attempt established. |
detail |
str
|
One sentence explaining the outcome — the provider's rejection, or what came back instead of what was asked for. |
anyinfer.ProbeOutcome
module-attribute
¶
ProbeOutcome = Literal[
"supported", "unsupported", "inconclusive"
]
What one probe established, if anything.
anyinfer.PROBEABLE_FEATURES
module-attribute
¶
PROBEABLE_FEATURES: tuple[Feature, ...] = (
Feature.JSON_SCHEMA,
Feature.JSON_MODE,
Feature.GRAMMAR,
Feature.TOOLS,
Feature.STREAMING,
)
Every feature a probe can settle.
Absent by design: REASONING (providers overwhelmingly accept the field and ignore it,
so a probe would return inconclusive nearly always), SYSTEM_PROMPT and CACHE_USAGE
(no single reply distinguishes honored from ignored), and the numeric bounds — finding a
context window by bisection would cost dozens of requests to learn what one catalog entry
already says.
anyinfer.DEFAULT_PROBE_FEATURES
module-attribute
¶
DEFAULT_PROBE_FEATURES: tuple[Feature, ...] = (
Feature.JSON_SCHEMA,
Feature.JSON_MODE,
Feature.TOOLS,
Feature.STREAMING,
)
What probe() tests when the caller names nothing: the four an OpenAI-compatible
endpoint most often misreports. Four requests. GRAMMAR is excluded because the engines
that have it declare it accurately, so paying a request to confirm buys nothing.
anyinfer.CostEstimate
dataclass
¶
CostEstimate(
low: Decimal, high: Decimal, currency: str = "USD"
)
A preflight cost range for one request.
Deliberately a range, never one number: the input estimate is two-sided
(anyinfer.capabilities.estimate) and the output spend is unknown until the
model stops. Kept strictly separate from
cost_usd, which is only ever computed from
reported usage — estimated and actual money must never be indistinguishable.
Attributes:
| Name | Type | Description |
|---|---|---|
low |
Decimal
|
Floor input tokens priced, with zero output — the least this can cost. |
high |
Decimal
|
Planning-estimate input plus the full output reserve priced — a spend ceiling under the budget's own assumptions. |
currency |
str
|
The pricing currency. |
anyinfer.PricingTable ¶
PricingTable(entries: dict[str, tuple[PricingEntry, ...]])
Per-provider model pricing with prefix-aware lookup.
entries_for ¶
entries_for(provider_id: str) -> tuple[PricingEntry, ...]
Every entry for one provider, or an empty tuple.
lookup ¶
lookup(
provider_id: str, model: str
) -> Sourced[Pricing] | None
from_mapping
classmethod
¶
from_mapping(data: Any) -> PricingTable
Build and validate a table from parsed JSON.
Raises:
| Type | Description |
|---|---|
ConfigError
|
On a malformed document — wrong format version, missing fields, or prices that do not parse as non-negative decimals. |
anyinfer.load_default_pricing
cached
¶
load_default_pricing() -> PricingTable
Load the pricing table bundled with this release.
anyinfer.fetch_pricing ¶
fetch_pricing(
url: str = DEFAULT_PRICING_URL,
*,
timeout_s: float = 30.0,
transport: Any | None = None,
) -> PricingTable
Fetch a maintained pricing table over HTTPS — the explicit freshness opt-in.
Nothing in the library calls this implicitly. An application that wants prices newer
than its installed release calls it on its own schedule and passes the result to the
client's pricing_table.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
url
|
str
|
Where to fetch from; defaults to the repo's continuously-updated file. |
DEFAULT_PRICING_URL
|
timeout_s
|
float
|
Request timeout. |
30.0
|
transport
|
Any | None
|
Test seam — an |
None
|
Returns:
| Type | Description |
|---|---|
PricingTable
|
The fetched, validated table. |
Raises:
| Type | Description |
|---|---|
ConfigError
|
If the fetch fails or the response is not a valid pricing document. |
Spend Accounting¶
An in-process rollup of what a client spent, and an optional ceiling checked before dispatch. Concepts: cost and spending.
anyinfer.SpendLedger ¶
SpendLedger(currency: str = 'USD')
A thread-safe rollup of one client's observed spend.
Subscribe it like any other observer::
ledger = SpendLedger()
client = ai.Client(providers, observers=[ledger])
...
print(ledger.totals().cost)
Two clients that should share a total are given the same ledger. There is deliberately no process-wide instance: a global would make a total depend on import order, and would silently merge the accounting of two libraries that happen to share a process.
Only completed requests are counted. A failed attempt that a retry replaced is not separately visible in the event stream, so its tokens are not in these totals — the figure is "what the successful requests cost", not "everything the provider might bill". Where that distinction matters, compare against the provider's own invoice.
on_event ¶
on_event(event: TelemetryEvent) -> None
Absorb one telemetry event.
Fast and non-blocking, as the observer contract requires: this is arithmetic under a lock, with no I/O.
record ¶
record(
target: ResolvedTarget,
usage: Usage,
*,
request_id: str | None = None,
) -> None
Fold one completed request into the totals.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
target
|
ResolvedTarget
|
What served the request. |
required |
usage
|
Usage
|
Its reported usage, with cost already computed by the core. |
required |
request_id
|
str | None
|
Correlation id, used to attribute the request to the labels its caller supplied. |
None
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If the usage carries a cost in a different currency than this ledger's. Converting would require a rate source, and a converted figure would have no provenance. |
reserve ¶
reserve(
request_id: str,
estimate: Decimal,
ceiling: Decimal | None,
) -> tuple[bool, Decimal, Decimal]
Atomically reserve a preflight estimate against a cumulative ceiling.
Re-reserving the same request replaces its prior estimate, which lets fallback targets update the bound without double-counting one caller request.
release ¶
release(request_id: str) -> None
Release a reservation that will not be replaced by a completion event.
by_target ¶
by_target() -> Mapping[str, SpendTotals]
Totals per provider:model, in first-seen order.
by_label ¶
by_label(key: str) -> Mapping[str, SpendTotals]
Totals per value of one caller-supplied metadata label.
The library never interprets these labels — a tenant id, a feature name, a job id are all the application's vocabulary, carried through untouched.
anyinfer.SpendTotals
dataclass
¶
SpendTotals(
cost: Decimal = Decimal(0),
currency: str = "USD",
requests: int = 0,
unknown_requests: int = 0,
input_tokens: int = 0,
output_tokens: int = 0,
cache_read_tokens: int = 0,
)
What was spent, and what could not be priced.
unknown_requests is the honest counterpart to cost. A provider whose pricing is
absent or untrusted produces no cost at all; never a zero, so a total that reported
only cost would quietly understate spend by however many calls it could not price.
Both numbers travel together for that reason, and every rendering of one should render
the other.
Attributes:
| Name | Type | Description |
|---|---|---|
cost |
Decimal
|
Summed cost of the requests that could be priced. |
currency |
str
|
Currency the costs are in. A ledger refuses to mix currencies rather than converting, because a conversion needs a rate source this library must not have. |
requests |
int
|
Completed requests observed. |
unknown_requests |
int
|
Completed requests whose cost could not be known. |
input_tokens |
int
|
Prompt tokens reported across those requests. |
output_tokens |
int
|
Generated tokens reported across those requests. |
cache_read_tokens |
int
|
Prompt tokens the providers reported serving from cache. |
from_json
classmethod
¶
from_json(data: Mapping[str, Any]) -> SpendTotals | None
Deserialize, returning None for anything unreadable.
anyinfer.SpendStore ¶
SpendStore(path: Path | str)
An optional, caller-owned file of accumulated spend.
The library persists nothing on its own. An application that wants a total that
survives a restart constructs one of these and points it somewhere — the same contract
anyinfer.benchmark.MeasurementStore uses, including its most important property:
reads are total. A missing, truncated, or foreign file yields nothing rather than
raising, because a cache that can break a program is worse than no cache.
load ¶
load() -> Mapping[str, SpendTotals]
Every stored bucket, keyed by name. Unreadable content yields nothing.
accumulate ¶
accumulate(
ledger: SpendLedger, *, bucket: str = "total"
) -> None
Add a ledger's current totals to the stored bucket, atomically.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
ledger
|
SpendLedger
|
The ledger whose totals to fold in. |
required |
bucket
|
str
|
Which stored bucket to add to — a process name, a job id, or the default single bucket. |
'total'
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If the ledger's currency differs from the stored bucket's. |
anyinfer.SpendPolicy
dataclass
¶
SpendPolicy(
max_total_usd: Decimal | None = None,
max_request_usd: Decimal | None = None,
on_unknown: Literal["allow", "refuse"] = "allow",
)
A ceiling on what one client may spend. Off unless supplied.
Checked before dispatch, beside the context gate, so a refusal costs nothing. This is the caller's own policy on their own client — it shares no state with any other process and enforces no organization quota, which this library deliberately leaves to the deployment around it.
Attributes:
| Name | Type | Description |
|---|---|---|
max_total_usd |
Decimal | None
|
Ceiling on this client's cumulative spend. |
max_request_usd |
Decimal | None
|
Ceiling on any single request's estimated cost. |
on_unknown |
Literal['allow', 'refuse']
|
What to do when a target's cost cannot be estimated, because its
pricing is missing or untrusted. |
Rate Governance¶
Client-side pacing for one provider instance, and the header dialect a provider reports its window in. Both are inert until configured. Concepts: routing and rate limits.
anyinfer.RateLimits
dataclass
¶
RateLimits(
max_concurrent: int | None = None,
requests_per_minute: float | None = None,
min_interval_s: float = 0.0,
respect_headers: bool = True,
reserve_fraction: float = 0.0,
)
Client-side pacing for one provider instance. Inert unless configured.
This paces this process's own requests to one provider so an application that fans out does not discover the provider's limits by being throttled by them. It shares no state with any other process, enforces no quota the provider did not state, and never influences which target is chosen — a limiter that picked a different provider because this one was busy would be load balancing, which this library deliberately does not do.
With every field left at its default, a request is dispatched exactly as it was before this existed: no permit, no delay, no bookkeeping.
Attributes:
| Name | Type | Description |
|---|---|---|
max_concurrent |
int | None
|
Most requests in flight at once for this instance. |
requests_per_minute |
float | None
|
Sustained request rate. Enforced as a token bucket, so a burst up to the per-minute allowance is permitted and then paced. |
min_interval_s |
float
|
Smallest gap between two dispatches, for providers that object to bursts regardless of the rate. |
respect_headers |
bool
|
Whether to slow down when the provider's own rate-limit headers say its window is nearly exhausted. Inert when the provider declares no header dialect, since there is nothing to read. |
reserve_fraction |
float
|
Fraction of the provider's stated remaining allowance to leave untouched, between 0 and 1. Matters whenever this process is not the only consumer of the key: stopping at the last request in the window means the other consumer is the one that gets throttled. |
active
property
¶
active: bool
Whether this policy can delay anything.
A bare RateLimits() is active: it means "pace me by what the provider reports",
which is the least a caller who asked for governance at all can mean. Opting out is
spelled by supplying no limits, not by supplying empty ones — so
RateLimits(respect_headers=False) with no bounds is the one inert instance, and
it is inert honestly rather than by accident.
__post_init__ ¶
__post_init__() -> None
Reject a limit that cannot be honoured.
Raises:
| Type | Description |
|---|---|
ValueError
|
On a non-positive bound, a negative interval, or a reserve fraction outside the unit interval. |
anyinfer.RateLimitHeaders
dataclass
¶
RateLimitHeaders(
requests_remaining: str = "",
requests_reset: str = "",
tokens_remaining: str = "",
tokens_reset: str = "",
limit_requests: str = "",
limit_tokens: str = "",
)
Which response headers a provider reports its rate-limit state in.
Header names are wire facts and differ per provider, so they are declared on the descriptor and recorded in that provider's contract snapshot; never branched on by provider id in the core.
A provider whose dialect cannot be verified from its documentation declares nothing.
An empty dialect is not a failure: pacing falls back to whatever bounds the caller
configured, which is a smaller promise honestly kept rather than a guessed header name
that silently reads None forever.
Attributes:
| Name | Type | Description |
|---|---|---|
requests_remaining |
str
|
Requests left in the current window. |
requests_reset |
str
|
When the request window resets. Read as seconds, or as a duration
like |
tokens_remaining |
str
|
Tokens left in the current window. |
tokens_reset |
str
|
When the token window resets, in the same two spellings. |
limit_requests |
str
|
The window's full request allowance, when the provider states it.
Only needed to turn |
limit_tokens |
str
|
The window's full token allowance, for the same reason. |