@cognipeer/agent-sdk 0.10.4 → 0.10.5

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.
package/dist/index.d.cts CHANGED
@@ -3874,6 +3874,37 @@ type OpenAIProviderConfig = {
3874
3874
  * where it matters most: a self-hosted model is named by whoever served it.
3875
3875
  */
3876
3876
  responsesApi?: "auto" | "never" | "always";
3877
+ /**
3878
+ * Models that go to the Responses API, named explicitly. Opt-in; unset by
3879
+ * default, and nothing here is inferred from a model name.
3880
+ *
3881
+ * A string matches the request's model (or `defaultModel`) exactly; a RegExp
3882
+ * is tested against it. A match routes the request to Responses whether or
3883
+ * not it carries `reasoning` — the case this exists for is a model that
3884
+ * reasons by default and rejects function tools on Chat Completions, where
3885
+ * sending no reasoning field does not help. `responsesApi: "never"` still
3886
+ * wins; under `"always"` the routing is unchanged.
3887
+ *
3888
+ * Requests for a matched model also get, on the Responses body only:
3889
+ * - Chat Completions spellings arriving through `extra`, which /responses
3890
+ * rejects with a 400, re-spelled with their values unchanged:
3891
+ * `reasoning_effort` → `reasoning.effort` (a request `reasoning` effort
3892
+ * still wins), `max_tokens` / `max_completion_tokens` →
3893
+ * `max_output_tokens`, `response_format` → `text.format`, and
3894
+ * `stream_options.include_usage` dropped.
3895
+ * - real SSE streaming, when `responsesStreaming` is also true.
3896
+ *
3897
+ * Honoured by `OpenAIProvider` itself only; `AzureProvider` and
3898
+ * `OpenAICompatibleProvider` ignore it.
3899
+ */
3900
+ responsesApiModels?: Array<string | RegExp>;
3901
+ /**
3902
+ * Stream Responses API output as it arrives (text and tool-call argument
3903
+ * deltas) for models in `responsesApiModels`. Default false, which keeps the
3904
+ * existing behaviour: one non-streaming Responses call, emitted as a single
3905
+ * chunk. Has no effect on any other request.
3906
+ */
3907
+ responsesStreaming?: boolean;
3877
3908
  };
3878
3909
  type AnthropicProviderConfig = {
3879
3910
  provider: "anthropic";
@@ -5509,7 +5540,11 @@ declare class OpenAIProvider extends BaseProvider {
5509
5540
  protected readonly defaultModel: string;
5510
5541
  protected readonly defaultHeaders: Record<string, string>;
5511
5542
  protected readonly responsesApi: "auto" | "never" | "always";
5543
+ private readonly responsesApiModels;
5544
+ private readonly responsesStreaming;
5512
5545
  constructor(config: OpenAIProviderConfig);
5546
+ /** True when the caller listed this request's model in `responsesApiModels`. */
5547
+ private isResponsesApiModel;
5513
5548
  /** True when this request should go to the Responses API rather than Chat
5514
5549
  * Completions — which exposes `reasoning.effort` and reasoning summaries that
5515
5550
  * Chat Completions silently drops.
@@ -5518,12 +5553,23 @@ declare class OpenAIProvider extends BaseProvider {
5518
5553
  * carries a reasoning config AND the model name looks like an o-series/gpt-5
5519
5554
  * model. `never`/`always` let a caller state the answer instead, because the
5520
5555
  * name check is a guess and the two APIs differ in far more than reasoning —
5521
- * see `responsesApi` in OpenAIProviderConfig. */
5556
+ * see `responsesApi` in OpenAIProviderConfig.
5557
+ *
5558
+ * A model listed in `responsesApiModels` is the caller stating the answer
5559
+ * for that model alone; `never` still overrides it. */
5522
5560
  protected useResponsesApi(request: ChatCompletionRequest): boolean;
5523
5561
  complete(request: ChatCompletionRequest): Promise<ChatCompletionResponse>;
5524
5562
  completeStream(request: ChatCompletionRequest): AsyncGenerator<ChatCompletionChunk, void, unknown>;
5525
5563
  protected buildRequestBody(request: ChatCompletionRequest, stream: boolean): Record<string, any>;
5526
5564
  protected completeResponses(request: ChatCompletionRequest): Promise<ChatCompletionResponse>;
5565
+ /** Real Responses SSE streaming, for models opted in through
5566
+ * `responsesApiModels` with `responsesStreaming` on. Emits the same chunk
5567
+ * shapes as the Chat Completions stream: text deltas as they arrive, a tool
5568
+ * call's id + name when it opens and its argument fragments under that id,
5569
+ * then one final chunk with usage, finish reason and any reasoning summary
5570
+ * (taken from the completed response, exactly as the non-streaming parse
5571
+ * reads it). */
5572
+ private streamResponses;
5527
5573
  protected buildResponsesBody(request: ChatCompletionRequest): Record<string, any>;
5528
5574
  /** Maps a unified message to a Responses API `input` item. */
5529
5575
  protected toResponsesInput(m: UnifiedMessage): Record<string, any>;
package/dist/index.d.ts CHANGED
@@ -3874,6 +3874,37 @@ type OpenAIProviderConfig = {
3874
3874
  * where it matters most: a self-hosted model is named by whoever served it.
3875
3875
  */
3876
3876
  responsesApi?: "auto" | "never" | "always";
3877
+ /**
3878
+ * Models that go to the Responses API, named explicitly. Opt-in; unset by
3879
+ * default, and nothing here is inferred from a model name.
3880
+ *
3881
+ * A string matches the request's model (or `defaultModel`) exactly; a RegExp
3882
+ * is tested against it. A match routes the request to Responses whether or
3883
+ * not it carries `reasoning` — the case this exists for is a model that
3884
+ * reasons by default and rejects function tools on Chat Completions, where
3885
+ * sending no reasoning field does not help. `responsesApi: "never"` still
3886
+ * wins; under `"always"` the routing is unchanged.
3887
+ *
3888
+ * Requests for a matched model also get, on the Responses body only:
3889
+ * - Chat Completions spellings arriving through `extra`, which /responses
3890
+ * rejects with a 400, re-spelled with their values unchanged:
3891
+ * `reasoning_effort` → `reasoning.effort` (a request `reasoning` effort
3892
+ * still wins), `max_tokens` / `max_completion_tokens` →
3893
+ * `max_output_tokens`, `response_format` → `text.format`, and
3894
+ * `stream_options.include_usage` dropped.
3895
+ * - real SSE streaming, when `responsesStreaming` is also true.
3896
+ *
3897
+ * Honoured by `OpenAIProvider` itself only; `AzureProvider` and
3898
+ * `OpenAICompatibleProvider` ignore it.
3899
+ */
3900
+ responsesApiModels?: Array<string | RegExp>;
3901
+ /**
3902
+ * Stream Responses API output as it arrives (text and tool-call argument
3903
+ * deltas) for models in `responsesApiModels`. Default false, which keeps the
3904
+ * existing behaviour: one non-streaming Responses call, emitted as a single
3905
+ * chunk. Has no effect on any other request.
3906
+ */
3907
+ responsesStreaming?: boolean;
3877
3908
  };
3878
3909
  type AnthropicProviderConfig = {
3879
3910
  provider: "anthropic";
@@ -5509,7 +5540,11 @@ declare class OpenAIProvider extends BaseProvider {
5509
5540
  protected readonly defaultModel: string;
5510
5541
  protected readonly defaultHeaders: Record<string, string>;
5511
5542
  protected readonly responsesApi: "auto" | "never" | "always";
5543
+ private readonly responsesApiModels;
5544
+ private readonly responsesStreaming;
5512
5545
  constructor(config: OpenAIProviderConfig);
5546
+ /** True when the caller listed this request's model in `responsesApiModels`. */
5547
+ private isResponsesApiModel;
5513
5548
  /** True when this request should go to the Responses API rather than Chat
5514
5549
  * Completions — which exposes `reasoning.effort` and reasoning summaries that
5515
5550
  * Chat Completions silently drops.
@@ -5518,12 +5553,23 @@ declare class OpenAIProvider extends BaseProvider {
5518
5553
  * carries a reasoning config AND the model name looks like an o-series/gpt-5
5519
5554
  * model. `never`/`always` let a caller state the answer instead, because the
5520
5555
  * name check is a guess and the two APIs differ in far more than reasoning —
5521
- * see `responsesApi` in OpenAIProviderConfig. */
5556
+ * see `responsesApi` in OpenAIProviderConfig.
5557
+ *
5558
+ * A model listed in `responsesApiModels` is the caller stating the answer
5559
+ * for that model alone; `never` still overrides it. */
5522
5560
  protected useResponsesApi(request: ChatCompletionRequest): boolean;
5523
5561
  complete(request: ChatCompletionRequest): Promise<ChatCompletionResponse>;
5524
5562
  completeStream(request: ChatCompletionRequest): AsyncGenerator<ChatCompletionChunk, void, unknown>;
5525
5563
  protected buildRequestBody(request: ChatCompletionRequest, stream: boolean): Record<string, any>;
5526
5564
  protected completeResponses(request: ChatCompletionRequest): Promise<ChatCompletionResponse>;
5565
+ /** Real Responses SSE streaming, for models opted in through
5566
+ * `responsesApiModels` with `responsesStreaming` on. Emits the same chunk
5567
+ * shapes as the Chat Completions stream: text deltas as they arrive, a tool
5568
+ * call's id + name when it opens and its argument fragments under that id,
5569
+ * then one final chunk with usage, finish reason and any reasoning summary
5570
+ * (taken from the completed response, exactly as the non-streaming parse
5571
+ * reads it). */
5572
+ private streamResponses;
5527
5573
  protected buildResponsesBody(request: ChatCompletionRequest): Record<string, any>;
5528
5574
  /** Maps a unified message to a Responses API `input` item. */
5529
5575
  protected toResponsesInput(m: UnifiedMessage): Record<string, any>;
package/dist/index.mjs CHANGED
@@ -14981,8 +14981,127 @@ function sanitizeBedrockDocName(name, fallback = "document") {
14981
14981
  return cleaned || fallback;
14982
14982
  }
14983
14983
 
14984
+ // src/providers/utils/jsonSchemaRefs.ts
14985
+ var DEFINITION_KEYS = ["definitions", "$defs"];
14986
+ function inlineJsonSchemaRefs(schema) {
14987
+ if (!isObject(schema) || !containsRef(schema)) return schema;
14988
+ const root = structuredClone(schema);
14989
+ const selfReaching = /* @__PURE__ */ new Map();
14990
+ const targetOf = (ref) => {
14991
+ const target = resolvePointer(root, ref);
14992
+ return isObject(target) ? target : void 0;
14993
+ };
14994
+ const reachesItself = (target) => {
14995
+ const known = selfReaching.get(target);
14996
+ if (known !== void 0) return known;
14997
+ const seen = /* @__PURE__ */ new Set();
14998
+ const walk = (node) => {
14999
+ if (Array.isArray(node)) return node.some(walk);
15000
+ if (!isObject(node)) return false;
15001
+ if (typeof node.$ref === "string") {
15002
+ const next = targetOf(node.$ref);
15003
+ if (next === target) return true;
15004
+ if (next && !seen.has(next)) {
15005
+ seen.add(next);
15006
+ if (walk(next)) return true;
15007
+ }
15008
+ }
15009
+ return Object.entries(node).some(([key, value]) => key !== "$ref" && walk(value));
15010
+ };
15011
+ const result2 = walk(target);
15012
+ selfReaching.set(target, result2);
15013
+ return result2;
15014
+ };
15015
+ const keptRefs = /* @__PURE__ */ new Set();
15016
+ const expand = (node) => {
15017
+ if (Array.isArray(node)) return node.map(expand);
15018
+ if (!isObject(node)) return node;
15019
+ if (typeof node.$ref === "string") {
15020
+ const target = targetOf(node.$ref);
15021
+ if (target && !reachesItself(target)) {
15022
+ const { $ref: _inlined, ...siblings } = node;
15023
+ return { ...expand(target), ...expandEntries(siblings) };
15024
+ }
15025
+ keptRefs.add(node.$ref);
15026
+ return expandEntries(node);
15027
+ }
15028
+ return expandEntries(node);
15029
+ };
15030
+ const expandEntries = (node) => {
15031
+ const out = {};
15032
+ for (const [key, value] of Object.entries(node)) out[key] = key === "$ref" ? value : expand(value);
15033
+ return out;
15034
+ };
15035
+ const { definitions: _definitions, $defs: _defs, ...rootBody } = root;
15036
+ const result = typeof root.$ref === "string" ? expand(rootBody) : expandEntries(rootBody);
15037
+ for (const key of DEFINITION_KEYS) delete result[key];
15038
+ for (const key of DEFINITION_KEYS) {
15039
+ const block = root[key];
15040
+ if (!isObject(block)) continue;
15041
+ const needed = neededEntries(key, block, keptRefs);
15042
+ if (needed.length === 0) continue;
15043
+ const kept = {};
15044
+ for (const name of Object.keys(block)) if (needed.includes(name)) kept[name] = block[name];
15045
+ result[key] = kept;
15046
+ }
15047
+ return result;
15048
+ }
15049
+ function neededEntries(key, block, keptRefs) {
15050
+ const needed = [];
15051
+ const queue = [...keptRefs];
15052
+ while (queue.length > 0) {
15053
+ const ref = queue.shift();
15054
+ const segments = pointerSegments(ref);
15055
+ if (!segments || segments[0] !== key || segments.length < 2) continue;
15056
+ const name = segments[1];
15057
+ if (needed.includes(name) || !(name in block)) continue;
15058
+ needed.push(name);
15059
+ collectRefs(block[name], queue);
15060
+ }
15061
+ return needed;
15062
+ }
15063
+ function collectRefs(node, into) {
15064
+ if (Array.isArray(node)) {
15065
+ node.forEach((child) => collectRefs(child, into));
15066
+ return;
15067
+ }
15068
+ if (!isObject(node)) return;
15069
+ if (typeof node.$ref === "string") into.push(node.$ref);
15070
+ for (const [k, value] of Object.entries(node)) if (k !== "$ref") collectRefs(value, into);
15071
+ }
15072
+ function containsRef(node) {
15073
+ if (Array.isArray(node)) return node.some(containsRef);
15074
+ if (!isObject(node)) return false;
15075
+ if (typeof node.$ref === "string") return true;
15076
+ return Object.values(node).some(containsRef);
15077
+ }
15078
+ function pointerSegments(ref) {
15079
+ if (ref === "#") return [];
15080
+ if (!ref.startsWith("#/")) return null;
15081
+ return ref.slice(2).split("/").map((segment) => segment.replace(/~1/g, "/").replace(/~0/g, "~"));
15082
+ }
15083
+ function resolvePointer(document, ref) {
15084
+ const segments = pointerSegments(ref);
15085
+ if (!segments) return void 0;
15086
+ let current = document;
15087
+ for (const segment of segments) {
15088
+ if (Array.isArray(current)) {
15089
+ if (!/^\d+$/.test(segment)) return void 0;
15090
+ current = current[Number(segment)];
15091
+ } else if (isObject(current) && Object.prototype.hasOwnProperty.call(current, segment)) {
15092
+ current = current[segment];
15093
+ } else {
15094
+ return void 0;
15095
+ }
15096
+ }
15097
+ return current;
15098
+ }
15099
+ function isObject(value) {
15100
+ return typeof value === "object" && value !== null && !Array.isArray(value);
15101
+ }
15102
+
14984
15103
  // src/providers/openai.ts
14985
- var OpenAIProvider = class extends BaseProvider {
15104
+ var OpenAIProvider = class _OpenAIProvider extends BaseProvider {
14986
15105
  constructor(config) {
14987
15106
  super();
14988
15107
  this.providerName = "openai";
@@ -14994,8 +15113,21 @@ var OpenAIProvider = class extends BaseProvider {
14994
15113
  ...config.organization ? { "OpenAI-Organization": config.organization } : {}
14995
15114
  };
14996
15115
  this.responsesApi = config.responsesApi ?? "auto";
15116
+ const ownInstance = new.target === _OpenAIProvider;
15117
+ this.responsesApiModels = ownInstance ? [...config.responsesApiModels ?? []] : [];
15118
+ this.responsesStreaming = ownInstance && config.responsesStreaming === true;
14997
15119
  if (config.retry) this.retryPolicy = { ...this.retryPolicy, ...config.retry };
14998
15120
  }
15121
+ /** True when the caller listed this request's model in `responsesApiModels`. */
15122
+ isResponsesApiModel(request) {
15123
+ if (this.responsesApiModels.length === 0) return false;
15124
+ const model = request.model || this.defaultModel;
15125
+ return this.responsesApiModels.some((entry) => {
15126
+ if (typeof entry === "string") return entry === model;
15127
+ entry.lastIndex = 0;
15128
+ return entry.test(model);
15129
+ });
15130
+ }
14999
15131
  /** True when this request should go to the Responses API rather than Chat
15000
15132
  * Completions — which exposes `reasoning.effort` and reasoning summaries that
15001
15133
  * Chat Completions silently drops.
@@ -15004,10 +15136,14 @@ var OpenAIProvider = class extends BaseProvider {
15004
15136
  * carries a reasoning config AND the model name looks like an o-series/gpt-5
15005
15137
  * model. `never`/`always` let a caller state the answer instead, because the
15006
15138
  * name check is a guess and the two APIs differ in far more than reasoning —
15007
- * see `responsesApi` in OpenAIProviderConfig. */
15139
+ * see `responsesApi` in OpenAIProviderConfig.
15140
+ *
15141
+ * A model listed in `responsesApiModels` is the caller stating the answer
15142
+ * for that model alone; `never` still overrides it. */
15008
15143
  useResponsesApi(request) {
15009
15144
  if (this.responsesApi === "never") return false;
15010
15145
  if (this.responsesApi === "always") return true;
15146
+ if (this.isResponsesApiModel(request)) return true;
15011
15147
  if (!request.reasoning) return false;
15012
15148
  return isReasoningModel(request.model || this.defaultModel);
15013
15149
  }
@@ -15022,6 +15158,10 @@ var OpenAIProvider = class extends BaseProvider {
15022
15158
  }
15023
15159
  async *completeStream(request) {
15024
15160
  if (this.useResponsesApi(request)) {
15161
+ if (this.responsesStreaming && this.isResponsesApiModel(request)) {
15162
+ yield* this.streamResponses(request);
15163
+ return;
15164
+ }
15025
15165
  const response = await this.completeResponses(request);
15026
15166
  yield {
15027
15167
  id: response.id,
@@ -15137,10 +15277,101 @@ var OpenAIProvider = class extends BaseProvider {
15137
15277
  // ─── Responses API (reasoning models) ──────────────────────────────────────
15138
15278
  async completeResponses(request) {
15139
15279
  const body = this.buildResponsesBody(request);
15280
+ if (this.isResponsesApiModel(request)) adaptChatExtrasForResponses(body, request.extra);
15140
15281
  const res = await this.responsesFetch(body);
15141
15282
  const json = await res.json();
15142
15283
  return this.parseResponsesResponse(json, request.model || this.defaultModel);
15143
15284
  }
15285
+ /** Real Responses SSE streaming, for models opted in through
15286
+ * `responsesApiModels` with `responsesStreaming` on. Emits the same chunk
15287
+ * shapes as the Chat Completions stream: text deltas as they arrive, a tool
15288
+ * call's id + name when it opens and its argument fragments under that id,
15289
+ * then one final chunk with usage, finish reason and any reasoning summary
15290
+ * (taken from the completed response, exactly as the non-streaming parse
15291
+ * reads it). */
15292
+ async *streamResponses(request) {
15293
+ const modelId = request.model || this.defaultModel;
15294
+ const body = this.buildResponsesBody(request);
15295
+ adaptChatExtrasForResponses(body, request.extra);
15296
+ body.stream = true;
15297
+ const res = await this.responsesFetch(body);
15298
+ if (!res.body) throw new ProviderError("No response body for stream", this.providerName);
15299
+ let id = "";
15300
+ let model = modelId;
15301
+ const calls = /* @__PURE__ */ new Map();
15302
+ let final;
15303
+ for await (const event of parseSSEStream(res.body)) {
15304
+ if (event.data === "[DONE]") break;
15305
+ let data;
15306
+ try {
15307
+ data = JSON.parse(event.data);
15308
+ } catch {
15309
+ continue;
15310
+ }
15311
+ switch (data.type ?? event.event) {
15312
+ case "response.created":
15313
+ case "response.in_progress":
15314
+ id = data.response?.id ?? id;
15315
+ model = data.response?.model ?? model;
15316
+ break;
15317
+ case "response.output_text.delta":
15318
+ if (data.delta) yield { id, model, delta: { content: data.delta } };
15319
+ break;
15320
+ case "response.output_item.added": {
15321
+ const item = data.item;
15322
+ if (item?.type !== "function_call") break;
15323
+ const call = { callId: item.call_id ?? item.id ?? "", name: item.name ?? "", arguments: item.arguments ?? "" };
15324
+ calls.set(item.id ?? call.callId, call);
15325
+ yield { id, model, delta: { toolCalls: [{ id: call.callId, name: call.name, arguments: call.arguments }] } };
15326
+ break;
15327
+ }
15328
+ case "response.function_call_arguments.delta": {
15329
+ const call = calls.get(data.item_id);
15330
+ if (!call || !data.delta) break;
15331
+ call.arguments += data.delta;
15332
+ yield { id, model, delta: { toolCalls: [{ id: call.callId, arguments: data.delta }] } };
15333
+ break;
15334
+ }
15335
+ case "response.output_item.done": {
15336
+ const item = data.item;
15337
+ if (item?.type !== "function_call") break;
15338
+ const full = typeof item.arguments === "string" ? item.arguments : "";
15339
+ const call = calls.get(item.id ?? item.call_id);
15340
+ if (!call) {
15341
+ const added = { callId: item.call_id ?? item.id ?? "", name: item.name ?? "", arguments: full };
15342
+ calls.set(item.id ?? added.callId, added);
15343
+ yield { id, model, delta: { toolCalls: [{ id: added.callId, name: added.name, arguments: full }] } };
15344
+ } else if (full.length > call.arguments.length && full.startsWith(call.arguments)) {
15345
+ const rest = full.slice(call.arguments.length);
15346
+ call.arguments = full;
15347
+ yield { id, model, delta: { toolCalls: [{ id: call.callId, arguments: rest }] } };
15348
+ }
15349
+ break;
15350
+ }
15351
+ case "response.completed":
15352
+ case "response.incomplete":
15353
+ final = this.parseResponsesResponse(data.response ?? {}, modelId);
15354
+ break;
15355
+ case "response.failed":
15356
+ case "error": {
15357
+ const err = data.response?.error ?? data.error ?? data;
15358
+ throw new ProviderError(
15359
+ `OpenAI Responses API stream error: ${err?.message ?? "unknown error"}`,
15360
+ this.providerName,
15361
+ void 0,
15362
+ data
15363
+ );
15364
+ }
15365
+ }
15366
+ }
15367
+ yield {
15368
+ id: final?.id || id,
15369
+ model: final?.model || model,
15370
+ delta: final?.reasoning ? { reasoning: final.reasoning } : {},
15371
+ usage: final?.usage,
15372
+ finishReason: final?.finishReason ?? (calls.size > 0 ? "tool_calls" : "stop")
15373
+ };
15374
+ }
15144
15375
  buildResponsesBody(request) {
15145
15376
  const body = {
15146
15377
  model: request.model || this.defaultModel,
@@ -15170,7 +15401,7 @@ var OpenAIProvider = class extends BaseProvider {
15170
15401
  format: {
15171
15402
  type: "json_schema",
15172
15403
  name: request.responseFormat.name ?? "response",
15173
- schema: request.responseFormat.schema,
15404
+ schema: inlineJsonSchemaRefs(request.responseFormat.schema),
15174
15405
  strict: true
15175
15406
  }
15176
15407
  };
@@ -15425,6 +15656,48 @@ function isReasoningModel(model) {
15425
15656
  const m = model.toLowerCase();
15426
15657
  return /(^|[/_-])o[0-9]/.test(m) || m.startsWith("o1") || m.startsWith("o3") || m.startsWith("o4") || m.includes("gpt-5") || m.includes("gpt5");
15427
15658
  }
15659
+ function adaptChatExtrasForResponses(body, extra) {
15660
+ const native = extra ?? {};
15661
+ if ("reasoning_effort" in body) {
15662
+ const effort = body.reasoning_effort;
15663
+ delete body.reasoning_effort;
15664
+ if (effort != null) {
15665
+ const reasoning = body.reasoning && typeof body.reasoning === "object" ? body.reasoning : {};
15666
+ if (reasoning.effort == null) body.reasoning = { ...reasoning, effort };
15667
+ }
15668
+ }
15669
+ for (const key of ["max_tokens", "max_completion_tokens"]) {
15670
+ if (!(key in body)) continue;
15671
+ const value = body[key];
15672
+ delete body[key];
15673
+ if (value != null && !("max_output_tokens" in native)) body.max_output_tokens = value;
15674
+ }
15675
+ if ("response_format" in body) {
15676
+ const format = toResponsesTextFormat(body.response_format);
15677
+ delete body.response_format;
15678
+ if (format && !("text" in native)) body.text = { ...body.text, format };
15679
+ }
15680
+ if (body.stream_options && typeof body.stream_options === "object" && "include_usage" in body.stream_options) {
15681
+ const { include_usage: _dropped, ...rest } = body.stream_options;
15682
+ if (Object.keys(rest).length > 0) body.stream_options = rest;
15683
+ else delete body.stream_options;
15684
+ }
15685
+ }
15686
+ function toResponsesTextFormat(rf) {
15687
+ if (!rf || typeof rf !== "object") return void 0;
15688
+ if (rf.type === "json_schema") {
15689
+ const js = rf.json_schema ?? {};
15690
+ return {
15691
+ type: "json_schema",
15692
+ name: js.name ?? "response",
15693
+ schema: inlineJsonSchemaRefs(js.schema),
15694
+ ...js.description != null ? { description: js.description } : {},
15695
+ ...js.strict != null ? { strict: js.strict } : {}
15696
+ };
15697
+ }
15698
+ if (rf.type === "json_object" || rf.type === "text") return { type: rf.type };
15699
+ return void 0;
15700
+ }
15428
15701
  function expandResponsesInput(input) {
15429
15702
  const out = [];
15430
15703
  for (const item of input) {