@yourgpt/llm-sdk 2.6.0-beta.0 → 2.6.0-beta.1

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 (55) hide show
  1. package/README.md +77 -0
  2. package/dist/adapters/index.d.mts +11 -13
  3. package/dist/adapters/index.d.ts +11 -13
  4. package/dist/adapters/index.js +255 -164
  5. package/dist/adapters/index.mjs +255 -164
  6. package/dist/{base-C58Dsr9p.d.ts → base-BdTpUAWI.d.ts} +2 -1
  7. package/dist/{base-tNgbBaSo.d.mts → base-CkeT7u-O.d.mts} +2 -1
  8. package/dist/fallback/index.d.mts +4 -4
  9. package/dist/fallback/index.d.ts +4 -4
  10. package/dist/fallback/index.js +75 -39
  11. package/dist/fallback/index.mjs +75 -39
  12. package/dist/index.d.mts +8 -7
  13. package/dist/index.d.ts +8 -7
  14. package/dist/index.js +75 -28
  15. package/dist/index.mjs +75 -28
  16. package/dist/providers/anthropic/index.d.mts +3 -3
  17. package/dist/providers/anthropic/index.d.ts +3 -3
  18. package/dist/providers/azure/index.d.mts +3 -3
  19. package/dist/providers/azure/index.d.ts +3 -3
  20. package/dist/providers/fireworks/index.d.mts +3 -3
  21. package/dist/providers/fireworks/index.d.ts +3 -3
  22. package/dist/providers/fireworks/index.js +255 -164
  23. package/dist/providers/fireworks/index.mjs +255 -164
  24. package/dist/providers/google/index.d.mts +3 -3
  25. package/dist/providers/google/index.d.ts +3 -3
  26. package/dist/providers/google/index.js +255 -164
  27. package/dist/providers/google/index.mjs +255 -164
  28. package/dist/providers/ollama/index.d.mts +4 -4
  29. package/dist/providers/ollama/index.d.ts +4 -4
  30. package/dist/providers/openai/index.d.mts +6 -23
  31. package/dist/providers/openai/index.d.ts +6 -23
  32. package/dist/providers/openai/index.js +656 -450
  33. package/dist/providers/openai/index.mjs +656 -450
  34. package/dist/providers/openrouter/index.d.mts +3 -3
  35. package/dist/providers/openrouter/index.d.ts +3 -3
  36. package/dist/providers/openrouter/index.js +255 -164
  37. package/dist/providers/openrouter/index.mjs +255 -164
  38. package/dist/providers/togetherai/index.d.mts +3 -3
  39. package/dist/providers/togetherai/index.d.ts +3 -3
  40. package/dist/providers/togetherai/index.js +255 -164
  41. package/dist/providers/togetherai/index.mjs +255 -164
  42. package/dist/providers/xai/index.d.mts +3 -3
  43. package/dist/providers/xai/index.d.ts +3 -3
  44. package/dist/providers/xai/index.js +255 -164
  45. package/dist/providers/xai/index.mjs +255 -164
  46. package/dist/{types-BkQCSiIt.d.mts → types-BW3csYo5.d.mts} +22 -1
  47. package/dist/{types-BkQCSiIt.d.ts → types-BW3csYo5.d.ts} +22 -1
  48. package/dist/{types-B6dhnguR.d.mts → types-BWRYIvl_.d.mts} +11 -4
  49. package/dist/{types-CCxPmkmK.d.ts → types-CpXcT-tY.d.ts} +11 -4
  50. package/dist/{types-BQ31QIsA.d.ts → types-DA54bSmX.d.ts} +3 -11
  51. package/dist/{types-BSSiJW2o.d.mts → types-NOswlhm9.d.mts} +3 -11
  52. package/dist/yourgpt/index.d.mts +1 -1
  53. package/dist/yourgpt/index.d.ts +1 -1
  54. package/package.json +115 -48
  55. package/LICENSE +0 -21
@@ -1,3 +1,96 @@
1
+ // src/core/openai-parameters.ts
2
+ function capabilities(model) {
3
+ const gpt = /^gpt-(\d+)(?:\.(\d+))?(?:-(.+))?$/i.exec(model);
4
+ const major = Number(gpt?.[1] ?? 0);
5
+ const minor = Number(gpt?.[2] ?? 0);
6
+ const chat = gpt?.[3]?.startsWith("chat") ?? false;
7
+ const reasoning = /^o\d+(?:-|$)/i.test(model) || major >= 5 && !chat;
8
+ const supportsNone = major === 5 && minor >= 1 && !chat || /^gpt-6-(?:sol|luna)(?:-\d{4}-\d{2}-\d{2})?$/i.test(model);
9
+ return { major, minor, reasoning, supportsNone };
10
+ }
11
+ function requiresOpenAIResponses(model, baseUrl, options = {}) {
12
+ if (options.parameterPolicy === "passthrough") return false;
13
+ const c = capabilities(model);
14
+ if (!c.reasoning || options.reasoningEffort === "none") return false;
15
+ if (!(c.major >= 6 || c.major === 5 && c.minor >= 4)) return false;
16
+ if (!baseUrl) return true;
17
+ try {
18
+ return new URL(baseUrl).hostname === "api.openai.com";
19
+ } catch {
20
+ return false;
21
+ }
22
+ }
23
+ function buildOpenAIParameters(model, maxTokens, temperature, options = {}, api = "chat") {
24
+ const c = capabilities(model ?? "");
25
+ const normalize = options.parameterPolicy !== "passthrough";
26
+ const effort = options.reasoningEffort;
27
+ if (normalize && c.reasoning && effort === "none" && !c.supportsNone) {
28
+ throw new Error(`${model} does not support reasoningEffort: none`);
29
+ }
30
+ if (normalize && c.major >= 6 && effort === "minimal") {
31
+ throw new Error(`${model} does not support reasoningEffort: minimal`);
32
+ }
33
+ const result = {
34
+ [api === "responses" ? "max_output_tokens" : normalize && c.reasoning ? "max_completion_tokens" : "max_tokens"]: maxTokens
35
+ };
36
+ if (effort !== void 0) {
37
+ if (api === "responses") result.reasoning = { effort };
38
+ else result.reasoning_effort = effort;
39
+ }
40
+ if (temperature !== void 0) {
41
+ if (!normalize || !c.reasoning || effort === "none" && c.supportsNone) {
42
+ result.temperature = temperature;
43
+ } else {
44
+ (options.onWarning ?? console.warn)(
45
+ `[llm-sdk] ${model}: omitted temperature because reasoning is enabled.`
46
+ );
47
+ }
48
+ }
49
+ return result;
50
+ }
51
+
52
+ // src/core/provider-error.ts
53
+ function providerErrorEvent(error, code) {
54
+ const details = error ?? {};
55
+ const event = {
56
+ type: "error",
57
+ message: error instanceof Error ? error.message : String(error),
58
+ code,
59
+ ...typeof details.status === "number" ? { status: details.status } : {},
60
+ ...typeof details.code === "string" ? { providerCode: details.code } : {},
61
+ ...typeof details.param === "string" ? { param: details.param } : {},
62
+ ...typeof details.request_id === "string" ? { requestId: details.request_id } : {}
63
+ };
64
+ Object.defineProperty(event, "cause", { value: error, enumerable: false });
65
+ return event;
66
+ }
67
+ function errorFromEvent(event) {
68
+ if (event.cause instanceof Error) return event.cause;
69
+ return Object.assign(new Error(event.message), {
70
+ status: event.status,
71
+ code: event.providerCode ?? event.code,
72
+ param: event.param,
73
+ request_id: event.requestId
74
+ });
75
+ }
76
+ function throwIfAborted(signal) {
77
+ if (!signal?.aborted) return;
78
+ const error = new Error("The operation was aborted");
79
+ error.name = "AbortError";
80
+ throw error;
81
+ }
82
+
83
+ // src/core/utils.ts
84
+ function generateId(prefix = "id") {
85
+ return `${prefix}_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
86
+ }
87
+ function generateMessageId() {
88
+ return generateId("msg");
89
+ }
90
+ function generateToolCallId() {
91
+ return generateId("call");
92
+ }
93
+
1
94
  // src/adapters/base.ts
2
95
  function stringifyForDebug(value) {
3
96
  return JSON.stringify(
@@ -104,16 +197,6 @@ function normalizeObjectJsonSchema(schema) {
104
197
  }
105
198
  return normalized;
106
199
  }
107
- function isOpenAIReasoningModel(modelId) {
108
- if (!modelId) return false;
109
- return /^(o1|o3|o4|gpt-5)/i.test(modelId);
110
- }
111
- function buildOpenAITokenParams(modelId, maxTokens, temperature) {
112
- if (isOpenAIReasoningModel(modelId)) {
113
- return { max_completion_tokens: maxTokens };
114
- }
115
- return { max_tokens: maxTokens, temperature };
116
- }
117
200
  function toOpenAIResponseFormat(rf) {
118
201
  if (!rf) return void 0;
119
202
  if (rf.type === "json_object") return { type: "json_object" };
@@ -233,291 +316,6 @@ function formatMessagesForOpenAI(messages, systemPrompt) {
233
316
  return formatted;
234
317
  }
235
318
 
236
- // src/providers/openai/provider.ts
237
- var OPENAI_MODELS = {
238
- // GPT-4o series
239
- "gpt-4o": { vision: true, tools: true, jsonMode: true, maxTokens: 128e3 },
240
- "gpt-5.2": {
241
- vision: true,
242
- tools: true,
243
- jsonMode: true,
244
- maxTokens: 128e3
245
- },
246
- "gpt-4o-2024-11-20": {
247
- vision: true,
248
- tools: true,
249
- jsonMode: true,
250
- maxTokens: 128e3
251
- },
252
- "gpt-4o-2024-08-06": {
253
- vision: true,
254
- tools: true,
255
- jsonMode: true,
256
- maxTokens: 128e3
257
- },
258
- // GPT-4 Turbo
259
- "gpt-4-turbo": {
260
- vision: true,
261
- tools: true,
262
- jsonMode: true,
263
- maxTokens: 128e3
264
- },
265
- "gpt-4-turbo-preview": {
266
- vision: false,
267
- tools: true,
268
- jsonMode: true,
269
- maxTokens: 128e3
270
- },
271
- // GPT-4
272
- "gpt-4": { vision: false, tools: true, jsonMode: false, maxTokens: 8192 },
273
- "gpt-4-32k": {
274
- vision: false,
275
- tools: true,
276
- jsonMode: false,
277
- maxTokens: 32768
278
- },
279
- // GPT-3.5
280
- "gpt-3.5-turbo": {
281
- vision: false,
282
- tools: true,
283
- jsonMode: true,
284
- maxTokens: 16385
285
- },
286
- // O1 series (reasoning)
287
- o1: { vision: true, tools: false, jsonMode: false, maxTokens: 128e3 },
288
- "o1-mini": { vision: true, tools: false, jsonMode: false, maxTokens: 128e3 },
289
- "o1-preview": {
290
- vision: true,
291
- tools: false,
292
- jsonMode: false,
293
- maxTokens: 128e3
294
- },
295
- // O3 series
296
- "o3-mini": { vision: true, tools: false, jsonMode: false, maxTokens: 128e3 }
297
- };
298
- function openai(modelId, options = {}) {
299
- const apiKey = options.apiKey ?? process.env.OPENAI_API_KEY;
300
- const baseURL = options.baseURL ?? "https://api.openai.com/v1";
301
- let client = null;
302
- async function getClient() {
303
- if (!client) {
304
- const { default: OpenAI } = await import('openai');
305
- client = new OpenAI({
306
- apiKey,
307
- baseURL,
308
- organization: options.organization,
309
- defaultHeaders: options.headers
310
- });
311
- }
312
- return client;
313
- }
314
- const modelConfig = OPENAI_MODELS[modelId] ?? OPENAI_MODELS["gpt-4o"];
315
- return {
316
- provider: "openai",
317
- modelId,
318
- capabilities: {
319
- supportsVision: modelConfig.vision,
320
- supportsTools: modelConfig.tools,
321
- supportsStreaming: true,
322
- supportsJsonMode: modelConfig.jsonMode,
323
- supportsThinking: false,
324
- supportsPDF: false,
325
- maxTokens: modelConfig.maxTokens,
326
- supportedImageTypes: modelConfig.vision ? ["image/png", "image/jpeg", "image/gif", "image/webp"] : []
327
- },
328
- async doGenerate(params) {
329
- const client2 = await getClient();
330
- const messages = formatMessagesForOpenAI2(params.messages);
331
- const response = await client2.chat.completions.create({
332
- model: modelId,
333
- messages,
334
- tools: params.tools,
335
- ...buildOpenAITokenParams(
336
- modelId,
337
- params.maxTokens,
338
- params.temperature
339
- ),
340
- response_format: toOpenAIResponseFormat(params.responseFormat)
341
- });
342
- const choice = response.choices[0];
343
- const message = choice.message;
344
- const toolCalls = (message.tool_calls ?? []).map(
345
- (tc) => ({
346
- id: tc.id,
347
- name: tc.function.name,
348
- args: JSON.parse(tc.function.arguments || "{}")
349
- })
350
- );
351
- return {
352
- text: message.content ?? "",
353
- toolCalls,
354
- finishReason: mapFinishReason(choice.finish_reason),
355
- usage: {
356
- promptTokens: response.usage?.prompt_tokens ?? 0,
357
- completionTokens: response.usage?.completion_tokens ?? 0,
358
- totalTokens: response.usage?.total_tokens ?? 0
359
- },
360
- rawResponse: response
361
- };
362
- },
363
- async *doStream(params) {
364
- const client2 = await getClient();
365
- const messages = formatMessagesForOpenAI2(params.messages);
366
- const stream = await client2.chat.completions.create({
367
- model: modelId,
368
- messages,
369
- tools: params.tools,
370
- ...buildOpenAITokenParams(
371
- modelId,
372
- params.maxTokens,
373
- params.temperature
374
- ),
375
- response_format: toOpenAIResponseFormat(params.responseFormat),
376
- stream: true
377
- });
378
- let currentToolCall = null;
379
- let totalPromptTokens = 0;
380
- let totalCompletionTokens = 0;
381
- for await (const chunk of stream) {
382
- if (params.signal?.aborted) {
383
- yield { type: "error", error: new Error("Aborted") };
384
- return;
385
- }
386
- const choice = chunk.choices[0];
387
- const delta = choice?.delta;
388
- if (delta?.content) {
389
- yield { type: "text-delta", text: delta.content };
390
- }
391
- if (delta?.tool_calls) {
392
- for (const tc of delta.tool_calls) {
393
- if (tc.id) {
394
- if (currentToolCall) {
395
- yield {
396
- type: "tool-call",
397
- toolCall: {
398
- id: currentToolCall.id,
399
- name: currentToolCall.name,
400
- args: JSON.parse(currentToolCall.arguments || "{}")
401
- }
402
- };
403
- }
404
- currentToolCall = {
405
- id: tc.id,
406
- name: tc.function?.name ?? "",
407
- arguments: tc.function?.arguments ?? ""
408
- };
409
- } else if (currentToolCall && tc.function?.arguments) {
410
- currentToolCall.arguments += tc.function.arguments;
411
- }
412
- }
413
- }
414
- if (choice?.finish_reason) {
415
- if (currentToolCall) {
416
- yield {
417
- type: "tool-call",
418
- toolCall: {
419
- id: currentToolCall.id,
420
- name: currentToolCall.name,
421
- args: JSON.parse(currentToolCall.arguments || "{}")
422
- }
423
- };
424
- currentToolCall = null;
425
- }
426
- if (chunk.usage) {
427
- totalPromptTokens = chunk.usage.prompt_tokens;
428
- totalCompletionTokens = chunk.usage.completion_tokens;
429
- }
430
- yield {
431
- type: "finish",
432
- finishReason: mapFinishReason(choice.finish_reason),
433
- usage: {
434
- promptTokens: totalPromptTokens,
435
- completionTokens: totalCompletionTokens,
436
- totalTokens: totalPromptTokens + totalCompletionTokens
437
- }
438
- };
439
- }
440
- }
441
- }
442
- };
443
- }
444
- function mapFinishReason(reason) {
445
- switch (reason) {
446
- case "stop":
447
- return "stop";
448
- case "length":
449
- return "length";
450
- case "tool_calls":
451
- case "function_call":
452
- return "tool-calls";
453
- case "content_filter":
454
- return "content-filter";
455
- default:
456
- return "unknown";
457
- }
458
- }
459
- function formatMessagesForOpenAI2(messages) {
460
- return messages.map((msg) => {
461
- switch (msg.role) {
462
- case "system":
463
- return { role: "system", content: msg.content };
464
- case "user":
465
- if (typeof msg.content === "string") {
466
- return { role: "user", content: msg.content };
467
- }
468
- return {
469
- role: "user",
470
- content: msg.content.map((part) => {
471
- if (part.type === "text") {
472
- return { type: "text", text: part.text };
473
- }
474
- if (part.type === "image") {
475
- const imageData = typeof part.image === "string" ? part.image : Buffer.from(part.image).toString("base64");
476
- const url = imageData.startsWith("data:") ? imageData : `data:${part.mimeType ?? "image/png"};base64,${imageData}`;
477
- return { type: "image_url", image_url: { url, detail: "auto" } };
478
- }
479
- return { type: "text", text: "" };
480
- })
481
- };
482
- case "assistant":
483
- const assistantMsg = {
484
- role: "assistant",
485
- content: msg.content
486
- };
487
- if (msg.toolCalls && msg.toolCalls.length > 0) {
488
- assistantMsg.tool_calls = msg.toolCalls.map((tc) => ({
489
- id: tc.id,
490
- type: "function",
491
- function: {
492
- name: tc.name,
493
- arguments: JSON.stringify(tc.args)
494
- }
495
- }));
496
- }
497
- return assistantMsg;
498
- case "tool":
499
- return {
500
- role: "tool",
501
- tool_call_id: msg.toolCallId,
502
- content: msg.content
503
- };
504
- default:
505
- return msg;
506
- }
507
- });
508
- }
509
-
510
- // src/core/utils.ts
511
- function generateId(prefix = "id") {
512
- return `${prefix}_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
513
- }
514
- function generateMessageId() {
515
- return generateId("msg");
516
- }
517
- function generateToolCallId() {
518
- return generateId("call");
519
- }
520
-
521
319
  // src/adapters/openai.ts
522
320
  var OpenAIAdapter = class _OpenAIAdapter {
523
321
  constructor(config) {
@@ -538,39 +336,38 @@ var OpenAIAdapter = class _OpenAIAdapter {
538
336
  const { default: OpenAI } = await import('openai');
539
337
  this.client = new OpenAI({
540
338
  apiKey: this.config.apiKey,
541
- baseURL: this.config.baseUrl
339
+ baseURL: this.config.baseUrl,
340
+ organization: this.config.organization,
341
+ defaultHeaders: this.config.headers
542
342
  });
543
343
  }
544
344
  return this.client;
545
345
  }
346
+ parameterOptions(request) {
347
+ return {
348
+ ...this.config,
349
+ reasoningEffort: request.config?.reasoningEffort ?? this.config.reasoningEffort
350
+ };
351
+ }
352
+ toolDefinitions(request) {
353
+ if (request.toolDefinitions?.length) return request.toolDefinitions;
354
+ return formatTools(request.actions ?? []).map((tool) => ({
355
+ name: tool.function.name,
356
+ description: tool.function.description ?? "",
357
+ location: "server",
358
+ inputSchema: tool.function.parameters
359
+ }));
360
+ }
546
361
  shouldUseResponsesApi(request) {
547
- const hasTools = Array.isArray(request.toolDefinitions) && request.toolDefinitions.length > 0;
548
- if (!hasTools) return false;
549
- if (request.providerToolOptions?.openai?.nativeToolSearch?.enabled === true && request.providerToolOptions.openai.nativeToolSearch.useResponsesApi !== false) {
362
+ if (this.toolDefinitions(request).length === 0) return false;
363
+ if (request.providerToolOptions?.openai?.nativeToolSearch?.enabled === true && request.providerToolOptions.openai.nativeToolSearch.useResponsesApi !== false)
550
364
  return true;
551
- }
552
- return this.requiresResponsesApiForTools(
553
- request.config?.model || this.model
365
+ return requiresOpenAIResponses(
366
+ request.config?.model || this.model,
367
+ this.config.baseUrl,
368
+ this.parameterOptions(request)
554
369
  );
555
370
  }
556
- /**
557
- * Whether this model rejects function tools on /v1/chat/completions.
558
- *
559
- * Applies to OpenAI proper only — the adapter is reused for OpenAI-compatible
560
- * providers (Azure, xAI, Google, Fireworks) whose endpoints have no such
561
- * restriction and may not implement /v1/responses at all.
562
- */
563
- requiresResponsesApiForTools(modelId) {
564
- if (!modelId) return false;
565
- if (!/^gpt-5\.(?:[4-9]|\d{2,})/i.test(modelId)) return false;
566
- const baseUrl = this.config.baseUrl;
567
- if (!baseUrl) return true;
568
- try {
569
- return new URL(baseUrl).hostname === "api.openai.com";
570
- } catch {
571
- return false;
572
- }
573
- }
574
371
  /**
575
372
  * Convert one message's content to Responses content parts.
576
373
  *
@@ -601,7 +398,7 @@ var OpenAIAdapter = class _OpenAIAdapter {
601
398
  });
602
399
  }
603
400
  buildResponsesInput(request) {
604
- const sourceMessages = request.rawMessages && request.rawMessages.length > 0 ? request.rawMessages : formatMessagesForOpenAI(request.messages, void 0);
401
+ const sourceMessages = request.rawMessages && request.rawMessages.length > 0 ? this.formatRawMessages(request.rawMessages) : formatMessagesForOpenAI(request.messages, void 0);
605
402
  const input = [];
606
403
  for (const message of sourceMessages) {
607
404
  if (message.role === "system") {
@@ -651,6 +448,26 @@ var OpenAIAdapter = class _OpenAIAdapter {
651
448
  }
652
449
  return input;
653
450
  }
451
+ /** Preserve Copilot screenshot attachments on every OpenAI transport. */
452
+ formatRawMessages(messages) {
453
+ return messages.map((message) => {
454
+ const { attachments, ...rest } = message;
455
+ let content = message.content;
456
+ if (message.role === "assistant" && Array.isArray(message.tool_calls) && message.tool_calls.length > 0 && content === "") {
457
+ content = null;
458
+ }
459
+ if (Array.isArray(attachments) && attachments.length > 0) {
460
+ const images = attachments.filter((attachment) => attachment && typeof attachment === "object").map(
461
+ (attachment) => attachmentToOpenAIImage(attachment)
462
+ ).filter((image) => image !== null);
463
+ if (images.length > 0) {
464
+ const parts = Array.isArray(content) ? content : content ? [{ type: "text", text: content }] : [];
465
+ content = [...parts, ...images];
466
+ }
467
+ }
468
+ return { ...rest, content };
469
+ });
470
+ }
654
471
  /**
655
472
  * Build the web_search tool for the Responses API, or undefined when web
656
473
  * search is off.
@@ -732,13 +549,7 @@ var OpenAIAdapter = class _OpenAIAdapter {
732
549
  const toolCalls = Array.isArray(response?.output) ? response.output.filter((item) => item?.type === "function_call").map((item) => ({
733
550
  id: item.call_id ?? item.id ?? generateToolCallId(),
734
551
  name: item.name,
735
- args: (() => {
736
- try {
737
- return JSON.parse(item.arguments ?? "{}");
738
- } catch {
739
- return {};
740
- }
741
- })()
552
+ args: JSON.parse(item.arguments ?? "{}")
742
553
  })) : [];
743
554
  return {
744
555
  content,
@@ -762,18 +573,19 @@ var OpenAIAdapter = class _OpenAIAdapter {
762
573
  instructions: request.systemPrompt,
763
574
  input: this.buildResponsesInput(request),
764
575
  tools: this.buildResponsesTools(
765
- request.toolDefinitions ?? [],
576
+ this.toolDefinitions(request),
766
577
  request.providerToolOptions?.openai?.nativeToolSearch?.enabled === true,
767
578
  this.buildResponsesWebSearchTool(request)
768
579
  ),
769
- tool_choice: openaiToolOptions?.toolChoice === "required" ? "required" : openaiToolOptions?.toolChoice === "auto" ? "auto" : void 0,
580
+ tool_choice: openaiToolOptions?.toolChoice,
770
581
  parallel_tool_calls: openaiToolOptions?.parallelToolCalls,
771
- // Reasoning models reject `temperature` outright — sending it would swap one
772
- // 400 for another. isOpenAIReasoningModel already encodes that family test.
773
- ...isOpenAIReasoningModel(modelIdForPayload) ? {} : {
774
- temperature: request.config?.temperature ?? this.config.temperature
775
- },
776
- max_output_tokens: request.config?.maxTokens ?? this.config.maxTokens,
582
+ ...buildOpenAIParameters(
583
+ modelIdForPayload,
584
+ request.config?.maxTokens ?? this.config.maxTokens,
585
+ request.config?.temperature ?? this.config.temperature,
586
+ this.parameterOptions(request),
587
+ "responses"
588
+ ),
777
589
  ...responsesTextFormat ? { text: { format: responsesTextFormat } } : {},
778
590
  stream
779
591
  };
@@ -781,10 +593,26 @@ var OpenAIAdapter = class _OpenAIAdapter {
781
593
  }
782
594
  async completeWithResponses(request) {
783
595
  const client = await this.getClient();
596
+ throwIfAborted(request.signal);
784
597
  const payload = this.buildResponsesPayload(request, false);
785
598
  logProviderPayload("openai", "request payload", payload, request.debug);
786
- const response = await client.responses.create(payload);
599
+ if (!client.responses?.create)
600
+ throw new Error(
601
+ "This OpenAI client does not support Responses; install an openai version with Responses support."
602
+ );
603
+ const response = await client.responses.create(payload, {
604
+ signal: request.signal
605
+ });
606
+ throwIfAborted(request.signal);
787
607
  logProviderPayload("openai", "response payload", response, request.debug);
608
+ if (response.status && response.status !== "completed") {
609
+ throw Object.assign(
610
+ new Error(
611
+ response.error?.message ?? `Responses request ${response.status}`
612
+ ),
613
+ response.error
614
+ );
615
+ }
788
616
  return this.parseResponsesResult(response);
789
617
  }
790
618
  /**
@@ -802,13 +630,26 @@ var OpenAIAdapter = class _OpenAIAdapter {
802
630
  */
803
631
  async *streamWithResponses(request) {
804
632
  const client = await this.getClient();
633
+ throwIfAborted(request.signal);
805
634
  const payload = this.buildResponsesPayload(request, true);
806
635
  logProviderPayload("openai", "request payload", payload, request.debug);
807
- const stream = await client.responses.create(payload);
636
+ if (!client.responses?.create)
637
+ throw new Error(
638
+ "This OpenAI client does not support Responses; install an openai version with Responses support."
639
+ );
640
+ const stream = await client.responses.create(payload, {
641
+ signal: request.signal
642
+ });
808
643
  const toolCalls = /* @__PURE__ */ new Map();
809
644
  let usage;
810
645
  const citations = [];
646
+ let completed = false;
811
647
  function* flushToolCalls() {
648
+ for (const call of toolCalls.values()) {
649
+ if (!call.name)
650
+ throw new Error("Responses tool call is missing its name");
651
+ JSON.parse(call.arguments || "{}");
652
+ }
812
653
  for (const call of toolCalls.values()) {
813
654
  yield { type: "action:start", id: call.id, name: call.name };
814
655
  yield { type: "action:args", id: call.id, args: call.arguments };
@@ -817,7 +658,7 @@ var OpenAIAdapter = class _OpenAIAdapter {
817
658
  toolCalls.clear();
818
659
  }
819
660
  for await (const event of stream) {
820
- if (request.signal?.aborted) break;
661
+ throwIfAborted(request.signal);
821
662
  logProviderPayload("openai", "stream chunk", event, request.debug);
822
663
  const index = event?.output_index ?? 0;
823
664
  switch (event?.type) {
@@ -847,8 +688,8 @@ var OpenAIAdapter = class _OpenAIAdapter {
847
688
  }
848
689
  break;
849
690
  }
850
- case "response.completed":
851
- case "response.incomplete": {
691
+ case "response.completed": {
692
+ completed = true;
852
693
  const found = this.collectResponsesCitations(
853
694
  event.response?.output,
854
695
  citations.length
@@ -864,13 +705,27 @@ var OpenAIAdapter = class _OpenAIAdapter {
864
705
  }
865
706
  break;
866
707
  }
867
- case "error":
868
- case "response.failed":
708
+ case "response.incomplete":
869
709
  throw new Error(
870
- event.message ?? event.response?.error?.message ?? "Responses stream failed"
710
+ `Responses stream incomplete: ${event.response?.incomplete_details?.reason ?? "unknown reason"}`
871
711
  );
712
+ case "error":
713
+ case "response.failed": {
714
+ const details = event.response?.error ?? event;
715
+ throw Object.assign(
716
+ new Error(details.message ?? "Responses stream failed"),
717
+ {
718
+ code: details.code,
719
+ param: details.param,
720
+ status: details.status
721
+ }
722
+ );
723
+ }
872
724
  }
873
725
  }
726
+ throwIfAborted(request.signal);
727
+ if (!completed)
728
+ throw new Error("Responses stream ended without a completed response");
874
729
  yield* flushToolCalls();
875
730
  if (citations.length > 0) {
876
731
  yield { type: "citation", citations: deduplicateCitations(citations) };
@@ -879,6 +734,7 @@ var OpenAIAdapter = class _OpenAIAdapter {
879
734
  yield { type: "done", usage };
880
735
  }
881
736
  async *stream(request) {
737
+ throwIfAborted(request.signal);
882
738
  if (this.shouldUseResponsesApi(request)) {
883
739
  const messageId2 = generateMessageId();
884
740
  yield { type: "message:start", id: messageId2 };
@@ -886,47 +742,14 @@ var OpenAIAdapter = class _OpenAIAdapter {
886
742
  yield* this.streamWithResponses(request);
887
743
  return;
888
744
  } catch (error) {
889
- yield {
890
- type: "error",
891
- message: error instanceof Error ? error.message : "Unknown error",
892
- code: "OPENAI_RESPONSES_ERROR"
893
- };
745
+ yield providerErrorEvent(error, "OPENAI_RESPONSES_ERROR");
894
746
  return;
895
747
  }
896
748
  }
897
749
  const client = await this.getClient();
898
750
  let messages;
899
751
  if (request.rawMessages && request.rawMessages.length > 0) {
900
- const processedMessages = request.rawMessages.map((msg) => {
901
- if (msg.role === "assistant" && Array.isArray(msg.tool_calls) && msg.tool_calls.length > 0 && msg.content === "") {
902
- return { ...msg, content: null };
903
- }
904
- const hasAttachments = msg.attachments && Array.isArray(msg.attachments) && msg.attachments.length > 0;
905
- if (hasAttachments) {
906
- const content = [];
907
- if (msg.content) {
908
- content.push({ type: "text", text: msg.content });
909
- }
910
- for (const attachment of msg.attachments) {
911
- if (attachment.type === "image") {
912
- let imageUrl;
913
- if (attachment.url) {
914
- imageUrl = attachment.url;
915
- } else if (attachment.data) {
916
- imageUrl = attachment.data.startsWith("data:") ? attachment.data : `data:${attachment.mimeType || "image/png"};base64,${attachment.data}`;
917
- } else {
918
- continue;
919
- }
920
- content.push({
921
- type: "image_url",
922
- image_url: { url: imageUrl, detail: "auto" }
923
- });
924
- }
925
- }
926
- return { ...msg, content, attachments: void 0 };
927
- }
928
- return msg;
929
- });
752
+ const processedMessages = this.formatRawMessages(request.rawMessages);
930
753
  if (request.systemPrompt) {
931
754
  const hasSystem = processedMessages.some((m) => m.role === "system");
932
755
  if (!hasSystem) {
@@ -946,7 +769,16 @@ var OpenAIAdapter = class _OpenAIAdapter {
946
769
  request.systemPrompt
947
770
  );
948
771
  }
949
- const tools = request.actions?.length ? formatTools(request.actions) : [];
772
+ const tools = request.toolDefinitions?.length ? request.toolDefinitions.filter((tool) => tool.available !== false).map((tool) => ({
773
+ type: "function",
774
+ function: {
775
+ name: tool.name,
776
+ description: tool.description,
777
+ parameters: normalizeObjectJsonSchema(
778
+ tool.inputSchema
779
+ )
780
+ }
781
+ })) : formatTools(request.actions ?? []);
950
782
  const webSearchConfig = request.webSearch ?? this.config.webSearch;
951
783
  if (webSearchConfig) {
952
784
  const webSearchTool = {
@@ -975,26 +807,28 @@ var OpenAIAdapter = class _OpenAIAdapter {
975
807
  tools: tools.length > 0 ? tools : void 0,
976
808
  tool_choice: tools.length > 0 ? toolChoice : void 0,
977
809
  parallel_tool_calls: tools.length > 0 ? openaiToolOptions?.parallelToolCalls : void 0,
978
- ...buildOpenAITokenParams(
810
+ ...buildOpenAIParameters(
979
811
  modelIdForPayload,
980
812
  request.config?.maxTokens ?? this.config.maxTokens,
981
- request.config?.temperature ?? this.config.temperature
813
+ request.config?.temperature ?? this.config.temperature,
814
+ this.parameterOptions(request)
982
815
  ),
983
816
  response_format: toOpenAIResponseFormat(request.config?.responseFormat),
984
817
  stream: true,
985
818
  stream_options: { include_usage: true }
986
819
  };
987
820
  logProviderPayload("openai", "request payload", payload, request.debug);
988
- const stream = await client.chat.completions.create(payload);
989
- let currentToolCall = null;
821
+ const stream = await client.chat.completions.create(payload, {
822
+ signal: request.signal
823
+ });
824
+ const toolCalls = /* @__PURE__ */ new Map();
825
+ let finishReason;
990
826
  const collectedCitations = [];
991
827
  let citationIndex = 0;
992
828
  let usage;
993
829
  for await (const chunk of stream) {
994
830
  logProviderPayload("openai", "stream chunk", chunk, request.debug);
995
- if (request.signal?.aborted) {
996
- break;
997
- }
831
+ throwIfAborted(request.signal);
998
832
  const delta = chunk.choices[0]?.delta;
999
833
  const choice = chunk.choices[0];
1000
834
  if (delta?.content) {
@@ -1017,38 +851,19 @@ var OpenAIAdapter = class _OpenAIAdapter {
1017
851
  }
1018
852
  }
1019
853
  }
1020
- if (delta?.tool_calls) {
1021
- for (const toolCall of delta.tool_calls) {
1022
- if (toolCall.id) {
1023
- if (currentToolCall) {
1024
- yield {
1025
- type: "action:args",
1026
- id: currentToolCall.id,
1027
- args: currentToolCall.arguments
1028
- };
1029
- yield {
1030
- type: "action:end",
1031
- id: currentToolCall.id,
1032
- name: currentToolCall.name
1033
- };
1034
- }
1035
- const tcExtraContent = toolCall.extra_content;
1036
- currentToolCall = {
1037
- id: toolCall.id,
1038
- name: toolCall.function?.name || "",
1039
- arguments: toolCall.function?.arguments || "",
1040
- ...tcExtraContent ? { extra_content: tcExtraContent } : {}
1041
- };
1042
- yield {
1043
- type: "action:start",
1044
- id: currentToolCall.id,
1045
- name: currentToolCall.name,
1046
- ...currentToolCall.extra_content ? { extra_content: currentToolCall.extra_content } : {}
1047
- };
1048
- } else if (currentToolCall && toolCall.function?.arguments) {
1049
- currentToolCall.arguments += toolCall.function.arguments;
1050
- }
1051
- }
854
+ for (const part of delta?.tool_calls ?? []) {
855
+ const index = part.index ?? 0;
856
+ const call = toolCalls.get(index) ?? {
857
+ id: "",
858
+ name: "",
859
+ arguments: ""
860
+ };
861
+ if (part.id) call.id = part.id;
862
+ if (part.function?.name) call.name += part.function.name;
863
+ if (part.function?.arguments)
864
+ call.arguments += part.function.arguments;
865
+ if (part.extra_content) call.extra_content = part.extra_content;
866
+ toolCalls.set(index, call);
1052
867
  }
1053
868
  if (chunk.usage) {
1054
869
  usage = {
@@ -1057,21 +872,28 @@ var OpenAIAdapter = class _OpenAIAdapter {
1057
872
  total_tokens: chunk.usage.total_tokens
1058
873
  };
1059
874
  }
1060
- if (choice?.finish_reason) {
1061
- if (currentToolCall) {
1062
- yield {
1063
- type: "action:args",
1064
- id: currentToolCall.id,
1065
- args: currentToolCall.arguments
1066
- };
1067
- yield {
1068
- type: "action:end",
1069
- id: currentToolCall.id,
1070
- name: currentToolCall.name
1071
- };
1072
- currentToolCall = null;
1073
- }
1074
- }
875
+ if (choice?.finish_reason) finishReason = choice.finish_reason;
876
+ }
877
+ throwIfAborted(request.signal);
878
+ if (!finishReason)
879
+ throw new Error("OpenAI stream ended without a finish reason");
880
+ if (toolCalls.size && !["tool_calls", "function_call"].includes(finishReason)) {
881
+ throw new Error("OpenAI stream ended with incomplete tool calls");
882
+ }
883
+ for (const call of toolCalls.values()) {
884
+ if (!call.id || !call.name)
885
+ throw new Error("OpenAI stream contains an incomplete tool call");
886
+ JSON.parse(call.arguments || "{}");
887
+ }
888
+ for (const call of toolCalls.values()) {
889
+ yield {
890
+ type: "action:start",
891
+ id: call.id,
892
+ name: call.name,
893
+ ...call.extra_content ? { extra_content: call.extra_content } : {}
894
+ };
895
+ yield { type: "action:args", id: call.id, args: call.arguments };
896
+ yield { type: "action:end", id: call.id, name: call.name };
1075
897
  }
1076
898
  if (collectedCitations.length > 0) {
1077
899
  const uniqueCitations = deduplicateCitations(collectedCitations);
@@ -1080,26 +902,18 @@ var OpenAIAdapter = class _OpenAIAdapter {
1080
902
  yield { type: "message:end" };
1081
903
  yield { type: "done", usage };
1082
904
  } catch (error) {
1083
- yield {
1084
- type: "error",
1085
- message: error instanceof Error ? error.message : "Unknown error",
1086
- code: `${this.provider.toUpperCase()}_ERROR`
1087
- };
905
+ yield providerErrorEvent(error, `${this.provider.toUpperCase()}_ERROR`);
1088
906
  }
1089
907
  }
1090
908
  async complete(request) {
909
+ throwIfAborted(request.signal);
1091
910
  if (this.shouldUseResponsesApi(request)) {
1092
911
  return this.completeWithResponses(request);
1093
912
  }
1094
913
  const client = await this.getClient();
1095
914
  let messages;
1096
915
  if (request.rawMessages && request.rawMessages.length > 0) {
1097
- const sanitized = request.rawMessages.map((msg) => {
1098
- if (msg.role === "assistant" && Array.isArray(msg.tool_calls) && msg.tool_calls.length > 0 && msg.content === "") {
1099
- return { ...msg, content: null };
1100
- }
1101
- return msg;
1102
- });
916
+ const sanitized = this.formatRawMessages(request.rawMessages);
1103
917
  if (request.systemPrompt && !sanitized.some((message2) => message2.role === "system")) {
1104
918
  messages = [
1105
919
  { role: "system", content: request.systemPrompt },
@@ -1114,7 +928,16 @@ var OpenAIAdapter = class _OpenAIAdapter {
1114
928
  request.systemPrompt
1115
929
  );
1116
930
  }
1117
- const tools = request.actions?.length ? formatTools(request.actions) : [];
931
+ const tools = request.toolDefinitions?.length ? request.toolDefinitions.filter((tool) => tool.available !== false).map((tool) => ({
932
+ type: "function",
933
+ function: {
934
+ name: tool.name,
935
+ description: tool.description,
936
+ parameters: normalizeObjectJsonSchema(
937
+ tool.inputSchema
938
+ )
939
+ }
940
+ })) : formatTools(request.actions ?? []);
1118
941
  const openaiToolOptions = request.providerToolOptions?.openai;
1119
942
  const toolChoice = openaiToolOptions?.toolChoice && typeof openaiToolOptions.toolChoice === "object" ? {
1120
943
  type: "function",
@@ -1129,16 +952,19 @@ var OpenAIAdapter = class _OpenAIAdapter {
1129
952
  tools: tools.length > 0 ? tools : void 0,
1130
953
  tool_choice: tools.length > 0 ? toolChoice : void 0,
1131
954
  parallel_tool_calls: tools.length > 0 ? openaiToolOptions?.parallelToolCalls : void 0,
1132
- ...buildOpenAITokenParams(
955
+ ...buildOpenAIParameters(
1133
956
  modelIdForCompletePayload,
1134
957
  request.config?.maxTokens ?? this.config.maxTokens,
1135
- request.config?.temperature ?? this.config.temperature
958
+ request.config?.temperature ?? this.config.temperature,
959
+ this.parameterOptions(request)
1136
960
  ),
1137
961
  response_format: toOpenAIResponseFormat(request.config?.responseFormat),
1138
962
  stream: false
1139
963
  };
1140
964
  logProviderPayload("openai", "request payload", payload, request.debug);
1141
- const response = await client.chat.completions.create(payload);
965
+ const response = await client.chat.completions.create(payload, {
966
+ signal: request.signal
967
+ });
1142
968
  logProviderPayload("openai", "response payload", response, request.debug);
1143
969
  const choice = response.choices?.[0];
1144
970
  const message = choice?.message;
@@ -1188,6 +1014,383 @@ function createOpenAIAdapter(config) {
1188
1014
  return new OpenAIAdapter(config);
1189
1015
  }
1190
1016
 
1017
+ // src/providers/openai/provider.ts
1018
+ var OPENAI_MODELS = {
1019
+ // GPT-4o series
1020
+ "gpt-4o": { vision: true, tools: true, jsonMode: true, maxTokens: 128e3 },
1021
+ "gpt-5.2": {
1022
+ vision: true,
1023
+ tools: true,
1024
+ jsonMode: true,
1025
+ maxTokens: 128e3
1026
+ },
1027
+ "gpt-4o-2024-11-20": {
1028
+ vision: true,
1029
+ tools: true,
1030
+ jsonMode: true,
1031
+ maxTokens: 128e3
1032
+ },
1033
+ "gpt-4o-2024-08-06": {
1034
+ vision: true,
1035
+ tools: true,
1036
+ jsonMode: true,
1037
+ maxTokens: 128e3
1038
+ },
1039
+ // GPT-4 Turbo
1040
+ "gpt-4-turbo": {
1041
+ vision: true,
1042
+ tools: true,
1043
+ jsonMode: true,
1044
+ maxTokens: 128e3
1045
+ },
1046
+ "gpt-4-turbo-preview": {
1047
+ vision: false,
1048
+ tools: true,
1049
+ jsonMode: true,
1050
+ maxTokens: 128e3
1051
+ },
1052
+ // GPT-4
1053
+ "gpt-4": { vision: false, tools: true, jsonMode: false, maxTokens: 8192 },
1054
+ "gpt-4-32k": {
1055
+ vision: false,
1056
+ tools: true,
1057
+ jsonMode: false,
1058
+ maxTokens: 32768
1059
+ },
1060
+ // GPT-3.5
1061
+ "gpt-3.5-turbo": {
1062
+ vision: false,
1063
+ tools: true,
1064
+ jsonMode: true,
1065
+ maxTokens: 16385
1066
+ },
1067
+ // O1 series (reasoning)
1068
+ o1: { vision: true, tools: false, jsonMode: false, maxTokens: 128e3 },
1069
+ "o1-mini": { vision: true, tools: false, jsonMode: false, maxTokens: 128e3 },
1070
+ "o1-preview": {
1071
+ vision: true,
1072
+ tools: false,
1073
+ jsonMode: false,
1074
+ maxTokens: 128e3
1075
+ },
1076
+ // O3 series
1077
+ "o3-mini": { vision: true, tools: false, jsonMode: false, maxTokens: 128e3 }
1078
+ };
1079
+ function openai(modelId, options = {}) {
1080
+ const apiKey = options.apiKey ?? process.env.OPENAI_API_KEY;
1081
+ const baseURL = options.baseURL ?? "https://api.openai.com/v1";
1082
+ let client = null;
1083
+ async function getClient() {
1084
+ if (!client) {
1085
+ const { default: OpenAI } = await import('openai');
1086
+ client = new OpenAI({
1087
+ apiKey,
1088
+ baseURL,
1089
+ organization: options.organization,
1090
+ defaultHeaders: options.headers
1091
+ });
1092
+ }
1093
+ return client;
1094
+ }
1095
+ function parameterOptions(params) {
1096
+ return {
1097
+ ...options,
1098
+ reasoningEffort: params.reasoningEffort ?? options.reasoningEffort
1099
+ };
1100
+ }
1101
+ function useResponses(params) {
1102
+ return !!params.tools?.length && requiresOpenAIResponses(modelId, baseURL, parameterOptions(params));
1103
+ }
1104
+ const responsesAdapter = new OpenAIAdapter({
1105
+ ...options,
1106
+ apiKey: apiKey ?? "",
1107
+ model: modelId,
1108
+ baseUrl: baseURL
1109
+ });
1110
+ function responsesRequest(params) {
1111
+ return {
1112
+ messages: [],
1113
+ rawMessages: formatMessagesForOpenAI2(params.messages),
1114
+ toolDefinitions: params.tools?.map((raw) => {
1115
+ const tool = raw;
1116
+ if (tool.type !== "function")
1117
+ throw new Error(
1118
+ "Only function tools are supported by this OpenAI generation path"
1119
+ );
1120
+ return {
1121
+ name: tool.function.name,
1122
+ description: tool.function.description ?? "",
1123
+ location: "server",
1124
+ inputSchema: tool.function.parameters
1125
+ };
1126
+ }),
1127
+ config: {
1128
+ temperature: params.temperature,
1129
+ maxTokens: params.maxTokens,
1130
+ responseFormat: params.responseFormat,
1131
+ reasoningEffort: parameterOptions(params).reasoningEffort
1132
+ },
1133
+ signal: params.signal
1134
+ };
1135
+ }
1136
+ const modelConfig = OPENAI_MODELS[modelId] ?? OPENAI_MODELS["gpt-4o"];
1137
+ return {
1138
+ provider: "openai",
1139
+ modelId,
1140
+ capabilities: {
1141
+ supportsVision: modelConfig.vision,
1142
+ supportsTools: modelConfig.tools,
1143
+ supportsStreaming: true,
1144
+ supportsJsonMode: modelConfig.jsonMode,
1145
+ supportsThinking: false,
1146
+ supportsPDF: false,
1147
+ maxTokens: modelConfig.maxTokens,
1148
+ supportedImageTypes: modelConfig.vision ? ["image/png", "image/jpeg", "image/gif", "image/webp"] : []
1149
+ },
1150
+ async doGenerate(params) {
1151
+ throwIfAborted(params.signal);
1152
+ if (useResponses(params)) {
1153
+ const result = await responsesAdapter.complete(
1154
+ responsesRequest(params)
1155
+ );
1156
+ return {
1157
+ text: result.content,
1158
+ toolCalls: result.toolCalls,
1159
+ finishReason: result.toolCalls.length ? "tool-calls" : "stop",
1160
+ usage: result.usage ?? {
1161
+ promptTokens: 0,
1162
+ completionTokens: 0,
1163
+ totalTokens: 0
1164
+ },
1165
+ rawResponse: result.rawResponse
1166
+ };
1167
+ }
1168
+ const client2 = await getClient();
1169
+ const messages = formatMessagesForOpenAI2(params.messages);
1170
+ const response = await client2.chat.completions.create(
1171
+ {
1172
+ model: modelId,
1173
+ messages,
1174
+ tools: params.tools,
1175
+ ...buildOpenAIParameters(
1176
+ modelId,
1177
+ params.maxTokens,
1178
+ params.temperature,
1179
+ parameterOptions(params)
1180
+ ),
1181
+ response_format: toOpenAIResponseFormat(params.responseFormat)
1182
+ },
1183
+ { signal: params.signal }
1184
+ );
1185
+ const choice = response.choices[0];
1186
+ const message = choice.message;
1187
+ const toolCalls = (message.tool_calls ?? []).map(
1188
+ (tc) => ({
1189
+ id: tc.id,
1190
+ name: tc.function.name,
1191
+ args: JSON.parse(tc.function.arguments || "{}")
1192
+ })
1193
+ );
1194
+ return {
1195
+ text: message.content ?? "",
1196
+ toolCalls,
1197
+ finishReason: mapFinishReason(choice.finish_reason),
1198
+ usage: {
1199
+ promptTokens: response.usage?.prompt_tokens ?? 0,
1200
+ completionTokens: response.usage?.completion_tokens ?? 0,
1201
+ totalTokens: response.usage?.total_tokens ?? 0
1202
+ },
1203
+ rawResponse: response
1204
+ };
1205
+ },
1206
+ async *doStream(params) {
1207
+ throwIfAborted(params.signal);
1208
+ if (useResponses(params)) {
1209
+ const calls2 = /* @__PURE__ */ new Map();
1210
+ let hasTools = false;
1211
+ for await (const event of responsesAdapter.stream(
1212
+ responsesRequest(params)
1213
+ )) {
1214
+ switch (event.type) {
1215
+ case "message:delta":
1216
+ yield { type: "text-delta", text: event.content };
1217
+ break;
1218
+ case "action:start":
1219
+ calls2.set(event.id, { name: event.name, args: "" });
1220
+ break;
1221
+ case "action:args": {
1222
+ const call = calls2.get(event.id);
1223
+ if (call) call.args += event.args;
1224
+ break;
1225
+ }
1226
+ case "action:end": {
1227
+ const call = calls2.get(event.id);
1228
+ if (!call)
1229
+ throw new Error("Responses tool call has no matching start");
1230
+ hasTools = true;
1231
+ yield {
1232
+ type: "tool-call",
1233
+ toolCall: {
1234
+ id: event.id,
1235
+ name: call.name,
1236
+ args: JSON.parse(call.args || "{}")
1237
+ }
1238
+ };
1239
+ calls2.delete(event.id);
1240
+ break;
1241
+ }
1242
+ case "error":
1243
+ throw errorFromEvent(event);
1244
+ case "done":
1245
+ yield {
1246
+ type: "finish",
1247
+ finishReason: hasTools ? "tool-calls" : "stop",
1248
+ usage: {
1249
+ promptTokens: event.usage?.prompt_tokens ?? 0,
1250
+ completionTokens: event.usage?.completion_tokens ?? 0,
1251
+ totalTokens: event.usage?.total_tokens ?? 0
1252
+ }
1253
+ };
1254
+ break;
1255
+ }
1256
+ }
1257
+ return;
1258
+ }
1259
+ const client2 = await getClient();
1260
+ const messages = formatMessagesForOpenAI2(params.messages);
1261
+ const stream = await client2.chat.completions.create(
1262
+ {
1263
+ model: modelId,
1264
+ messages,
1265
+ tools: params.tools,
1266
+ ...buildOpenAIParameters(
1267
+ modelId,
1268
+ params.maxTokens,
1269
+ params.temperature,
1270
+ parameterOptions(params)
1271
+ ),
1272
+ response_format: toOpenAIResponseFormat(params.responseFormat),
1273
+ stream: true,
1274
+ stream_options: { include_usage: true }
1275
+ },
1276
+ { signal: params.signal }
1277
+ );
1278
+ const toolCalls = /* @__PURE__ */ new Map();
1279
+ let usage = { promptTokens: 0, completionTokens: 0, totalTokens: 0 };
1280
+ let finishReason;
1281
+ for await (const chunk of stream) {
1282
+ throwIfAborted(params.signal);
1283
+ if (chunk.usage) {
1284
+ usage = {
1285
+ promptTokens: chunk.usage.prompt_tokens ?? 0,
1286
+ completionTokens: chunk.usage.completion_tokens ?? 0,
1287
+ totalTokens: chunk.usage.total_tokens ?? (chunk.usage.prompt_tokens ?? 0) + (chunk.usage.completion_tokens ?? 0)
1288
+ };
1289
+ }
1290
+ const choice = chunk.choices?.[0];
1291
+ if (choice?.delta?.content)
1292
+ yield { type: "text-delta", text: choice.delta.content };
1293
+ for (const part of choice?.delta?.tool_calls ?? []) {
1294
+ const index = part.index ?? 0;
1295
+ const call = toolCalls.get(index) ?? {
1296
+ id: "",
1297
+ name: "",
1298
+ arguments: ""
1299
+ };
1300
+ if (part.id) call.id = part.id;
1301
+ if (part.function?.name) call.name += part.function.name;
1302
+ if (part.function?.arguments)
1303
+ call.arguments += part.function.arguments;
1304
+ toolCalls.set(index, call);
1305
+ }
1306
+ if (choice?.finish_reason)
1307
+ finishReason = mapFinishReason(choice.finish_reason);
1308
+ }
1309
+ throwIfAborted(params.signal);
1310
+ if (!finishReason)
1311
+ throw new Error("OpenAI stream ended without a finish reason");
1312
+ const calls = [...toolCalls.values()].map((call) => {
1313
+ if (!call.id || !call.name)
1314
+ throw new Error("OpenAI stream contains an incomplete tool call");
1315
+ return {
1316
+ id: call.id,
1317
+ name: call.name,
1318
+ args: JSON.parse(call.arguments || "{}")
1319
+ };
1320
+ });
1321
+ if (calls.length && finishReason !== "tool-calls")
1322
+ throw new Error("OpenAI stream ended with incomplete tool calls");
1323
+ for (const toolCall of calls) yield { type: "tool-call", toolCall };
1324
+ yield { type: "finish", finishReason, usage };
1325
+ }
1326
+ };
1327
+ }
1328
+ function mapFinishReason(reason) {
1329
+ switch (reason) {
1330
+ case "stop":
1331
+ return "stop";
1332
+ case "length":
1333
+ return "length";
1334
+ case "tool_calls":
1335
+ case "function_call":
1336
+ return "tool-calls";
1337
+ case "content_filter":
1338
+ return "content-filter";
1339
+ default:
1340
+ return "unknown";
1341
+ }
1342
+ }
1343
+ function formatMessagesForOpenAI2(messages) {
1344
+ return messages.map((msg) => {
1345
+ switch (msg.role) {
1346
+ case "system":
1347
+ return { role: "system", content: msg.content };
1348
+ case "user":
1349
+ if (typeof msg.content === "string") {
1350
+ return { role: "user", content: msg.content };
1351
+ }
1352
+ return {
1353
+ role: "user",
1354
+ content: msg.content.map((part) => {
1355
+ if (part.type === "text") {
1356
+ return { type: "text", text: part.text };
1357
+ }
1358
+ if (part.type === "image") {
1359
+ const imageData = typeof part.image === "string" ? part.image : Buffer.from(part.image).toString("base64");
1360
+ const url = imageData.startsWith("data:") ? imageData : `data:${part.mimeType ?? "image/png"};base64,${imageData}`;
1361
+ return { type: "image_url", image_url: { url, detail: "auto" } };
1362
+ }
1363
+ return { type: "text", text: "" };
1364
+ })
1365
+ };
1366
+ case "assistant":
1367
+ const assistantMsg = {
1368
+ role: "assistant",
1369
+ content: msg.content
1370
+ };
1371
+ if (msg.toolCalls && msg.toolCalls.length > 0) {
1372
+ assistantMsg.tool_calls = msg.toolCalls.map((tc) => ({
1373
+ id: tc.id,
1374
+ type: "function",
1375
+ function: {
1376
+ name: tc.name,
1377
+ arguments: JSON.stringify(tc.args)
1378
+ }
1379
+ }));
1380
+ }
1381
+ return assistantMsg;
1382
+ case "tool":
1383
+ return {
1384
+ role: "tool",
1385
+ tool_call_id: msg.toolCallId,
1386
+ content: msg.content
1387
+ };
1388
+ default:
1389
+ return msg;
1390
+ }
1391
+ });
1392
+ }
1393
+
1191
1394
  // src/providers/types.ts
1192
1395
  function createCallableProvider(providerFn, properties) {
1193
1396
  Object.defineProperty(providerFn, "name", {
@@ -1318,7 +1521,10 @@ function createOpenAI(config = {}) {
1318
1521
  return createOpenAIAdapter({
1319
1522
  apiKey,
1320
1523
  model: modelId,
1321
- baseUrl: config.baseUrl
1524
+ baseUrl: config.baseUrl,
1525
+ reasoningEffort: config.reasoningEffort,
1526
+ parameterPolicy: config.parameterPolicy,
1527
+ onWarning: config.onWarning
1322
1528
  });
1323
1529
  };
1324
1530
  const getCapabilities = (modelId) => {