@happyvertical/ai 0.79.0 → 0.80.1

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 (63) hide show
  1. package/AGENT.md +1 -1
  2. package/dist/chunks/anthropic-DA8sdt12.js +438 -0
  3. package/dist/chunks/anthropic-DA8sdt12.js.map +1 -0
  4. package/dist/chunks/bedrock-DDiYFUw0.js +699 -0
  5. package/dist/chunks/bedrock-DDiYFUw0.js.map +1 -0
  6. package/dist/chunks/bifrost-fM9FpW_w.js +198 -0
  7. package/dist/chunks/bifrost-fM9FpW_w.js.map +1 -0
  8. package/dist/chunks/claude-cli-CnJh5KQT.js +498 -0
  9. package/dist/chunks/claude-cli-CnJh5KQT.js.map +1 -0
  10. package/dist/chunks/gateway-admin-CiKvHst7.js +283 -0
  11. package/dist/chunks/gateway-admin-CiKvHst7.js.map +1 -0
  12. package/dist/chunks/gemini-Bu7Fke7c.js +593 -0
  13. package/dist/chunks/gemini-Bu7Fke7c.js.map +1 -0
  14. package/dist/chunks/huggingface-B07_6wHW.js +295 -0
  15. package/dist/chunks/huggingface-B07_6wHW.js.map +1 -0
  16. package/dist/chunks/litellm-D_Oo9OQ_.js +185 -0
  17. package/dist/chunks/litellm-D_Oo9OQ_.js.map +1 -0
  18. package/dist/chunks/ollama-D4ksOTO8.js +626 -0
  19. package/dist/chunks/ollama-D4ksOTO8.js.map +1 -0
  20. package/dist/chunks/openai--F38QHiJ.js +691 -0
  21. package/dist/chunks/openai--F38QHiJ.js.map +1 -0
  22. package/dist/chunks/qwen-tts-DRRbOrhA.js +333 -0
  23. package/dist/chunks/qwen-tts-DRRbOrhA.js.map +1 -0
  24. package/dist/chunks/rate-limit-BwyPGXFW.js +199 -0
  25. package/dist/chunks/rate-limit-BwyPGXFW.js.map +1 -0
  26. package/dist/chunks/safety-CEnoJS6X.js +260 -0
  27. package/dist/chunks/safety-CEnoJS6X.js.map +1 -0
  28. package/dist/chunks/types-FHs-KbGL.js +117 -0
  29. package/dist/chunks/types-FHs-KbGL.js.map +1 -0
  30. package/dist/chunks/usage-C1Y1Nlg4.js +35 -0
  31. package/dist/chunks/usage-C1Y1Nlg4.js.map +1 -0
  32. package/dist/cli/claude-context.js +17 -17
  33. package/dist/cli/claude-context.js.map +1 -1
  34. package/dist/index.js +717 -23
  35. package/dist/index.js.map +1 -1
  36. package/metadata.json +1 -1
  37. package/package.json +5 -5
  38. package/dist/chunks/anthropic-2z-82zgr.js +0 -530
  39. package/dist/chunks/anthropic-2z-82zgr.js.map +0 -1
  40. package/dist/chunks/bedrock-Dc2eVPUD.js +0 -920
  41. package/dist/chunks/bedrock-Dc2eVPUD.js.map +0 -1
  42. package/dist/chunks/bifrost-CEnCsciy.js +0 -258
  43. package/dist/chunks/bifrost-CEnCsciy.js.map +0 -1
  44. package/dist/chunks/claude-cli-X1ONjE8K.js +0 -603
  45. package/dist/chunks/claude-cli-X1ONjE8K.js.map +0 -1
  46. package/dist/chunks/gateway-admin-BUhBzXZb.js +0 -359
  47. package/dist/chunks/gateway-admin-BUhBzXZb.js.map +0 -1
  48. package/dist/chunks/gemini-CS56gY0D.js +0 -768
  49. package/dist/chunks/gemini-CS56gY0D.js.map +0 -1
  50. package/dist/chunks/huggingface-wQSfO5xA.js +0 -411
  51. package/dist/chunks/huggingface-wQSfO5xA.js.map +0 -1
  52. package/dist/chunks/index-DCXO0nZA.js +0 -1293
  53. package/dist/chunks/index-DCXO0nZA.js.map +0 -1
  54. package/dist/chunks/litellm-BMFTYbWc.js +0 -245
  55. package/dist/chunks/litellm-BMFTYbWc.js.map +0 -1
  56. package/dist/chunks/ollama-DsGDrA-c.js +0 -962
  57. package/dist/chunks/ollama-DsGDrA-c.js.map +0 -1
  58. package/dist/chunks/openai-CJEo69jb.js +0 -882
  59. package/dist/chunks/openai-CJEo69jb.js.map +0 -1
  60. package/dist/chunks/qwen-tts-BLYZ6d9s.js +0 -365
  61. package/dist/chunks/qwen-tts-BLYZ6d9s.js.map +0 -1
  62. package/dist/chunks/usage-DMWiJ2oB.js +0 -21
  63. package/dist/chunks/usage-DMWiJ2oB.js.map +0 -1
@@ -1,882 +0,0 @@
1
- import OpenAI from "openai";
2
- import { b as normalizeBaseAIOptions, n as normalizeChatOptions, p as prepareRequestControls, A as AIError, a as normalizeImageGenerationOptions, C as ContentFilterError, c as ContextLengthError, M as ModelNotFoundError, R as RateLimitError, e as extractRetryAfterSeconds, d as AuthenticationError } from "./index-DCXO0nZA.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 = normalizeBaseAIOptions({
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
- let controls;
112
- try {
113
- const model = options.model || this.options.defaultModel || this.profile.defaultModel;
114
- options = normalizeChatOptions(
115
- this.options,
116
- options,
117
- this.profile.providerName,
118
- model
119
- );
120
- controls = prepareRequestControls(this.options, options);
121
- const request = {
122
- model,
123
- messages: this.mapMessagesToOpenAI(messages),
124
- max_tokens: options.maxTokens,
125
- temperature: options.temperature,
126
- top_p: options.topP,
127
- n: options.n,
128
- stop: options.stop,
129
- frequency_penalty: options.frequencyPenalty,
130
- presence_penalty: options.presencePenalty,
131
- user: options.user,
132
- tools: options.tools?.map((tool) => ({
133
- type: "function",
134
- function: {
135
- name: tool.function.name,
136
- description: tool.function.description,
137
- parameters: tool.function.parameters
138
- }
139
- })),
140
- tool_choice: this.mapToolChoice(options.toolChoice),
141
- response_format: options.responseFormat,
142
- seed: options.seed,
143
- stream: false
144
- };
145
- if ((this.profile.providerName === "bifrost" || this.profile.providerName === "litellm") && (options.reasoning?.maxTokens || 0) > 0) {
146
- request.reasoning = {
147
- effort: options.reasoning?.effort,
148
- max_tokens: options.reasoning?.maxTokens,
149
- include_thoughts: options.reasoning?.includeThoughts
150
- };
151
- }
152
- const response = await this.client.chat.completions.create(request, {
153
- signal: controls.signal,
154
- timeout: controls.timeout
155
- });
156
- const choice = response.choices[0];
157
- if (!choice) {
158
- throw new AIError(
159
- `No choices returned from ${this.profile.providerLabel}`,
160
- "NO_CHOICES",
161
- this.profile.providerName
162
- );
163
- }
164
- const usage = this.mapUsage(response.usage);
165
- emitUsage(
166
- this.options,
167
- this.profile.providerName,
168
- "chat",
169
- response.model || model,
170
- usage,
171
- startTime,
172
- options.usageTags
173
- );
174
- return {
175
- content: choice.message.content || "",
176
- usage,
177
- model: response.model,
178
- finishReason: this.mapFinishReason(choice.finish_reason),
179
- toolCalls: choice.message.tool_calls?.filter((call) => call.type === "function").map((call) => ({
180
- id: call.id,
181
- type: "function",
182
- function: {
183
- name: call.function.name,
184
- arguments: call.function.arguments
185
- }
186
- }))
187
- };
188
- } catch (error) {
189
- if (controls?.didTimeout()) {
190
- throw new AIError(
191
- `AI request timed out after ${controls.timeout}ms`,
192
- "AI_TIMEOUT",
193
- this.profile.providerName,
194
- options.model
195
- );
196
- }
197
- if (options.signal?.aborted) {
198
- throw new AIError(
199
- "AI request aborted by caller",
200
- "AI_ABORTED",
201
- this.profile.providerName,
202
- options.model
203
- );
204
- }
205
- throw this.mapError(error);
206
- } finally {
207
- controls?.cleanup();
208
- }
209
- }
210
- /**
211
- * Generate a text completion for a given prompt
212
- * @param prompt - The text prompt to complete
213
- * @param options - Optional configuration for the completion
214
- * @returns Promise resolving to the AI response
215
- * @throws {AIError} When the API request fails
216
- *
217
- * @example
218
- * ```typescript
219
- * const response = await provider.complete('The weather today is', {
220
- * model: 'gpt-4o',
221
- * maxTokens: 50,
222
- * temperature: 0.5
223
- * });
224
- * ```
225
- */
226
- async complete(prompt, options = {}) {
227
- return this.chat([{ role: "user", content: prompt }], {
228
- model: options.model,
229
- maxTokens: options.maxTokens,
230
- temperature: options.temperature,
231
- topP: options.topP,
232
- n: options.n,
233
- stop: options.stop,
234
- stream: options.stream,
235
- onProgress: options.onProgress,
236
- signal: options.signal,
237
- timeout: options.timeout,
238
- reasoning: options.reasoning,
239
- usageTags: options.usageTags
240
- });
241
- }
242
- /**
243
- * Simple message interface for single-turn interactions with optional history
244
- *
245
- * @param text - The message text to send
246
- * @param options - Configuration options including history, model, etc.
247
- * @returns Promise resolving to the response content string
248
- *
249
- * @example
250
- * ```typescript
251
- * // Simple usage
252
- * const response = await provider.message('Hello!');
253
- *
254
- * // With options
255
- * const response = await provider.message('Analyze this', {
256
- * model: 'gpt-4o',
257
- * responseFormat: { type: 'json_object' }
258
- * });
259
- *
260
- * // With history
261
- * const response = await provider.message('What was my question?', {
262
- * history: [
263
- * { role: 'user', content: 'What is 2+2?' },
264
- * { role: 'assistant', content: '4' }
265
- * ]
266
- * });
267
- * ```
268
- */
269
- async message(text, options = {}) {
270
- const messages = [
271
- ...options.history || [],
272
- { role: options.role || "user", content: text }
273
- ];
274
- const response = await this.chat(messages, {
275
- model: options.model,
276
- maxTokens: options.maxTokens,
277
- temperature: options.temperature,
278
- topP: options.topP,
279
- stop: options.stop,
280
- stream: options.stream,
281
- frequencyPenalty: options.frequencyPenalty,
282
- presencePenalty: options.presencePenalty,
283
- responseFormat: options.responseFormat,
284
- seed: options.seed,
285
- tools: options.tools,
286
- toolChoice: options.toolChoice,
287
- onProgress: options.onProgress,
288
- signal: options.signal,
289
- timeout: options.timeout,
290
- reasoning: options.reasoning,
291
- usageTags: options.usageTags
292
- });
293
- return response.content;
294
- }
295
- /**
296
- * Generate embeddings for the given text(s)
297
- * @param text - Single text string or array of texts to embed
298
- * @param options - Optional configuration for embeddings
299
- * @returns Promise resolving to embeddings response with vector arrays
300
- * @throws {AIError} When the API request fails
301
- *
302
- * @example
303
- * ```typescript
304
- * // Single text embedding
305
- * const response1 = await provider.embed('Hello world');
306
- * console.log(response1.embeddings[0]); // Array of numbers
307
- *
308
- * // Multiple text embeddings
309
- * const response2 = await provider.embed(['Text 1', 'Text 2']);
310
- * console.log(response2.embeddings.length); // 2
311
- * ```
312
- */
313
- async embed(text, options = {}) {
314
- const startTime = Date.now();
315
- try {
316
- const model = options.model || "text-embedding-3-small";
317
- const input = Array.isArray(text) ? text : [text];
318
- const response = await this.client.embeddings.create({
319
- model,
320
- input,
321
- encoding_format: options.encodingFormat,
322
- dimensions: options.dimensions,
323
- user: options.user
324
- });
325
- const usage = this.mapUsage(response.usage);
326
- emitUsage(
327
- this.options,
328
- this.profile.providerName,
329
- "embed",
330
- response.model || model,
331
- usage,
332
- startTime,
333
- options.usageTags
334
- );
335
- return {
336
- embeddings: response.data.map((item) => item.embedding),
337
- usage,
338
- model: response.model
339
- };
340
- } catch (error) {
341
- throw this.mapError(error);
342
- }
343
- }
344
- /**
345
- * Convert an image to a base64 data URL
346
- * @param image - Image as URL, base64 data URL, or Buffer
347
- * @returns base64 data URL string
348
- * @private
349
- */
350
- async imageToBase64(image, signal) {
351
- if (Buffer.isBuffer(image)) {
352
- return `data:image/png;base64,${image.toString("base64")}`;
353
- }
354
- if (image.startsWith("data:")) {
355
- return image;
356
- }
357
- const response = await fetch(image, { signal });
358
- if (!response.ok) {
359
- throw new AIError(
360
- `Failed to fetch image: ${response.status} ${response.statusText}`,
361
- "IMAGE_FETCH_ERROR",
362
- this.profile.providerName
363
- );
364
- }
365
- const arrayBuffer = await response.arrayBuffer();
366
- const buffer = Buffer.from(arrayBuffer);
367
- const contentType = response.headers.get("content-type") || "image/png";
368
- return `data:${contentType};base64,${buffer.toString("base64")}`;
369
- }
370
- /**
371
- * Generate a text description of an image
372
- * @param image - Image as URL, base64 data URL, or Buffer
373
- * @param prompt - Custom prompt for description (optional)
374
- * @param options - Optional configuration
375
- * @returns Promise resolving to the description string
376
- *
377
- * @example
378
- * ```typescript
379
- * const description = await provider.describeImage('https://example.com/image.jpg');
380
- * ```
381
- */
382
- async describeImage(image, prompt, options = {}) {
383
- let controls;
384
- try {
385
- const defaultPrompt = "Describe this image for a search index. Include objects, mood, lighting, and any visible text.";
386
- controls = prepareRequestControls(this.options, options);
387
- const imageUrl = await this.imageToBase64(image, controls.signal);
388
- const response = await this.chat(
389
- [
390
- {
391
- role: "user",
392
- content: [
393
- { type: "text", text: prompt || defaultPrompt },
394
- {
395
- type: "image_url",
396
- image_url: {
397
- url: imageUrl,
398
- detail: options.detail || "auto"
399
- }
400
- }
401
- ]
402
- }
403
- ],
404
- {
405
- model: options.model || this.options.defaultModel || this.profile.defaultModel,
406
- maxTokens: options.maxTokens ?? 500,
407
- signal: controls.signal,
408
- timeout: controls.timeout,
409
- reasoning: options.reasoning,
410
- usageTags: options.usageTags
411
- }
412
- );
413
- return response.content;
414
- } catch (error) {
415
- if (controls?.didTimeout()) {
416
- throw new AIError(
417
- `AI request timed out after ${controls.timeout}ms`,
418
- "AI_TIMEOUT",
419
- this.profile.providerName,
420
- options.model
421
- );
422
- }
423
- if (options.signal?.aborted) {
424
- throw new AIError(
425
- "AI request aborted by caller",
426
- "AI_ABORTED",
427
- this.profile.providerName,
428
- options.model
429
- );
430
- }
431
- throw this.mapError(error);
432
- } finally {
433
- controls?.cleanup();
434
- }
435
- }
436
- /**
437
- * Generate embeddings for an image using describe-then-embed pattern
438
- * @param image - Image as URL, base64 data URL, or Buffer
439
- * @param options - Optional configuration for image embeddings
440
- * @returns Promise resolving to embeddings response
441
- *
442
- * @example
443
- * ```typescript
444
- * const embedding = await provider.embedImage('https://example.com/image.jpg');
445
- * ```
446
- */
447
- async embedImage(image, options = {}) {
448
- try {
449
- const description = await this.describeImage(
450
- image,
451
- "Describe this image in detail for semantic embedding. Include all visible objects, colors, textures, composition, text, people, actions, and overall scene context."
452
- );
453
- return this.embed(description, {
454
- model: options.model || "text-embedding-3-small",
455
- dimensions: options.dimensions,
456
- user: options.user
457
- });
458
- } catch (error) {
459
- throw this.mapError(error);
460
- }
461
- }
462
- /**
463
- * Generate an image from a text prompt using DALL-E
464
- * @param prompt - Text description of the image to generate
465
- * @param options - Optional configuration for image generation
466
- * @returns Promise resolving to generated image(s)
467
- *
468
- * @example
469
- * ```typescript
470
- * const result = await provider.generateImage('A sunset over mountains');
471
- * fs.writeFileSync('image.png', result.images[0].data);
472
- * ```
473
- */
474
- async generateImage(prompt, options = {}) {
475
- let controls;
476
- try {
477
- const model = options.model || "dall-e-3";
478
- options = normalizeImageGenerationOptions(
479
- this.options,
480
- options,
481
- this.profile.providerName,
482
- model
483
- );
484
- controls = prepareRequestControls(this.options, options);
485
- const requestParams = {
486
- model,
487
- prompt,
488
- n: model === "dall-e-3" ? 1 : options.n,
489
- size: options.size || "1024x1024",
490
- response_format: options.outputFormat === "url" ? "url" : "b64_json"
491
- };
492
- if (model === "dall-e-3") {
493
- if (options.style) {
494
- requestParams.style = options.style;
495
- }
496
- if (options.quality) {
497
- requestParams.quality = options.quality;
498
- }
499
- }
500
- const response = await this.client.images.generate(requestParams, {
501
- signal: controls.signal,
502
- timeout: controls.timeout
503
- });
504
- const images = (response.data || []).map((item) => {
505
- let data;
506
- const mimeType = "image/png";
507
- if (options.outputFormat === "url") {
508
- data = item.url || "";
509
- } else if (options.outputFormat === "base64") {
510
- data = item.b64_json || "";
511
- } else {
512
- data = Buffer.from(item.b64_json || "", "base64");
513
- }
514
- return {
515
- data,
516
- mimeType,
517
- revisedPrompt: item.revised_prompt
518
- };
519
- });
520
- return {
521
- images,
522
- model
523
- };
524
- } catch (error) {
525
- if (controls?.didTimeout()) {
526
- throw new AIError(
527
- `AI request timed out after ${controls.timeout}ms`,
528
- "AI_TIMEOUT",
529
- this.profile.providerName,
530
- options.model
531
- );
532
- }
533
- if (options.signal?.aborted) {
534
- throw new AIError(
535
- "AI request aborted by caller",
536
- "AI_ABORTED",
537
- this.profile.providerName,
538
- options.model
539
- );
540
- }
541
- throw this.mapError(error);
542
- } finally {
543
- controls?.cleanup();
544
- }
545
- }
546
- /**
547
- * Stream a chat completion response in real-time
548
- * @param messages - Array of conversation messages
549
- * @param options - Optional configuration for the chat completion
550
- * @yields Individual content chunks as they arrive
551
- * @throws {AIError} When the streaming request fails
552
- *
553
- * @example
554
- * ```typescript
555
- * for await (const chunk of provider.stream([
556
- * { role: 'user', content: 'Write a story about AI' }
557
- * ])) {
558
- * process.stdout.write(chunk);
559
- * }
560
- * ```
561
- */
562
- async *stream(messages, options = {}) {
563
- const startTime = Date.now();
564
- let controls;
565
- try {
566
- const model = options.model || this.options.defaultModel || this.profile.defaultModel;
567
- options = normalizeChatOptions(
568
- this.options,
569
- options,
570
- this.profile.providerName,
571
- model
572
- );
573
- controls = prepareRequestControls(this.options, options);
574
- const request = {
575
- model,
576
- messages: this.mapMessagesToOpenAI(messages),
577
- max_tokens: options.maxTokens,
578
- temperature: options.temperature,
579
- top_p: options.topP,
580
- stop: options.stop,
581
- frequency_penalty: options.frequencyPenalty,
582
- presence_penalty: options.presencePenalty,
583
- user: options.user,
584
- stream: true
585
- };
586
- if ((this.profile.providerName === "bifrost" || this.profile.providerName === "litellm") && (options.reasoning?.maxTokens || 0) > 0) {
587
- request.reasoning = {
588
- effort: options.reasoning?.effort,
589
- max_tokens: options.reasoning?.maxTokens,
590
- include_thoughts: options.reasoning?.includeThoughts
591
- };
592
- }
593
- const stream = await this.client.chat.completions.create(request, {
594
- signal: controls.signal,
595
- timeout: controls.timeout
596
- });
597
- for await (const chunk of stream) {
598
- const content = chunk.choices[0]?.delta?.content;
599
- if (content) {
600
- if (options.onProgress) {
601
- options.onProgress(content);
602
- }
603
- yield content;
604
- }
605
- }
606
- emitUsage(
607
- this.options,
608
- this.profile.providerName,
609
- "stream",
610
- model,
611
- void 0,
612
- startTime,
613
- options.usageTags
614
- );
615
- } catch (error) {
616
- if (controls?.didTimeout()) {
617
- throw new AIError(
618
- `AI request timed out after ${controls.timeout}ms`,
619
- "AI_TIMEOUT",
620
- this.profile.providerName,
621
- options.model
622
- );
623
- }
624
- if (options.signal?.aborted) {
625
- throw new AIError(
626
- "AI request aborted by caller",
627
- "AI_ABORTED",
628
- this.profile.providerName,
629
- options.model
630
- );
631
- }
632
- throw this.mapError(error);
633
- } finally {
634
- controls?.cleanup();
635
- }
636
- }
637
- /**
638
- * Count the number of tokens in the given text
639
- * @param text - The text to count tokens for
640
- * @returns Promise resolving to the estimated token count
641
- *
642
- * @remarks
643
- * OpenAI doesn't provide a direct token counting API, so this is an approximation
644
- * based on the general rule of ~4 characters per token. For precise counting,
645
- * consider using a dedicated tokenizer library.
646
- *
647
- * @example
648
- * ```typescript
649
- * const count = await provider.countTokens('Hello, world!');
650
- * console.log(count); // Approximately 4 tokens
651
- * ```
652
- */
653
- async countTokens(text) {
654
- return Math.ceil(text.length / 4);
655
- }
656
- /**
657
- * Get a list of available OpenAI models
658
- * @returns Promise resolving to an array of model information
659
- * @throws {AIError} When the API request fails
660
- *
661
- * @example
662
- * ```typescript
663
- * const models = await provider.getModels();
664
- * const gptModels = models.filter(m => m.id.includes('gpt'));
665
- * ```
666
- */
667
- async getModels() {
668
- try {
669
- const response = await this.client.models.list();
670
- return response.data.filter((model) => this.profile.shouldIncludeModel(model.id)).map((model) => ({
671
- id: model.id,
672
- name: model.id,
673
- description: this.profile.describeModel(model.id),
674
- contextLength: this.profile.getContextLength(model.id),
675
- capabilities: this.profile.getModelCapabilities(model.id),
676
- supportsFunctions: this.profile.supportsFunctions(model.id),
677
- supportsVision: this.profile.supportsVision(model.id)
678
- }));
679
- } catch (error) {
680
- throw this.mapError(error);
681
- }
682
- }
683
- /**
684
- * Get the capabilities supported by this OpenAI provider
685
- * @returns Promise resolving to provider capabilities
686
- *
687
- * @example
688
- * ```typescript
689
- * const caps = await provider.getCapabilities();
690
- * if (caps.functions) {
691
- * // Provider supports function calling
692
- * }
693
- * ```
694
- */
695
- async getCapabilities() {
696
- return { ...this.profile.capabilities };
697
- }
698
- // ============================================================================
699
- // TTS Methods (Not supported - use Qwen3-TTS provider)
700
- // ============================================================================
701
- async synthesizeSpeech(_text, _options) {
702
- throw new AIError(
703
- `TTS is not supported by ${this.profile.providerLabel} provider. Use Qwen3-TTS provider.`,
704
- "NOT_IMPLEMENTED",
705
- this.profile.providerName
706
- );
707
- }
708
- streamSpeech(_text, _options) {
709
- const error = new AIError(
710
- `TTS streaming is not supported by ${this.profile.providerLabel} provider. Use Qwen3-TTS provider.`,
711
- "NOT_IMPLEMENTED",
712
- this.profile.providerName
713
- );
714
- return {
715
- [Symbol.asyncIterator]: () => ({
716
- next: () => Promise.reject(error)
717
- })
718
- };
719
- }
720
- async cloneVoice(_options) {
721
- throw new AIError(
722
- `Voice cloning is not supported by ${this.profile.providerLabel} provider. Use Qwen3-TTS provider.`,
723
- "NOT_IMPLEMENTED",
724
- this.profile.providerName
725
- );
726
- }
727
- async designVoice(_options) {
728
- throw new AIError(
729
- `Voice design is not supported by ${this.profile.providerLabel} provider. Use Qwen3-TTS provider.`,
730
- "NOT_IMPLEMENTED",
731
- this.profile.providerName
732
- );
733
- }
734
- async getVoices(_options) {
735
- throw new AIError(
736
- `Voice listing is not supported by ${this.profile.providerLabel} provider. Use Qwen3-TTS provider.`,
737
- "NOT_IMPLEMENTED",
738
- this.profile.providerName
739
- );
740
- }
741
- /**
742
- * Maps internal AI messages to OpenAI's message format
743
- * @param messages - Array of internal AI messages
744
- * @returns Array of OpenAI-compatible message parameters
745
- * @private
746
- */
747
- mapMessagesToOpenAI(messages) {
748
- return messages.map((message) => {
749
- let content;
750
- if (typeof message.content === "string") {
751
- content = message.content;
752
- } else {
753
- content = message.content.map((part) => {
754
- if (part.type === "text") {
755
- return { type: "text", text: part.text };
756
- }
757
- return {
758
- type: "image_url",
759
- image_url: {
760
- url: part.image_url.url,
761
- detail: part.image_url.detail
762
- }
763
- };
764
- });
765
- }
766
- const baseMessage = {
767
- role: message.role,
768
- content
769
- };
770
- if (message.name && (message.role === "system" || message.role === "user" || message.role === "function")) {
771
- baseMessage.name = message.name;
772
- }
773
- if (message.tool_calls && message.role === "assistant") {
774
- baseMessage.tool_calls = message.tool_calls;
775
- }
776
- return baseMessage;
777
- });
778
- }
779
- /**
780
- * Maps internal tool choice format to OpenAI's tool choice format
781
- * @param toolChoice - Internal tool choice specification
782
- * @returns OpenAI-compatible tool choice option or undefined
783
- * @private
784
- */
785
- mapToolChoice(toolChoice) {
786
- if (!toolChoice) return void 0;
787
- if (typeof toolChoice === "string") return toolChoice;
788
- return {
789
- type: "function",
790
- function: { name: toolChoice.function.name }
791
- };
792
- }
793
- /**
794
- * Maps OpenAI usage information to internal token usage format
795
- * @param usage - OpenAI usage object from API response
796
- * @returns Internal token usage object or undefined
797
- * @private
798
- */
799
- mapUsage(usage) {
800
- if (!usage) return void 0;
801
- return {
802
- promptTokens: usage.prompt_tokens || 0,
803
- completionTokens: usage.completion_tokens || 0,
804
- totalTokens: usage.total_tokens || 0
805
- };
806
- }
807
- /**
808
- * Maps OpenAI finish reason to internal finish reason format
809
- * @param reason - OpenAI finish reason from API response
810
- * @returns Internal finish reason
811
- * @private
812
- */
813
- mapFinishReason(reason) {
814
- switch (reason) {
815
- case "stop":
816
- return "stop";
817
- case "length":
818
- return "length";
819
- case "tool_calls":
820
- return "tool_calls";
821
- case "content_filter":
822
- return "content_filter";
823
- default:
824
- return "stop";
825
- }
826
- }
827
- /**
828
- * Gets the context length for a given OpenAI model
829
- * @param modelId - The OpenAI model identifier
830
- * @returns Maximum context length in tokens
831
- * @private
832
- */
833
- /**
834
- * Maps OpenAI API errors to internal AI error types
835
- * @param error - The error object from OpenAI API
836
- * @returns Appropriate internal AI error instance
837
- * @private
838
- */
839
- mapError(error) {
840
- if (error instanceof OpenAI.APIError) {
841
- switch (error.status) {
842
- case 401:
843
- return new AuthenticationError(this.profile.providerName);
844
- case 429: {
845
- return new RateLimitError(
846
- this.profile.providerName,
847
- extractRetryAfterSeconds(error)
848
- );
849
- }
850
- case 404:
851
- return new ModelNotFoundError(
852
- error.message,
853
- this.profile.providerName
854
- );
855
- case 413:
856
- return new ContextLengthError(this.profile.providerName);
857
- default:
858
- if (error.message.includes("content_filter")) {
859
- return new ContentFilterError(this.profile.providerName);
860
- }
861
- return new AIError(
862
- error.message,
863
- "API_ERROR",
864
- this.profile.providerName
865
- );
866
- }
867
- }
868
- if (error instanceof AIError) {
869
- return error;
870
- }
871
- const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
872
- return new AIError(
873
- errorMessage,
874
- "UNKNOWN_ERROR",
875
- this.profile.providerName
876
- );
877
- }
878
- }
879
- export {
880
- OpenAIProvider
881
- };
882
- //# sourceMappingURL=openai-CJEo69jb.js.map