@aeye/openai 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,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 Completions**: Support for GPT-4, GPT-4 Turbo, GPT-3.5 Turbo, and other chat models
8
+ - **Vision**: GPT-4 Vision (GPT-4V) for image understanding
9
+ - **Reasoning Models**: o1, o1-mini, o3-mini for advanced reasoning tasks
10
+ - **Image Generation**: DALL-E 2 and DALL-E 3 for creating images from text
11
+ - **Speech-to-Text**: Whisper models for audio transcription
12
+ - **Text-to-Speech**: TTS models for voice synthesis
13
+ - **Embeddings**: Text embeddings for semantic search and similarity
14
+ - **Function Calling**: Tool/function calling with automatic schema conversion
15
+ - **Structured Outputs**: JSON mode and schema-based structured outputs
16
+ - **Streaming**: Real-time streaming responses for chat completions
17
+ - **Multi-modal**: Support for text, images, audio, and files in conversations
18
+ - **Extensible**: Base class for OpenAI-compatible providers (Azure OpenAI, OpenRouter, etc.)
19
+
20
+ ## Installation
21
+
22
+ ```bash
23
+ npm install @aeye/openai @aeye/ai @aeye/core openai zod
24
+ ```
25
+
26
+ ## Quick Start
27
+
28
+ ```typescript
29
+ import { OpenAIProvider } from '@aeye/openai';
30
+
31
+ // Create provider instance
32
+ const provider = new OpenAIProvider({
33
+ apiKey: process.env.OPENAI_API_KEY!,
34
+ });
35
+
36
+ // Create executor
37
+ const executor = provider.createExecutor();
38
+
39
+ // Make a request
40
+ const response = await executor(
41
+ {
42
+ messages: [
43
+ { role: 'user', content: 'Tell me a joke about TypeScript' }
44
+ ]
45
+ },
46
+ {},
47
+ { model: 'gpt-4' }
48
+ );
49
+
50
+ console.log(response.content);
51
+ ```
52
+
53
+ ## Configuration
54
+
55
+ ### Basic Configuration
56
+
57
+ ```typescript
58
+ import { OpenAIProvider, OpenAIConfig } from '@aeye/openai';
59
+
60
+ const config: OpenAIConfig = {
61
+ apiKey: process.env.OPENAI_API_KEY!,
62
+ organization: 'org-123456', // Optional: for multi-org accounts
63
+ };
64
+
65
+ const provider = new OpenAIProvider(config);
66
+ ```
67
+
68
+ ### Custom Base URL (for Azure OpenAI or compatible APIs)
69
+
70
+ ```typescript
71
+ const provider = new OpenAIProvider({
72
+ apiKey: process.env.AZURE_API_KEY!,
73
+ baseURL: 'https://your-resource.openai.azure.com/openai/deployments/your-deployment',
74
+ });
75
+ ```
76
+
77
+ ## Usage Examples
78
+
79
+ ### Chat Completions
80
+
81
+ #### Basic Chat
82
+
83
+ ```typescript
84
+ const executor = provider.createExecutor();
85
+
86
+ const response = await executor(
87
+ {
88
+ messages: [
89
+ { role: 'system', content: 'You are a helpful assistant.' },
90
+ { role: 'user', content: 'What is the capital of France?' }
91
+ ],
92
+ temperature: 0.7,
93
+ maxTokens: 150,
94
+ },
95
+ {},
96
+ { model: 'gpt-4' }
97
+ );
98
+
99
+ console.log(response.content);
100
+ console.log('Tokens used:', response.usage.totalTokens);
101
+ ```
102
+
103
+ #### Streaming Chat
104
+
105
+ ```typescript
106
+ const streamer = provider.createStreamer();
107
+
108
+ for await (const chunk of streamer(
109
+ {
110
+ messages: [
111
+ { role: 'user', content: 'Write a short story about a robot' }
112
+ ]
113
+ },
114
+ {},
115
+ { model: 'gpt-4' }
116
+ )) {
117
+ if (chunk.content) {
118
+ process.stdout.write(chunk.content);
119
+ }
120
+ }
121
+ ```
122
+
123
+ #### Multi-turn Conversation
124
+
125
+ ```typescript
126
+ const conversation = [
127
+ { role: 'system' as const, content: 'You are a helpful assistant.' },
128
+ { role: 'user' as const, content: 'What is TypeScript?' },
129
+ ];
130
+
131
+ // First response
132
+ let response = await executor(
133
+ { messages: conversation },
134
+ {},
135
+ { model: 'gpt-4' }
136
+ );
137
+
138
+ conversation.push({ role: 'assistant', content: response.content });
139
+ conversation.push({ role: 'user', content: 'Can you give me an example?' });
140
+
141
+ // Second response
142
+ response = await executor(
143
+ { messages: conversation },
144
+ {},
145
+ { model: 'gpt-4' }
146
+ );
147
+ ```
148
+
149
+ ### Vision (GPT-4V)
150
+
151
+ #### Analyze an Image
152
+
153
+ ```typescript
154
+ const response = await executor(
155
+ {
156
+ messages: [
157
+ {
158
+ role: 'user',
159
+ content: [
160
+ { type: 'text', content: 'What is in this image?' },
161
+ {
162
+ type: 'image',
163
+ content: 'https://example.com/image.jpg',
164
+ // Or use base64: content: 'data:image/jpeg;base64,/9j/4AAQ...'
165
+ }
166
+ ]
167
+ }
168
+ ]
169
+ },
170
+ {},
171
+ { model: 'gpt-4-vision-preview' }
172
+ );
173
+ ```
174
+
175
+ #### Multiple Images
176
+
177
+ ```typescript
178
+ const response = await executor(
179
+ {
180
+ messages: [
181
+ {
182
+ role: 'user',
183
+ content: [
184
+ { type: 'text', content: 'Compare these two images' },
185
+ { type: 'image', content: 'https://example.com/image1.jpg' },
186
+ { type: 'image', content: 'https://example.com/image2.jpg' }
187
+ ]
188
+ }
189
+ ]
190
+ },
191
+ {},
192
+ { model: 'gpt-4-vision-preview' }
193
+ );
194
+ ```
195
+
196
+ ### Function Calling
197
+
198
+ #### Define and Use Tools
199
+
200
+ ```typescript
201
+ import z from 'zod';
202
+
203
+ const response = await executor(
204
+ {
205
+ messages: [
206
+ { role: 'user', content: 'What is the weather like in San Francisco?' }
207
+ ],
208
+ tools: [
209
+ {
210
+ name: 'get_current_weather',
211
+ description: 'Get the current weather in a given location',
212
+ parameters: z.object({
213
+ location: z.string().describe('The city and state, e.g., San Francisco, CA'),
214
+ unit: z.enum(['celsius', 'fahrenheit']).optional(),
215
+ }),
216
+ }
217
+ ],
218
+ toolChoice: 'auto', // Can be 'auto', 'required', 'none', or { tool: 'tool_name' }
219
+ },
220
+ {},
221
+ { model: 'gpt-4' }
222
+ );
223
+
224
+ if (response.toolCalls) {
225
+ for (const toolCall of response.toolCalls) {
226
+ console.log('Tool:', toolCall.name);
227
+ console.log('Arguments:', JSON.parse(toolCall.arguments));
228
+
229
+ // Execute the function and send result back
230
+ const functionResult = await getWeather(JSON.parse(toolCall.arguments));
231
+
232
+ const finalResponse = await executor(
233
+ {
234
+ messages: [
235
+ { role: 'user', content: 'What is the weather like in San Francisco?' },
236
+ { role: 'assistant', content: '', toolCalls: [toolCall] },
237
+ {
238
+ role: 'tool',
239
+ content: JSON.stringify(functionResult),
240
+ toolCallId: toolCall.id,
241
+ }
242
+ ]
243
+ },
244
+ {},
245
+ { model: 'gpt-4' }
246
+ );
247
+ }
248
+ }
249
+ ```
250
+
251
+ #### Force Specific Tool
252
+
253
+ ```typescript
254
+ const response = await executor(
255
+ {
256
+ messages: [
257
+ { role: 'user', content: 'Get the weather for Boston' }
258
+ ],
259
+ tools: [
260
+ {
261
+ name: 'get_weather',
262
+ description: 'Get weather information',
263
+ parameters: z.object({
264
+ location: z.string(),
265
+ }),
266
+ }
267
+ ],
268
+ toolChoice: { tool: 'get_weather' }, // Force this specific tool
269
+ },
270
+ {},
271
+ { model: 'gpt-4' }
272
+ );
273
+ ```
274
+
275
+ ### Structured Outputs
276
+
277
+ #### JSON Mode
278
+
279
+ ```typescript
280
+ const response = await executor(
281
+ {
282
+ messages: [
283
+ {
284
+ role: 'user',
285
+ content: 'List three famous scientists and their main contributions in JSON format'
286
+ }
287
+ ],
288
+ responseFormat: 'json', // Ensures valid JSON output
289
+ },
290
+ {},
291
+ { model: 'gpt-4' }
292
+ );
293
+
294
+ const scientists = JSON.parse(response.content);
295
+ ```
296
+
297
+ #### Schema-based Structured Output
298
+
299
+ ```typescript
300
+ import z from 'zod';
301
+
302
+ const PersonSchema = z.object({
303
+ name: z.string(),
304
+ age: z.number(),
305
+ occupation: z.string(),
306
+ hobbies: z.array(z.string()),
307
+ });
308
+
309
+ const response = await executor(
310
+ {
311
+ messages: [
312
+ {
313
+ role: 'user',
314
+ content: 'Generate a profile for a fictional software engineer'
315
+ }
316
+ ],
317
+ responseFormat: PersonSchema, // Strict schema enforcement
318
+ },
319
+ {},
320
+ { model: 'gpt-4' }
321
+ );
322
+
323
+ // Response is guaranteed to match the schema
324
+ const person = JSON.parse(response.content);
325
+ console.log(person.name, person.age); // Type-safe
326
+ ```
327
+
328
+ ### Image Generation (DALL-E)
329
+
330
+ #### Generate with DALL-E 3
331
+
332
+ ```typescript
333
+ const imageResponse = await provider.generateImage(
334
+ {
335
+ prompt: 'A serene landscape with mountains and a lake at sunset',
336
+ model: 'dall-e-3',
337
+ size: '1024x1024',
338
+ quality: 'hd', // 'standard' or 'hd'
339
+ style: 'vivid', // 'vivid' or 'natural'
340
+ n: 1,
341
+ },
342
+ {}
343
+ );
344
+
345
+ console.log('Generated image URL:', imageResponse.images[0].url);
346
+ console.log('Revised prompt:', imageResponse.images[0].revisedPrompt);
347
+ ```
348
+
349
+ #### Generate Multiple Images with DALL-E 2
350
+
351
+ ```typescript
352
+ const imageResponse = await provider.generateImage(
353
+ {
354
+ prompt: 'A cute robot playing with a puppy',
355
+ model: 'dall-e-2',
356
+ size: '512x512',
357
+ n: 3, // Generate 3 variations
358
+ },
359
+ {}
360
+ );
361
+
362
+ imageResponse.images.forEach((img, i) => {
363
+ console.log(`Image ${i + 1}:`, img.url);
364
+ });
365
+ ```
366
+
367
+ #### Get Base64 Image Data
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
+
383
+ ### Speech Synthesis (TTS)
384
+
385
+ #### Generate Speech
386
+
387
+ ```typescript
388
+ import fs from 'fs';
389
+
390
+ const speechResponse = await provider.generateSpeech(
391
+ {
392
+ text: 'Hello! This is a text-to-speech example using OpenAI.',
393
+ model: 'tts-1', // or 'tts-1-hd' for higher quality
394
+ voice: 'alloy', // alloy, echo, fable, onyx, nova, shimmer
395
+ speed: 1.0, // 0.25 to 4.0
396
+ responseFormat: 'mp3', // mp3, opus, aac, flac, wav, pcm
397
+ },
398
+ {}
399
+ );
400
+
401
+ // Save to file
402
+ fs.writeFileSync('output.mp3', speechResponse.audioBuffer);
403
+ console.log('Audio saved to output.mp3');
404
+ ```
405
+
406
+ #### Different Voices
407
+
408
+ ```typescript
409
+ const voices = ['alloy', 'echo', 'fable', 'onyx', 'nova', 'shimmer'];
410
+
411
+ for (const voice of voices) {
412
+ const response = await provider.generateSpeech(
413
+ {
414
+ text: `This is the ${voice} voice speaking.`,
415
+ voice,
416
+ model: 'tts-1',
417
+ },
418
+ {}
419
+ );
420
+
421
+ fs.writeFileSync(`voice_${voice}.mp3`, response.audioBuffer);
422
+ }
423
+ ```
424
+
425
+ ### Transcription (Whisper)
426
+
427
+ #### Transcribe Audio File
428
+
429
+ ```typescript
430
+ import fs from 'fs';
431
+
432
+ const audioBuffer = fs.readFileSync('audio.mp3');
433
+
434
+ const transcription = await provider.transcribe(
435
+ {
436
+ audio: audioBuffer,
437
+ model: 'whisper-1',
438
+ language: 'en', // Optional: ISO-639-1 language code
439
+ prompt: 'This is a podcast about AI and technology', // Optional: context
440
+ },
441
+ {}
442
+ );
443
+
444
+ console.log('Transcription:', transcription.text);
445
+ ```
446
+
447
+ #### Transcribe with Timestamps
448
+
449
+ ```typescript
450
+ const transcription = await provider.transcribe(
451
+ {
452
+ audio: audioBuffer,
453
+ model: 'whisper-1',
454
+ responseFormat: 'verbose_json',
455
+ timestampGranularities: ['word', 'segment'],
456
+ },
457
+ {}
458
+ );
459
+
460
+ 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
+
469
+ // Segment-level timestamps
470
+ transcription.segments?.forEach(segment => {
471
+ console.log(`${segment.text}: ${segment.start}s - ${segment.end}s`);
472
+ });
473
+ ```
474
+
475
+ #### Different Output Formats
476
+
477
+ ```typescript
478
+ // Plain text
479
+ const text = await provider.transcribe(
480
+ { audio: audioBuffer, responseFormat: 'text' },
481
+ {}
482
+ );
483
+
484
+ // SRT subtitles
485
+ const srt = await provider.transcribe(
486
+ { audio: audioBuffer, responseFormat: 'srt' },
487
+ {}
488
+ );
489
+
490
+ // VTT subtitles
491
+ const vtt = await provider.transcribe(
492
+ { audio: audioBuffer, responseFormat: 'vtt' },
493
+ {}
494
+ );
495
+ ```
496
+
497
+ ### Embeddings
498
+
499
+ #### Generate Embeddings
500
+
501
+ ```typescript
502
+ const embeddingResponse = await provider.embed(
503
+ {
504
+ texts: [
505
+ 'The quick brown fox jumps over the lazy dog',
506
+ 'Machine learning is a subset of artificial intelligence',
507
+ 'OpenAI develops advanced AI models'
508
+ ],
509
+ model: 'text-embedding-3-small', // or 'text-embedding-3-large'
510
+ },
511
+ {}
512
+ );
513
+
514
+ embeddingResponse.embeddings.forEach((item, i) => {
515
+ console.log(`Embedding ${i}:`, item.embedding.length, 'dimensions');
516
+ });
517
+
518
+ console.log('Tokens used:', embeddingResponse.usage?.inputTokens);
519
+ ```
520
+
521
+ #### Custom Dimensions (for smaller embeddings)
522
+
523
+ ```typescript
524
+ const embeddingResponse = await provider.embed(
525
+ {
526
+ texts: ['Some text to embed'],
527
+ model: 'text-embedding-3-large',
528
+ dimensions: 256, // Reduce from default 3072 to 256
529
+ },
530
+ {}
531
+ );
532
+ ```
533
+
534
+ #### Calculate Similarity
535
+
536
+ ```typescript
537
+ function cosineSimilarity(a: number[], b: number[]): number {
538
+ const dotProduct = a.reduce((sum, val, i) => sum + val * b[i], 0);
539
+ const magnitudeA = Math.sqrt(a.reduce((sum, val) => sum + val * val, 0));
540
+ const magnitudeB = Math.sqrt(b.reduce((sum, val) => sum + val * val, 0));
541
+ return dotProduct / (magnitudeA * magnitudeB);
542
+ }
543
+
544
+ const response = await provider.embed(
545
+ {
546
+ texts: [
547
+ 'I love programming in TypeScript',
548
+ 'TypeScript is my favorite language',
549
+ 'I enjoy cooking Italian food'
550
+ ],
551
+ model: 'text-embedding-3-small',
552
+ },
553
+ {}
554
+ );
555
+
556
+ const [emb1, emb2, emb3] = response.embeddings.map(e => e.embedding);
557
+
558
+ console.log('Similarity 1-2:', cosineSimilarity(emb1, emb2)); // High similarity
559
+ console.log('Similarity 1-3:', cosineSimilarity(emb1, emb3)); // Low similarity
560
+ ```
561
+
562
+ ### Reasoning Models (o1, o3-mini)
563
+
564
+ #### Using o1 Models
565
+
566
+ The o1 and o3-mini models use extended thinking to solve complex problems.
567
+
568
+ ```typescript
569
+ const response = await executor(
570
+ {
571
+ messages: [
572
+ {
573
+ role: 'user',
574
+ 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
+ }
576
+ ],
577
+ // Note: o1 models don't support temperature or system messages
578
+ },
579
+ {},
580
+ { model: 'o1-preview' } // or 'o1-mini', 'o3-mini'
581
+ );
582
+
583
+ console.log('Reasoning:', response.reasoning); // Internal reasoning process
584
+ console.log('Answer:', response.content);
585
+ ```
586
+
587
+ **Important**: o1 models have different capabilities:
588
+ - No system messages support (use user messages instead)
589
+ - No temperature control (model uses internal reasoning)
590
+ - No streaming support
591
+ - Extended processing time for complex problems
592
+
593
+ ### Advanced Features
594
+
595
+ #### Abort Requests
596
+
597
+ ```typescript
598
+ const controller = new AbortController();
599
+
600
+ // Set timeout
601
+ setTimeout(() => controller.abort(), 5000);
602
+
603
+ try {
604
+ const response = await executor(
605
+ { messages: [{ role: 'user', content: 'Tell me a long story' }] },
606
+ {},
607
+ { model: 'gpt-4' },
608
+ controller.signal
609
+ );
610
+ } catch (error) {
611
+ if (error.name === 'AbortError') {
612
+ console.log('Request was aborted');
613
+ }
614
+ }
615
+ ```
616
+
617
+ #### List Available Models
618
+
619
+ ```typescript
620
+ const models = await provider.listModels();
621
+
622
+ models.forEach(model => {
623
+ console.log(`${model.id}: ${model.capabilities}`);
624
+ console.log(` Context window: ${model.contextWindow}`);
625
+ console.log(` Pricing: $${model.pricing.inputTokensPer1M}/1M input tokens`);
626
+ });
627
+ ```
628
+
629
+ #### Check Provider Health
630
+
631
+ ```typescript
632
+ const isHealthy = await provider.checkHealth();
633
+ console.log('OpenAI API is', isHealthy ? 'accessible' : 'not accessible');
634
+ ```
635
+
636
+ #### Default Metadata
637
+
638
+ ```typescript
639
+ // Set default metadata for all requests
640
+ provider.defaultMetadata = {
641
+ model: 'gpt-4',
642
+ user: 'user-123',
643
+ };
644
+
645
+ // Now model is automatically set
646
+ const response = await executor(
647
+ { messages: [{ role: 'user', content: 'Hello' }] },
648
+ {}
649
+ // No need to pass { model: 'gpt-4' }
650
+ );
651
+ ```
652
+
653
+ ## Extending for Custom Providers
654
+
655
+ The `OpenAIProvider` class can be extended to support OpenAI-compatible APIs like Azure OpenAI, OpenRouter, or custom deployments.
656
+
657
+ ### Example: Custom Provider
658
+
659
+ ```typescript
660
+ import { OpenAIProvider, OpenAIConfig } from '@aeye/openai';
661
+ import OpenAI from 'openai';
662
+
663
+ interface CustomConfig extends OpenAIConfig {
664
+ customOption?: string;
665
+ }
666
+
667
+ class CustomProvider extends OpenAIProvider<CustomConfig> {
668
+ readonly name = 'custom-provider';
669
+
670
+ protected createClient(config: CustomConfig): OpenAI {
671
+ return new OpenAI({
672
+ apiKey: config.apiKey,
673
+ baseURL: 'https://api.custom-provider.com/v1',
674
+ defaultHeaders: {
675
+ 'X-Custom-Header': config.customOption || '',
676
+ },
677
+ });
678
+ }
679
+
680
+ protected customizeChatParams(params: any, config: CustomConfig, request: any) {
681
+ // Add custom parameters
682
+ return {
683
+ ...params,
684
+ custom_param: config.customOption,
685
+ };
686
+ }
687
+ }
688
+
689
+ // Use the custom provider
690
+ const customProvider = new CustomProvider({
691
+ apiKey: process.env.CUSTOM_API_KEY!,
692
+ customOption: 'value',
693
+ });
694
+ ```
695
+
696
+ ### Example: Azure OpenAI
697
+
698
+ ```typescript
699
+ class AzureOpenAIProvider extends OpenAIProvider {
700
+ readonly name = 'azure-openai';
701
+
702
+ protected createClient(config: OpenAIConfig): OpenAI {
703
+ return new OpenAI({
704
+ apiKey: config.apiKey,
705
+ baseURL: config.baseURL, // Azure endpoint
706
+ defaultQuery: { 'api-version': '2024-02-01' },
707
+ });
708
+ }
709
+ }
710
+
711
+ const azureProvider = new AzureOpenAIProvider({
712
+ apiKey: process.env.AZURE_OPENAI_KEY!,
713
+ baseURL: 'https://your-resource.openai.azure.com/openai/deployments/your-deployment',
714
+ });
715
+ ```
716
+
717
+ ## Configuration Options
718
+
719
+ ### OpenAIConfig
720
+
721
+ | Property | Type | Required | Description |
722
+ |----------|------|----------|-------------|
723
+ | `apiKey` | `string` | Yes | OpenAI API key |
724
+ | `baseURL` | `string` | No | Custom base URL for API endpoint |
725
+ | `organization` | `string` | No | OpenAI organization ID |
726
+
727
+ ### Request Parameters
728
+
729
+ #### Chat Completion Request
730
+
731
+ | Parameter | Type | Description |
732
+ |-----------|------|-------------|
733
+ | `messages` | `Message[]` | Array of conversation messages |
734
+ | `temperature` | `number` | Sampling temperature (0-2) |
735
+ | `topP` | `number` | Nucleus sampling parameter |
736
+ | `maxTokens` | `number` | Maximum tokens to generate |
737
+ | `stop` | `string \| string[]` | Stop sequences |
738
+ | `tools` | `Tool[]` | Function definitions for tool calling |
739
+ | `toolChoice` | `'auto' \| 'required' \| 'none' \| { tool: string }` | Tool selection mode |
740
+ | `responseFormat` | `'text' \| 'json' \| ZodSchema` | Response format constraint |
741
+
742
+ #### Image Generation Request
743
+
744
+ | Parameter | Type | Description |
745
+ |-----------|------|-------------|
746
+ | `prompt` | `string` | Text description of desired image |
747
+ | `model` | `string` | Model to use (dall-e-2, dall-e-3) |
748
+ | `size` | `string` | Image size (512x512, 1024x1024, etc.) |
749
+ | `quality` | `'standard' \| 'hd'` | Image quality (DALL-E 3 only) |
750
+ | `style` | `'vivid' \| 'natural'` | Image style (DALL-E 3 only) |
751
+ | `n` | `number` | Number of images to generate |
752
+ | `responseFormat` | `'url' \| 'b64_json'` | Response format |
753
+
754
+ #### Transcription Request
755
+
756
+ | Parameter | Type | Description |
757
+ |-----------|------|-------------|
758
+ | `audio` | `Buffer \| string \| Blob` | Audio file to transcribe |
759
+ | `model` | `string` | Model to use (whisper-1) |
760
+ | `language` | `string` | ISO-639-1 language code |
761
+ | `prompt` | `string` | Context for better accuracy |
762
+ | `responseFormat` | `'json' \| 'text' \| 'srt' \| 'vtt' \| 'verbose_json'` | Output format |
763
+ | `timestampGranularities` | `('word' \| 'segment')[]` | Timestamp detail level |
764
+ | `temperature` | `number` | Sampling temperature |
765
+
766
+ #### Speech Synthesis Request
767
+
768
+ | Parameter | Type | Description |
769
+ |-----------|------|-------------|
770
+ | `text` | `string` | Text to convert to speech |
771
+ | `model` | `string` | Model to use (tts-1, tts-1-hd) |
772
+ | `voice` | `string` | Voice ID (alloy, echo, fable, onyx, nova, shimmer) |
773
+ | `speed` | `number` | Playback speed (0.25-4.0) |
774
+ | `responseFormat` | `'mp3' \| 'opus' \| 'aac' \| 'flac' \| 'wav' \| 'pcm'` | Audio format |
775
+
776
+ #### Embedding Request
777
+
778
+ | Parameter | Type | Description |
779
+ |-----------|------|-------------|
780
+ | `texts` | `string[]` | Texts to embed |
781
+ | `model` | `string` | Model to use (text-embedding-3-small, text-embedding-3-large, text-embedding-ada-002) |
782
+ | `dimensions` | `number` | Output dimensions (embedding-3 models only) |
783
+ | `encodingFormat` | `'float' \| 'base64'` | Encoding format |
784
+
785
+ ## Error Handling
786
+
787
+ The provider throws specific error types for different failure scenarios:
788
+
789
+ ```typescript
790
+ import { ProviderError, RateLimitError, ProviderAuthError } from '@aeye/openai';
791
+
792
+ try {
793
+ const response = await executor(
794
+ { messages: [{ role: 'user', content: 'Hello' }] },
795
+ {},
796
+ { model: 'gpt-4' }
797
+ );
798
+ } catch (error) {
799
+ if (error instanceof RateLimitError) {
800
+ console.error('Rate limit exceeded');
801
+ if (error.retryAfter) {
802
+ console.log(`Retry after ${error.retryAfter} seconds`);
803
+ }
804
+ } else if (error instanceof ProviderAuthError) {
805
+ console.error('Authentication failed - check your API key');
806
+ } else if (error instanceof ProviderError) {
807
+ console.error(`Provider error: ${error.message}`);
808
+ console.error('Original error:', error.cause);
809
+ }
810
+ }
811
+ ```
812
+
813
+ ### Error Types
814
+
815
+ - **`ProviderError`**: Base error for all provider-related errors
816
+ - **`ProviderAuthError`**: Authentication/authorization failures
817
+ - **`RateLimitError`**: Rate limit exceeded (includes `retryAfter` if available)
818
+ - **`ProviderRateLimitError`**: Specialized rate limit error
819
+ - **`ProviderQuotaError`**: Quota/usage limit exceeded
820
+
821
+ ## API Reference
822
+
823
+ ### OpenAIProvider
824
+
825
+ Main provider class implementing the @aeye Provider interface.
826
+
827
+ **Constructor**: `new OpenAIProvider(config: OpenAIConfig)`
828
+
829
+ **Methods**:
830
+ - `listModels(config?: TConfig): Promise<ModelInfo[]>` - List available models
831
+ - `checkHealth(config?: TConfig): Promise<boolean>` - Check API health
832
+ - `createExecutor<TContext, TMetadata>(config?: TConfig): Executor` - Create chat executor
833
+ - `createStreamer<TContext, TMetadata>(config?: TConfig): Streamer` - Create streaming executor
834
+ - `generateImage<TContext>(request, ctx, config?): Promise<ImageGenerationResponse>` - Generate images
835
+ - `transcribe<TContext>(request, ctx, config?): Promise<TranscriptionResponse>` - Transcribe audio
836
+ - `generateSpeech<TContext>(request, ctx, config?): Promise<SpeechResponse>` - Generate speech
837
+ - `embed<TContext>(request, ctx, config?): Promise<EmbeddingResponse>` - Generate embeddings
838
+
839
+ **Protected Methods** (for extending):
840
+ - `createClient(config): OpenAI` - Create OpenAI client
841
+ - `convertModel(model): ModelInfo` - Convert model format
842
+ - `convertMessages(request): OpenAI.ChatCompletionMessageParam[]` - Convert messages
843
+ - `convertTools(request): OpenAI.ChatCompletionTool[]` - Convert tools
844
+ - `convertToolChoice(request): OpenAI.ChatCompletionToolChoiceOption` - Convert tool choice
845
+ - `convertResponseFormat(request): ResponseFormat` - Convert response format
846
+ - `customizeChatParams(params, config, request)` - Customize chat params
847
+ - `customizeImageParams(params, config)` - Customize image params
848
+ - `customizeTranscriptionParams(params, config)` - Customize transcription params
849
+ - `customizeSpeechParams(params, config)` - Customize speech params
850
+ - `customizeEmbeddingParams(params, config)` - Customize embedding params
851
+
852
+ ## Model Support
853
+
854
+ ### Chat Models
855
+
856
+ - **GPT-4 Turbo**: `gpt-4-turbo`, `gpt-4-turbo-preview`
857
+ - **GPT-4**: `gpt-4`, `gpt-4-0613`, `gpt-4-32k`
858
+ - **GPT-4 Vision**: `gpt-4-vision-preview`, `gpt-4-turbo-2024-04-09`
859
+ - **GPT-3.5 Turbo**: `gpt-3.5-turbo`, `gpt-3.5-turbo-16k`
860
+ - **Reasoning**: `o1-preview`, `o1-mini`, `o3-mini`
861
+
862
+ ### Image Models
863
+
864
+ - **DALL-E 3**: `dall-e-3`
865
+ - **DALL-E 2**: `dall-e-2`
866
+
867
+ ### Audio Models
868
+
869
+ - **Whisper**: `whisper-1`
870
+ - **TTS**: `tts-1`, `tts-1-hd`
871
+
872
+ ### Embedding Models
873
+
874
+ - **Embedding v3**: `text-embedding-3-small`, `text-embedding-3-large`
875
+ - **Embedding v2**: `text-embedding-ada-002`
876
+
877
+ ## Best Practices
878
+
879
+ 1. **API Key Security**: Never hardcode API keys. Use environment variables or secure key management.
880
+
881
+ 2. **Error Handling**: Always wrap API calls in try-catch blocks and handle rate limits gracefully.
882
+
883
+ 3. **Streaming for Long Responses**: Use streaming for better user experience with lengthy responses.
884
+
885
+ 4. **Token Management**: Monitor token usage to control costs. Use `maxTokens` to limit response length.
886
+
887
+ 5. **Model Selection**: Choose the appropriate model for your use case:
888
+ - GPT-4 for complex tasks requiring high accuracy
889
+ - GPT-3.5 Turbo for simpler tasks and faster responses
890
+ - o1/o3-mini for reasoning-heavy problems
891
+
892
+ 6. **Function Calling**: Use structured schemas with Zod for type-safe function definitions.
893
+
894
+ 7. **Retry Logic**: Implement exponential backoff for rate limit errors.
895
+
896
+ 8. **Context Window**: Be aware of model context limits and truncate messages if needed.
897
+
898
+ ## Related Packages
899
+
900
+ - **[@aeye/core](../core)**: Core @aeye framework types and interfaces
901
+ - **[@aeye/ai](../ai)**: AI abstractions and utilities
902
+ - **[@aeye/anthropic](../anthropic)**: Anthropic Claude provider
903
+ - **[@aeye/openrouter](../openrouter)**: OpenRouter multi-provider gateway
904
+
905
+ ## License
906
+
907
+ MIT
908
+
909
+ ## Contributing
910
+
911
+ Contributions are welcome! Please see the main [@aeye repository](https://github.com/ClickerMonkey/aeye) for contribution guidelines.
912
+
913
+ ## Support
914
+
915
+ For issues and questions:
916
+ - GitHub Issues: https://github.com/ClickerMonkey/aeye/issues
917
+ - Documentation: https://github.com/ClickerMonkey/aeye
918
+
919
+ ## Changelog
920
+
921
+ See [CHANGELOG.md](CHANGELOG.md) for version history and updates.