@em3odme/agentic 0.1.0
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.
- package/LICENSE.md +21 -0
- package/README.md +145 -0
- package/dist/index.cjs +782 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +111 -0
- package/dist/index.d.ts +111 -0
- package/dist/index.js +751 -0
- package/dist/index.js.map +1 -0
- package/package.json +62 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,782 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
ConfigurationError: () => ConfigurationError,
|
|
24
|
+
ModelRunner: () => ModelRunner,
|
|
25
|
+
ProviderError: () => ProviderError,
|
|
26
|
+
cloudflareAIModel: () => cloudflareAIModel,
|
|
27
|
+
groqAIModel: () => groqAIModel
|
|
28
|
+
});
|
|
29
|
+
module.exports = __toCommonJS(index_exports);
|
|
30
|
+
|
|
31
|
+
// src/providers/cloudflare.ts
|
|
32
|
+
var cloudflareAIModel = (model) => ({
|
|
33
|
+
provider: "cloudflare",
|
|
34
|
+
model
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
// src/providers/groq.ts
|
|
38
|
+
var groqAIModel = (model) => ({
|
|
39
|
+
provider: "groq",
|
|
40
|
+
model
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
// src/types.ts
|
|
44
|
+
var ProviderError = class extends Error {
|
|
45
|
+
constructor(message, provider, statusCode, originalError) {
|
|
46
|
+
super(message);
|
|
47
|
+
this.provider = provider;
|
|
48
|
+
this.statusCode = statusCode;
|
|
49
|
+
this.originalError = originalError;
|
|
50
|
+
this.name = "ProviderError";
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
var ConfigurationError = class extends Error {
|
|
54
|
+
constructor(message, provider) {
|
|
55
|
+
super(message);
|
|
56
|
+
this.provider = provider;
|
|
57
|
+
this.name = "ConfigurationError";
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
// src/providers/BaseModelProvider.ts
|
|
62
|
+
var BaseModelProvider = class {
|
|
63
|
+
constructor(environment) {
|
|
64
|
+
this.environment = environment;
|
|
65
|
+
}
|
|
66
|
+
validateRequiredKeys(envKeys) {
|
|
67
|
+
const missing = envKeys.filter((key) => !this.environment[key]);
|
|
68
|
+
if (missing.length > 0) {
|
|
69
|
+
throw new ConfigurationError(
|
|
70
|
+
`Missing required environment variables: ${missing.join(", ")}`,
|
|
71
|
+
this.providerType
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
createError(message, statusCode, originalError) {
|
|
76
|
+
return new ProviderError(
|
|
77
|
+
message,
|
|
78
|
+
this.providerType,
|
|
79
|
+
statusCode,
|
|
80
|
+
originalError
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
buildUsageObject(promptTokens, completionTokens, totalTokens) {
|
|
84
|
+
return {
|
|
85
|
+
promptTokens,
|
|
86
|
+
completionTokens,
|
|
87
|
+
totalTokens
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
filterDuplicateToolCalls(toolCalls) {
|
|
91
|
+
const seen = /* @__PURE__ */ new Set();
|
|
92
|
+
return toolCalls.filter((toolCall) => {
|
|
93
|
+
const key = toolCall.function.name;
|
|
94
|
+
if (seen.has(key)) {
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
97
|
+
seen.add(key);
|
|
98
|
+
return true;
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
// src/providers/utils.ts
|
|
104
|
+
var delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
105
|
+
async function runWithRetry(fn, maxRetries = 3, delayMs = 1e3, info) {
|
|
106
|
+
for (let i = 0; i < maxRetries; i++) {
|
|
107
|
+
try {
|
|
108
|
+
return await fn();
|
|
109
|
+
} catch (error) {
|
|
110
|
+
const isRetryable = error instanceof Error && (error.message?.includes("1031") || error.toString().includes("1031") || error.message?.includes("500"));
|
|
111
|
+
if (isRetryable && i < maxRetries - 1) {
|
|
112
|
+
if (info)
|
|
113
|
+
info(`AI Error (1031/500). Retrying ${i + 1}/${maxRetries}...`);
|
|
114
|
+
await delay(delayMs * (i + 1));
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
throw error;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// src/providers/CloudflareProvider.ts
|
|
123
|
+
var CloudflareProvider = class extends BaseModelProvider {
|
|
124
|
+
constructor(environment) {
|
|
125
|
+
super(environment);
|
|
126
|
+
this.providerType = "cloudflare";
|
|
127
|
+
this.validateRequiredKeys(["AI"]);
|
|
128
|
+
}
|
|
129
|
+
supportsJsonMode() {
|
|
130
|
+
return true;
|
|
131
|
+
}
|
|
132
|
+
supportsTools() {
|
|
133
|
+
return true;
|
|
134
|
+
}
|
|
135
|
+
async execute(request) {
|
|
136
|
+
const payload = {
|
|
137
|
+
messages: request.messages,
|
|
138
|
+
...request.options
|
|
139
|
+
};
|
|
140
|
+
if (request.jsonMode) {
|
|
141
|
+
payload.response_format = { type: "json_object" };
|
|
142
|
+
}
|
|
143
|
+
try {
|
|
144
|
+
const response = await runWithRetry(
|
|
145
|
+
async () => {
|
|
146
|
+
return this.environment.AI.run(
|
|
147
|
+
request.model,
|
|
148
|
+
payload
|
|
149
|
+
);
|
|
150
|
+
},
|
|
151
|
+
3,
|
|
152
|
+
1e3
|
|
153
|
+
);
|
|
154
|
+
return this.parseResponse(response);
|
|
155
|
+
} catch (error) {
|
|
156
|
+
throw this.createError(
|
|
157
|
+
`Cloudflare AI execution failed: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
158
|
+
void 0,
|
|
159
|
+
error
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
parseResponse(response) {
|
|
164
|
+
const content = this.extractContent(response);
|
|
165
|
+
const toolCalls = this.extractToolCalls(response);
|
|
166
|
+
const usage = this.buildUsageObject(0, 0, 0);
|
|
167
|
+
return {
|
|
168
|
+
content,
|
|
169
|
+
toolCalls,
|
|
170
|
+
raw: response,
|
|
171
|
+
usage
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
extractContent(response) {
|
|
175
|
+
if (typeof response === "string") {
|
|
176
|
+
return response;
|
|
177
|
+
}
|
|
178
|
+
if (response && typeof response === "object") {
|
|
179
|
+
if ("response" in response) {
|
|
180
|
+
return String(response.response || "");
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
return "";
|
|
184
|
+
}
|
|
185
|
+
extractToolCalls(response) {
|
|
186
|
+
if (!response || typeof response !== "object") {
|
|
187
|
+
return [];
|
|
188
|
+
}
|
|
189
|
+
const toolCalls = response.tool_calls;
|
|
190
|
+
if (!Array.isArray(toolCalls) || toolCalls.length === 0) {
|
|
191
|
+
return [];
|
|
192
|
+
}
|
|
193
|
+
return toolCalls.map((toolCall, index) => {
|
|
194
|
+
const tc = toolCall;
|
|
195
|
+
const args = tc.arguments;
|
|
196
|
+
const argsString = typeof args === "string" ? args : JSON.stringify(args);
|
|
197
|
+
return {
|
|
198
|
+
id: `tool-call-${index}`,
|
|
199
|
+
type: "function",
|
|
200
|
+
function: {
|
|
201
|
+
name: String(tc.name || ""),
|
|
202
|
+
arguments: argsString
|
|
203
|
+
}
|
|
204
|
+
};
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
// src/providers/GroqProvider.ts
|
|
210
|
+
var GroqProvider = class extends BaseModelProvider {
|
|
211
|
+
constructor(environment) {
|
|
212
|
+
super(environment);
|
|
213
|
+
this.providerType = "groq";
|
|
214
|
+
this.apiEndpoint = "https://api.groq.com/openai/v1/chat/completions";
|
|
215
|
+
this.validateRequiredKeys(["GROQ_API_KEY"]);
|
|
216
|
+
}
|
|
217
|
+
supportsJsonMode() {
|
|
218
|
+
return true;
|
|
219
|
+
}
|
|
220
|
+
supportsTools() {
|
|
221
|
+
return true;
|
|
222
|
+
}
|
|
223
|
+
async execute(request) {
|
|
224
|
+
const payload = this.buildPayload(request);
|
|
225
|
+
try {
|
|
226
|
+
const response = await this.makeRequest(payload);
|
|
227
|
+
return this.parseResponse(response);
|
|
228
|
+
} catch (error) {
|
|
229
|
+
if (error instanceof Response) {
|
|
230
|
+
const errorText = await error.text();
|
|
231
|
+
throw this.createError(
|
|
232
|
+
`Groq API Error (${error.status}): ${errorText}. Model: ${request.model}`,
|
|
233
|
+
error.status,
|
|
234
|
+
errorText
|
|
235
|
+
);
|
|
236
|
+
}
|
|
237
|
+
throw this.createError(
|
|
238
|
+
`Groq execution failed: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
239
|
+
void 0,
|
|
240
|
+
error
|
|
241
|
+
);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
buildPayload(request) {
|
|
245
|
+
const payload = {
|
|
246
|
+
model: request.model,
|
|
247
|
+
messages: request.messages,
|
|
248
|
+
...request.options
|
|
249
|
+
};
|
|
250
|
+
if (request.options?.tools) {
|
|
251
|
+
payload.tool_choice = "auto";
|
|
252
|
+
}
|
|
253
|
+
if (request.jsonMode && !request.options?.tools) {
|
|
254
|
+
payload.response_format = { type: "json_object" };
|
|
255
|
+
}
|
|
256
|
+
return payload;
|
|
257
|
+
}
|
|
258
|
+
async makeRequest(payload) {
|
|
259
|
+
const response = await runWithRetry(
|
|
260
|
+
async () => {
|
|
261
|
+
return fetch(this.apiEndpoint, {
|
|
262
|
+
method: "POST",
|
|
263
|
+
headers: {
|
|
264
|
+
Authorization: `Bearer ${this.environment.GROQ_API_KEY}`,
|
|
265
|
+
"Content-Type": "application/json"
|
|
266
|
+
},
|
|
267
|
+
body: JSON.stringify(payload)
|
|
268
|
+
});
|
|
269
|
+
},
|
|
270
|
+
3,
|
|
271
|
+
1e3
|
|
272
|
+
);
|
|
273
|
+
if (!response) {
|
|
274
|
+
throw this.createError(
|
|
275
|
+
`Groq execution failed: No response from Groq API`,
|
|
276
|
+
void 0,
|
|
277
|
+
void 0
|
|
278
|
+
);
|
|
279
|
+
}
|
|
280
|
+
return response?.json();
|
|
281
|
+
}
|
|
282
|
+
extractContent(data) {
|
|
283
|
+
return data.choices?.[0]?.message?.content || "";
|
|
284
|
+
}
|
|
285
|
+
extractToolCalls(data) {
|
|
286
|
+
return data.choices?.[0]?.message?.tool_calls || [];
|
|
287
|
+
}
|
|
288
|
+
parseResponse(data) {
|
|
289
|
+
const content = this.extractContent(data);
|
|
290
|
+
const toolCalls = this.filterDuplicateToolCalls(
|
|
291
|
+
this.extractToolCalls(data)
|
|
292
|
+
);
|
|
293
|
+
const usage = data.usage ? this.buildUsageObject(
|
|
294
|
+
data.usage.prompt_tokens,
|
|
295
|
+
data.usage.completion_tokens,
|
|
296
|
+
data.usage.total_tokens
|
|
297
|
+
) : this.buildUsageObject(0, 0, 0);
|
|
298
|
+
return {
|
|
299
|
+
content,
|
|
300
|
+
toolCalls,
|
|
301
|
+
raw: data,
|
|
302
|
+
usage
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
};
|
|
306
|
+
|
|
307
|
+
// src/providers/ProviderFactory.ts
|
|
308
|
+
var ProviderFactory = class {
|
|
309
|
+
static createProvider(providerType, environment) {
|
|
310
|
+
const ProviderClass = this.providers.get(providerType);
|
|
311
|
+
if (!ProviderClass) {
|
|
312
|
+
throw new ConfigurationError(
|
|
313
|
+
`Unknown provider: ${providerType}. Supported providers: ${Array.from(this.providers.keys()).join(", ")}`,
|
|
314
|
+
providerType
|
|
315
|
+
);
|
|
316
|
+
}
|
|
317
|
+
return new ProviderClass(environment);
|
|
318
|
+
}
|
|
319
|
+
static getSupportedProviders() {
|
|
320
|
+
return Array.from(this.providers.keys());
|
|
321
|
+
}
|
|
322
|
+
static registerProvider(providerType, providerClass) {
|
|
323
|
+
this.providers.set(providerType, providerClass);
|
|
324
|
+
}
|
|
325
|
+
static isProviderSupported(providerType) {
|
|
326
|
+
return this.providers.has(providerType);
|
|
327
|
+
}
|
|
328
|
+
};
|
|
329
|
+
ProviderFactory.providers = /* @__PURE__ */ new Map([
|
|
330
|
+
["cloudflare", CloudflareProvider],
|
|
331
|
+
["groq", GroqProvider]
|
|
332
|
+
]);
|
|
333
|
+
|
|
334
|
+
// src/response/JsonParser.ts
|
|
335
|
+
var JsonParser = class {
|
|
336
|
+
/**
|
|
337
|
+
* Enhanced JSON parsing with multiple fallback strategies
|
|
338
|
+
*/
|
|
339
|
+
static parse(input, expectJson = false) {
|
|
340
|
+
if (!input || typeof input !== "string") {
|
|
341
|
+
return null;
|
|
342
|
+
}
|
|
343
|
+
if (expectJson && !this.looksLikeJson(input)) {
|
|
344
|
+
return null;
|
|
345
|
+
}
|
|
346
|
+
try {
|
|
347
|
+
return JSON.parse(input);
|
|
348
|
+
} catch {
|
|
349
|
+
const markdownJson = this.extractFromMarkdown(input);
|
|
350
|
+
if (markdownJson) {
|
|
351
|
+
try {
|
|
352
|
+
return JSON.parse(markdownJson);
|
|
353
|
+
} catch {
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
const genericCode = this.extractFromCodeBlock(input);
|
|
357
|
+
if (genericCode) {
|
|
358
|
+
try {
|
|
359
|
+
return JSON.parse(genericCode);
|
|
360
|
+
} catch {
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
const braceMatch = this.extractByBraces(input);
|
|
364
|
+
if (braceMatch) {
|
|
365
|
+
try {
|
|
366
|
+
return JSON.parse(braceMatch);
|
|
367
|
+
} catch {
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
const fixedJson = this.attemptJsonFixes(input);
|
|
371
|
+
if (fixedJson) {
|
|
372
|
+
try {
|
|
373
|
+
return JSON.parse(fixedJson);
|
|
374
|
+
} catch {
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
return null;
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
static looksLikeJson(input) {
|
|
381
|
+
const trimmed = input.trim();
|
|
382
|
+
return trimmed.startsWith("{") || trimmed.startsWith("[") || trimmed.includes('"');
|
|
383
|
+
}
|
|
384
|
+
static extractFromMarkdown(input) {
|
|
385
|
+
const match = input.match(/```json\s*(\{[\s\S]*?\})\s*```/);
|
|
386
|
+
return match ? match[1] : null;
|
|
387
|
+
}
|
|
388
|
+
static extractFromCodeBlock(input) {
|
|
389
|
+
const match = input.match(/```\s*(\{[\s\S]*?\})\s*```/);
|
|
390
|
+
return match ? match[1] : null;
|
|
391
|
+
}
|
|
392
|
+
static extractByBraces(input) {
|
|
393
|
+
const match = input.match(/\{[\s\S]*\}/);
|
|
394
|
+
return match ? match[0] : null;
|
|
395
|
+
}
|
|
396
|
+
static attemptJsonFixes(input) {
|
|
397
|
+
let fixed = input.trim();
|
|
398
|
+
fixed = fixed.replace(/,(\s*[}\]])/g, "$1");
|
|
399
|
+
fixed = fixed.replace(/'/g, '"');
|
|
400
|
+
fixed = fixed.replace(/"\s*:\s*"([^"]*?)"/g, (match, content) => {
|
|
401
|
+
const escaped = content.replace(/"/g, '\\"');
|
|
402
|
+
return `: "${escaped}"`;
|
|
403
|
+
});
|
|
404
|
+
fixed = fixed.replace(/\/\/.*$/gm, "");
|
|
405
|
+
fixed = fixed.replace(/\/\*[\s\S]*?\*\//g, "");
|
|
406
|
+
if (this.looksLikeJson(fixed)) {
|
|
407
|
+
return fixed;
|
|
408
|
+
}
|
|
409
|
+
return null;
|
|
410
|
+
}
|
|
411
|
+
/**
|
|
412
|
+
* Safely stringify any value to JSON
|
|
413
|
+
*/
|
|
414
|
+
static stringify(value, pretty = false) {
|
|
415
|
+
try {
|
|
416
|
+
return pretty ? JSON.stringify(value, null, 2) : JSON.stringify(value);
|
|
417
|
+
} catch {
|
|
418
|
+
return String(value);
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
};
|
|
422
|
+
|
|
423
|
+
// src/response/ResponseBuilder.ts
|
|
424
|
+
var ResponseBuilder = class _ResponseBuilder {
|
|
425
|
+
constructor() {
|
|
426
|
+
this.content = "";
|
|
427
|
+
this.toolCalls = [];
|
|
428
|
+
this.rawResponse = null;
|
|
429
|
+
this.usage = {
|
|
430
|
+
promptTokens: 0,
|
|
431
|
+
completionTokens: 0,
|
|
432
|
+
totalTokens: 0
|
|
433
|
+
};
|
|
434
|
+
this.jsonMode = false;
|
|
435
|
+
}
|
|
436
|
+
setContent(content) {
|
|
437
|
+
this.content = content;
|
|
438
|
+
return this;
|
|
439
|
+
}
|
|
440
|
+
setToolCalls(toolCalls) {
|
|
441
|
+
this.toolCalls = toolCalls;
|
|
442
|
+
return this;
|
|
443
|
+
}
|
|
444
|
+
setRawResponse(raw) {
|
|
445
|
+
this.rawResponse = raw;
|
|
446
|
+
return this;
|
|
447
|
+
}
|
|
448
|
+
setUsage(usage) {
|
|
449
|
+
this.usage = usage;
|
|
450
|
+
return this;
|
|
451
|
+
}
|
|
452
|
+
setJsonMode(jsonMode) {
|
|
453
|
+
this.jsonMode = jsonMode;
|
|
454
|
+
return this;
|
|
455
|
+
}
|
|
456
|
+
build() {
|
|
457
|
+
const isJson = this.toolCalls.length === 0 && (this.jsonMode || this.content.trim().startsWith("{"));
|
|
458
|
+
return {
|
|
459
|
+
content: this.content,
|
|
460
|
+
isJson,
|
|
461
|
+
tool_calls: this.toolCalls,
|
|
462
|
+
raw: this.rawResponse,
|
|
463
|
+
usage: this.usage,
|
|
464
|
+
json: () => JsonParser.parse(this.content, isJson)
|
|
465
|
+
};
|
|
466
|
+
}
|
|
467
|
+
static create() {
|
|
468
|
+
return new _ResponseBuilder();
|
|
469
|
+
}
|
|
470
|
+
};
|
|
471
|
+
|
|
472
|
+
// src/config/Configuration.ts
|
|
473
|
+
var ConfigurationValidator = class {
|
|
474
|
+
static validateProvider(provider, environment) {
|
|
475
|
+
const config = this.providerConfigs.get(provider);
|
|
476
|
+
if (!config) {
|
|
477
|
+
throw new Error(`Unknown provider: ${provider}`);
|
|
478
|
+
}
|
|
479
|
+
this.validateEnvironmentVariables(provider, config, environment);
|
|
480
|
+
}
|
|
481
|
+
static validateModel(provider, model) {
|
|
482
|
+
const config = this.providerConfigs.get(provider);
|
|
483
|
+
if (!config || !config.modelValidation) {
|
|
484
|
+
return;
|
|
485
|
+
}
|
|
486
|
+
const validation = config.modelValidation;
|
|
487
|
+
if (validation.minLength && model.length < validation.minLength) {
|
|
488
|
+
throw new Error(
|
|
489
|
+
`Model name must be at least ${validation.minLength} characters long`
|
|
490
|
+
);
|
|
491
|
+
}
|
|
492
|
+
if (validation.maxLength && model.length > validation.maxLength) {
|
|
493
|
+
throw new Error(
|
|
494
|
+
`Model name must not exceed ${validation.maxLength} characters`
|
|
495
|
+
);
|
|
496
|
+
}
|
|
497
|
+
if (validation.pattern && !validation.pattern.test(model)) {
|
|
498
|
+
throw new Error(
|
|
499
|
+
`Model name does not match required pattern: ${validation.pattern}`
|
|
500
|
+
);
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
static validateOptions(provider, options) {
|
|
504
|
+
const config = this.providerConfigs.get(provider);
|
|
505
|
+
if (!config) {
|
|
506
|
+
return;
|
|
507
|
+
}
|
|
508
|
+
if ("temperature" in options) {
|
|
509
|
+
const temp = options.temperature;
|
|
510
|
+
if (typeof temp !== "number" || temp < 0 || temp > 2) {
|
|
511
|
+
throw new Error("Temperature must be a number between 0 and 2");
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
if ("max_tokens" in options) {
|
|
515
|
+
const tokens = options.max_tokens;
|
|
516
|
+
if (typeof tokens !== "number" || tokens <= 0) {
|
|
517
|
+
throw new Error("Max tokens must be a positive number");
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
static getProviderConfig(provider) {
|
|
522
|
+
return this.providerConfigs.get(provider);
|
|
523
|
+
}
|
|
524
|
+
static registerProviderConfig(provider, config) {
|
|
525
|
+
this.providerConfigs.set(provider, config);
|
|
526
|
+
}
|
|
527
|
+
static validateEnvironmentVariables(provider, config, environment) {
|
|
528
|
+
const missing = config.requiredEnvVars.filter((key) => !environment[key]);
|
|
529
|
+
if (missing.length > 0) {
|
|
530
|
+
throw new Error(
|
|
531
|
+
`Missing required environment variables for ${provider}: ${missing.join(", ")}`
|
|
532
|
+
);
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
};
|
|
536
|
+
ConfigurationValidator.providerConfigs = /* @__PURE__ */ new Map([
|
|
537
|
+
[
|
|
538
|
+
"cloudflare",
|
|
539
|
+
{
|
|
540
|
+
requiredEnvVars: ["AI"],
|
|
541
|
+
optionalEnvVars: [],
|
|
542
|
+
defaultOptions: {},
|
|
543
|
+
supportedFeatures: {
|
|
544
|
+
jsonMode: true,
|
|
545
|
+
tools: true,
|
|
546
|
+
streaming: false
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
],
|
|
550
|
+
[
|
|
551
|
+
"groq",
|
|
552
|
+
{
|
|
553
|
+
requiredEnvVars: ["GROQ_API_KEY"],
|
|
554
|
+
optionalEnvVars: [],
|
|
555
|
+
defaultOptions: {
|
|
556
|
+
temperature: 0.7,
|
|
557
|
+
max_tokens: 1024
|
|
558
|
+
},
|
|
559
|
+
supportedFeatures: {
|
|
560
|
+
jsonMode: true,
|
|
561
|
+
tools: true,
|
|
562
|
+
streaming: true
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
]
|
|
566
|
+
]);
|
|
567
|
+
var RuntimeConfigManager = class {
|
|
568
|
+
static getConfig() {
|
|
569
|
+
return { ...this.defaultConfig };
|
|
570
|
+
}
|
|
571
|
+
static updateConfig(updates) {
|
|
572
|
+
this.defaultConfig = this.deepMerge(this.defaultConfig, updates);
|
|
573
|
+
}
|
|
574
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
575
|
+
static deepMerge(target, source) {
|
|
576
|
+
const result = { ...target };
|
|
577
|
+
for (const key in source) {
|
|
578
|
+
if (source[key] && typeof source[key] === "object" && !Array.isArray(source[key])) {
|
|
579
|
+
result[key] = this.deepMerge(result[key] || {}, source[key]);
|
|
580
|
+
} else {
|
|
581
|
+
result[key] = source[key];
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
return result;
|
|
585
|
+
}
|
|
586
|
+
};
|
|
587
|
+
RuntimeConfigManager.defaultConfig = {
|
|
588
|
+
timeout: 3e4,
|
|
589
|
+
retries: {
|
|
590
|
+
maxAttempts: 3,
|
|
591
|
+
baseDelay: 1e3,
|
|
592
|
+
maxDelay: 1e4
|
|
593
|
+
},
|
|
594
|
+
caching: {
|
|
595
|
+
enabled: false,
|
|
596
|
+
ttl: 3e5
|
|
597
|
+
// 5 minutes
|
|
598
|
+
},
|
|
599
|
+
logging: {
|
|
600
|
+
enabled: true,
|
|
601
|
+
level: "info"
|
|
602
|
+
}
|
|
603
|
+
};
|
|
604
|
+
|
|
605
|
+
// src/errors/RetryHandler.ts
|
|
606
|
+
var RetryHandler = class {
|
|
607
|
+
static async executeWithRetry(operation, config = {}, context) {
|
|
608
|
+
const fullConfig = { ...this.defaultConfig, ...config };
|
|
609
|
+
let lastError;
|
|
610
|
+
for (let attempt = 0; attempt <= fullConfig.maxRetries; attempt++) {
|
|
611
|
+
try {
|
|
612
|
+
return await operation();
|
|
613
|
+
} catch (error) {
|
|
614
|
+
lastError = error;
|
|
615
|
+
if (attempt === fullConfig.maxRetries) {
|
|
616
|
+
break;
|
|
617
|
+
}
|
|
618
|
+
if (!this.shouldRetry(error, fullConfig)) {
|
|
619
|
+
break;
|
|
620
|
+
}
|
|
621
|
+
const delay2 = this.calculateDelay(attempt, fullConfig);
|
|
622
|
+
if (context) {
|
|
623
|
+
console.warn(
|
|
624
|
+
`${context} - Attempt ${attempt + 1}/${fullConfig.maxRetries + 1} failed. Retrying in ${delay2}ms...`
|
|
625
|
+
);
|
|
626
|
+
}
|
|
627
|
+
await this.sleep(delay2);
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
throw lastError;
|
|
631
|
+
}
|
|
632
|
+
static shouldRetry(error, config) {
|
|
633
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
634
|
+
const errorString = errorMessage.toLowerCase();
|
|
635
|
+
if (config.retryableErrors.some(
|
|
636
|
+
(retryableError) => errorString.includes(retryableError.toLowerCase())
|
|
637
|
+
)) {
|
|
638
|
+
return true;
|
|
639
|
+
}
|
|
640
|
+
if (error && typeof error === "object" && "status" in error && typeof error.status === "number") {
|
|
641
|
+
return config.retryableStatusCodes.includes(error.status);
|
|
642
|
+
}
|
|
643
|
+
if (error instanceof TypeError && error.message.includes("fetch")) {
|
|
644
|
+
return true;
|
|
645
|
+
}
|
|
646
|
+
return false;
|
|
647
|
+
}
|
|
648
|
+
static calculateDelay(attempt, config) {
|
|
649
|
+
const exponentialDelay = config.baseDelay * Math.pow(config.backoffFactor, attempt);
|
|
650
|
+
const jitter = Math.random() * 0.1 * exponentialDelay;
|
|
651
|
+
return Math.min(exponentialDelay + jitter, config.maxDelay);
|
|
652
|
+
}
|
|
653
|
+
static sleep(ms) {
|
|
654
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
655
|
+
}
|
|
656
|
+
};
|
|
657
|
+
RetryHandler.defaultConfig = {
|
|
658
|
+
maxRetries: 3,
|
|
659
|
+
baseDelay: 1e3,
|
|
660
|
+
maxDelay: 1e4,
|
|
661
|
+
backoffFactor: 2,
|
|
662
|
+
retryableErrors: ["1031", "500", "502", "503", "504", "timeout", "network"],
|
|
663
|
+
retryableStatusCodes: [500, 502, 503, 504, 429]
|
|
664
|
+
};
|
|
665
|
+
var ErrorHandler = class {
|
|
666
|
+
static wrapProviderError(error, provider, context) {
|
|
667
|
+
const contextMessage = context ? ` (${context})` : "";
|
|
668
|
+
if (error instanceof Error) {
|
|
669
|
+
return new Error(
|
|
670
|
+
`[${provider.toUpperCase()}]${contextMessage} ${error.message}`
|
|
671
|
+
);
|
|
672
|
+
}
|
|
673
|
+
if (typeof error === "string") {
|
|
674
|
+
return new Error(`[${provider.toUpperCase()}]${contextMessage} ${error}`);
|
|
675
|
+
}
|
|
676
|
+
return new Error(
|
|
677
|
+
`[${provider.toUpperCase()}]${contextMessage} Unknown error occurred`
|
|
678
|
+
);
|
|
679
|
+
}
|
|
680
|
+
static isNetworkError(error) {
|
|
681
|
+
return error instanceof TypeError && (error.message.includes("fetch") || error.message.includes("network") || error.message.includes("ECONNREFUSED"));
|
|
682
|
+
}
|
|
683
|
+
static isTimeoutError(error) {
|
|
684
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
685
|
+
return errorMessage.toLowerCase().includes("timeout");
|
|
686
|
+
}
|
|
687
|
+
static isRateLimitError(error) {
|
|
688
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
689
|
+
return errorMessage.toLowerCase().includes("rate limit") || Boolean(
|
|
690
|
+
error && typeof error === "object" && "status" in error && error.status === 429
|
|
691
|
+
);
|
|
692
|
+
}
|
|
693
|
+
};
|
|
694
|
+
|
|
695
|
+
// src/ModelRunner.ts
|
|
696
|
+
var ModelRunner = class {
|
|
697
|
+
constructor(environment) {
|
|
698
|
+
this.environment = environment;
|
|
699
|
+
}
|
|
700
|
+
async run({
|
|
701
|
+
messages,
|
|
702
|
+
model,
|
|
703
|
+
jsonMode = false,
|
|
704
|
+
options = {}
|
|
705
|
+
}) {
|
|
706
|
+
const { provider, model: modelName } = model;
|
|
707
|
+
try {
|
|
708
|
+
this.validateInputs(provider, modelName, options);
|
|
709
|
+
const providerInstance = ProviderFactory.createProvider(
|
|
710
|
+
provider,
|
|
711
|
+
this.environment
|
|
712
|
+
);
|
|
713
|
+
this.validateCapabilities(providerInstance, jsonMode, options);
|
|
714
|
+
const response = await RetryHandler.executeWithRetry(
|
|
715
|
+
() => providerInstance.execute({
|
|
716
|
+
messages,
|
|
717
|
+
jsonMode,
|
|
718
|
+
options,
|
|
719
|
+
model: modelName
|
|
720
|
+
}),
|
|
721
|
+
RuntimeConfigManager.getConfig().retries,
|
|
722
|
+
`ModelRunner execution with ${provider}`
|
|
723
|
+
);
|
|
724
|
+
return ResponseBuilder.create().setContent(response.content).setToolCalls(response.toolCalls).setRawResponse(response.raw).setUsage(response.usage).setJsonMode(jsonMode).build();
|
|
725
|
+
} catch (error) {
|
|
726
|
+
throw ErrorHandler.wrapProviderError(error, provider, "ModelRunner.run");
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
/**
|
|
730
|
+
* Get supported providers
|
|
731
|
+
*/
|
|
732
|
+
static getSupportedProviders() {
|
|
733
|
+
return ProviderFactory.getSupportedProviders();
|
|
734
|
+
}
|
|
735
|
+
/**
|
|
736
|
+
* Check if a provider supports specific features
|
|
737
|
+
*/
|
|
738
|
+
static getProviderCapabilities(provider) {
|
|
739
|
+
const config = ConfigurationValidator.getProviderConfig(provider);
|
|
740
|
+
return config?.supportedFeatures;
|
|
741
|
+
}
|
|
742
|
+
/**
|
|
743
|
+
* Update runtime configuration
|
|
744
|
+
*/
|
|
745
|
+
static updateRuntimeConfig(config) {
|
|
746
|
+
RuntimeConfigManager.updateConfig(config);
|
|
747
|
+
}
|
|
748
|
+
/**
|
|
749
|
+
* Validate inputs before processing
|
|
750
|
+
*/
|
|
751
|
+
validateInputs(provider, model, options) {
|
|
752
|
+
ConfigurationValidator.validateProvider(provider, this.environment);
|
|
753
|
+
ConfigurationValidator.validateModel(provider, model);
|
|
754
|
+
ConfigurationValidator.validateOptions(provider, options);
|
|
755
|
+
}
|
|
756
|
+
/**
|
|
757
|
+
* Validate that the provider supports the requested features
|
|
758
|
+
*/
|
|
759
|
+
validateCapabilities(provider, jsonMode, options) {
|
|
760
|
+
if (jsonMode && !provider.supportsJsonMode()) {
|
|
761
|
+
throw new ConfigurationError(
|
|
762
|
+
`Provider ${provider.providerType} does not support JSON mode`,
|
|
763
|
+
provider.providerType
|
|
764
|
+
);
|
|
765
|
+
}
|
|
766
|
+
if (options?.tools && !provider.supportsTools()) {
|
|
767
|
+
throw new ConfigurationError(
|
|
768
|
+
`Provider ${provider.providerType} does not support tools`,
|
|
769
|
+
provider.providerType
|
|
770
|
+
);
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
};
|
|
774
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
775
|
+
0 && (module.exports = {
|
|
776
|
+
ConfigurationError,
|
|
777
|
+
ModelRunner,
|
|
778
|
+
ProviderError,
|
|
779
|
+
cloudflareAIModel,
|
|
780
|
+
groqAIModel
|
|
781
|
+
});
|
|
782
|
+
//# sourceMappingURL=index.cjs.map
|