import type { ChatImage, ChatMessage, ProviderId, ReasoningPreference, ToolChoice, ToolDefinition } from "../types.js"; import { type RequestPlanV1 } from "../llm/request-plan.js"; /** * One serialized-request accounting service (doc 06 ยง4, MR-004/MR-007/MR-021). * * Every component that needs a token figure for a fit or admission decision * goes through this module: the canonical conservative text estimator, the * per-message estimator (content, native tool calls, reasoning artifacts, * images), the effective context-limit policy, and plan-level accounting with * headroom. Nothing else owns a chars-per-token ratio. */ export declare const RESERVED_OUTPUT_TOKENS = 24576; export declare const SAFETY_MARGIN_TOKENS = 2048; /** * Conservative mixed text/code/JSON estimator. It deliberately over-estimates: * compacting one turn too early is cheaper than losing state to a provider * context-window error. */ export declare function estimateTextTokens(text: string): number; export declare function estimateImageTokens(image: ChatImage): number; export declare function estimateMessageTokens(message: ChatMessage): number; export declare function estimateMessagesTokens(messages: readonly ChatMessage[]): number; export declare function estimateToolSchemaTokens(tools: readonly ToolDefinition[] | undefined): number; export type EffectiveLimitSource = "session-override" | "model-window" | "unknown"; export interface EffectiveContextLimit { readonly limitTokens?: number | undefined; readonly source: EffectiveLimitSource; readonly reservedOutputTokens: number; readonly safetyMarginTokens: number; /** Largest request that still leaves the reserved output and margin free. */ readonly effectiveSafeTokens?: number | undefined; } export declare function resolveEffectiveContextLimit(input?: { readonly provider?: ProviderId | undefined; readonly model?: string | undefined; readonly contextLimitTokens?: number | undefined; readonly reservedOutputTokens?: number | undefined; readonly safetyMarginTokens?: number | undefined; }): EffectiveContextLimit; export interface RequestAccounting { /** * Whole serialized request: timeline sections plus tool schemas, corrected by * the route's observed estimator bias when it has been measured. Every fit, * trigger and display decision uses this figure. */ readonly requestTokens: number; /** The same total before route correction; the only value fit to calibrate. */ readonly rawRequestTokens: number; readonly instructionsTokens: number; readonly historyTokens: number; readonly liveTokens: number; readonly toolsTokens: number; readonly artifactTokens: number; readonly replayedArtifactCount: number; readonly imageTokens: number; readonly imageCount: number; readonly messageCount: number; readonly limit: EffectiveContextLimit; /** effectiveSafeTokens - requestTokens, when a limit is known. */ readonly headroomTokens?: number | undefined; /** True only when the request cannot fit the effective safe limit. */ readonly overLimit?: boolean | undefined; /** `calibrated` once this route's estimator bias has been measured. */ readonly precision: "estimate" | "calibrated"; } export declare function accountRequestPlan(plan: RequestPlanV1, policy?: { readonly provider?: ProviderId | undefined; readonly model?: string | undefined; readonly contextLimitTokens?: number | undefined; readonly reservedOutputTokens?: number | undefined; readonly safetyMarginTokens?: number | undefined; }): RequestAccounting; /** * Compile the canonical plan for an assembled request and account it in one * step. This is the only fit check callers should perform on a request that is * about to be dispatched. */ export declare function accountAssembledRequest(input: { readonly provider: ProviderId; readonly model: string; readonly messages: readonly ChatMessage[]; readonly stream: boolean; readonly tools?: readonly ToolDefinition[] | undefined; readonly toolChoice?: ToolChoice | undefined; readonly parallelToolCalls?: boolean | undefined; readonly reasoning?: ReasoningPreference | undefined; readonly contextLimitTokens?: number | undefined; readonly reservedOutputTokens?: number | undefined; readonly safetyMarginTokens?: number | undefined; }): { plan: RequestPlanV1; accounting: RequestAccounting; }; /** The final assembled request cannot fit the effective safe limit. */ export declare class RequestOverLimitError extends Error { constructor(message: string); }