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