@tanstack/openai-base 0.9.9 → 0.9.10
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/esm/adapters/chat-completions-text.js +844 -962
- package/dist/esm/adapters/chat-completions-text.js.map +1 -1
- package/dist/esm/adapters/chat-completions-tool-converter.js +54 -38
- package/dist/esm/adapters/chat-completions-tool-converter.js.map +1 -1
- package/dist/esm/adapters/responses-text.js +1171 -1319
- package/dist/esm/adapters/responses-text.js.map +1 -1
- package/dist/esm/adapters/responses-tool-converter.js +50 -34
- package/dist/esm/adapters/responses-tool-converter.js.map +1 -1
- package/dist/esm/index.js +4 -43
- package/dist/esm/tools/apply-patch-tool.js +20 -13
- package/dist/esm/tools/apply-patch-tool.js.map +1 -1
- package/dist/esm/tools/code-interpreter-tool.js +26 -18
- package/dist/esm/tools/code-interpreter-tool.js.map +1 -1
- package/dist/esm/tools/computer-use-tool.js +26 -19
- package/dist/esm/tools/computer-use-tool.js.map +1 -1
- package/dist/esm/tools/custom-tool.js +23 -21
- package/dist/esm/tools/custom-tool.js.map +1 -1
- package/dist/esm/tools/file-search-tool.js +30 -30
- package/dist/esm/tools/file-search-tool.js.map +1 -1
- package/dist/esm/tools/function-tool.js +44 -31
- package/dist/esm/tools/function-tool.js.map +1 -1
- package/dist/esm/tools/image-generation-tool.js +30 -23
- package/dist/esm/tools/image-generation-tool.js.map +1 -1
- package/dist/esm/tools/local-shell-tool.js +20 -13
- package/dist/esm/tools/local-shell-tool.js.map +1 -1
- package/dist/esm/tools/mcp-tool.js +31 -25
- package/dist/esm/tools/mcp-tool.js.map +1 -1
- package/dist/esm/tools/shell-tool.js +25 -21
- package/dist/esm/tools/shell-tool.js.map +1 -1
- package/dist/esm/tools/tool-converter.js +37 -46
- package/dist/esm/tools/tool-converter.js.map +1 -1
- package/dist/esm/tools/web-search-preview-tool.js +26 -15
- package/dist/esm/tools/web-search-preview-tool.js.map +1 -1
- package/dist/esm/tools/web-search-tool.js +27 -15
- package/dist/esm/tools/web-search-tool.js.map +1 -1
- package/dist/esm/usage.js +88 -76
- package/dist/esm/usage.js.map +1 -1
- package/dist/esm/utils/request-options.js +20 -9
- package/dist/esm/utils/request-options.js.map +1 -1
- package/dist/esm/utils/schema-converter.js +193 -146
- package/dist/esm/utils/schema-converter.js.map +1 -1
- package/package.json +7 -7
- package/src/adapters/responses-text.ts +5 -2
- package/dist/esm/index.js.map +0 -1
|
@@ -1,1324 +1,1176 @@
|
|
|
1
|
+
import { makeStructuredOutputCompatible } from "../utils/schema-converter.js";
|
|
2
|
+
import { buildResponsesUsage } from "../usage.js";
|
|
3
|
+
import { extractRequestOptions } from "../utils/request-options.js";
|
|
4
|
+
import { convertToolsToResponsesFormat } from "./responses-tool-converter.js";
|
|
1
5
|
import { EventType, normalizeSystemPrompts } from "@tanstack/ai";
|
|
2
6
|
import { BaseTextAdapter } from "@tanstack/ai/adapters";
|
|
3
7
|
import { toRunErrorPayload, toRunErrorRawEvent } from "@tanstack/ai/adapter-internals";
|
|
4
8
|
import { generateId } from "@tanstack/ai-utils";
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
...textFormat ?? {}
|
|
1169
|
-
};
|
|
1170
|
-
}
|
|
1171
|
-
/**
|
|
1172
|
-
* The OpenAI Responses API supports `tools` and `text.format: json_schema`
|
|
1173
|
-
* together in a single streaming request (per issue #605). Subclasses
|
|
1174
|
-
* that route to providers without this capability should override.
|
|
1175
|
-
*/
|
|
1176
|
-
supportsCombinedToolsAndSchema() {
|
|
1177
|
-
return true;
|
|
1178
|
-
}
|
|
1179
|
-
/**
|
|
1180
|
-
* Converts ModelMessage[] to Responses API ResponseInput format.
|
|
1181
|
-
* Override this in subclasses for provider-specific message format quirks.
|
|
1182
|
-
*
|
|
1183
|
-
* Key differences from Chat Completions:
|
|
1184
|
-
* - Tool results use `function_call_output` type (not `tool` role)
|
|
1185
|
-
* - Assistant tool calls are `function_call` objects (not nested in `tool_calls`)
|
|
1186
|
-
* - User content uses `input_text`, `input_image`, `input_file` types
|
|
1187
|
-
* - System prompts go in `instructions`, not as messages
|
|
1188
|
-
*/
|
|
1189
|
-
convertMessagesToInput(messages) {
|
|
1190
|
-
const result = [];
|
|
1191
|
-
for (const message of messages) {
|
|
1192
|
-
if (message.role === "tool") {
|
|
1193
|
-
const toolContent = message.content;
|
|
1194
|
-
const output = Array.isArray(toolContent) ? toolContent.map((part) => this.convertContentPartToInput(part)) : typeof toolContent === "string" ? toolContent : JSON.stringify(toolContent);
|
|
1195
|
-
result.push({
|
|
1196
|
-
type: "function_call_output",
|
|
1197
|
-
call_id: message.toolCallId || "",
|
|
1198
|
-
output
|
|
1199
|
-
});
|
|
1200
|
-
continue;
|
|
1201
|
-
}
|
|
1202
|
-
if (message.role === "assistant") {
|
|
1203
|
-
if (message.toolCalls && message.toolCalls.length > 0) {
|
|
1204
|
-
for (const toolCall of message.toolCalls) {
|
|
1205
|
-
const argumentsString = typeof toolCall.function.arguments === "string" ? toolCall.function.arguments : JSON.stringify(toolCall.function.arguments);
|
|
1206
|
-
result.push({
|
|
1207
|
-
type: "function_call",
|
|
1208
|
-
call_id: toolCall.id,
|
|
1209
|
-
name: toolCall.function.name,
|
|
1210
|
-
arguments: argumentsString
|
|
1211
|
-
});
|
|
1212
|
-
}
|
|
1213
|
-
}
|
|
1214
|
-
if (message.content) {
|
|
1215
|
-
const contentStr = this.extractTextContent(message.content);
|
|
1216
|
-
if (contentStr) {
|
|
1217
|
-
result.push({
|
|
1218
|
-
type: "message",
|
|
1219
|
-
role: "assistant",
|
|
1220
|
-
content: contentStr
|
|
1221
|
-
});
|
|
1222
|
-
}
|
|
1223
|
-
}
|
|
1224
|
-
continue;
|
|
1225
|
-
}
|
|
1226
|
-
const contentParts = this.normalizeContent(message.content);
|
|
1227
|
-
const inputContent = [];
|
|
1228
|
-
for (const part of contentParts) {
|
|
1229
|
-
inputContent.push(this.convertContentPartToInput(part));
|
|
1230
|
-
}
|
|
1231
|
-
if (inputContent.length === 0) {
|
|
1232
|
-
throw new Error(
|
|
1233
|
-
`User message for ${this.name} has no content parts. Empty user messages would produce a paid request with no input; provide at least one text/image/audio part or omit the message.`
|
|
1234
|
-
);
|
|
1235
|
-
}
|
|
1236
|
-
result.push({
|
|
1237
|
-
type: "message",
|
|
1238
|
-
role: "user",
|
|
1239
|
-
content: inputContent
|
|
1240
|
-
});
|
|
1241
|
-
}
|
|
1242
|
-
return result;
|
|
1243
|
-
}
|
|
1244
|
-
/**
|
|
1245
|
-
* Converts a ContentPart to Responses API input content item.
|
|
1246
|
-
* Handles text, image, and audio content parts.
|
|
1247
|
-
* Override this in subclasses for additional content types or provider-specific metadata.
|
|
1248
|
-
*/
|
|
1249
|
-
convertContentPartToInput(part) {
|
|
1250
|
-
switch (part.type) {
|
|
1251
|
-
case "text":
|
|
1252
|
-
return {
|
|
1253
|
-
type: "input_text",
|
|
1254
|
-
text: part.content
|
|
1255
|
-
};
|
|
1256
|
-
case "image": {
|
|
1257
|
-
const imageMetadata = part.metadata;
|
|
1258
|
-
if (part.source.type === "url") {
|
|
1259
|
-
return {
|
|
1260
|
-
type: "input_image",
|
|
1261
|
-
image_url: part.source.value,
|
|
1262
|
-
detail: imageMetadata?.detail || "auto"
|
|
1263
|
-
};
|
|
1264
|
-
}
|
|
1265
|
-
const imageValue = part.source.value;
|
|
1266
|
-
const imageMime = part.source.mimeType || "application/octet-stream";
|
|
1267
|
-
const imageUrl = imageValue.startsWith("data:") ? imageValue : `data:${imageMime};base64,${imageValue}`;
|
|
1268
|
-
return {
|
|
1269
|
-
type: "input_image",
|
|
1270
|
-
image_url: imageUrl,
|
|
1271
|
-
detail: imageMetadata?.detail || "auto"
|
|
1272
|
-
};
|
|
1273
|
-
}
|
|
1274
|
-
case "audio": {
|
|
1275
|
-
if (part.source.type === "url") {
|
|
1276
|
-
return {
|
|
1277
|
-
type: "input_file",
|
|
1278
|
-
file_url: part.source.value
|
|
1279
|
-
};
|
|
1280
|
-
}
|
|
1281
|
-
const audioValue = part.source.value;
|
|
1282
|
-
const audioMime = part.source.mimeType || "application/octet-stream";
|
|
1283
|
-
const audioFileData = audioValue.startsWith("data:") ? audioValue : `data:${audioMime};base64,${audioValue}`;
|
|
1284
|
-
return {
|
|
1285
|
-
type: "input_file",
|
|
1286
|
-
file_data: audioFileData
|
|
1287
|
-
};
|
|
1288
|
-
}
|
|
1289
|
-
case "video":
|
|
1290
|
-
case "document":
|
|
1291
|
-
default:
|
|
1292
|
-
throw new Error(`Unsupported content part type: ${part.type}`);
|
|
1293
|
-
}
|
|
1294
|
-
}
|
|
1295
|
-
/**
|
|
1296
|
-
* Normalizes message content to an array of ContentPart.
|
|
1297
|
-
* Handles backward compatibility with string content.
|
|
1298
|
-
*/
|
|
1299
|
-
normalizeContent(content) {
|
|
1300
|
-
if (content === null) {
|
|
1301
|
-
return [];
|
|
1302
|
-
}
|
|
1303
|
-
if (typeof content === "string") {
|
|
1304
|
-
return [{ type: "text", content }];
|
|
1305
|
-
}
|
|
1306
|
-
return content;
|
|
1307
|
-
}
|
|
1308
|
-
/**
|
|
1309
|
-
* Extracts text content from a content value that may be string, null, or ContentPart array.
|
|
1310
|
-
*/
|
|
1311
|
-
extractTextContent(content) {
|
|
1312
|
-
if (content === null) {
|
|
1313
|
-
return "";
|
|
1314
|
-
}
|
|
1315
|
-
if (typeof content === "string") {
|
|
1316
|
-
return content;
|
|
1317
|
-
}
|
|
1318
|
-
return content.filter((p) => p.type === "text").map((p) => p.content).join("");
|
|
1319
|
-
}
|
|
1320
|
-
}
|
|
1321
|
-
export {
|
|
1322
|
-
OpenAIBaseResponsesTextAdapter
|
|
9
|
+
//#region src/adapters/responses-text.ts
|
|
10
|
+
/**
|
|
11
|
+
* Shared implementation of the OpenAI Responses API. Holds the stream-event
|
|
12
|
+
* accumulator + AG-UI lifecycle and calls the OpenAI SDK directly. Subclasses
|
|
13
|
+
* (today: ai-openai) construct an OpenAI client with their provider-specific
|
|
14
|
+
* `baseURL` / headers and pass it in.
|
|
15
|
+
*/
|
|
16
|
+
var OpenAIBaseResponsesTextAdapter = class extends BaseTextAdapter {
|
|
17
|
+
kind = "text";
|
|
18
|
+
name;
|
|
19
|
+
client;
|
|
20
|
+
constructor(model, name, client) {
|
|
21
|
+
super({}, model);
|
|
22
|
+
this.name = name;
|
|
23
|
+
this.client = client;
|
|
24
|
+
}
|
|
25
|
+
async *chatStream(options) {
|
|
26
|
+
const toolCallMetadata = /* @__PURE__ */ new Map();
|
|
27
|
+
const aguiState = {
|
|
28
|
+
runId: options.runId ?? generateId(this.name),
|
|
29
|
+
threadId: options.threadId ?? generateId(this.name),
|
|
30
|
+
messageId: generateId(this.name),
|
|
31
|
+
hasEmittedRunStarted: false
|
|
32
|
+
};
|
|
33
|
+
try {
|
|
34
|
+
const requestParams = this.mapOptionsToRequest(options);
|
|
35
|
+
options.logger.request(`activity=chat provider=${this.name} model=${this.model} messages=${options.messages.length} tools=${options.tools?.length ?? 0} stream=true`, {
|
|
36
|
+
provider: this.name,
|
|
37
|
+
model: this.model
|
|
38
|
+
});
|
|
39
|
+
const response = await this.client.responses.create({
|
|
40
|
+
...requestParams,
|
|
41
|
+
stream: true
|
|
42
|
+
}, extractRequestOptions(options.request));
|
|
43
|
+
yield* this.processStreamChunks(response, toolCallMetadata, options, aguiState);
|
|
44
|
+
} catch (error) {
|
|
45
|
+
const errorPayload = toRunErrorPayload(error, `${this.name}.chatStream failed`);
|
|
46
|
+
const rawEvent = toRunErrorRawEvent(error);
|
|
47
|
+
if (!aguiState.hasEmittedRunStarted) {
|
|
48
|
+
aguiState.hasEmittedRunStarted = true;
|
|
49
|
+
yield {
|
|
50
|
+
type: EventType.RUN_STARTED,
|
|
51
|
+
runId: aguiState.runId,
|
|
52
|
+
threadId: aguiState.threadId,
|
|
53
|
+
model: options.model,
|
|
54
|
+
timestamp: Date.now(),
|
|
55
|
+
parentRunId: options.parentRunId
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
yield {
|
|
59
|
+
type: EventType.RUN_ERROR,
|
|
60
|
+
model: options.model,
|
|
61
|
+
timestamp: Date.now(),
|
|
62
|
+
message: errorPayload.message,
|
|
63
|
+
code: errorPayload.code,
|
|
64
|
+
...rawEvent !== void 0 && { rawEvent },
|
|
65
|
+
error: {
|
|
66
|
+
message: errorPayload.message,
|
|
67
|
+
code: errorPayload.code
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
options.logger.errors(`${this.name}.chatStream fatal`, {
|
|
71
|
+
error: errorPayload,
|
|
72
|
+
source: `${this.name}.chatStream`
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Generate structured output using the provider's native JSON Schema response format.
|
|
78
|
+
* Uses stream: false to get the complete response in one call.
|
|
79
|
+
*
|
|
80
|
+
* OpenAI-compatible Responses APIs have strict requirements for structured output:
|
|
81
|
+
* - All properties must be in the `required` array
|
|
82
|
+
* - Optional fields should have null added to their type union
|
|
83
|
+
* - additionalProperties must be false for all objects
|
|
84
|
+
*
|
|
85
|
+
* The outputSchema is already JSON Schema (converted in the ai layer).
|
|
86
|
+
* We apply provider-specific transformations for structured output compatibility.
|
|
87
|
+
*/
|
|
88
|
+
async structuredOutput(options) {
|
|
89
|
+
const { chatOptions, outputSchema } = options;
|
|
90
|
+
const requestParams = this.mapOptionsToRequest(chatOptions);
|
|
91
|
+
const jsonSchema = this.makeStructuredOutputCompatible(outputSchema, outputSchema.required);
|
|
92
|
+
try {
|
|
93
|
+
const { stream: _stream, stream_options: _streamOptions, ...cleanParams } = requestParams;
|
|
94
|
+
chatOptions.logger.request(`activity=structuredOutput provider=${this.name} model=${this.model} messages=${chatOptions.messages.length}`, {
|
|
95
|
+
provider: this.name,
|
|
96
|
+
model: this.model
|
|
97
|
+
});
|
|
98
|
+
const response = await this.client.responses.create({
|
|
99
|
+
...cleanParams,
|
|
100
|
+
stream: false,
|
|
101
|
+
text: { format: {
|
|
102
|
+
type: "json_schema",
|
|
103
|
+
name: "structured_output",
|
|
104
|
+
schema: jsonSchema,
|
|
105
|
+
strict: true
|
|
106
|
+
} }
|
|
107
|
+
}, extractRequestOptions(chatOptions.request));
|
|
108
|
+
const rawText = this.extractTextFromResponse(response);
|
|
109
|
+
if (rawText.length === 0) throw new Error(`${this.name}.structuredOutput: response contained no content`);
|
|
110
|
+
let parsed;
|
|
111
|
+
try {
|
|
112
|
+
parsed = JSON.parse(rawText);
|
|
113
|
+
} catch {
|
|
114
|
+
throw new Error(`Failed to parse structured output as JSON. Content: ${rawText.slice(0, 200)}${rawText.length > 200 ? "..." : ""}`);
|
|
115
|
+
}
|
|
116
|
+
return {
|
|
117
|
+
data: this.transformStructuredOutput(parsed),
|
|
118
|
+
rawText
|
|
119
|
+
};
|
|
120
|
+
} catch (error) {
|
|
121
|
+
chatOptions.logger.errors(`${this.name}.structuredOutput fatal`, {
|
|
122
|
+
error: toRunErrorPayload(error, `${this.name}.structuredOutput failed`),
|
|
123
|
+
source: `${this.name}.structuredOutput`
|
|
124
|
+
});
|
|
125
|
+
throw error;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Stream structured output via the Responses API: single request with
|
|
130
|
+
* `text.format: json_schema` + `stream: true`. Consumes Responses-API
|
|
131
|
+
* events (`response.output_text.delta`, `response.reasoning_text.delta`,
|
|
132
|
+
* `response.reasoning_summary_text.delta`, `response.refusal.delta`,
|
|
133
|
+
* `response.completed`, `response.failed`) and re-emits the standard AG-UI
|
|
134
|
+
* lifecycle ending with `CUSTOM 'structured-output.complete'`.
|
|
135
|
+
*
|
|
136
|
+
* Tools are stripped (structured output is mutually exclusive with tool
|
|
137
|
+
* calls in this path). Reasoning text is accumulated and surfaced both as
|
|
138
|
+
* REASONING_* lifecycle events during the stream and on the terminal
|
|
139
|
+
* CUSTOM event's `value.reasoning`.
|
|
140
|
+
*/
|
|
141
|
+
async *structuredOutputStream(options) {
|
|
142
|
+
const { chatOptions, outputSchema } = options;
|
|
143
|
+
const requestParams = this.mapOptionsToRequest(chatOptions);
|
|
144
|
+
const jsonSchema = this.makeStructuredOutputCompatible(outputSchema, outputSchema.required);
|
|
145
|
+
const timestamp = Date.now();
|
|
146
|
+
const aguiState = {
|
|
147
|
+
runId: generateId(this.name),
|
|
148
|
+
threadId: chatOptions.threadId ?? generateId(this.name),
|
|
149
|
+
messageId: generateId(this.name),
|
|
150
|
+
timestamp,
|
|
151
|
+
hasEmittedRunStarted: false
|
|
152
|
+
};
|
|
153
|
+
let accumulatedContent = "";
|
|
154
|
+
let accumulatedReasoning = "";
|
|
155
|
+
let hasEmittedTextMessageStart = false;
|
|
156
|
+
let reasoningMessageId;
|
|
157
|
+
let stepId;
|
|
158
|
+
let hasClosedReasoning = false;
|
|
159
|
+
let model = chatOptions.model;
|
|
160
|
+
let usage;
|
|
161
|
+
const closeReasoning = function* () {
|
|
162
|
+
if (reasoningMessageId && !hasClosedReasoning) {
|
|
163
|
+
hasClosedReasoning = true;
|
|
164
|
+
yield {
|
|
165
|
+
type: EventType.REASONING_MESSAGE_END,
|
|
166
|
+
messageId: reasoningMessageId,
|
|
167
|
+
model,
|
|
168
|
+
timestamp
|
|
169
|
+
};
|
|
170
|
+
yield {
|
|
171
|
+
type: EventType.REASONING_END,
|
|
172
|
+
messageId: reasoningMessageId,
|
|
173
|
+
model,
|
|
174
|
+
timestamp
|
|
175
|
+
};
|
|
176
|
+
if (stepId) yield {
|
|
177
|
+
type: EventType.STEP_FINISHED,
|
|
178
|
+
stepName: stepId,
|
|
179
|
+
stepId,
|
|
180
|
+
model,
|
|
181
|
+
timestamp,
|
|
182
|
+
content: accumulatedReasoning
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
}.bind(this);
|
|
186
|
+
const openReasoning = function* () {
|
|
187
|
+
if (reasoningMessageId) return;
|
|
188
|
+
reasoningMessageId = generateId(this.name);
|
|
189
|
+
stepId = generateId(this.name);
|
|
190
|
+
yield {
|
|
191
|
+
type: EventType.REASONING_START,
|
|
192
|
+
messageId: reasoningMessageId,
|
|
193
|
+
model,
|
|
194
|
+
timestamp
|
|
195
|
+
};
|
|
196
|
+
yield {
|
|
197
|
+
type: EventType.REASONING_MESSAGE_START,
|
|
198
|
+
messageId: reasoningMessageId,
|
|
199
|
+
role: "reasoning",
|
|
200
|
+
model,
|
|
201
|
+
timestamp
|
|
202
|
+
};
|
|
203
|
+
yield {
|
|
204
|
+
type: EventType.STEP_STARTED,
|
|
205
|
+
stepName: stepId,
|
|
206
|
+
stepId,
|
|
207
|
+
model,
|
|
208
|
+
timestamp,
|
|
209
|
+
stepType: "thinking"
|
|
210
|
+
};
|
|
211
|
+
}.bind(this);
|
|
212
|
+
try {
|
|
213
|
+
const { tools: _tools, ...cleanParams } = requestParams;
|
|
214
|
+
chatOptions.logger.request(`activity=structuredOutputStream provider=${this.name} model=${this.model} messages=${chatOptions.messages.length}`, {
|
|
215
|
+
provider: this.name,
|
|
216
|
+
model: this.model
|
|
217
|
+
});
|
|
218
|
+
const stream = await this.client.responses.create({
|
|
219
|
+
...cleanParams,
|
|
220
|
+
stream: true,
|
|
221
|
+
text: { format: {
|
|
222
|
+
type: "json_schema",
|
|
223
|
+
name: "structured_output",
|
|
224
|
+
schema: jsonSchema,
|
|
225
|
+
strict: true
|
|
226
|
+
} }
|
|
227
|
+
}, extractRequestOptions(chatOptions.request));
|
|
228
|
+
for await (const chunk of stream) {
|
|
229
|
+
chatOptions.logger.provider(`provider=${this.name} type=${chunk.type}`, {
|
|
230
|
+
provider: this.name,
|
|
231
|
+
type: chunk.type
|
|
232
|
+
});
|
|
233
|
+
if (!aguiState.hasEmittedRunStarted) {
|
|
234
|
+
aguiState.hasEmittedRunStarted = true;
|
|
235
|
+
yield {
|
|
236
|
+
type: EventType.RUN_STARTED,
|
|
237
|
+
runId: aguiState.runId,
|
|
238
|
+
threadId: aguiState.threadId,
|
|
239
|
+
model,
|
|
240
|
+
timestamp,
|
|
241
|
+
parentRunId: chatOptions.parentRunId
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
if (chunk.type === "response.created" || chunk.type === "response.in_progress") {
|
|
245
|
+
const responseModel = chunk.response?.model;
|
|
246
|
+
if (responseModel) model = responseModel;
|
|
247
|
+
continue;
|
|
248
|
+
}
|
|
249
|
+
if (chunk.type === "response.refusal.delta") {
|
|
250
|
+
const delta = typeof chunk.delta === "string" ? chunk.delta : "";
|
|
251
|
+
yield {
|
|
252
|
+
type: EventType.RUN_ERROR,
|
|
253
|
+
runId: aguiState.runId,
|
|
254
|
+
model,
|
|
255
|
+
timestamp,
|
|
256
|
+
message: `Model refused: ${delta}`,
|
|
257
|
+
code: "refusal",
|
|
258
|
+
error: {
|
|
259
|
+
message: `Model refused: ${delta}`,
|
|
260
|
+
code: "refusal"
|
|
261
|
+
}
|
|
262
|
+
};
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
if (chunk.type === "response.reasoning_text.delta" || chunk.type === "response.reasoning_summary_text.delta") {
|
|
266
|
+
const raw = chunk.delta;
|
|
267
|
+
const reasoningDelta = Array.isArray(raw) ? raw.join("") : typeof raw === "string" ? raw : "";
|
|
268
|
+
if (!reasoningDelta) continue;
|
|
269
|
+
yield* openReasoning();
|
|
270
|
+
if (!reasoningMessageId) continue;
|
|
271
|
+
accumulatedReasoning += reasoningDelta;
|
|
272
|
+
yield {
|
|
273
|
+
type: EventType.REASONING_MESSAGE_CONTENT,
|
|
274
|
+
messageId: reasoningMessageId,
|
|
275
|
+
delta: reasoningDelta,
|
|
276
|
+
model,
|
|
277
|
+
timestamp
|
|
278
|
+
};
|
|
279
|
+
continue;
|
|
280
|
+
}
|
|
281
|
+
if (chunk.type === "response.output_text.delta") {
|
|
282
|
+
const raw = chunk.delta;
|
|
283
|
+
const textDelta = Array.isArray(raw) ? raw.join("") : typeof raw === "string" ? raw : "";
|
|
284
|
+
if (!textDelta) continue;
|
|
285
|
+
yield* closeReasoning();
|
|
286
|
+
if (!hasEmittedTextMessageStart) {
|
|
287
|
+
hasEmittedTextMessageStart = true;
|
|
288
|
+
yield {
|
|
289
|
+
type: EventType.TEXT_MESSAGE_START,
|
|
290
|
+
messageId: aguiState.messageId,
|
|
291
|
+
model,
|
|
292
|
+
timestamp,
|
|
293
|
+
role: "assistant"
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
accumulatedContent += textDelta;
|
|
297
|
+
yield {
|
|
298
|
+
type: EventType.TEXT_MESSAGE_CONTENT,
|
|
299
|
+
messageId: aguiState.messageId,
|
|
300
|
+
model,
|
|
301
|
+
timestamp,
|
|
302
|
+
delta: textDelta,
|
|
303
|
+
content: accumulatedContent
|
|
304
|
+
};
|
|
305
|
+
continue;
|
|
306
|
+
}
|
|
307
|
+
if (chunk.type === "response.completed") {
|
|
308
|
+
const response = chunk.response;
|
|
309
|
+
if (response.usage) usage = response.usage;
|
|
310
|
+
if (response.model) model = response.model;
|
|
311
|
+
continue;
|
|
312
|
+
}
|
|
313
|
+
if (chunk.type === "response.failed") {
|
|
314
|
+
const response = chunk.response;
|
|
315
|
+
const message = response?.error?.message || "Responses API stream failed";
|
|
316
|
+
const code = response?.error?.code;
|
|
317
|
+
yield {
|
|
318
|
+
type: EventType.RUN_ERROR,
|
|
319
|
+
runId: aguiState.runId,
|
|
320
|
+
model,
|
|
321
|
+
timestamp,
|
|
322
|
+
message,
|
|
323
|
+
...code !== void 0 && { code },
|
|
324
|
+
error: {
|
|
325
|
+
message,
|
|
326
|
+
...code !== void 0 && { code }
|
|
327
|
+
}
|
|
328
|
+
};
|
|
329
|
+
return;
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
yield* closeReasoning();
|
|
333
|
+
if (hasEmittedTextMessageStart) yield {
|
|
334
|
+
type: EventType.TEXT_MESSAGE_END,
|
|
335
|
+
messageId: aguiState.messageId,
|
|
336
|
+
model,
|
|
337
|
+
timestamp
|
|
338
|
+
};
|
|
339
|
+
if (accumulatedContent.length === 0) {
|
|
340
|
+
yield {
|
|
341
|
+
type: EventType.RUN_ERROR,
|
|
342
|
+
runId: aguiState.runId,
|
|
343
|
+
model,
|
|
344
|
+
timestamp,
|
|
345
|
+
message: `${this.name}.structuredOutputStream: response contained no content`,
|
|
346
|
+
code: "empty-response",
|
|
347
|
+
error: {
|
|
348
|
+
message: `${this.name}.structuredOutputStream: response contained no content`,
|
|
349
|
+
code: "empty-response"
|
|
350
|
+
}
|
|
351
|
+
};
|
|
352
|
+
return;
|
|
353
|
+
}
|
|
354
|
+
let parsed;
|
|
355
|
+
try {
|
|
356
|
+
parsed = JSON.parse(accumulatedContent);
|
|
357
|
+
} catch {
|
|
358
|
+
yield {
|
|
359
|
+
type: EventType.RUN_ERROR,
|
|
360
|
+
runId: aguiState.runId,
|
|
361
|
+
model,
|
|
362
|
+
timestamp,
|
|
363
|
+
message: `Failed to parse structured output as JSON. Content: ${accumulatedContent.slice(0, 200)}${accumulatedContent.length > 200 ? "..." : ""}`,
|
|
364
|
+
code: "parse-error",
|
|
365
|
+
error: {
|
|
366
|
+
message: "Failed to parse structured output as JSON",
|
|
367
|
+
code: "parse-error"
|
|
368
|
+
}
|
|
369
|
+
};
|
|
370
|
+
return;
|
|
371
|
+
}
|
|
372
|
+
const transformed = this.transformStructuredOutput(parsed);
|
|
373
|
+
yield {
|
|
374
|
+
type: EventType.CUSTOM,
|
|
375
|
+
name: "structured-output.complete",
|
|
376
|
+
value: {
|
|
377
|
+
object: transformed,
|
|
378
|
+
raw: accumulatedContent,
|
|
379
|
+
...accumulatedReasoning ? { reasoning: accumulatedReasoning } : {}
|
|
380
|
+
},
|
|
381
|
+
model,
|
|
382
|
+
timestamp
|
|
383
|
+
};
|
|
384
|
+
yield {
|
|
385
|
+
type: EventType.RUN_FINISHED,
|
|
386
|
+
runId: aguiState.runId,
|
|
387
|
+
threadId: aguiState.threadId,
|
|
388
|
+
model,
|
|
389
|
+
timestamp,
|
|
390
|
+
finishReason: "stop",
|
|
391
|
+
...usage && { usage: buildResponsesUsage(usage) }
|
|
392
|
+
};
|
|
393
|
+
} catch (error) {
|
|
394
|
+
if (!aguiState.hasEmittedRunStarted) {
|
|
395
|
+
aguiState.hasEmittedRunStarted = true;
|
|
396
|
+
yield {
|
|
397
|
+
type: EventType.RUN_STARTED,
|
|
398
|
+
runId: aguiState.runId,
|
|
399
|
+
threadId: aguiState.threadId,
|
|
400
|
+
model,
|
|
401
|
+
timestamp,
|
|
402
|
+
parentRunId: chatOptions.parentRunId
|
|
403
|
+
};
|
|
404
|
+
}
|
|
405
|
+
const isAbort = this.isAbortError(error);
|
|
406
|
+
const errorPayload = toRunErrorPayload(error, `${this.name}.structuredOutputStream failed`);
|
|
407
|
+
const resolvedCode = isAbort ? "aborted" : errorPayload.code;
|
|
408
|
+
const rawEvent = isAbort ? void 0 : toRunErrorRawEvent(error);
|
|
409
|
+
yield {
|
|
410
|
+
type: EventType.RUN_ERROR,
|
|
411
|
+
runId: aguiState.runId,
|
|
412
|
+
model,
|
|
413
|
+
timestamp,
|
|
414
|
+
message: errorPayload.message,
|
|
415
|
+
...resolvedCode !== void 0 && { code: resolvedCode },
|
|
416
|
+
...rawEvent !== void 0 && { rawEvent },
|
|
417
|
+
error: {
|
|
418
|
+
message: errorPayload.message,
|
|
419
|
+
...resolvedCode !== void 0 && { code: resolvedCode }
|
|
420
|
+
}
|
|
421
|
+
};
|
|
422
|
+
chatOptions.logger.errors(`${this.name}.structuredOutputStream fatal`, {
|
|
423
|
+
error: errorPayload,
|
|
424
|
+
source: `${this.name}.structuredOutputStream`
|
|
425
|
+
});
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
/**
|
|
429
|
+
* Cross-SDK abort detection for `structuredOutputStream`. Mirrors the
|
|
430
|
+
* Chat Completions base; subclasses with proprietary error types override.
|
|
431
|
+
*/
|
|
432
|
+
isAbortError(error) {
|
|
433
|
+
if (!error || typeof error !== "object") return false;
|
|
434
|
+
const e = error;
|
|
435
|
+
return e.name === "APIUserAbortError" || e.name === "AbortError" || e.code === "ERR_CANCELED";
|
|
436
|
+
}
|
|
437
|
+
/**
|
|
438
|
+
* Applies provider-specific transformations for structured output compatibility.
|
|
439
|
+
* Override this in subclasses to handle provider-specific quirks.
|
|
440
|
+
*/
|
|
441
|
+
makeStructuredOutputCompatible(schema, originalRequired) {
|
|
442
|
+
return makeStructuredOutputCompatible(schema, originalRequired);
|
|
443
|
+
}
|
|
444
|
+
/**
|
|
445
|
+
* Final shaping pass applied to parsed structured-output JSON before it is
|
|
446
|
+
* returned to the caller. Default is a passthrough.
|
|
447
|
+
*
|
|
448
|
+
* Provider `null`s are no longer stripped here: strict-mode null-widening is
|
|
449
|
+
* now undone precisely by the engine (`undoNullWidening`, driven by the
|
|
450
|
+
* schema's null-widening map) the moment the result is captured, so a blind
|
|
451
|
+
* `transformNullsToUndefined` at the adapter would only destroy genuine
|
|
452
|
+
* `.nullable()` nulls. Subclasses may still override to remap or reshape the
|
|
453
|
+
* provider's structured output.
|
|
454
|
+
*/
|
|
455
|
+
transformStructuredOutput(parsed) {
|
|
456
|
+
return parsed;
|
|
457
|
+
}
|
|
458
|
+
/**
|
|
459
|
+
* Extract text content from a non-streaming Responses API response.
|
|
460
|
+
* Override this in subclasses for provider-specific response shapes.
|
|
461
|
+
*/
|
|
462
|
+
extractTextFromResponse(response) {
|
|
463
|
+
let textContent = "";
|
|
464
|
+
let refusal;
|
|
465
|
+
let sawMessageItem = false;
|
|
466
|
+
const observedItemTypes = /* @__PURE__ */ new Set();
|
|
467
|
+
for (const item of response.output) {
|
|
468
|
+
observedItemTypes.add(item.type);
|
|
469
|
+
if (item.type === "message") {
|
|
470
|
+
sawMessageItem = true;
|
|
471
|
+
for (const part of item.content) {
|
|
472
|
+
const partType = part.type;
|
|
473
|
+
if (partType === "output_text") textContent += part.text ?? "";
|
|
474
|
+
else if (partType === "refusal") refusal = part.refusal || refusal || "Refused without explanation";
|
|
475
|
+
else throw new Error(`${this.name}.extractTextFromResponse: unsupported message content part type "${partType}"`);
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
if (!textContent && refusal !== void 0) {
|
|
480
|
+
const err = /* @__PURE__ */ new Error(`Model refused to respond: ${refusal}`);
|
|
481
|
+
err.code = "refusal";
|
|
482
|
+
throw err;
|
|
483
|
+
}
|
|
484
|
+
if (!textContent && response.output.length > 0 && !sawMessageItem) throw new Error(`${this.name}.extractTextFromResponse: response.output contained items of type(s) [${[...observedItemTypes].sort().join(", ")}] but no message text — the model returned a non-text response`);
|
|
485
|
+
return textContent;
|
|
486
|
+
}
|
|
487
|
+
/**
|
|
488
|
+
* Processes streamed chunks from the Responses API and yields AG-UI events.
|
|
489
|
+
* Override this in subclasses to handle provider-specific stream behavior.
|
|
490
|
+
*
|
|
491
|
+
* Handles the following event types:
|
|
492
|
+
* - response.created / response.incomplete / response.failed
|
|
493
|
+
* - response.output_text.delta
|
|
494
|
+
* - response.reasoning_text.delta
|
|
495
|
+
* - response.reasoning_summary_text.delta
|
|
496
|
+
* - response.content_part.added / response.content_part.done
|
|
497
|
+
* - response.output_item.added
|
|
498
|
+
* - response.function_call_arguments.delta / response.function_call_arguments.done
|
|
499
|
+
* - response.completed
|
|
500
|
+
* - error
|
|
501
|
+
*/
|
|
502
|
+
async *processStreamChunks(stream, toolCallMetadata, options, aguiState) {
|
|
503
|
+
let accumulatedContent = "";
|
|
504
|
+
let accumulatedReasoning = "";
|
|
505
|
+
let hasStreamedContentDeltas = false;
|
|
506
|
+
let hasStreamedReasoningDeltas = false;
|
|
507
|
+
let model = options.model;
|
|
508
|
+
let stepId = null;
|
|
509
|
+
let hasEmittedTextMessageStart = false;
|
|
510
|
+
let hasEmittedStepStarted = false;
|
|
511
|
+
let runFinishedEmitted = false;
|
|
512
|
+
try {
|
|
513
|
+
for await (const chunk of stream) {
|
|
514
|
+
options.logger.provider(`provider=${this.name} type=${chunk.type}`, {
|
|
515
|
+
provider: this.name,
|
|
516
|
+
type: chunk.type
|
|
517
|
+
});
|
|
518
|
+
if (!aguiState.hasEmittedRunStarted) {
|
|
519
|
+
aguiState.hasEmittedRunStarted = true;
|
|
520
|
+
yield {
|
|
521
|
+
type: EventType.RUN_STARTED,
|
|
522
|
+
runId: aguiState.runId,
|
|
523
|
+
threadId: aguiState.threadId,
|
|
524
|
+
model: model || options.model,
|
|
525
|
+
timestamp: Date.now(),
|
|
526
|
+
parentRunId: options.parentRunId
|
|
527
|
+
};
|
|
528
|
+
}
|
|
529
|
+
const handleContentPart = (contentPart) => {
|
|
530
|
+
if (contentPart.type === "output_text") {
|
|
531
|
+
accumulatedContent += contentPart.text || "";
|
|
532
|
+
return {
|
|
533
|
+
type: EventType.TEXT_MESSAGE_CONTENT,
|
|
534
|
+
messageId: aguiState.messageId,
|
|
535
|
+
model: model || options.model,
|
|
536
|
+
timestamp: Date.now(),
|
|
537
|
+
delta: contentPart.text || "",
|
|
538
|
+
content: accumulatedContent
|
|
539
|
+
};
|
|
540
|
+
}
|
|
541
|
+
if (contentPart.type === "reasoning_text") {
|
|
542
|
+
accumulatedReasoning += contentPart.text || "";
|
|
543
|
+
if (!stepId) stepId = generateId(this.name);
|
|
544
|
+
return {
|
|
545
|
+
type: EventType.STEP_FINISHED,
|
|
546
|
+
stepName: stepId,
|
|
547
|
+
stepId,
|
|
548
|
+
model: model || options.model,
|
|
549
|
+
timestamp: Date.now(),
|
|
550
|
+
delta: contentPart.text || "",
|
|
551
|
+
content: accumulatedReasoning
|
|
552
|
+
};
|
|
553
|
+
}
|
|
554
|
+
const isRefusal = contentPart.type === "refusal";
|
|
555
|
+
const message = isRefusal ? contentPart.refusal || "Refused without explanation" : `Unsupported response content_part type: ${contentPart.type}`;
|
|
556
|
+
const code = isRefusal ? "refusal" : contentPart.type;
|
|
557
|
+
return {
|
|
558
|
+
type: EventType.RUN_ERROR,
|
|
559
|
+
model: model || options.model,
|
|
560
|
+
timestamp: Date.now(),
|
|
561
|
+
message,
|
|
562
|
+
code,
|
|
563
|
+
error: {
|
|
564
|
+
message,
|
|
565
|
+
code
|
|
566
|
+
}
|
|
567
|
+
};
|
|
568
|
+
};
|
|
569
|
+
if (chunk.type === "response.created" || chunk.type === "response.incomplete" || chunk.type === "response.failed") model = chunk.response.model;
|
|
570
|
+
if (chunk.type === "response.created") {
|
|
571
|
+
hasStreamedContentDeltas = false;
|
|
572
|
+
hasStreamedReasoningDeltas = false;
|
|
573
|
+
hasEmittedTextMessageStart = false;
|
|
574
|
+
hasEmittedStepStarted = false;
|
|
575
|
+
accumulatedContent = "";
|
|
576
|
+
accumulatedReasoning = "";
|
|
577
|
+
}
|
|
578
|
+
if (chunk.type === "response.failed" || chunk.type === "response.incomplete") {
|
|
579
|
+
if (hasEmittedTextMessageStart) {
|
|
580
|
+
yield {
|
|
581
|
+
type: EventType.TEXT_MESSAGE_END,
|
|
582
|
+
messageId: aguiState.messageId,
|
|
583
|
+
model: chunk.response.model,
|
|
584
|
+
timestamp: Date.now()
|
|
585
|
+
};
|
|
586
|
+
hasEmittedTextMessageStart = false;
|
|
587
|
+
}
|
|
588
|
+
const errorMessage = chunk.response.error?.message || chunk.response.incomplete_details?.reason || (chunk.type === "response.failed" ? "Response failed" : "Response ended incomplete");
|
|
589
|
+
const errorCode = chunk.response.error?.code ?? (chunk.response.incomplete_details ? "incomplete" : void 0) ?? void 0;
|
|
590
|
+
yield {
|
|
591
|
+
type: EventType.RUN_ERROR,
|
|
592
|
+
model: chunk.response.model,
|
|
593
|
+
timestamp: Date.now(),
|
|
594
|
+
message: errorMessage,
|
|
595
|
+
...errorCode !== void 0 && { code: errorCode },
|
|
596
|
+
error: {
|
|
597
|
+
message: errorMessage,
|
|
598
|
+
...errorCode !== void 0 && { code: errorCode }
|
|
599
|
+
}
|
|
600
|
+
};
|
|
601
|
+
runFinishedEmitted = true;
|
|
602
|
+
return;
|
|
603
|
+
}
|
|
604
|
+
if (chunk.type === "response.output_text.delta" && chunk.delta) {
|
|
605
|
+
const textDelta = Array.isArray(chunk.delta) ? chunk.delta.join("") : typeof chunk.delta === "string" ? chunk.delta : "";
|
|
606
|
+
if (textDelta) {
|
|
607
|
+
if (!hasEmittedTextMessageStart) {
|
|
608
|
+
hasEmittedTextMessageStart = true;
|
|
609
|
+
yield {
|
|
610
|
+
type: EventType.TEXT_MESSAGE_START,
|
|
611
|
+
messageId: aguiState.messageId,
|
|
612
|
+
model: model || options.model,
|
|
613
|
+
timestamp: Date.now(),
|
|
614
|
+
role: "assistant"
|
|
615
|
+
};
|
|
616
|
+
}
|
|
617
|
+
accumulatedContent += textDelta;
|
|
618
|
+
hasStreamedContentDeltas = true;
|
|
619
|
+
yield {
|
|
620
|
+
type: EventType.TEXT_MESSAGE_CONTENT,
|
|
621
|
+
messageId: aguiState.messageId,
|
|
622
|
+
model: model || options.model,
|
|
623
|
+
timestamp: Date.now(),
|
|
624
|
+
delta: textDelta,
|
|
625
|
+
content: accumulatedContent
|
|
626
|
+
};
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
if (chunk.type === "response.reasoning_text.delta" && chunk.delta) {
|
|
630
|
+
const reasoningDelta = Array.isArray(chunk.delta) ? chunk.delta.join("") : typeof chunk.delta === "string" ? chunk.delta : "";
|
|
631
|
+
if (reasoningDelta) {
|
|
632
|
+
if (!hasEmittedStepStarted) {
|
|
633
|
+
hasEmittedStepStarted = true;
|
|
634
|
+
stepId = generateId(this.name);
|
|
635
|
+
yield {
|
|
636
|
+
type: EventType.STEP_STARTED,
|
|
637
|
+
stepName: stepId,
|
|
638
|
+
stepId,
|
|
639
|
+
model: model || options.model,
|
|
640
|
+
timestamp: Date.now(),
|
|
641
|
+
stepType: "thinking"
|
|
642
|
+
};
|
|
643
|
+
}
|
|
644
|
+
accumulatedReasoning += reasoningDelta;
|
|
645
|
+
hasStreamedReasoningDeltas = true;
|
|
646
|
+
const fallbackStepId = stepId || generateId(this.name);
|
|
647
|
+
yield {
|
|
648
|
+
type: EventType.STEP_FINISHED,
|
|
649
|
+
stepName: fallbackStepId,
|
|
650
|
+
stepId: fallbackStepId,
|
|
651
|
+
model: model || options.model,
|
|
652
|
+
timestamp: Date.now(),
|
|
653
|
+
delta: reasoningDelta,
|
|
654
|
+
content: accumulatedReasoning
|
|
655
|
+
};
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
if (chunk.type === "response.reasoning_summary_text.delta" && chunk.delta) {
|
|
659
|
+
const summaryDelta = typeof chunk.delta === "string" ? chunk.delta : "";
|
|
660
|
+
if (summaryDelta) {
|
|
661
|
+
if (!hasEmittedStepStarted) {
|
|
662
|
+
hasEmittedStepStarted = true;
|
|
663
|
+
stepId = generateId(this.name);
|
|
664
|
+
yield {
|
|
665
|
+
type: EventType.STEP_STARTED,
|
|
666
|
+
stepName: stepId,
|
|
667
|
+
stepId,
|
|
668
|
+
model: model || options.model,
|
|
669
|
+
timestamp: Date.now(),
|
|
670
|
+
stepType: "thinking"
|
|
671
|
+
};
|
|
672
|
+
}
|
|
673
|
+
accumulatedReasoning += summaryDelta;
|
|
674
|
+
hasStreamedReasoningDeltas = true;
|
|
675
|
+
const fallbackStepId = stepId || generateId(this.name);
|
|
676
|
+
yield {
|
|
677
|
+
type: EventType.STEP_FINISHED,
|
|
678
|
+
stepName: fallbackStepId,
|
|
679
|
+
stepId: fallbackStepId,
|
|
680
|
+
model: model || options.model,
|
|
681
|
+
timestamp: Date.now(),
|
|
682
|
+
delta: summaryDelta,
|
|
683
|
+
content: accumulatedReasoning
|
|
684
|
+
};
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
if (chunk.type === "response.content_part.added") {
|
|
688
|
+
const contentPart = chunk.part;
|
|
689
|
+
if (contentPart.type === "output_text" && !hasEmittedTextMessageStart) {
|
|
690
|
+
hasEmittedTextMessageStart = true;
|
|
691
|
+
yield {
|
|
692
|
+
type: EventType.TEXT_MESSAGE_START,
|
|
693
|
+
messageId: aguiState.messageId,
|
|
694
|
+
model: model || options.model,
|
|
695
|
+
timestamp: Date.now(),
|
|
696
|
+
role: "assistant"
|
|
697
|
+
};
|
|
698
|
+
}
|
|
699
|
+
if (contentPart.type === "reasoning_text" && !hasEmittedStepStarted) {
|
|
700
|
+
hasEmittedStepStarted = true;
|
|
701
|
+
stepId = generateId(this.name);
|
|
702
|
+
yield {
|
|
703
|
+
type: EventType.STEP_STARTED,
|
|
704
|
+
stepName: stepId,
|
|
705
|
+
stepId,
|
|
706
|
+
model: model || options.model,
|
|
707
|
+
timestamp: Date.now(),
|
|
708
|
+
stepType: "thinking"
|
|
709
|
+
};
|
|
710
|
+
}
|
|
711
|
+
if (contentPart.type === "output_text") hasStreamedContentDeltas = true;
|
|
712
|
+
else if (contentPart.type === "reasoning_text") hasStreamedReasoningDeltas = true;
|
|
713
|
+
const partChunk = handleContentPart(contentPart);
|
|
714
|
+
yield partChunk;
|
|
715
|
+
if (partChunk.type === "RUN_ERROR") {
|
|
716
|
+
runFinishedEmitted = true;
|
|
717
|
+
return;
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
if (chunk.type === "response.content_part.done") {
|
|
721
|
+
const contentPart = chunk.part;
|
|
722
|
+
if (contentPart.type === "output_text" && hasStreamedContentDeltas) continue;
|
|
723
|
+
if (contentPart.type === "reasoning_text" && hasStreamedReasoningDeltas) continue;
|
|
724
|
+
if (contentPart.type === "output_text" && !hasEmittedTextMessageStart) {
|
|
725
|
+
hasEmittedTextMessageStart = true;
|
|
726
|
+
yield {
|
|
727
|
+
type: EventType.TEXT_MESSAGE_START,
|
|
728
|
+
messageId: aguiState.messageId,
|
|
729
|
+
model: model || options.model,
|
|
730
|
+
timestamp: Date.now(),
|
|
731
|
+
role: "assistant"
|
|
732
|
+
};
|
|
733
|
+
} else if (contentPart.type === "reasoning_text" && !hasEmittedStepStarted) {
|
|
734
|
+
hasEmittedStepStarted = true;
|
|
735
|
+
stepId = generateId(this.name);
|
|
736
|
+
yield {
|
|
737
|
+
type: EventType.STEP_STARTED,
|
|
738
|
+
stepName: stepId,
|
|
739
|
+
stepId,
|
|
740
|
+
model: model || options.model,
|
|
741
|
+
timestamp: Date.now(),
|
|
742
|
+
stepType: "thinking"
|
|
743
|
+
};
|
|
744
|
+
}
|
|
745
|
+
const doneChunk = handleContentPart(contentPart);
|
|
746
|
+
yield doneChunk;
|
|
747
|
+
if (doneChunk.type === "RUN_ERROR") {
|
|
748
|
+
runFinishedEmitted = true;
|
|
749
|
+
return;
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
if (chunk.type === "response.output_item.added") {
|
|
753
|
+
const item = chunk.item;
|
|
754
|
+
if (item.type === "function_call" && item.id) {
|
|
755
|
+
let metadata = toolCallMetadata.get(item.id);
|
|
756
|
+
if (!metadata) {
|
|
757
|
+
metadata = {
|
|
758
|
+
index: chunk.output_index,
|
|
759
|
+
name: item.name || "",
|
|
760
|
+
started: false
|
|
761
|
+
};
|
|
762
|
+
toolCallMetadata.set(item.id, metadata);
|
|
763
|
+
} else if (!metadata.name && item.name) metadata.name = item.name;
|
|
764
|
+
if (!metadata.started && metadata.name) {
|
|
765
|
+
yield {
|
|
766
|
+
type: EventType.TOOL_CALL_START,
|
|
767
|
+
toolCallId: item.id,
|
|
768
|
+
toolCallName: metadata.name,
|
|
769
|
+
toolName: metadata.name,
|
|
770
|
+
parentMessageId: aguiState.messageId,
|
|
771
|
+
model: model || options.model,
|
|
772
|
+
timestamp: Date.now(),
|
|
773
|
+
index: chunk.output_index
|
|
774
|
+
};
|
|
775
|
+
metadata.started = true;
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
if (chunk.type === "response.function_call_arguments.delta" && chunk.delta) {
|
|
780
|
+
if (!toolCallMetadata.get(chunk.item_id)?.started) {
|
|
781
|
+
options.logger.errors(`${this.name}.processStreamChunks orphan function_call_arguments.delta`, {
|
|
782
|
+
source: `${this.name}.processStreamChunks`,
|
|
783
|
+
toolCallId: chunk.item_id,
|
|
784
|
+
rawDelta: chunk.delta
|
|
785
|
+
});
|
|
786
|
+
continue;
|
|
787
|
+
}
|
|
788
|
+
yield {
|
|
789
|
+
type: EventType.TOOL_CALL_ARGS,
|
|
790
|
+
toolCallId: chunk.item_id,
|
|
791
|
+
model: model || options.model,
|
|
792
|
+
timestamp: Date.now(),
|
|
793
|
+
delta: chunk.delta
|
|
794
|
+
};
|
|
795
|
+
}
|
|
796
|
+
if (chunk.type === "response.function_call_arguments.done") {
|
|
797
|
+
const { item_id } = chunk;
|
|
798
|
+
const metadata = toolCallMetadata.get(item_id);
|
|
799
|
+
if (!metadata?.started) {
|
|
800
|
+
if (metadata) metadata.pendingArguments = chunk.arguments;
|
|
801
|
+
options.logger.errors(`${this.name}.processStreamChunks deferring function_call_arguments.done — TOOL_CALL_START not yet emitted (waiting for name)`, {
|
|
802
|
+
source: `${this.name}.processStreamChunks`,
|
|
803
|
+
toolCallId: item_id,
|
|
804
|
+
rawArguments: chunk.arguments
|
|
805
|
+
});
|
|
806
|
+
continue;
|
|
807
|
+
}
|
|
808
|
+
if (metadata.ended) continue;
|
|
809
|
+
const name = metadata.name || "";
|
|
810
|
+
metadata.ended = true;
|
|
811
|
+
let parsedInput = {};
|
|
812
|
+
if (chunk.arguments) try {
|
|
813
|
+
const parsed = JSON.parse(chunk.arguments);
|
|
814
|
+
parsedInput = parsed && typeof parsed === "object" ? parsed : {};
|
|
815
|
+
} catch (parseError) {
|
|
816
|
+
options.logger.errors(`${this.name}.processStreamChunks tool-args JSON parse failed`, {
|
|
817
|
+
error: toRunErrorPayload(parseError, `tool ${name} (${item_id}) returned malformed JSON arguments`),
|
|
818
|
+
source: `${this.name}.processStreamChunks`,
|
|
819
|
+
toolCallId: item_id,
|
|
820
|
+
toolName: name,
|
|
821
|
+
rawArguments: chunk.arguments
|
|
822
|
+
});
|
|
823
|
+
parsedInput = {};
|
|
824
|
+
}
|
|
825
|
+
yield {
|
|
826
|
+
type: EventType.TOOL_CALL_END,
|
|
827
|
+
toolCallId: item_id,
|
|
828
|
+
toolCallName: name,
|
|
829
|
+
toolName: name,
|
|
830
|
+
model: model || options.model,
|
|
831
|
+
timestamp: Date.now(),
|
|
832
|
+
input: parsedInput
|
|
833
|
+
};
|
|
834
|
+
}
|
|
835
|
+
if (chunk.type === "response.output_item.done") {
|
|
836
|
+
const item = chunk.item;
|
|
837
|
+
if (item.type === "function_call" && item.id) {
|
|
838
|
+
const metadata = toolCallMetadata.get(item.id) ?? {
|
|
839
|
+
index: chunk.output_index,
|
|
840
|
+
name: item.name || "",
|
|
841
|
+
started: false
|
|
842
|
+
};
|
|
843
|
+
if (!toolCallMetadata.has(item.id)) toolCallMetadata.set(item.id, metadata);
|
|
844
|
+
else if (!metadata.name && item.name) metadata.name = item.name;
|
|
845
|
+
if (!metadata.started && metadata.name) {
|
|
846
|
+
yield {
|
|
847
|
+
type: EventType.TOOL_CALL_START,
|
|
848
|
+
toolCallId: item.id,
|
|
849
|
+
toolCallName: metadata.name,
|
|
850
|
+
toolName: metadata.name,
|
|
851
|
+
parentMessageId: aguiState.messageId,
|
|
852
|
+
model: model || options.model,
|
|
853
|
+
timestamp: Date.now(),
|
|
854
|
+
index: metadata.index
|
|
855
|
+
};
|
|
856
|
+
metadata.started = true;
|
|
857
|
+
}
|
|
858
|
+
const rawArgs = typeof item.arguments === "string" && item.arguments.length > 0 ? item.arguments : metadata.pendingArguments;
|
|
859
|
+
if (metadata.started && !metadata.ended && rawArgs !== void 0) {
|
|
860
|
+
const name = metadata.name || "";
|
|
861
|
+
let parsedInput = {};
|
|
862
|
+
if (rawArgs) try {
|
|
863
|
+
const parsed = JSON.parse(rawArgs);
|
|
864
|
+
parsedInput = parsed && typeof parsed === "object" ? parsed : {};
|
|
865
|
+
} catch (parseError) {
|
|
866
|
+
options.logger.errors(`${this.name}.processStreamChunks tool-args JSON parse failed (output_item.done backfill)`, {
|
|
867
|
+
error: toRunErrorPayload(parseError, `tool ${name} (${item.id}) returned malformed JSON arguments`),
|
|
868
|
+
source: `${this.name}.processStreamChunks`,
|
|
869
|
+
toolCallId: item.id,
|
|
870
|
+
toolName: name,
|
|
871
|
+
rawArguments: rawArgs
|
|
872
|
+
});
|
|
873
|
+
parsedInput = {};
|
|
874
|
+
}
|
|
875
|
+
yield {
|
|
876
|
+
type: EventType.TOOL_CALL_END,
|
|
877
|
+
toolCallId: item.id,
|
|
878
|
+
toolCallName: name,
|
|
879
|
+
toolName: name,
|
|
880
|
+
model: model || options.model,
|
|
881
|
+
timestamp: Date.now(),
|
|
882
|
+
input: parsedInput
|
|
883
|
+
};
|
|
884
|
+
metadata.ended = true;
|
|
885
|
+
metadata.pendingArguments = void 0;
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
}
|
|
889
|
+
if (chunk.type === "response.completed") {
|
|
890
|
+
for (const item of chunk.response.output) {
|
|
891
|
+
if (item.type !== "function_call" || !item.id) continue;
|
|
892
|
+
const metadata = toolCallMetadata.get(item.id) ?? {
|
|
893
|
+
index: 0,
|
|
894
|
+
name: item.name || "",
|
|
895
|
+
started: false
|
|
896
|
+
};
|
|
897
|
+
if (!toolCallMetadata.has(item.id)) toolCallMetadata.set(item.id, metadata);
|
|
898
|
+
else if (!metadata.name && item.name) metadata.name = item.name;
|
|
899
|
+
if (!metadata.started && metadata.name) {
|
|
900
|
+
yield {
|
|
901
|
+
type: EventType.TOOL_CALL_START,
|
|
902
|
+
toolCallId: item.id,
|
|
903
|
+
toolCallName: metadata.name,
|
|
904
|
+
toolName: metadata.name,
|
|
905
|
+
parentMessageId: aguiState.messageId,
|
|
906
|
+
model: model || options.model,
|
|
907
|
+
timestamp: Date.now(),
|
|
908
|
+
index: metadata.index
|
|
909
|
+
};
|
|
910
|
+
metadata.started = true;
|
|
911
|
+
}
|
|
912
|
+
const rawArgs = typeof item.arguments === "string" && item.arguments.length > 0 ? item.arguments : metadata.pendingArguments;
|
|
913
|
+
if (metadata.started && !metadata.ended) {
|
|
914
|
+
const name = metadata.name || "";
|
|
915
|
+
let parsedInput = {};
|
|
916
|
+
if (rawArgs) try {
|
|
917
|
+
const parsed = JSON.parse(rawArgs);
|
|
918
|
+
parsedInput = parsed && typeof parsed === "object" ? parsed : {};
|
|
919
|
+
} catch (parseError) {
|
|
920
|
+
options.logger.errors(`${this.name}.processStreamChunks tool-args JSON parse failed (response.completed backfill)`, {
|
|
921
|
+
error: toRunErrorPayload(parseError, `tool ${name} (${item.id}) returned malformed JSON arguments`),
|
|
922
|
+
source: `${this.name}.processStreamChunks`,
|
|
923
|
+
toolCallId: item.id,
|
|
924
|
+
toolName: name,
|
|
925
|
+
rawArguments: rawArgs
|
|
926
|
+
});
|
|
927
|
+
parsedInput = {};
|
|
928
|
+
}
|
|
929
|
+
yield {
|
|
930
|
+
type: EventType.TOOL_CALL_END,
|
|
931
|
+
toolCallId: item.id,
|
|
932
|
+
toolCallName: name,
|
|
933
|
+
toolName: name,
|
|
934
|
+
model: model || options.model,
|
|
935
|
+
timestamp: Date.now(),
|
|
936
|
+
input: parsedInput
|
|
937
|
+
};
|
|
938
|
+
metadata.ended = true;
|
|
939
|
+
metadata.pendingArguments = void 0;
|
|
940
|
+
}
|
|
941
|
+
}
|
|
942
|
+
if (hasEmittedTextMessageStart) {
|
|
943
|
+
yield {
|
|
944
|
+
type: EventType.TEXT_MESSAGE_END,
|
|
945
|
+
messageId: aguiState.messageId,
|
|
946
|
+
model: model || options.model,
|
|
947
|
+
timestamp: Date.now()
|
|
948
|
+
};
|
|
949
|
+
hasEmittedTextMessageStart = false;
|
|
950
|
+
}
|
|
951
|
+
const hasFunctionCalls = chunk.response.output.some((item) => item.type === "function_call");
|
|
952
|
+
const incompleteReason = chunk.response.incomplete_details?.reason;
|
|
953
|
+
const finishReason = hasFunctionCalls ? "tool_calls" : incompleteReason === "max_output_tokens" ? "length" : incompleteReason === "content_filter" ? "content_filter" : "stop";
|
|
954
|
+
yield {
|
|
955
|
+
type: EventType.RUN_FINISHED,
|
|
956
|
+
runId: aguiState.runId,
|
|
957
|
+
threadId: aguiState.threadId,
|
|
958
|
+
model: model || options.model,
|
|
959
|
+
timestamp: Date.now(),
|
|
960
|
+
...chunk.response.usage && { usage: buildResponsesUsage(chunk.response.usage) },
|
|
961
|
+
finishReason
|
|
962
|
+
};
|
|
963
|
+
runFinishedEmitted = true;
|
|
964
|
+
}
|
|
965
|
+
if (chunk.type === "error") {
|
|
966
|
+
const code = chunk.code ?? void 0;
|
|
967
|
+
yield {
|
|
968
|
+
type: EventType.RUN_ERROR,
|
|
969
|
+
model: model || options.model,
|
|
970
|
+
timestamp: Date.now(),
|
|
971
|
+
message: chunk.message,
|
|
972
|
+
...code !== void 0 && { code },
|
|
973
|
+
error: {
|
|
974
|
+
message: chunk.message,
|
|
975
|
+
...code !== void 0 && { code }
|
|
976
|
+
}
|
|
977
|
+
};
|
|
978
|
+
runFinishedEmitted = true;
|
|
979
|
+
return;
|
|
980
|
+
}
|
|
981
|
+
}
|
|
982
|
+
if (!runFinishedEmitted && aguiState.hasEmittedRunStarted) {
|
|
983
|
+
if (hasEmittedTextMessageStart) yield {
|
|
984
|
+
type: EventType.TEXT_MESSAGE_END,
|
|
985
|
+
messageId: aguiState.messageId,
|
|
986
|
+
model: model || options.model,
|
|
987
|
+
timestamp: Date.now()
|
|
988
|
+
};
|
|
989
|
+
yield {
|
|
990
|
+
type: EventType.RUN_FINISHED,
|
|
991
|
+
runId: aguiState.runId,
|
|
992
|
+
threadId: aguiState.threadId,
|
|
993
|
+
model: model || options.model,
|
|
994
|
+
timestamp: Date.now(),
|
|
995
|
+
finishReason: toolCallMetadata.size > 0 ? "tool_calls" : "stop"
|
|
996
|
+
};
|
|
997
|
+
}
|
|
998
|
+
} catch (error) {
|
|
999
|
+
const errorPayload = toRunErrorPayload(error, `${this.name}.processStreamChunks failed`);
|
|
1000
|
+
const rawEvent = toRunErrorRawEvent(error);
|
|
1001
|
+
options.logger.errors(`${this.name}.processStreamChunks fatal`, {
|
|
1002
|
+
error: errorPayload,
|
|
1003
|
+
source: `${this.name}.processStreamChunks`
|
|
1004
|
+
});
|
|
1005
|
+
yield {
|
|
1006
|
+
type: EventType.RUN_ERROR,
|
|
1007
|
+
model: options.model,
|
|
1008
|
+
timestamp: Date.now(),
|
|
1009
|
+
message: errorPayload.message,
|
|
1010
|
+
...errorPayload.code !== void 0 && { code: errorPayload.code },
|
|
1011
|
+
...rawEvent !== void 0 && { rawEvent },
|
|
1012
|
+
error: {
|
|
1013
|
+
message: errorPayload.message,
|
|
1014
|
+
...errorPayload.code !== void 0 && { code: errorPayload.code }
|
|
1015
|
+
}
|
|
1016
|
+
};
|
|
1017
|
+
}
|
|
1018
|
+
}
|
|
1019
|
+
/**
|
|
1020
|
+
* Maps common TextOptions to Responses API request format.
|
|
1021
|
+
* Override this in subclasses to add provider-specific options.
|
|
1022
|
+
*/
|
|
1023
|
+
mapOptionsToRequest(options) {
|
|
1024
|
+
const input = this.convertMessagesToInput(options.messages);
|
|
1025
|
+
const tools = options.tools ? convertToolsToResponsesFormat(options.tools, this.makeStructuredOutputCompatible.bind(this)) : void 0;
|
|
1026
|
+
const modelOptions = options.modelOptions;
|
|
1027
|
+
const combinedSchema = options.outputSchema;
|
|
1028
|
+
const textFormat = combinedSchema ? { text: { format: {
|
|
1029
|
+
type: "json_schema",
|
|
1030
|
+
name: "structured_output",
|
|
1031
|
+
schema: this.makeStructuredOutputCompatible(combinedSchema, Array.isArray(combinedSchema.required) ? combinedSchema.required : void 0),
|
|
1032
|
+
strict: true
|
|
1033
|
+
} } } : void 0;
|
|
1034
|
+
return {
|
|
1035
|
+
...modelOptions,
|
|
1036
|
+
model: options.model,
|
|
1037
|
+
...options.metadata !== void 0 && { metadata: options.metadata },
|
|
1038
|
+
...(() => {
|
|
1039
|
+
const prompts = normalizeSystemPrompts(options.systemPrompts);
|
|
1040
|
+
if (prompts.length === 0) return {};
|
|
1041
|
+
return { instructions: prompts.map((p) => p.content).join("\n") };
|
|
1042
|
+
})(),
|
|
1043
|
+
input,
|
|
1044
|
+
...tools && tools.length > 0 && { tools },
|
|
1045
|
+
...textFormat ?? {}
|
|
1046
|
+
};
|
|
1047
|
+
}
|
|
1048
|
+
/**
|
|
1049
|
+
* The OpenAI Responses API supports `tools` and `text.format: json_schema`
|
|
1050
|
+
* together in a single streaming request (per issue #605). Subclasses
|
|
1051
|
+
* that route to providers without this capability should override.
|
|
1052
|
+
*/
|
|
1053
|
+
supportsCombinedToolsAndSchema() {
|
|
1054
|
+
return true;
|
|
1055
|
+
}
|
|
1056
|
+
/**
|
|
1057
|
+
* Converts ModelMessage[] to Responses API ResponseInput format.
|
|
1058
|
+
* Override this in subclasses for provider-specific message format quirks.
|
|
1059
|
+
*
|
|
1060
|
+
* Key differences from Chat Completions:
|
|
1061
|
+
* - Tool results use `function_call_output` type (not `tool` role)
|
|
1062
|
+
* - Assistant tool calls are `function_call` objects (not nested in `tool_calls`)
|
|
1063
|
+
* - User content uses `input_text`, `input_image`, `input_file` types
|
|
1064
|
+
* - System prompts go in `instructions`, not as messages
|
|
1065
|
+
*/
|
|
1066
|
+
convertMessagesToInput(messages) {
|
|
1067
|
+
const result = [];
|
|
1068
|
+
for (const message of messages) {
|
|
1069
|
+
if (message.role === "tool") {
|
|
1070
|
+
const toolContent = message.content;
|
|
1071
|
+
const output = Array.isArray(toolContent) ? toolContent.map((part) => this.convertContentPartToInput(part)) : typeof toolContent === "string" ? toolContent : JSON.stringify(toolContent);
|
|
1072
|
+
result.push({
|
|
1073
|
+
type: "function_call_output",
|
|
1074
|
+
call_id: message.toolCallId || "",
|
|
1075
|
+
output
|
|
1076
|
+
});
|
|
1077
|
+
continue;
|
|
1078
|
+
}
|
|
1079
|
+
if (message.role === "assistant") {
|
|
1080
|
+
if (message.toolCalls && message.toolCalls.length > 0) for (const toolCall of message.toolCalls) {
|
|
1081
|
+
const argumentsString = typeof toolCall.function.arguments === "string" ? toolCall.function.arguments : JSON.stringify(toolCall.function.arguments);
|
|
1082
|
+
result.push({
|
|
1083
|
+
type: "function_call",
|
|
1084
|
+
call_id: toolCall.id,
|
|
1085
|
+
name: toolCall.function.name,
|
|
1086
|
+
arguments: argumentsString
|
|
1087
|
+
});
|
|
1088
|
+
}
|
|
1089
|
+
if (message.content) {
|
|
1090
|
+
const contentStr = this.extractTextContent(message.content);
|
|
1091
|
+
if (contentStr) result.push({
|
|
1092
|
+
type: "message",
|
|
1093
|
+
role: "assistant",
|
|
1094
|
+
content: contentStr
|
|
1095
|
+
});
|
|
1096
|
+
}
|
|
1097
|
+
continue;
|
|
1098
|
+
}
|
|
1099
|
+
const contentParts = this.normalizeContent(message.content);
|
|
1100
|
+
const inputContent = [];
|
|
1101
|
+
for (const part of contentParts) inputContent.push(this.convertContentPartToInput(part));
|
|
1102
|
+
if (inputContent.length === 0) throw new Error(`User message for ${this.name} has no content parts. Empty user messages would produce a paid request with no input; provide at least one text/image/audio part or omit the message.`);
|
|
1103
|
+
result.push({
|
|
1104
|
+
type: "message",
|
|
1105
|
+
role: "user",
|
|
1106
|
+
content: inputContent
|
|
1107
|
+
});
|
|
1108
|
+
}
|
|
1109
|
+
return result;
|
|
1110
|
+
}
|
|
1111
|
+
/**
|
|
1112
|
+
* Converts a ContentPart to Responses API input content item.
|
|
1113
|
+
* Handles text, image, and audio content parts.
|
|
1114
|
+
* Override this in subclasses for additional content types or provider-specific metadata.
|
|
1115
|
+
*/
|
|
1116
|
+
convertContentPartToInput(part) {
|
|
1117
|
+
switch (part.type) {
|
|
1118
|
+
case "text": return {
|
|
1119
|
+
type: "input_text",
|
|
1120
|
+
text: part.content
|
|
1121
|
+
};
|
|
1122
|
+
case "image": {
|
|
1123
|
+
const imageMetadata = part.metadata;
|
|
1124
|
+
if (part.source.type === "url") return {
|
|
1125
|
+
type: "input_image",
|
|
1126
|
+
image_url: part.source.value,
|
|
1127
|
+
detail: imageMetadata?.detail || "auto"
|
|
1128
|
+
};
|
|
1129
|
+
const imageValue = part.source.value;
|
|
1130
|
+
const imageMime = part.source.mimeType || "application/octet-stream";
|
|
1131
|
+
return {
|
|
1132
|
+
type: "input_image",
|
|
1133
|
+
image_url: imageValue.startsWith("data:") ? imageValue : `data:${imageMime};base64,${imageValue}`,
|
|
1134
|
+
detail: imageMetadata?.detail || "auto"
|
|
1135
|
+
};
|
|
1136
|
+
}
|
|
1137
|
+
case "audio": {
|
|
1138
|
+
if (part.source.type === "url") return {
|
|
1139
|
+
type: "input_file",
|
|
1140
|
+
file_url: part.source.value
|
|
1141
|
+
};
|
|
1142
|
+
const audioValue = part.source.value;
|
|
1143
|
+
const audioMime = part.source.mimeType || "application/octet-stream";
|
|
1144
|
+
return {
|
|
1145
|
+
type: "input_file",
|
|
1146
|
+
file_data: audioValue.startsWith("data:") ? audioValue : `data:${audioMime};base64,${audioValue}`
|
|
1147
|
+
};
|
|
1148
|
+
}
|
|
1149
|
+
default: throw new Error(`Unsupported content part type: ${part.type}`);
|
|
1150
|
+
}
|
|
1151
|
+
}
|
|
1152
|
+
/**
|
|
1153
|
+
* Normalizes message content to an array of ContentPart.
|
|
1154
|
+
* Handles backward compatibility with string content.
|
|
1155
|
+
*/
|
|
1156
|
+
normalizeContent(content) {
|
|
1157
|
+
if (content === null) return [];
|
|
1158
|
+
if (typeof content === "string") return [{
|
|
1159
|
+
type: "text",
|
|
1160
|
+
content
|
|
1161
|
+
}];
|
|
1162
|
+
return content;
|
|
1163
|
+
}
|
|
1164
|
+
/**
|
|
1165
|
+
* Extracts text content from a content value that may be string, null, or ContentPart array.
|
|
1166
|
+
*/
|
|
1167
|
+
extractTextContent(content) {
|
|
1168
|
+
if (content === null) return "";
|
|
1169
|
+
if (typeof content === "string") return content;
|
|
1170
|
+
return content.filter((p) => p.type === "text").map((p) => p.content).join("");
|
|
1171
|
+
}
|
|
1323
1172
|
};
|
|
1324
|
-
//#
|
|
1173
|
+
//#endregion
|
|
1174
|
+
export { OpenAIBaseResponsesTextAdapter };
|
|
1175
|
+
|
|
1176
|
+
//# sourceMappingURL=responses-text.js.map
|