import type { PipelineTrace } from '../pipeline/types.js'; import type { CapabilityWarning } from './capabilities.js'; import type { ContextWindowUsage } from './context-window.js'; import type { AssetInput } from './images.js'; /** * Token accounting for one operation. * * `inputTokens` is the billed uncached input. Cached reads and writes are reported separately * because they are priced differently, and `reasoningTokens` is the share of `outputTokens` the * provider attributes to internal reasoning rather than to visible output. */ export interface TokenUsage { /** Billed input tokens that were not served from the provider's cache. */ inputTokens: number; /** Output tokens, reasoning included. */ outputTokens: number; /** Input tokens read from the provider's prompt cache, priced lower. */ cachedReadTokens?: number; /** Input tokens written to the provider's prompt cache, priced higher on some providers. */ cachedWriteTokens?: number; /** The part of `outputTokens` spent on internal reasoning. */ reasoningTokens?: number; /** Every token counted. */ totalTokens: number; } /** * Numeric cost of one operation. * * `basis` distinguishes a local estimate from a provider-reported figure. Bundled prices are * defaults, not financial truth; override them through `models.registry` when exact numbers matter. */ export interface ResponseCost { /** Total cost. */ amount: number; /** Currency of every amount, such as `USD`. */ currency: string; /** `estimated` from registry prices, or `reported` by the provider. */ basis: 'estimated' | 'reported'; /** Cost of uncached input. */ input?: number; /** Cost of output. */ output?: number; /** Cost of cache reads. */ cachedRead?: number; /** Cost of cache writes. */ cachedWrite?: number; } /** * How a completion was produced: provider, model, timing, tokens, cost, and every policy that * touched it. */ export interface ResponseMeta { /** The request's id. */ requestId: string; /** Provider that answered. */ providerUsed: string; /** Model that answered. */ modelUsed: string; /** Duration in milliseconds. */ latencyMs: number; /** Input tokens, as the provider reported them. */ tokensInput: number; /** Output tokens, as the provider reported them. */ tokensOutput: number; /** Tokens removed by optimization before the request was sent. */ tokensSaved: number; /** Full token breakdown, including cached and reasoning tokens when the provider reports them. */ usage?: TokenUsage; /** Numeric cost, priced per token class. */ cost?: ResponseCost; /** Options the target model could not honor as written, under a non-strict capability policy. */ capabilityWarnings?: CapabilityWarning[]; /** True when the response came from the response cache and no provider was called. */ cacheHit: boolean; /** What the response cache did for this request, and why. Present when the cache is enabled. */ cache?: CacheOutcome; /** Guardrails and techniques applied, such as redaction, trimming, or a retry. */ guardrailsApplied: string[]; /** Why the router chose the provider, and how many fallbacks were available. */ routingDecision?: { /** Why the router chose this route. */ reason: string; /** How many fallbacks the route had. */ fallbacksConsidered: number; /** The provider the answering attempt was sent to. */ provider?: string; /** The model the answering attempt was sent to, as the registry names it. The response is priced on it. */ model?: string; }; /** What context-window trimming did to the request. */ contextWindow?: ContextWindowUsage; /** Grounding check of the answer against its sources, when verification ran. */ verification?: { ok: boolean; supportRatio: number; factsChecked: number; unsupportedFacts: string[]; }; /** Per-stage timings of the request pipeline, when tracing of the pipeline is on. */ pipeline?: PipelineTrace; /** * The attempts that failed before the one that answered, in order: retried on the same provider, or * failed over to the next. Absent when the first attempt answered. */ failedAttempts?: ResponseAttempt[]; /** Whether the semantic cache answered the request, and how similar the match was. */ semanticCache?: { hit: boolean; score: number; key: string; }; } /** One attempt that failed before a response: the provider and model it went to, why, and when. */ export interface ResponseAttempt { /** The provider it was sent to. */ provider: string; /** The model it asked for. */ model: string; /** Why it failed, with any credential removed. */ error: string; /** The error's category, such as `rate_limit` or `timeout`, when the provider classified it. */ category?: string; /** ISO-8601 time it failed. */ at: string; } /** A tool call the model made. */ export interface ToolCall { /** The provider's id for the call, which the tool result must carry back. */ id: string; /** Always `function`. */ type: 'function'; /** The tool's name and its arguments as JSON text, unparsed. */ function: { name: string; arguments: string; }; } /** A completion. */ export interface NexusResponse { /** The model's text. Empty when it only called tools. */ content: string; /** Always `assistant`. */ role: 'assistant'; /** Tool calls the model made. */ toolCalls?: ToolCall[]; /** * Assets the model produced alongside its text, such as images from a model whose * `outputModalities` include `image`. Bytes stay bytes; nothing is encoded into `content`. */ assets?: AssetInput[]; /** * Why generation stopped: a natural end, tool calls, the token limit, a content filter, or an * error. */ finishReason: 'stop' | 'tool_calls' | 'length' | 'content_filter' | 'error'; /** * Each token of the answer with its log probability, and its most likely alternatives, when the * request asked for `logprobs` and the provider reported them. */ logprobs?: TokenLogprob[]; /** How the completion was produced. */ meta: ResponseMeta; } /** One token of an answer: its text, its log probability, and the alternatives most likely in its place. */ export interface TokenLogprob { /** The token's text. */ token: string; /** Its natural log probability, 0 or below. */ logprob: number; /** The most likely tokens at its place, itself among them, most likely first, when they were asked for. */ top?: Array<{ token: string; logprob: number; }>; } /** * One streamed event. * * `'reasoning'` carries a reasoning summary and is deliberately separate from `'text'` so existing * consumers that switch on `type` keep receiving only visible output. */ export interface StreamChunk { /** * `text` for visible output, `reasoning` for a reasoning summary, `tool_call`, `done` once * complete, or `error`. */ type: 'text' | 'reasoning' | 'tool_call' | 'done' | 'error'; /** Text of a `text` or `reasoning` chunk. */ content?: string; /** The call, for a `tool_call` chunk. */ toolCall?: ToolCall; /** Metadata, filled in on the final chunk. */ meta?: Partial; /** What went wrong, for an `error` chunk. */ error?: string; /** * For a `text` chunk, its tokens' log probabilities, in order, when the request asked for * `logprobs` and the provider streams them: the chunks' together are the response's. */ logprobs?: TokenLogprob[]; } /** A streamed completion: iterate it for chunks, or abort it. */ export interface NexusStream extends AsyncIterable { /** Iterates the chunks as they arrive. */ [Symbol.asyncIterator](): AsyncIterator; /** Stops the stream and the provider request behind it. */ abort(): void; } /** * What the response cache did for one request: `hit` (answered from it), `miss` (looked, found * nothing), `stale` (found an entry that had expired), `bypass` (the request skipped the lookup), or * `error` (the cache failed, and the request went to the provider as on a miss). Recorded on * `response.meta.cache` and on the request's trace. */ export interface CacheOutcome { /** What happened. */ outcome: 'hit' | 'miss' | 'stale' | 'bypass' | 'error'; /** Which layer answered or was asked last: the exact cache or the semantic one. */ layer?: 'exact' | 'semantic'; /** Why, in a few words, such as `no entry for this request` or the cache's error message. */ reason: string; /** The namespace the lookup ran in, when one applies. */ namespace?: string; /** Set when storing the response failed; the response was still returned. */ writeError?: string; }