@aeye/openai 0.3.10 → 0.7.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/README.md +1076 -921
- package/aeye-openai.md +31 -18
- package/dist/convert.d.ts +81 -0
- package/dist/convert.d.ts.map +1 -0
- package/dist/convert.js +108 -0
- package/dist/convert.js.map +1 -0
- package/dist/decisions.d.ts +110 -0
- package/dist/decisions.d.ts.map +1 -0
- package/dist/decisions.js +48 -0
- package/dist/decisions.js.map +1 -0
- package/dist/images.d.ts +47 -0
- package/dist/images.d.ts.map +1 -0
- package/dist/images.js +80 -0
- package/dist/images.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/openai.d.ts +452 -206
- package/dist/openai.d.ts.map +1 -1
- package/dist/openai.js +912 -1424
- package/dist/openai.js.map +1 -1
- package/dist/responses.d.ts +67 -0
- package/dist/responses.d.ts.map +1 -0
- package/dist/responses.js +236 -0
- package/dist/responses.js.map +1 -0
- package/package.json +6 -6
package/README.md
CHANGED
|
@@ -1,921 +1,1076 @@
|
|
|
1
|
-
# @aeye/openai
|
|
2
|
-
|
|
3
|
-
OpenAI provider for the @aeye (AI TypeScript) framework. This package provides a comprehensive integration with OpenAI's API, supporting the full range of capabilities including chat completions, image generation, speech synthesis, transcription, and embeddings.
|
|
4
|
-
|
|
5
|
-
## Features
|
|
6
|
-
|
|
7
|
-
- **Chat
|
|
8
|
-
- **Vision**:
|
|
9
|
-
- **Reasoning
|
|
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
|
-
const provider = new OpenAIProvider(
|
|
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
|
-
#### Generate
|
|
350
|
-
|
|
351
|
-
```typescript
|
|
352
|
-
const imageResponse = await provider.generateImage(
|
|
353
|
-
{
|
|
354
|
-
prompt: 'A
|
|
355
|
-
model: '
|
|
356
|
-
size: '
|
|
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
|
-
import
|
|
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
|
-
|
|
1
|
+
# @aeye/openai
|
|
2
|
+
|
|
3
|
+
OpenAI provider for the @aeye (AI TypeScript) framework. This package provides a comprehensive integration with OpenAI's API, supporting the full range of capabilities including chat completions, image generation, speech synthesis, transcription, and embeddings.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Chat**: GPT-5.x, GPT-6, GPT-4.1 and other chat models, through Chat Completions or the Responses API
|
|
8
|
+
- **Vision**: Image and file (PDF) understanding
|
|
9
|
+
- **Reasoning**: Efforts `none` through `max`, reasoning summaries, and encrypted reasoning carried across turns
|
|
10
|
+
- **Hosted Tools**: Web search, file search, code interpreter, image generation, and remote MCP servers run by OpenAI
|
|
11
|
+
- **Image Generation**: GPT Image (`gpt-image-2` by default) for generating and editing images
|
|
12
|
+
- **Speech-to-Text**: `gpt-transcribe`, diarization with known speakers, and Whisper
|
|
13
|
+
- **Text-to-Speech**: TTS models with built-in or custom voices
|
|
14
|
+
- **Audio Output**: Spoken answers from audio-capable chat models
|
|
15
|
+
- **Embeddings**: Text embeddings for semantic search and similarity
|
|
16
|
+
- **Decisions**: Probabilities, choices, and scores from `/v1/decisions` (`gpt-6-luna`)
|
|
17
|
+
- **Batches**: Chat requests as one batch at about half the price
|
|
18
|
+
- **Function Calling**: Tool/function calling with automatic schema conversion, free-text and grammar tools, tool search, async tools, and allowed tools
|
|
19
|
+
- **Structured Outputs**: JSON mode and schema-based structured outputs
|
|
20
|
+
- **Streaming**: Real-time streaming responses for chat completions
|
|
21
|
+
- **Multi-modal**: Support for text, images, audio, and files in conversations
|
|
22
|
+
- **Extensible**: Base class for OpenAI-compatible providers (Azure OpenAI, OpenRouter, etc.)
|
|
23
|
+
|
|
24
|
+
## Installation
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
npm install @aeye/openai @aeye/ai @aeye/core openai zod
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Requires Node >= 22 and the `openai` SDK v7.
|
|
31
|
+
|
|
32
|
+
## Quick Start
|
|
33
|
+
|
|
34
|
+
```typescript
|
|
35
|
+
import { OpenAIProvider } from '@aeye/openai';
|
|
36
|
+
|
|
37
|
+
// Create provider instance
|
|
38
|
+
const provider = new OpenAIProvider({
|
|
39
|
+
apiKey: process.env.OPENAI_API_KEY!,
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
// Create executor
|
|
43
|
+
const executor = provider.createExecutor();
|
|
44
|
+
|
|
45
|
+
// Make a request
|
|
46
|
+
const response = await executor(
|
|
47
|
+
{
|
|
48
|
+
messages: [
|
|
49
|
+
{ role: 'user', content: 'Tell me a joke about TypeScript' }
|
|
50
|
+
]
|
|
51
|
+
},
|
|
52
|
+
{},
|
|
53
|
+
{ model: 'gpt-5.4-mini' }
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
console.log(response.content);
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Configuration
|
|
60
|
+
|
|
61
|
+
### Basic Configuration
|
|
62
|
+
|
|
63
|
+
```typescript
|
|
64
|
+
import { OpenAIProvider, OpenAIConfig } from '@aeye/openai';
|
|
65
|
+
|
|
66
|
+
const config: OpenAIConfig = {
|
|
67
|
+
apiKey: process.env.OPENAI_API_KEY!,
|
|
68
|
+
organization: 'org-123456', // Optional: for multi-org accounts
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
const provider = new OpenAIProvider(config);
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Chat Completions or Responses API
|
|
75
|
+
|
|
76
|
+
```typescript
|
|
77
|
+
const provider = new OpenAIProvider({
|
|
78
|
+
apiKey: process.env.OPENAI_API_KEY!,
|
|
79
|
+
api: 'auto', // 'auto' (default) | 'chat' | 'responses'
|
|
80
|
+
});
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
With `'auto'`, chat uses Chat Completions unless the model is Responses-only (codex, pre-5.6 pro models, deep-research, computer-use), the request has `hostedTools`, a tool sets `format`, `defer`, or `async`, `reason` sets `mode`, `summary`, or `context`, or the request has tools and a reasoning `effort` other than `none`. Through Responses, reasoning comes back encrypted (unless `store` is on) and is replayed on later turns.
|
|
84
|
+
|
|
85
|
+
### Custom Base URL (for Azure OpenAI or compatible APIs)
|
|
86
|
+
|
|
87
|
+
```typescript
|
|
88
|
+
const provider = new OpenAIProvider({
|
|
89
|
+
apiKey: process.env.AZURE_API_KEY!,
|
|
90
|
+
baseURL: 'https://your-resource.openai.azure.com/openai/deployments/your-deployment',
|
|
91
|
+
});
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## Usage Examples
|
|
95
|
+
|
|
96
|
+
### Chat Completions
|
|
97
|
+
|
|
98
|
+
#### Basic Chat
|
|
99
|
+
|
|
100
|
+
```typescript
|
|
101
|
+
const executor = provider.createExecutor();
|
|
102
|
+
|
|
103
|
+
const response = await executor(
|
|
104
|
+
{
|
|
105
|
+
messages: [
|
|
106
|
+
{ role: 'system', content: 'You are a helpful assistant.' },
|
|
107
|
+
{ role: 'user', content: 'What is the capital of France?' }
|
|
108
|
+
],
|
|
109
|
+
temperature: 0.7,
|
|
110
|
+
maxTokens: 150,
|
|
111
|
+
},
|
|
112
|
+
{},
|
|
113
|
+
{ model: 'gpt-5.4-mini' }
|
|
114
|
+
);
|
|
115
|
+
|
|
116
|
+
console.log(response.content);
|
|
117
|
+
console.log('Tokens used:', response.usage.totalTokens);
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
#### Streaming Chat
|
|
121
|
+
|
|
122
|
+
```typescript
|
|
123
|
+
const streamer = provider.createStreamer();
|
|
124
|
+
|
|
125
|
+
for await (const chunk of streamer(
|
|
126
|
+
{
|
|
127
|
+
messages: [
|
|
128
|
+
{ role: 'user', content: 'Write a short story about a robot' }
|
|
129
|
+
]
|
|
130
|
+
},
|
|
131
|
+
{},
|
|
132
|
+
{ model: 'gpt-5.4-mini' }
|
|
133
|
+
)) {
|
|
134
|
+
if (chunk.content) {
|
|
135
|
+
process.stdout.write(chunk.content);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
#### Multi-turn Conversation
|
|
141
|
+
|
|
142
|
+
```typescript
|
|
143
|
+
const conversation = [
|
|
144
|
+
{ role: 'system' as const, content: 'You are a helpful assistant.' },
|
|
145
|
+
{ role: 'user' as const, content: 'What is TypeScript?' },
|
|
146
|
+
];
|
|
147
|
+
|
|
148
|
+
// First response
|
|
149
|
+
let response = await executor(
|
|
150
|
+
{ messages: conversation },
|
|
151
|
+
{},
|
|
152
|
+
{ model: 'gpt-5.4-mini' }
|
|
153
|
+
);
|
|
154
|
+
|
|
155
|
+
conversation.push({ role: 'assistant', content: response.content });
|
|
156
|
+
conversation.push({ role: 'user', content: 'Can you give me an example?' });
|
|
157
|
+
|
|
158
|
+
// Second response
|
|
159
|
+
response = await executor(
|
|
160
|
+
{ messages: conversation },
|
|
161
|
+
{},
|
|
162
|
+
{ model: 'gpt-5.4-mini' }
|
|
163
|
+
);
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### Vision
|
|
167
|
+
|
|
168
|
+
#### Analyze an Image
|
|
169
|
+
|
|
170
|
+
```typescript
|
|
171
|
+
const response = await executor(
|
|
172
|
+
{
|
|
173
|
+
messages: [
|
|
174
|
+
{
|
|
175
|
+
role: 'user',
|
|
176
|
+
content: [
|
|
177
|
+
{ type: 'text', content: 'What is in this image?' },
|
|
178
|
+
{
|
|
179
|
+
type: 'image',
|
|
180
|
+
content: 'https://example.com/image.jpg',
|
|
181
|
+
// Or use base64: content: 'data:image/jpeg;base64,/9j/4AAQ...'
|
|
182
|
+
}
|
|
183
|
+
]
|
|
184
|
+
}
|
|
185
|
+
]
|
|
186
|
+
},
|
|
187
|
+
{},
|
|
188
|
+
{ model: 'gpt-5.4-mini' }
|
|
189
|
+
);
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
#### Multiple Images
|
|
193
|
+
|
|
194
|
+
```typescript
|
|
195
|
+
const response = await executor(
|
|
196
|
+
{
|
|
197
|
+
messages: [
|
|
198
|
+
{
|
|
199
|
+
role: 'user',
|
|
200
|
+
content: [
|
|
201
|
+
{ type: 'text', content: 'Compare these two images' },
|
|
202
|
+
{ type: 'image', content: 'https://example.com/image1.jpg' },
|
|
203
|
+
{ type: 'image', content: 'https://example.com/image2.jpg' }
|
|
204
|
+
]
|
|
205
|
+
}
|
|
206
|
+
]
|
|
207
|
+
},
|
|
208
|
+
{},
|
|
209
|
+
{ model: 'gpt-5.4-mini' }
|
|
210
|
+
);
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
### Function Calling
|
|
214
|
+
|
|
215
|
+
#### Define and Use Tools
|
|
216
|
+
|
|
217
|
+
```typescript
|
|
218
|
+
import z from 'zod';
|
|
219
|
+
|
|
220
|
+
const response = await executor(
|
|
221
|
+
{
|
|
222
|
+
messages: [
|
|
223
|
+
{ role: 'user', content: 'What is the weather like in San Francisco?' }
|
|
224
|
+
],
|
|
225
|
+
tools: [
|
|
226
|
+
{
|
|
227
|
+
name: 'get_current_weather',
|
|
228
|
+
description: 'Get the current weather in a given location',
|
|
229
|
+
parameters: z.object({
|
|
230
|
+
location: z.string().describe('The city and state, e.g., San Francisco, CA'),
|
|
231
|
+
unit: z.enum(['celsius', 'fahrenheit']).optional(),
|
|
232
|
+
}),
|
|
233
|
+
}
|
|
234
|
+
],
|
|
235
|
+
toolChoice: 'auto', // 'auto', 'required', 'none', { tool: 'tool_name' }, or { allowed: ['tool_name'], required? }
|
|
236
|
+
},
|
|
237
|
+
{},
|
|
238
|
+
{ model: 'gpt-5.4-mini' }
|
|
239
|
+
);
|
|
240
|
+
|
|
241
|
+
if (response.toolCalls) {
|
|
242
|
+
for (const toolCall of response.toolCalls) {
|
|
243
|
+
console.log('Tool:', toolCall.name);
|
|
244
|
+
console.log('Arguments:', JSON.parse(toolCall.arguments));
|
|
245
|
+
|
|
246
|
+
// Execute the function and send result back
|
|
247
|
+
const functionResult = await getWeather(JSON.parse(toolCall.arguments));
|
|
248
|
+
|
|
249
|
+
const finalResponse = await executor(
|
|
250
|
+
{
|
|
251
|
+
messages: [
|
|
252
|
+
{ role: 'user', content: 'What is the weather like in San Francisco?' },
|
|
253
|
+
{ role: 'assistant', content: '', toolCalls: [toolCall] },
|
|
254
|
+
{
|
|
255
|
+
role: 'tool',
|
|
256
|
+
content: JSON.stringify(functionResult),
|
|
257
|
+
toolCallId: toolCall.id,
|
|
258
|
+
}
|
|
259
|
+
]
|
|
260
|
+
},
|
|
261
|
+
{},
|
|
262
|
+
{ model: 'gpt-5.4-mini' }
|
|
263
|
+
);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
#### Force Specific Tool
|
|
269
|
+
|
|
270
|
+
```typescript
|
|
271
|
+
const response = await executor(
|
|
272
|
+
{
|
|
273
|
+
messages: [
|
|
274
|
+
{ role: 'user', content: 'Get the weather for Boston' }
|
|
275
|
+
],
|
|
276
|
+
tools: [
|
|
277
|
+
{
|
|
278
|
+
name: 'get_weather',
|
|
279
|
+
description: 'Get weather information',
|
|
280
|
+
parameters: z.object({
|
|
281
|
+
location: z.string(),
|
|
282
|
+
}),
|
|
283
|
+
}
|
|
284
|
+
],
|
|
285
|
+
toolChoice: { tool: 'get_weather' }, // Force this specific tool
|
|
286
|
+
},
|
|
287
|
+
{},
|
|
288
|
+
{ model: 'gpt-5.4-mini' }
|
|
289
|
+
);
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
### Structured Outputs
|
|
293
|
+
|
|
294
|
+
#### JSON Mode
|
|
295
|
+
|
|
296
|
+
```typescript
|
|
297
|
+
const response = await executor(
|
|
298
|
+
{
|
|
299
|
+
messages: [
|
|
300
|
+
{
|
|
301
|
+
role: 'user',
|
|
302
|
+
content: 'List three famous scientists and their main contributions in JSON format'
|
|
303
|
+
}
|
|
304
|
+
],
|
|
305
|
+
responseFormat: 'json', // Ensures valid JSON output
|
|
306
|
+
},
|
|
307
|
+
{},
|
|
308
|
+
{ model: 'gpt-5.4-mini' }
|
|
309
|
+
);
|
|
310
|
+
|
|
311
|
+
const scientists = JSON.parse(response.content);
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
#### Schema-based Structured Output
|
|
315
|
+
|
|
316
|
+
```typescript
|
|
317
|
+
import z from 'zod';
|
|
318
|
+
|
|
319
|
+
const PersonSchema = z.object({
|
|
320
|
+
name: z.string(),
|
|
321
|
+
age: z.number(),
|
|
322
|
+
occupation: z.string(),
|
|
323
|
+
hobbies: z.array(z.string()),
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
const response = await executor(
|
|
327
|
+
{
|
|
328
|
+
messages: [
|
|
329
|
+
{
|
|
330
|
+
role: 'user',
|
|
331
|
+
content: 'Generate a profile for a fictional software engineer'
|
|
332
|
+
}
|
|
333
|
+
],
|
|
334
|
+
responseFormat: PersonSchema, // Strict schema enforcement
|
|
335
|
+
},
|
|
336
|
+
{},
|
|
337
|
+
{ model: 'gpt-5.4-mini' }
|
|
338
|
+
);
|
|
339
|
+
|
|
340
|
+
// Response is guaranteed to match the schema
|
|
341
|
+
const person = JSON.parse(response.content);
|
|
342
|
+
console.log(person.name, person.age); // Type-safe
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
A schema that fits OpenAI's strict dialect is sent with `strict: true`. One that doesn't (nested more than 10 levels, for example) goes non-strict, and through the Responses API it is marked `strict: false`, since Responses treats an unflagged `json_schema` as strict.
|
|
346
|
+
|
|
347
|
+
### Image Generation (GPT Image)
|
|
348
|
+
|
|
349
|
+
#### Generate an Image
|
|
350
|
+
|
|
351
|
+
```typescript
|
|
352
|
+
const imageResponse = await provider.generateImage(
|
|
353
|
+
{
|
|
354
|
+
prompt: 'A serene landscape with mountains and a lake at sunset',
|
|
355
|
+
model: 'gpt-image-2', // the default
|
|
356
|
+
size: '1024x1024', // or 'auto'; other sizes snap to the nearest the model supports
|
|
357
|
+
quality: 'high', // 'auto' | 'low' | 'medium' | 'high' | 'xhigh' | 'max' (per model)
|
|
358
|
+
outputFormat: 'webp', // 'png' | 'jpeg' | 'webp'
|
|
359
|
+
outputCompression: 80, // 0-100, for jpeg and webp
|
|
360
|
+
background: 'transparent',
|
|
361
|
+
n: 1,
|
|
362
|
+
},
|
|
363
|
+
{}
|
|
364
|
+
);
|
|
365
|
+
|
|
366
|
+
const base64Image = imageResponse.images[0].b64_json;
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
#### Edit with Reference Images
|
|
370
|
+
|
|
371
|
+
```typescript
|
|
372
|
+
const edited = await provider.editImage(
|
|
373
|
+
{
|
|
374
|
+
prompt: 'Put the product from the second image on the table in the first',
|
|
375
|
+
image: fs.readFileSync('room.png'), // source image
|
|
376
|
+
references: [fs.readFileSync('product.png')], // more input images
|
|
377
|
+
mask: fs.readFileSync('mask.png'), // optional, over the source image
|
|
378
|
+
inputFidelity: 'high', // keep input details closely
|
|
379
|
+
},
|
|
380
|
+
{}
|
|
381
|
+
);
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
Usage reports the quality and size actually produced, so per-image pricing applies; GPT Image 2 and later report output image tokens in `usage.image.tokens`.
|
|
385
|
+
|
|
386
|
+
### Speech Synthesis (TTS)
|
|
387
|
+
|
|
388
|
+
#### Generate Speech
|
|
389
|
+
|
|
390
|
+
```typescript
|
|
391
|
+
import fs from 'fs';
|
|
392
|
+
import { Readable } from 'stream';
|
|
393
|
+
|
|
394
|
+
const speechResponse = await provider.generateSpeech(
|
|
395
|
+
{
|
|
396
|
+
text: 'Hello! This is a text-to-speech example using OpenAI.',
|
|
397
|
+
model: 'gpt-4o-mini-tts', // the default
|
|
398
|
+
voice: 'alloy', // any built-in voice name or a custom voice id
|
|
399
|
+
instructions: 'Speak warmly and a little slowly.',
|
|
400
|
+
speed: 1.0, // 0.25 to 4.0
|
|
401
|
+
responseFormat: 'mp3', // mp3, opus, aac, flac, wav, pcm
|
|
402
|
+
},
|
|
403
|
+
{}
|
|
404
|
+
);
|
|
405
|
+
|
|
406
|
+
// Save to file
|
|
407
|
+
Readable.fromWeb(speechResponse.audio).pipe(fs.createWriteStream('output.mp3'));
|
|
408
|
+
console.log('Audio saved to output.mp3');
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
#### Different Voices
|
|
412
|
+
|
|
413
|
+
```typescript
|
|
414
|
+
const voices = ['alloy', 'echo', 'fable', 'onyx', 'nova', 'shimmer'];
|
|
415
|
+
|
|
416
|
+
for (const voice of voices) {
|
|
417
|
+
const response = await provider.generateSpeech(
|
|
418
|
+
{
|
|
419
|
+
text: `This is the ${voice} voice speaking.`,
|
|
420
|
+
voice,
|
|
421
|
+
model: 'gpt-4o-mini-tts',
|
|
422
|
+
},
|
|
423
|
+
{}
|
|
424
|
+
);
|
|
425
|
+
|
|
426
|
+
Readable.fromWeb(response.audio).pipe(fs.createWriteStream(`voice_${voice}.mp3`));
|
|
427
|
+
}
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
### Transcription
|
|
431
|
+
|
|
432
|
+
#### Transcribe Audio File
|
|
433
|
+
|
|
434
|
+
```typescript
|
|
435
|
+
import fs from 'fs';
|
|
436
|
+
|
|
437
|
+
const audioBuffer = fs.readFileSync('audio.mp3');
|
|
438
|
+
|
|
439
|
+
const transcription = await provider.transcribe(
|
|
440
|
+
{
|
|
441
|
+
audio: audioBuffer,
|
|
442
|
+
model: 'gpt-transcribe', // the default
|
|
443
|
+
language: 'en', // Optional: ISO-639-1 language code
|
|
444
|
+
prompt: 'This is a podcast about AI and technology', // Optional: context
|
|
445
|
+
},
|
|
446
|
+
{}
|
|
447
|
+
);
|
|
448
|
+
|
|
449
|
+
console.log('Transcription:', transcription.text);
|
|
450
|
+
```
|
|
451
|
+
|
|
452
|
+
#### Transcribe with Timestamps
|
|
453
|
+
|
|
454
|
+
```typescript
|
|
455
|
+
const transcription = await provider.transcribe(
|
|
456
|
+
{
|
|
457
|
+
audio: audioBuffer,
|
|
458
|
+
model: 'whisper-1',
|
|
459
|
+
responseFormat: 'verbose_json',
|
|
460
|
+
timestampGranularities: ['word', 'segment'],
|
|
461
|
+
},
|
|
462
|
+
{}
|
|
463
|
+
);
|
|
464
|
+
|
|
465
|
+
console.log('Full text:', transcription.text);
|
|
466
|
+
|
|
467
|
+
// Segment-level timestamps
|
|
468
|
+
transcription.segments?.forEach(segment => {
|
|
469
|
+
console.log(`${segment.text}: ${segment.start}s - ${segment.end}s`);
|
|
470
|
+
});
|
|
471
|
+
```
|
|
472
|
+
|
|
473
|
+
#### Speaker Diarization
|
|
474
|
+
|
|
475
|
+
```typescript
|
|
476
|
+
const transcription = await provider.transcribe(
|
|
477
|
+
{
|
|
478
|
+
audio: fs.readFileSync('meeting.mp3'),
|
|
479
|
+
model: 'gpt-4o-transcribe-diarize',
|
|
480
|
+
responseFormat: 'diarized_json',
|
|
481
|
+
knownSpeakers: [
|
|
482
|
+
{ name: 'Alice', audio: fs.readFileSync('alice.wav') },
|
|
483
|
+
{ name: 'Bob', audio: fs.readFileSync('bob.wav') },
|
|
484
|
+
],
|
|
485
|
+
},
|
|
486
|
+
{}
|
|
487
|
+
);
|
|
488
|
+
|
|
489
|
+
transcription.segments?.forEach(segment => {
|
|
490
|
+
console.log(`${segment.speaker}: ${segment.text}`);
|
|
491
|
+
});
|
|
492
|
+
```
|
|
493
|
+
|
|
494
|
+
#### Different Output Formats
|
|
495
|
+
|
|
496
|
+
```typescript
|
|
497
|
+
// Plain text
|
|
498
|
+
const text = await provider.transcribe(
|
|
499
|
+
{ audio: audioBuffer, responseFormat: 'text' },
|
|
500
|
+
{}
|
|
501
|
+
);
|
|
502
|
+
|
|
503
|
+
// SRT subtitles
|
|
504
|
+
const srt = await provider.transcribe(
|
|
505
|
+
{ audio: audioBuffer, responseFormat: 'srt' },
|
|
506
|
+
{}
|
|
507
|
+
);
|
|
508
|
+
|
|
509
|
+
// VTT subtitles
|
|
510
|
+
const vtt = await provider.transcribe(
|
|
511
|
+
{ audio: audioBuffer, responseFormat: 'vtt' },
|
|
512
|
+
{}
|
|
513
|
+
);
|
|
514
|
+
```
|
|
515
|
+
|
|
516
|
+
### Embeddings
|
|
517
|
+
|
|
518
|
+
#### Generate Embeddings
|
|
519
|
+
|
|
520
|
+
```typescript
|
|
521
|
+
const embeddingResponse = await provider.embed(
|
|
522
|
+
{
|
|
523
|
+
texts: [
|
|
524
|
+
'The quick brown fox jumps over the lazy dog',
|
|
525
|
+
'Machine learning is a subset of artificial intelligence',
|
|
526
|
+
'OpenAI develops advanced AI models'
|
|
527
|
+
],
|
|
528
|
+
model: 'text-embedding-3-small', // or 'text-embedding-3-large'
|
|
529
|
+
},
|
|
530
|
+
{}
|
|
531
|
+
);
|
|
532
|
+
|
|
533
|
+
embeddingResponse.embeddings.forEach((item, i) => {
|
|
534
|
+
console.log(`Embedding ${i}:`, item.embedding.length, 'dimensions');
|
|
535
|
+
});
|
|
536
|
+
|
|
537
|
+
console.log('Tokens used:', embeddingResponse.usage?.text?.input);
|
|
538
|
+
```
|
|
539
|
+
|
|
540
|
+
#### Custom Dimensions (for smaller embeddings)
|
|
541
|
+
|
|
542
|
+
```typescript
|
|
543
|
+
const embeddingResponse = await provider.embed(
|
|
544
|
+
{
|
|
545
|
+
texts: ['Some text to embed'],
|
|
546
|
+
model: 'text-embedding-3-large',
|
|
547
|
+
dimensions: 256, // Reduce from default 3072 to 256
|
|
548
|
+
},
|
|
549
|
+
{}
|
|
550
|
+
);
|
|
551
|
+
```
|
|
552
|
+
|
|
553
|
+
#### Calculate Similarity
|
|
554
|
+
|
|
555
|
+
```typescript
|
|
556
|
+
function cosineSimilarity(a: number[], b: number[]): number {
|
|
557
|
+
const dotProduct = a.reduce((sum, val, i) => sum + val * b[i], 0);
|
|
558
|
+
const magnitudeA = Math.sqrt(a.reduce((sum, val) => sum + val * val, 0));
|
|
559
|
+
const magnitudeB = Math.sqrt(b.reduce((sum, val) => sum + val * val, 0));
|
|
560
|
+
return dotProduct / (magnitudeA * magnitudeB);
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
const response = await provider.embed(
|
|
564
|
+
{
|
|
565
|
+
texts: [
|
|
566
|
+
'I love programming in TypeScript',
|
|
567
|
+
'TypeScript is my favorite language',
|
|
568
|
+
'I enjoy cooking Italian food'
|
|
569
|
+
],
|
|
570
|
+
model: 'text-embedding-3-small',
|
|
571
|
+
},
|
|
572
|
+
{}
|
|
573
|
+
);
|
|
574
|
+
|
|
575
|
+
const [emb1, emb2, emb3] = response.embeddings.map(e => e.embedding);
|
|
576
|
+
|
|
577
|
+
console.log('Similarity 1-2:', cosineSimilarity(emb1, emb2)); // High similarity
|
|
578
|
+
console.log('Similarity 1-3:', cosineSimilarity(emb1, emb3)); // Low similarity
|
|
579
|
+
```
|
|
580
|
+
|
|
581
|
+
### Reasoning
|
|
582
|
+
|
|
583
|
+
```typescript
|
|
584
|
+
const response = await executor(
|
|
585
|
+
{
|
|
586
|
+
messages: [
|
|
587
|
+
{
|
|
588
|
+
role: 'user',
|
|
589
|
+
content: 'Solve this logic puzzle: There are 100 doors, all closed. You make 100 passes. On pass 1, toggle all doors. On pass 2, toggle every 2nd door. On pass 3, toggle every 3rd door, etc. Which doors are open after 100 passes?'
|
|
590
|
+
}
|
|
591
|
+
],
|
|
592
|
+
reason: {
|
|
593
|
+
effort: 'high', // 'none' | 'minimal' | 'low' | 'medium' | 'high' | 'xhigh' | 'max'
|
|
594
|
+
summary: 'auto', // a readable summary (uses the Responses API)
|
|
595
|
+
},
|
|
596
|
+
},
|
|
597
|
+
{},
|
|
598
|
+
{ model: 'gpt-5.5' }
|
|
599
|
+
);
|
|
600
|
+
|
|
601
|
+
console.log('Reasoning:', response.reasoning?.content);
|
|
602
|
+
console.log('Answer:', response.content);
|
|
603
|
+
```
|
|
604
|
+
|
|
605
|
+
- The effort is clamped to the model's accepted efforts when its `ModelInfo.reasoningEfforts` lists them.
|
|
606
|
+
- `mode`, `summary`, or `context`, or an effort other than `none` with tools, send the request to the Responses API. There, reasoning comes back encrypted (`openai-responses-v1` details) unless `store` is on; keep the assistant message's `reasoning` in the conversation and it is replayed on the next turn.
|
|
607
|
+
- `reason.maxTokens` and `reason.exclude` are not used by OpenAI.
|
|
608
|
+
- Only reasoning OpenAI produced (`reasoning.provider` is `'openai'`, or unset) is replayed; reasoning relayed by OpenRouter or made on Bedrock is left out, since OpenAI can't decrypt it.
|
|
609
|
+
- An assistant turn of only tool calls is sent with no content rather than an empty text part.
|
|
610
|
+
|
|
611
|
+
### Hosted Tools
|
|
612
|
+
|
|
613
|
+
Tools OpenAI runs itself go in `hostedTools` and use the Responses API:
|
|
614
|
+
|
|
615
|
+
```typescript
|
|
616
|
+
const response = await executor(
|
|
617
|
+
{
|
|
618
|
+
messages: [{ role: 'user', content: 'Summarize this week\'s TypeScript news.' }],
|
|
619
|
+
hostedTools: [
|
|
620
|
+
{ type: 'web_search', allowedDomains: ['devblogs.microsoft.com'] },
|
|
621
|
+
{ type: 'code_interpreter' },
|
|
622
|
+
{ type: 'file_search', vectorStoreIds: ['vs_123'], maxResults: 5 },
|
|
623
|
+
{ type: 'mcp', label: 'docs', url: 'https://example.com/mcp', allowedTools: ['search'] },
|
|
624
|
+
{ type: 'image_generation' },
|
|
625
|
+
],
|
|
626
|
+
},
|
|
627
|
+
{},
|
|
628
|
+
{ model: 'gpt-5.4-mini' }
|
|
629
|
+
);
|
|
630
|
+
|
|
631
|
+
console.log(response.citations); // [{ url, title, start, end }]
|
|
632
|
+
console.log(response.usage?.tools); // { web_search: 1, ... }
|
|
633
|
+
```
|
|
634
|
+
|
|
635
|
+
`web_fetch` is not available on OpenAI (web search fetches pages itself), and web search can only allow domains, not block them (`blockedDomains` is rejected). Images from `image_generation` come back on `response.images`.
|
|
636
|
+
|
|
637
|
+
### Audio Output
|
|
638
|
+
|
|
639
|
+
```typescript
|
|
640
|
+
const response = await executor(
|
|
641
|
+
{
|
|
642
|
+
messages: [{ role: 'user', content: 'Say hello in French.' }],
|
|
643
|
+
modalities: ['text', 'audio'],
|
|
644
|
+
audio: { voice: 'alloy', format: 'mp3' }, // streaming defaults to pcm
|
|
645
|
+
},
|
|
646
|
+
{},
|
|
647
|
+
{ model: 'gpt-audio-1.5' }
|
|
648
|
+
);
|
|
649
|
+
|
|
650
|
+
console.log(response.audio?.transcript); // response.audio.data is base64
|
|
651
|
+
```
|
|
652
|
+
|
|
653
|
+
### Other Request Options
|
|
654
|
+
|
|
655
|
+
- `seed`, `verbosity` (`'low' | 'medium' | 'high'`), `serviceTier` (`'auto' | 'default' | 'flex' | 'fast' | 'scale'`), and `metadata` are passed through. Chat Completions sends `metadata` only when `store` is on.
|
|
656
|
+
- `moderate: true` runs inline moderation (`omni-moderation-latest`); verdicts come back on `response.moderation`.
|
|
657
|
+
- Content parts take `cacheBreakpoint` (a prompt cache breakpoint), `detail` for images, and `name` for files.
|
|
658
|
+
- `response.serviceTier` reports the tier used (`priority` reads as `fast`).
|
|
659
|
+
|
|
660
|
+
### Tool Features
|
|
661
|
+
|
|
662
|
+
- `toolChoice: { allowed: [...], required? }` is sent as `allowed_tools` on both APIs: every tool is still sent (keeping the prompt cacheable), but only the allowed ones may be called.
|
|
663
|
+
- A tool with `format: { type: 'text' }` or `format: { type: 'grammar', syntax: 'lark' | 'regex', definition }` is a Responses custom tool: the model writes free text, constrained by the grammar, and it arrives as `{ input }` arguments (`TEXT_TOOL_SCHEMA` from `@aeye/core`). With `api: 'chat'` it is a one-field function whose description carries the grammar.
|
|
664
|
+
- `defer: true` sends the tool with `defer_loading` and adds the hosted `tool_search` tool, so it is loaded only when the model searches for it.
|
|
665
|
+
- `async: true` marks an async tool; the model keeps working while it runs.
|
|
666
|
+
|
|
667
|
+
```typescript
|
|
668
|
+
import { TEXT_TOOL_SCHEMA } from '@aeye/core';
|
|
669
|
+
|
|
670
|
+
const response = await executor(
|
|
671
|
+
{
|
|
672
|
+
messages: [{ role: 'user', content: 'How many orders did customer 42 place?' }],
|
|
673
|
+
tools: [{
|
|
674
|
+
name: 'sql',
|
|
675
|
+
description: 'Run a read-only SQL query',
|
|
676
|
+
parameters: TEXT_TOOL_SCHEMA,
|
|
677
|
+
format: { type: 'grammar', syntax: 'regex', definition: 'SELECT [^;]+;' },
|
|
678
|
+
}],
|
|
679
|
+
},
|
|
680
|
+
{},
|
|
681
|
+
{ model: 'gpt-5.4-mini' }
|
|
682
|
+
);
|
|
683
|
+
|
|
684
|
+
console.log(response.toolCalls?.[0]?.arguments); // {"input":"SELECT COUNT(*) FROM orders WHERE customer_id = 42;"}
|
|
685
|
+
```
|
|
686
|
+
|
|
687
|
+
### Decisions
|
|
688
|
+
|
|
689
|
+
Typed answers from `/v1/decisions` (default model `gpt-6-luna`, or `defaultModels.decision`), through `ai.decide`:
|
|
690
|
+
|
|
691
|
+
```typescript
|
|
692
|
+
import { AI } from '@aeye/ai';
|
|
693
|
+
|
|
694
|
+
const ai = AI.with().providers({ openai: provider }).create();
|
|
695
|
+
|
|
696
|
+
const { answers } = await ai.decide.get({
|
|
697
|
+
input: 'I was charged twice for my order.',
|
|
698
|
+
questions: {
|
|
699
|
+
billing: { type: 'predicate', instructions: 'Is this a billing problem?' },
|
|
700
|
+
urgency: { type: 'score', instructions: 'How urgent is it?', levels: ['Can wait', 'This week', 'Now'] },
|
|
701
|
+
},
|
|
702
|
+
});
|
|
703
|
+
```
|
|
704
|
+
|
|
705
|
+
Decisions report through the `decide` hooks.
|
|
706
|
+
|
|
707
|
+
### Batches
|
|
708
|
+
|
|
709
|
+
`ai.batch` uploads chat requests as a JSONL file and runs them as one batch with a 24 hour window, at about half the price. Results come back in request order; a failed request has `error`.
|
|
710
|
+
|
|
711
|
+
```typescript
|
|
712
|
+
const job = await ai.batch.submit(
|
|
713
|
+
questions.map(question => ({ messages: [{ role: 'user', content: question }] })),
|
|
714
|
+
{ metadata: { model: 'gpt-5.4-mini' } },
|
|
715
|
+
);
|
|
716
|
+
|
|
717
|
+
const done = await ai.batch.wait(job, {});
|
|
718
|
+
done.results?.forEach(result => console.log(result.response?.content ?? result.error));
|
|
719
|
+
```
|
|
720
|
+
|
|
721
|
+
### Advanced Features
|
|
722
|
+
|
|
723
|
+
#### Abort Requests
|
|
724
|
+
|
|
725
|
+
```typescript
|
|
726
|
+
const controller = new AbortController();
|
|
727
|
+
|
|
728
|
+
// Set timeout
|
|
729
|
+
setTimeout(() => controller.abort(), 5000);
|
|
730
|
+
|
|
731
|
+
try {
|
|
732
|
+
const response = await executor(
|
|
733
|
+
{ messages: [{ role: 'user', content: 'Tell me a long story' }] },
|
|
734
|
+
{},
|
|
735
|
+
{ model: 'gpt-5.4-mini' },
|
|
736
|
+
controller.signal
|
|
737
|
+
);
|
|
738
|
+
} catch (error) {
|
|
739
|
+
if (error.name === 'AbortError') {
|
|
740
|
+
console.log('Request was aborted');
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
```
|
|
744
|
+
|
|
745
|
+
#### List Available Models
|
|
746
|
+
|
|
747
|
+
```typescript
|
|
748
|
+
const models = await provider.listModels();
|
|
749
|
+
|
|
750
|
+
models.forEach(model => {
|
|
751
|
+
console.log(`${model.id}: ${model.capabilities}`);
|
|
752
|
+
console.log(` Context window: ${model.contextWindow}`);
|
|
753
|
+
console.log(` Pricing: $${model.pricing.text.input}/1M input tokens`);
|
|
754
|
+
});
|
|
755
|
+
```
|
|
756
|
+
|
|
757
|
+
#### Check Provider Health
|
|
758
|
+
|
|
759
|
+
```typescript
|
|
760
|
+
const isHealthy = await provider.checkHealth();
|
|
761
|
+
console.log('OpenAI API is', isHealthy ? 'accessible' : 'not accessible');
|
|
762
|
+
```
|
|
763
|
+
|
|
764
|
+
#### Default Metadata
|
|
765
|
+
|
|
766
|
+
```typescript
|
|
767
|
+
// Set default metadata for all requests
|
|
768
|
+
provider.defaultMetadata = {
|
|
769
|
+
model: 'gpt-5.4-mini',
|
|
770
|
+
user: 'user-123',
|
|
771
|
+
};
|
|
772
|
+
|
|
773
|
+
// Now model is automatically set
|
|
774
|
+
const response = await executor(
|
|
775
|
+
{ messages: [{ role: 'user', content: 'Hello' }] },
|
|
776
|
+
{}
|
|
777
|
+
// No need to pass { model: 'gpt-5.4-mini' }
|
|
778
|
+
);
|
|
779
|
+
```
|
|
780
|
+
|
|
781
|
+
## Extending for Custom Providers
|
|
782
|
+
|
|
783
|
+
The `OpenAIProvider` class can be extended to support OpenAI-compatible APIs like Azure OpenAI, OpenRouter, or custom deployments.
|
|
784
|
+
|
|
785
|
+
### Example: Custom Provider
|
|
786
|
+
|
|
787
|
+
```typescript
|
|
788
|
+
import type { Request } from '@aeye/core';
|
|
789
|
+
import { OpenAIProvider, OpenAIConfig } from '@aeye/openai';
|
|
790
|
+
import OpenAI from 'openai';
|
|
791
|
+
|
|
792
|
+
interface CustomConfig extends OpenAIConfig {
|
|
793
|
+
customOption?: string;
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
class CustomProvider extends OpenAIProvider<CustomConfig> {
|
|
797
|
+
readonly name = 'custom-provider';
|
|
798
|
+
|
|
799
|
+
protected createClient(config: CustomConfig): OpenAI {
|
|
800
|
+
return new OpenAI({
|
|
801
|
+
apiKey: config.apiKey,
|
|
802
|
+
baseURL: 'https://api.custom-provider.com/v1',
|
|
803
|
+
defaultHeaders: {
|
|
804
|
+
'X-Custom-Header': config.customOption || '',
|
|
805
|
+
},
|
|
806
|
+
});
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
protected augmentChatRequest<TParams extends OpenAI.Chat.ChatCompletionCreateParams>(
|
|
810
|
+
params: TParams,
|
|
811
|
+
request: Request,
|
|
812
|
+
config: CustomConfig,
|
|
813
|
+
) {
|
|
814
|
+
// Add custom parameters (mutate in place)
|
|
815
|
+
Object.assign(params, { custom_param: config.customOption });
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
// Use the custom provider
|
|
820
|
+
const customProvider = new CustomProvider({
|
|
821
|
+
apiKey: process.env.CUSTOM_API_KEY!,
|
|
822
|
+
customOption: 'value',
|
|
823
|
+
});
|
|
824
|
+
```
|
|
825
|
+
|
|
826
|
+
### Example: Azure OpenAI
|
|
827
|
+
|
|
828
|
+
```typescript
|
|
829
|
+
class AzureOpenAIProvider extends OpenAIProvider {
|
|
830
|
+
readonly name = 'azure-openai';
|
|
831
|
+
|
|
832
|
+
protected createClient(config: OpenAIConfig): OpenAI {
|
|
833
|
+
return new OpenAI({
|
|
834
|
+
apiKey: config.apiKey,
|
|
835
|
+
baseURL: config.baseURL, // Azure endpoint
|
|
836
|
+
defaultQuery: { 'api-version': '2024-02-01' },
|
|
837
|
+
});
|
|
838
|
+
}
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
const azureProvider = new AzureOpenAIProvider({
|
|
842
|
+
apiKey: process.env.AZURE_OPENAI_KEY!,
|
|
843
|
+
baseURL: 'https://your-resource.openai.azure.com/openai/deployments/your-deployment',
|
|
844
|
+
});
|
|
845
|
+
```
|
|
846
|
+
|
|
847
|
+
## Configuration Options
|
|
848
|
+
|
|
849
|
+
### OpenAIConfig
|
|
850
|
+
|
|
851
|
+
| Property | Type | Required | Description |
|
|
852
|
+
|----------|------|----------|-------------|
|
|
853
|
+
| `apiKey` | `string` | Yes | OpenAI API key |
|
|
854
|
+
| `baseURL` | `string` | No | Custom base URL for API endpoint |
|
|
855
|
+
| `organization` | `string` | No | OpenAI organization ID |
|
|
856
|
+
| `project` | `string` | No | OpenAI project ID |
|
|
857
|
+
| `store` | `boolean` | No | Let OpenAI store requests (default `false`) |
|
|
858
|
+
| `api` | `'auto' \| 'chat' \| 'responses'` | No | Which API chat uses (default `'auto'`) |
|
|
859
|
+
| `retry` / `retryEvents` | `RetryConfig` / `RetryEvents` | No | Retry behavior and events |
|
|
860
|
+
| `hooks` | `OpenAIHooks` | No | `beforeRequest` / `afterRequest` / `onError` for `chat`, `responses`, `imageGenerate`, `imageEdit`, `transcribe`, `speech`, `embed`, `rerank`, `decide` |
|
|
861
|
+
| `defaultModels` | `object` | No | Per-operation defaults; built in: `gpt-image-2` (generate, edit), `gpt-5.4-mini` (image analysis), `gpt-transcribe`, `gpt-4o-mini-tts`, `text-embedding-3-small`, `gpt-6-luna` (decision); `rerank` has no OpenAI default |
|
|
862
|
+
|
|
863
|
+
### Request Parameters
|
|
864
|
+
|
|
865
|
+
#### Chat Completion Request
|
|
866
|
+
|
|
867
|
+
| Parameter | Type | Description |
|
|
868
|
+
|-----------|------|-------------|
|
|
869
|
+
| `messages` | `Message[]` | Array of conversation messages |
|
|
870
|
+
| `temperature` | `number` | Sampling temperature (0-2) |
|
|
871
|
+
| `topP` | `number` | Nucleus sampling parameter |
|
|
872
|
+
| `maxTokens` | `number` | Maximum tokens to generate |
|
|
873
|
+
| `stop` | `string \| string[]` | Stop sequences |
|
|
874
|
+
| `tools` | `Tool[]` | Function definitions for tool calling |
|
|
875
|
+
| `toolsOneAtATime` | `boolean` | Sends `parallel_tool_calls: false`. Otherwise Chat Completions leaves the parameter out (parallel calls are the default, and o3 rejects the parameter) |
|
|
876
|
+
| `toolChoice` | `'auto' \| 'required' \| 'none' \| { tool: string } \| { allowed: string[]; required?: boolean }` | Tool selection mode |
|
|
877
|
+
| `responseFormat` | `'text' \| 'json' \| ZodSchema` | Response format constraint |
|
|
878
|
+
| `reason` | `Reason` | `{ effort, summary, mode, context }` |
|
|
879
|
+
| `seed` / `verbosity` / `serviceTier` / `metadata` | | Passed through |
|
|
880
|
+
| `modalities` / `audio` | | Audio output |
|
|
881
|
+
| `hostedTools` | `HostedTool[]` | Tools OpenAI runs (Responses API) |
|
|
882
|
+
| `moderate` | `boolean` | Inline moderation |
|
|
883
|
+
|
|
884
|
+
#### Image Generation Request
|
|
885
|
+
|
|
886
|
+
| Parameter | Type | Description |
|
|
887
|
+
|-----------|------|-------------|
|
|
888
|
+
| `prompt` | `string` | Text description of desired image |
|
|
889
|
+
| `model` | `string` | GPT Image model (default gpt-image-2) |
|
|
890
|
+
| `size` | `string` | `WIDTHxHEIGHT` or `'auto'`; snaps to the model's sizes |
|
|
891
|
+
| `quality` | `'auto' \| 'low' \| 'medium' \| 'high' \| 'xhigh' \| 'max'` | Image quality (models accept subsets) |
|
|
892
|
+
| `background` | `'transparent' \| 'opaque' \| 'auto'` | Background |
|
|
893
|
+
| `outputFormat` | `'png' \| 'jpeg' \| 'webp'` | Encoding |
|
|
894
|
+
| `outputCompression` | `number` | 0-100, for jpeg and webp |
|
|
895
|
+
| `moderation` | `'auto' \| 'low'` | Moderation strictness (generate) |
|
|
896
|
+
| `references` / `mask` / `inputFidelity` | | Extra input images, mask, and detail preservation (edit) |
|
|
897
|
+
| `n` | `number` | Number of images to generate |
|
|
898
|
+
| `streamCount` | `number` | Partial images to stream |
|
|
899
|
+
|
|
900
|
+
#### Transcription Request
|
|
901
|
+
|
|
902
|
+
| Parameter | Type | Description |
|
|
903
|
+
|-----------|------|-------------|
|
|
904
|
+
| `audio` | `Buffer \| string \| Blob` | Audio file to transcribe |
|
|
905
|
+
| `model` | `string` | Model to use (default gpt-transcribe) |
|
|
906
|
+
| `language` | `string` | ISO-639-1 language code |
|
|
907
|
+
| `prompt` | `string` | Context for better accuracy |
|
|
908
|
+
| `responseFormat` | `'json' \| 'text' \| 'srt' \| 'vtt' \| 'verbose_json' \| 'diarized_json'` | Output format |
|
|
909
|
+
| `knownSpeakers` | `{ name, audio }[]` | Reference clips for diarization |
|
|
910
|
+
| `timestampGranularities` | `('word' \| 'segment')[]` | Timestamp detail level |
|
|
911
|
+
| `temperature` | `number` | Sampling temperature |
|
|
912
|
+
|
|
913
|
+
#### Speech Synthesis Request
|
|
914
|
+
|
|
915
|
+
| Parameter | Type | Description |
|
|
916
|
+
|-----------|------|-------------|
|
|
917
|
+
| `text` | `string` | Text to convert to speech |
|
|
918
|
+
| `model` | `string` | Model to use (default gpt-4o-mini-tts) |
|
|
919
|
+
| `voice` | `string` | Built-in voice name or custom voice id (default alloy) |
|
|
920
|
+
| `instructions` | `string` | Style instructions |
|
|
921
|
+
| `speed` | `number` | Playback speed (0.25-4.0) |
|
|
922
|
+
| `responseFormat` | `'mp3' \| 'opus' \| 'aac' \| 'flac' \| 'wav' \| 'pcm'` | Audio format |
|
|
923
|
+
|
|
924
|
+
#### Embedding Request
|
|
925
|
+
|
|
926
|
+
| Parameter | Type | Description |
|
|
927
|
+
|-----------|------|-------------|
|
|
928
|
+
| `texts` | `string[]` | Texts to embed |
|
|
929
|
+
| `model` | `string` | Model to use (text-embedding-3-small, text-embedding-3-large, text-embedding-ada-002) |
|
|
930
|
+
| `dimensions` | `number` | Output dimensions (embedding-3 models only) |
|
|
931
|
+
| `encodingFormat` | `'float' \| 'base64'` | Encoding format |
|
|
932
|
+
|
|
933
|
+
## Error Handling
|
|
934
|
+
|
|
935
|
+
The provider throws specific error types for different failure scenarios:
|
|
936
|
+
|
|
937
|
+
```typescript
|
|
938
|
+
import { ProviderError, RateLimitError, ProviderAuthError } from '@aeye/openai';
|
|
939
|
+
|
|
940
|
+
try {
|
|
941
|
+
const response = await executor(
|
|
942
|
+
{ messages: [{ role: 'user', content: 'Hello' }] },
|
|
943
|
+
{},
|
|
944
|
+
{ model: 'gpt-5.4-mini' }
|
|
945
|
+
);
|
|
946
|
+
} catch (error) {
|
|
947
|
+
if (error instanceof RateLimitError) {
|
|
948
|
+
console.error('Rate limit exceeded');
|
|
949
|
+
if (error.retryAfter) {
|
|
950
|
+
console.log(`Retry after ${error.retryAfter} seconds`);
|
|
951
|
+
}
|
|
952
|
+
} else if (error instanceof ProviderAuthError) {
|
|
953
|
+
console.error('Authentication failed - check your API key');
|
|
954
|
+
} else if (error instanceof ProviderError) {
|
|
955
|
+
console.error(`Provider error: ${error.message}`);
|
|
956
|
+
console.error('Original error:', error.cause);
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
```
|
|
960
|
+
|
|
961
|
+
### Error Types
|
|
962
|
+
|
|
963
|
+
- **`ProviderError`**: Base error for all provider-related errors
|
|
964
|
+
- **`ProviderAuthError`**: Authentication/authorization failures
|
|
965
|
+
- **`RateLimitError`**: Rate limit exceeded (includes `retryAfter` if available)
|
|
966
|
+
- **`ProviderRateLimitError`**: Specialized rate limit error
|
|
967
|
+
- **`ProviderQuotaError`**: Quota/usage limit exceeded
|
|
968
|
+
|
|
969
|
+
## API Reference
|
|
970
|
+
|
|
971
|
+
### OpenAIProvider
|
|
972
|
+
|
|
973
|
+
Main provider class implementing the @aeye Provider interface.
|
|
974
|
+
|
|
975
|
+
**Constructor**: `new OpenAIProvider(config: OpenAIConfig)`
|
|
976
|
+
|
|
977
|
+
**Methods**:
|
|
978
|
+
- `listModels(config?: TConfig): Promise<ModelInfo[]>` - List available models
|
|
979
|
+
- `checkHealth(config?: TConfig): Promise<boolean>` - Check API health
|
|
980
|
+
- `createExecutor<TContext, TMetadata>(config?: TConfig): Executor` - Create chat executor
|
|
981
|
+
- `createStreamer<TContext, TMetadata>(config?: TConfig): Streamer` - Create streaming executor
|
|
982
|
+
- `generateImage<TContext>(request, ctx, config?): Promise<ImageGenerationResponse>` - Generate images
|
|
983
|
+
- `editImage<TContext>(request, ctx, config?): Promise<ImageGenerationResponse>` - Edit images
|
|
984
|
+
- `transcribe<TContext>(request, ctx, config?): Promise<TranscriptionResponse>` - Transcribe audio
|
|
985
|
+
- `speech(request, ctx, config?): Promise<SpeechResponse>` - Generate speech
|
|
986
|
+
- `embed<TContext>(request, ctx, config?): Promise<EmbeddingResponse>` - Generate embeddings
|
|
987
|
+
- `decide(request, ctx, config?): Promise<DecisionResponse>` - Answer decision questions
|
|
988
|
+
- `submitBatch(requests, ctx, config?): Promise<BatchJob>` / `getBatch(job, ctx, config?): Promise<BatchJob>` - Run and read batches
|
|
989
|
+
|
|
990
|
+
**Protected Methods** (for extending):
|
|
991
|
+
- `createClient(config): OpenAI` - Create OpenAI client
|
|
992
|
+
- `convertModel(model): ModelInfo` - Convert model format
|
|
993
|
+
- `convertMessages(request): OpenAI.ChatCompletionMessageParam[]` - Convert messages
|
|
994
|
+
- `convertTools(request): OpenAI.ChatCompletionTool[]` - Convert tools
|
|
995
|
+
- `convertToolChoice(request): OpenAI.ChatCompletionToolChoiceOption` - Convert tool choice
|
|
996
|
+
- `convertResponseFormat(request): ResponseFormat` - Convert response format
|
|
997
|
+
- `augmentChatRequest(params, request, config)` - Add to Chat Completions params (mutate in place)
|
|
998
|
+
- `chatHeaders(params)` - Extra HTTP headers for a Chat Completions request (none by default; OpenRouter adds a feature flag here)
|
|
999
|
+
- `augmentChatMessage(chatMessage, message, config)` / `augmentChatContent(...)` - Add to converted messages and content parts
|
|
1000
|
+
- `augmentChatResponse(completion, response, config)` / `augmentChatChunk(chunk, ...)` - Read extra fields from responses and chunks
|
|
1001
|
+
- `augmentImageGenerateRequest?` / `augmentImageEditRequest?` / `augmentTranscriptionRequest?` / `augmentSpeechRequest?` / `augmentEmbeddingRequest?` `(params, request, config)` - Optional hooks to add to those params
|
|
1002
|
+
- `usesResponses(request, modelId, config, model?)` - Whether a chat request goes to the Responses API
|
|
1003
|
+
- `createBatch(batch, run)` / `retrieveBatch(job, run)` - Submit and read batches (OpenRouter overrides these for inline batches)
|
|
1004
|
+
- `send(op, run, request, params, call)` / `finish(op, run, request, params, response)` - Run an API call with retries inside the operation's `beforeRequest` / `onError` hooks, then report its response to `afterRequest` (OpenRouter's `rerank` and `decide` use these)
|
|
1005
|
+
|
|
1006
|
+
## Model Support
|
|
1007
|
+
|
|
1008
|
+
### Chat Models
|
|
1009
|
+
|
|
1010
|
+
- **GPT-6**: `gpt-6-luna`, `gpt-6-sol`, `gpt-6-astra`, `gpt-6.1-sol`, `gpt-6.1-terra`
|
|
1011
|
+
- **GPT-5.x**: `gpt-5.6-*`, `gpt-5.5`, `gpt-5.4`, `gpt-5.4-mini`, `gpt-5.4-nano`, `chat-latest`
|
|
1012
|
+
- **Pro and codex** (Responses API): `gpt-5.5-pro`, `gpt-5.4-pro`, `gpt-5.3-codex`
|
|
1013
|
+
- **GPT-4.1 / GPT-4o** (retiring): `gpt-4.1`, `gpt-4.1-mini`, `gpt-4o`, `gpt-4o-mini`
|
|
1014
|
+
- **Audio**: `gpt-audio-1.5`
|
|
1015
|
+
|
|
1016
|
+
### Image Models
|
|
1017
|
+
|
|
1018
|
+
- **GPT Image**: `gpt-image-2`, `gpt-image-2.5-*`
|
|
1019
|
+
|
|
1020
|
+
### Audio Models
|
|
1021
|
+
|
|
1022
|
+
- **Transcription**: `gpt-transcribe`, `gpt-4o-transcribe`, `gpt-4o-transcribe-diarize`, `whisper-1`
|
|
1023
|
+
- **TTS**: `gpt-4o-mini-tts`, `tts-1`, `tts-1-hd`
|
|
1024
|
+
|
|
1025
|
+
The `@aeye/models` registry records each model's `retiresAt` date; retired models are never selected.
|
|
1026
|
+
|
|
1027
|
+
### Embedding Models
|
|
1028
|
+
|
|
1029
|
+
- **Embedding v3**: `text-embedding-3-small`, `text-embedding-3-large`
|
|
1030
|
+
- **Embedding v2**: `text-embedding-ada-002`
|
|
1031
|
+
|
|
1032
|
+
## Best Practices
|
|
1033
|
+
|
|
1034
|
+
1. **API Key Security**: Never hardcode API keys. Use environment variables or secure key management.
|
|
1035
|
+
|
|
1036
|
+
2. **Error Handling**: Always wrap API calls in try-catch blocks and handle rate limits gracefully.
|
|
1037
|
+
|
|
1038
|
+
3. **Streaming for Long Responses**: Use streaming for better user experience with lengthy responses.
|
|
1039
|
+
|
|
1040
|
+
4. **Token Management**: Monitor token usage to control costs. Use `maxTokens` to limit response length.
|
|
1041
|
+
|
|
1042
|
+
5. **Model Selection**: Choose the appropriate model for your use case:
|
|
1043
|
+
- GPT-6 or GPT-5.5 for complex tasks requiring high accuracy
|
|
1044
|
+
- `gpt-5.4-mini` / `gpt-5.4-nano` for simpler tasks and faster responses
|
|
1045
|
+
- A higher `reason.effort` for reasoning-heavy problems
|
|
1046
|
+
|
|
1047
|
+
6. **Function Calling**: Use structured schemas with Zod for type-safe function definitions.
|
|
1048
|
+
|
|
1049
|
+
7. **Retry Logic**: Implement exponential backoff for rate limit errors.
|
|
1050
|
+
|
|
1051
|
+
8. **Context Window**: Be aware of model context limits and truncate messages if needed.
|
|
1052
|
+
|
|
1053
|
+
## Related Packages
|
|
1054
|
+
|
|
1055
|
+
- **[@aeye/core](../core)**: Core @aeye framework types and interfaces
|
|
1056
|
+
- **[@aeye/ai](../ai)**: AI abstractions and utilities
|
|
1057
|
+
- **[@aeye/anthropic](../anthropic)**: Anthropic Claude provider
|
|
1058
|
+
- **[@aeye/openrouter](../openrouter)**: OpenRouter multi-provider gateway
|
|
1059
|
+
|
|
1060
|
+
## License
|
|
1061
|
+
|
|
1062
|
+
GPL-3.0
|
|
1063
|
+
|
|
1064
|
+
## Contributing
|
|
1065
|
+
|
|
1066
|
+
Contributions are welcome! Please see the main [@aeye repository](https://github.com/ClickerMonkey/aeye) for contribution guidelines.
|
|
1067
|
+
|
|
1068
|
+
## Support
|
|
1069
|
+
|
|
1070
|
+
For issues and questions:
|
|
1071
|
+
- GitHub Issues: https://github.com/ClickerMonkey/aeye/issues
|
|
1072
|
+
- Documentation: https://github.com/ClickerMonkey/aeye
|
|
1073
|
+
|
|
1074
|
+
## Changelog
|
|
1075
|
+
|
|
1076
|
+
See [CHANGELOG.md](CHANGELOG.md) for version history and updates.
|