@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,934 @@
1
+ import { ValidationError, ApiError, loadEnvConfig } from "@happyvertical/utils";
2
+ import OpenAI from "openai";
3
+ const AI_PROVIDER_TYPES = [
4
+ "openai",
5
+ "litellm",
6
+ "bifrost",
7
+ "ollama",
8
+ "gemini",
9
+ "anthropic",
10
+ "huggingface",
11
+ "bedrock",
12
+ "claude-cli",
13
+ "qwen3-tts"
14
+ ];
15
+ function extractTextContent(content) {
16
+ if (typeof content === "string") {
17
+ return content;
18
+ }
19
+ return content.filter((part) => part.type === "text").map((part) => part.text).join("\n");
20
+ }
21
+ class AIError extends Error {
22
+ constructor(message, code, provider, model, retryable = false) {
23
+ super(message);
24
+ this.code = code;
25
+ this.provider = provider;
26
+ this.model = model;
27
+ this.retryable = retryable;
28
+ this.name = "AIError";
29
+ }
30
+ }
31
+ class AuthenticationError extends AIError {
32
+ constructor(provider) {
33
+ super("Authentication failed", "AUTH_ERROR", provider, void 0, false);
34
+ this.name = "AuthenticationError";
35
+ }
36
+ }
37
+ class RateLimitError extends AIError {
38
+ retryAfter;
39
+ constructor(provider, retryAfter) {
40
+ super(
41
+ `Rate limit exceeded${retryAfter ? `, retry after ${retryAfter}s` : ""}`,
42
+ "RATE_LIMIT",
43
+ provider,
44
+ void 0,
45
+ true
46
+ );
47
+ this.name = "RateLimitError";
48
+ this.retryAfter = retryAfter;
49
+ }
50
+ }
51
+ class ModelNotFoundError extends AIError {
52
+ constructor(model, provider) {
53
+ super(
54
+ `Model not found: ${model}`,
55
+ "MODEL_NOT_FOUND",
56
+ provider,
57
+ model,
58
+ false
59
+ );
60
+ this.name = "ModelNotFoundError";
61
+ }
62
+ }
63
+ class ContextLengthError extends AIError {
64
+ constructor(provider, model) {
65
+ super(
66
+ "Input exceeds maximum context length",
67
+ "CONTEXT_LENGTH_EXCEEDED",
68
+ provider,
69
+ model,
70
+ false
71
+ );
72
+ this.name = "ContextLengthError";
73
+ }
74
+ }
75
+ class ContentFilterError extends AIError {
76
+ constructor(provider, model) {
77
+ super(
78
+ "Content filtered by safety systems",
79
+ "CONTENT_FILTERED",
80
+ provider,
81
+ model,
82
+ false
83
+ );
84
+ this.name = "ContentFilterError";
85
+ }
86
+ }
87
+ function isOpenAIClientOptions(options) {
88
+ return options.type === "openai" && "apiKey" in options && !!options.apiKey;
89
+ }
90
+ function isAIClientInstance(value) {
91
+ return value instanceof AIClient;
92
+ }
93
+ class AIClient {
94
+ /**
95
+ * Configuration options for this client
96
+ */
97
+ options;
98
+ /**
99
+ * Creates a new AIClient
100
+ *
101
+ * @param options - Client configuration options
102
+ */
103
+ constructor(options) {
104
+ this.options = options;
105
+ }
106
+ /**
107
+ * Sends a message to the AI
108
+ * Base implementation returns a placeholder response
109
+ *
110
+ * @param text - Message text
111
+ * @param options - Message options
112
+ * @returns Promise resolving to a placeholder response
113
+ */
114
+ async message(_text, _options = { role: "user" }) {
115
+ return "not a real ai message, this is the base class!";
116
+ }
117
+ /**
118
+ * Factory method to create appropriate AI client based on options
119
+ *
120
+ * @param options - Client configuration options
121
+ * @returns Promise resolving to an initialized AI client
122
+ * @throws Error if client type is invalid
123
+ */
124
+ static async create(options) {
125
+ if (isAIClientInstance(options)) {
126
+ return options;
127
+ }
128
+ const clientOptions = options;
129
+ if (isOpenAIClientOptions(clientOptions)) {
130
+ return OpenAIClient.create(clientOptions);
131
+ }
132
+ const providedType = clientOptions.type;
133
+ if (providedType && providedType !== "openai") {
134
+ const { getAI: getAI2 } = await Promise.resolve().then(() => factory);
135
+ return await getAI2(clientOptions);
136
+ }
137
+ if (providedType === "openai") {
138
+ throw new ValidationError(
139
+ "OpenAI API key is required but missing or empty",
140
+ {
141
+ supportedTypes: [...AI_PROVIDER_TYPES],
142
+ providedType,
143
+ hint: "Set OPENAI_API_KEY environment variable or pass apiKey in options"
144
+ }
145
+ );
146
+ }
147
+ throw new ValidationError("Invalid client type specified", {
148
+ supportedTypes: [...AI_PROVIDER_TYPES],
149
+ providedType
150
+ });
151
+ }
152
+ /**
153
+ * Gets a text completion from the AI
154
+ * In base class, delegates to message method
155
+ *
156
+ * @param text - Input text for completion
157
+ * @param options - Completion options
158
+ * @returns Promise resolving to the completion result
159
+ */
160
+ textCompletion(text, options = {
161
+ role: "user"
162
+ }) {
163
+ return this.message(text, options);
164
+ }
165
+ }
166
+ async function getOpenAI(options) {
167
+ return new OpenAI({
168
+ apiKey: options.apiKey,
169
+ baseURL: options.baseUrl
170
+ });
171
+ }
172
+ class OpenAIClient extends AIClient {
173
+ /**
174
+ * OpenAI client instance
175
+ */
176
+ openai;
177
+ /**
178
+ * Configuration options for this client
179
+ */
180
+ options;
181
+ /**
182
+ * Creates a new OpenAIClient
183
+ *
184
+ * @param options - OpenAI client configuration options
185
+ */
186
+ constructor(options) {
187
+ super(options);
188
+ this.options = options;
189
+ }
190
+ /**
191
+ * Sends a message to OpenAI
192
+ *
193
+ * @param text - Message text
194
+ * @param options - Message options
195
+ * @returns Promise resolving to the OpenAI response
196
+ */
197
+ async message(text, options = { role: "user" }) {
198
+ const response = await this.textCompletion(text, options);
199
+ return response;
200
+ }
201
+ /**
202
+ * Factory method to create and initialize an OpenAIClient
203
+ *
204
+ * @param options - OpenAI client configuration options
205
+ * @returns Promise resolving to an initialized OpenAIClient
206
+ */
207
+ static async create(options) {
208
+ const client = new OpenAIClient(options);
209
+ await client.initialize();
210
+ return client;
211
+ }
212
+ /**
213
+ * Initializes the OpenAI client
214
+ */
215
+ async initialize() {
216
+ this.openai = new OpenAI({
217
+ apiKey: this.options.apiKey,
218
+ baseURL: this.options.baseUrl
219
+ });
220
+ }
221
+ /**
222
+ * Sends a text completion request to the OpenAI API
223
+ *
224
+ * @param message - The message to send
225
+ * @param options - Configuration options for the completion request
226
+ * @returns Promise resolving to the completion text
227
+ * @throws Error if the OpenAI API response is invalid
228
+ */
229
+ async textCompletion(message, options = {}) {
230
+ const {
231
+ model = "gpt-4o",
232
+ role = "user",
233
+ history = [],
234
+ name: _name,
235
+ frequencyPenalty = 0,
236
+ logitBias,
237
+ logprobs = false,
238
+ topLogprobs,
239
+ maxTokens,
240
+ n = 1,
241
+ presencePenalty = 0,
242
+ responseFormat,
243
+ seed,
244
+ stop,
245
+ stream: _stream = false,
246
+ temperature = 1,
247
+ topProbability: topP = 1,
248
+ tools,
249
+ toolChoice,
250
+ user,
251
+ onProgress
252
+ } = options;
253
+ const messages = [
254
+ ...history,
255
+ {
256
+ role,
257
+ content: message
258
+ }
259
+ ];
260
+ if (onProgress) {
261
+ const stream = await this.openai.chat.completions.create({
262
+ model,
263
+ messages,
264
+ stream: true,
265
+ frequency_penalty: frequencyPenalty,
266
+ logit_bias: logitBias,
267
+ logprobs,
268
+ top_logprobs: topLogprobs,
269
+ max_tokens: maxTokens,
270
+ n,
271
+ presence_penalty: presencePenalty,
272
+ response_format: responseFormat,
273
+ seed,
274
+ stop,
275
+ temperature,
276
+ top_p: topP,
277
+ tools,
278
+ tool_choice: toolChoice,
279
+ user
280
+ });
281
+ let fullContent = "";
282
+ for await (const chunk of stream) {
283
+ const content = chunk.choices[0]?.delta?.content || "";
284
+ fullContent += content;
285
+ onProgress(content);
286
+ }
287
+ return fullContent;
288
+ }
289
+ const response = await this.openai.chat.completions.create({
290
+ model,
291
+ messages,
292
+ frequency_penalty: frequencyPenalty,
293
+ logit_bias: logitBias,
294
+ logprobs,
295
+ top_logprobs: topLogprobs,
296
+ max_tokens: maxTokens,
297
+ n,
298
+ presence_penalty: presencePenalty,
299
+ response_format: responseFormat,
300
+ seed,
301
+ stop,
302
+ stream: false,
303
+ temperature,
304
+ top_p: topP,
305
+ tools,
306
+ tool_choice: toolChoice,
307
+ user
308
+ });
309
+ const choice = response.choices[0];
310
+ if (!choice || !choice.message || !choice.message.content) {
311
+ throw new ApiError("Invalid response from OpenAI API: Missing content", {
312
+ model,
313
+ responseId: response.id,
314
+ choices: response.choices?.length || 0,
315
+ hasChoice: !!choice,
316
+ hasMessage: !!choice?.message,
317
+ hasContent: !!choice?.message?.content
318
+ });
319
+ }
320
+ return choice.message.content;
321
+ }
322
+ }
323
+ async function getAIClient(options) {
324
+ const { getAI: getAI2 } = await Promise.resolve().then(() => factory);
325
+ return await getAI2(options);
326
+ }
327
+ const RATE_LIMITED_METHODS = /* @__PURE__ */ new Set([
328
+ "chat",
329
+ "complete",
330
+ "message",
331
+ "embed",
332
+ "embedImage",
333
+ "describeImage",
334
+ "generateImage",
335
+ "getModels",
336
+ "synthesizeSpeech",
337
+ "cloneVoice",
338
+ "designVoice",
339
+ "getVoices"
340
+ ]);
341
+ const MAX_BUDGET_COORDINATORS = 128;
342
+ const BUDGET_COORDINATOR_TTL_MS = 15 * 60 * 1e3;
343
+ class BudgetCoordinator {
344
+ nextAvailableAt = 0;
345
+ pendingSchedules = 0;
346
+ tail = Promise.resolve();
347
+ lastUsedAt = Date.now();
348
+ touch() {
349
+ this.lastUsedAt = Date.now();
350
+ }
351
+ schedule(work) {
352
+ this.pendingSchedules += 1;
353
+ this.touch();
354
+ const run = this.tail.then(work, work);
355
+ this.tail = run.then(
356
+ () => void 0,
357
+ () => void 0
358
+ );
359
+ return run.finally(() => {
360
+ this.pendingSchedules = Math.max(0, this.pendingSchedules - 1);
361
+ this.touch();
362
+ });
363
+ }
364
+ async waitUntilReady() {
365
+ this.touch();
366
+ const delayMs = this.nextAvailableAt - Date.now();
367
+ if (delayMs > 0) {
368
+ await sleep(delayMs);
369
+ }
370
+ }
371
+ delayFor(delayMs) {
372
+ this.touch();
373
+ if (delayMs <= 0) {
374
+ return;
375
+ }
376
+ this.nextAvailableAt = Math.max(this.nextAvailableAt, Date.now() + delayMs);
377
+ }
378
+ isEvictable(now) {
379
+ return this.pendingSchedules === 0 && now >= this.nextAvailableAt;
380
+ }
381
+ isExpired(now) {
382
+ return this.isEvictable(now) && now - this.lastUsedAt >= BUDGET_COORDINATOR_TTL_MS;
383
+ }
384
+ getLastUsedAt() {
385
+ return this.lastUsedAt;
386
+ }
387
+ }
388
+ const budgetCoordinators = /* @__PURE__ */ new Map();
389
+ function sleep(delayMs) {
390
+ return new Promise((resolve) => {
391
+ setTimeout(resolve, delayMs);
392
+ });
393
+ }
394
+ function pruneBudgetCoordinators() {
395
+ const now = Date.now();
396
+ for (const [key, coordinator] of budgetCoordinators.entries()) {
397
+ if (coordinator.isExpired(now)) {
398
+ budgetCoordinators.delete(key);
399
+ }
400
+ }
401
+ while (budgetCoordinators.size > MAX_BUDGET_COORDINATORS) {
402
+ const evictableEntries = [...budgetCoordinators.entries()].filter(([, coordinator]) => coordinator.isEvictable(now)).sort(
403
+ ([, leftCoordinator], [, rightCoordinator]) => leftCoordinator.getLastUsedAt() - rightCoordinator.getLastUsedAt()
404
+ );
405
+ if (evictableEntries.length === 0) {
406
+ break;
407
+ }
408
+ budgetCoordinators.delete(evictableEntries[0][0]);
409
+ }
410
+ }
411
+ function getBudgetCoordinator(key) {
412
+ pruneBudgetCoordinators();
413
+ let coordinator = budgetCoordinators.get(key);
414
+ if (!coordinator) {
415
+ coordinator = new BudgetCoordinator();
416
+ budgetCoordinators.set(key, coordinator);
417
+ pruneBudgetCoordinators();
418
+ }
419
+ coordinator.touch();
420
+ return coordinator;
421
+ }
422
+ function hasPacingConfig(rateLimit) {
423
+ if (!rateLimit || rateLimit.enabled === false) {
424
+ return false;
425
+ }
426
+ return rateLimit.enabled === true || rateLimit.key !== void 0 || rateLimit.cooldownMs !== void 0 || rateLimit.initialDelayMs !== void 0 || rateLimit.maxAttempts !== void 0;
427
+ }
428
+ function normalizeNonNegativeInteger(value, fallback) {
429
+ if (typeof value !== "number" || !Number.isFinite(value)) {
430
+ return fallback;
431
+ }
432
+ return Math.max(0, Math.trunc(value));
433
+ }
434
+ function hashKey(value) {
435
+ let hash = 2166136261;
436
+ for (let index = 0; index < value.length; index += 1) {
437
+ hash ^= value.charCodeAt(index);
438
+ hash = Math.imul(hash, 16777619);
439
+ }
440
+ return (hash >>> 0).toString(36);
441
+ }
442
+ function deriveBudgetKey(options) {
443
+ const type = options.type;
444
+ const provider = typeof type === "string" && type ? type : "openai";
445
+ const credentialLikeValues = [
446
+ "apiKey" in options ? options.apiKey : void 0,
447
+ "apiToken" in options ? options.apiToken : void 0,
448
+ "credentials" in options ? options.credentials?.accessKeyId : void 0,
449
+ "endpoint" in options ? options.endpoint : void 0,
450
+ "baseUrl" in options ? options.baseUrl : void 0,
451
+ "cliPath" in options ? options.cliPath : void 0
452
+ ];
453
+ const seed = credentialLikeValues.find(
454
+ (value) => typeof value === "string" && value.length > 0
455
+ );
456
+ return seed ? `${provider}:${hashKey(seed)}` : `${provider}:default`;
457
+ }
458
+ function normalizeRateLimitConfig(options) {
459
+ const rateLimit = "rateLimit" in options ? options.rateLimit : void 0;
460
+ if (!hasPacingConfig(rateLimit)) {
461
+ return null;
462
+ }
463
+ return {
464
+ cooldownMs: normalizeNonNegativeInteger(rateLimit?.cooldownMs, 0),
465
+ initialDelayMs: normalizeNonNegativeInteger(
466
+ rateLimit?.initialDelayMs,
467
+ 5e3
468
+ ),
469
+ key: rateLimit?.key?.trim() || deriveBudgetKey(options),
470
+ maxAttempts: Math.max(
471
+ 1,
472
+ normalizeNonNegativeInteger(rateLimit?.maxAttempts, 3)
473
+ )
474
+ };
475
+ }
476
+ function getRetryDelayMs(error, config) {
477
+ const hintedDelayMs = typeof error.retryAfter === "number" && Number.isFinite(error.retryAfter) ? Math.max(0, Math.ceil(error.retryAfter * 1e3)) : void 0;
478
+ if (hintedDelayMs !== void 0) {
479
+ return Math.max(config.cooldownMs, hintedDelayMs);
480
+ }
481
+ return Math.max(config.cooldownMs, config.initialDelayMs);
482
+ }
483
+ async function invokeWithPacing(execute, coordinator, config) {
484
+ let attempt = 1;
485
+ while (true) {
486
+ await coordinator.waitUntilReady();
487
+ try {
488
+ const result = await execute();
489
+ coordinator.delayFor(config.cooldownMs);
490
+ return result;
491
+ } catch (error) {
492
+ if (error instanceof RateLimitError) {
493
+ coordinator.delayFor(getRetryDelayMs(error, config));
494
+ if (error.retryable && attempt < config.maxAttempts) {
495
+ attempt += 1;
496
+ continue;
497
+ }
498
+ }
499
+ throw error;
500
+ }
501
+ }
502
+ }
503
+ function parseRetryAfterSeconds(retryAfter) {
504
+ if (typeof retryAfter === "number" && Number.isFinite(retryAfter)) {
505
+ return Math.max(0, retryAfter);
506
+ }
507
+ if (typeof retryAfter !== "string") {
508
+ return void 0;
509
+ }
510
+ const trimmed = retryAfter.trim();
511
+ if (!trimmed) {
512
+ return void 0;
513
+ }
514
+ const seconds = Number.parseFloat(trimmed);
515
+ if (!Number.isNaN(seconds)) {
516
+ return Math.max(0, seconds);
517
+ }
518
+ const retryAt = Date.parse(trimmed);
519
+ if (Number.isNaN(retryAt)) {
520
+ return void 0;
521
+ }
522
+ return Math.max(0, Math.ceil((retryAt - Date.now()) / 1e3));
523
+ }
524
+ function getHeaderValue(headers, headerName) {
525
+ if (!headers || typeof headers !== "object") {
526
+ return void 0;
527
+ }
528
+ if ("get" in headers && typeof headers.get === "function") {
529
+ return headers.get(headerName) ?? headers.get(headerName.toLowerCase());
530
+ }
531
+ const objectHeaders = headers;
532
+ return objectHeaders[headerName] ?? objectHeaders[headerName.toLowerCase()];
533
+ }
534
+ function extractRetryAfterSeconds(error) {
535
+ if (!error || typeof error !== "object") {
536
+ return void 0;
537
+ }
538
+ if ("retryAfter" in error) {
539
+ const retryAfter = parseRetryAfterSeconds(
540
+ error.retryAfter
541
+ );
542
+ if (retryAfter !== void 0) {
543
+ return retryAfter;
544
+ }
545
+ }
546
+ if ("headers" in error) {
547
+ const retryAfter = parseRetryAfterSeconds(
548
+ getHeaderValue(error.headers, "retry-after")
549
+ );
550
+ if (retryAfter !== void 0) {
551
+ return retryAfter;
552
+ }
553
+ }
554
+ const message = "message" in error && typeof error.message === "string" ? error.message : "";
555
+ const retryAfterMatch = message.match(/retry after\s+(\d+(?:\.\d+)?)\s*s?/i) ?? message.match(/retryDelay[^\d]*(\d+(?:\.\d+)?)s/i);
556
+ return retryAfterMatch ? parseRetryAfterSeconds(retryAfterMatch[1]) : void 0;
557
+ }
558
+ function createRateLimitedAI(client, options) {
559
+ const config = normalizeRateLimitConfig(options);
560
+ if (!config) {
561
+ return client;
562
+ }
563
+ const wrappedMethods = /* @__PURE__ */ new Map();
564
+ return new Proxy(client, {
565
+ get(target, property, receiver) {
566
+ const value = Reflect.get(target, property, receiver);
567
+ if (typeof value !== "function") {
568
+ return value;
569
+ }
570
+ if (!RATE_LIMITED_METHODS.has(property)) {
571
+ if (!wrappedMethods.has(property)) {
572
+ wrappedMethods.set(property, value.bind(target));
573
+ }
574
+ return wrappedMethods.get(property);
575
+ }
576
+ if (!wrappedMethods.has(property)) {
577
+ wrappedMethods.set(property, (...args) => {
578
+ const coordinator = getBudgetCoordinator(config.key);
579
+ return coordinator.schedule(
580
+ () => invokeWithPacing(
581
+ () => Reflect.apply(value, target, args),
582
+ coordinator,
583
+ config
584
+ )
585
+ );
586
+ });
587
+ }
588
+ return wrappedMethods.get(property);
589
+ }
590
+ });
591
+ }
592
+ function isOpenAIOptions(options) {
593
+ return !options.type || options.type === "openai";
594
+ }
595
+ function isLiteLLMOptions(options) {
596
+ return options.type === "litellm";
597
+ }
598
+ function isBifrostOptions(options) {
599
+ return options.type === "bifrost";
600
+ }
601
+ function isOllamaOptions(options) {
602
+ return options.type === "ollama";
603
+ }
604
+ function isGeminiOptions(options) {
605
+ return options.type === "gemini";
606
+ }
607
+ function isAnthropicOptions(options) {
608
+ return options.type === "anthropic";
609
+ }
610
+ function isHuggingFaceOptions(options) {
611
+ return options.type === "huggingface";
612
+ }
613
+ function isBedrockOptions(options) {
614
+ return options.type === "bedrock";
615
+ }
616
+ function isClaudeCliOptions(options) {
617
+ return options.type === "claude-cli";
618
+ }
619
+ function isQwen3TTSOptions(options) {
620
+ return options.type === "qwen3-tts";
621
+ }
622
+ async function getAI(options = {}) {
623
+ options = loadEnvConfig(options, {
624
+ packageName: "ai",
625
+ schema: {
626
+ provider: "string",
627
+ type: "string",
628
+ // Alias for provider
629
+ model: "string",
630
+ defaultModel: "string",
631
+ timeout: "number",
632
+ maxRetries: "number",
633
+ apiKey: "string",
634
+ baseUrl: "string",
635
+ adminApiKey: "string",
636
+ adminBaseUrl: "string",
637
+ adminUrl: "string",
638
+ adminUser: "string",
639
+ adminUsername: "string",
640
+ adminPassword: "string"
641
+ }
642
+ });
643
+ if ("provider" in options && !options.type) {
644
+ options.type = options.provider;
645
+ }
646
+ if ("model" in options && !options.defaultModel) {
647
+ options.defaultModel = options.model;
648
+ }
649
+ let client;
650
+ if (isOpenAIOptions(options)) {
651
+ const { OpenAIProvider } = await import("./openai-5snI2diE.js");
652
+ client = new OpenAIProvider(options);
653
+ } else if (isLiteLLMOptions(options)) {
654
+ const { LiteLLMProvider } = await import("./litellm-DhPKa_Jz.js");
655
+ client = new LiteLLMProvider(options);
656
+ } else if (isBifrostOptions(options)) {
657
+ const { BifrostProvider } = await import("./bifrost-3mXtQsTj.js");
658
+ client = new BifrostProvider(options);
659
+ } else if (isOllamaOptions(options)) {
660
+ const { OllamaProvider } = await import("./ollama-Di1ldur0.js");
661
+ client = new OllamaProvider(options);
662
+ } else if (isGeminiOptions(options)) {
663
+ const { GeminiProvider } = await import("./gemini-BfpHXDIQ.js");
664
+ client = new GeminiProvider(options);
665
+ } else if (isAnthropicOptions(options)) {
666
+ const { AnthropicProvider } = await import("./anthropic-BRwbhwIl.js");
667
+ client = new AnthropicProvider(options);
668
+ } else if (isHuggingFaceOptions(options)) {
669
+ const { HuggingFaceProvider } = await import("./huggingface-280qv9iv.js");
670
+ client = new HuggingFaceProvider(options);
671
+ } else if (isBedrockOptions(options)) {
672
+ const { BedrockProvider } = await import("./bedrock-Cf1xUerN.js");
673
+ client = new BedrockProvider(options);
674
+ } else if (isClaudeCliOptions(options)) {
675
+ const { ClaudeCliProvider } = await import("./claude-cli-BrHRfkry.js");
676
+ client = new ClaudeCliProvider(options);
677
+ } else if (isQwen3TTSOptions(options)) {
678
+ const { Qwen3TTSProvider } = await import("./qwen-tts-DgPgdXxG.js");
679
+ client = new Qwen3TTSProvider(options);
680
+ } else {
681
+ throw new ValidationError("Unsupported AI provider type", {
682
+ supportedTypes: [...AI_PROVIDER_TYPES],
683
+ providedType: options.type
684
+ });
685
+ }
686
+ return createRateLimitedAI(client, options);
687
+ }
688
+ async function getAIAuto(options) {
689
+ const baseUrl = String(options.baseUrl || "");
690
+ const hasKeepAliveOption = "keepAlive" in options && options.keepAlive !== void 0;
691
+ if (/((?:localhost|127\.0\.0\.1)(?::11434)?(?:\/(?:api|v1))?|ollama(?:\.com)?(?:\/(?:api|v1))?)\/?$/i.test(
692
+ baseUrl
693
+ ) || hasKeepAliveOption) {
694
+ return getAI({ ...options, type: "ollama" });
695
+ }
696
+ if (options.apiKey && !options.type) {
697
+ return getAI({ ...options, type: "openai" });
698
+ }
699
+ if (options.apiToken) {
700
+ return getAI({ ...options, type: "huggingface" });
701
+ }
702
+ if (options.region && options.credentials) {
703
+ return getAI({ ...options, type: "bedrock" });
704
+ }
705
+ if (options.projectId || options.anthropicVersion) {
706
+ if (options.anthropicVersion) {
707
+ return getAI({ ...options, type: "anthropic" });
708
+ }
709
+ if (options.projectId) {
710
+ return getAI({ ...options, type: "gemini" });
711
+ }
712
+ }
713
+ throw new ValidationError("Could not auto-detect AI provider from options", {
714
+ hint: 'Please specify a "type" field in options or provide provider-specific credentials',
715
+ supportedTypes: [...AI_PROVIDER_TYPES],
716
+ providedOptions: Object.keys(options)
717
+ });
718
+ }
719
+ const factory = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
720
+ __proto__: null,
721
+ getAI,
722
+ getAIAuto
723
+ }, Symbol.toStringTag, { value: "Module" }));
724
+ class AIMessage {
725
+ /**
726
+ * Original options used to create this message
727
+ */
728
+ options;
729
+ /**
730
+ * Name of the message sender
731
+ */
732
+ name;
733
+ /**
734
+ * Content of the message
735
+ */
736
+ content;
737
+ /**
738
+ * Role of the message sender in the conversation
739
+ */
740
+ role;
741
+ /**
742
+ * Creates a new AI message
743
+ *
744
+ * @param options - Message configuration
745
+ * @param options.role - Role of the message sender
746
+ * @param options.content - Content of the message
747
+ * @param options.name - Name of the message sender
748
+ */
749
+ constructor(options) {
750
+ this.options = options;
751
+ this.role = options.role;
752
+ this.content = options.content;
753
+ this.name = options.name;
754
+ }
755
+ /**
756
+ * Factory method to create a new AI message
757
+ *
758
+ * @param options - Message configuration
759
+ * @param options.thread - Thread this message belongs to
760
+ * @param options.role - Role of the message sender
761
+ * @param options.content - Content of the message
762
+ * @param options.name - Name of the message sender
763
+ * @returns Promise resolving to a new AIMessage instance
764
+ */
765
+ static async create(options) {
766
+ return new AIMessage(options);
767
+ }
768
+ }
769
+ class AIThread {
770
+ /**
771
+ * AI client instance for this thread
772
+ */
773
+ ai;
774
+ /**
775
+ * Options used to configure this thread
776
+ */
777
+ options;
778
+ /**
779
+ * Messages in this conversation thread
780
+ */
781
+ messages = [];
782
+ /**
783
+ * Reference materials to include in the conversation context
784
+ */
785
+ references = {};
786
+ /**
787
+ * Creates a new AI thread
788
+ *
789
+ * @param options - Thread configuration options
790
+ */
791
+ constructor(options) {
792
+ this.options = options;
793
+ }
794
+ /**
795
+ * Factory method to create and initialize a new AI thread
796
+ *
797
+ * @param options - Thread configuration options
798
+ * @returns Promise resolving to an initialized AIThread
799
+ */
800
+ static async create(options) {
801
+ const thread = new AIThread(options);
802
+ await thread.initialize();
803
+ return thread;
804
+ }
805
+ /**
806
+ * Initializes the AI client for this thread
807
+ */
808
+ async initialize() {
809
+ this.ai = await AIClient.create(this.options.ai);
810
+ }
811
+ /**
812
+ * Adds a system message to the conversation
813
+ *
814
+ * @param prompt - System message content
815
+ * @returns Promise resolving to the created AIMessage
816
+ */
817
+ async addSystem(prompt) {
818
+ const message = await AIMessage.create({
819
+ thread: this,
820
+ role: "system",
821
+ name: "system",
822
+ content: prompt
823
+ });
824
+ this.messages.push(message);
825
+ return message;
826
+ }
827
+ /**
828
+ * Adds a message to the conversation
829
+ *
830
+ * @param options - Message options
831
+ * @param options.role - Role of the message sender
832
+ * @param options.name - Optional name of the message sender
833
+ * @param options.content - Content of the message
834
+ * @returns Promise resolving to the created AIMessage
835
+ */
836
+ async add(options) {
837
+ const message = await AIMessage.create({
838
+ thread: this,
839
+ role: options.role,
840
+ name: options.name || options.role,
841
+ // Default name to role if not provided
842
+ content: options.content
843
+ });
844
+ this.messages.push(message);
845
+ return message;
846
+ }
847
+ /**
848
+ * Gets all messages in this thread
849
+ *
850
+ * @returns Array of AIMessage objects
851
+ */
852
+ get() {
853
+ return this.messages;
854
+ }
855
+ /**
856
+ * Adds a reference to be included in the conversation context
857
+ *
858
+ * @param name - Name of the reference
859
+ * @param body - Content of the reference
860
+ */
861
+ addReference(name, body) {
862
+ this.references[name] = body;
863
+ }
864
+ /**
865
+ * Assembles the conversation history for sending to the AI
866
+ * Properly orders system message, references, and conversation messages
867
+ *
868
+ * @returns Array of message parameters formatted for the OpenAI API
869
+ */
870
+ assembleHistory() {
871
+ const history = [];
872
+ const systemMessage = this.messages.find((m) => m.role === "system");
873
+ if (systemMessage) {
874
+ history.push({
875
+ role: systemMessage.role,
876
+ content: systemMessage.content
877
+ });
878
+ }
879
+ for (const name in this.references) {
880
+ history.push({
881
+ role: "user",
882
+ content: `Reference - ${name}:
883
+ ${this.references[name]}`
884
+ });
885
+ }
886
+ this.messages.filter((m) => m.role !== "system").forEach((message) => {
887
+ history.push({ role: message.role, content: message.content });
888
+ });
889
+ return history;
890
+ }
891
+ /**
892
+ * Sends a prompt to the AI and gets a response
893
+ *
894
+ * @param prompt - Prompt message to send
895
+ * @param options - Options for the AI response
896
+ * @param options.responseFormat - Format for the AI to respond with
897
+ * @returns Promise resolving to the AI response
898
+ */
899
+ async do(prompt, options = {
900
+ responseFormat: "text"
901
+ }) {
902
+ const { responseFormat } = options;
903
+ const history = this.assembleHistory();
904
+ const response = await this.ai.textCompletion(prompt, {
905
+ history,
906
+ responseFormat: {
907
+ type: responseFormat === "json" ? "json_object" : "text"
908
+ }
909
+ });
910
+ return response;
911
+ }
912
+ }
913
+ const PACKAGE_VERSION_INITIALIZED = true;
914
+ export {
915
+ AIError as A,
916
+ ContentFilterError as C,
917
+ ModelNotFoundError as M,
918
+ OpenAIClient as O,
919
+ PACKAGE_VERSION_INITIALIZED as P,
920
+ RateLimitError as R,
921
+ ContextLengthError as a,
922
+ AuthenticationError as b,
923
+ extractTextContent as c,
924
+ AIClient as d,
925
+ extractRetryAfterSeconds as e,
926
+ AIMessage as f,
927
+ AIThread as g,
928
+ AI_PROVIDER_TYPES as h,
929
+ getAI as i,
930
+ getAIAuto as j,
931
+ getAIClient as k,
932
+ getOpenAI as l
933
+ };
934
+ //# sourceMappingURL=index-BT4thAvS.js.map