import type { TokenUsage } from '../common/index.js' import type { Message, ProviderRoute, ReasoningBlock, ToolCall } from '../message/index.js' import type { LLMToolSchema } from '../tool/index.js' export type ToolChoice = | 'auto' | 'none' | 'required' | { type: 'function'; function: { name: string } } export type ResponseFormat = | { type: 'json_object' } | { type: 'json_schema' json_schema: { name: string schema: Record strict?: boolean } } export interface CacheControl { type: 'auto' | 'ephemeral' } export interface ChatCompletionParams { model: string messages: Message[] /** * Exact configured member receiving this call. * * Set by the query/fallback runtime so provider adapters can decide whether * historical native replay state belongs to this route. Direct primary * calls may omit it; a provider then uses its own id, `model`, and index 0. */ providerRoute?: ProviderRoute /** Opt in to provider-hosted web search. Absent means no hosted search. */ webSearch?: { mode: 'cached' | 'live' } tools?: LLMToolSchema[] /** * Provider hint naming tools whose model-facing JSON Schema should be * enforced through constrained generation when the selected transport and * model support it. * * This is not a wire field. Provider implementations must consume or omit * it instead of serializing ChatCompletionParams wholesale. */ enforceToolInputSchema?: readonly string[] temperature?: number maxTokens?: number stream?: boolean stop?: string[] /** * Per-call cancellation. Aborting it tears down the in-flight model * request (the provider passes it to the underlying fetch / SDK) AND the * runtime races the stream consumer against it, so a Stop stops the * CURRENT turn mid-flight — not only between turns. Optional and inert * when unset: a non-aborted signal is behaviourally identical to omitting * it, so existing callers are unaffected. */ signal?: AbortSignal toolChoice?: ToolChoice parallelToolCalls?: boolean cacheControl?: CacheControl topP?: number topK?: number frequencyPenalty?: number presencePenalty?: number repetitionPenalty?: number responseFormat?: ResponseFormat /** * Extended-thinking request. * * Absent from this struct entirely before, and `buildCreateParams` * assembles the request body key-by-key with no passthrough, so * thinking could not be *requested* — while on models where it is on by * default the runtime received blocks it then discarded. Neither half * was expressible. * * A driver that has no extended-thinking wire must refuse `enabled` and * `adaptive` rather than return an ordinary answer that looks honoured. * `disabled` may be accepted as an explicit no-op by a driver that was * already going to leave thinking off. */ thinking?: ThinkingConfig /** * How much work to put into the response. See {@link ReasoningEffort}. * * A driver with no effort concept must refuse the field rather than silently * use the model default. Drivers whose accepted levels vary by model resolve * that narrower set according to their documented provider policy. */ effort?: ReasoningEffort } export interface ThinkingConfig { /** * Which thinking mode to ask for. * * `'adaptive'` lets the model decide whether and how deeply to think per * request; depth is steered by {@link ChatCompletionParams.effort} rather * than a token budget. `'enabled'` is the older manual mode, where * {@link budgetTokens} fixes the depth and the model thinks on every * request. * * **These are not interchangeable, and a driver must not guess.** Vendors * reject the wrong one for a given model outright rather than degrading: * newer models refuse `'enabled'`, older ones refuse `'adaptive'`, and * some refuse `'disabled'` because they cannot stop thinking at all. A * driver that sends a mode the model does not accept produces a failed * request, not a worse answer — which is why this is a declared intent * that each driver resolves against the model it is about to call. */ type: 'adaptive' | 'enabled' | 'disabled' /** * Token allowance for the thinking pass. Manual mode only — the adaptive * mode has no budget, and depth is set by `effort`. */ budgetTokens?: number /** * Whether the thinking text comes back or only its signature. * * `'omitted'` returns the blocks with an empty body and a signature, which * is enough to replay them on the next turn (see `replayReasoning`) while * keeping the text out of the response. `'summarized'` returns a summary * of the reasoning. * * Worth setting explicitly. This defaults to `'omitted'` on newer models, * so a caller that wants to show reasoning and does not ask for it gets * thinking blocks whose text is empty and no indication why. * * The values were `'full' | 'summarized'` here, and `'full'` was never a * value any vendor accepted — a declared option that could only ever have * been rejected, next to a real one that was missing. */ display?: 'summarized' | 'omitted' } /** * How much work the model should put into a response. * * A sibling of {@link ChatCompletionParams.thinking}, not a field inside it, * because it is not exclusively a thinking control: it shapes the whole * response, and at least one manual-mode model accepts it alongside a token * budget, where effort shapes the answer and the budget sets thinking depth. * Nesting it under `thinking` would have made that combination unsayable. * * In adaptive mode it is the primary depth lever — low effort may skip * thinking entirely on easy input. In manual mode `budgetTokens` sets depth * and effort does not move it. * * This union describes caller intent, not a claim that every provider or model * accepts every level. For example, `minimal` and `none` are distinct levels on * different compatible model generations, `max` is available only on newer * families, and `ultra` can be a host-level value rather than a model wire * capability. A provider must resolve the selected model's actual set before * transport or leave the vendor to refuse an unknown compatible-endpoint * extension explicitly. */ export type ReasoningEffort = | 'none' | 'minimal' | 'low' | 'medium' | 'high' | 'xhigh' | 'max' | 'ultra' export interface ChatCompletionResponse { id: string model: string message: { role: 'assistant' content: string | null /** Ordered public text items; content selects explicit final answers when present. */ textParts?: readonly import('../message/index.js').AssistantTextPart[] toolCalls?: ToolCall[] /** Reasoning blocks the model emitted, in original block order. */ reasoning?: readonly ReasoningBlock[] /** Adapter-private lossless-JSON state for restoring native metadata. */ replayState?: unknown /** Passages this turn cites, in the order the model made them. */ citations?: readonly import('../message/index.js').Citation[] } finishReason: 'stop' | 'tool_calls' | 'length' | 'content_filter' usage: TokenUsage }