@aeye/openai 0.3.10 → 0.4.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 +267 -119
- package/aeye-openai.md +189 -180
- 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 +72 -72
package/README.md
CHANGED
|
@@ -4,14 +4,18 @@ OpenAI provider for the @aeye (AI TypeScript) framework. This package provides a
|
|
|
4
4
|
|
|
5
5
|
## Features
|
|
6
6
|
|
|
7
|
-
- **Chat
|
|
8
|
-
- **Vision**:
|
|
9
|
-
- **Reasoning
|
|
10
|
-
- **
|
|
11
|
-
- **
|
|
12
|
-
- **
|
|
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
|
|
13
15
|
- **Embeddings**: Text embeddings for semantic search and similarity
|
|
14
|
-
- **
|
|
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
|
|
15
19
|
- **Structured Outputs**: JSON mode and schema-based structured outputs
|
|
16
20
|
- **Streaming**: Real-time streaming responses for chat completions
|
|
17
21
|
- **Multi-modal**: Support for text, images, audio, and files in conversations
|
|
@@ -23,6 +27,8 @@ OpenAI provider for the @aeye (AI TypeScript) framework. This package provides a
|
|
|
23
27
|
npm install @aeye/openai @aeye/ai @aeye/core openai zod
|
|
24
28
|
```
|
|
25
29
|
|
|
30
|
+
Requires Node >= 22 and the `openai` SDK v7.
|
|
31
|
+
|
|
26
32
|
## Quick Start
|
|
27
33
|
|
|
28
34
|
```typescript
|
|
@@ -44,7 +50,7 @@ const response = await executor(
|
|
|
44
50
|
]
|
|
45
51
|
},
|
|
46
52
|
{},
|
|
47
|
-
{ model: 'gpt-4' }
|
|
53
|
+
{ model: 'gpt-5.4-mini' }
|
|
48
54
|
);
|
|
49
55
|
|
|
50
56
|
console.log(response.content);
|
|
@@ -65,6 +71,17 @@ const config: OpenAIConfig = {
|
|
|
65
71
|
const provider = new OpenAIProvider(config);
|
|
66
72
|
```
|
|
67
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
|
+
|
|
68
85
|
### Custom Base URL (for Azure OpenAI or compatible APIs)
|
|
69
86
|
|
|
70
87
|
```typescript
|
|
@@ -93,7 +110,7 @@ const response = await executor(
|
|
|
93
110
|
maxTokens: 150,
|
|
94
111
|
},
|
|
95
112
|
{},
|
|
96
|
-
{ model: 'gpt-4' }
|
|
113
|
+
{ model: 'gpt-5.4-mini' }
|
|
97
114
|
);
|
|
98
115
|
|
|
99
116
|
console.log(response.content);
|
|
@@ -112,7 +129,7 @@ for await (const chunk of streamer(
|
|
|
112
129
|
]
|
|
113
130
|
},
|
|
114
131
|
{},
|
|
115
|
-
{ model: 'gpt-4' }
|
|
132
|
+
{ model: 'gpt-5.4-mini' }
|
|
116
133
|
)) {
|
|
117
134
|
if (chunk.content) {
|
|
118
135
|
process.stdout.write(chunk.content);
|
|
@@ -132,7 +149,7 @@ const conversation = [
|
|
|
132
149
|
let response = await executor(
|
|
133
150
|
{ messages: conversation },
|
|
134
151
|
{},
|
|
135
|
-
{ model: 'gpt-4' }
|
|
152
|
+
{ model: 'gpt-5.4-mini' }
|
|
136
153
|
);
|
|
137
154
|
|
|
138
155
|
conversation.push({ role: 'assistant', content: response.content });
|
|
@@ -142,11 +159,11 @@ conversation.push({ role: 'user', content: 'Can you give me an example?' });
|
|
|
142
159
|
response = await executor(
|
|
143
160
|
{ messages: conversation },
|
|
144
161
|
{},
|
|
145
|
-
{ model: 'gpt-4' }
|
|
162
|
+
{ model: 'gpt-5.4-mini' }
|
|
146
163
|
);
|
|
147
164
|
```
|
|
148
165
|
|
|
149
|
-
### Vision
|
|
166
|
+
### Vision
|
|
150
167
|
|
|
151
168
|
#### Analyze an Image
|
|
152
169
|
|
|
@@ -168,7 +185,7 @@ const response = await executor(
|
|
|
168
185
|
]
|
|
169
186
|
},
|
|
170
187
|
{},
|
|
171
|
-
{ model: 'gpt-4-
|
|
188
|
+
{ model: 'gpt-5.4-mini' }
|
|
172
189
|
);
|
|
173
190
|
```
|
|
174
191
|
|
|
@@ -189,7 +206,7 @@ const response = await executor(
|
|
|
189
206
|
]
|
|
190
207
|
},
|
|
191
208
|
{},
|
|
192
|
-
{ model: 'gpt-4-
|
|
209
|
+
{ model: 'gpt-5.4-mini' }
|
|
193
210
|
);
|
|
194
211
|
```
|
|
195
212
|
|
|
@@ -215,10 +232,10 @@ const response = await executor(
|
|
|
215
232
|
}),
|
|
216
233
|
}
|
|
217
234
|
],
|
|
218
|
-
toolChoice: 'auto', //
|
|
235
|
+
toolChoice: 'auto', // 'auto', 'required', 'none', { tool: 'tool_name' }, or { allowed: ['tool_name'], required? }
|
|
219
236
|
},
|
|
220
237
|
{},
|
|
221
|
-
{ model: 'gpt-4' }
|
|
238
|
+
{ model: 'gpt-5.4-mini' }
|
|
222
239
|
);
|
|
223
240
|
|
|
224
241
|
if (response.toolCalls) {
|
|
@@ -242,7 +259,7 @@ if (response.toolCalls) {
|
|
|
242
259
|
]
|
|
243
260
|
},
|
|
244
261
|
{},
|
|
245
|
-
{ model: 'gpt-4' }
|
|
262
|
+
{ model: 'gpt-5.4-mini' }
|
|
246
263
|
);
|
|
247
264
|
}
|
|
248
265
|
}
|
|
@@ -268,7 +285,7 @@ const response = await executor(
|
|
|
268
285
|
toolChoice: { tool: 'get_weather' }, // Force this specific tool
|
|
269
286
|
},
|
|
270
287
|
{},
|
|
271
|
-
{ model: 'gpt-4' }
|
|
288
|
+
{ model: 'gpt-5.4-mini' }
|
|
272
289
|
);
|
|
273
290
|
```
|
|
274
291
|
|
|
@@ -288,7 +305,7 @@ const response = await executor(
|
|
|
288
305
|
responseFormat: 'json', // Ensures valid JSON output
|
|
289
306
|
},
|
|
290
307
|
{},
|
|
291
|
-
{ model: 'gpt-4' }
|
|
308
|
+
{ model: 'gpt-5.4-mini' }
|
|
292
309
|
);
|
|
293
310
|
|
|
294
311
|
const scientists = JSON.parse(response.content);
|
|
@@ -317,7 +334,7 @@ const response = await executor(
|
|
|
317
334
|
responseFormat: PersonSchema, // Strict schema enforcement
|
|
318
335
|
},
|
|
319
336
|
{},
|
|
320
|
-
{ model: 'gpt-4' }
|
|
337
|
+
{ model: 'gpt-5.4-mini' }
|
|
321
338
|
);
|
|
322
339
|
|
|
323
340
|
// Response is guaranteed to match the schema
|
|
@@ -325,60 +342,44 @@ const person = JSON.parse(response.content);
|
|
|
325
342
|
console.log(person.name, person.age); // Type-safe
|
|
326
343
|
```
|
|
327
344
|
|
|
328
|
-
### Image Generation (
|
|
345
|
+
### Image Generation (GPT Image)
|
|
329
346
|
|
|
330
|
-
#### Generate
|
|
347
|
+
#### Generate an Image
|
|
331
348
|
|
|
332
349
|
```typescript
|
|
333
350
|
const imageResponse = await provider.generateImage(
|
|
334
351
|
{
|
|
335
352
|
prompt: 'A serene landscape with mountains and a lake at sunset',
|
|
336
|
-
model: '
|
|
337
|
-
size: '1024x1024',
|
|
338
|
-
quality: '
|
|
339
|
-
|
|
353
|
+
model: 'gpt-image-2', // the default
|
|
354
|
+
size: '1024x1024', // or 'auto'; other sizes snap to the nearest the model supports
|
|
355
|
+
quality: 'high', // 'auto' | 'low' | 'medium' | 'high' | 'xhigh' | 'max' (per model)
|
|
356
|
+
outputFormat: 'webp', // 'png' | 'jpeg' | 'webp'
|
|
357
|
+
outputCompression: 80, // 0-100, for jpeg and webp
|
|
358
|
+
background: 'transparent',
|
|
340
359
|
n: 1,
|
|
341
360
|
},
|
|
342
361
|
{}
|
|
343
362
|
);
|
|
344
363
|
|
|
345
|
-
|
|
346
|
-
console.log('Revised prompt:', imageResponse.images[0].revisedPrompt);
|
|
364
|
+
const base64Image = imageResponse.images[0].b64_json;
|
|
347
365
|
```
|
|
348
366
|
|
|
349
|
-
####
|
|
367
|
+
#### Edit with Reference Images
|
|
350
368
|
|
|
351
369
|
```typescript
|
|
352
|
-
const
|
|
370
|
+
const edited = await provider.editImage(
|
|
353
371
|
{
|
|
354
|
-
prompt: '
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
372
|
+
prompt: 'Put the product from the second image on the table in the first',
|
|
373
|
+
image: fs.readFileSync('room.png'), // source image
|
|
374
|
+
references: [fs.readFileSync('product.png')], // more input images
|
|
375
|
+
mask: fs.readFileSync('mask.png'), // optional, over the source image
|
|
376
|
+
inputFidelity: 'high', // keep input details closely
|
|
358
377
|
},
|
|
359
378
|
{}
|
|
360
379
|
);
|
|
361
|
-
|
|
362
|
-
imageResponse.images.forEach((img, i) => {
|
|
363
|
-
console.log(`Image ${i + 1}:`, img.url);
|
|
364
|
-
});
|
|
365
380
|
```
|
|
366
381
|
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
```typescript
|
|
370
|
-
const imageResponse = await provider.generateImage(
|
|
371
|
-
{
|
|
372
|
-
prompt: 'A futuristic city skyline',
|
|
373
|
-
model: 'dall-e-3',
|
|
374
|
-
responseFormat: 'b64_json', // Get base64 instead of URL
|
|
375
|
-
},
|
|
376
|
-
{}
|
|
377
|
-
);
|
|
378
|
-
|
|
379
|
-
const base64Image = imageResponse.images[0].b64_json;
|
|
380
|
-
// Save or process the base64 data
|
|
381
|
-
```
|
|
382
|
+
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`.
|
|
382
383
|
|
|
383
384
|
### Speech Synthesis (TTS)
|
|
384
385
|
|
|
@@ -386,12 +387,14 @@ const base64Image = imageResponse.images[0].b64_json;
|
|
|
386
387
|
|
|
387
388
|
```typescript
|
|
388
389
|
import fs from 'fs';
|
|
390
|
+
import { Readable } from 'stream';
|
|
389
391
|
|
|
390
392
|
const speechResponse = await provider.generateSpeech(
|
|
391
393
|
{
|
|
392
394
|
text: 'Hello! This is a text-to-speech example using OpenAI.',
|
|
393
|
-
model: 'tts
|
|
394
|
-
voice: 'alloy', //
|
|
395
|
+
model: 'gpt-4o-mini-tts', // the default
|
|
396
|
+
voice: 'alloy', // any built-in voice name or a custom voice id
|
|
397
|
+
instructions: 'Speak warmly and a little slowly.',
|
|
395
398
|
speed: 1.0, // 0.25 to 4.0
|
|
396
399
|
responseFormat: 'mp3', // mp3, opus, aac, flac, wav, pcm
|
|
397
400
|
},
|
|
@@ -399,7 +402,7 @@ const speechResponse = await provider.generateSpeech(
|
|
|
399
402
|
);
|
|
400
403
|
|
|
401
404
|
// Save to file
|
|
402
|
-
fs.
|
|
405
|
+
Readable.fromWeb(speechResponse.audio).pipe(fs.createWriteStream('output.mp3'));
|
|
403
406
|
console.log('Audio saved to output.mp3');
|
|
404
407
|
```
|
|
405
408
|
|
|
@@ -413,16 +416,16 @@ for (const voice of voices) {
|
|
|
413
416
|
{
|
|
414
417
|
text: `This is the ${voice} voice speaking.`,
|
|
415
418
|
voice,
|
|
416
|
-
model: 'tts
|
|
419
|
+
model: 'gpt-4o-mini-tts',
|
|
417
420
|
},
|
|
418
421
|
{}
|
|
419
422
|
);
|
|
420
423
|
|
|
421
|
-
fs.
|
|
424
|
+
Readable.fromWeb(response.audio).pipe(fs.createWriteStream(`voice_${voice}.mp3`));
|
|
422
425
|
}
|
|
423
426
|
```
|
|
424
427
|
|
|
425
|
-
### Transcription
|
|
428
|
+
### Transcription
|
|
426
429
|
|
|
427
430
|
#### Transcribe Audio File
|
|
428
431
|
|
|
@@ -434,7 +437,7 @@ const audioBuffer = fs.readFileSync('audio.mp3');
|
|
|
434
437
|
const transcription = await provider.transcribe(
|
|
435
438
|
{
|
|
436
439
|
audio: audioBuffer,
|
|
437
|
-
model: '
|
|
440
|
+
model: 'gpt-transcribe', // the default
|
|
438
441
|
language: 'en', // Optional: ISO-639-1 language code
|
|
439
442
|
prompt: 'This is a podcast about AI and technology', // Optional: context
|
|
440
443
|
},
|
|
@@ -458,13 +461,6 @@ const transcription = await provider.transcribe(
|
|
|
458
461
|
);
|
|
459
462
|
|
|
460
463
|
console.log('Full text:', transcription.text);
|
|
461
|
-
console.log('Language:', transcription.language);
|
|
462
|
-
console.log('Duration:', transcription.duration);
|
|
463
|
-
|
|
464
|
-
// Word-level timestamps
|
|
465
|
-
transcription.words?.forEach(word => {
|
|
466
|
-
console.log(`${word.word}: ${word.start}s - ${word.end}s`);
|
|
467
|
-
});
|
|
468
464
|
|
|
469
465
|
// Segment-level timestamps
|
|
470
466
|
transcription.segments?.forEach(segment => {
|
|
@@ -472,6 +468,27 @@ transcription.segments?.forEach(segment => {
|
|
|
472
468
|
});
|
|
473
469
|
```
|
|
474
470
|
|
|
471
|
+
#### Speaker Diarization
|
|
472
|
+
|
|
473
|
+
```typescript
|
|
474
|
+
const transcription = await provider.transcribe(
|
|
475
|
+
{
|
|
476
|
+
audio: fs.readFileSync('meeting.mp3'),
|
|
477
|
+
model: 'gpt-4o-transcribe-diarize',
|
|
478
|
+
responseFormat: 'diarized_json',
|
|
479
|
+
knownSpeakers: [
|
|
480
|
+
{ name: 'Alice', audio: fs.readFileSync('alice.wav') },
|
|
481
|
+
{ name: 'Bob', audio: fs.readFileSync('bob.wav') },
|
|
482
|
+
],
|
|
483
|
+
},
|
|
484
|
+
{}
|
|
485
|
+
);
|
|
486
|
+
|
|
487
|
+
transcription.segments?.forEach(segment => {
|
|
488
|
+
console.log(`${segment.speaker}: ${segment.text}`);
|
|
489
|
+
});
|
|
490
|
+
```
|
|
491
|
+
|
|
475
492
|
#### Different Output Formats
|
|
476
493
|
|
|
477
494
|
```typescript
|
|
@@ -559,11 +576,7 @@ console.log('Similarity 1-2:', cosineSimilarity(emb1, emb2)); // High similarity
|
|
|
559
576
|
console.log('Similarity 1-3:', cosineSimilarity(emb1, emb3)); // Low similarity
|
|
560
577
|
```
|
|
561
578
|
|
|
562
|
-
### Reasoning
|
|
563
|
-
|
|
564
|
-
#### Using o1 Models
|
|
565
|
-
|
|
566
|
-
The o1 and o3-mini models use extended thinking to solve complex problems.
|
|
579
|
+
### Reasoning
|
|
567
580
|
|
|
568
581
|
```typescript
|
|
569
582
|
const response = await executor(
|
|
@@ -574,21 +587,132 @@ const response = await executor(
|
|
|
574
587
|
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?'
|
|
575
588
|
}
|
|
576
589
|
],
|
|
577
|
-
|
|
590
|
+
reason: {
|
|
591
|
+
effort: 'high', // 'none' | 'minimal' | 'low' | 'medium' | 'high' | 'xhigh' | 'max'
|
|
592
|
+
summary: 'auto', // a readable summary (uses the Responses API)
|
|
593
|
+
},
|
|
578
594
|
},
|
|
579
595
|
{},
|
|
580
|
-
{ model: '
|
|
596
|
+
{ model: 'gpt-5.5' }
|
|
581
597
|
);
|
|
582
598
|
|
|
583
|
-
console.log('Reasoning:', response.reasoning);
|
|
599
|
+
console.log('Reasoning:', response.reasoning?.content);
|
|
584
600
|
console.log('Answer:', response.content);
|
|
585
601
|
```
|
|
586
602
|
|
|
587
|
-
|
|
588
|
-
-
|
|
589
|
-
-
|
|
590
|
-
|
|
591
|
-
|
|
603
|
+
- The effort is clamped to the model's accepted efforts when its `ModelInfo.reasoningEfforts` lists them.
|
|
604
|
+
- `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.
|
|
605
|
+
- `reason.maxTokens` and `reason.exclude` are not used by OpenAI.
|
|
606
|
+
|
|
607
|
+
### Hosted Tools
|
|
608
|
+
|
|
609
|
+
Tools OpenAI runs itself go in `hostedTools` and use the Responses API:
|
|
610
|
+
|
|
611
|
+
```typescript
|
|
612
|
+
const response = await executor(
|
|
613
|
+
{
|
|
614
|
+
messages: [{ role: 'user', content: 'Summarize this week\'s TypeScript news.' }],
|
|
615
|
+
hostedTools: [
|
|
616
|
+
{ type: 'web_search', allowedDomains: ['devblogs.microsoft.com'] },
|
|
617
|
+
{ type: 'code_interpreter' },
|
|
618
|
+
{ type: 'file_search', vectorStoreIds: ['vs_123'], maxResults: 5 },
|
|
619
|
+
{ type: 'mcp', label: 'docs', url: 'https://example.com/mcp', allowedTools: ['search'] },
|
|
620
|
+
{ type: 'image_generation' },
|
|
621
|
+
],
|
|
622
|
+
},
|
|
623
|
+
{},
|
|
624
|
+
{ model: 'gpt-5.4-mini' }
|
|
625
|
+
);
|
|
626
|
+
|
|
627
|
+
console.log(response.citations); // [{ url, title, start, end }]
|
|
628
|
+
console.log(response.usage?.tools); // { web_search: 1, ... }
|
|
629
|
+
```
|
|
630
|
+
|
|
631
|
+
`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`.
|
|
632
|
+
|
|
633
|
+
### Audio Output
|
|
634
|
+
|
|
635
|
+
```typescript
|
|
636
|
+
const response = await executor(
|
|
637
|
+
{
|
|
638
|
+
messages: [{ role: 'user', content: 'Say hello in French.' }],
|
|
639
|
+
modalities: ['text', 'audio'],
|
|
640
|
+
audio: { voice: 'alloy', format: 'mp3' }, // streaming defaults to pcm
|
|
641
|
+
},
|
|
642
|
+
{},
|
|
643
|
+
{ model: 'gpt-audio-1.5' }
|
|
644
|
+
);
|
|
645
|
+
|
|
646
|
+
console.log(response.audio?.transcript); // response.audio.data is base64
|
|
647
|
+
```
|
|
648
|
+
|
|
649
|
+
### Other Request Options
|
|
650
|
+
|
|
651
|
+
- `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.
|
|
652
|
+
- `moderate: true` runs inline moderation (`omni-moderation-latest`); verdicts come back on `response.moderation`.
|
|
653
|
+
- Content parts take `cacheBreakpoint` (a prompt cache breakpoint), `detail` for images, and `name` for files.
|
|
654
|
+
- `response.serviceTier` reports the tier used (`priority` reads as `fast`).
|
|
655
|
+
|
|
656
|
+
### Tool Features
|
|
657
|
+
|
|
658
|
+
- `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.
|
|
659
|
+
- 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.
|
|
660
|
+
- `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.
|
|
661
|
+
- `async: true` marks an async tool; the model keeps working while it runs.
|
|
662
|
+
|
|
663
|
+
```typescript
|
|
664
|
+
import { TEXT_TOOL_SCHEMA } from '@aeye/core';
|
|
665
|
+
|
|
666
|
+
const response = await executor(
|
|
667
|
+
{
|
|
668
|
+
messages: [{ role: 'user', content: 'How many orders did customer 42 place?' }],
|
|
669
|
+
tools: [{
|
|
670
|
+
name: 'sql',
|
|
671
|
+
description: 'Run a read-only SQL query',
|
|
672
|
+
parameters: TEXT_TOOL_SCHEMA,
|
|
673
|
+
format: { type: 'grammar', syntax: 'regex', definition: 'SELECT [^;]+;' },
|
|
674
|
+
}],
|
|
675
|
+
},
|
|
676
|
+
{},
|
|
677
|
+
{ model: 'gpt-5.4-mini' }
|
|
678
|
+
);
|
|
679
|
+
|
|
680
|
+
console.log(response.toolCalls?.[0]?.arguments); // {"input":"SELECT COUNT(*) FROM orders WHERE customer_id = 42;"}
|
|
681
|
+
```
|
|
682
|
+
|
|
683
|
+
### Decisions
|
|
684
|
+
|
|
685
|
+
Typed answers from `/v1/decisions` (default model `gpt-6-luna`, or `defaultModels.decision`), through `ai.decide`:
|
|
686
|
+
|
|
687
|
+
```typescript
|
|
688
|
+
import { AI } from '@aeye/ai';
|
|
689
|
+
|
|
690
|
+
const ai = AI.with().providers({ openai: provider }).create();
|
|
691
|
+
|
|
692
|
+
const { answers } = await ai.decide.get({
|
|
693
|
+
input: 'I was charged twice for my order.',
|
|
694
|
+
questions: {
|
|
695
|
+
billing: { type: 'predicate', instructions: 'Is this a billing problem?' },
|
|
696
|
+
urgency: { type: 'score', instructions: 'How urgent is it?', levels: ['Can wait', 'This week', 'Now'] },
|
|
697
|
+
},
|
|
698
|
+
});
|
|
699
|
+
```
|
|
700
|
+
|
|
701
|
+
Decisions report through the `decide` hooks.
|
|
702
|
+
|
|
703
|
+
### Batches
|
|
704
|
+
|
|
705
|
+
`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`.
|
|
706
|
+
|
|
707
|
+
```typescript
|
|
708
|
+
const job = await ai.batch.submit(
|
|
709
|
+
questions.map(question => ({ messages: [{ role: 'user', content: question }] })),
|
|
710
|
+
{ metadata: { model: 'gpt-5.4-mini' } },
|
|
711
|
+
);
|
|
712
|
+
|
|
713
|
+
const done = await ai.batch.wait(job, {});
|
|
714
|
+
done.results?.forEach(result => console.log(result.response?.content ?? result.error));
|
|
715
|
+
```
|
|
592
716
|
|
|
593
717
|
### Advanced Features
|
|
594
718
|
|
|
@@ -604,7 +728,7 @@ try {
|
|
|
604
728
|
const response = await executor(
|
|
605
729
|
{ messages: [{ role: 'user', content: 'Tell me a long story' }] },
|
|
606
730
|
{},
|
|
607
|
-
{ model: 'gpt-4' },
|
|
731
|
+
{ model: 'gpt-5.4-mini' },
|
|
608
732
|
controller.signal
|
|
609
733
|
);
|
|
610
734
|
} catch (error) {
|
|
@@ -638,7 +762,7 @@ console.log('OpenAI API is', isHealthy ? 'accessible' : 'not accessible');
|
|
|
638
762
|
```typescript
|
|
639
763
|
// Set default metadata for all requests
|
|
640
764
|
provider.defaultMetadata = {
|
|
641
|
-
model: 'gpt-4',
|
|
765
|
+
model: 'gpt-5.4-mini',
|
|
642
766
|
user: 'user-123',
|
|
643
767
|
};
|
|
644
768
|
|
|
@@ -646,7 +770,7 @@ provider.defaultMetadata = {
|
|
|
646
770
|
const response = await executor(
|
|
647
771
|
{ messages: [{ role: 'user', content: 'Hello' }] },
|
|
648
772
|
{}
|
|
649
|
-
// No need to pass { model: 'gpt-4' }
|
|
773
|
+
// No need to pass { model: 'gpt-5.4-mini' }
|
|
650
774
|
);
|
|
651
775
|
```
|
|
652
776
|
|
|
@@ -657,6 +781,7 @@ The `OpenAIProvider` class can be extended to support OpenAI-compatible APIs lik
|
|
|
657
781
|
### Example: Custom Provider
|
|
658
782
|
|
|
659
783
|
```typescript
|
|
784
|
+
import type { Request } from '@aeye/core';
|
|
660
785
|
import { OpenAIProvider, OpenAIConfig } from '@aeye/openai';
|
|
661
786
|
import OpenAI from 'openai';
|
|
662
787
|
|
|
@@ -677,12 +802,13 @@ class CustomProvider extends OpenAIProvider<CustomConfig> {
|
|
|
677
802
|
});
|
|
678
803
|
}
|
|
679
804
|
|
|
680
|
-
protected
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
805
|
+
protected augmentChatRequest<TParams extends OpenAI.Chat.ChatCompletionCreateParams>(
|
|
806
|
+
params: TParams,
|
|
807
|
+
request: Request,
|
|
808
|
+
config: CustomConfig,
|
|
809
|
+
) {
|
|
810
|
+
// Add custom parameters (mutate in place)
|
|
811
|
+
Object.assign(params, { custom_param: config.customOption });
|
|
686
812
|
}
|
|
687
813
|
}
|
|
688
814
|
|
|
@@ -723,6 +849,12 @@ const azureProvider = new AzureOpenAIProvider({
|
|
|
723
849
|
| `apiKey` | `string` | Yes | OpenAI API key |
|
|
724
850
|
| `baseURL` | `string` | No | Custom base URL for API endpoint |
|
|
725
851
|
| `organization` | `string` | No | OpenAI organization ID |
|
|
852
|
+
| `project` | `string` | No | OpenAI project ID |
|
|
853
|
+
| `store` | `boolean` | No | Let OpenAI store requests (default `false`) |
|
|
854
|
+
| `api` | `'auto' \| 'chat' \| 'responses'` | No | Which API chat uses (default `'auto'`) |
|
|
855
|
+
| `retry` / `retryEvents` | `RetryConfig` / `RetryEvents` | No | Retry behavior and events |
|
|
856
|
+
| `hooks` | `OpenAIHooks` | No | `beforeRequest` / `afterRequest` / `onError` for `chat`, `responses`, `imageGenerate`, `imageEdit`, `transcribe`, `speech`, `embed` |
|
|
857
|
+
| `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 |
|
|
726
858
|
|
|
727
859
|
### Request Parameters
|
|
728
860
|
|
|
@@ -736,30 +868,40 @@ const azureProvider = new AzureOpenAIProvider({
|
|
|
736
868
|
| `maxTokens` | `number` | Maximum tokens to generate |
|
|
737
869
|
| `stop` | `string \| string[]` | Stop sequences |
|
|
738
870
|
| `tools` | `Tool[]` | Function definitions for tool calling |
|
|
739
|
-
| `toolChoice` | `'auto' \| 'required' \| 'none' \| { tool: string }` | Tool selection mode |
|
|
871
|
+
| `toolChoice` | `'auto' \| 'required' \| 'none' \| { tool: string } \| { allowed: string[]; required?: boolean }` | Tool selection mode |
|
|
740
872
|
| `responseFormat` | `'text' \| 'json' \| ZodSchema` | Response format constraint |
|
|
873
|
+
| `reason` | `Reason` | `{ effort, summary, mode, context }` |
|
|
874
|
+
| `seed` / `verbosity` / `serviceTier` / `metadata` | | Passed through |
|
|
875
|
+
| `modalities` / `audio` | | Audio output |
|
|
876
|
+
| `hostedTools` | `HostedTool[]` | Tools OpenAI runs (Responses API) |
|
|
877
|
+
| `moderate` | `boolean` | Inline moderation |
|
|
741
878
|
|
|
742
879
|
#### Image Generation Request
|
|
743
880
|
|
|
744
881
|
| Parameter | Type | Description |
|
|
745
882
|
|-----------|------|-------------|
|
|
746
883
|
| `prompt` | `string` | Text description of desired image |
|
|
747
|
-
| `model` | `string` |
|
|
748
|
-
| `size` | `string` |
|
|
749
|
-
| `quality` | `'
|
|
750
|
-
| `
|
|
884
|
+
| `model` | `string` | GPT Image model (default gpt-image-2) |
|
|
885
|
+
| `size` | `string` | `WIDTHxHEIGHT` or `'auto'`; snaps to the model's sizes |
|
|
886
|
+
| `quality` | `'auto' \| 'low' \| 'medium' \| 'high' \| 'xhigh' \| 'max'` | Image quality (models accept subsets) |
|
|
887
|
+
| `background` | `'transparent' \| 'opaque' \| 'auto'` | Background |
|
|
888
|
+
| `outputFormat` | `'png' \| 'jpeg' \| 'webp'` | Encoding |
|
|
889
|
+
| `outputCompression` | `number` | 0-100, for jpeg and webp |
|
|
890
|
+
| `moderation` | `'auto' \| 'low'` | Moderation strictness (generate) |
|
|
891
|
+
| `references` / `mask` / `inputFidelity` | | Extra input images, mask, and detail preservation (edit) |
|
|
751
892
|
| `n` | `number` | Number of images to generate |
|
|
752
|
-
| `
|
|
893
|
+
| `streamCount` | `number` | Partial images to stream |
|
|
753
894
|
|
|
754
895
|
#### Transcription Request
|
|
755
896
|
|
|
756
897
|
| Parameter | Type | Description |
|
|
757
898
|
|-----------|------|-------------|
|
|
758
899
|
| `audio` | `Buffer \| string \| Blob` | Audio file to transcribe |
|
|
759
|
-
| `model` | `string` | Model to use (
|
|
900
|
+
| `model` | `string` | Model to use (default gpt-transcribe) |
|
|
760
901
|
| `language` | `string` | ISO-639-1 language code |
|
|
761
902
|
| `prompt` | `string` | Context for better accuracy |
|
|
762
|
-
| `responseFormat` | `'json' \| 'text' \| 'srt' \| 'vtt' \| 'verbose_json'` | Output format |
|
|
903
|
+
| `responseFormat` | `'json' \| 'text' \| 'srt' \| 'vtt' \| 'verbose_json' \| 'diarized_json'` | Output format |
|
|
904
|
+
| `knownSpeakers` | `{ name, audio }[]` | Reference clips for diarization |
|
|
763
905
|
| `timestampGranularities` | `('word' \| 'segment')[]` | Timestamp detail level |
|
|
764
906
|
| `temperature` | `number` | Sampling temperature |
|
|
765
907
|
|
|
@@ -768,8 +910,9 @@ const azureProvider = new AzureOpenAIProvider({
|
|
|
768
910
|
| Parameter | Type | Description |
|
|
769
911
|
|-----------|------|-------------|
|
|
770
912
|
| `text` | `string` | Text to convert to speech |
|
|
771
|
-
| `model` | `string` | Model to use (
|
|
772
|
-
| `voice` | `string` |
|
|
913
|
+
| `model` | `string` | Model to use (default gpt-4o-mini-tts) |
|
|
914
|
+
| `voice` | `string` | Built-in voice name or custom voice id (default alloy) |
|
|
915
|
+
| `instructions` | `string` | Style instructions |
|
|
773
916
|
| `speed` | `number` | Playback speed (0.25-4.0) |
|
|
774
917
|
| `responseFormat` | `'mp3' \| 'opus' \| 'aac' \| 'flac' \| 'wav' \| 'pcm'` | Audio format |
|
|
775
918
|
|
|
@@ -793,7 +936,7 @@ try {
|
|
|
793
936
|
const response = await executor(
|
|
794
937
|
{ messages: [{ role: 'user', content: 'Hello' }] },
|
|
795
938
|
{},
|
|
796
|
-
{ model: 'gpt-4' }
|
|
939
|
+
{ model: 'gpt-5.4-mini' }
|
|
797
940
|
);
|
|
798
941
|
} catch (error) {
|
|
799
942
|
if (error instanceof RateLimitError) {
|
|
@@ -832,9 +975,12 @@ Main provider class implementing the @aeye Provider interface.
|
|
|
832
975
|
- `createExecutor<TContext, TMetadata>(config?: TConfig): Executor` - Create chat executor
|
|
833
976
|
- `createStreamer<TContext, TMetadata>(config?: TConfig): Streamer` - Create streaming executor
|
|
834
977
|
- `generateImage<TContext>(request, ctx, config?): Promise<ImageGenerationResponse>` - Generate images
|
|
978
|
+
- `editImage<TContext>(request, ctx, config?): Promise<ImageGenerationResponse>` - Edit images
|
|
835
979
|
- `transcribe<TContext>(request, ctx, config?): Promise<TranscriptionResponse>` - Transcribe audio
|
|
836
|
-
- `
|
|
980
|
+
- `speech(request, ctx, config?): Promise<SpeechResponse>` - Generate speech
|
|
837
981
|
- `embed<TContext>(request, ctx, config?): Promise<EmbeddingResponse>` - Generate embeddings
|
|
982
|
+
- `decide(request, ctx, config?): Promise<DecisionResponse>` - Answer decision questions
|
|
983
|
+
- `submitBatch(requests, ctx, config?): Promise<BatchJob>` / `getBatch(job, ctx, config?): Promise<BatchJob>` - Run and read batches
|
|
838
984
|
|
|
839
985
|
**Protected Methods** (for extending):
|
|
840
986
|
- `createClient(config): OpenAI` - Create OpenAI client
|
|
@@ -843,31 +989,33 @@ Main provider class implementing the @aeye Provider interface.
|
|
|
843
989
|
- `convertTools(request): OpenAI.ChatCompletionTool[]` - Convert tools
|
|
844
990
|
- `convertToolChoice(request): OpenAI.ChatCompletionToolChoiceOption` - Convert tool choice
|
|
845
991
|
- `convertResponseFormat(request): ResponseFormat` - Convert response format
|
|
846
|
-
- `
|
|
847
|
-
- `
|
|
848
|
-
- `
|
|
849
|
-
- `
|
|
850
|
-
- `
|
|
992
|
+
- `augmentChatRequest(params, request, config)` - Add to Chat Completions params (mutate in place)
|
|
993
|
+
- `augmentChatMessage(chatMessage, message, config)` / `augmentChatContent(...)` - Add to converted messages and content parts
|
|
994
|
+
- `augmentChatResponse(completion, response, config)` / `augmentChatChunk(chunk, ...)` - Read extra fields from responses and chunks
|
|
995
|
+
- `augmentImageGenerateRequest?` / `augmentImageEditRequest?` / `augmentTranscriptionRequest?` / `augmentSpeechRequest?` / `augmentEmbeddingRequest?` `(params, request, config)` - Optional hooks to add to those params
|
|
996
|
+
- `usesResponses(request, modelId, config, model?)` - Whether a chat request goes to the Responses API
|
|
997
|
+
- `createBatch(batch, run)` / `retrieveBatch(job, run)` - Submit and read batches (OpenRouter overrides these for inline batches)
|
|
851
998
|
|
|
852
999
|
## Model Support
|
|
853
1000
|
|
|
854
1001
|
### Chat Models
|
|
855
1002
|
|
|
856
|
-
- **GPT-
|
|
857
|
-
- **GPT-
|
|
858
|
-
- **
|
|
859
|
-
- **GPT-
|
|
860
|
-
- **
|
|
1003
|
+
- **GPT-6**: `gpt-6-luna`, `gpt-6-sol`, `gpt-6-astra`, `gpt-6.1-sol`, `gpt-6.1-terra`
|
|
1004
|
+
- **GPT-5.x**: `gpt-5.6-*`, `gpt-5.5`, `gpt-5.4`, `gpt-5.4-mini`, `gpt-5.4-nano`, `chat-latest`
|
|
1005
|
+
- **Pro and codex** (Responses API): `gpt-5.5-pro`, `gpt-5.4-pro`, `gpt-5.3-codex`
|
|
1006
|
+
- **GPT-4.1 / GPT-4o** (retiring): `gpt-4.1`, `gpt-4.1-mini`, `gpt-4o`, `gpt-4o-mini`
|
|
1007
|
+
- **Audio**: `gpt-audio-1.5`
|
|
861
1008
|
|
|
862
1009
|
### Image Models
|
|
863
1010
|
|
|
864
|
-
- **
|
|
865
|
-
- **DALL-E 2**: `dall-e-2`
|
|
1011
|
+
- **GPT Image**: `gpt-image-2`, `gpt-image-2.5-*`
|
|
866
1012
|
|
|
867
1013
|
### Audio Models
|
|
868
1014
|
|
|
869
|
-
- **
|
|
870
|
-
- **TTS**: `tts-1`, `tts-1-hd`
|
|
1015
|
+
- **Transcription**: `gpt-transcribe`, `gpt-4o-transcribe`, `gpt-4o-transcribe-diarize`, `whisper-1`
|
|
1016
|
+
- **TTS**: `gpt-4o-mini-tts`, `tts-1`, `tts-1-hd`
|
|
1017
|
+
|
|
1018
|
+
The `@aeye/models` registry records each model's `retiresAt` date; retired models are never selected.
|
|
871
1019
|
|
|
872
1020
|
### Embedding Models
|
|
873
1021
|
|
|
@@ -885,9 +1033,9 @@ Main provider class implementing the @aeye Provider interface.
|
|
|
885
1033
|
4. **Token Management**: Monitor token usage to control costs. Use `maxTokens` to limit response length.
|
|
886
1034
|
|
|
887
1035
|
5. **Model Selection**: Choose the appropriate model for your use case:
|
|
888
|
-
- GPT-
|
|
889
|
-
-
|
|
890
|
-
-
|
|
1036
|
+
- GPT-6 or GPT-5.5 for complex tasks requiring high accuracy
|
|
1037
|
+
- `gpt-5.4-mini` / `gpt-5.4-nano` for simpler tasks and faster responses
|
|
1038
|
+
- A higher `reason.effort` for reasoning-heavy problems
|
|
891
1039
|
|
|
892
1040
|
6. **Function Calling**: Use structured schemas with Zod for type-safe function definitions.
|
|
893
1041
|
|