@warlock.js/ai-openai 5.31.0 → 5.32.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/cjs/index.cjs +304 -8
  3. package/cjs/index.cjs.map +1 -1
  4. package/esm/config.type.d.mts +8 -10
  5. package/esm/config.type.d.mts.map +1 -1
  6. package/esm/embedder.d.mts +1 -3
  7. package/esm/embedder.d.mts.map +1 -1
  8. package/esm/image.d.mts +1 -3
  9. package/esm/image.d.mts.map +1 -1
  10. package/esm/index.d.mts +4 -3
  11. package/esm/index.mjs +3 -2
  12. package/esm/kimi-tools.d.mts +69 -0
  13. package/esm/kimi-tools.d.mts.map +1 -0
  14. package/esm/kimi-tools.mjs +256 -0
  15. package/esm/kimi-tools.mjs.map +1 -0
  16. package/esm/known-pricing.d.mts +8 -4
  17. package/esm/known-pricing.d.mts.map +1 -1
  18. package/esm/known-pricing.mjs +5 -1
  19. package/esm/known-pricing.mjs.map +1 -1
  20. package/esm/model.mjs +8 -1
  21. package/esm/model.mjs.map +1 -1
  22. package/esm/provider-options.d.mts +7 -2
  23. package/esm/provider-options.d.mts.map +1 -1
  24. package/esm/provider-options.mjs +9 -2
  25. package/esm/provider-options.mjs.map +1 -1
  26. package/esm/responses-model.mjs +11 -4
  27. package/esm/responses-model.mjs.map +1 -1
  28. package/esm/sdk.d.mts +1 -3
  29. package/esm/sdk.d.mts.map +1 -1
  30. package/esm/speech.d.mts +1 -3
  31. package/esm/speech.d.mts.map +1 -1
  32. package/esm/transcription.d.mts +1 -3
  33. package/esm/transcription.d.mts.map +1 -1
  34. package/esm/utils/hosted-output.mjs +22 -0
  35. package/esm/utils/hosted-output.mjs.map +1 -0
  36. package/esm/utils/index.mjs +1 -1
  37. package/esm/utils/prompt-cache-params.mjs +1 -1
  38. package/esm/utils/prompt-cache-params.mjs.map +1 -1
  39. package/esm/utils/reasoning-replay.mjs +1 -1
  40. package/esm/utils/reasoning-replay.mjs.map +1 -1
  41. package/esm/utils/structured-output.mjs +1 -1
  42. package/esm/utils/to-openai-messages.mjs.map +1 -1
  43. package/llms-full.txt +108 -1
  44. package/llms.txt +1 -1
  45. package/package.json +3 -3
  46. package/skills/setup-openai/SKILL.md +108 -1
package/esm/model.mjs CHANGED
@@ -7,7 +7,7 @@ import { inferStructuredOutput, planStructuredOutput } from "./utils/structured-
7
7
  import { toOpenAIMessages } from "./utils/to-openai-messages.mjs";
8
8
  import { toOpenAITools } from "./utils/to-openai-tools.mjs";
9
9
  import "./utils/index.mjs";
10
- import { mergeProviderBody } from "./provider-options.mjs";
10
+ import { compatibleSearchParams, mergeProviderBody } from "./provider-options.mjs";
11
11
  import { inferReasoningCapability } from "./known-reasoning-models.mjs";
12
12
  import { inferVisionCapability } from "./known-vision-models.mjs";
13
13
  import { InvalidRequestError, ProviderError, resolveStreamChunks, safeJsonParse, selectProviderTools } from "@warlock.js/ai";
@@ -91,6 +91,7 @@ var OpenAIModel = class {
91
91
  try {
92
92
  response = await this.client.chat.completions.create(mergeProviderBody({
93
93
  model: this.name,
94
+ ...compatibleSearchParams(this.provider, options),
94
95
  messages: toOpenAIMessages(messages, this.promptCacheBreakpoints(options)),
95
96
  ...this.buildSamplingParams(options),
96
97
  ...buildPromptCacheParams(this.provider, this.config),
@@ -147,6 +148,7 @@ var OpenAIModel = class {
147
148
  try {
148
149
  stream = await this.client.chat.completions.create(mergeProviderBody({
149
150
  model: this.name,
151
+ ...compatibleSearchParams(this.provider, options),
150
152
  messages: toOpenAIMessages(messages, this.promptCacheBreakpoints(options)),
151
153
  ...this.buildSamplingParams(options),
152
154
  ...buildPromptCacheParams(this.provider, this.config),
@@ -256,6 +258,11 @@ var OpenAIModel = class {
256
258
  model: this.name,
257
259
  logger: this.logger
258
260
  });
261
+ if ((this.provider === "moonshot" || this.provider === "kimi") && definitions.some((definition) => {
262
+ const fn = definition.function;
263
+ return definition.type === "builtin_function" && typeof fn === "object" && fn !== null && "name" in fn && fn.name === "$web_search";
264
+ })) throw new InvalidRequestError("Kimi's builtin $web_search retires on 20 October 2026. Use kimiSearchTool() / kimiFetchTool() from @warlock.js/ai-openai.");
265
+ if (this.provider === "xai" && definitions.some((definition) => definition.type === "web_search" || definition.type === "x_search")) throw new InvalidRequestError("xAI native web_search / x_search requires POST /v1/responses. XaiSDK currently supports Chat Completions only; use xAI's Responses API with the vendor SDK for native search.");
259
266
  const mapped = [...toOpenAITools(tools) ?? [], ...definitions];
260
267
  return mapped.length > 0 ? { tools: mapped } : {};
261
268
  }
package/esm/model.mjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"model.mjs","names":[],"sources":["../../../../../../../packages/ai-openai/src/model.ts"],"sourcesContent":["import {\n InvalidRequestError,\n ProviderError,\n resolveStreamChunks,\n safeJsonParse,\n selectProviderTools,\n type Message,\n type ModelCallOptions,\n type ModelCapabilities,\n type ModelContract,\n type ModelPricing,\n type ModelResponse,\n type ModelStreamChunk,\n type ModelToolCallRequest,\n type Usage,\n} from \"@warlock.js/ai\";\nimport { log, type Logger } from \"@warlock.js/logger\";\nimport type OpenAI from \"openai\";\nimport { mergeProviderBody } from \"./provider-options\";\nimport type { OpenAIModelConfig } from \"./config.type\";\nimport { inferReasoningCapability } from \"./known-reasoning-models\";\nimport { inferVisionCapability } from \"./known-vision-models\";\nimport {\n buildPromptCacheParams,\n buildUsage,\n inferStructuredOutput,\n mapFinishReason,\n planStructuredOutput,\n toOpenAIMessages,\n toOpenAIReasoningEffort,\n toOpenAITools,\n wrapOpenAIError,\n} from \"./utils\";\n\nconst LOG_MODULE = \"ai.openai\";\n\n/**\n * OpenAI-backed implementation of `ModelContract`.\n *\n * **Role.** The provider-facing bridge between the vendor-neutral\n * `@warlock.js/ai` agent runtime and the official `openai` SDK. Agents,\n * workflows, and supervisors never talk to OpenAI directly — they hold a\n * `ModelContract`, and this class is what makes that contract concrete for\n * any OpenAI-compatible endpoint (OpenAI, Azure OpenAI, OpenRouter, local\n * gateways that speak the Chat Completions protocol).\n *\n * **Responsibility.**\n * - Owns: a long-lived `OpenAI` client + frozen `ModelConfig` (name,\n * temperature, maxTokens) used as defaults for every call.\n * - Owns: translating vendor-neutral `Message[]` and\n * `ToolContract[]` into OpenAI wire shapes on the way out, and\n * translating OpenAI's response (content, finish reason, tool calls,\n * usage) back into the neutral shapes on the way in.\n * - Does NOT own: dispatching tools, deciding whether to loop, tracking\n * conversation history, or retrying on failure — those are agent\n * concerns. The model is a stateless (per-call) protocol adapter.\n *\n * Because it holds a live client and shared defaults, it is modeled as a\n * class (see §4.2 of code-style.md — \"long-lived state across calls\").\n *\n * @example\n * import OpenAI from \"openai\";\n * const client = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });\n * const model = new OpenAIModel(client, { name: \"gpt-4o\", temperature: 0.3 });\n *\n * const myAgent = agent({\n * model,\n * systemPrompt: \"You are a helpful assistant.\",\n * tools: [searchTool],\n * });\n *\n * const result = await myAgent.execute(\"Summarize today's news.\");\n */\nexport class OpenAIModel implements ModelContract {\n public readonly name: string;\n public readonly provider: string;\n public readonly capabilities: ModelCapabilities;\n public readonly pricing?: ModelPricing;\n\n private readonly client: OpenAI;\n private readonly config: OpenAIModelConfig;\n private readonly logger: Logger = log;\n\n public constructor(client: OpenAI, config: OpenAIModelConfig, provider: string = \"openai\") {\n // `api: \"responses\"` belongs to `OpenAIResponsesModel`, which only the\n // `OpenAISDK.model()` factory constructs. This class speaks Chat\n // Completions only; accepting the flag here would silently ignore it.\n if (config.api === \"responses\") {\n throw new InvalidRequestError(\n 'OpenAIModel speaks Chat Completions only and cannot serve api: \"responses\". Use OpenAISDK.model({ api: \"responses\" }) on the direct OpenAI provider.',\n { context: { provider, model: config.name, api: config.api } },\n );\n }\n\n this.client = client;\n this.config = config;\n this.name = config.name;\n this.provider = provider;\n this.pricing = config.pricing;\n this.capabilities = {\n structuredOutput: config.structuredOutput ?? inferStructuredOutput(config.responseFormat),\n vision: config.vision ?? inferVisionCapability(config.name),\n // o-series + gpt-5 / gpt-6 models surface a reasoning channel and accept\n // the `reasoning_effort` param. Explicit config wins over the\n // name-prefix inference.\n reasoning: config.reasoning ?? inferReasoningCapability(config.name),\n // Direct OpenAI requests support automatic cache hits and explicit\n // content-part breakpoints. Wrapper endpoints have distinct provider\n // labels and may reject these OpenAI-only request fields.\n promptCaching: this.provider === \"openai\",\n // PDF + audio INPUT are off by default — OpenAI accepts `file`\n // (PDF) and `input_audio` parts only on specific models, so the\n // flags are conservative/honest and opt-in via config rather than\n // name-inferred. When set, the agent admits the attachments and\n // `toOpenAIMessages` maps them to the real wire parts.\n pdf: config.pdf ?? false,\n audio: config.audio ?? false,\n };\n }\n\n /**\n * Single-shot completion. Sends the full message list to the Chat\n * Completions endpoint, waits for the terminal response, and reshapes it\n * into a vendor-neutral `ModelResponse`. Per-call `options` override the\n * instance's `ModelConfig` defaults for this call only.\n */\n public async complete(messages: Message[], options?: ModelCallOptions): Promise<ModelResponse> {\n // Per-call request/response logs are hot-path in production agents\n // — keep them at `debug` so `info` stays reserved for lifecycle\n // events (agent starting/completed, etc.). Operators who need to\n // audit every LLM call can raise log-level at runtime.\n this.logger.debug(LOG_MODULE, \"request\", \"Starting call to chat.completions\", {\n model: this.name,\n messageCount: messages.length,\n streaming: false,\n toolCount: options?.tools?.length ?? 0,\n });\n\n let response: OpenAI.Chat.Completions.ChatCompletion;\n\n try {\n response = await this.client.chat.completions.create(\n mergeProviderBody<OpenAI.Chat.Completions.ChatCompletionCreateParamsNonStreaming>(\n {\n model: this.name,\n messages: toOpenAIMessages(messages, this.promptCacheBreakpoints(options)),\n ...this.buildSamplingParams(options),\n ...buildPromptCacheParams(this.provider, this.config),\n ...this.buildTools(options),\n ...this.buildResponseFormat(options?.responseSchema),\n ...this.buildReasoningParams(options?.reasoning, Boolean(options?.tools?.length)),\n },\n options,\n ),\n options?.signal ? { signal: options.signal } : undefined,\n );\n } catch (thrown) {\n const wrapped = wrapOpenAIError(thrown);\n\n this.logger.error(LOG_MODULE, \"error\", wrapped.message, {\n code: wrapped.code,\n context: wrapped.context,\n });\n\n throw wrapped;\n }\n\n const [choice] = response.choices;\n\n if (choice === undefined) {\n // A 200 carrying an empty `choices` array. Every line below reads off\n // `choice`, so without this the caller got a bare `TypeError: Cannot\n // read properties of undefined` — no provider, no model, no hint that\n // the response itself was the problem rather than our handling of it.\n //\n // `ProviderError` because that is what this is: the provider answered\n // successfully and returned nothing usable. Callers already branch on\n // `instanceof ProviderError` for any provider-side failure.\n throw new ProviderError(\"OpenAI returned a response with no choices.\", {\n context: { provider: \"openai\", model: this.name, choices: response.choices.length },\n });\n }\n\n const finishReason = mapFinishReason(choice.finish_reason);\n const usage = this.extractUsage(response.usage);\n\n this.logger.debug(LOG_MODULE, \"response\", \"call to chat.completions succeeded\", {\n finishReason,\n usage,\n });\n\n return {\n content: choice.message.content ?? \"\",\n finishReason,\n usage,\n toolCalls: this.extractToolCalls(choice.message.tool_calls),\n };\n }\n\n /**\n * Incremental streaming completion. Yields neutral `ModelStreamChunk`s —\n * `delta` for text tokens, `tool-call` when the model requests a tool,\n * and a terminal `done` carrying the final finish reason + usage totals.\n * Callers consume it with `for await`.\n *\n * With `options.streamChunks` it also yields `tool-args` for each\n * `tool_calls[].function.arguments` fragment and `reasoning` for a\n * vendor's `reasoning_content` / `reasoning` delta field. Usage arrives\n * only on the final chunk here, so no `usage` chunk is emitted.\n */\n public async *stream(\n messages: Message[],\n options?: ModelCallOptions,\n ): AsyncIterable<ModelStreamChunk> {\n this.logger.debug(LOG_MODULE, \"request\", \"Starting streaming call to chat.completions\", {\n model: this.name,\n messageCount: messages.length,\n streaming: true,\n toolCount: options?.tools?.length ?? 0,\n });\n\n let stream: Awaited<ReturnType<typeof this.client.chat.completions.create>>;\n\n try {\n stream = await this.client.chat.completions.create(\n mergeProviderBody<OpenAI.Chat.Completions.ChatCompletionCreateParamsStreaming>(\n {\n model: this.name,\n messages: toOpenAIMessages(messages, this.promptCacheBreakpoints(options)),\n ...this.buildSamplingParams(options),\n ...buildPromptCacheParams(this.provider, this.config),\n ...this.buildTools(options),\n stream: true,\n stream_options: { include_usage: true },\n ...this.buildResponseFormat(options?.responseSchema),\n ...this.buildReasoningParams(options?.reasoning, Boolean(options?.tools?.length)),\n },\n options,\n ),\n options?.signal ? { signal: options.signal } : undefined,\n );\n } catch (thrown) {\n const wrapped = wrapOpenAIError(thrown);\n\n this.logger.error(LOG_MODULE, \"error\", wrapped.message, {\n code: wrapped.code,\n context: wrapped.context,\n });\n\n throw wrapped;\n }\n\n let rawFinishReason: string = \"stop\";\n let usage: Usage = { input: 0, output: 0, total: 0 };\n const want = resolveStreamChunks(options);\n const toolCallAccum = new Map<\n number,\n { id: string; name: string; arguments: string; streamed: number }\n >();\n\n try {\n for await (const chunk of stream as AsyncIterable<OpenAI.Chat.Completions.ChatCompletionChunk>) {\n const delta = chunk.choices[0]?.delta;\n const finish = chunk.choices[0]?.finish_reason;\n\n if (want.reasoning && delta) {\n const reasoning = readReasoningDelta(delta);\n\n if (reasoning) {\n yield { type: \"reasoning\", delta: reasoning };\n }\n }\n\n if (delta?.content) {\n yield { type: \"delta\", content: delta.content };\n }\n\n if (delta?.tool_calls) {\n for (const toolCall of delta.tool_calls) {\n const idx = toolCall.index ?? 0;\n if (!toolCallAccum.has(idx)) {\n toolCallAccum.set(idx, { id: \"\", name: \"\", arguments: \"\", streamed: 0 });\n }\n const acc = toolCallAccum.get(idx)!;\n if (toolCall.id) acc.id = toolCall.id;\n if (toolCall.function?.name) acc.name = toolCall.function.name;\n if (toolCall.function?.arguments) acc.arguments += toolCall.function.arguments;\n\n // The first fragment of a call carries its id and name; later\n // ones only `index` + argument text. Anything that arrived\n // before the name is flushed once the call is identified.\n if (want.toolArgs && acc.name && acc.arguments.length > acc.streamed) {\n yield {\n type: \"tool-args\",\n callId: acc.id,\n name: acc.name,\n delta: acc.arguments.slice(acc.streamed),\n };\n acc.streamed = acc.arguments.length;\n }\n }\n }\n\n if (finish) {\n rawFinishReason = finish;\n }\n\n if (chunk.usage) {\n usage = this.extractUsage(chunk.usage);\n }\n }\n\n for (const acc of toolCallAccum.values()) {\n // Skip accumulators that never received a function name — those\n // are partial fragments the model started but never identified\n // (e.g. arguments-only deltas with no originating `id`/`name`).\n // Yielding them produces nameless tool-calls the agent runtime\n // can't dispatch and would mis-attribute as a registered tool.\n if (!acc.name) continue;\n\n yield {\n type: \"tool-call\",\n id: acc.id,\n name: acc.name,\n input: safeJsonParse<Record<string, unknown>>(acc.arguments, {}),\n };\n }\n } catch (thrown) {\n const wrapped = wrapOpenAIError(thrown);\n\n this.logger.error(LOG_MODULE, \"error\", wrapped.message, {\n code: wrapped.code,\n context: wrapped.context,\n });\n\n throw wrapped;\n }\n\n const finishReason = mapFinishReason(rawFinishReason);\n\n this.logger.debug(LOG_MODULE, \"response\", \"Streaming call to chat.completions succeeded\", {\n finishReason,\n usage,\n });\n\n yield { type: \"done\", finishReason, usage };\n }\n\n /**\n * Spread-friendly `tools` fragment: the function tools, then the\n * provider-defined entries whose `provider` equals this model's\n * provider label (`\"openai\"`, or the vendor label an OpenAI-compatible\n * wrapper sets, e.g. `\"groq\"`), sent verbatim. Chat Completions only\n * accepts `function` and `custom` tool types from OpenAI itself; its\n * hosted tools (`web_search`, `file_search`, …) need `api: \"responses\"`\n * and OpenAI rejects them here with a 400.\n */\n private buildTools(options: ModelCallOptions | undefined): {\n tools?: OpenAI.Chat.Completions.ChatCompletionTool[];\n } {\n const { tools, definitions } = selectProviderTools(options, {\n accepts: [this.provider],\n module: LOG_MODULE,\n model: this.name,\n logger: this.logger,\n });\n const mapped = [\n ...(toOpenAITools(tools) ?? []),\n ...(definitions as unknown as OpenAI.Chat.Completions.ChatCompletionTool[]),\n ];\n\n return mapped.length > 0 ? { tools: mapped } : {};\n }\n\n /** Map the neutral cache hint only for the direct OpenAI provider. */\n private promptCacheBreakpoints(options: ModelCallOptions | undefined): number {\n return this.provider === \"openai\" ? (options?.cacheControl?.breakpoints ?? 0) : 0;\n }\n\n /**\n * Translate the neutral `responseSchema` option into OpenAI's\n * `response_format` parameter. The mode decision (override handling,\n * strict-compatibility downgrade to `json_object`) lives in\n * `planStructuredOutput`, shared with the Responses adapter.\n *\n * Returns an empty spread when no schema was supplied, so the caller\n * can unconditionally `...buildResponseFormat(...)` into the request.\n */\n private buildResponseFormat(responseSchema: Record<string, unknown> | undefined): {\n response_format?: OpenAI.Chat.Completions.ChatCompletionCreateParams[\"response_format\"];\n } {\n const plan = planStructuredOutput(responseSchema, this.config.responseFormat);\n\n if (plan.mode === \"json_schema\") {\n return {\n response_format: {\n type: \"json_schema\",\n json_schema: { name: \"response\", schema: plan.schema, strict: true },\n },\n };\n }\n\n if (plan.mode === \"json_object\") {\n return { response_format: { type: \"json_object\" } };\n }\n\n return {};\n }\n\n /**\n * Normalize OpenAI's `usage` block (which may be absent on some responses\n * or partials) into the neutral `Usage` shape. Missing usage collapses to\n * zeros rather than propagating `undefined`, so downstream aggregation\n * math stays safe.\n *\n * `cachedTokens` mirrors `prompt_tokens_details.cached_tokens` (the\n * subset of the prompt served from OpenAI's automatic prompt cache);\n * `reasoningTokens` mirrors `completion_tokens_details.reasoning_tokens`\n * (the hidden reasoning channel on o-series / gpt-5 models, already\n * counted within `output`). Both are emitted only when the provider\n * reports a positive value, so non-reasoning / uncached calls keep the\n * lean `{ input, output, total }` shape. `cacheWriteTokens` mirrors\n * `prompt_tokens_details.cache_write_tokens` (billed writes on the\n * models that charge for them). `cost` is attached when priced.\n */\n private extractUsage(raw: OpenAI.Completions.CompletionUsage | undefined): Usage {\n if (!raw) {\n return { input: 0, output: 0, total: 0 };\n }\n\n return buildUsage(\n {\n input: raw.prompt_tokens ?? 0,\n output: raw.completion_tokens ?? 0,\n total: raw.total_tokens ?? 0,\n cachedTokens: raw.prompt_tokens_details?.cached_tokens,\n cacheWriteTokens: raw.prompt_tokens_details?.cache_write_tokens,\n reasoningTokens: raw.completion_tokens_details?.reasoning_tokens,\n },\n this.pricing,\n );\n }\n\n /**\n * Translate the neutral `ModelCallOptions.reasoning` hint into OpenAI's\n * `reasoning_effort` request param. Only `effort` maps — OpenAI's Chat\n * Completions API exposes a discrete effort knob, not a token budget,\n * so `reasoning.maxTokens` (the Anthropic extended-thinking cap) has no\n * wire equivalent here and is silently ignored.\n *\n * The neutral `ReasoningEffort` (`\"low\" | \"medium\" | \"high\" | \"none\"`)\n * is a subset of OpenAI's accepted values, so it forwards verbatim —\n * `\"none\"` included. `\"none\"` is load-bearing: gpt-5 / o-series models\n * **reject function tools** on Chat Completions while reasoning is\n * active, and the endpoint accepts tools only when `reasoning_effort`\n * is `\"none\"` (the alternative is the Responses API). This is why the\n * param is EMITTED for `\"none\"` rather than omitted — omitting it\n * leaves the model reasoning server-side by default, so tools would\n * still be rejected.\n *\n * No-ops when the model is not reasoning-capable\n * (`capabilities.reasoning` is false — e.g. `gpt-4o`, which 400s on any\n * `reasoning_effort`).\n *\n * When the caller gave no explicit `effort`: defaults to `\"none\"` IF\n * `hasTools` is true, else omits the param (provider default — the\n * pre-existing behavior for a tool-less call). The default exists\n * because, on a reasoning-capable model, there is no working alternative\n * to `\"none\"` when tools are attached — omitting `reasoning_effort`\n * leaves reasoning on server-side, which Chat Completions rejects tools\n * for (empty replies on some model generations, a hard 400 — \"Function\n * tools with reasoning_effort are not supported ... in\n * /v1/chat/completions\" — on newer ones). An explicit `effort` from the\n * caller always wins over this default, in either direction.\n *\n * Returns an empty spread when nothing applies, so the caller can\n * unconditionally `...buildReasoningParams(...)` into the request.\n */\n /**\n * Output-length and sampling params, in current Chat Completions shapes only.\n *\n * - `max_completion_tokens`, never the legacy `max_tokens`: current\n * models (the gpt-5 family and later) answer `400 unsupported_parameter`\n * to `max_tokens`, and `max_completion_tokens` is accepted everywhere.\n * - No `temperature` for a reasoning-capable model: those accept only the\n * default value and reject any other with a 400.\n */\n private buildSamplingParams(options: ModelCallOptions | undefined): {\n max_completion_tokens?: number;\n temperature?: number;\n } {\n const maxTokens = options?.maxTokens ?? this.config.maxTokens;\n const temperature = options?.temperature ?? this.config.temperature;\n\n return {\n max_completion_tokens: maxTokens,\n ...(!this.capabilities.reasoning && { temperature }),\n };\n }\n\n private buildReasoningParams(\n reasoning: ModelCallOptions[\"reasoning\"],\n hasTools: boolean,\n ): {\n reasoning_effort?: OpenAI.Chat.Completions.ChatCompletionCreateParams[\"reasoning_effort\"];\n } {\n if (!this.capabilities.reasoning) {\n return {};\n }\n\n if (!reasoning?.effort) {\n return hasTools ? { reasoning_effort: \"none\" } : {};\n }\n\n return { reasoning_effort: toOpenAIReasoningEffort(reasoning.effort) };\n }\n\n /**\n * Reshape OpenAI's `tool_calls` array into the neutral\n * `ModelToolCallRequest[]`. The raw `arguments` field is a JSON string\n * per OpenAI's protocol — we parse it defensively via `safeJsonParse` so\n * malformed or empty arguments yield an empty object instead of crashing\n * the trip. Returns `undefined` when no tools were requested so callers\n * can branch on presence.\n */\n private extractToolCalls(\n rawToolCalls: OpenAI.Chat.Completions.ChatCompletionMessageToolCall[] | undefined,\n ): ModelToolCallRequest[] | undefined {\n if (!rawToolCalls || rawToolCalls.length === 0) {\n return undefined;\n }\n\n return rawToolCalls.map((toolCall) => ({\n id: toolCall.id,\n name: (toolCall as any).function.name,\n input: safeJsonParse<Record<string, unknown>>((toolCall as any).function.arguments, {}),\n }));\n }\n}\n\n/**\n * Reasoning text an OpenAI-compatible vendor puts on a Chat Completions\n * stream delta. OpenAI itself streams none on this API. DeepSeek documents\n * `delta.reasoning_content` (API reference, chat completion chunk); Groq\n * documents the parsed reasoning under a `reasoning` field. Neither is in\n * the OpenAI SDK's delta type, so the delta is read as a plain record.\n */\nfunction readReasoningDelta(delta: object): string | undefined {\n const record = delta as Record<string, unknown>;\n\n for (const key of [\"reasoning_content\", \"reasoning\"]) {\n const value = record[key];\n\n if (typeof value === \"string\" && value !== \"\") {\n return value;\n }\n }\n\n return undefined;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"model.mjs","names":[],"sources":["../../../../../../../packages/ai-openai/src/model.ts"],"sourcesContent":["import {\n InvalidRequestError,\n ProviderError,\n resolveStreamChunks,\n safeJsonParse,\n selectProviderTools,\n type Message,\n type ModelCallOptions,\n type ModelCapabilities,\n type ModelContract,\n type ModelPricing,\n type ModelResponse,\n type ModelStreamChunk,\n type ModelToolCallRequest,\n type Usage,\n} from \"@warlock.js/ai\";\nimport { log, type Logger } from \"@warlock.js/logger\";\nimport type OpenAI from \"openai\";\nimport { compatibleSearchParams, mergeProviderBody } from \"./provider-options\";\nimport type { OpenAIModelConfig } from \"./config.type\";\nimport { inferReasoningCapability } from \"./known-reasoning-models\";\nimport { inferVisionCapability } from \"./known-vision-models\";\nimport {\n buildPromptCacheParams,\n buildUsage,\n inferStructuredOutput,\n mapFinishReason,\n planStructuredOutput,\n toOpenAIMessages,\n toOpenAIReasoningEffort,\n toOpenAITools,\n wrapOpenAIError,\n} from \"./utils\";\n\nconst LOG_MODULE = \"ai.openai\";\n\n/**\n * OpenAI-backed implementation of `ModelContract`.\n *\n * **Role.** The provider-facing bridge between the vendor-neutral\n * `@warlock.js/ai` agent runtime and the official `openai` SDK. Agents,\n * workflows, and supervisors never talk to OpenAI directly — they hold a\n * `ModelContract`, and this class is what makes that contract concrete for\n * any OpenAI-compatible endpoint (OpenAI, Azure OpenAI, OpenRouter, local\n * gateways that speak the Chat Completions protocol).\n *\n * **Responsibility.**\n * - Owns: a long-lived `OpenAI` client + frozen `ModelConfig` (name,\n * temperature, maxTokens) used as defaults for every call.\n * - Owns: translating vendor-neutral `Message[]` and\n * `ToolContract[]` into OpenAI wire shapes on the way out, and\n * translating OpenAI's response (content, finish reason, tool calls,\n * usage) back into the neutral shapes on the way in.\n * - Does NOT own: dispatching tools, deciding whether to loop, tracking\n * conversation history, or retrying on failure — those are agent\n * concerns. The model is a stateless (per-call) protocol adapter.\n *\n * Because it holds a live client and shared defaults, it is modeled as a\n * class (see §4.2 of code-style.md — \"long-lived state across calls\").\n *\n * @example\n * import OpenAI from \"openai\";\n * const client = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });\n * const model = new OpenAIModel(client, { name: \"gpt-4o\", temperature: 0.3 });\n *\n * const myAgent = agent({\n * model,\n * systemPrompt: \"You are a helpful assistant.\",\n * tools: [searchTool],\n * });\n *\n * const result = await myAgent.execute(\"Summarize today's news.\");\n */\nexport class OpenAIModel implements ModelContract {\n public readonly name: string;\n public readonly provider: string;\n public readonly capabilities: ModelCapabilities;\n public readonly pricing?: ModelPricing;\n\n private readonly client: OpenAI;\n private readonly config: OpenAIModelConfig;\n private readonly logger: Logger = log;\n\n public constructor(client: OpenAI, config: OpenAIModelConfig, provider: string = \"openai\") {\n // `api: \"responses\"` belongs to `OpenAIResponsesModel`, which only the\n // `OpenAISDK.model()` factory constructs. This class speaks Chat\n // Completions only; accepting the flag here would silently ignore it.\n if (config.api === \"responses\") {\n throw new InvalidRequestError(\n 'OpenAIModel speaks Chat Completions only and cannot serve api: \"responses\". Use OpenAISDK.model({ api: \"responses\" }) on the direct OpenAI provider.',\n { context: { provider, model: config.name, api: config.api } },\n );\n }\n\n this.client = client;\n this.config = config;\n this.name = config.name;\n this.provider = provider;\n this.pricing = config.pricing;\n this.capabilities = {\n structuredOutput: config.structuredOutput ?? inferStructuredOutput(config.responseFormat),\n vision: config.vision ?? inferVisionCapability(config.name),\n // o-series + gpt-5 / gpt-6 models surface a reasoning channel and accept\n // the `reasoning_effort` param. Explicit config wins over the\n // name-prefix inference.\n reasoning: config.reasoning ?? inferReasoningCapability(config.name),\n // Direct OpenAI requests support automatic cache hits and explicit\n // content-part breakpoints. Wrapper endpoints have distinct provider\n // labels and may reject these OpenAI-only request fields.\n promptCaching: this.provider === \"openai\",\n // PDF + audio INPUT are off by default — OpenAI accepts `file`\n // (PDF) and `input_audio` parts only on specific models, so the\n // flags are conservative/honest and opt-in via config rather than\n // name-inferred. When set, the agent admits the attachments and\n // `toOpenAIMessages` maps them to the real wire parts.\n pdf: config.pdf ?? false,\n audio: config.audio ?? false,\n };\n }\n\n /**\n * Single-shot completion. Sends the full message list to the Chat\n * Completions endpoint, waits for the terminal response, and reshapes it\n * into a vendor-neutral `ModelResponse`. Per-call `options` override the\n * instance's `ModelConfig` defaults for this call only.\n */\n public async complete(messages: Message[], options?: ModelCallOptions): Promise<ModelResponse> {\n // Per-call request/response logs are hot-path in production agents\n // — keep them at `debug` so `info` stays reserved for lifecycle\n // events (agent starting/completed, etc.). Operators who need to\n // audit every LLM call can raise log-level at runtime.\n this.logger.debug(LOG_MODULE, \"request\", \"Starting call to chat.completions\", {\n model: this.name,\n messageCount: messages.length,\n streaming: false,\n toolCount: options?.tools?.length ?? 0,\n });\n\n let response: OpenAI.Chat.Completions.ChatCompletion;\n\n try {\n response = await this.client.chat.completions.create(\n mergeProviderBody<OpenAI.Chat.Completions.ChatCompletionCreateParamsNonStreaming>(\n {\n model: this.name,\n ...compatibleSearchParams(this.provider, options),\n messages: toOpenAIMessages(messages, this.promptCacheBreakpoints(options)),\n ...this.buildSamplingParams(options),\n ...buildPromptCacheParams(this.provider, this.config),\n ...this.buildTools(options),\n ...this.buildResponseFormat(options?.responseSchema),\n ...this.buildReasoningParams(options?.reasoning, Boolean(options?.tools?.length)),\n },\n options,\n ),\n options?.signal ? { signal: options.signal } : undefined,\n );\n } catch (thrown) {\n const wrapped = wrapOpenAIError(thrown);\n\n this.logger.error(LOG_MODULE, \"error\", wrapped.message, {\n code: wrapped.code,\n context: wrapped.context,\n });\n\n throw wrapped;\n }\n\n const [choice] = response.choices;\n\n if (choice === undefined) {\n // A 200 carrying an empty `choices` array. Every line below reads off\n // `choice`, so without this the caller got a bare `TypeError: Cannot\n // read properties of undefined` — no provider, no model, no hint that\n // the response itself was the problem rather than our handling of it.\n //\n // `ProviderError` because that is what this is: the provider answered\n // successfully and returned nothing usable. Callers already branch on\n // `instanceof ProviderError` for any provider-side failure.\n throw new ProviderError(\"OpenAI returned a response with no choices.\", {\n context: { provider: \"openai\", model: this.name, choices: response.choices.length },\n });\n }\n\n const finishReason = mapFinishReason(choice.finish_reason);\n const usage = this.extractUsage(response.usage);\n\n this.logger.debug(LOG_MODULE, \"response\", \"call to chat.completions succeeded\", {\n finishReason,\n usage,\n });\n\n return {\n content: choice.message.content ?? \"\",\n finishReason,\n usage,\n toolCalls: this.extractToolCalls(choice.message.tool_calls),\n };\n }\n\n /**\n * Incremental streaming completion. Yields neutral `ModelStreamChunk`s —\n * `delta` for text tokens, `tool-call` when the model requests a tool,\n * and a terminal `done` carrying the final finish reason + usage totals.\n * Callers consume it with `for await`.\n *\n * With `options.streamChunks` it also yields `tool-args` for each\n * `tool_calls[].function.arguments` fragment and `reasoning` for a\n * vendor's `reasoning_content` / `reasoning` delta field. Usage arrives\n * only on the final chunk here, so no `usage` chunk is emitted.\n */\n public async *stream(\n messages: Message[],\n options?: ModelCallOptions,\n ): AsyncIterable<ModelStreamChunk> {\n this.logger.debug(LOG_MODULE, \"request\", \"Starting streaming call to chat.completions\", {\n model: this.name,\n messageCount: messages.length,\n streaming: true,\n toolCount: options?.tools?.length ?? 0,\n });\n\n let stream: Awaited<ReturnType<typeof this.client.chat.completions.create>>;\n\n try {\n stream = await this.client.chat.completions.create(\n mergeProviderBody<OpenAI.Chat.Completions.ChatCompletionCreateParamsStreaming>(\n {\n model: this.name,\n ...compatibleSearchParams(this.provider, options),\n messages: toOpenAIMessages(messages, this.promptCacheBreakpoints(options)),\n ...this.buildSamplingParams(options),\n ...buildPromptCacheParams(this.provider, this.config),\n ...this.buildTools(options),\n stream: true,\n stream_options: { include_usage: true },\n ...this.buildResponseFormat(options?.responseSchema),\n ...this.buildReasoningParams(options?.reasoning, Boolean(options?.tools?.length)),\n },\n options,\n ),\n options?.signal ? { signal: options.signal } : undefined,\n );\n } catch (thrown) {\n const wrapped = wrapOpenAIError(thrown);\n\n this.logger.error(LOG_MODULE, \"error\", wrapped.message, {\n code: wrapped.code,\n context: wrapped.context,\n });\n\n throw wrapped;\n }\n\n let rawFinishReason: string = \"stop\";\n let usage: Usage = { input: 0, output: 0, total: 0 };\n const want = resolveStreamChunks(options);\n const toolCallAccum = new Map<\n number,\n { id: string; name: string; arguments: string; streamed: number }\n >();\n\n try {\n for await (const chunk of stream as AsyncIterable<OpenAI.Chat.Completions.ChatCompletionChunk>) {\n const delta = chunk.choices[0]?.delta;\n const finish = chunk.choices[0]?.finish_reason;\n\n if (want.reasoning && delta) {\n const reasoning = readReasoningDelta(delta);\n\n if (reasoning) {\n yield { type: \"reasoning\", delta: reasoning };\n }\n }\n\n if (delta?.content) {\n yield { type: \"delta\", content: delta.content };\n }\n\n if (delta?.tool_calls) {\n for (const toolCall of delta.tool_calls) {\n const idx = toolCall.index ?? 0;\n if (!toolCallAccum.has(idx)) {\n toolCallAccum.set(idx, { id: \"\", name: \"\", arguments: \"\", streamed: 0 });\n }\n const acc = toolCallAccum.get(idx)!;\n if (toolCall.id) acc.id = toolCall.id;\n if (toolCall.function?.name) acc.name = toolCall.function.name;\n if (toolCall.function?.arguments) acc.arguments += toolCall.function.arguments;\n\n // The first fragment of a call carries its id and name; later\n // ones only `index` + argument text. Anything that arrived\n // before the name is flushed once the call is identified.\n if (want.toolArgs && acc.name && acc.arguments.length > acc.streamed) {\n yield {\n type: \"tool-args\",\n callId: acc.id,\n name: acc.name,\n delta: acc.arguments.slice(acc.streamed),\n };\n acc.streamed = acc.arguments.length;\n }\n }\n }\n\n if (finish) {\n rawFinishReason = finish;\n }\n\n if (chunk.usage) {\n usage = this.extractUsage(chunk.usage);\n }\n }\n\n for (const acc of toolCallAccum.values()) {\n // Skip accumulators that never received a function name — those\n // are partial fragments the model started but never identified\n // (e.g. arguments-only deltas with no originating `id`/`name`).\n // Yielding them produces nameless tool-calls the agent runtime\n // can't dispatch and would mis-attribute as a registered tool.\n if (!acc.name) continue;\n\n yield {\n type: \"tool-call\",\n id: acc.id,\n name: acc.name,\n input: safeJsonParse<Record<string, unknown>>(acc.arguments, {}),\n };\n }\n } catch (thrown) {\n const wrapped = wrapOpenAIError(thrown);\n\n this.logger.error(LOG_MODULE, \"error\", wrapped.message, {\n code: wrapped.code,\n context: wrapped.context,\n });\n\n throw wrapped;\n }\n\n const finishReason = mapFinishReason(rawFinishReason);\n\n this.logger.debug(LOG_MODULE, \"response\", \"Streaming call to chat.completions succeeded\", {\n finishReason,\n usage,\n });\n\n yield {\n type: \"done\",\n finishReason,\n usage,\n };\n }\n\n /**\n * Spread-friendly `tools` fragment: the function tools, then the\n * provider-defined entries whose `provider` equals this model's\n * provider label (`\"openai\"`, or the vendor label an OpenAI-compatible\n * wrapper sets, e.g. `\"groq\"`), sent verbatim. Chat Completions only\n * accepts `function` and `custom` tool types from OpenAI itself; its\n * hosted tools (`web_search`, `file_search`, …) need `api: \"responses\"`\n * and OpenAI rejects them here with a 400.\n */\n private buildTools(options: ModelCallOptions | undefined): {\n tools?: OpenAI.Chat.Completions.ChatCompletionTool[];\n } {\n const { tools, definitions } = selectProviderTools(options, {\n accepts: [this.provider],\n module: LOG_MODULE,\n model: this.name,\n logger: this.logger,\n });\n // https://platform.kimi.ai/docs/guide/use-web-search — retires 2026-10-20.\n if (\n (this.provider === \"moonshot\" || this.provider === \"kimi\") &&\n definitions.some((definition) => {\n const fn = definition.function;\n return (\n definition.type === \"builtin_function\" &&\n typeof fn === \"object\" &&\n fn !== null &&\n \"name\" in fn &&\n fn.name === \"$web_search\"\n );\n })\n ) {\n throw new InvalidRequestError(\n \"Kimi's builtin $web_search retires on 20 October 2026. Use kimiSearchTool() / kimiFetchTool() from @warlock.js/ai-openai.\",\n );\n }\n // Native search is a Responses-only xAI feature. Fail before transport.\n // https://docs.x.ai/developers/tools/web-search\n if (\n this.provider === \"xai\" &&\n definitions.some(\n (definition) => definition.type === \"web_search\" || definition.type === \"x_search\",\n )\n ) {\n throw new InvalidRequestError(\n \"xAI native web_search / x_search requires POST /v1/responses. XaiSDK currently supports Chat Completions only; use xAI's Responses API with the vendor SDK for native search.\",\n );\n }\n const mapped = [\n ...(toOpenAITools(tools) ?? []),\n ...(definitions as unknown as OpenAI.Chat.Completions.ChatCompletionTool[]),\n ];\n\n return mapped.length > 0 ? { tools: mapped } : {};\n }\n\n /** Map the neutral cache hint only for the direct OpenAI provider. */\n private promptCacheBreakpoints(options: ModelCallOptions | undefined): number {\n return this.provider === \"openai\" ? (options?.cacheControl?.breakpoints ?? 0) : 0;\n }\n\n /**\n * Translate the neutral `responseSchema` option into OpenAI's\n * `response_format` parameter. The mode decision (override handling,\n * strict-compatibility downgrade to `json_object`) lives in\n * `planStructuredOutput`, shared with the Responses adapter.\n *\n * Returns an empty spread when no schema was supplied, so the caller\n * can unconditionally `...buildResponseFormat(...)` into the request.\n */\n private buildResponseFormat(responseSchema: Record<string, unknown> | undefined): {\n response_format?: OpenAI.Chat.Completions.ChatCompletionCreateParams[\"response_format\"];\n } {\n const plan = planStructuredOutput(responseSchema, this.config.responseFormat);\n\n if (plan.mode === \"json_schema\") {\n return {\n response_format: {\n type: \"json_schema\",\n json_schema: { name: \"response\", schema: plan.schema, strict: true },\n },\n };\n }\n\n if (plan.mode === \"json_object\") {\n return { response_format: { type: \"json_object\" } };\n }\n\n return {};\n }\n\n /**\n * Normalize OpenAI's `usage` block (which may be absent on some responses\n * or partials) into the neutral `Usage` shape. Missing usage collapses to\n * zeros rather than propagating `undefined`, so downstream aggregation\n * math stays safe.\n *\n * `cachedTokens` mirrors `prompt_tokens_details.cached_tokens` (the\n * subset of the prompt served from OpenAI's automatic prompt cache);\n * `reasoningTokens` mirrors `completion_tokens_details.reasoning_tokens`\n * (the hidden reasoning channel on o-series / gpt-5 models, already\n * counted within `output`). Both are emitted only when the provider\n * reports a positive value, so non-reasoning / uncached calls keep the\n * lean `{ input, output, total }` shape. `cacheWriteTokens` mirrors\n * `prompt_tokens_details.cache_write_tokens` (billed writes on the\n * models that charge for them). `cost` is attached when priced.\n */\n private extractUsage(raw: OpenAI.Completions.CompletionUsage | undefined): Usage {\n if (!raw) {\n return { input: 0, output: 0, total: 0 };\n }\n\n return buildUsage(\n {\n input: raw.prompt_tokens ?? 0,\n output: raw.completion_tokens ?? 0,\n total: raw.total_tokens ?? 0,\n cachedTokens: raw.prompt_tokens_details?.cached_tokens,\n cacheWriteTokens: raw.prompt_tokens_details?.cache_write_tokens,\n reasoningTokens: raw.completion_tokens_details?.reasoning_tokens,\n },\n this.pricing,\n );\n }\n\n /**\n * Translate the neutral `ModelCallOptions.reasoning` hint into OpenAI's\n * `reasoning_effort` request param. Only `effort` maps — OpenAI's Chat\n * Completions API exposes a discrete effort knob, not a token budget,\n * so `reasoning.maxTokens` (the Anthropic extended-thinking cap) has no\n * wire equivalent here and is silently ignored.\n *\n * The neutral `ReasoningEffort` (`\"low\" | \"medium\" | \"high\" | \"none\"`)\n * is a subset of OpenAI's accepted values, so it forwards verbatim —\n * `\"none\"` included. `\"none\"` is load-bearing: gpt-5 / o-series models\n * **reject function tools** on Chat Completions while reasoning is\n * active, and the endpoint accepts tools only when `reasoning_effort`\n * is `\"none\"` (the alternative is the Responses API). This is why the\n * param is EMITTED for `\"none\"` rather than omitted — omitting it\n * leaves the model reasoning server-side by default, so tools would\n * still be rejected.\n *\n * No-ops when the model is not reasoning-capable\n * (`capabilities.reasoning` is false — e.g. `gpt-4o`, which 400s on any\n * `reasoning_effort`).\n *\n * When the caller gave no explicit `effort`: defaults to `\"none\"` IF\n * `hasTools` is true, else omits the param (provider default — the\n * pre-existing behavior for a tool-less call). The default exists\n * because, on a reasoning-capable model, there is no working alternative\n * to `\"none\"` when tools are attached — omitting `reasoning_effort`\n * leaves reasoning on server-side, which Chat Completions rejects tools\n * for (empty replies on some model generations, a hard 400 — \"Function\n * tools with reasoning_effort are not supported ... in\n * /v1/chat/completions\" — on newer ones). An explicit `effort` from the\n * caller always wins over this default, in either direction.\n *\n * Returns an empty spread when nothing applies, so the caller can\n * unconditionally `...buildReasoningParams(...)` into the request.\n */\n /**\n * Output-length and sampling params, in current Chat Completions shapes only.\n *\n * - `max_completion_tokens`, never the legacy `max_tokens`: current\n * models (the gpt-5 family and later) answer `400 unsupported_parameter`\n * to `max_tokens`, and `max_completion_tokens` is accepted everywhere.\n * - No `temperature` for a reasoning-capable model: those accept only the\n * default value and reject any other with a 400.\n */\n private buildSamplingParams(options: ModelCallOptions | undefined): {\n max_completion_tokens?: number;\n temperature?: number;\n } {\n const maxTokens = options?.maxTokens ?? this.config.maxTokens;\n const temperature = options?.temperature ?? this.config.temperature;\n\n return {\n max_completion_tokens: maxTokens,\n ...(!this.capabilities.reasoning && { temperature }),\n };\n }\n\n private buildReasoningParams(\n reasoning: ModelCallOptions[\"reasoning\"],\n hasTools: boolean,\n ): {\n reasoning_effort?: OpenAI.Chat.Completions.ChatCompletionCreateParams[\"reasoning_effort\"];\n } {\n if (!this.capabilities.reasoning) {\n return {};\n }\n\n if (!reasoning?.effort) {\n return hasTools ? { reasoning_effort: \"none\" } : {};\n }\n\n return { reasoning_effort: toOpenAIReasoningEffort(reasoning.effort) };\n }\n\n /**\n * Reshape OpenAI's `tool_calls` array into the neutral\n * `ModelToolCallRequest[]`. The raw `arguments` field is a JSON string\n * per OpenAI's protocol — we parse it defensively via `safeJsonParse` so\n * malformed or empty arguments yield an empty object instead of crashing\n * the trip. Returns `undefined` when no tools were requested so callers\n * can branch on presence.\n */\n private extractToolCalls(\n rawToolCalls: OpenAI.Chat.Completions.ChatCompletionMessageToolCall[] | undefined,\n ): ModelToolCallRequest[] | undefined {\n if (!rawToolCalls || rawToolCalls.length === 0) {\n return undefined;\n }\n\n return rawToolCalls.map((toolCall) => ({\n id: toolCall.id,\n name: (toolCall as any).function.name,\n input: safeJsonParse<Record<string, unknown>>((toolCall as any).function.arguments, {}),\n }));\n }\n}\n\n/**\n * Reasoning text an OpenAI-compatible vendor puts on a Chat Completions\n * stream delta. OpenAI itself streams none on this API. DeepSeek documents\n * `delta.reasoning_content` (API reference, chat completion chunk); Groq\n * documents the parsed reasoning under a `reasoning` field. Neither is in\n * the OpenAI SDK's delta type, so the delta is read as a plain record.\n */\nfunction readReasoningDelta(delta: object): string | undefined {\n const record = delta as Record<string, unknown>;\n\n for (const key of [\"reasoning_content\", \"reasoning\"]) {\n const value = record[key];\n\n if (typeof value === \"string\" && value !== \"\") {\n return value;\n }\n }\n\n return undefined;\n}\n"],"mappings":"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"}
@@ -1,8 +1,13 @@
1
+ import "@warlock.js/ai";
1
2
  //#region ../../packages/ai-openai/src/provider-options.d.ts
2
3
  /** Adapter-only fields merged after the neutral request parameters. */
3
- type OpenAICallOptions = {
4
+ export type OpenAICallOptions = {
4
5
  body?: Record<string, unknown>;
5
6
  };
7
+ /** DashScope OpenAI-compatible request flag (not a tool).
8
+ * https://www.alibabacloud.com/help/en/model-studio/web-search */
9
+ export type QwenCallOptions = {
10
+ enableSearch?: boolean;
11
+ };
6
12
  //#endregion
7
- export { OpenAICallOptions };
8
13
  //# sourceMappingURL=provider-options.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"provider-options.d.mts","names":[],"sources":["../../../../../../../packages/ai-openai/src/provider-options.ts"],"mappings":";;KAGY,iBAAA;EAAsB,IAAA,GAAO,MAAM;AAAA"}
1
+ {"version":3,"file":"provider-options.d.mts","names":[],"sources":["../../../../../../../packages/ai-openai/src/provider-options.ts"],"mappings":";;;YAGY;EAAsB,OAAO;;;;YA6D7B;EAAoB"}
@@ -1,7 +1,7 @@
1
1
  import { InvalidRequestError } from "@warlock.js/ai";
2
2
 
3
3
  //#region ../../packages/ai-openai/src/provider-options.ts
4
- const RESERVED_FIELDS = new Set([
4
+ const RESERVED_FIELDS = /* @__PURE__ */ new Set([
5
5
  "model",
6
6
  "messages",
7
7
  "input",
@@ -43,7 +43,14 @@ function isPlainObject(value) {
43
43
  const prototype = Object.getPrototypeOf(value);
44
44
  return prototype === Object.prototype || prototype === null;
45
45
  }
46
+ function compatibleSearchParams(provider, options) {
47
+ if (provider !== "qwen" && provider !== "dashscope") return {};
48
+ const value = options?.providerOptions?.[provider]?.enableSearch;
49
+ if (value === void 0) return {};
50
+ if (typeof value !== "boolean") throw new InvalidRequestError(`providerOptions.${provider}.enableSearch must be a boolean`);
51
+ return { enable_search: value };
52
+ }
46
53
 
47
54
  //#endregion
48
- export { mergeProviderBody };
55
+ export { compatibleSearchParams, mergeProviderBody };
49
56
  //# sourceMappingURL=provider-options.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"provider-options.mjs","names":[],"sources":["../../../../../../../packages/ai-openai/src/provider-options.ts"],"sourcesContent":["import { InvalidRequestError, type ModelCallOptions } from \"@warlock.js/ai\";\n\n/** Adapter-only fields merged after the neutral request parameters. */\nexport type OpenAICallOptions = { body?: Record<string, unknown> };\n\nconst RESERVED_FIELDS = new Set([\n \"model\",\n \"messages\",\n \"input\",\n \"instructions\",\n \"tools\",\n \"stream\",\n \"stream_options\",\n \"previous_response_id\",\n \"conversation\",\n \"store\",\n \"include\",\n \"functions\",\n \"function_call\",\n \"prompt\",\n \"background\",\n]);\n\nexport function mergeProviderBody<T extends object>(\n built: T,\n options: ModelCallOptions | undefined,\n): T {\n const provider = options?.providerOptions?.openai as OpenAICallOptions | undefined;\n const body = provider?.body ?? {};\n const reserved = Object.keys(body).filter((key) => RESERVED_FIELDS.has(key));\n if (reserved.length > 0) {\n throw new InvalidRequestError(\n `providerOptions.openai.body cannot set ${reserved.join(\", \")}: use the call's messages, tools and model options.`,\n { context: { fields: reserved } },\n );\n }\n const defaults = built as Record<string, unknown>;\n const overrides = Object.fromEntries(\n Object.entries(body).map(([key, value]) => {\n const previous = defaults[key];\n if (!isPlainObject(previous) || !isPlainObject(value)) return [key, value];\n // A different discriminator selects a whole SDK variant. Keeping fields\n // from the previous variant can make an otherwise valid override invalid.\n if (\n typeof previous.type === \"string\" &&\n typeof value.type === \"string\" &&\n previous.type !== value.type\n )\n return [key, value];\n return [key, { ...previous, ...value }];\n }),\n );\n return { ...built, ...overrides };\n}\n\n/** Merge only plain objects; arrays, null and provider scalar values replace the field. */\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n if (value === null || typeof value !== \"object\") return false;\n const prototype = Object.getPrototypeOf(value);\n return prototype === Object.prototype || prototype === null;\n}\n"],"mappings":";;;AAKA,MAAM,kBAAkB,IAAI,IAAI;CAC9B;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF,CAAC;AAED,SAAgB,kBACd,OACA,SACG;CAEH,MAAM,QADW,SAAS,iBAAiB,OACtB,EAAE,QAAQ,CAAC;CAChC,MAAM,WAAW,OAAO,KAAK,IAAI,CAAC,CAAC,QAAQ,QAAQ,gBAAgB,IAAI,GAAG,CAAC;CAC3E,IAAI,SAAS,SAAS,GACpB,MAAM,IAAI,oBACR,0CAA0C,SAAS,KAAK,IAAI,EAAE,sDAC9D,EAAE,SAAS,EAAE,QAAQ,SAAS,EAAE,CAClC;CAEF,MAAM,WAAW;CACjB,MAAM,YAAY,OAAO,YACvB,OAAO,QAAQ,IAAI,CAAC,CAAC,KAAK,CAAC,KAAK,WAAW;EACzC,MAAM,WAAW,SAAS;EAC1B,IAAI,CAAC,cAAc,QAAQ,KAAK,CAAC,cAAc,KAAK,GAAG,OAAO,CAAC,KAAK,KAAK;EAGzE,IACE,OAAO,SAAS,SAAS,YACzB,OAAO,MAAM,SAAS,YACtB,SAAS,SAAS,MAAM,MAExB,OAAO,CAAC,KAAK,KAAK;EACpB,OAAO,CAAC,KAAK;GAAE,GAAG;GAAU,GAAG;EAAM,CAAC;CACxC,CAAC,CACH;CACA,OAAO;EAAE,GAAG;EAAO,GAAG;CAAU;AAClC;;AAGA,SAAS,cAAc,OAAkD;CACvE,IAAI,UAAU,QAAQ,OAAO,UAAU,UAAU,OAAO;CACxD,MAAM,YAAY,OAAO,eAAe,KAAK;CAC7C,OAAO,cAAc,OAAO,aAAa,cAAc;AACzD"}
1
+ {"version":3,"file":"provider-options.mjs","names":[],"sources":["../../../../../../../packages/ai-openai/src/provider-options.ts"],"sourcesContent":["import { InvalidRequestError, type ModelCallOptions } from \"@warlock.js/ai\";\n\n/** Adapter-only fields merged after the neutral request parameters. */\nexport type OpenAICallOptions = { body?: Record<string, unknown> };\n\nconst RESERVED_FIELDS = new Set([\n \"model\",\n \"messages\",\n \"input\",\n \"instructions\",\n \"tools\",\n \"stream\",\n \"stream_options\",\n \"previous_response_id\",\n \"conversation\",\n \"store\",\n \"include\",\n \"functions\",\n \"function_call\",\n \"prompt\",\n \"background\",\n]);\n\nexport function mergeProviderBody<T extends object>(\n built: T,\n options: ModelCallOptions | undefined,\n): T {\n const provider = options?.providerOptions?.openai as OpenAICallOptions | undefined;\n const body = provider?.body ?? {};\n const reserved = Object.keys(body).filter((key) => RESERVED_FIELDS.has(key));\n if (reserved.length > 0) {\n throw new InvalidRequestError(\n `providerOptions.openai.body cannot set ${reserved.join(\", \")}: use the call's messages, tools and model options.`,\n { context: { fields: reserved } },\n );\n }\n const defaults = built as Record<string, unknown>;\n const overrides = Object.fromEntries(\n Object.entries(body).map(([key, value]) => {\n const previous = defaults[key];\n if (!isPlainObject(previous) || !isPlainObject(value)) return [key, value];\n // A different discriminator selects a whole SDK variant. Keeping fields\n // from the previous variant can make an otherwise valid override invalid.\n if (\n typeof previous.type === \"string\" &&\n typeof value.type === \"string\" &&\n previous.type !== value.type\n )\n return [key, value];\n return [key, { ...previous, ...value }];\n }),\n );\n return { ...built, ...overrides };\n}\n\n/** Merge only plain objects; arrays, null and provider scalar values replace the field. */\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n if (value === null || typeof value !== \"object\") return false;\n const prototype = Object.getPrototypeOf(value);\n return prototype === Object.prototype || prototype === null;\n}\n\n/** DashScope OpenAI-compatible request flag (not a tool).\n * https://www.alibabacloud.com/help/en/model-studio/web-search */\nexport type QwenCallOptions = { enableSearch?: boolean };\n\nexport function compatibleSearchParams(\n provider: string,\n options: ModelCallOptions | undefined,\n): { enable_search?: boolean } {\n if (provider !== \"qwen\" && provider !== \"dashscope\") return {};\n const value = options?.providerOptions?.[provider]?.enableSearch;\n if (value === undefined) return {};\n if (typeof value !== \"boolean\")\n throw new InvalidRequestError(`providerOptions.${provider}.enableSearch must be a boolean`);\n return { enable_search: value };\n}\n"],"mappings":";;;AAKA,MAAM,kCAAkB,IAAI,IAAI;CAC9B;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF,CAAC;AAED,SAAgB,kBACd,OACA,SACG;CAEH,MAAM,QADW,SAAS,iBAAiB,OACtB,EAAE,QAAQ,CAAC;CAChC,MAAM,WAAW,OAAO,KAAK,IAAI,CAAC,CAAC,QAAQ,QAAQ,gBAAgB,IAAI,GAAG,CAAC;CAC3E,IAAI,SAAS,SAAS,GACpB,MAAM,IAAI,oBACR,0CAA0C,SAAS,KAAK,IAAI,EAAE,sDAC9D,EAAE,SAAS,EAAE,QAAQ,SAAS,EAAE,CAClC;CAEF,MAAM,WAAW;CACjB,MAAM,YAAY,OAAO,YACvB,OAAO,QAAQ,IAAI,CAAC,CAAC,KAAK,CAAC,KAAK,WAAW;EACzC,MAAM,WAAW,SAAS;EAC1B,IAAI,CAAC,cAAc,QAAQ,KAAK,CAAC,cAAc,KAAK,GAAG,OAAO,CAAC,KAAK,KAAK;EAGzE,IACE,OAAO,SAAS,SAAS,YACzB,OAAO,MAAM,SAAS,YACtB,SAAS,SAAS,MAAM,MAExB,OAAO,CAAC,KAAK,KAAK;EACpB,OAAO,CAAC,KAAK;GAAE,GAAG;GAAU,GAAG;EAAM,CAAC;CACxC,CAAC,CACH;CACA,OAAO;EAAE,GAAG;EAAO,GAAG;CAAU;AAClC;;AAGA,SAAS,cAAc,OAAkD;CACvE,IAAI,UAAU,QAAQ,OAAO,UAAU,UAAU,OAAO;CACxD,MAAM,YAAY,OAAO,eAAe,KAAK;CAC7C,OAAO,cAAc,OAAO,aAAa,cAAc;AACzD;AAMA,SAAgB,uBACd,UACA,SAC6B;CAC7B,IAAI,aAAa,UAAU,aAAa,aAAa,OAAO,CAAC;CAC7D,MAAM,QAAQ,SAAS,kBAAkB,SAAS,EAAE;CACpD,IAAI,UAAU,QAAW,OAAO,CAAC;CACjC,IAAI,OAAO,UAAU,WACnB,MAAM,IAAI,oBAAoB,mBAAmB,SAAS,gCAAgC;CAC5F,OAAO,EAAE,eAAe,MAAM;AAChC"}
@@ -11,6 +11,7 @@ import "./utils/index.mjs";
11
11
  import { mergeProviderBody } from "./provider-options.mjs";
12
12
  import { inferReasoningCapability } from "./known-reasoning-models.mjs";
13
13
  import { inferVisionCapability } from "./known-vision-models.mjs";
14
+ import { hostedOutput } from "./utils/hosted-output.mjs";
14
15
  import { InvalidRequestError, ProviderError, resolveStreamChunks, safeJsonParse, selectProviderTools } from "@warlock.js/ai";
15
16
  import { log } from "@warlock.js/logger";
16
17
 
@@ -21,7 +22,7 @@ const LOG_MODULE = "ai.openai";
21
22
  * (installed SDK `ResponseOutputItem` union). The adapter maps only
22
23
  * `function_call` to a neutral tool call, so these fail closed.
23
24
  */
24
- const CLIENT_RUN_ITEM_TYPES = new Set([
25
+ const CLIENT_RUN_ITEM_TYPES = /* @__PURE__ */ new Set([
25
26
  "computer_call",
26
27
  "custom_tool_call",
27
28
  "local_shell_call",
@@ -62,7 +63,8 @@ const CLIENT_RUN_ITEM_TYPES = new Set([
62
63
  *
63
64
  * **Provider-defined tools.** `ai.providerTool({ provider: "openai" })`
64
65
  * definitions are appended to `tools` verbatim. Hosted tools run inside the
65
- * response; their output items (`web_search_call`, …) are not replayed
66
+ * response; their output items (`web_search_call`, …) are retained in
67
+ * `providerMetadata.openai.output` for inspection and are not replayed
66
68
  * (with `store: false` the next turn only needs the answer text), and a
67
69
  * reasoning item that led into one is not replayed either (see
68
70
  * `pickTurnReasoning`).
@@ -117,6 +119,7 @@ var OpenAIResponsesModel = class {
117
119
  hasToolCalls: toolCalls !== void 0
118
120
  });
119
121
  const usage = this.extractUsage(response.usage);
122
+ const providerMetadata = hostedOutput(usage, output, this.pricing);
120
123
  this.logger.debug(LOG_MODULE, "response", "call to responses.create succeeded", {
121
124
  finishReason,
122
125
  usage
@@ -125,6 +128,7 @@ var OpenAIResponsesModel = class {
125
128
  content,
126
129
  finishReason,
127
130
  usage,
131
+ ...providerMetadata ? { providerMetadata } : {},
128
132
  ...toolCalls ? { toolCalls } : {}
129
133
  };
130
134
  }
@@ -258,6 +262,8 @@ var OpenAIResponsesModel = class {
258
262
  hasToolCalls: toolCalls.length > 0
259
263
  });
260
264
  const usage = this.extractUsage(terminal.usage);
265
+ const output = terminal.output?.length ? terminal.output : outputItems;
266
+ const providerMetadata = hostedOutput(usage, output, this.pricing);
261
267
  this.logger.debug(LOG_MODULE, "response", "Streaming call to responses.create succeeded", {
262
268
  finishReason,
263
269
  usage
@@ -265,7 +271,8 @@ var OpenAIResponsesModel = class {
265
271
  yield {
266
272
  type: "done",
267
273
  finishReason,
268
- usage
274
+ usage,
275
+ ...providerMetadata ? { providerMetadata } : {}
269
276
  };
270
277
  }
271
278
  /**
@@ -292,7 +299,7 @@ var OpenAIResponsesModel = class {
292
299
  ...instructions !== void 0 ? { instructions } : {},
293
300
  input,
294
301
  store: false,
295
- include: ["reasoning.encrypted_content"],
302
+ include: ["reasoning.encrypted_content", ...tools?.some((tool) => tool.type === "web_search" || tool.type === "web_search_2025_08_26" || tool.type === "web_search_preview" || tool.type === "web_search_preview_2025_03_11") ? ["web_search_call.action.sources"] : []],
296
303
  ...maxTokens !== void 0 ? { max_output_tokens: maxTokens } : {},
297
304
  ...!this.capabilities.reasoning && temperature !== void 0 ? { temperature } : {},
298
305
  ...tools ? { tools } : {},
@@ -1 +1 @@
1
- {"version":3,"file":"responses-model.mjs","names":[],"sources":["../../../../../../../packages/ai-openai/src/responses-model.ts"],"sourcesContent":["import {\n InvalidRequestError,\n ProviderError,\n resolveStreamChunks,\n safeJsonParse,\n selectProviderTools,\n type Message,\n type ModelCallOptions,\n type ModelCapabilities,\n type ModelContract,\n type ModelPricing,\n type ModelResponse,\n type ModelStreamChunk,\n type ModelToolCallRequest,\n type Usage,\n} from \"@warlock.js/ai\";\nimport { log, type Logger } from \"@warlock.js/logger\";\nimport type OpenAI from \"openai\";\nimport { mergeProviderBody } from \"./provider-options\";\nimport type { OpenAIModelConfig } from \"./config.type\";\nimport { inferReasoningCapability } from \"./known-reasoning-models\";\nimport { inferVisionCapability } from \"./known-vision-models\";\nimport {\n attachReasoningReplay,\n buildPromptCacheParams,\n buildUsage,\n inferStructuredOutput,\n mapResponsesFinishReason,\n pickTurnReasoning,\n planStructuredOutput,\n toOpenAIReasoningEffort,\n toResponseFailure,\n toResponsesInput,\n toResponsesToolChoice,\n toResponsesTools,\n wrapOpenAIError,\n} from \"./utils\";\n\nconst LOG_MODULE = \"ai.openai\";\n\n/**\n * Responses output item types of built-in tools the CALLER must execute\n * (installed SDK `ResponseOutputItem` union). The adapter maps only\n * `function_call` to a neutral tool call, so these fail closed.\n */\nconst CLIENT_RUN_ITEM_TYPES: ReadonlySet<string> = new Set([\n \"computer_call\",\n \"custom_tool_call\",\n \"local_shell_call\",\n \"shell_call\",\n \"apply_patch_call\",\n \"mcp_approval_request\",\n]);\n\n/**\n * OpenAI Responses API implementation of `ModelContract` (`api: \"responses\"`).\n *\n * **Role.** A sibling of `OpenAIModel` that speaks `POST /v1/responses`\n * instead of Chat Completions. Its point is letting a tool-using agent run a\n * reasoning model with reasoning ON: the Chat path has to force\n * `reasoning_effort: \"none\"` whenever tools are attached, Responses does not.\n *\n * **Why a separate class.** Only `OpenAISDK.model()` constructs it, and only\n * for the direct `\"openai\"` provider. The wrapper packages (deepseek, groq,\n * xai, mistral) build `OpenAIModel` and cannot reach this class, so an\n * endpoint that does not implement `/v1/responses` is never sent there. The\n * constructor re-checks the provider label as a second lock.\n *\n * **Stateless.** Every request sends `store: false`; nothing is kept on the\n * server and no `previous_response_id` is used. Full history is re-sent each\n * call, exactly like the Chat path.\n *\n * **Reasoning replay.** Every request asks for\n * `include: [\"reasoning.encrypted_content\"]`. The `reasoning` items of a\n * tool-calling turn are stored on the FIRST tool call as\n * `providerMetadata.openaiResponses = { model, reasoningItems }` and sent back\n * before that turn's `function_call` items on the next request (only when the\n * stored model equals the current one). Text-only turns are not replayed.\n *\n * **Streaming.** `stream()` uses `responses.create({ stream: true })` and yields\n * the same chunk sequence as the Chat path: text `delta`s, then the\n * `tool-call`s, then one `done`. With `streamChunks` it also yields\n * `reasoning` (reasoning summary / reasoning text deltas) and `tool-args`\n * (`response.function_call_arguments.delta`).\n *\n * **Provider-defined tools.** `ai.providerTool({ provider: \"openai\" })`\n * definitions are appended to `tools` verbatim. Hosted tools run inside the\n * response; their output items (`web_search_call`, …) are not replayed\n * (with `store: false` the next turn only needs the answer text), and a\n * reasoning item that led into one is not replayed either (see\n * `pickTurnReasoning`).\n */\nexport class OpenAIResponsesModel implements ModelContract {\n public readonly name: string;\n public readonly provider: string;\n public readonly capabilities: ModelCapabilities;\n public readonly pricing?: ModelPricing;\n\n private readonly client: OpenAI;\n private readonly config: OpenAIModelConfig;\n private readonly logger: Logger = log;\n\n public constructor(client: OpenAI, config: OpenAIModelConfig, provider: string = \"openai\") {\n if (provider !== \"openai\") {\n throw new InvalidRequestError(\n `api: \"responses\" is supported only on the direct \"openai\" provider; got provider \"${provider}\".`,\n { context: { provider, model: config.name, api: \"responses\" } },\n );\n }\n\n this.client = client;\n this.config = config;\n this.name = config.name;\n this.provider = provider;\n this.pricing = config.pricing;\n this.capabilities = {\n structuredOutput: config.structuredOutput ?? inferStructuredOutput(config.responseFormat),\n vision: config.vision ?? inferVisionCapability(config.name),\n reasoning: config.reasoning ?? inferReasoningCapability(config.name),\n // Read-side cache accounting (`cachedTokens`, `cacheWriteTokens`) works\n // and `prompt_cache_key` is forwarded. Explicit write breakpoints are not\n // mapped onto Responses yet, so a `cacheControl.breakpoints` hint is\n // ignored rather than rejected.\n promptCaching: true,\n // PDF and audio input are off under Responses in this adapter: the\n // `input_file` / `input_audio` field-level mapping is unverified. The\n // `pdf` / `audio` config flags are deliberately NOT honoured here.\n pdf: false,\n audio: false,\n };\n }\n\n /**\n * Single-shot completion against `client.responses.create`. Sends the full\n * history as Responses `input` items, walks `response.output`, and reshapes\n * it into a vendor-neutral `ModelResponse`. Per-call `options` override the\n * instance defaults for this call only.\n */\n public async complete(messages: Message[], options?: ModelCallOptions): Promise<ModelResponse> {\n this.logger.debug(LOG_MODULE, \"request\", \"Starting call to responses.create\", {\n model: this.name,\n messageCount: messages.length,\n streaming: false,\n toolCount: options?.tools?.length ?? 0,\n });\n\n let response: OpenAI.Responses.Response;\n\n try {\n response = await this.client.responses.create(\n this.buildRequest(messages, options),\n options?.signal ? { signal: options.signal } : undefined,\n );\n } catch (thrown) {\n throw this.logAndWrap(thrown);\n }\n\n if (response.status === \"failed\") {\n throw this.logAndWrap(toResponseFailure(response, this.name));\n }\n\n const output = Array.isArray(response.output) ? response.output : [];\n const { content, toolCalls } = this.readOutput(output);\n const finishReason = mapResponsesFinishReason({\n status: response.status,\n incomplete_details: response.incomplete_details,\n hasToolCalls: toolCalls !== undefined,\n });\n const usage = this.extractUsage(response.usage);\n\n this.logger.debug(LOG_MODULE, \"response\", \"call to responses.create succeeded\", {\n finishReason,\n usage,\n });\n\n return { content, finishReason, usage, ...(toolCalls ? { toolCalls } : {}) };\n }\n\n /** Count native Responses input tokens using the same converted input and tools. */\n public async countTokens(messages: Message[], options?: ModelCallOptions): Promise<number> {\n const request = this.buildRequest(messages, options);\n const body: OpenAI.Responses.InputTokenCountParams = {\n model: request.model,\n input: request.input,\n instructions: request.instructions,\n tools: request.tools,\n tool_choice: request.tool_choice,\n reasoning: request.reasoning,\n text: request.text,\n parallel_tool_calls: request.parallel_tool_calls,\n truncation: request.truncation ?? undefined,\n };\n try {\n const response = await this.client.responses.inputTokens.count(\n body,\n options?.signal ? { signal: options.signal } : undefined,\n );\n return response.input_tokens;\n } catch (thrown) {\n throw this.logAndWrap(thrown);\n }\n }\n\n /**\n * Incremental streaming completion via `responses.create({ stream: true })`\n * (the SDK's `Stream<ResponseStreamEvent>`, `responses.d.ts:55`). Yields the\n * same neutral `ModelStreamChunk` sequence as the Chat path:\n *\n * - `response.output_text.delta` / `response.refusal.delta` -> `delta`.\n * - `response.output_item.done` with a completed `function_call` item ->\n * buffered; `reasoning` items are captured for replay. The `done` copy is\n * used because the `added` copy may be incomplete (`responses.d.ts:5711`).\n * - After the loop: the buffered `tool-call`s (the first carries the\n * replay metadata), then a single `done` with the finish reason and usage\n * from `response.completed` / `response.incomplete`.\n * - `response.failed` and `error` events throw a typed `AIError`; a stream\n * that ends with no terminal event throws `ProviderError`.\n * - Abort goes through the request `signal`; the SDK rejects the read and\n * the error is wrapped like any other.\n */\n public async *stream(\n messages: Message[],\n options?: ModelCallOptions,\n ): AsyncIterable<ModelStreamChunk> {\n this.logger.debug(LOG_MODULE, \"request\", \"Starting streaming call to responses.create\", {\n model: this.name,\n messageCount: messages.length,\n streaming: true,\n toolCount: options?.tools?.length ?? 0,\n });\n\n let events: AsyncIterable<OpenAI.Responses.ResponseStreamEvent>;\n\n try {\n events = await this.client.responses.create(\n { ...this.buildRequest(messages, options), stream: true },\n options?.signal ? { signal: options.signal } : undefined,\n );\n } catch (thrown) {\n throw this.logAndWrap(thrown);\n }\n\n const toolCalls: ModelToolCallRequest[] = [];\n const outputItems: OpenAI.Responses.ResponseOutputItem[] = [];\n const want = resolveStreamChunks(options);\n // `function_call_arguments.delta` names its item by `item_id` only; the\n // `call_id` and `name` come from that item's `output_item.added`.\n const streamingCalls = new Map<string, { callId: string; name: string }>();\n let terminal: OpenAI.Responses.Response | undefined;\n\n try {\n for await (const event of events) {\n if (\n event.type === \"response.output_text.delta\" ||\n event.type === \"response.refusal.delta\"\n ) {\n if (event.delta) {\n yield { type: \"delta\", content: event.delta };\n }\n\n continue;\n }\n\n if (\n event.type === \"response.reasoning_summary_text.delta\" ||\n event.type === \"response.reasoning_text.delta\"\n ) {\n if (want.reasoning && event.delta) {\n yield { type: \"reasoning\", delta: event.delta };\n }\n\n continue;\n }\n\n if (event.type === \"response.output_item.added\") {\n const { item } = event;\n\n if (item.type === \"function_call\" && item.id) {\n streamingCalls.set(item.id, { callId: item.call_id, name: item.name });\n }\n\n continue;\n }\n\n if (event.type === \"response.function_call_arguments.delta\") {\n const call = streamingCalls.get(event.item_id);\n\n if (want.toolArgs && call && event.delta) {\n yield { type: \"tool-args\", callId: call.callId, name: call.name, delta: event.delta };\n }\n\n continue;\n }\n\n if (event.type === \"response.output_item.done\") {\n const { item } = event;\n\n this.assertServerRunItem(item);\n outputItems.push(item);\n\n if (item.type === \"function_call\" && item.status !== \"incomplete\") {\n toolCalls.push(this.toToolCall(item));\n }\n\n continue;\n }\n\n if (event.type === \"response.completed\" || event.type === \"response.incomplete\") {\n terminal = event.response;\n continue;\n }\n\n if (event.type === \"response.failed\") {\n throw toResponseFailure(event.response, this.name);\n }\n\n if (event.type === \"error\") {\n throw wrapOpenAIError({ code: event.code, message: event.message, param: event.param });\n }\n }\n\n if (terminal === undefined) {\n throw new ProviderError(\"OpenAI Responses stream ended before response.completed.\", {\n context: { provider: this.provider, model: this.name },\n });\n }\n } catch (thrown) {\n throw this.logAndWrap(thrown);\n }\n\n for (const call of attachReasoningReplay(\n toolCalls,\n this.name,\n pickTurnReasoning(outputItems),\n )) {\n yield {\n type: \"tool-call\",\n id: call.id,\n name: call.name,\n input: call.input,\n ...(call.providerMetadata ? { providerMetadata: call.providerMetadata } : {}),\n };\n }\n\n const finishReason = mapResponsesFinishReason({\n status: terminal.status,\n incomplete_details: terminal.incomplete_details,\n hasToolCalls: toolCalls.length > 0,\n });\n const usage = this.extractUsage(terminal.usage);\n\n this.logger.debug(LOG_MODULE, \"response\", \"Streaming call to responses.create succeeded\", {\n finishReason,\n usage,\n });\n\n yield { type: \"done\", finishReason, usage };\n }\n\n /**\n * Assemble the `responses.create` body.\n *\n * - System prompt -> `instructions`; history -> `input` items.\n * - `store: false` always (the API default is `true` when omitted), plus\n * `include: [\"reasoning.encrypted_content\"]` for stateless reasoning replay.\n * - `max_output_tokens` caps reasoning and visible output together.\n * - `temperature` only for non-reasoning models (same rule as Chat).\n * - `tools` flat with `strict: false`; `tool_choice` only with tools.\n * - `reasoning.effort` only when the caller gave one, through the same\n * clamp table as Chat. With no explicit effort the provider default\n * applies EVEN with tools attached - that is the point of this API.\n */\n private buildRequest(\n messages: Message[],\n options: ModelCallOptions | undefined,\n ): OpenAI.Responses.ResponseCreateParamsNonStreaming {\n const { instructions, input } = toResponsesInput(messages, this.name);\n const tools = this.buildTools(options);\n const toolChoice = tools ? toResponsesToolChoice(options?.toolChoice) : undefined;\n const maxTokens = options?.maxTokens ?? this.config.maxTokens;\n const temperature = options?.temperature ?? this.config.temperature;\n\n return mergeProviderBody<OpenAI.Responses.ResponseCreateParamsNonStreaming>(\n {\n model: this.name,\n ...(instructions !== undefined ? { instructions } : {}),\n input,\n store: false,\n // Stateless replay: ask for the encrypted reasoning blobs so they can be\n // sent back on the next tool turn (decision 3).\n include: [\"reasoning.encrypted_content\"],\n ...(maxTokens !== undefined ? { max_output_tokens: maxTokens } : {}),\n ...(!this.capabilities.reasoning && temperature !== undefined ? { temperature } : {}),\n ...(tools ? { tools } : {}),\n ...(toolChoice !== undefined ? { tool_choice: toolChoice } : {}),\n ...this.buildTextFormat(options?.responseSchema),\n ...this.buildReasoningParams(options?.reasoning),\n ...buildPromptCacheParams(this.provider, this.config),\n },\n options,\n );\n }\n\n /**\n * The request `tools`: function tools (flat, `strict: false`), then the\n * `ai.providerTool({ provider: \"openai\" })` definitions verbatim — the\n * Responses built-in tools (`web_search`, `file_search`,\n * `code_interpreter`, `image_generation`, `mcp`, …). Other providers'\n * definitions are skipped with a warning. `undefined` when there are none.\n */\n private buildTools(options: ModelCallOptions | undefined): OpenAI.Responses.Tool[] | undefined {\n const { tools, definitions } = selectProviderTools(options, {\n accepts: [this.provider],\n module: LOG_MODULE,\n model: this.name,\n logger: this.logger,\n });\n const mapped: OpenAI.Responses.Tool[] = [\n ...(toResponsesTools(tools) ?? []),\n ...(definitions as unknown as OpenAI.Responses.Tool[]),\n ];\n\n return mapped.length > 0 ? mapped : undefined;\n }\n\n /**\n * Structured output on Responses lives in `text.format`, and `name` /\n * `schema` / `strict` sit directly on the format object (Chat nests them\n * under `json_schema`). The strict-or-downgrade decision is the shared\n * `planStructuredOutput`.\n */\n private buildTextFormat(responseSchema: Record<string, unknown> | undefined): {\n text?: OpenAI.Responses.ResponseTextConfig;\n } {\n const plan = planStructuredOutput(responseSchema, this.config.responseFormat);\n\n if (plan.mode === \"json_schema\") {\n return {\n text: {\n format: { type: \"json_schema\", name: \"response\", schema: plan.schema, strict: true },\n },\n };\n }\n\n if (plan.mode === \"json_object\") {\n return { text: { format: { type: \"json_object\" } } };\n }\n\n return {};\n }\n\n /** `reasoning.effort` for reasoning-capable models, only when the caller set one. */\n private buildReasoningParams(reasoning: ModelCallOptions[\"reasoning\"]): {\n reasoning?: { effort: ReturnType<typeof toOpenAIReasoningEffort> };\n } {\n if (!this.capabilities.reasoning || !reasoning?.effort) {\n return {};\n }\n\n return { reasoning: { effort: toOpenAIReasoningEffort(reasoning.effort) } };\n }\n\n /**\n * Walk `response.output`: `message` items contribute their `output_text`\n * text and any `refusal` text (surfaced as ordinary content, decision 5);\n * each completed `function_call` item becomes one tool call with\n * `id = call_id`. `reasoning` items carry no visible content and are\n * not content. A `function_call` the API marked `incomplete` (truncated\n * arguments) is skipped rather than dispatched with empty input.\n * `reasoning` items are captured and attached to the first tool call for\n * replay (see `attachReasoningReplay`).\n */\n private readOutput(output: OpenAI.Responses.ResponseOutputItem[]): {\n content: string;\n toolCalls: ModelToolCallRequest[] | undefined;\n } {\n let content = \"\";\n const toolCalls: ModelToolCallRequest[] = [];\n\n for (const item of output) {\n this.assertServerRunItem(item);\n\n if (item.type === \"message\") {\n for (const part of item.content) {\n content += part.type === \"refusal\" ? part.refusal : part.text;\n }\n\n continue;\n }\n\n if (item.type === \"function_call\" && item.status !== \"incomplete\") {\n toolCalls.push(this.toToolCall(item));\n }\n }\n\n return {\n content,\n toolCalls:\n toolCalls.length > 0\n ? attachReasoningReplay(toolCalls, this.name, pickTurnReasoning(output))\n : undefined,\n };\n }\n\n /**\n * Fail closed on a built-in tool call that YOUR code would have to run\n * (`shell_call`, `apply_patch_call`, `computer_call`, …, from a\n * provider-defined client tool). These items have their own output item\n * types that this adapter does not map, so the turn would otherwise end\n * silently with the call dropped. Hosted (server-run) tool items pass.\n */\n private assertServerRunItem(item: { type: string }): void {\n if (!CLIENT_RUN_ITEM_TYPES.has(item.type)) {\n return;\n }\n\n throw new InvalidRequestError(\n `OpenAI returned a \"${item.type}\" item: that built-in tool is executed by the caller, ` +\n \"which the Responses adapter does not support. Use OpenAI's server-run built-in tools \" +\n \"(web_search, file_search, code_interpreter, image_generation, mcp without approval) \" +\n \"with ai.providerTool(), or a function tool from ai.tool().\",\n { context: { provider: this.provider, model: this.name, itemType: item.type } },\n );\n }\n\n /** One completed `function_call` item as a neutral tool call (`call_id` -> `id`). */\n private toToolCall(item: OpenAI.Responses.ResponseFunctionToolCall): ModelToolCallRequest {\n return {\n id: item.call_id,\n name: item.name,\n input: safeJsonParse<Record<string, unknown>>(item.arguments, {}),\n };\n }\n\n /**\n * Normalize `usage` (absent on some partials) into the neutral `Usage`.\n * `input_tokens_details.cached_tokens` -> `cachedTokens`,\n * `cache_write_tokens` -> `cacheWriteTokens`,\n * `output_tokens_details.reasoning_tokens` -> `reasoningTokens`. The\n * detail blocks are typed non-optional but read defensively.\n */\n private extractUsage(raw: OpenAI.Responses.ResponseUsage | undefined): Usage {\n if (!raw) {\n return { input: 0, output: 0, total: 0 };\n }\n\n return buildUsage(\n {\n input: raw.input_tokens,\n output: raw.output_tokens,\n total: raw.total_tokens,\n cachedTokens: raw.input_tokens_details?.cached_tokens,\n cacheWriteTokens: raw.input_tokens_details?.cache_write_tokens,\n reasoningTokens: raw.output_tokens_details?.reasoning_tokens,\n },\n this.pricing,\n );\n }\n\n /** Wrap (idempotently), log and return a failure for the caller to throw. */\n private logAndWrap(thrown: unknown): ReturnType<typeof wrapOpenAIError> {\n const wrapped = wrapOpenAIError(thrown);\n\n this.logger.error(LOG_MODULE, \"error\", wrapped.message, {\n code: wrapped.code,\n context: wrapped.context,\n });\n\n return wrapped;\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"responses-model.mjs","names":[],"sources":["../../../../../../../packages/ai-openai/src/responses-model.ts"],"sourcesContent":["import {\n InvalidRequestError,\n ProviderError,\n resolveStreamChunks,\n safeJsonParse,\n selectProviderTools,\n type Message,\n type ModelCallOptions,\n type ModelCapabilities,\n type ModelContract,\n type ModelPricing,\n type ModelResponse,\n type ModelStreamChunk,\n type ModelToolCallRequest,\n type Usage,\n} from \"@warlock.js/ai\";\nimport { log, type Logger } from \"@warlock.js/logger\";\nimport type OpenAI from \"openai\";\nimport { hostedOutput } from \"./utils/hosted-output\";\nimport { mergeProviderBody } from \"./provider-options\";\nimport type { OpenAIModelConfig } from \"./config.type\";\nimport { inferReasoningCapability } from \"./known-reasoning-models\";\nimport { inferVisionCapability } from \"./known-vision-models\";\nimport {\n attachReasoningReplay,\n buildPromptCacheParams,\n buildUsage,\n inferStructuredOutput,\n mapResponsesFinishReason,\n pickTurnReasoning,\n planStructuredOutput,\n toOpenAIReasoningEffort,\n toResponseFailure,\n toResponsesInput,\n toResponsesToolChoice,\n toResponsesTools,\n wrapOpenAIError,\n} from \"./utils\";\n\nconst LOG_MODULE = \"ai.openai\";\n\n/**\n * Responses output item types of built-in tools the CALLER must execute\n * (installed SDK `ResponseOutputItem` union). The adapter maps only\n * `function_call` to a neutral tool call, so these fail closed.\n */\nconst CLIENT_RUN_ITEM_TYPES: ReadonlySet<string> = new Set([\n \"computer_call\",\n \"custom_tool_call\",\n \"local_shell_call\",\n \"shell_call\",\n \"apply_patch_call\",\n \"mcp_approval_request\",\n]);\n\n/**\n * OpenAI Responses API implementation of `ModelContract` (`api: \"responses\"`).\n *\n * **Role.** A sibling of `OpenAIModel` that speaks `POST /v1/responses`\n * instead of Chat Completions. Its point is letting a tool-using agent run a\n * reasoning model with reasoning ON: the Chat path has to force\n * `reasoning_effort: \"none\"` whenever tools are attached, Responses does not.\n *\n * **Why a separate class.** Only `OpenAISDK.model()` constructs it, and only\n * for the direct `\"openai\"` provider. The wrapper packages (deepseek, groq,\n * xai, mistral) build `OpenAIModel` and cannot reach this class, so an\n * endpoint that does not implement `/v1/responses` is never sent there. The\n * constructor re-checks the provider label as a second lock.\n *\n * **Stateless.** Every request sends `store: false`; nothing is kept on the\n * server and no `previous_response_id` is used. Full history is re-sent each\n * call, exactly like the Chat path.\n *\n * **Reasoning replay.** Every request asks for\n * `include: [\"reasoning.encrypted_content\"]`. The `reasoning` items of a\n * tool-calling turn are stored on the FIRST tool call as\n * `providerMetadata.openaiResponses = { model, reasoningItems }` and sent back\n * before that turn's `function_call` items on the next request (only when the\n * stored model equals the current one). Text-only turns are not replayed.\n *\n * **Streaming.** `stream()` uses `responses.create({ stream: true })` and yields\n * the same chunk sequence as the Chat path: text `delta`s, then the\n * `tool-call`s, then one `done`. With `streamChunks` it also yields\n * `reasoning` (reasoning summary / reasoning text deltas) and `tool-args`\n * (`response.function_call_arguments.delta`).\n *\n * **Provider-defined tools.** `ai.providerTool({ provider: \"openai\" })`\n * definitions are appended to `tools` verbatim. Hosted tools run inside the\n * response; their output items (`web_search_call`, …) are retained in\n * `providerMetadata.openai.output` for inspection and are not replayed\n * (with `store: false` the next turn only needs the answer text), and a\n * reasoning item that led into one is not replayed either (see\n * `pickTurnReasoning`).\n */\nexport class OpenAIResponsesModel implements ModelContract {\n public readonly name: string;\n public readonly provider: string;\n public readonly capabilities: ModelCapabilities;\n public readonly pricing?: ModelPricing;\n\n private readonly client: OpenAI;\n private readonly config: OpenAIModelConfig;\n private readonly logger: Logger = log;\n\n public constructor(client: OpenAI, config: OpenAIModelConfig, provider: string = \"openai\") {\n if (provider !== \"openai\") {\n throw new InvalidRequestError(\n `api: \"responses\" is supported only on the direct \"openai\" provider; got provider \"${provider}\".`,\n { context: { provider, model: config.name, api: \"responses\" } },\n );\n }\n\n this.client = client;\n this.config = config;\n this.name = config.name;\n this.provider = provider;\n this.pricing = config.pricing;\n this.capabilities = {\n structuredOutput: config.structuredOutput ?? inferStructuredOutput(config.responseFormat),\n vision: config.vision ?? inferVisionCapability(config.name),\n reasoning: config.reasoning ?? inferReasoningCapability(config.name),\n // Read-side cache accounting (`cachedTokens`, `cacheWriteTokens`) works\n // and `prompt_cache_key` is forwarded. Explicit write breakpoints are not\n // mapped onto Responses yet, so a `cacheControl.breakpoints` hint is\n // ignored rather than rejected.\n promptCaching: true,\n // PDF and audio input are off under Responses in this adapter: the\n // `input_file` / `input_audio` field-level mapping is unverified. The\n // `pdf` / `audio` config flags are deliberately NOT honoured here.\n pdf: false,\n audio: false,\n };\n }\n\n /**\n * Single-shot completion against `client.responses.create`. Sends the full\n * history as Responses `input` items, walks `response.output`, and reshapes\n * it into a vendor-neutral `ModelResponse`. Per-call `options` override the\n * instance defaults for this call only.\n */\n public async complete(messages: Message[], options?: ModelCallOptions): Promise<ModelResponse> {\n this.logger.debug(LOG_MODULE, \"request\", \"Starting call to responses.create\", {\n model: this.name,\n messageCount: messages.length,\n streaming: false,\n toolCount: options?.tools?.length ?? 0,\n });\n\n let response: OpenAI.Responses.Response;\n\n try {\n response = await this.client.responses.create(\n this.buildRequest(messages, options),\n options?.signal ? { signal: options.signal } : undefined,\n );\n } catch (thrown) {\n throw this.logAndWrap(thrown);\n }\n\n if (response.status === \"failed\") {\n throw this.logAndWrap(toResponseFailure(response, this.name));\n }\n\n const output = Array.isArray(response.output) ? response.output : [];\n const { content, toolCalls } = this.readOutput(output);\n const finishReason = mapResponsesFinishReason({\n status: response.status,\n incomplete_details: response.incomplete_details,\n hasToolCalls: toolCalls !== undefined,\n });\n const usage = this.extractUsage(response.usage);\n const providerMetadata = hostedOutput(usage, output, this.pricing);\n\n this.logger.debug(LOG_MODULE, \"response\", \"call to responses.create succeeded\", {\n finishReason,\n usage,\n });\n\n return {\n content,\n finishReason,\n usage,\n ...(providerMetadata ? { providerMetadata } : {}),\n ...(toolCalls ? { toolCalls } : {}),\n };\n }\n\n /** Count native Responses input tokens using the same converted input and tools. */\n public async countTokens(messages: Message[], options?: ModelCallOptions): Promise<number> {\n const request = this.buildRequest(messages, options);\n const body: OpenAI.Responses.InputTokenCountParams = {\n model: request.model,\n input: request.input,\n instructions: request.instructions,\n tools: request.tools,\n tool_choice: request.tool_choice,\n reasoning: request.reasoning,\n text: request.text,\n parallel_tool_calls: request.parallel_tool_calls,\n truncation: request.truncation ?? undefined,\n };\n try {\n const response = await this.client.responses.inputTokens.count(\n body,\n options?.signal ? { signal: options.signal } : undefined,\n );\n return response.input_tokens;\n } catch (thrown) {\n throw this.logAndWrap(thrown);\n }\n }\n\n /**\n * Incremental streaming completion via `responses.create({ stream: true })`\n * (the SDK's `Stream<ResponseStreamEvent>`, `responses.d.ts:55`). Yields the\n * same neutral `ModelStreamChunk` sequence as the Chat path:\n *\n * - `response.output_text.delta` / `response.refusal.delta` -> `delta`.\n * - `response.output_item.done` with a completed `function_call` item ->\n * buffered; `reasoning` items are captured for replay. The `done` copy is\n * used because the `added` copy may be incomplete (`responses.d.ts:5711`).\n * - After the loop: the buffered `tool-call`s (the first carries the\n * replay metadata), then a single `done` with the finish reason and usage\n * from `response.completed` / `response.incomplete`.\n * - `response.failed` and `error` events throw a typed `AIError`; a stream\n * that ends with no terminal event throws `ProviderError`.\n * - Abort goes through the request `signal`; the SDK rejects the read and\n * the error is wrapped like any other.\n */\n public async *stream(\n messages: Message[],\n options?: ModelCallOptions,\n ): AsyncIterable<ModelStreamChunk> {\n this.logger.debug(LOG_MODULE, \"request\", \"Starting streaming call to responses.create\", {\n model: this.name,\n messageCount: messages.length,\n streaming: true,\n toolCount: options?.tools?.length ?? 0,\n });\n\n let events: AsyncIterable<OpenAI.Responses.ResponseStreamEvent>;\n\n try {\n events = await this.client.responses.create(\n { ...this.buildRequest(messages, options), stream: true },\n options?.signal ? { signal: options.signal } : undefined,\n );\n } catch (thrown) {\n throw this.logAndWrap(thrown);\n }\n\n const toolCalls: ModelToolCallRequest[] = [];\n const outputItems: OpenAI.Responses.ResponseOutputItem[] = [];\n const want = resolveStreamChunks(options);\n // `function_call_arguments.delta` names its item by `item_id` only; the\n // `call_id` and `name` come from that item's `output_item.added`.\n const streamingCalls = new Map<string, { callId: string; name: string }>();\n let terminal: OpenAI.Responses.Response | undefined;\n\n try {\n for await (const event of events) {\n if (\n event.type === \"response.output_text.delta\" ||\n event.type === \"response.refusal.delta\"\n ) {\n if (event.delta) {\n yield { type: \"delta\", content: event.delta };\n }\n\n continue;\n }\n\n if (\n event.type === \"response.reasoning_summary_text.delta\" ||\n event.type === \"response.reasoning_text.delta\"\n ) {\n if (want.reasoning && event.delta) {\n yield { type: \"reasoning\", delta: event.delta };\n }\n\n continue;\n }\n\n if (event.type === \"response.output_item.added\") {\n const { item } = event;\n\n if (item.type === \"function_call\" && item.id) {\n streamingCalls.set(item.id, { callId: item.call_id, name: item.name });\n }\n\n continue;\n }\n\n if (event.type === \"response.function_call_arguments.delta\") {\n const call = streamingCalls.get(event.item_id);\n\n if (want.toolArgs && call && event.delta) {\n yield { type: \"tool-args\", callId: call.callId, name: call.name, delta: event.delta };\n }\n\n continue;\n }\n\n if (event.type === \"response.output_item.done\") {\n const { item } = event;\n\n this.assertServerRunItem(item);\n outputItems.push(item);\n\n if (item.type === \"function_call\" && item.status !== \"incomplete\") {\n toolCalls.push(this.toToolCall(item));\n }\n\n continue;\n }\n\n if (event.type === \"response.completed\" || event.type === \"response.incomplete\") {\n terminal = event.response;\n continue;\n }\n\n if (event.type === \"response.failed\") {\n throw toResponseFailure(event.response, this.name);\n }\n\n if (event.type === \"error\") {\n throw wrapOpenAIError({ code: event.code, message: event.message, param: event.param });\n }\n }\n\n if (terminal === undefined) {\n throw new ProviderError(\"OpenAI Responses stream ended before response.completed.\", {\n context: { provider: this.provider, model: this.name },\n });\n }\n } catch (thrown) {\n throw this.logAndWrap(thrown);\n }\n\n for (const call of attachReasoningReplay(\n toolCalls,\n this.name,\n pickTurnReasoning(outputItems),\n )) {\n yield {\n type: \"tool-call\",\n id: call.id,\n name: call.name,\n input: call.input,\n ...(call.providerMetadata ? { providerMetadata: call.providerMetadata } : {}),\n };\n }\n\n const finishReason = mapResponsesFinishReason({\n status: terminal.status,\n incomplete_details: terminal.incomplete_details,\n hasToolCalls: toolCalls.length > 0,\n });\n const usage = this.extractUsage(terminal.usage);\n // Terminal output is authoritative; fall back to item.done events when absent.\n // Never add both snapshots, which would double-count hosted calls.\n const output = terminal.output?.length ? terminal.output : outputItems;\n const providerMetadata = hostedOutput(usage, output, this.pricing);\n\n this.logger.debug(LOG_MODULE, \"response\", \"Streaming call to responses.create succeeded\", {\n finishReason,\n usage,\n });\n\n yield { type: \"done\", finishReason, usage, ...(providerMetadata ? { providerMetadata } : {}) };\n }\n\n /**\n * Assemble the `responses.create` body.\n *\n * - System prompt -> `instructions`; history -> `input` items.\n * - `store: false` always (the API default is `true` when omitted), plus\n * `include: [\"reasoning.encrypted_content\"]` for stateless reasoning replay.\n * - `max_output_tokens` caps reasoning and visible output together.\n * - `temperature` only for non-reasoning models (same rule as Chat).\n * - `tools` flat with `strict: false`; `tool_choice` only with tools.\n * - `reasoning.effort` only when the caller gave one, through the same\n * clamp table as Chat. With no explicit effort the provider default\n * applies EVEN with tools attached - that is the point of this API.\n */\n private buildRequest(\n messages: Message[],\n options: ModelCallOptions | undefined,\n ): OpenAI.Responses.ResponseCreateParamsNonStreaming {\n const { instructions, input } = toResponsesInput(messages, this.name);\n const tools = this.buildTools(options);\n const toolChoice = tools ? toResponsesToolChoice(options?.toolChoice) : undefined;\n const maxTokens = options?.maxTokens ?? this.config.maxTokens;\n const temperature = options?.temperature ?? this.config.temperature;\n\n return mergeProviderBody<OpenAI.Responses.ResponseCreateParamsNonStreaming>(\n {\n model: this.name,\n ...(instructions !== undefined ? { instructions } : {}),\n input,\n store: false,\n // Stateless replay: ask for the encrypted reasoning blobs so they can be\n // sent back on the next tool turn (decision 3).\n include: [\n \"reasoning.encrypted_content\",\n ...(tools?.some(\n (tool) =>\n tool.type === \"web_search\" ||\n tool.type === \"web_search_2025_08_26\" ||\n tool.type === \"web_search_preview\" ||\n tool.type === \"web_search_preview_2025_03_11\",\n )\n ? [\"web_search_call.action.sources\" as const]\n : []),\n ],\n ...(maxTokens !== undefined ? { max_output_tokens: maxTokens } : {}),\n ...(!this.capabilities.reasoning && temperature !== undefined ? { temperature } : {}),\n ...(tools ? { tools } : {}),\n ...(toolChoice !== undefined ? { tool_choice: toolChoice } : {}),\n ...this.buildTextFormat(options?.responseSchema),\n ...this.buildReasoningParams(options?.reasoning),\n ...buildPromptCacheParams(this.provider, this.config),\n },\n options,\n );\n }\n\n /**\n * The request `tools`: function tools (flat, `strict: false`), then the\n * `ai.providerTool({ provider: \"openai\" })` definitions verbatim — the\n * Responses built-in tools (`web_search`, `file_search`,\n * `code_interpreter`, `image_generation`, `mcp`, …). Other providers'\n * definitions are skipped with a warning. `undefined` when there are none.\n */\n private buildTools(options: ModelCallOptions | undefined): OpenAI.Responses.Tool[] | undefined {\n const { tools, definitions } = selectProviderTools(options, {\n accepts: [this.provider],\n module: LOG_MODULE,\n model: this.name,\n logger: this.logger,\n });\n const mapped: OpenAI.Responses.Tool[] = [\n ...(toResponsesTools(tools) ?? []),\n ...(definitions as unknown as OpenAI.Responses.Tool[]),\n ];\n\n return mapped.length > 0 ? mapped : undefined;\n }\n\n /**\n * Structured output on Responses lives in `text.format`, and `name` /\n * `schema` / `strict` sit directly on the format object (Chat nests them\n * under `json_schema`). The strict-or-downgrade decision is the shared\n * `planStructuredOutput`.\n */\n private buildTextFormat(responseSchema: Record<string, unknown> | undefined): {\n text?: OpenAI.Responses.ResponseTextConfig;\n } {\n const plan = planStructuredOutput(responseSchema, this.config.responseFormat);\n\n if (plan.mode === \"json_schema\") {\n return {\n text: {\n format: { type: \"json_schema\", name: \"response\", schema: plan.schema, strict: true },\n },\n };\n }\n\n if (plan.mode === \"json_object\") {\n return { text: { format: { type: \"json_object\" } } };\n }\n\n return {};\n }\n\n /** `reasoning.effort` for reasoning-capable models, only when the caller set one. */\n private buildReasoningParams(reasoning: ModelCallOptions[\"reasoning\"]): {\n reasoning?: { effort: ReturnType<typeof toOpenAIReasoningEffort> };\n } {\n if (!this.capabilities.reasoning || !reasoning?.effort) {\n return {};\n }\n\n return { reasoning: { effort: toOpenAIReasoningEffort(reasoning.effort) } };\n }\n\n /**\n * Walk `response.output`: `message` items contribute their `output_text`\n * text and any `refusal` text (surfaced as ordinary content, decision 5);\n * each completed `function_call` item becomes one tool call with\n * `id = call_id`. `reasoning` items carry no visible content and are\n * not content. A `function_call` the API marked `incomplete` (truncated\n * arguments) is skipped rather than dispatched with empty input.\n * `reasoning` items are captured and attached to the first tool call for\n * replay (see `attachReasoningReplay`).\n */\n private readOutput(output: OpenAI.Responses.ResponseOutputItem[]): {\n content: string;\n toolCalls: ModelToolCallRequest[] | undefined;\n } {\n let content = \"\";\n const toolCalls: ModelToolCallRequest[] = [];\n\n for (const item of output) {\n this.assertServerRunItem(item);\n\n if (item.type === \"message\") {\n for (const part of item.content) {\n content += part.type === \"refusal\" ? part.refusal : part.text;\n }\n\n continue;\n }\n\n if (item.type === \"function_call\" && item.status !== \"incomplete\") {\n toolCalls.push(this.toToolCall(item));\n }\n }\n\n return {\n content,\n toolCalls:\n toolCalls.length > 0\n ? attachReasoningReplay(toolCalls, this.name, pickTurnReasoning(output))\n : undefined,\n };\n }\n\n /**\n * Fail closed on a built-in tool call that YOUR code would have to run\n * (`shell_call`, `apply_patch_call`, `computer_call`, …, from a\n * provider-defined client tool). These items have their own output item\n * types that this adapter does not map, so the turn would otherwise end\n * silently with the call dropped. Hosted (server-run) tool items pass.\n */\n private assertServerRunItem(item: { type: string }): void {\n if (!CLIENT_RUN_ITEM_TYPES.has(item.type)) {\n return;\n }\n\n throw new InvalidRequestError(\n `OpenAI returned a \"${item.type}\" item: that built-in tool is executed by the caller, ` +\n \"which the Responses adapter does not support. Use OpenAI's server-run built-in tools \" +\n \"(web_search, file_search, code_interpreter, image_generation, mcp without approval) \" +\n \"with ai.providerTool(), or a function tool from ai.tool().\",\n { context: { provider: this.provider, model: this.name, itemType: item.type } },\n );\n }\n\n /** One completed `function_call` item as a neutral tool call (`call_id` -> `id`). */\n private toToolCall(item: OpenAI.Responses.ResponseFunctionToolCall): ModelToolCallRequest {\n return {\n id: item.call_id,\n name: item.name,\n input: safeJsonParse<Record<string, unknown>>(item.arguments, {}),\n };\n }\n\n /**\n * Normalize `usage` (absent on some partials) into the neutral `Usage`.\n * `input_tokens_details.cached_tokens` -> `cachedTokens`,\n * `cache_write_tokens` -> `cacheWriteTokens`,\n * `output_tokens_details.reasoning_tokens` -> `reasoningTokens`. The\n * detail blocks are typed non-optional but read defensively.\n */\n private extractUsage(raw: OpenAI.Responses.ResponseUsage | undefined): Usage {\n if (!raw) {\n return { input: 0, output: 0, total: 0 };\n }\n\n return buildUsage(\n {\n input: raw.input_tokens,\n output: raw.output_tokens,\n total: raw.total_tokens,\n cachedTokens: raw.input_tokens_details?.cached_tokens,\n cacheWriteTokens: raw.input_tokens_details?.cache_write_tokens,\n reasoningTokens: raw.output_tokens_details?.reasoning_tokens,\n },\n this.pricing,\n );\n }\n\n /** Wrap (idempotently), log and return a failure for the caller to throw. */\n private logAndWrap(thrown: unknown): ReturnType<typeof wrapOpenAIError> {\n const wrapped = wrapOpenAIError(thrown);\n\n this.logger.error(LOG_MODULE, \"error\", wrapped.message, {\n code: wrapped.code,\n context: wrapped.context,\n });\n\n return wrapped;\n }\n}\n"],"mappings":"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"}
package/esm/sdk.d.mts CHANGED
@@ -1,6 +1,5 @@
1
1
  import { OpenAIEmbedderConfig, OpenAIImageConfig, OpenAIModelConfig, OpenAISDKConfig, OpenAISpeechConfig, OpenAITranscriptionConfig } from "./config.type.mjs";
2
2
  import { EmbedderContract, ImageModelContract, ModelContract, SDKAdapterContract, SpeechModelContract, TranscriptionModelContract } from "@warlock.js/ai";
3
-
4
3
  //#region ../../packages/ai-openai/src/sdk.d.ts
5
4
  /**
6
5
  * OpenAI-backed implementation of `SDKAdapterContract`.
@@ -36,7 +35,7 @@ import { EmbedderContract, ImageModelContract, ModelContract, SDKAdapterContract
36
35
  * const ai = { agent, tool, systemPrompt, persona, instruction, openai: new OpenAISDK({ apiKey }) };
37
36
  * const myAgent = ai.agent({ model: ai.openai.model({ name: "gpt-4o-mini" }) });
38
37
  */
39
- declare class OpenAISDK implements SDKAdapterContract {
38
+ export declare class OpenAISDK implements SDKAdapterContract {
40
39
  private readonly client;
41
40
  private readonly provider;
42
41
  private readonly pricing?;
@@ -125,5 +124,4 @@ declare class OpenAISDK implements SDKAdapterContract {
125
124
  transcribe(config: OpenAITranscriptionConfig): TranscriptionModelContract;
126
125
  }
127
126
  //#endregion
128
- export { OpenAISDK };
129
127
  //# sourceMappingURL=sdk.d.mts.map
package/esm/sdk.d.mts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"sdk.d.mts","names":[],"sources":["../../../../../../../packages/ai-openai/src/sdk.ts"],"mappings":";;;;;;AA6DA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cAAa,SAAA,YAAqB,kBAAA;EAAA,iBACf,MAAA;EAAA,iBACA,QAAA;EAAA,iBACA,OAAA;cAEE,MAAA,EAAQ,eAAA;EAyGpB;;;;;;;;;;;;;AAkCyE;;;;;;;EA3GzE,KAAA,CAAM,MAAA,EAAQ,iBAAA,GAAoB,aAAA;;;;;;;;EAsC5B,KAAA,CAAM,IAAA,UAAc,MAAA,YAAkB,OAAA;;;;;;;;;;;EAc5C,QAAA,CAAS,MAAA,EAAQ,oBAAA,GAAuB,gBAAA;;;;;;;;;;;;;;;;;;EAqBxC,KAAA,CAAM,MAAA,EAAQ,iBAAA,GAAoB,kBAAA;;;;;;;;;;;;EAmBlC,MAAA,CAAO,MAAA,EAAQ,kBAAA,GAAqB,mBAAA;;;;;;;;;;;;EAepC,UAAA,CAAW,MAAA,EAAQ,yBAAA,GAA4B,0BAAA;AAAA"}
1
+ {"version":3,"file":"sdk.d.mts","names":[],"sources":["../../../../../../../packages/ai-openai/src/sdk.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;qBA6Da,qBAAqB;mBACf;mBACA;mBACA;EAEjB,YAAmB,QAAQ;;;;;;;;;;;;;;;;;;;;;EAgCpB,MAAM,QAAQ,oBAAoB;;;;;;;;EAsC5B,MAAM,cAAc,kBAAkB;;;;;;;;;;;EAc5C,SAAS,QAAQ,uBAAuB;;;;;;;;;;;;;;;;;;EAqBxC,MAAM,QAAQ,oBAAoB;;;;;;;;;;;;EAmBlC,OAAO,QAAQ,qBAAqB;;;;;;;;;;;;EAepC,WAAW,QAAQ,4BAA4B"}
package/esm/speech.d.mts CHANGED
@@ -1,7 +1,6 @@
1
1
  import { OpenAISpeechConfig } from "./config.type.mjs";
2
2
  import OpenAI from "openai";
3
3
  import { SpeechGenerationResponse, SpeechModelContract, SpeechModelPricing, SpeechOptions } from "@warlock.js/ai";
4
-
5
4
  //#region ../../packages/ai-openai/src/speech.d.ts
6
5
  /**
7
6
  * OpenAI-backed implementation of `SpeechModelContract` (text-to-speech)
@@ -18,7 +17,7 @@ import { SpeechGenerationResponse, SpeechModelContract, SpeechModelPricing, Spee
18
17
  * const tts = new OpenAISpeechModel(client, { name: "tts-1", voice: "alloy" }, "openai");
19
18
  * const { audio } = await tts.generate("Welcome aboard.");
20
19
  */
21
- declare class OpenAISpeechModel implements SpeechModelContract {
20
+ export declare class OpenAISpeechModel implements SpeechModelContract {
22
21
  readonly name: string;
23
22
  readonly provider: string;
24
23
  readonly pricing?: SpeechModelPricing;
@@ -29,5 +28,4 @@ declare class OpenAISpeechModel implements SpeechModelContract {
29
28
  generate(text: string, options?: SpeechOptions): Promise<SpeechGenerationResponse>;
30
29
  }
31
30
  //#endregion
32
- export { OpenAISpeechModel };
33
31
  //# sourceMappingURL=speech.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"speech.d.mts","names":[],"sources":["../../../../../../../packages/ai-openai/src/speech.ts"],"mappings":";;;;;;;AA8CA;;;;;;;;;;;;;cAAa,iBAAA,YAA6B,mBAAA;EAAA,SACxB,IAAA;EAAA,SACA,QAAA;EAAA,SACA,OAAA,GAAU,kBAAA;EAAA,iBAET,MAAA;EAAA,iBACA,YAAA;EAAA,iBACA,MAAA;cAEE,MAAA,EAAQ,MAAA,EAAQ,MAAA,EAAQ,kBAAA,EAAoB,QAAA;EAQlD,QAAA,CAAS,IAAA,UAAc,OAAA,GAAU,aAAA,GAAgB,OAAA,CAAQ,wBAAA;AAAA"}
1
+ {"version":3,"file":"speech.d.mts","names":[],"sources":["../../../../../../../packages/ai-openai/src/speech.ts"],"mappings":";;;;;;;;;;;;;;;;;;;qBA8Ca,6BAA6B;WACxB;WACA;WACA,UAAU;mBAET;mBACA;mBACA;EAEjB,YAAmB,QAAQ,QAAQ,QAAQ,oBAAoB;EAQlD,SAAS,cAAc,UAAU,gBAAgB,QAAQ"}
@@ -1,7 +1,6 @@
1
1
  import { OpenAITranscriptionConfig } from "./config.type.mjs";
2
2
  import OpenAI from "openai";
3
3
  import { AudioInput, TranscribeOptions, TranscriptionModelContract, TranscriptionModelPricing, TranscriptionResponse } from "@warlock.js/ai";
4
-
5
4
  //#region ../../packages/ai-openai/src/transcription.d.ts
6
5
  /**
7
6
  * OpenAI-backed implementation of `TranscriptionModelContract`
@@ -22,7 +21,7 @@ import { AudioInput, TranscribeOptions, TranscriptionModelContract, Transcriptio
22
21
  * const stt = new OpenAITranscriptionModel(client, { name: "whisper-1" }, "openai");
23
22
  * const { text } = await stt.transcribe({ base64, mediaType: "audio/mpeg" });
24
23
  */
25
- declare class OpenAITranscriptionModel implements TranscriptionModelContract {
24
+ export declare class OpenAITranscriptionModel implements TranscriptionModelContract {
26
25
  readonly name: string;
27
26
  readonly provider: string;
28
27
  readonly pricing?: TranscriptionModelPricing;
@@ -32,5 +31,4 @@ declare class OpenAITranscriptionModel implements TranscriptionModelContract {
32
31
  transcribe(audio: AudioInput, options?: TranscribeOptions): Promise<TranscriptionResponse>;
33
32
  }
34
33
  //#endregion
35
- export { OpenAITranscriptionModel };
36
34
  //# sourceMappingURL=transcription.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"transcription.d.mts","names":[],"sources":["../../../../../../../packages/ai-openai/src/transcription.ts"],"mappings":";;;;;;;AAiDA;;;;;;;;;;;;;;;;;cAAa,wBAAA,YAAoC,0BAAA;EAAA,SAC/B,IAAA;EAAA,SACA,QAAA;EAAA,SACA,OAAA,GAAU,yBAAA;EAAA,iBAET,MAAA;EAAA,iBACA,MAAA;cAGf,MAAA,EAAQ,MAAA,EACR,MAAA,EAAQ,yBAAA,EACR,QAAA;EAQW,UAAA,CACX,KAAA,EAAO,UAAA,EACP,OAAA,GAAU,iBAAA,GACT,OAAA,CAAQ,qBAAA;AAAA"}
1
+ {"version":3,"file":"transcription.d.mts","names":[],"sources":["../../../../../../../packages/ai-openai/src/transcription.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;qBAiDa,oCAAoC;WAC/B;WACA;WACA,UAAU;mBAET;mBACA;EAEjB,YACE,QAAQ,QACR,QAAQ,2BACR;EAQW,WACX,OAAO,YACP,UAAU,oBACT,QAAQ"}
@@ -0,0 +1,22 @@
1
+ import { attachCost } from "@warlock.js/ai";
2
+
3
+ //#region ../../packages/ai-openai/src/utils/hosted-output.ts
4
+ /** Installed openai 7.23.0 ResponseFunctionWebSearch (action includes search/open_page/find).
5
+ * https://developers.openai.com/api/docs/guides/tools-web-search
6
+ * Count output items, never URLs, sources, answer text, or function calls.
7
+ * Fees and search content tokens are separate; preview pricing varies:
8
+ * https://developers.openai.com/api/docs/pricing#built-in-tools */
9
+ function hostedOutput(usage, output, pricing) {
10
+ const hosted = output.filter((item) => item.type !== "message" && item.type !== "reasoning" && item.type !== "function_call");
11
+ const searches = hosted.filter((item) => item.type === "web_search_call");
12
+ if (searches.length) usage.serverToolUse = {
13
+ ...usage.serverToolUse,
14
+ webSearchRequests: searches.length
15
+ };
16
+ attachCost(usage, pricing);
17
+ return hosted.length ? { openai: { output: hosted } } : void 0;
18
+ }
19
+
20
+ //#endregion
21
+ export { hostedOutput };
22
+ //# sourceMappingURL=hosted-output.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hosted-output.mjs","names":[],"sources":["../../../../../../../../packages/ai-openai/src/utils/hosted-output.ts"],"sourcesContent":["import { attachCost, type ModelPricing, type Usage } from \"@warlock.js/ai\";\nimport type OpenAI from \"openai\";\n\n/** Installed openai 7.23.0 ResponseFunctionWebSearch (action includes search/open_page/find).\n * https://developers.openai.com/api/docs/guides/tools-web-search\n * Count output items, never URLs, sources, answer text, or function calls.\n * Fees and search content tokens are separate; preview pricing varies:\n * https://developers.openai.com/api/docs/pricing#built-in-tools */\nexport function hostedOutput(\n usage: Usage,\n output: OpenAI.Responses.ResponseOutputItem[],\n pricing: ModelPricing | undefined,\n): Record<string, unknown> | undefined {\n const hosted = output.filter(\n (item) => item.type !== \"message\" && item.type !== \"reasoning\" && item.type !== \"function_call\",\n );\n const searches = hosted.filter((item) => item.type === \"web_search_call\");\n if (searches.length)\n usage.serverToolUse = { ...usage.serverToolUse, webSearchRequests: searches.length };\n attachCost(usage, pricing);\n return hosted.length ? { openai: { output: hosted } } : undefined;\n}\n"],"mappings":";;;;;;;;AAQA,SAAgB,aACd,OACA,QACA,SACqC;CACrC,MAAM,SAAS,OAAO,QACnB,SAAS,KAAK,SAAS,aAAa,KAAK,SAAS,eAAe,KAAK,SAAS,eAClF;CACA,MAAM,WAAW,OAAO,QAAQ,SAAS,KAAK,SAAS,iBAAiB;CACxE,IAAI,SAAS,QACX,MAAM,gBAAgB;EAAE,GAAG,MAAM;EAAe,mBAAmB,SAAS;CAAO;CACrF,WAAW,OAAO,OAAO;CACzB,OAAO,OAAO,SAAS,EAAE,QAAQ,EAAE,QAAQ,OAAO,EAAE,IAAI;AAC1D"}
@@ -11,4 +11,4 @@ import { toOpenAITools } from "./to-openai-tools.mjs";
11
11
  import { toResponsesInput } from "./to-responses-input.mjs";
12
12
  import { toResponsesToolChoice, toResponsesTools } from "./to-responses-tools.mjs";
13
13
 
14
- export { };
14
+ export { attachReasoningReplay, buildPromptCacheParams, buildUsage, inferStructuredOutput, mapFinishReason, mapResponsesFinishReason, pickTurnReasoning, planStructuredOutput, toOpenAIMessages, toOpenAIReasoningEffort, toOpenAITools, toResponseFailure, toResponsesInput, toResponsesToolChoice, toResponsesTools, wrapOpenAIError };
@@ -12,7 +12,7 @@
12
12
  * Groq and DeepSeek cache automatically and document no request hint, so
13
13
  * they receive nothing (checked 2026-10-17).
14
14
  */
15
- const PROMPT_CACHE_KEY_PROVIDERS = new Set([
15
+ const PROMPT_CACHE_KEY_PROVIDERS = /* @__PURE__ */ new Set([
16
16
  "openai",
17
17
  "xai",
18
18
  "mistral"