@openai/agents-extensions 0.0.16 → 0.1.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.
package/dist/aiSdk.js CHANGED
@@ -1,14 +1,22 @@
1
- import { createGenerationSpan, resetCurrentSpan, setCurrentSpan, Usage, UserError, withGenerationSpan, getLogger, } from '@openai/agents';
2
- import { isZodObject } from '@openai/agents/utils';
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AiSdkModel = void 0;
4
+ exports.itemsToLanguageV2Messages = itemsToLanguageV2Messages;
5
+ exports.toolToLanguageV2Tool = toolToLanguageV2Tool;
6
+ exports.getResponseFormat = getResponseFormat;
7
+ exports.aisdk = aisdk;
8
+ exports.parseArguments = parseArguments;
9
+ const agents_1 = require("@openai/agents");
10
+ const utils_1 = require("@openai/agents/utils");
3
11
  /**
4
12
  * @internal
5
- * Converts a list of model items to a list of language model v1 messages.
13
+ * Converts a list of model items to a list of language model V2 messages.
6
14
  *
7
15
  * @param model - The model to use.
8
16
  * @param items - The items to convert.
9
- * @returns The list of language model v1 messages.
17
+ * @returns The list of language model V2 messages.
10
18
  */
11
- export function itemsToLanguageV1Messages(model, items) {
19
+ function itemsToLanguageV2Messages(model, items) {
12
20
  const messages = [];
13
21
  let currentAssistantMessage;
14
22
  for (const item of items) {
@@ -18,7 +26,7 @@ export function itemsToLanguageV1Messages(model, items) {
18
26
  messages.push({
19
27
  role: 'system',
20
28
  content: content,
21
- providerMetadata: {
29
+ providerOptions: {
22
30
  ...(providerData ?? {}),
23
31
  },
24
32
  });
@@ -35,7 +43,7 @@ export function itemsToLanguageV1Messages(model, items) {
35
43
  return {
36
44
  type: 'text',
37
45
  text: c.text,
38
- providerMetadata: {
46
+ providerOptions: {
39
47
  ...(contentProviderData ?? {}),
40
48
  },
41
49
  };
@@ -43,30 +51,31 @@ export function itemsToLanguageV1Messages(model, items) {
43
51
  if (c.type === 'input_image') {
44
52
  const url = new URL(c.image);
45
53
  return {
46
- type: 'image',
47
- image: url,
48
- providerMetadata: {
54
+ type: 'file',
55
+ data: url,
56
+ mediaType: 'image/*',
57
+ providerOptions: {
49
58
  ...(contentProviderData ?? {}),
50
59
  },
51
60
  };
52
61
  }
53
62
  if (c.type === 'input_file') {
54
63
  if (typeof c.file !== 'string') {
55
- throw new UserError('File ID is not supported');
64
+ throw new agents_1.UserError('File ID is not supported');
56
65
  }
57
66
  return {
58
67
  type: 'file',
59
68
  file: c.file,
60
- mimeType: 'application/octet-stream',
69
+ mediaType: 'application/octet-stream',
61
70
  data: c.file,
62
- providerMetadata: {
71
+ providerOptions: {
63
72
  ...(contentProviderData ?? {}),
64
73
  },
65
74
  };
66
75
  }
67
- throw new UserError(`Unknown content type: ${c.type}`);
76
+ throw new agents_1.UserError(`Unknown content type: ${c.type}`);
68
77
  }),
69
- providerMetadata: {
78
+ providerOptions: {
70
79
  ...(providerData ?? {}),
71
80
  },
72
81
  });
@@ -86,12 +95,12 @@ export function itemsToLanguageV1Messages(model, items) {
86
95
  return {
87
96
  type: 'text',
88
97
  text: c.text,
89
- providerMetadata: {
98
+ providerOptions: {
90
99
  ...(contentProviderData ?? {}),
91
100
  },
92
101
  };
93
102
  }),
94
- providerMetadata: {
103
+ providerOptions: {
95
104
  ...(providerData ?? {}),
96
105
  },
97
106
  });
@@ -105,7 +114,7 @@ export function itemsToLanguageV1Messages(model, items) {
105
114
  currentAssistantMessage = {
106
115
  role: 'assistant',
107
116
  content: [],
108
- providerMetadata: {
117
+ providerOptions: {
109
118
  ...(item.providerData ?? {}),
110
119
  },
111
120
  };
@@ -116,8 +125,8 @@ export function itemsToLanguageV1Messages(model, items) {
116
125
  type: 'tool-call',
117
126
  toolCallId: item.callId,
118
127
  toolName: item.name,
119
- args: parseArguments(item.arguments),
120
- providerMetadata: {
128
+ input: parseArguments(item.arguments),
129
+ providerOptions: {
121
130
  ...(item.providerData ?? {}),
122
131
  },
123
132
  };
@@ -134,28 +143,28 @@ export function itemsToLanguageV1Messages(model, items) {
134
143
  type: 'tool-result',
135
144
  toolCallId: item.callId,
136
145
  toolName: item.name,
137
- result: item.output,
138
- providerMetadata: {
146
+ output: convertToAiSdkOutput(item.output),
147
+ providerOptions: {
139
148
  ...(item.providerData ?? {}),
140
149
  },
141
150
  };
142
151
  messages.push({
143
152
  role: 'tool',
144
153
  content: [toolResult],
145
- providerMetadata: {
154
+ providerOptions: {
146
155
  ...(item.providerData ?? {}),
147
156
  },
148
157
  });
149
158
  continue;
150
159
  }
151
160
  if (item.type === 'hosted_tool_call') {
152
- throw new UserError('Hosted tool calls are not supported');
161
+ throw new agents_1.UserError('Hosted tool calls are not supported');
153
162
  }
154
163
  if (item.type === 'computer_call') {
155
- throw new UserError('Computer calls are not supported');
164
+ throw new agents_1.UserError('Computer calls are not supported');
156
165
  }
157
166
  if (item.type === 'computer_call_result') {
158
- throw new UserError('Computer call results are not supported');
167
+ throw new agents_1.UserError('Computer call results are not supported');
159
168
  }
160
169
  if (item.type === 'reasoning' &&
161
170
  item.content.length > 0 &&
@@ -166,10 +175,10 @@ export function itemsToLanguageV1Messages(model, items) {
166
175
  {
167
176
  type: 'reasoning',
168
177
  text: item.content[0].text,
169
- providerMetadata: { ...(item.providerData ?? {}) },
178
+ providerOptions: { ...(item.providerData ?? {}) },
170
179
  },
171
180
  ],
172
- providerMetadata: {
181
+ providerOptions: {
173
182
  ...(item.providerData ?? {}),
174
183
  },
175
184
  });
@@ -180,10 +189,10 @@ export function itemsToLanguageV1Messages(model, items) {
180
189
  continue;
181
190
  }
182
191
  if (item) {
183
- throw new UserError(`Unknown item type: ${item.type}`);
192
+ throw new agents_1.UserError(`Unknown item type: ${item.type}`);
184
193
  }
185
194
  const itemType = item;
186
- throw new UserError(`Unknown item type: ${itemType}`);
195
+ throw new agents_1.UserError(`Unknown item type: ${itemType}`);
187
196
  }
188
197
  if (currentAssistantMessage) {
189
198
  messages.push(currentAssistantMessage);
@@ -192,33 +201,54 @@ export function itemsToLanguageV1Messages(model, items) {
192
201
  }
193
202
  /**
194
203
  * @internal
195
- * Converts a handoff to a language model v1 tool.
204
+ * Converts a handoff to a language model V2 tool.
196
205
  *
197
206
  * @param model - The model to use.
198
207
  * @param handoff - The handoff to convert.
199
208
  */
200
- function handoffToLanguageV1Tool(model, handoff) {
209
+ function handoffToLanguageV2Tool(model, handoff) {
201
210
  return {
202
211
  type: 'function',
203
212
  name: handoff.toolName,
204
213
  description: handoff.toolDescription,
205
- parameters: handoff.inputJsonSchema,
214
+ inputSchema: handoff.inputJsonSchema,
206
215
  };
207
216
  }
217
+ function convertToAiSdkOutput(output) {
218
+ const anyOutput = output;
219
+ if (anyOutput?.type === 'text' && typeof anyOutput.text === 'string') {
220
+ return { type: 'text', value: anyOutput.text };
221
+ }
222
+ if (anyOutput?.type === 'image' &&
223
+ typeof anyOutput.data === 'string' &&
224
+ typeof anyOutput.mediaType === 'string') {
225
+ return {
226
+ type: 'content',
227
+ value: [
228
+ {
229
+ type: 'media',
230
+ data: anyOutput.data,
231
+ mediaType: anyOutput.mediaType,
232
+ },
233
+ ],
234
+ };
235
+ }
236
+ throw new agents_1.UserError(`Unsupported tool output type: ${String(anyOutput?.type)}`);
237
+ }
208
238
  /**
209
239
  * @internal
210
- * Converts a tool to a language model v1 tool.
240
+ * Converts a tool to a language model V2 tool.
211
241
  *
212
242
  * @param model - The model to use.
213
243
  * @param tool - The tool to convert.
214
244
  */
215
- export function toolToLanguageV1Tool(model, tool) {
245
+ function toolToLanguageV2Tool(model, tool) {
216
246
  if (tool.type === 'function') {
217
247
  return {
218
248
  type: 'function',
219
249
  name: tool.name,
220
250
  description: tool.description,
221
- parameters: tool.parameters,
251
+ inputSchema: tool.parameters,
222
252
  };
223
253
  }
224
254
  if (tool.type === 'hosted_tool') {
@@ -246,12 +276,12 @@ export function toolToLanguageV1Tool(model, tool) {
246
276
  }
247
277
  /**
248
278
  * @internal
249
- * Converts an output type to a language model v1 response format.
279
+ * Converts an output type to a language model V2 response format.
250
280
  *
251
281
  * @param outputType - The output type to convert.
252
- * @returns The language model v1 response format.
282
+ * @returns The language model V2 response format.
253
283
  */
254
- export function getResponseFormat(outputType) {
284
+ function getResponseFormat(outputType) {
255
285
  if (outputType === 'text') {
256
286
  return {
257
287
  type: 'text',
@@ -264,7 +294,7 @@ export function getResponseFormat(outputType) {
264
294
  };
265
295
  }
266
296
  /**
267
- * Wraps a model from the AI SDK that adheres to the LanguageModelV1 spec to be used used as a model
297
+ * Wraps a model from the AI SDK that adheres to the LanguageModelV2 spec to be used used as a model
268
298
  * in the OpenAI Agents SDK to use other models.
269
299
  *
270
300
  * While you can use this with the OpenAI models, it is recommended to use the default OpenAI model
@@ -287,14 +317,14 @@ export function getResponseFormat(outputType) {
287
317
  * @param model - The Vercel AI SDK model to wrap.
288
318
  * @returns The wrapped model.
289
319
  */
290
- export class AiSdkModel {
320
+ class AiSdkModel {
291
321
  #model;
292
- #logger = getLogger('openai-agents:extensions:ai-sdk');
322
+ #logger = (0, agents_1.getLogger)('openai-agents:extensions:ai-sdk');
293
323
  constructor(model) {
294
324
  this.#model = model;
295
325
  }
296
326
  async getResponse(request) {
297
- return withGenerationSpan(async (span) => {
327
+ return (0, agents_1.withGenerationSpan)(async (span) => {
298
328
  try {
299
329
  span.spanData.model = this.#model.provider + ':' + this.#model.modelId;
300
330
  span.spanData.model_config = {
@@ -308,7 +338,7 @@ export class AiSdkModel {
308
338
  content: [{ type: 'text', text: request.input }],
309
339
  },
310
340
  ]
311
- : itemsToLanguageV1Messages(this.#model, request.input);
341
+ : itemsToLanguageV2Messages(this.#model, request.input);
312
342
  if (request.systemInstructions) {
313
343
  input = [
314
344
  {
@@ -318,29 +348,25 @@ export class AiSdkModel {
318
348
  ...input,
319
349
  ];
320
350
  }
321
- const tools = request.tools.map((tool) => toolToLanguageV1Tool(this.#model, tool));
351
+ const tools = request.tools.map((tool) => toolToLanguageV2Tool(this.#model, tool));
322
352
  request.handoffs.forEach((handoff) => {
323
- tools.push(handoffToLanguageV1Tool(this.#model, handoff));
353
+ tools.push(handoffToLanguageV2Tool(this.#model, handoff));
324
354
  });
325
355
  if (span && request.tracing === true) {
326
356
  span.spanData.input = input;
327
357
  }
328
- if (isZodObject(request.outputType)) {
329
- throw new UserError('Zod output type is not yet supported');
358
+ if ((0, utils_1.isZodObject)(request.outputType)) {
359
+ throw new agents_1.UserError('Zod output type is not yet supported');
330
360
  }
331
361
  const responseFormat = getResponseFormat(request.outputType);
332
362
  const aiSdkRequest = {
333
- inputFormat: 'messages',
334
- mode: {
335
- type: 'regular',
336
- tools,
337
- },
363
+ tools,
338
364
  prompt: input,
339
365
  temperature: request.modelSettings.temperature,
340
366
  topP: request.modelSettings.topP,
341
367
  frequencyPenalty: request.modelSettings.frequencyPenalty,
342
368
  presencePenalty: request.modelSettings.presencePenalty,
343
- maxTokens: request.modelSettings.maxTokens,
369
+ maxOutputTokens: request.modelSettings.maxTokens,
344
370
  responseFormat,
345
371
  abortSignal: request.signal,
346
372
  ...(request.modelSettings.providerData ?? {}),
@@ -349,52 +375,59 @@ export class AiSdkModel {
349
375
  this.#logger.debug('Request sent');
350
376
  }
351
377
  else {
352
- this.#logger.debug('Request:', aiSdkRequest);
378
+ this.#logger.debug('Request:', JSON.stringify(aiSdkRequest, null, 2));
353
379
  }
354
380
  const result = await this.#model.doGenerate(aiSdkRequest);
355
381
  const output = [];
356
- result.toolCalls?.forEach((toolCall) => {
382
+ const resultContent = result.content ?? [];
383
+ const toolCalls = resultContent.filter((c) => c && c.type === 'tool-call');
384
+ const hasToolCalls = toolCalls.length > 0;
385
+ for (const toolCall of toolCalls) {
357
386
  output.push({
358
387
  type: 'function_call',
359
388
  callId: toolCall.toolCallId,
360
389
  name: toolCall.toolName,
361
- arguments: toolCall.args,
390
+ arguments: typeof toolCall.input === 'string'
391
+ ? toolCall.input
392
+ : JSON.stringify(toolCall.input ?? {}),
362
393
  status: 'completed',
363
- providerData: !result.text ? result.providerMetadata : undefined,
394
+ providerData: hasToolCalls ? result.providerMetadata : undefined,
364
395
  });
365
- });
396
+ }
366
397
  // Some of other platforms may return both tool calls and text.
367
398
  // Putting a text message here will let the agent loop to complete,
368
399
  // so adding this item only when the tool calls are empty.
369
400
  // Note that the same support is not available for streaming mode.
370
- if ((!result.toolCalls || result.toolCalls.length === 0) &&
371
- result.text) {
372
- output.push({
373
- type: 'message',
374
- content: [{ type: 'output_text', text: result.text }],
375
- role: 'assistant',
376
- status: 'completed',
377
- providerData: result.providerMetadata,
378
- });
401
+ if (!hasToolCalls) {
402
+ const textItem = resultContent.find((c) => c && c.type === 'text' && typeof c.text === 'string');
403
+ if (textItem) {
404
+ output.push({
405
+ type: 'message',
406
+ content: [{ type: 'output_text', text: textItem.text }],
407
+ role: 'assistant',
408
+ status: 'completed',
409
+ providerData: result.providerMetadata,
410
+ });
411
+ }
379
412
  }
380
413
  if (span && request.tracing === true) {
381
414
  span.spanData.output = output;
382
415
  }
383
416
  const response = {
384
417
  responseId: result.response?.id ?? 'FAKE_ID',
385
- usage: new Usage({
386
- inputTokens: Number.isNaN(result.usage?.promptTokens)
418
+ usage: new agents_1.Usage({
419
+ inputTokens: Number.isNaN(result.usage?.inputTokens)
387
420
  ? 0
388
- : (result.usage?.promptTokens ?? 0),
389
- outputTokens: Number.isNaN(result.usage?.completionTokens)
421
+ : (result.usage?.inputTokens ?? 0),
422
+ outputTokens: Number.isNaN(result.usage?.outputTokens)
390
423
  ? 0
391
- : (result.usage?.completionTokens ?? 0),
392
- totalTokens: (Number.isNaN(result.usage?.promptTokens)
424
+ : (result.usage?.outputTokens ?? 0),
425
+ totalTokens: (Number.isNaN(result.usage?.inputTokens)
393
426
  ? 0
394
- : (result.usage?.promptTokens ?? 0)) +
395
- (Number.isNaN(result.usage?.completionTokens)
427
+ : (result.usage?.inputTokens ?? 0)) +
428
+ (Number.isNaN(result.usage?.outputTokens)
396
429
  ? 0
397
- : (result.usage?.completionTokens ?? 0)),
430
+ : (result.usage?.outputTokens ?? 0)) || 0,
398
431
  }),
399
432
  output,
400
433
  providerData: result,
@@ -411,7 +444,7 @@ export class AiSdkModel {
411
444
  this.#logger.debug('Response ready');
412
445
  }
413
446
  else {
414
- this.#logger.debug('Response:', response);
447
+ this.#logger.debug('Response:', JSON.stringify(response, null, 2));
415
448
  }
416
449
  return response;
417
450
  }
@@ -445,11 +478,11 @@ export class AiSdkModel {
445
478
  });
446
479
  }
447
480
  async *getStreamedResponse(request) {
448
- const span = request.tracing ? createGenerationSpan() : undefined;
481
+ const span = request.tracing ? (0, agents_1.createGenerationSpan)() : undefined;
449
482
  try {
450
483
  if (span) {
451
484
  span.start();
452
- setCurrentSpan(span);
485
+ (0, agents_1.setCurrentSpan)(span);
453
486
  }
454
487
  if (span?.spanData) {
455
488
  span.spanData.model = this.#model.provider + ':' + this.#model.modelId;
@@ -465,7 +498,7 @@ export class AiSdkModel {
465
498
  content: [{ type: 'text', text: request.input }],
466
499
  },
467
500
  ]
468
- : itemsToLanguageV1Messages(this.#model, request.input);
501
+ : itemsToLanguageV2Messages(this.#model, request.input);
469
502
  if (request.systemInstructions) {
470
503
  input = [
471
504
  {
@@ -475,26 +508,22 @@ export class AiSdkModel {
475
508
  ...input,
476
509
  ];
477
510
  }
478
- const tools = request.tools.map((tool) => toolToLanguageV1Tool(this.#model, tool));
511
+ const tools = request.tools.map((tool) => toolToLanguageV2Tool(this.#model, tool));
479
512
  request.handoffs.forEach((handoff) => {
480
- tools.push(handoffToLanguageV1Tool(this.#model, handoff));
513
+ tools.push(handoffToLanguageV2Tool(this.#model, handoff));
481
514
  });
482
515
  if (span && request.tracing === true) {
483
516
  span.spanData.input = input;
484
517
  }
485
518
  const responseFormat = getResponseFormat(request.outputType);
486
519
  const aiSdkRequest = {
487
- inputFormat: 'messages',
488
- mode: {
489
- type: 'regular',
490
- tools,
491
- },
520
+ tools,
492
521
  prompt: input,
493
522
  temperature: request.modelSettings.temperature,
494
523
  topP: request.modelSettings.topP,
495
524
  frequencyPenalty: request.modelSettings.frequencyPenalty,
496
525
  presencePenalty: request.modelSettings.presencePenalty,
497
- maxTokens: request.modelSettings.maxTokens,
526
+ maxOutputTokens: request.modelSettings.maxTokens,
498
527
  responseFormat,
499
528
  abortSignal: request.signal,
500
529
  ...(request.modelSettings.providerData ?? {}),
@@ -503,7 +532,7 @@ export class AiSdkModel {
503
532
  this.#logger.debug('Request received (streamed)');
504
533
  }
505
534
  else {
506
- this.#logger.debug('Request (streamed):', aiSdkRequest);
535
+ this.#logger.debug('Request (streamed):', JSON.stringify(aiSdkRequest, null, 2));
507
536
  }
508
537
  const { stream } = await this.#model.doStream(aiSdkRequest);
509
538
  let started = false;
@@ -523,36 +552,23 @@ export class AiSdkModel {
523
552
  if (!textOutput) {
524
553
  textOutput = { type: 'output_text', text: '' };
525
554
  }
526
- textOutput.text += part.textDelta;
527
- yield { type: 'output_text_delta', delta: part.textDelta };
555
+ textOutput.text += part.delta;
556
+ yield { type: 'output_text_delta', delta: part.delta };
528
557
  break;
529
558
  }
530
559
  case 'tool-call': {
531
- if (part.toolCallType === 'function') {
532
- functionCalls[part.toolCallId] = {
560
+ const toolCallId = part.toolCallId;
561
+ if (toolCallId) {
562
+ functionCalls[toolCallId] = {
533
563
  type: 'function_call',
534
- callId: part.toolCallId,
564
+ callId: toolCallId,
535
565
  name: part.toolName,
536
- arguments: part.args,
566
+ arguments: part.input ?? '',
537
567
  status: 'completed',
538
568
  };
539
569
  }
540
570
  break;
541
571
  }
542
- case 'tool-call-delta': {
543
- if (part.toolCallType === 'function') {
544
- const fc = functionCalls[part.toolCallId] ?? {
545
- type: 'function_call',
546
- callId: part.toolCallId,
547
- name: '',
548
- arguments: '',
549
- };
550
- fc.name += part.toolName;
551
- fc.arguments += part.argsTextDelta;
552
- functionCalls[part.toolCallId] = fc;
553
- }
554
- break;
555
- }
556
572
  case 'response-metadata': {
557
573
  if (part.id) {
558
574
  responseId = part.id;
@@ -560,12 +576,12 @@ export class AiSdkModel {
560
576
  break;
561
577
  }
562
578
  case 'finish': {
563
- usagePromptTokens = Number.isNaN(part.usage?.promptTokens)
579
+ usagePromptTokens = Number.isNaN(part.usage?.inputTokens)
564
580
  ? 0
565
- : (part.usage?.promptTokens ?? 0);
566
- usageCompletionTokens = Number.isNaN(part.usage?.completionTokens)
581
+ : (part.usage?.inputTokens ?? 0);
582
+ usageCompletionTokens = Number.isNaN(part.usage?.outputTokens)
567
583
  ? 0
568
- : (part.usage?.completionTokens ?? 0);
584
+ : (part.usage?.outputTokens ?? 0);
569
585
  break;
570
586
  }
571
587
  case 'error': {
@@ -612,7 +628,7 @@ export class AiSdkModel {
612
628
  this.#logger.debug('Response ready (streamed)');
613
629
  }
614
630
  else {
615
- this.#logger.debug('Response (streamed):', finalEvent.response);
631
+ this.#logger.debug('Response (streamed):', JSON.stringify(finalEvent.response, null, 2));
616
632
  }
617
633
  yield finalEvent;
618
634
  }
@@ -634,13 +650,14 @@ export class AiSdkModel {
634
650
  finally {
635
651
  if (span) {
636
652
  span.end();
637
- resetCurrentSpan();
653
+ (0, agents_1.resetCurrentSpan)();
638
654
  }
639
655
  }
640
656
  }
641
657
  }
658
+ exports.AiSdkModel = AiSdkModel;
642
659
  /**
643
- * Wraps a model from the AI SDK that adheres to the LanguageModelV1 spec to be used used as a model
660
+ * Wraps a model from the AI SDK that adheres to the LanguageModelV2 spec to be used used as a model
644
661
  * in the OpenAI Agents SDK to use other models.
645
662
  *
646
663
  * While you can use this with the OpenAI models, it is recommended to use the default OpenAI model
@@ -663,10 +680,10 @@ export class AiSdkModel {
663
680
  * @param model - The Vercel AI SDK model to wrap.
664
681
  * @returns The wrapped model.
665
682
  */
666
- export function aisdk(model) {
683
+ function aisdk(model) {
667
684
  return new AiSdkModel(model);
668
685
  }
669
- export function parseArguments(args) {
686
+ function parseArguments(args) {
670
687
  if (!args) {
671
688
  return {};
672
689
  }