@happyvertical/ai 0.89.7 → 0.89.8

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.
@@ -1,707 +0,0 @@
1
- import { a as ContextLengthError, i as ContentFilterError, o as ModelNotFoundError, r as AuthenticationError, s as RateLimitError, t as AIError } from "./types-sSecpd_N.js";
2
- import { n as extractRetryAfterSeconds } from "./rate-limit-C2oPwLGI.js";
3
- import { a as normalizeBaseAIOptions, c as prepareRequestControls, o as normalizeChatOptions, s as normalizeImageGenerationOptions } from "./safety-ClfpyKEN.js";
4
- import { t as emitUsage } from "./usage-C1Y1Nlg4.js";
5
- import OpenAI from "openai";
6
- //#region src/shared/providers/openai.ts
7
- /**
8
- * OpenAI provider implementation
9
- *
10
- * Provides a standardized interface for interacting with OpenAI's GPT models,
11
- * including chat completions, text completions, embeddings, and streaming responses.
12
- * Supports all major OpenAI features including function calling, vision models,
13
- * and custom fine-tuned models.
14
- */
15
- var OPENAI_PROFILE = {
16
- providerLabel: "OpenAI",
17
- providerName: "openai",
18
- defaultModel: "gpt-4o",
19
- capabilities: {
20
- chat: true,
21
- completion: true,
22
- embeddings: true,
23
- streaming: true,
24
- functions: true,
25
- vision: true,
26
- fineTuning: true,
27
- imageEmbeddings: true,
28
- imageGeneration: true,
29
- videoGeneration: false,
30
- tts: false,
31
- voiceCloning: false,
32
- voiceDesign: false,
33
- maxContextLength: 128e3,
34
- supportedOperations: [
35
- "chat",
36
- "completion",
37
- "embedding",
38
- "streaming",
39
- "functions",
40
- "vision",
41
- "image_embedding",
42
- "image_generation"
43
- ]
44
- },
45
- describeModel: (modelId) => `OpenAI model: ${modelId}`,
46
- getContextLength(modelId) {
47
- if (modelId.includes("gpt-4o")) return 128e3;
48
- if (modelId.includes("gpt-4.1")) return 128e3;
49
- if (modelId.includes("gpt-4-turbo")) return 128e3;
50
- if (modelId.includes("gpt-4")) return 8192;
51
- if (modelId.includes("gpt-3.5-turbo")) return 16385;
52
- if (modelId.includes("text-embedding")) return 8192;
53
- return 4096;
54
- },
55
- getModelCapabilities(modelId) {
56
- const capabilities = ["text"];
57
- if (modelId.includes("gpt")) capabilities.push("chat", "functions");
58
- if (modelId.includes("vision") || modelId === "gpt-4o" || modelId.includes("gpt-4.1")) capabilities.push("vision");
59
- if (modelId.includes("embedding")) capabilities.push("embeddings");
60
- return capabilities;
61
- },
62
- shouldIncludeModel(modelId) {
63
- return modelId.includes("gpt") || modelId.includes("text-embedding");
64
- },
65
- supportsFunctions(modelId) {
66
- return modelId.includes("gpt-4") || modelId.includes("gpt-3.5");
67
- },
68
- supportsVision(modelId) {
69
- return modelId.includes("vision") || modelId === "gpt-4o" || modelId.includes("gpt-4.1");
70
- }
71
- };
72
- /**
73
- * OpenAI provider implementation that handles all interactions with OpenAI's API.
74
- * Supports GPT models, embeddings, function calling, streaming, and vision capabilities.
75
- */
76
- var OpenAIProvider = class {
77
- client;
78
- options;
79
- profile;
80
- /**
81
- * Creates a new OpenAI provider instance
82
- * @param options - Configuration options for the OpenAI provider
83
- */
84
- constructor(options, profile = OPENAI_PROFILE) {
85
- this.profile = profile;
86
- this.options = normalizeBaseAIOptions({
87
- defaultModel: this.profile.defaultModel,
88
- ...options
89
- });
90
- this.client = new OpenAI({
91
- apiKey: this.options.apiKey,
92
- baseURL: this.options.baseUrl,
93
- organization: this.options.organization,
94
- timeout: this.options.timeout,
95
- maxRetries: this.options.maxRetries,
96
- defaultHeaders: this.options.headers
97
- });
98
- }
99
- /**
100
- * Generate a chat completion using OpenAI's chat models
101
- * @param messages - Array of conversation messages
102
- * @param options - Optional configuration for the chat completion
103
- * @returns Promise resolving to the AI response with content and metadata
104
- * @throws {AIError} When the API request fails or returns invalid data
105
- *
106
- * @example
107
- * ```typescript
108
- * const response = await provider.chat([
109
- * { role: 'system', content: 'You are a helpful assistant.' },
110
- * { role: 'user', content: 'What is the capital of France?' }
111
- * ], {
112
- * model: 'gpt-4o',
113
- * temperature: 0.7,
114
- * maxTokens: 150
115
- * });
116
- * console.log(response.content); // "Paris is the capital of France."
117
- * ```
118
- */
119
- async chat(messages, options = {}) {
120
- const startTime = Date.now();
121
- let controls;
122
- try {
123
- const model = options.model || this.options.defaultModel || this.profile.defaultModel;
124
- options = normalizeChatOptions(this.options, options, this.profile.providerName, model);
125
- controls = prepareRequestControls(this.options, options);
126
- const request = {
127
- model,
128
- messages: this.mapMessagesToOpenAI(messages),
129
- max_tokens: options.maxTokens,
130
- temperature: options.temperature,
131
- top_p: options.topP,
132
- n: options.n,
133
- stop: options.stop,
134
- frequency_penalty: options.frequencyPenalty,
135
- presence_penalty: options.presencePenalty,
136
- user: options.user,
137
- tools: options.tools?.map((tool) => ({
138
- type: "function",
139
- function: {
140
- name: tool.function.name,
141
- description: tool.function.description,
142
- parameters: tool.function.parameters
143
- }
144
- })),
145
- tool_choice: this.mapToolChoice(options.toolChoice),
146
- response_format: options.responseFormat,
147
- seed: options.seed,
148
- stream: false
149
- };
150
- if ((this.profile.providerName === "bifrost" || this.profile.providerName === "litellm") && (options.reasoning?.maxTokens || 0) > 0) request.reasoning = {
151
- effort: options.reasoning?.effort,
152
- max_tokens: options.reasoning?.maxTokens,
153
- include_thoughts: options.reasoning?.includeThoughts
154
- };
155
- const response = await this.client.chat.completions.create(request, {
156
- signal: controls.signal,
157
- timeout: controls.timeout
158
- });
159
- const choice = response.choices[0];
160
- if (!choice) throw new AIError(`No choices returned from ${this.profile.providerLabel}`, "NO_CHOICES", this.profile.providerName);
161
- const usage = this.mapUsage(response.usage);
162
- emitUsage(this.options, this.profile.providerName, "chat", response.model || model, usage, startTime, options.usageTags);
163
- return {
164
- content: choice.message.content || "",
165
- usage,
166
- model: response.model,
167
- finishReason: this.mapFinishReason(choice.finish_reason),
168
- toolCalls: choice.message.tool_calls?.filter((call) => call.type === "function").map((call) => ({
169
- id: call.id,
170
- type: "function",
171
- function: {
172
- name: call.function.name,
173
- arguments: call.function.arguments
174
- }
175
- }))
176
- };
177
- } catch (error) {
178
- if (controls?.didTimeout()) throw new AIError(`AI request timed out after ${controls.timeout}ms`, "AI_TIMEOUT", this.profile.providerName, options.model);
179
- if (options.signal?.aborted) throw new AIError("AI request aborted by caller", "AI_ABORTED", this.profile.providerName, options.model);
180
- throw this.mapError(error);
181
- } finally {
182
- controls?.cleanup();
183
- }
184
- }
185
- /**
186
- * Generate a text completion for a given prompt
187
- * @param prompt - The text prompt to complete
188
- * @param options - Optional configuration for the completion
189
- * @returns Promise resolving to the AI response
190
- * @throws {AIError} When the API request fails
191
- *
192
- * @example
193
- * ```typescript
194
- * const response = await provider.complete('The weather today is', {
195
- * model: 'gpt-4o',
196
- * maxTokens: 50,
197
- * temperature: 0.5
198
- * });
199
- * ```
200
- */
201
- async complete(prompt, options = {}) {
202
- return this.chat([{
203
- role: "user",
204
- content: prompt
205
- }], {
206
- model: options.model,
207
- maxTokens: options.maxTokens,
208
- temperature: options.temperature,
209
- topP: options.topP,
210
- n: options.n,
211
- stop: options.stop,
212
- stream: options.stream,
213
- onProgress: options.onProgress,
214
- signal: options.signal,
215
- timeout: options.timeout,
216
- reasoning: options.reasoning,
217
- usageTags: options.usageTags
218
- });
219
- }
220
- /**
221
- * Simple message interface for single-turn interactions with optional history
222
- *
223
- * @param text - The message text to send
224
- * @param options - Configuration options including history, model, etc.
225
- * @returns Promise resolving to the response content string
226
- *
227
- * @example
228
- * ```typescript
229
- * // Simple usage
230
- * const response = await provider.message('Hello!');
231
- *
232
- * // With options
233
- * const response = await provider.message('Analyze this', {
234
- * model: 'gpt-4o',
235
- * responseFormat: { type: 'json_object' }
236
- * });
237
- *
238
- * // With history
239
- * const response = await provider.message('What was my question?', {
240
- * history: [
241
- * { role: 'user', content: 'What is 2+2?' },
242
- * { role: 'assistant', content: '4' }
243
- * ]
244
- * });
245
- * ```
246
- */
247
- async message(text, options = {}) {
248
- const messages = [...options.history || [], {
249
- role: options.role || "user",
250
- content: text
251
- }];
252
- return (await this.chat(messages, {
253
- model: options.model,
254
- maxTokens: options.maxTokens,
255
- temperature: options.temperature,
256
- topP: options.topP,
257
- stop: options.stop,
258
- stream: options.stream,
259
- frequencyPenalty: options.frequencyPenalty,
260
- presencePenalty: options.presencePenalty,
261
- responseFormat: options.responseFormat,
262
- seed: options.seed,
263
- tools: options.tools,
264
- toolChoice: options.toolChoice,
265
- onProgress: options.onProgress,
266
- signal: options.signal,
267
- timeout: options.timeout,
268
- reasoning: options.reasoning,
269
- usageTags: options.usageTags
270
- })).content;
271
- }
272
- /**
273
- * Generate embeddings for the given text(s)
274
- * @param text - Single text string or array of texts to embed
275
- * @param options - Optional configuration for embeddings
276
- * @returns Promise resolving to embeddings response with vector arrays
277
- * @throws {AIError} When the API request fails
278
- *
279
- * @example
280
- * ```typescript
281
- * // Single text embedding
282
- * const response1 = await provider.embed('Hello world');
283
- * console.log(response1.embeddings[0]); // Array of numbers
284
- *
285
- * // Multiple text embeddings
286
- * const response2 = await provider.embed(['Text 1', 'Text 2']);
287
- * console.log(response2.embeddings.length); // 2
288
- * ```
289
- */
290
- async embed(text, options = {}) {
291
- const startTime = Date.now();
292
- try {
293
- const model = options.model || "text-embedding-3-small";
294
- const input = Array.isArray(text) ? text : [text];
295
- const response = await this.client.embeddings.create({
296
- model,
297
- input,
298
- encoding_format: options.encodingFormat,
299
- dimensions: options.dimensions,
300
- user: options.user
301
- });
302
- const usage = this.mapUsage(response.usage);
303
- emitUsage(this.options, this.profile.providerName, "embed", response.model || model, usage, startTime, options.usageTags);
304
- return {
305
- embeddings: response.data.map((item) => item.embedding),
306
- usage,
307
- model: response.model
308
- };
309
- } catch (error) {
310
- throw this.mapError(error);
311
- }
312
- }
313
- /**
314
- * Convert an image to a base64 data URL
315
- * @param image - Image as URL, base64 data URL, or Buffer
316
- * @returns base64 data URL string
317
- * @private
318
- */
319
- async imageToBase64(image, signal) {
320
- if (Buffer.isBuffer(image)) return `data:image/png;base64,${image.toString("base64")}`;
321
- if (image.startsWith("data:")) return image;
322
- const response = await fetch(image, { signal });
323
- if (!response.ok) throw new AIError(`Failed to fetch image: ${response.status} ${response.statusText}`, "IMAGE_FETCH_ERROR", this.profile.providerName);
324
- const arrayBuffer = await response.arrayBuffer();
325
- const buffer = Buffer.from(arrayBuffer);
326
- return `data:${response.headers.get("content-type") || "image/png"};base64,${buffer.toString("base64")}`;
327
- }
328
- /**
329
- * Generate a text description of an image
330
- * @param image - Image as URL, base64 data URL, or Buffer
331
- * @param prompt - Custom prompt for description (optional)
332
- * @param options - Optional configuration
333
- * @returns Promise resolving to the description string
334
- *
335
- * @example
336
- * ```typescript
337
- * const description = await provider.describeImage('https://example.com/image.jpg');
338
- * ```
339
- */
340
- async describeImage(image, prompt, options = {}) {
341
- let controls;
342
- try {
343
- const defaultPrompt = "Describe this image for a search index. Include objects, mood, lighting, and any visible text.";
344
- controls = prepareRequestControls(this.options, options);
345
- const imageUrl = await this.imageToBase64(image, controls.signal);
346
- return (await this.chat([{
347
- role: "user",
348
- content: [{
349
- type: "text",
350
- text: prompt || defaultPrompt
351
- }, {
352
- type: "image_url",
353
- image_url: {
354
- url: imageUrl,
355
- detail: options.detail || "auto"
356
- }
357
- }]
358
- }], {
359
- model: options.model || this.options.defaultModel || this.profile.defaultModel,
360
- maxTokens: options.maxTokens ?? 500,
361
- signal: controls.signal,
362
- timeout: controls.timeout,
363
- reasoning: options.reasoning,
364
- usageTags: options.usageTags
365
- })).content;
366
- } catch (error) {
367
- if (controls?.didTimeout()) throw new AIError(`AI request timed out after ${controls.timeout}ms`, "AI_TIMEOUT", this.profile.providerName, options.model);
368
- if (options.signal?.aborted) throw new AIError("AI request aborted by caller", "AI_ABORTED", this.profile.providerName, options.model);
369
- throw this.mapError(error);
370
- } finally {
371
- controls?.cleanup();
372
- }
373
- }
374
- /**
375
- * Generate embeddings for an image using describe-then-embed pattern
376
- * @param image - Image as URL, base64 data URL, or Buffer
377
- * @param options - Optional configuration for image embeddings
378
- * @returns Promise resolving to embeddings response
379
- *
380
- * @example
381
- * ```typescript
382
- * const embedding = await provider.embedImage('https://example.com/image.jpg');
383
- * ```
384
- */
385
- async embedImage(image, options = {}) {
386
- try {
387
- const description = await this.describeImage(image, "Describe this image in detail for semantic embedding. Include all visible objects, colors, textures, composition, text, people, actions, and overall scene context.");
388
- return this.embed(description, {
389
- model: options.model || "text-embedding-3-small",
390
- dimensions: options.dimensions,
391
- user: options.user
392
- });
393
- } catch (error) {
394
- throw this.mapError(error);
395
- }
396
- }
397
- /**
398
- * Generate an image from a text prompt using DALL-E
399
- * @param prompt - Text description of the image to generate
400
- * @param options - Optional configuration for image generation
401
- * @returns Promise resolving to generated image(s)
402
- *
403
- * @example
404
- * ```typescript
405
- * const result = await provider.generateImage('A sunset over mountains');
406
- * fs.writeFileSync('image.png', result.images[0].data);
407
- * ```
408
- */
409
- async generateImage(prompt, options = {}) {
410
- let controls;
411
- try {
412
- const model = options.model || "dall-e-3";
413
- options = normalizeImageGenerationOptions(this.options, options, this.profile.providerName, model);
414
- controls = prepareRequestControls(this.options, options);
415
- const requestParams = {
416
- model,
417
- prompt,
418
- n: model === "dall-e-3" ? 1 : options.n,
419
- size: options.size || "1024x1024",
420
- response_format: options.outputFormat === "url" ? "url" : "b64_json"
421
- };
422
- if (model === "dall-e-3") {
423
- if (options.style) requestParams.style = options.style;
424
- if (options.quality) requestParams.quality = options.quality;
425
- }
426
- return {
427
- images: ((await this.client.images.generate(requestParams, {
428
- signal: controls.signal,
429
- timeout: controls.timeout
430
- })).data || []).map((item) => {
431
- let data;
432
- const mimeType = "image/png";
433
- if (options.outputFormat === "url") data = item.url || "";
434
- else if (options.outputFormat === "base64") data = item.b64_json || "";
435
- else data = Buffer.from(item.b64_json || "", "base64");
436
- return {
437
- data,
438
- mimeType,
439
- revisedPrompt: item.revised_prompt
440
- };
441
- }),
442
- model
443
- };
444
- } catch (error) {
445
- if (controls?.didTimeout()) throw new AIError(`AI request timed out after ${controls.timeout}ms`, "AI_TIMEOUT", this.profile.providerName, options.model);
446
- if (options.signal?.aborted) throw new AIError("AI request aborted by caller", "AI_ABORTED", this.profile.providerName, options.model);
447
- throw this.mapError(error);
448
- } finally {
449
- controls?.cleanup();
450
- }
451
- }
452
- async submitVideoGenerationJob(_options) {
453
- throw new AIError(`Video generation is not supported by ${this.profile.providerLabel} provider. Use gemini, byteplus-modelark, or openai-compat-video provider.`, "NOT_IMPLEMENTED", this.profile.providerName);
454
- }
455
- async getVideoGenerationJob(_handle) {
456
- throw new AIError(`Video generation is not supported by ${this.profile.providerLabel} provider. Use gemini, byteplus-modelark, or openai-compat-video provider.`, "NOT_IMPLEMENTED", this.profile.providerName);
457
- }
458
- async fetchVideoGenerationResult(_handle) {
459
- throw new AIError(`Video generation is not supported by ${this.profile.providerLabel} provider. Use gemini, byteplus-modelark, or openai-compat-video provider.`, "NOT_IMPLEMENTED", this.profile.providerName);
460
- }
461
- async cancelVideoGenerationJob(_handle) {
462
- throw new AIError(`Video generation is not supported by ${this.profile.providerLabel} provider. Use gemini, byteplus-modelark, or openai-compat-video provider.`, "NOT_IMPLEMENTED", this.profile.providerName);
463
- }
464
- async validateVideoGenerationAccess() {
465
- throw new AIError(`Video generation is not supported by ${this.profile.providerLabel} provider. Use gemini, byteplus-modelark, or openai-compat-video provider.`, "NOT_IMPLEMENTED", this.profile.providerName);
466
- }
467
- /**
468
- * Stream a chat completion response in real-time
469
- * @param messages - Array of conversation messages
470
- * @param options - Optional configuration for the chat completion
471
- * @yields Individual content chunks as they arrive
472
- * @throws {AIError} When the streaming request fails
473
- *
474
- * @example
475
- * ```typescript
476
- * for await (const chunk of provider.stream([
477
- * { role: 'user', content: 'Write a story about AI' }
478
- * ])) {
479
- * process.stdout.write(chunk);
480
- * }
481
- * ```
482
- */
483
- async *stream(messages, options = {}) {
484
- const startTime = Date.now();
485
- let controls;
486
- try {
487
- const model = options.model || this.options.defaultModel || this.profile.defaultModel;
488
- options = normalizeChatOptions(this.options, options, this.profile.providerName, model);
489
- controls = prepareRequestControls(this.options, options);
490
- const request = {
491
- model,
492
- messages: this.mapMessagesToOpenAI(messages),
493
- max_tokens: options.maxTokens,
494
- temperature: options.temperature,
495
- top_p: options.topP,
496
- stop: options.stop,
497
- frequency_penalty: options.frequencyPenalty,
498
- presence_penalty: options.presencePenalty,
499
- user: options.user,
500
- stream: true
501
- };
502
- if ((this.profile.providerName === "bifrost" || this.profile.providerName === "litellm") && (options.reasoning?.maxTokens || 0) > 0) request.reasoning = {
503
- effort: options.reasoning?.effort,
504
- max_tokens: options.reasoning?.maxTokens,
505
- include_thoughts: options.reasoning?.includeThoughts
506
- };
507
- const stream = await this.client.chat.completions.create(request, {
508
- signal: controls.signal,
509
- timeout: controls.timeout
510
- });
511
- for await (const chunk of stream) {
512
- const content = chunk.choices[0]?.delta?.content;
513
- if (content) {
514
- if (options.onProgress) options.onProgress(content);
515
- yield content;
516
- }
517
- }
518
- emitUsage(this.options, this.profile.providerName, "stream", model, void 0, startTime, options.usageTags);
519
- } catch (error) {
520
- if (controls?.didTimeout()) throw new AIError(`AI request timed out after ${controls.timeout}ms`, "AI_TIMEOUT", this.profile.providerName, options.model);
521
- if (options.signal?.aborted) throw new AIError("AI request aborted by caller", "AI_ABORTED", this.profile.providerName, options.model);
522
- throw this.mapError(error);
523
- } finally {
524
- controls?.cleanup();
525
- }
526
- }
527
- /**
528
- * Count the number of tokens in the given text
529
- * @param text - The text to count tokens for
530
- * @returns Promise resolving to the estimated token count
531
- *
532
- * @remarks
533
- * OpenAI doesn't provide a direct token counting API, so this is an approximation
534
- * based on the general rule of ~4 characters per token. For precise counting,
535
- * consider using a dedicated tokenizer library.
536
- *
537
- * @example
538
- * ```typescript
539
- * const count = await provider.countTokens('Hello, world!');
540
- * console.log(count); // Approximately 4 tokens
541
- * ```
542
- */
543
- async countTokens(text) {
544
- return Math.ceil(text.length / 4);
545
- }
546
- /**
547
- * Get a list of available OpenAI models
548
- * @returns Promise resolving to an array of model information
549
- * @throws {AIError} When the API request fails
550
- *
551
- * @example
552
- * ```typescript
553
- * const models = await provider.getModels();
554
- * const gptModels = models.filter(m => m.id.includes('gpt'));
555
- * ```
556
- */
557
- async getModels() {
558
- try {
559
- return (await this.client.models.list()).data.filter((model) => this.profile.shouldIncludeModel(model.id)).map((model) => ({
560
- id: model.id,
561
- name: model.id,
562
- description: this.profile.describeModel(model.id),
563
- contextLength: this.profile.getContextLength(model.id),
564
- capabilities: this.profile.getModelCapabilities(model.id),
565
- supportsFunctions: this.profile.supportsFunctions(model.id),
566
- supportsVision: this.profile.supportsVision(model.id)
567
- }));
568
- } catch (error) {
569
- throw this.mapError(error);
570
- }
571
- }
572
- /**
573
- * Get the capabilities supported by this OpenAI provider
574
- * @returns Promise resolving to provider capabilities
575
- *
576
- * @example
577
- * ```typescript
578
- * const caps = await provider.getCapabilities();
579
- * if (caps.functions) {
580
- * // Provider supports function calling
581
- * }
582
- * ```
583
- */
584
- async getCapabilities() {
585
- return { ...this.profile.capabilities };
586
- }
587
- async synthesizeSpeech(_text, _options) {
588
- throw new AIError(`TTS is not supported by ${this.profile.providerLabel} provider. Use Qwen3-TTS provider.`, "NOT_IMPLEMENTED", this.profile.providerName);
589
- }
590
- streamSpeech(_text, _options) {
591
- const error = new AIError(`TTS streaming is not supported by ${this.profile.providerLabel} provider. Use Qwen3-TTS provider.`, "NOT_IMPLEMENTED", this.profile.providerName);
592
- return { [Symbol.asyncIterator]: () => ({ next: () => Promise.reject(error) }) };
593
- }
594
- async cloneVoice(_options) {
595
- throw new AIError(`Voice cloning is not supported by ${this.profile.providerLabel} provider. Use Qwen3-TTS provider.`, "NOT_IMPLEMENTED", this.profile.providerName);
596
- }
597
- async designVoice(_options) {
598
- throw new AIError(`Voice design is not supported by ${this.profile.providerLabel} provider. Use Qwen3-TTS provider.`, "NOT_IMPLEMENTED", this.profile.providerName);
599
- }
600
- async getVoices(_options) {
601
- throw new AIError(`Voice listing is not supported by ${this.profile.providerLabel} provider. Use Qwen3-TTS provider.`, "NOT_IMPLEMENTED", this.profile.providerName);
602
- }
603
- /**
604
- * Maps internal AI messages to OpenAI's message format
605
- * @param messages - Array of internal AI messages
606
- * @returns Array of OpenAI-compatible message parameters
607
- * @private
608
- */
609
- mapMessagesToOpenAI(messages) {
610
- return messages.map((message) => {
611
- let content;
612
- if (typeof message.content === "string") content = message.content;
613
- else content = message.content.map((part) => {
614
- if (part.type === "text") return {
615
- type: "text",
616
- text: part.text
617
- };
618
- return {
619
- type: "image_url",
620
- image_url: {
621
- url: part.image_url.url,
622
- detail: part.image_url.detail
623
- }
624
- };
625
- });
626
- const baseMessage = {
627
- role: message.role,
628
- content
629
- };
630
- if (message.name && (message.role === "system" || message.role === "user" || message.role === "function")) baseMessage.name = message.name;
631
- if (message.tool_calls && message.role === "assistant") baseMessage.tool_calls = message.tool_calls;
632
- return baseMessage;
633
- });
634
- }
635
- /**
636
- * Maps internal tool choice format to OpenAI's tool choice format
637
- * @param toolChoice - Internal tool choice specification
638
- * @returns OpenAI-compatible tool choice option or undefined
639
- * @private
640
- */
641
- mapToolChoice(toolChoice) {
642
- if (!toolChoice) return void 0;
643
- if (typeof toolChoice === "string") return toolChoice;
644
- return {
645
- type: "function",
646
- function: { name: toolChoice.function.name }
647
- };
648
- }
649
- /**
650
- * Maps OpenAI usage information to internal token usage format
651
- * @param usage - OpenAI usage object from API response
652
- * @returns Internal token usage object or undefined
653
- * @private
654
- */
655
- mapUsage(usage) {
656
- if (!usage) return void 0;
657
- return {
658
- promptTokens: usage.prompt_tokens || 0,
659
- completionTokens: usage.completion_tokens || 0,
660
- totalTokens: usage.total_tokens || 0
661
- };
662
- }
663
- /**
664
- * Maps OpenAI finish reason to internal finish reason format
665
- * @param reason - OpenAI finish reason from API response
666
- * @returns Internal finish reason
667
- * @private
668
- */
669
- mapFinishReason(reason) {
670
- switch (reason) {
671
- case "stop": return "stop";
672
- case "length": return "length";
673
- case "tool_calls": return "tool_calls";
674
- case "content_filter": return "content_filter";
675
- default: return "stop";
676
- }
677
- }
678
- /**
679
- * Gets the context length for a given OpenAI model
680
- * @param modelId - The OpenAI model identifier
681
- * @returns Maximum context length in tokens
682
- * @private
683
- */
684
- /**
685
- * Maps OpenAI API errors to internal AI error types
686
- * @param error - The error object from OpenAI API
687
- * @returns Appropriate internal AI error instance
688
- * @private
689
- */
690
- mapError(error) {
691
- if (error instanceof OpenAI.APIError) switch (error.status) {
692
- case 401: return new AuthenticationError(this.profile.providerName);
693
- case 429: return new RateLimitError(this.profile.providerName, extractRetryAfterSeconds(error));
694
- case 404: return new ModelNotFoundError(error.message, this.profile.providerName);
695
- case 413: return new ContextLengthError(this.profile.providerName);
696
- default:
697
- if (error.message.includes("content_filter")) return new ContentFilterError(this.profile.providerName);
698
- return new AIError(error.message, "API_ERROR", this.profile.providerName);
699
- }
700
- if (error instanceof AIError) return error;
701
- return new AIError(error instanceof Error ? error.message : "Unknown error occurred", "UNKNOWN_ERROR", this.profile.providerName);
702
- }
703
- };
704
- //#endregion
705
- export { OpenAIProvider };
706
-
707
- //# sourceMappingURL=openai-BuwvBruA.js.map