@happyvertical/ai 0.80.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 (61) hide show
  1. package/dist/chunks/anthropic-DA8sdt12.js +438 -0
  2. package/dist/chunks/anthropic-DA8sdt12.js.map +1 -0
  3. package/dist/chunks/bedrock-DDiYFUw0.js +699 -0
  4. package/dist/chunks/bedrock-DDiYFUw0.js.map +1 -0
  5. package/dist/chunks/bifrost-fM9FpW_w.js +198 -0
  6. package/dist/chunks/bifrost-fM9FpW_w.js.map +1 -0
  7. package/dist/chunks/claude-cli-CnJh5KQT.js +498 -0
  8. package/dist/chunks/claude-cli-CnJh5KQT.js.map +1 -0
  9. package/dist/chunks/gateway-admin-CiKvHst7.js +283 -0
  10. package/dist/chunks/gateway-admin-CiKvHst7.js.map +1 -0
  11. package/dist/chunks/gemini-Bu7Fke7c.js +593 -0
  12. package/dist/chunks/gemini-Bu7Fke7c.js.map +1 -0
  13. package/dist/chunks/huggingface-B07_6wHW.js +295 -0
  14. package/dist/chunks/huggingface-B07_6wHW.js.map +1 -0
  15. package/dist/chunks/litellm-D_Oo9OQ_.js +185 -0
  16. package/dist/chunks/litellm-D_Oo9OQ_.js.map +1 -0
  17. package/dist/chunks/ollama-D4ksOTO8.js +626 -0
  18. package/dist/chunks/ollama-D4ksOTO8.js.map +1 -0
  19. package/dist/chunks/openai--F38QHiJ.js +691 -0
  20. package/dist/chunks/openai--F38QHiJ.js.map +1 -0
  21. package/dist/chunks/qwen-tts-DRRbOrhA.js +333 -0
  22. package/dist/chunks/qwen-tts-DRRbOrhA.js.map +1 -0
  23. package/dist/chunks/rate-limit-BwyPGXFW.js +199 -0
  24. package/dist/chunks/rate-limit-BwyPGXFW.js.map +1 -0
  25. package/dist/chunks/safety-CEnoJS6X.js +260 -0
  26. package/dist/chunks/safety-CEnoJS6X.js.map +1 -0
  27. package/dist/chunks/types-FHs-KbGL.js +117 -0
  28. package/dist/chunks/types-FHs-KbGL.js.map +1 -0
  29. package/dist/chunks/usage-C1Y1Nlg4.js +35 -0
  30. package/dist/chunks/usage-C1Y1Nlg4.js.map +1 -0
  31. package/dist/cli/claude-context.js +17 -17
  32. package/dist/cli/claude-context.js.map +1 -1
  33. package/dist/index.js +717 -23
  34. package/dist/index.js.map +1 -1
  35. package/package.json +5 -5
  36. package/dist/chunks/anthropic-2z-82zgr.js +0 -530
  37. package/dist/chunks/anthropic-2z-82zgr.js.map +0 -1
  38. package/dist/chunks/bedrock-Dc2eVPUD.js +0 -920
  39. package/dist/chunks/bedrock-Dc2eVPUD.js.map +0 -1
  40. package/dist/chunks/bifrost-CEnCsciy.js +0 -258
  41. package/dist/chunks/bifrost-CEnCsciy.js.map +0 -1
  42. package/dist/chunks/claude-cli-X1ONjE8K.js +0 -603
  43. package/dist/chunks/claude-cli-X1ONjE8K.js.map +0 -1
  44. package/dist/chunks/gateway-admin-BUhBzXZb.js +0 -359
  45. package/dist/chunks/gateway-admin-BUhBzXZb.js.map +0 -1
  46. package/dist/chunks/gemini-CS56gY0D.js +0 -768
  47. package/dist/chunks/gemini-CS56gY0D.js.map +0 -1
  48. package/dist/chunks/huggingface-wQSfO5xA.js +0 -411
  49. package/dist/chunks/huggingface-wQSfO5xA.js.map +0 -1
  50. package/dist/chunks/index-DCXO0nZA.js +0 -1293
  51. package/dist/chunks/index-DCXO0nZA.js.map +0 -1
  52. package/dist/chunks/litellm-BMFTYbWc.js +0 -245
  53. package/dist/chunks/litellm-BMFTYbWc.js.map +0 -1
  54. package/dist/chunks/ollama-DsGDrA-c.js +0 -962
  55. package/dist/chunks/ollama-DsGDrA-c.js.map +0 -1
  56. package/dist/chunks/openai-CJEo69jb.js +0 -882
  57. package/dist/chunks/openai-CJEo69jb.js.map +0 -1
  58. package/dist/chunks/qwen-tts-BLYZ6d9s.js +0 -365
  59. package/dist/chunks/qwen-tts-BLYZ6d9s.js.map +0 -1
  60. package/dist/chunks/usage-DMWiJ2oB.js +0 -21
  61. package/dist/chunks/usage-DMWiJ2oB.js.map +0 -1
@@ -0,0 +1,199 @@
1
+ import { s as RateLimitError } from "./types-FHs-KbGL.js";
2
+ //#region src/shared/rate-limit.ts
3
+ var RATE_LIMITED_METHODS = /* @__PURE__ */ new Set([
4
+ "chat",
5
+ "complete",
6
+ "message",
7
+ "embed",
8
+ "embedImage",
9
+ "describeImage",
10
+ "generateImage",
11
+ "getModels",
12
+ "synthesizeSpeech",
13
+ "cloneVoice",
14
+ "designVoice",
15
+ "getVoices"
16
+ ]);
17
+ var MAX_BUDGET_COORDINATORS = 128;
18
+ var BUDGET_COORDINATOR_TTL_MS = 900 * 1e3;
19
+ var BudgetCoordinator = class {
20
+ nextAvailableAt = 0;
21
+ pendingSchedules = 0;
22
+ tail = Promise.resolve();
23
+ lastUsedAt = Date.now();
24
+ touch() {
25
+ this.lastUsedAt = Date.now();
26
+ }
27
+ schedule(work) {
28
+ this.pendingSchedules += 1;
29
+ this.touch();
30
+ const run = this.tail.then(work, work);
31
+ this.tail = run.then(() => void 0, () => void 0);
32
+ return run.finally(() => {
33
+ this.pendingSchedules = Math.max(0, this.pendingSchedules - 1);
34
+ this.touch();
35
+ });
36
+ }
37
+ async waitUntilReady() {
38
+ this.touch();
39
+ const delayMs = this.nextAvailableAt - Date.now();
40
+ if (delayMs > 0) await sleep(delayMs);
41
+ }
42
+ delayFor(delayMs) {
43
+ this.touch();
44
+ if (delayMs <= 0) return;
45
+ this.nextAvailableAt = Math.max(this.nextAvailableAt, Date.now() + delayMs);
46
+ }
47
+ isEvictable(now) {
48
+ return this.pendingSchedules === 0 && now >= this.nextAvailableAt;
49
+ }
50
+ isExpired(now) {
51
+ return this.isEvictable(now) && now - this.lastUsedAt >= BUDGET_COORDINATOR_TTL_MS;
52
+ }
53
+ getLastUsedAt() {
54
+ return this.lastUsedAt;
55
+ }
56
+ };
57
+ var budgetCoordinators = /* @__PURE__ */ new Map();
58
+ function sleep(delayMs) {
59
+ return new Promise((resolve) => {
60
+ setTimeout(resolve, delayMs);
61
+ });
62
+ }
63
+ function pruneBudgetCoordinators() {
64
+ const now = Date.now();
65
+ for (const [key, coordinator] of budgetCoordinators.entries()) if (coordinator.isExpired(now)) budgetCoordinators.delete(key);
66
+ while (budgetCoordinators.size > MAX_BUDGET_COORDINATORS) {
67
+ const evictableEntries = [...budgetCoordinators.entries()].filter(([, coordinator]) => coordinator.isEvictable(now)).sort(([, leftCoordinator], [, rightCoordinator]) => leftCoordinator.getLastUsedAt() - rightCoordinator.getLastUsedAt());
68
+ if (evictableEntries.length === 0) break;
69
+ budgetCoordinators.delete(evictableEntries[0][0]);
70
+ }
71
+ }
72
+ function getBudgetCoordinator(key) {
73
+ pruneBudgetCoordinators();
74
+ let coordinator = budgetCoordinators.get(key);
75
+ if (!coordinator) {
76
+ coordinator = new BudgetCoordinator();
77
+ budgetCoordinators.set(key, coordinator);
78
+ pruneBudgetCoordinators();
79
+ }
80
+ coordinator.touch();
81
+ return coordinator;
82
+ }
83
+ function hasPacingConfig(rateLimit) {
84
+ if (!rateLimit || rateLimit.enabled === false) return false;
85
+ return rateLimit.enabled === true || rateLimit.key !== void 0 || rateLimit.cooldownMs !== void 0 || rateLimit.initialDelayMs !== void 0 || rateLimit.maxAttempts !== void 0;
86
+ }
87
+ function normalizeNonNegativeInteger(value, fallback) {
88
+ if (typeof value !== "number" || !Number.isFinite(value)) return fallback;
89
+ return Math.max(0, Math.trunc(value));
90
+ }
91
+ function hashKey(value) {
92
+ let hash = 2166136261;
93
+ for (let index = 0; index < value.length; index += 1) {
94
+ hash ^= value.charCodeAt(index);
95
+ hash = Math.imul(hash, 16777619);
96
+ }
97
+ return (hash >>> 0).toString(36);
98
+ }
99
+ function deriveBudgetKey(options) {
100
+ const type = options.type;
101
+ const provider = typeof type === "string" && type ? type : "openai";
102
+ const seed = [
103
+ "apiKey" in options ? options.apiKey : void 0,
104
+ "apiToken" in options ? options.apiToken : void 0,
105
+ "credentials" in options ? options.credentials?.accessKeyId : void 0,
106
+ "endpoint" in options ? options.endpoint : void 0,
107
+ "baseUrl" in options ? options.baseUrl : void 0,
108
+ "cliPath" in options ? options.cliPath : void 0
109
+ ].find((value) => typeof value === "string" && value.length > 0);
110
+ return seed ? `${provider}:${hashKey(seed)}` : `${provider}:default`;
111
+ }
112
+ function normalizeRateLimitConfig(options) {
113
+ const rateLimit = "rateLimit" in options ? options.rateLimit : void 0;
114
+ if (!hasPacingConfig(rateLimit)) return null;
115
+ return {
116
+ cooldownMs: normalizeNonNegativeInteger(rateLimit?.cooldownMs, 0),
117
+ initialDelayMs: normalizeNonNegativeInteger(rateLimit?.initialDelayMs, 5e3),
118
+ key: rateLimit?.key?.trim() || deriveBudgetKey(options),
119
+ maxAttempts: Math.max(1, normalizeNonNegativeInteger(rateLimit?.maxAttempts, 1))
120
+ };
121
+ }
122
+ function getRetryDelayMs(error, config) {
123
+ const hintedDelayMs = typeof error.retryAfter === "number" && Number.isFinite(error.retryAfter) ? Math.max(0, Math.ceil(error.retryAfter * 1e3)) : void 0;
124
+ if (hintedDelayMs !== void 0) return Math.max(config.cooldownMs, hintedDelayMs);
125
+ return Math.max(config.cooldownMs, config.initialDelayMs);
126
+ }
127
+ async function invokeWithPacing(execute, coordinator, config) {
128
+ let attempt = 1;
129
+ while (true) {
130
+ await coordinator.waitUntilReady();
131
+ try {
132
+ const result = await execute();
133
+ coordinator.delayFor(config.cooldownMs);
134
+ return result;
135
+ } catch (error) {
136
+ if (error instanceof RateLimitError) {
137
+ coordinator.delayFor(getRetryDelayMs(error, config));
138
+ if (error.retryable && attempt < config.maxAttempts) {
139
+ attempt += 1;
140
+ continue;
141
+ }
142
+ }
143
+ throw error;
144
+ }
145
+ }
146
+ }
147
+ function parseRetryAfterSeconds(retryAfter) {
148
+ if (typeof retryAfter === "number" && Number.isFinite(retryAfter)) return Math.max(0, retryAfter);
149
+ if (typeof retryAfter !== "string") return;
150
+ const trimmed = retryAfter.trim();
151
+ if (!trimmed) return;
152
+ const seconds = Number.parseFloat(trimmed);
153
+ if (!Number.isNaN(seconds)) return Math.max(0, seconds);
154
+ const retryAt = Date.parse(trimmed);
155
+ if (Number.isNaN(retryAt)) return;
156
+ return Math.max(0, Math.ceil((retryAt - Date.now()) / 1e3));
157
+ }
158
+ function getHeaderValue(headers, headerName) {
159
+ if (!headers || typeof headers !== "object") return;
160
+ if ("get" in headers && typeof headers.get === "function") return headers.get(headerName) ?? headers.get(headerName.toLowerCase());
161
+ const objectHeaders = headers;
162
+ return objectHeaders[headerName] ?? objectHeaders[headerName.toLowerCase()];
163
+ }
164
+ function extractRetryAfterSeconds(error) {
165
+ if (!error || typeof error !== "object") return;
166
+ if ("retryAfter" in error) {
167
+ const retryAfter = parseRetryAfterSeconds(error.retryAfter);
168
+ if (retryAfter !== void 0) return retryAfter;
169
+ }
170
+ if ("headers" in error) {
171
+ const retryAfter = parseRetryAfterSeconds(getHeaderValue(error.headers, "retry-after"));
172
+ if (retryAfter !== void 0) return retryAfter;
173
+ }
174
+ const message = "message" in error && typeof error.message === "string" ? error.message : "";
175
+ const retryAfterMatch = message.match(/retry after\s+(\d+(?:\.\d+)?)\s*s?/i) ?? message.match(/retryDelay[^\d]*(\d+(?:\.\d+)?)s/i);
176
+ return retryAfterMatch ? parseRetryAfterSeconds(retryAfterMatch[1]) : void 0;
177
+ }
178
+ function createRateLimitedAI(client, options) {
179
+ const config = normalizeRateLimitConfig(options);
180
+ if (!config) return client;
181
+ const wrappedMethods = /* @__PURE__ */ new Map();
182
+ return new Proxy(client, { get(target, property, receiver) {
183
+ const value = Reflect.get(target, property, receiver);
184
+ if (typeof value !== "function") return value;
185
+ if (!RATE_LIMITED_METHODS.has(property)) {
186
+ if (!wrappedMethods.has(property)) wrappedMethods.set(property, value.bind(target));
187
+ return wrappedMethods.get(property);
188
+ }
189
+ if (!wrappedMethods.has(property)) wrappedMethods.set(property, (...args) => {
190
+ const coordinator = getBudgetCoordinator(config.key);
191
+ return coordinator.schedule(() => invokeWithPacing(() => Reflect.apply(value, target, args), coordinator, config));
192
+ });
193
+ return wrappedMethods.get(property);
194
+ } });
195
+ }
196
+ //#endregion
197
+ export { extractRetryAfterSeconds as n, createRateLimitedAI as t };
198
+
199
+ //# sourceMappingURL=rate-limit-BwyPGXFW.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rate-limit-BwyPGXFW.js","names":[],"sources":["../../src/shared/rate-limit.ts"],"sourcesContent":["import type { AIClientOptions } from './client';\nimport type { AIInterface, AIRateLimitOptions, GetAIOptions } from './types';\nimport { AIError, RateLimitError } from './types';\n\nconst RATE_LIMITED_METHODS = new Set<keyof AIInterface>([\n 'chat',\n 'complete',\n 'message',\n 'embed',\n 'embedImage',\n 'describeImage',\n 'generateImage',\n 'getModels',\n 'synthesizeSpeech',\n 'cloneVoice',\n 'designVoice',\n 'getVoices',\n]);\n\nconst MAX_BUDGET_COORDINATORS = 128;\nconst BUDGET_COORDINATOR_TTL_MS = 15 * 60 * 1000;\n\ninterface NormalizedRateLimitConfig {\n cooldownMs: number;\n initialDelayMs: number;\n key: string;\n maxAttempts: number;\n}\n\nclass BudgetCoordinator {\n private nextAvailableAt = 0;\n private pendingSchedules = 0;\n private tail: Promise<void> = Promise.resolve();\n private lastUsedAt = Date.now();\n\n touch(): void {\n this.lastUsedAt = Date.now();\n }\n\n schedule<T>(work: () => Promise<T>): Promise<T> {\n this.pendingSchedules += 1;\n this.touch();\n\n const run = this.tail.then(work, work);\n this.tail = run.then(\n () => undefined,\n () => undefined,\n );\n\n return run.finally(() => {\n this.pendingSchedules = Math.max(0, this.pendingSchedules - 1);\n this.touch();\n });\n }\n\n async waitUntilReady(): Promise<void> {\n this.touch();\n const delayMs = this.nextAvailableAt - Date.now();\n if (delayMs > 0) {\n await sleep(delayMs);\n }\n }\n\n delayFor(delayMs: number): void {\n this.touch();\n if (delayMs <= 0) {\n return;\n }\n\n this.nextAvailableAt = Math.max(this.nextAvailableAt, Date.now() + delayMs);\n }\n\n isEvictable(now: number): boolean {\n return this.pendingSchedules === 0 && now >= this.nextAvailableAt;\n }\n\n isExpired(now: number): boolean {\n return (\n this.isEvictable(now) &&\n now - this.lastUsedAt >= BUDGET_COORDINATOR_TTL_MS\n );\n }\n\n getLastUsedAt(): number {\n return this.lastUsedAt;\n }\n}\n\nconst budgetCoordinators = new Map<string, BudgetCoordinator>();\n\nfunction sleep(delayMs: number): Promise<void> {\n return new Promise((resolve) => {\n setTimeout(resolve, delayMs);\n });\n}\n\nfunction pruneBudgetCoordinators(): void {\n const now = Date.now();\n\n for (const [key, coordinator] of budgetCoordinators.entries()) {\n if (coordinator.isExpired(now)) {\n budgetCoordinators.delete(key);\n }\n }\n\n while (budgetCoordinators.size > MAX_BUDGET_COORDINATORS) {\n const evictableEntries = [...budgetCoordinators.entries()]\n .filter(([, coordinator]) => coordinator.isEvictable(now))\n .sort(\n ([, leftCoordinator], [, rightCoordinator]) =>\n leftCoordinator.getLastUsedAt() - rightCoordinator.getLastUsedAt(),\n );\n\n if (evictableEntries.length === 0) {\n break;\n }\n\n budgetCoordinators.delete(evictableEntries[0][0]);\n }\n}\n\nfunction getBudgetCoordinator(key: string): BudgetCoordinator {\n pruneBudgetCoordinators();\n\n let coordinator = budgetCoordinators.get(key);\n if (!coordinator) {\n coordinator = new BudgetCoordinator();\n budgetCoordinators.set(key, coordinator);\n pruneBudgetCoordinators();\n }\n\n coordinator.touch();\n return coordinator;\n}\n\nfunction hasPacingConfig(rateLimit?: AIRateLimitOptions): boolean {\n if (!rateLimit || rateLimit.enabled === false) {\n return false;\n }\n\n return (\n rateLimit.enabled === true ||\n rateLimit.key !== undefined ||\n rateLimit.cooldownMs !== undefined ||\n rateLimit.initialDelayMs !== undefined ||\n rateLimit.maxAttempts !== undefined\n );\n}\n\nfunction normalizeNonNegativeInteger(\n value: number | undefined,\n fallback: number,\n): number {\n if (typeof value !== 'number' || !Number.isFinite(value)) {\n return fallback;\n }\n\n return Math.max(0, Math.trunc(value));\n}\n\nfunction hashKey(value: string): string {\n let hash = 2166136261;\n\n for (let index = 0; index < value.length; index += 1) {\n hash ^= value.charCodeAt(index);\n hash = Math.imul(hash, 16777619);\n }\n\n return (hash >>> 0).toString(36);\n}\n\nfunction deriveBudgetKey(options: GetAIOptions | AIClientOptions): string {\n const type = (options as { type?: string }).type;\n const provider = typeof type === 'string' && type ? type : 'openai';\n\n const credentialLikeValues = [\n 'apiKey' in options ? options.apiKey : undefined,\n 'apiToken' in options ? options.apiToken : undefined,\n 'credentials' in options ? options.credentials?.accessKeyId : undefined,\n 'endpoint' in options ? options.endpoint : undefined,\n 'baseUrl' in options ? options.baseUrl : undefined,\n 'cliPath' in options ? options.cliPath : undefined,\n ];\n\n const seed = credentialLikeValues.find(\n (value): value is string => typeof value === 'string' && value.length > 0,\n );\n\n return seed ? `${provider}:${hashKey(seed)}` : `${provider}:default`;\n}\n\nfunction normalizeRateLimitConfig(\n options: GetAIOptions | AIClientOptions,\n): NormalizedRateLimitConfig | null {\n const rateLimit = 'rateLimit' in options ? options.rateLimit : undefined;\n\n if (!hasPacingConfig(rateLimit)) {\n return null;\n }\n\n return {\n cooldownMs: normalizeNonNegativeInteger(rateLimit?.cooldownMs, 0),\n initialDelayMs: normalizeNonNegativeInteger(\n rateLimit?.initialDelayMs,\n 5000,\n ),\n key: rateLimit?.key?.trim() || deriveBudgetKey(options),\n maxAttempts: Math.max(\n 1,\n normalizeNonNegativeInteger(rateLimit?.maxAttempts, 1),\n ),\n };\n}\n\nfunction getRetryDelayMs(\n error: RateLimitError,\n config: NormalizedRateLimitConfig,\n): number {\n const hintedDelayMs =\n typeof error.retryAfter === 'number' && Number.isFinite(error.retryAfter)\n ? Math.max(0, Math.ceil(error.retryAfter * 1000))\n : undefined;\n\n if (hintedDelayMs !== undefined) {\n return Math.max(config.cooldownMs, hintedDelayMs);\n }\n\n return Math.max(config.cooldownMs, config.initialDelayMs);\n}\n\nasync function invokeWithPacing<T>(\n execute: () => Promise<T>,\n coordinator: BudgetCoordinator,\n config: NormalizedRateLimitConfig,\n): Promise<T> {\n let attempt = 1;\n\n while (true) {\n await coordinator.waitUntilReady();\n\n try {\n const result = await execute();\n coordinator.delayFor(config.cooldownMs);\n return result;\n } catch (error) {\n if (error instanceof RateLimitError) {\n coordinator.delayFor(getRetryDelayMs(error, config));\n\n if (error.retryable && attempt < config.maxAttempts) {\n attempt += 1;\n continue;\n }\n }\n\n throw error;\n }\n }\n}\n\nexport function parseRetryAfterSeconds(\n retryAfter: number | string | null | undefined,\n): number | undefined {\n if (typeof retryAfter === 'number' && Number.isFinite(retryAfter)) {\n return Math.max(0, retryAfter);\n }\n\n if (typeof retryAfter !== 'string') {\n return undefined;\n }\n\n const trimmed = retryAfter.trim();\n if (!trimmed) {\n return undefined;\n }\n\n const seconds = Number.parseFloat(trimmed);\n if (!Number.isNaN(seconds)) {\n return Math.max(0, seconds);\n }\n\n const retryAt = Date.parse(trimmed);\n if (Number.isNaN(retryAt)) {\n return undefined;\n }\n\n return Math.max(0, Math.ceil((retryAt - Date.now()) / 1000));\n}\n\nfunction getHeaderValue(\n headers: unknown,\n headerName: string,\n): number | string | undefined {\n if (!headers || typeof headers !== 'object') {\n return undefined;\n }\n\n if ('get' in headers && typeof headers.get === 'function') {\n return headers.get(headerName) ?? headers.get(headerName.toLowerCase());\n }\n\n const objectHeaders = headers as Record<string, number | string | undefined>;\n return objectHeaders[headerName] ?? objectHeaders[headerName.toLowerCase()];\n}\n\nexport function extractRetryAfterSeconds(error: unknown): number | undefined {\n if (!error || typeof error !== 'object') {\n return undefined;\n }\n\n if ('retryAfter' in error) {\n const retryAfter = parseRetryAfterSeconds(\n (error as { retryAfter?: number | string | null }).retryAfter,\n );\n if (retryAfter !== undefined) {\n return retryAfter;\n }\n }\n\n if ('headers' in error) {\n const retryAfter = parseRetryAfterSeconds(\n getHeaderValue((error as { headers?: unknown }).headers, 'retry-after'),\n );\n if (retryAfter !== undefined) {\n return retryAfter;\n }\n }\n\n const message =\n 'message' in error && typeof error.message === 'string'\n ? error.message\n : '';\n\n const retryAfterMatch =\n message.match(/retry after\\s+(\\d+(?:\\.\\d+)?)\\s*s?/i) ??\n message.match(/retryDelay[^\\d]*(\\d+(?:\\.\\d+)?)s/i);\n\n return retryAfterMatch\n ? parseRetryAfterSeconds(retryAfterMatch[1])\n : undefined;\n}\n\nexport function createRateLimitedAI<T extends AIInterface>(\n client: T,\n options: GetAIOptions | AIClientOptions,\n): T {\n const config = normalizeRateLimitConfig(options);\n if (!config) {\n return client;\n }\n\n const wrappedMethods = new Map<PropertyKey, unknown>();\n\n return new Proxy(client, {\n get(target, property, receiver) {\n const value = Reflect.get(target, property, receiver);\n\n if (typeof value !== 'function') {\n return value;\n }\n\n if (!RATE_LIMITED_METHODS.has(property as keyof AIInterface)) {\n if (!wrappedMethods.has(property)) {\n wrappedMethods.set(property, value.bind(target));\n }\n\n return wrappedMethods.get(property);\n }\n\n if (!wrappedMethods.has(property)) {\n wrappedMethods.set(property, (...args: unknown[]) => {\n const coordinator = getBudgetCoordinator(config.key);\n return coordinator.schedule(() =>\n invokeWithPacing(\n () => Reflect.apply(value, target, args) as Promise<unknown>,\n coordinator,\n config,\n ),\n );\n });\n }\n\n return wrappedMethods.get(property);\n },\n }) as T;\n}\n\nexport function __resetAIRateLimitStateForTests(): void {\n budgetCoordinators.clear();\n}\n\nexport function __getAIRateLimitStateForTests(): {\n count: number;\n maxBudgetCoordinators: number;\n ttlMs: number;\n} {\n return {\n count: budgetCoordinators.size,\n maxBudgetCoordinators: MAX_BUDGET_COORDINATORS,\n ttlMs: BUDGET_COORDINATOR_TTL_MS,\n };\n}\n\nexport function isRetryableAIError(error: unknown): error is AIError {\n return error instanceof AIError && error.retryable;\n}\n"],"mappings":"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"}
@@ -0,0 +1,260 @@
1
+ import { t as AIError } from "./types-FHs-KbGL.js";
2
+ //#region src/shared/safety.ts
3
+ var DEFAULT_AI_TIMEOUT_MS = 12e4;
4
+ var DEFAULT_AI_MAX_RETRIES = 0;
5
+ var DEFAULT_AI_GENERATION_LIMITS = Object.freeze({
6
+ maxOutputTokens: 4096,
7
+ maxReasoningTokens: 1024,
8
+ maxImagesPerRequest: 1,
9
+ onExceeded: "error"
10
+ });
11
+ function positiveInteger(value, fallback, field) {
12
+ if (value === void 0) return fallback;
13
+ if (!Number.isFinite(value) || value <= 0) throw new AIError(`${field} must be a positive finite number`, "AI_LIMIT_INVALID");
14
+ return Math.trunc(value);
15
+ }
16
+ function normalizeBaseAIOptions(options) {
17
+ const configuredLimits = options.generationLimits || {};
18
+ const generationLimits = {
19
+ maxOutputTokens: positiveInteger(configuredLimits.maxOutputTokens, DEFAULT_AI_GENERATION_LIMITS.maxOutputTokens, "generationLimits.maxOutputTokens"),
20
+ maxReasoningTokens: positiveInteger(configuredLimits.maxReasoningTokens, DEFAULT_AI_GENERATION_LIMITS.maxReasoningTokens, "generationLimits.maxReasoningTokens"),
21
+ maxImagesPerRequest: positiveInteger(configuredLimits.maxImagesPerRequest, DEFAULT_AI_GENERATION_LIMITS.maxImagesPerRequest, "generationLimits.maxImagesPerRequest"),
22
+ onExceeded: configuredLimits.onExceeded || DEFAULT_AI_GENERATION_LIMITS.onExceeded
23
+ };
24
+ if (!["error", "clamp"].includes(generationLimits.onExceeded)) throw new AIError("generationLimits.onExceeded must be \"error\" or \"clamp\"", "AI_LIMIT_INVALID");
25
+ return {
26
+ ...options,
27
+ timeout: positiveInteger(options.timeout, DEFAULT_AI_TIMEOUT_MS, "timeout"),
28
+ maxRetries: (() => {
29
+ if (options.maxRetries === void 0) return 0;
30
+ if (!Number.isFinite(options.maxRetries) || options.maxRetries < 0) throw new AIError("maxRetries must be a non-negative finite number", "AI_LIMIT_INVALID");
31
+ return Math.trunc(options.maxRetries);
32
+ })(),
33
+ generationLimits
34
+ };
35
+ }
36
+ function reasoningTokensForEffort(effort) {
37
+ switch (effort) {
38
+ case false:
39
+ case "none": return 0;
40
+ case "minimal": return 1024;
41
+ case "low": return 2048;
42
+ case "medium": return 4096;
43
+ case "high": return 8192;
44
+ default: return;
45
+ }
46
+ }
47
+ function enforceLimit(requested, limit, behavior, label, provider, model) {
48
+ if (!Number.isFinite(requested) || requested < 0) throw new AIError(`${label} must be a non-negative finite number`, "AI_LIMIT_INVALID", provider, model);
49
+ const normalized = Math.trunc(requested);
50
+ if (normalized <= limit) return normalized;
51
+ if (behavior === "clamp") return limit;
52
+ throw new AIError(`${label} ${normalized} exceeds configured ceiling ${limit}`, "AI_LIMIT_EXCEEDED", provider, model);
53
+ }
54
+ function normalizeChatOptions(providerOptions, options, provider, model) {
55
+ const limits = normalizeBaseAIOptions(providerOptions).generationLimits;
56
+ const requestedOutput = options.maxTokens ?? limits.maxOutputTokens;
57
+ const legacyEffort = options.thinkingLevel ?? providerOptions.thinkingLevel;
58
+ const requestedReasoning = options.reasoning !== void 0 || legacyEffort !== void 0 || options.includeThoughts === true ? options.reasoning?.maxTokens ?? reasoningTokensForEffort(options.reasoning?.effort ?? legacyEffort) ?? limits.maxReasoningTokens : 0;
59
+ return {
60
+ ...options,
61
+ maxTokens: enforceLimit(requestedOutput, limits.maxOutputTokens, limits.onExceeded, "maxTokens", provider, model),
62
+ reasoning: {
63
+ ...options.reasoning,
64
+ maxTokens: enforceLimit(requestedReasoning, limits.maxReasoningTokens, limits.onExceeded, "reasoning.maxTokens", provider, model),
65
+ includeThoughts: options.reasoning?.includeThoughts ?? options.includeThoughts
66
+ }
67
+ };
68
+ }
69
+ function normalizeImageGenerationOptions(providerOptions, options, provider, model) {
70
+ const limits = normalizeBaseAIOptions(providerOptions).generationLimits;
71
+ return {
72
+ ...options,
73
+ n: enforceLimit(options.n ?? 1, limits.maxImagesPerRequest, limits.onExceeded, "n", provider, model)
74
+ };
75
+ }
76
+ function prepareRequestControls(providerOptions, requestOptions = {}) {
77
+ const normalized = normalizeBaseAIOptions(providerOptions);
78
+ const timeout = positiveInteger(requestOptions.timeout, normalized.timeout, "timeout");
79
+ const controller = new AbortController();
80
+ let timedOut = false;
81
+ const abortFromCaller = () => controller.abort(requestOptions.signal?.reason);
82
+ if (requestOptions.signal?.aborted) abortFromCaller();
83
+ else requestOptions.signal?.addEventListener("abort", abortFromCaller, { once: true });
84
+ const timer = setTimeout(() => {
85
+ timedOut = true;
86
+ controller.abort(/* @__PURE__ */ new Error(`AI request timed out after ${timeout}ms`));
87
+ }, timeout);
88
+ return {
89
+ signal: controller.signal,
90
+ timeout,
91
+ didTimeout: () => timedOut,
92
+ cleanup: () => {
93
+ clearTimeout(timer);
94
+ requestOptions.signal?.removeEventListener("abort", abortFromCaller);
95
+ }
96
+ };
97
+ }
98
+ function mergeTags(providerOptions, callTags) {
99
+ return providerOptions.usageTags || callTags ? {
100
+ ...providerOptions.usageTags,
101
+ ...callTags
102
+ } : void 0;
103
+ }
104
+ function emitRequestEvent(providerOptions, event) {
105
+ if (!providerOptions.onRequest) return;
106
+ try {
107
+ providerOptions.onRequest({
108
+ ...event,
109
+ tags: mergeTags(providerOptions, event.tags)
110
+ });
111
+ } catch {}
112
+ }
113
+ function classifyRequestFailure(error, didTimeout = false) {
114
+ const code = error instanceof AIError ? error.code : void 0;
115
+ if (code === "AI_LIMIT_EXCEEDED" || code === "AI_LIMIT_INVALID") return {
116
+ status: "rejected",
117
+ errorCode: code
118
+ };
119
+ if (didTimeout || code === "AI_TIMEOUT" || code === "TIMEOUT") return {
120
+ status: "timed_out",
121
+ errorCode: code || "AI_TIMEOUT"
122
+ };
123
+ if (error instanceof Error && error.name === "AbortError") return {
124
+ status: "aborted",
125
+ errorCode: "ABORTED"
126
+ };
127
+ if (code === "AI_ABORTED") return {
128
+ status: "aborted",
129
+ errorCode: code
130
+ };
131
+ return {
132
+ status: "failed",
133
+ errorCode: code || (error instanceof Error ? error.name : void 0)
134
+ };
135
+ }
136
+ function requestEventBase(options) {
137
+ const normalized = normalizeBaseAIOptions(options.providerOptions);
138
+ return {
139
+ provider: options.provider,
140
+ model: options.model,
141
+ operation: options.operation,
142
+ attempts: normalized.maxRetries + 1,
143
+ requestedMaxOutputTokens: options.callOptions?.maxTokens,
144
+ effectiveMaxOutputTokens: options.operation === "generateImage" ? void 0 : options.effectiveMaxOutputTokens ?? normalized.generationLimits.maxOutputTokens,
145
+ tags: options.callOptions?.usageTags
146
+ };
147
+ }
148
+ var OBSERVED_OPERATIONS = /* @__PURE__ */ new Set([
149
+ "chat",
150
+ "complete",
151
+ "message",
152
+ "describeImage",
153
+ "generateImage",
154
+ "stream"
155
+ ]);
156
+ function callOptionsFor(operation, args) {
157
+ const value = args[operation === "describeImage" ? 2 : 1];
158
+ return value && typeof value === "object" ? value : void 0;
159
+ }
160
+ function providerName(options) {
161
+ return options.type || "openai";
162
+ }
163
+ function effectiveOutputTokens(operation, providerOptions, callOptions) {
164
+ if (operation === "generateImage") {
165
+ normalizeImageGenerationOptions(providerOptions, callOptions || {}, providerName(providerOptions), callOptions?.model);
166
+ return;
167
+ }
168
+ return normalizeChatOptions(providerOptions, callOptions || {}, providerName(providerOptions), callOptions?.model || providerOptions.defaultModel).maxTokens;
169
+ }
170
+ /**
171
+ * Wrap a public provider instance with prompt-free lifecycle observation.
172
+ * Provider adapters still enforce limits internally so direct construction is
173
+ * protected; this wrapper adds one terminal event to the public getAI() path.
174
+ */
175
+ function createObservedAI(client, providerOptions) {
176
+ if (!providerOptions.onRequest) return client;
177
+ return new Proxy(client, { get(target, property, receiver) {
178
+ const value = Reflect.get(target, property, receiver);
179
+ if (typeof property !== "string" || typeof value !== "function" || !OBSERVED_OPERATIONS.has(property)) return typeof value === "function" ? value.bind(target) : value;
180
+ const operation = property;
181
+ if (operation === "stream") return async function* (...args) {
182
+ const startedAt = Date.now();
183
+ const callOptions = callOptionsFor(operation, args);
184
+ let base;
185
+ try {
186
+ base = requestEventBase({
187
+ providerOptions,
188
+ provider: providerName(providerOptions),
189
+ model: callOptions?.model || providerOptions.defaultModel || "unknown",
190
+ operation,
191
+ callOptions,
192
+ effectiveMaxOutputTokens: effectiveOutputTokens(operation, providerOptions, callOptions)
193
+ });
194
+ const iterable = Reflect.apply(value, target, args);
195
+ for await (const chunk of iterable) yield chunk;
196
+ emitRequestEvent(providerOptions, {
197
+ ...base,
198
+ status: "succeeded",
199
+ duration: Date.now() - startedAt
200
+ });
201
+ } catch (error) {
202
+ const failure = classifyRequestFailure(error);
203
+ base ||= requestEventBase({
204
+ providerOptions,
205
+ provider: providerName(providerOptions),
206
+ model: callOptions?.model || providerOptions.defaultModel || "unknown",
207
+ operation,
208
+ callOptions
209
+ });
210
+ emitRequestEvent(providerOptions, {
211
+ ...base,
212
+ ...failure,
213
+ duration: Date.now() - startedAt
214
+ });
215
+ throw error;
216
+ }
217
+ };
218
+ return async (...args) => {
219
+ const startedAt = Date.now();
220
+ const callOptions = callOptionsFor(operation, args);
221
+ let base;
222
+ try {
223
+ base = requestEventBase({
224
+ providerOptions,
225
+ provider: providerName(providerOptions),
226
+ model: callOptions?.model || providerOptions.defaultModel || "unknown",
227
+ operation,
228
+ callOptions,
229
+ effectiveMaxOutputTokens: effectiveOutputTokens(operation, providerOptions, callOptions)
230
+ });
231
+ const result = await Reflect.apply(value, target, args);
232
+ emitRequestEvent(providerOptions, {
233
+ ...base,
234
+ status: "succeeded",
235
+ duration: Date.now() - startedAt
236
+ });
237
+ return result;
238
+ } catch (error) {
239
+ const failure = classifyRequestFailure(error);
240
+ base ||= requestEventBase({
241
+ providerOptions,
242
+ provider: providerName(providerOptions),
243
+ model: callOptions?.model || providerOptions.defaultModel || "unknown",
244
+ operation,
245
+ callOptions
246
+ });
247
+ emitRequestEvent(providerOptions, {
248
+ ...base,
249
+ ...failure,
250
+ duration: Date.now() - startedAt
251
+ });
252
+ throw error;
253
+ }
254
+ };
255
+ } });
256
+ }
257
+ //#endregion
258
+ export { normalizeBaseAIOptions as a, prepareRequestControls as c, createObservedAI as i, DEFAULT_AI_MAX_RETRIES as n, normalizeChatOptions as o, DEFAULT_AI_TIMEOUT_MS as r, normalizeImageGenerationOptions as s, DEFAULT_AI_GENERATION_LIMITS as t };
259
+
260
+ //# sourceMappingURL=safety-CEnoJS6X.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"safety-CEnoJS6X.js","names":[],"sources":["../../src/shared/safety.ts"],"sourcesContent":["import type {\n AIGenerationLimits,\n AIInterface,\n AIReasoningOptions,\n AIRequestControls,\n AIRequestEvent,\n AIRequestOperation,\n AIRequestStatus,\n BaseAIOptions,\n ChatOptions,\n ImageGenerationOptions,\n} from './types';\nimport { AIError } from './types';\n\nexport const DEFAULT_AI_TIMEOUT_MS = 120_000;\nexport const DEFAULT_AI_MAX_RETRIES = 0;\n\nexport const DEFAULT_AI_GENERATION_LIMITS: Readonly<AIGenerationLimits> =\n Object.freeze({\n maxOutputTokens: 4096,\n maxReasoningTokens: 1024,\n maxImagesPerRequest: 1,\n onExceeded: 'error',\n });\n\nexport interface NormalizedBaseAIOptions extends BaseAIOptions {\n timeout: number;\n maxRetries: number;\n generationLimits: AIGenerationLimits;\n}\n\nexport interface PreparedRequestControls {\n signal: AbortSignal;\n timeout: number;\n cleanup(): void;\n didTimeout(): boolean;\n}\n\nfunction positiveInteger(\n value: number | undefined,\n fallback: number,\n field: string,\n): number {\n if (value === undefined) return fallback;\n if (!Number.isFinite(value) || value <= 0) {\n throw new AIError(\n `${field} must be a positive finite number`,\n 'AI_LIMIT_INVALID',\n );\n }\n return Math.trunc(value);\n}\n\nexport function normalizeBaseAIOptions<T extends BaseAIOptions>(\n options: T,\n): T & NormalizedBaseAIOptions {\n const configuredLimits = options.generationLimits || {};\n const generationLimits: AIGenerationLimits = {\n maxOutputTokens: positiveInteger(\n configuredLimits.maxOutputTokens,\n DEFAULT_AI_GENERATION_LIMITS.maxOutputTokens,\n 'generationLimits.maxOutputTokens',\n ),\n maxReasoningTokens: positiveInteger(\n configuredLimits.maxReasoningTokens,\n DEFAULT_AI_GENERATION_LIMITS.maxReasoningTokens,\n 'generationLimits.maxReasoningTokens',\n ),\n maxImagesPerRequest: positiveInteger(\n configuredLimits.maxImagesPerRequest,\n DEFAULT_AI_GENERATION_LIMITS.maxImagesPerRequest,\n 'generationLimits.maxImagesPerRequest',\n ),\n onExceeded:\n configuredLimits.onExceeded || DEFAULT_AI_GENERATION_LIMITS.onExceeded,\n };\n\n if (!['error', 'clamp'].includes(generationLimits.onExceeded)) {\n throw new AIError(\n 'generationLimits.onExceeded must be \"error\" or \"clamp\"',\n 'AI_LIMIT_INVALID',\n );\n }\n\n return {\n ...options,\n timeout: positiveInteger(options.timeout, DEFAULT_AI_TIMEOUT_MS, 'timeout'),\n maxRetries: (() => {\n if (options.maxRetries === undefined) return DEFAULT_AI_MAX_RETRIES;\n if (!Number.isFinite(options.maxRetries) || options.maxRetries < 0) {\n throw new AIError(\n 'maxRetries must be a non-negative finite number',\n 'AI_LIMIT_INVALID',\n );\n }\n return Math.trunc(options.maxRetries);\n })(),\n generationLimits,\n };\n}\n\nfunction reasoningTokensForEffort(\n effort: AIReasoningOptions['effort'] | false | undefined,\n): number | undefined {\n switch (effort) {\n case false:\n case 'none':\n return 0;\n case 'minimal':\n return 1024;\n case 'low':\n return 2048;\n case 'medium':\n return 4096;\n case 'high':\n return 8192;\n default:\n return undefined;\n }\n}\n\nfunction enforceLimit(\n requested: number,\n limit: number,\n behavior: AIGenerationLimits['onExceeded'],\n label: string,\n provider?: string,\n model?: string,\n): number {\n if (!Number.isFinite(requested) || requested < 0) {\n throw new AIError(\n `${label} must be a non-negative finite number`,\n 'AI_LIMIT_INVALID',\n provider,\n model,\n );\n }\n const normalized = Math.trunc(requested);\n if (normalized <= limit) return normalized;\n if (behavior === 'clamp') return limit;\n throw new AIError(\n `${label} ${normalized} exceeds configured ceiling ${limit}`,\n 'AI_LIMIT_EXCEEDED',\n provider,\n model,\n );\n}\n\nexport function normalizeChatOptions<\n T extends AIRequestControls & {\n maxTokens?: number;\n thinkingLevel?: ChatOptions['thinkingLevel'];\n includeThoughts?: boolean;\n },\n>(\n providerOptions: BaseAIOptions,\n options: T,\n provider?: string,\n model?: string,\n): T & { maxTokens: number; reasoning: AIReasoningOptions } {\n const normalizedProvider = normalizeBaseAIOptions(providerOptions);\n const limits = normalizedProvider.generationLimits;\n const requestedOutput = options.maxTokens ?? limits.maxOutputTokens;\n const legacyEffort =\n options.thinkingLevel ??\n (\n providerOptions as BaseAIOptions & {\n thinkingLevel?: ChatOptions['thinkingLevel'];\n }\n ).thinkingLevel;\n const reasoningWasRequested =\n options.reasoning !== undefined ||\n legacyEffort !== undefined ||\n options.includeThoughts === true;\n const requestedReasoning = reasoningWasRequested\n ? (options.reasoning?.maxTokens ??\n reasoningTokensForEffort(options.reasoning?.effort ?? legacyEffort) ??\n limits.maxReasoningTokens)\n : 0;\n\n return {\n ...options,\n maxTokens: enforceLimit(\n requestedOutput,\n limits.maxOutputTokens,\n limits.onExceeded,\n 'maxTokens',\n provider,\n model,\n ),\n reasoning: {\n ...options.reasoning,\n maxTokens: enforceLimit(\n requestedReasoning,\n limits.maxReasoningTokens,\n limits.onExceeded,\n 'reasoning.maxTokens',\n provider,\n model,\n ),\n includeThoughts:\n options.reasoning?.includeThoughts ?? options.includeThoughts,\n },\n };\n}\n\nexport function normalizeImageGenerationOptions<\n T extends ImageGenerationOptions,\n>(\n providerOptions: BaseAIOptions,\n options: T,\n provider?: string,\n model?: string,\n): T & { n: number } {\n const normalizedProvider = normalizeBaseAIOptions(providerOptions);\n const limits = normalizedProvider.generationLimits;\n return {\n ...options,\n n: enforceLimit(\n options.n ?? 1,\n limits.maxImagesPerRequest,\n limits.onExceeded,\n 'n',\n provider,\n model,\n ),\n };\n}\n\nexport function prepareRequestControls(\n providerOptions: BaseAIOptions,\n requestOptions: AIRequestControls = {},\n): PreparedRequestControls {\n const normalized = normalizeBaseAIOptions(providerOptions);\n const timeout = positiveInteger(\n requestOptions.timeout,\n normalized.timeout,\n 'timeout',\n );\n const controller = new AbortController();\n let timedOut = false;\n\n const abortFromCaller = () => controller.abort(requestOptions.signal?.reason);\n if (requestOptions.signal?.aborted) {\n abortFromCaller();\n } else {\n requestOptions.signal?.addEventListener('abort', abortFromCaller, {\n once: true,\n });\n }\n\n const timer = setTimeout(() => {\n timedOut = true;\n controller.abort(new Error(`AI request timed out after ${timeout}ms`));\n }, timeout);\n\n return {\n signal: controller.signal,\n timeout,\n didTimeout: () => timedOut,\n cleanup: () => {\n clearTimeout(timer);\n requestOptions.signal?.removeEventListener('abort', abortFromCaller);\n },\n };\n}\n\nfunction mergeTags(\n providerOptions: BaseAIOptions,\n callTags?: Record<string, string>,\n): Record<string, string> | undefined {\n return providerOptions.usageTags || callTags\n ? { ...providerOptions.usageTags, ...callTags }\n : undefined;\n}\n\nexport function emitRequestEvent(\n providerOptions: BaseAIOptions,\n event: Omit<AIRequestEvent, 'tags'> & {\n tags?: Record<string, string>;\n },\n): void {\n if (!providerOptions.onRequest) return;\n try {\n providerOptions.onRequest({\n ...event,\n tags: mergeTags(providerOptions, event.tags),\n });\n } catch {\n // Consumer telemetry must never affect an AI request.\n }\n}\n\nexport function classifyRequestFailure(\n error: unknown,\n didTimeout = false,\n): { status: AIRequestStatus; errorCode?: string } {\n const code = error instanceof AIError ? error.code : undefined;\n if (code === 'AI_LIMIT_EXCEEDED' || code === 'AI_LIMIT_INVALID') {\n return { status: 'rejected', errorCode: code };\n }\n if (didTimeout || code === 'AI_TIMEOUT' || code === 'TIMEOUT') {\n return { status: 'timed_out', errorCode: code || 'AI_TIMEOUT' };\n }\n if (error instanceof Error && error.name === 'AbortError') {\n return { status: 'aborted', errorCode: 'ABORTED' };\n }\n if (code === 'AI_ABORTED') {\n return { status: 'aborted', errorCode: code };\n }\n return {\n status: 'failed',\n errorCode: code || (error instanceof Error ? error.name : undefined),\n };\n}\n\nexport function requestEventBase(options: {\n providerOptions: BaseAIOptions;\n provider: string;\n model: string;\n operation: AIRequestOperation;\n callOptions?: { maxTokens?: number; usageTags?: Record<string, string> };\n effectiveMaxOutputTokens?: number;\n}): Omit<AIRequestEvent, 'status' | 'duration'> & {\n tags?: Record<string, string>;\n} {\n const normalized = normalizeBaseAIOptions(options.providerOptions);\n return {\n provider: options.provider,\n model: options.model,\n operation: options.operation,\n attempts: normalized.maxRetries + 1,\n requestedMaxOutputTokens: options.callOptions?.maxTokens,\n effectiveMaxOutputTokens:\n options.operation === 'generateImage'\n ? undefined\n : (options.effectiveMaxOutputTokens ??\n normalized.generationLimits.maxOutputTokens),\n tags: options.callOptions?.usageTags,\n };\n}\n\nconst OBSERVED_OPERATIONS = new Set<AIRequestOperation>([\n 'chat',\n 'complete',\n 'message',\n 'describeImage',\n 'generateImage',\n 'stream',\n]);\n\nfunction callOptionsFor(\n operation: AIRequestOperation,\n args: unknown[],\n):\n | (AIRequestControls & {\n model?: string;\n maxTokens?: number;\n usageTags?: Record<string, string>;\n })\n | undefined {\n const index = operation === 'describeImage' ? 2 : 1;\n const value = args[index];\n return value && typeof value === 'object'\n ? (value as AIRequestControls & {\n model?: string;\n maxTokens?: number;\n usageTags?: Record<string, string>;\n })\n : undefined;\n}\n\nfunction providerName(options: BaseAIOptions): string {\n const configured = (options as BaseAIOptions & { type?: string }).type;\n return configured || 'openai';\n}\n\nfunction effectiveOutputTokens(\n operation: AIRequestOperation,\n providerOptions: BaseAIOptions,\n callOptions:\n | (AIRequestControls & {\n model?: string;\n maxTokens?: number;\n n?: number;\n })\n | undefined,\n): number | undefined {\n if (operation === 'generateImage') {\n normalizeImageGenerationOptions(\n providerOptions,\n callOptions || {},\n providerName(providerOptions),\n callOptions?.model,\n );\n return undefined;\n }\n\n return normalizeChatOptions(\n providerOptions,\n callOptions || {},\n providerName(providerOptions),\n callOptions?.model || providerOptions.defaultModel,\n ).maxTokens;\n}\n\n/**\n * Wrap a public provider instance with prompt-free lifecycle observation.\n * Provider adapters still enforce limits internally so direct construction is\n * protected; this wrapper adds one terminal event to the public getAI() path.\n */\nexport function createObservedAI<T extends AIInterface>(\n client: T,\n providerOptions: BaseAIOptions,\n): T {\n if (!providerOptions.onRequest) return client;\n\n return new Proxy(client, {\n get(target, property, receiver) {\n const value = Reflect.get(target, property, receiver);\n if (\n typeof property !== 'string' ||\n typeof value !== 'function' ||\n !OBSERVED_OPERATIONS.has(property as AIRequestOperation)\n ) {\n return typeof value === 'function' ? value.bind(target) : value;\n }\n\n const operation = property as AIRequestOperation;\n if (operation === 'stream') {\n return async function* (...args: unknown[]) {\n const startedAt = Date.now();\n const callOptions = callOptionsFor(operation, args);\n let base: ReturnType<typeof requestEventBase> | undefined;\n try {\n base = requestEventBase({\n providerOptions,\n provider: providerName(providerOptions),\n model:\n callOptions?.model || providerOptions.defaultModel || 'unknown',\n operation,\n callOptions,\n effectiveMaxOutputTokens: effectiveOutputTokens(\n operation,\n providerOptions,\n callOptions,\n ),\n });\n const iterable = Reflect.apply(\n value,\n target,\n args,\n ) as AsyncIterable<string>;\n for await (const chunk of iterable) yield chunk;\n emitRequestEvent(providerOptions, {\n ...base,\n status: 'succeeded',\n duration: Date.now() - startedAt,\n });\n } catch (error) {\n const failure = classifyRequestFailure(error);\n base ||= requestEventBase({\n providerOptions,\n provider: providerName(providerOptions),\n model:\n callOptions?.model || providerOptions.defaultModel || 'unknown',\n operation,\n callOptions,\n });\n emitRequestEvent(providerOptions, {\n ...base,\n ...failure,\n duration: Date.now() - startedAt,\n });\n throw error;\n }\n };\n }\n\n return async (...args: unknown[]) => {\n const startedAt = Date.now();\n const callOptions = callOptionsFor(operation, args);\n let base: ReturnType<typeof requestEventBase> | undefined;\n try {\n base = requestEventBase({\n providerOptions,\n provider: providerName(providerOptions),\n model:\n callOptions?.model || providerOptions.defaultModel || 'unknown',\n operation,\n callOptions,\n effectiveMaxOutputTokens: effectiveOutputTokens(\n operation,\n providerOptions,\n callOptions,\n ),\n });\n const result = await Reflect.apply(value, target, args);\n emitRequestEvent(providerOptions, {\n ...base,\n status: 'succeeded',\n duration: Date.now() - startedAt,\n });\n return result;\n } catch (error) {\n const failure = classifyRequestFailure(error);\n base ||= requestEventBase({\n providerOptions,\n provider: providerName(providerOptions),\n model:\n callOptions?.model || providerOptions.defaultModel || 'unknown',\n operation,\n callOptions,\n });\n emitRequestEvent(providerOptions, {\n ...base,\n ...failure,\n duration: Date.now() - startedAt,\n });\n throw error;\n }\n };\n },\n });\n}\n"],"mappings":"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"}
@@ -0,0 +1,117 @@
1
+ //#region src/shared/types.ts
2
+ /**
3
+ * Supported AI provider types
4
+ */
5
+ var AI_PROVIDER_TYPES = [
6
+ "openai",
7
+ "litellm",
8
+ "bifrost",
9
+ "ollama",
10
+ "gemini",
11
+ "anthropic",
12
+ "huggingface",
13
+ "bedrock",
14
+ "claude-cli",
15
+ "qwen3-tts"
16
+ ];
17
+ /**
18
+ * Extract text content from a message content field.
19
+ *
20
+ * Handles both simple string content and multimodal content arrays,
21
+ * extracting only the text parts and concatenating them.
22
+ *
23
+ * @param content - The message content (string or ContentPart array)
24
+ * @returns The extracted text content
25
+ */
26
+ function extractTextContent(content) {
27
+ if (typeof content === "string") return content;
28
+ return content.filter((part) => part.type === "text").map((part) => part.text).join("\n");
29
+ }
30
+ /**
31
+ * Base error class for all AI operations.
32
+ * Provider-specific errors are mapped to subclasses for structured error handling.
33
+ *
34
+ * @param message - Human-readable error description
35
+ * @param code - Machine-readable error code (e.g., 'AUTH_ERROR', 'RATE_LIMIT')
36
+ * @param provider - Provider that raised the error (e.g., 'openai', 'anthropic')
37
+ * @param model - Model involved in the error, if applicable
38
+ */
39
+ var AIError = class extends Error {
40
+ code;
41
+ provider;
42
+ model;
43
+ retryable;
44
+ constructor(message, code, provider, model, retryable = false) {
45
+ super(message);
46
+ this.code = code;
47
+ this.provider = provider;
48
+ this.model = model;
49
+ this.retryable = retryable;
50
+ this.name = "AIError";
51
+ }
52
+ };
53
+ /**
54
+ * Thrown when API key or credentials are invalid or missing.
55
+ *
56
+ * @param provider - Provider that rejected authentication
57
+ */
58
+ var AuthenticationError = class extends AIError {
59
+ constructor(provider) {
60
+ super("Authentication failed", "AUTH_ERROR", provider, void 0, false);
61
+ this.name = "AuthenticationError";
62
+ }
63
+ };
64
+ /**
65
+ * Thrown when the provider's rate limit has been exceeded.
66
+ *
67
+ * @param provider - Provider that enforced the rate limit
68
+ * @param retryAfter - Seconds to wait before retrying, if provided by the API
69
+ */
70
+ var RateLimitError = class extends AIError {
71
+ retryAfter;
72
+ constructor(provider, retryAfter) {
73
+ super(`Rate limit exceeded${retryAfter ? `, retry after ${retryAfter}s` : ""}`, "RATE_LIMIT", provider, void 0, true);
74
+ this.name = "RateLimitError";
75
+ this.retryAfter = retryAfter;
76
+ }
77
+ };
78
+ /**
79
+ * Thrown when the requested model does not exist or is not available.
80
+ *
81
+ * @param model - The model identifier that was not found
82
+ * @param provider - Provider that was queried
83
+ */
84
+ var ModelNotFoundError = class extends AIError {
85
+ constructor(model, provider) {
86
+ super(`Model not found: ${model}`, "MODEL_NOT_FOUND", provider, model, false);
87
+ this.name = "ModelNotFoundError";
88
+ }
89
+ };
90
+ /**
91
+ * Thrown when the input exceeds the model's maximum context window.
92
+ *
93
+ * @param provider - Provider that reported the error
94
+ * @param model - Model whose context limit was exceeded
95
+ */
96
+ var ContextLengthError = class extends AIError {
97
+ constructor(provider, model) {
98
+ super("Input exceeds maximum context length", "CONTEXT_LENGTH_EXCEEDED", provider, model, false);
99
+ this.name = "ContextLengthError";
100
+ }
101
+ };
102
+ /**
103
+ * Thrown when content is blocked by the provider's safety/content filters.
104
+ *
105
+ * @param provider - Provider that filtered the content
106
+ * @param model - Model that triggered the filter
107
+ */
108
+ var ContentFilterError = class extends AIError {
109
+ constructor(provider, model) {
110
+ super("Content filtered by safety systems", "CONTENT_FILTERED", provider, model, false);
111
+ this.name = "ContentFilterError";
112
+ }
113
+ };
114
+ //#endregion
115
+ export { ContextLengthError as a, extractTextContent as c, ContentFilterError as i, AI_PROVIDER_TYPES as n, ModelNotFoundError as o, AuthenticationError as r, RateLimitError as s, AIError as t };
116
+
117
+ //# sourceMappingURL=types-FHs-KbGL.js.map