/** * Operator-declared rate limits — spec §4 "Rung 3: CONFIGURED, operator-asserted". * * A published table cannot express what an operator knows and no ledger can measure: one * account's daily ceiling is shared across EVERY model and key on that account, and inferring it * by counting is exactly what `target-facts.ts` forbids. So it is declared in config, and * `configured` is its honest provenance label — a sibling to `provider-stated` and * `derived:`, never passed off as a measurement. * * Pure lookups only: no IO, no clock, no request-path role of its own. The availability ladders * (spec §5) consume these figures; the limits themselves never refuse a request. * * This module deliberately carries the whole axis vocabulary so a consumer (the availability * lane, a CLI renderer) can import it WITHOUT dragging config internals: the `Config` edge below * is type-only and erased at runtime. */ import type { Config } from "./config-types.js"; import type { QuotaAxis, QuotaPeriod } from "./quota-observation.js"; import { CONFIGURED_LIMIT_AXES, type ConfiguredLimitAxis, type HardRateLimits, type ProviderRateLimits, type ProviderLimitsConfig } from "./config-types.js"; export { CONFIGURED_LIMIT_AXES, type ConfiguredLimitAxis, type HardRateLimits, type ProviderRateLimits, type ProviderLimitsConfig, }; /** * Validate the `hard` sub-block of a `limits` declaration at load time. FLAT AXES ONLY: a * per-deployment cap rides inside that deployment's own limits entry * (`limits.models..hard`), so every `limits` block — provider, slot or model override — * carries exactly the same grammar and the resolver can mirror the soft ladder site for site. * Month/hour/week spellings (`mpd`, `rph`) are not in the axis list, so they are rejected BY * NAME rather than silently ignored: a cap the ledger cannot read could never fire, and its * presence would lie about what this relay enforces. */ export declare function parseHardLimits(raw: unknown, where: string): HardRateLimits | undefined; /** * Validate a `limits` block at load time. * * A malformed block is a HARD ERROR, not a dropped field: these figures gate how much traffic a * credential may absorb, so a typo ("RPM", "rps", "tph") that were silently ignored would look * like a declared ceiling while asserting nothing — the operator believes the lane is bounded * when it is not. That is worse than refusing to start, which is why this does not follow the * credentials[] convention of dropping the bad slot with a warning. Unknown keys are rejected by * NAME because the closed axis list IS the feature; `models` keys are arbitrary backend model * ids and are deliberately not checked against any catalog. * * Returns undefined when nothing was declared; `{}` is legal and declares nothing. */ export declare function parseConfiguredLimits(raw: unknown, where: string): ProviderLimitsConfig | undefined; /** Where one axis' figure was found in the declaration ladder, most-specific first. */ export type ConfiguredLimitSource = "provider" | "credential" | "provider-model" | "credential-model"; /** Where one hard-cap axis' figure was found; alias for ConfiguredLimitSource. */ export type HardCapSource = ConfiguredLimitSource; /** What `resolveConfiguredLimits` returns: figures plus WHICH declaration supplied each. */ export interface ConfiguredLimits { rpm?: number; rpd?: number; tpm?: number; tpd?: number; /** * G2 refusal ceilings resolved through the SAME per-axis ladder as the soft figures. Present * ONLY when something hard was declared anywhere on this provider/slot pair — a result with no * caps carries no `hard` key at all, keeping the pre-G2 soft shape byte-identical for callers * that never opted in. */ hard?: HardRateLimits; /** Which declaration level supplied each hard axis — provenance beside every ceiling. */ hardSource?: Partial>; /** Uniform by construction — every figure here came out of config, never from a probe. */ basis: "configured"; source: Partial>; } /** * Resolve the operator-declared limits for one target, or null when nothing is declared for it. * * Precedence is per AXIS, most-specific first: a credential's model override beats the * provider's model override beats that credential's flat limits beats the provider's flat * limits. Independent resolution is the point — a model override that sets only `rpm` inherits * `rpd`/`tpm`/`tpd` from above rather than blanking them. * * Never sums across credentials (each slot is its own metered allowance) and never invents an * axis nobody wrote. A `credentialLabel` naming no declared slot simply falls through to the * provider level — the relay narrows only what config actually declares. */ export declare function resolveConfiguredLimits(cfg: Config, provider: string, credentialLabel: string | null, model: string | null): ConfiguredLimits | null; /** * Map an axis onto the quota-observation vocabulary, so a consumer can stand a configured * ceiling BESIDE a provider-stated observation for the same (axis, period) bucket instead of * inventing its own dimension names. */ export declare function configuredLimitQuotaShape(axis: ConfiguredLimitAxis): { axis: QuotaAxis; period: Extract; };