@umituz/web-ai 1.0.0 → 1.0.2
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/dist/{ABTesting-DDqNhHtP.d.ts → AIErrors-B2ogd1PB.d.ts} +14 -1
- package/dist/{ABTesting-DWBtKTMQ.d.cts → AIErrors-C5EasV_c.d.cts} +14 -1
- package/dist/{ProviderConfig-a55tLhWT.d.cts → ProviderConfig-DIFa9nmG.d.cts} +10 -1
- package/dist/{ProviderConfig-a55tLhWT.d.ts → ProviderConfig-DIFa9nmG.d.ts} +10 -1
- package/dist/chat/index.cjs +114 -23
- package/dist/chat/index.cjs.map +1 -1
- package/dist/chat/index.d.cts +4 -4
- package/dist/chat/index.d.ts +4 -4
- package/dist/chat/index.js +114 -23
- package/dist/chat/index.js.map +1 -1
- package/dist/{chat.service-DBKq9I2c.d.cts → chat.service-CL1qtYxe.d.cts} +7 -7
- package/dist/{chat.service-RQn8UTXD.d.ts → chat.service-q05GnhEm.d.ts} +7 -7
- package/dist/content/index.cjs +506 -193
- package/dist/content/index.cjs.map +1 -1
- package/dist/content/index.d.cts +7 -8
- package/dist/content/index.d.ts +7 -8
- package/dist/content/index.js +506 -193
- package/dist/content/index.js.map +1 -1
- package/dist/core/index.cjs +122 -15
- package/dist/core/index.cjs.map +1 -1
- package/dist/core/index.d.cts +13 -31
- package/dist/core/index.d.ts +13 -31
- package/dist/core/index.js +119 -16
- package/dist/core/index.js.map +1 -1
- package/dist/{create-ai-BhNaz86i.d.ts → create-ai-9PW752z8.d.ts} +14 -21
- package/dist/{create-ai-DGZFBOA1.d.cts → create-ai-CXYKPK6G.d.cts} +14 -21
- package/dist/hooks/index.cjs +631 -179
- package/dist/hooks/index.cjs.map +1 -1
- package/dist/hooks/index.d.cts +33 -7
- package/dist/hooks/index.d.ts +33 -7
- package/dist/hooks/index.js +631 -180
- package/dist/hooks/index.js.map +1 -1
- package/dist/index-DIHz8_OJ.d.ts +48 -0
- package/dist/index-DKHNcMbl.d.cts +48 -0
- package/dist/index.cjs +666 -104
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +8 -7
- package/dist/index.d.ts +8 -7
- package/dist/index.js +645 -105
- package/dist/index.js.map +1 -1
- package/dist/{provider.factory-COsnprLH.d.cts → provider.factory-BxsfK4WW.d.cts} +1 -1
- package/dist/{provider.factory-CpWFyjbw.d.ts → provider.factory-DfDfk0dK.d.ts} +1 -1
- package/dist/providers/index.cjs +973 -130
- package/dist/providers/index.cjs.map +1 -1
- package/dist/providers/index.d.cts +84 -18
- package/dist/providers/index.d.ts +84 -18
- package/dist/providers/index.js +956 -131
- package/dist/providers/index.js.map +1 -1
- package/dist/text-generation.service-BQO3Tvz2.d.cts +66 -0
- package/dist/text-generation.service-CcYQM6Ch.d.ts +66 -0
- package/dist/wizard/index.d.cts +1 -1
- package/dist/wizard/index.d.ts +1 -1
- package/package.json +9 -1
- package/dist/index-DjX9ZKuM.d.cts +0 -14
- package/dist/index-DxLvE588.d.ts +0 -14
package/dist/providers/index.js
CHANGED
|
@@ -6,25 +6,49 @@ function isMediaProvider(provider) {
|
|
|
6
6
|
return provider.capabilities.includes("image") || provider.capabilities.includes("video");
|
|
7
7
|
}
|
|
8
8
|
|
|
9
|
-
// src/
|
|
10
|
-
var
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
9
|
+
// src/core/errors/ai-error-types.ts
|
|
10
|
+
var AIErrorType = /* @__PURE__ */ ((AIErrorType2) => {
|
|
11
|
+
AIErrorType2["INVALID_API_KEY"] = "INVALID_API_KEY";
|
|
12
|
+
AIErrorType2["MISSING_CONFIG"] = "MISSING_CONFIG";
|
|
13
|
+
AIErrorType2["NETWORK_ERROR"] = "NETWORK_ERROR";
|
|
14
|
+
AIErrorType2["ABORT_ERROR"] = "ABORT_ERROR";
|
|
15
|
+
AIErrorType2["RATE_LIMIT_ERROR"] = "RATE_LIMIT_ERROR";
|
|
16
|
+
AIErrorType2["SERVER_ERROR"] = "SERVER_ERROR";
|
|
17
|
+
AIErrorType2["CLIENT_ERROR"] = "CLIENT_ERROR";
|
|
18
|
+
AIErrorType2["UNKNOWN_ERROR"] = "UNKNOWN_ERROR";
|
|
19
|
+
return AIErrorType2;
|
|
20
|
+
})(AIErrorType || {});
|
|
21
|
+
|
|
22
|
+
// src/core/errors/ai-error.ts
|
|
23
|
+
var KNOWN_ERROR_TYPES = new Set(Object.values(AIErrorType));
|
|
24
|
+
function mapCodeToType(code) {
|
|
25
|
+
switch (code) {
|
|
26
|
+
case "VALIDATION_ERROR":
|
|
27
|
+
case "CLIENT_ERROR":
|
|
28
|
+
return "CLIENT_ERROR" /* CLIENT_ERROR */;
|
|
29
|
+
case "INVALID_API_KEY":
|
|
30
|
+
return "INVALID_API_KEY" /* INVALID_API_KEY */;
|
|
31
|
+
case "MISSING_CONFIG":
|
|
32
|
+
case "PROVIDER_UNAVAILABLE":
|
|
33
|
+
return "MISSING_CONFIG" /* MISSING_CONFIG */;
|
|
34
|
+
case "PROVIDER_QUOTA_EXHAUSTED":
|
|
35
|
+
case "RATE_LIMIT_ERROR":
|
|
36
|
+
return "RATE_LIMIT_ERROR" /* RATE_LIMIT_ERROR */;
|
|
37
|
+
case "NETWORK_ERROR":
|
|
38
|
+
case "TIMEOUT":
|
|
39
|
+
return "NETWORK_ERROR" /* NETWORK_ERROR */;
|
|
40
|
+
case "ABORT_ERROR":
|
|
41
|
+
case "ABORTED":
|
|
42
|
+
return "ABORT_ERROR" /* ABORT_ERROR */;
|
|
43
|
+
case "SERVER_ERROR":
|
|
44
|
+
case "PROVIDER_ERROR":
|
|
45
|
+
case "NO_RESULT":
|
|
46
|
+
return "SERVER_ERROR" /* SERVER_ERROR */;
|
|
47
|
+
default:
|
|
48
|
+
return "UNKNOWN_ERROR" /* UNKNOWN_ERROR */;
|
|
25
49
|
}
|
|
26
|
-
}
|
|
27
|
-
function mapMessageToCode(message, fallback) {
|
|
50
|
+
}
|
|
51
|
+
function mapMessageToCode(message, fallback = "UNKNOWN") {
|
|
28
52
|
const lower = message.toLowerCase();
|
|
29
53
|
if (lower.includes("validation")) return "VALIDATION_ERROR";
|
|
30
54
|
if (lower.includes("quota")) return "PROVIDER_QUOTA_EXHAUSTED";
|
|
@@ -37,6 +61,53 @@ function mapMessageToCode(message, fallback) {
|
|
|
37
61
|
if (lower.includes("network") || lower.includes("fetch")) return "NETWORK_ERROR";
|
|
38
62
|
return fallback;
|
|
39
63
|
}
|
|
64
|
+
var AIError = class _AIError extends Error {
|
|
65
|
+
type;
|
|
66
|
+
code;
|
|
67
|
+
cause;
|
|
68
|
+
metadata;
|
|
69
|
+
constructor(typeOrMessage, messageOrCode, cause, metadata = {}) {
|
|
70
|
+
let effectiveType;
|
|
71
|
+
let effectiveMessage;
|
|
72
|
+
let effectiveCode;
|
|
73
|
+
if (KNOWN_ERROR_TYPES.has(typeOrMessage)) {
|
|
74
|
+
effectiveType = typeOrMessage;
|
|
75
|
+
effectiveMessage = messageOrCode ?? "";
|
|
76
|
+
effectiveCode = effectiveType;
|
|
77
|
+
} else {
|
|
78
|
+
effectiveMessage = typeOrMessage;
|
|
79
|
+
effectiveCode = messageOrCode ?? "UNKNOWN";
|
|
80
|
+
effectiveType = mapCodeToType(effectiveCode);
|
|
81
|
+
}
|
|
82
|
+
super(effectiveMessage);
|
|
83
|
+
this.name = "AIError";
|
|
84
|
+
this.type = effectiveType;
|
|
85
|
+
this.code = effectiveCode;
|
|
86
|
+
this.cause = cause;
|
|
87
|
+
this.metadata = metadata;
|
|
88
|
+
}
|
|
89
|
+
static from(error, fallbackCode = "UNKNOWN") {
|
|
90
|
+
if (error instanceof _AIError) return error;
|
|
91
|
+
if (error instanceof Error) {
|
|
92
|
+
const code = mapMessageToCode(error.message, fallbackCode);
|
|
93
|
+
return new _AIError(error.message, code, error);
|
|
94
|
+
}
|
|
95
|
+
return new _AIError(String(error), fallbackCode, error);
|
|
96
|
+
}
|
|
97
|
+
toString() {
|
|
98
|
+
const statusPart = typeof this.metadata.status === "number" ? ` (HTTP ${this.metadata.status})` : "";
|
|
99
|
+
return `${this.name} [${this.type}]: ${this.message}${statusPart}`;
|
|
100
|
+
}
|
|
101
|
+
toJSON() {
|
|
102
|
+
return {
|
|
103
|
+
name: this.name,
|
|
104
|
+
type: this.type,
|
|
105
|
+
code: this.code,
|
|
106
|
+
message: this.message,
|
|
107
|
+
metadata: this.metadata
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
};
|
|
40
111
|
|
|
41
112
|
// src/content/domain/limits/ProviderTimingConfig.ts
|
|
42
113
|
var ProviderTimingConfig = {
|
|
@@ -111,29 +182,11 @@ var ProviderError = class extends AIError {
|
|
|
111
182
|
providerId;
|
|
112
183
|
};
|
|
113
184
|
|
|
114
|
-
// src/core/errors/
|
|
115
|
-
var
|
|
116
|
-
type;
|
|
117
|
-
cause;
|
|
118
|
-
metadata;
|
|
185
|
+
// src/core/errors/groq-error.ts
|
|
186
|
+
var GroqError = class extends AIError {
|
|
119
187
|
constructor(type, message, cause, metadata = {}) {
|
|
120
|
-
super(message);
|
|
121
|
-
this.name = "
|
|
122
|
-
this.type = type;
|
|
123
|
-
this.cause = cause;
|
|
124
|
-
this.metadata = metadata;
|
|
125
|
-
}
|
|
126
|
-
toString() {
|
|
127
|
-
const statusPart = typeof this.metadata.status === "number" ? ` (HTTP ${this.metadata.status})` : "";
|
|
128
|
-
return `${this.name} [${this.type}]: ${this.message}${statusPart}`;
|
|
129
|
-
}
|
|
130
|
-
toJSON() {
|
|
131
|
-
return {
|
|
132
|
-
name: this.name,
|
|
133
|
-
type: this.type,
|
|
134
|
-
message: this.message,
|
|
135
|
-
metadata: this.metadata
|
|
136
|
-
};
|
|
188
|
+
super(type, message, cause, metadata);
|
|
189
|
+
this.name = "GroqError";
|
|
137
190
|
}
|
|
138
191
|
};
|
|
139
192
|
|
|
@@ -262,7 +315,7 @@ var ProviderFactory = class {
|
|
|
262
315
|
}
|
|
263
316
|
}
|
|
264
317
|
}
|
|
265
|
-
const failure = new
|
|
318
|
+
const failure = new AIError(
|
|
266
319
|
"SERVER_ERROR" /* SERVER_ERROR */,
|
|
267
320
|
`All providers failed for capability 'text'`,
|
|
268
321
|
lastError instanceof Error ? lastError : void 0
|
|
@@ -276,12 +329,16 @@ var ProviderFactory = class {
|
|
|
276
329
|
let wake = null;
|
|
277
330
|
let finished = false;
|
|
278
331
|
let failure;
|
|
332
|
+
const controller = new AbortController();
|
|
333
|
+
const onAbort = () => controller.abort();
|
|
334
|
+
request.signal?.addEventListener("abort", onAbort);
|
|
335
|
+
const effectiveRequest = { ...request, signal: controller.signal };
|
|
279
336
|
const notify = () => {
|
|
280
337
|
const resolve = wake;
|
|
281
338
|
wake = null;
|
|
282
339
|
resolve?.();
|
|
283
340
|
};
|
|
284
|
-
const completion = this.chatStream(
|
|
341
|
+
const completion = this.chatStream(effectiveRequest, {
|
|
285
342
|
onChunk: (chunk) => {
|
|
286
343
|
queue.push(chunk);
|
|
287
344
|
notify();
|
|
@@ -297,18 +354,23 @@ var ProviderFactory = class {
|
|
|
297
354
|
notify();
|
|
298
355
|
}
|
|
299
356
|
);
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
357
|
+
try {
|
|
358
|
+
while (!finished || queue.length > 0) {
|
|
359
|
+
if (queue.length === 0 && !finished) {
|
|
360
|
+
await new Promise((resolve) => {
|
|
361
|
+
wake = resolve;
|
|
362
|
+
});
|
|
363
|
+
}
|
|
364
|
+
while (queue.length > 0) {
|
|
365
|
+
yield queue.shift();
|
|
366
|
+
}
|
|
367
|
+
if (failure) throw failure;
|
|
308
368
|
}
|
|
309
|
-
|
|
369
|
+
await completion;
|
|
370
|
+
} finally {
|
|
371
|
+
controller.abort();
|
|
372
|
+
request.signal?.removeEventListener("abort", onAbort);
|
|
310
373
|
}
|
|
311
|
-
await completion;
|
|
312
374
|
}
|
|
313
375
|
async generateJson(request) {
|
|
314
376
|
const effective = request.model || request.tier ? request : { ...request, tier: this.defaultTier };
|
|
@@ -416,7 +478,7 @@ var ProviderFactory = class {
|
|
|
416
478
|
}
|
|
417
479
|
}
|
|
418
480
|
}
|
|
419
|
-
throw new
|
|
481
|
+
throw new AIError(
|
|
420
482
|
"SERVER_ERROR" /* SERVER_ERROR */,
|
|
421
483
|
`All providers failed for capability '${capability}'`,
|
|
422
484
|
lastError instanceof Error ? lastError : void 0
|
|
@@ -445,7 +507,7 @@ var ProviderFactory = class {
|
|
|
445
507
|
}
|
|
446
508
|
}
|
|
447
509
|
noProviderError(capability) {
|
|
448
|
-
return new
|
|
510
|
+
return new AIError(
|
|
449
511
|
"MISSING_CONFIG" /* MISSING_CONFIG */,
|
|
450
512
|
`No provider registered for capability '${capability}' \u2014 create one with createAI() and include its config block`
|
|
451
513
|
);
|
|
@@ -455,24 +517,81 @@ var ProviderFactory = class {
|
|
|
455
517
|
// src/core/constants/protocol.constants.ts
|
|
456
518
|
var SSE_PREFIX = "data: ";
|
|
457
519
|
var SSE_DONE_MARKER = "[DONE]";
|
|
458
|
-
var JSON_CODE_BLOCK_PATTERN = /```json
|
|
520
|
+
var JSON_CODE_BLOCK_PATTERN = /```(?:json)?\s*\n?([\s\S]*?)\n?```/;
|
|
459
521
|
var JSON_OBJECT_PATTERN = /\{[\s\S]*\}/;
|
|
522
|
+
var JSON_ARRAY_PATTERN = /\[[\s\S]*\]/;
|
|
460
523
|
|
|
461
524
|
// src/core/parsers/sse-lines.ts
|
|
462
525
|
function isSseDataLine(line) {
|
|
463
|
-
return line.startsWith(SSE_PREFIX);
|
|
526
|
+
return line.startsWith(SSE_PREFIX) || line.startsWith("data:");
|
|
464
527
|
}
|
|
465
528
|
function extractSseDataPayload(line) {
|
|
466
|
-
|
|
529
|
+
if (line.startsWith(SSE_PREFIX)) {
|
|
530
|
+
return line.slice(SSE_PREFIX.length);
|
|
531
|
+
}
|
|
532
|
+
if (line.startsWith("data:")) {
|
|
533
|
+
return line.slice(5);
|
|
534
|
+
}
|
|
535
|
+
return line;
|
|
467
536
|
}
|
|
468
537
|
function isSseDoneMarker(payload) {
|
|
469
538
|
return payload === SSE_DONE_MARKER;
|
|
470
539
|
}
|
|
471
540
|
|
|
541
|
+
// src/core/parsers/streaming-response.parser.ts
|
|
542
|
+
var StreamingResponseParser = class {
|
|
543
|
+
constructor(callbacks, extractDelta) {
|
|
544
|
+
this.callbacks = callbacks;
|
|
545
|
+
this.extractDelta = extractDelta;
|
|
546
|
+
}
|
|
547
|
+
callbacks;
|
|
548
|
+
extractDelta;
|
|
549
|
+
decoder = new TextDecoder();
|
|
550
|
+
async parse(stream) {
|
|
551
|
+
const reader = stream.getReader();
|
|
552
|
+
const contentChunks = [];
|
|
553
|
+
let buffer = "";
|
|
554
|
+
const consumeLine = (rawLine) => {
|
|
555
|
+
const line = rawLine.endsWith("\r") ? rawLine.slice(0, -1) : rawLine;
|
|
556
|
+
const trimmed = line.trim();
|
|
557
|
+
if (trimmed.length === 0 || !isSseDataLine(trimmed)) return;
|
|
558
|
+
const payload = extractSseDataPayload(trimmed);
|
|
559
|
+
if (isSseDoneMarker(payload)) return;
|
|
560
|
+
const content = this.extractDelta(payload);
|
|
561
|
+
if (content !== null) {
|
|
562
|
+
contentChunks.push(content);
|
|
563
|
+
this.callbacks.onChunk(content);
|
|
564
|
+
}
|
|
565
|
+
};
|
|
566
|
+
try {
|
|
567
|
+
while (true) {
|
|
568
|
+
const { done, value } = await reader.read();
|
|
569
|
+
if (done) break;
|
|
570
|
+
buffer += this.decoder.decode(value, { stream: true });
|
|
571
|
+
const lines = buffer.split("\n");
|
|
572
|
+
buffer = lines.pop() ?? "";
|
|
573
|
+
for (const line of lines) consumeLine(line);
|
|
574
|
+
}
|
|
575
|
+
buffer += this.decoder.decode();
|
|
576
|
+
if (buffer.length > 0) consumeLine(buffer);
|
|
577
|
+
} catch (error) {
|
|
578
|
+
if (error instanceof AIError) throw error;
|
|
579
|
+
throw new AIError(
|
|
580
|
+
"NETWORK_ERROR" /* NETWORK_ERROR */,
|
|
581
|
+
"Stream was interrupted",
|
|
582
|
+
error instanceof Error ? error : void 0
|
|
583
|
+
);
|
|
584
|
+
} finally {
|
|
585
|
+
reader.releaseLock();
|
|
586
|
+
}
|
|
587
|
+
return contentChunks.join("");
|
|
588
|
+
}
|
|
589
|
+
};
|
|
590
|
+
|
|
472
591
|
// src/core/parsers/json-content-extractor.ts
|
|
473
592
|
function extractJsonFromCodeFence(content) {
|
|
474
593
|
const match = content.match(JSON_CODE_BLOCK_PATTERN);
|
|
475
|
-
if (match && match[1]) return match[1];
|
|
594
|
+
if (match && match[1]) return match[1].trim();
|
|
476
595
|
return null;
|
|
477
596
|
}
|
|
478
597
|
function extractJsonObject(content) {
|
|
@@ -480,8 +599,27 @@ function extractJsonObject(content) {
|
|
|
480
599
|
if (match && match[0]) return match[0];
|
|
481
600
|
return null;
|
|
482
601
|
}
|
|
602
|
+
function extractJsonArray(content) {
|
|
603
|
+
const match = content.match(JSON_ARRAY_PATTERN);
|
|
604
|
+
if (match && match[0]) return match[0];
|
|
605
|
+
return null;
|
|
606
|
+
}
|
|
607
|
+
function repairCommonJsonDefects(text) {
|
|
608
|
+
return text.replace(/(:\s*-?\d+(?:\.\d+)?)\s*"/g, "$1").replace(/(:\s*"[^"]*")"/g, "$1");
|
|
609
|
+
}
|
|
483
610
|
function extractJsonCandidate(content) {
|
|
484
|
-
|
|
611
|
+
const fromFence = extractJsonFromCodeFence(content);
|
|
612
|
+
if (fromFence) return fromFence;
|
|
613
|
+
const objectIndex = content.indexOf("{");
|
|
614
|
+
const arrayIndex = content.indexOf("[");
|
|
615
|
+
if (objectIndex !== -1 && (arrayIndex === -1 || objectIndex < arrayIndex)) {
|
|
616
|
+
const obj = extractJsonObject(content);
|
|
617
|
+
if (obj) return obj;
|
|
618
|
+
} else if (arrayIndex !== -1) {
|
|
619
|
+
const arr = extractJsonArray(content);
|
|
620
|
+
if (arr) return arr;
|
|
621
|
+
}
|
|
622
|
+
return content.trim();
|
|
485
623
|
}
|
|
486
624
|
|
|
487
625
|
// src/core/parsers/json-response.parser.ts
|
|
@@ -490,12 +628,17 @@ var JsonResponseParser = class {
|
|
|
490
628
|
const candidate = extractJsonCandidate(rawContent);
|
|
491
629
|
try {
|
|
492
630
|
return JSON.parse(candidate);
|
|
493
|
-
} catch
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
631
|
+
} catch {
|
|
632
|
+
try {
|
|
633
|
+
return JSON.parse(repairCommonJsonDefects(candidate));
|
|
634
|
+
} catch (error) {
|
|
635
|
+
throw new AIError(
|
|
636
|
+
"UNKNOWN_ERROR" /* UNKNOWN_ERROR */,
|
|
637
|
+
"Failed to parse structured response as JSON",
|
|
638
|
+
error instanceof Error ? error : void 0,
|
|
639
|
+
{ rawContent }
|
|
640
|
+
);
|
|
641
|
+
}
|
|
499
642
|
}
|
|
500
643
|
}
|
|
501
644
|
};
|
|
@@ -621,7 +764,7 @@ var AnthropicHttpTransport = class {
|
|
|
621
764
|
});
|
|
622
765
|
if (!response.ok) await this.throwFromErrorResponse(response);
|
|
623
766
|
if (!response.body) {
|
|
624
|
-
throw new
|
|
767
|
+
throw new AIError("NETWORK_ERROR" /* NETWORK_ERROR */, "Response body is null");
|
|
625
768
|
}
|
|
626
769
|
return response.body;
|
|
627
770
|
} catch (error) {
|
|
@@ -654,7 +797,7 @@ var AnthropicHttpTransport = class {
|
|
|
654
797
|
status: response.status,
|
|
655
798
|
url: response.url
|
|
656
799
|
};
|
|
657
|
-
throw new
|
|
800
|
+
throw new AIError(
|
|
658
801
|
classifyHttpStatus(response.status),
|
|
659
802
|
errorMessage,
|
|
660
803
|
void 0,
|
|
@@ -662,24 +805,24 @@ var AnthropicHttpTransport = class {
|
|
|
662
805
|
);
|
|
663
806
|
}
|
|
664
807
|
normalizeError(error) {
|
|
665
|
-
if (error instanceof
|
|
808
|
+
if (error instanceof AIError) return error;
|
|
666
809
|
if (error instanceof Error) {
|
|
667
810
|
if (error.name === "AbortError") {
|
|
668
|
-
return new
|
|
811
|
+
return new AIError(
|
|
669
812
|
"ABORT_ERROR" /* ABORT_ERROR */,
|
|
670
813
|
"Request was aborted by the client",
|
|
671
814
|
error
|
|
672
815
|
);
|
|
673
816
|
}
|
|
674
817
|
if (error.name === "TypeError" && error.message.toLowerCase().includes("network")) {
|
|
675
|
-
return new
|
|
818
|
+
return new AIError(
|
|
676
819
|
"NETWORK_ERROR" /* NETWORK_ERROR */,
|
|
677
820
|
"Network error: Unable to connect to the AI service",
|
|
678
821
|
error
|
|
679
822
|
);
|
|
680
823
|
}
|
|
681
824
|
}
|
|
682
|
-
return new
|
|
825
|
+
return new AIError(
|
|
683
826
|
"UNKNOWN_ERROR" /* UNKNOWN_ERROR */,
|
|
684
827
|
error instanceof Error ? error.message : "Unknown error occurred",
|
|
685
828
|
error instanceof Error ? error : void 0
|
|
@@ -809,11 +952,14 @@ async function consumeAnthropicStream(stream, callbacks) {
|
|
|
809
952
|
break;
|
|
810
953
|
case "message_stop":
|
|
811
954
|
break;
|
|
812
|
-
case "error":
|
|
813
|
-
|
|
814
|
-
|
|
955
|
+
case "error": {
|
|
956
|
+
const errType = event.error.type;
|
|
957
|
+
const mappedType = errType === "rate_limit_error" ? "RATE_LIMIT_ERROR" /* RATE_LIMIT_ERROR */ : errType === "authentication_error" ? "INVALID_API_KEY" /* INVALID_API_KEY */ : errType === "invalid_request_error" ? "CLIENT_ERROR" /* CLIENT_ERROR */ : "SERVER_ERROR" /* SERVER_ERROR */;
|
|
958
|
+
throw new AIError(
|
|
959
|
+
mappedType,
|
|
815
960
|
event.error.message || "Anthropic stream failed mid-flight"
|
|
816
961
|
);
|
|
962
|
+
}
|
|
817
963
|
}
|
|
818
964
|
};
|
|
819
965
|
try {
|
|
@@ -828,8 +974,8 @@ async function consumeAnthropicStream(stream, callbacks) {
|
|
|
828
974
|
buffer += decoder.decode();
|
|
829
975
|
if (buffer.length > 0) consumeLine(buffer);
|
|
830
976
|
} catch (error) {
|
|
831
|
-
if (error instanceof
|
|
832
|
-
throw new
|
|
977
|
+
if (error instanceof AIError) throw error;
|
|
978
|
+
throw new AIError(
|
|
833
979
|
"NETWORK_ERROR" /* NETWORK_ERROR */,
|
|
834
980
|
"Stream was interrupted",
|
|
835
981
|
error instanceof Error ? error : void 0
|
|
@@ -891,7 +1037,7 @@ var AnthropicProvider = class {
|
|
|
891
1037
|
const messages = [...request.messages];
|
|
892
1038
|
const lastUserIndex = findLastIndex(messages, (m) => m.role === "user");
|
|
893
1039
|
if (lastUserIndex === -1) {
|
|
894
|
-
throw new
|
|
1040
|
+
throw new AIError(
|
|
895
1041
|
"UNKNOWN_ERROR" /* UNKNOWN_ERROR */,
|
|
896
1042
|
"generateStructured requires at least one user message"
|
|
897
1043
|
);
|
|
@@ -946,7 +1092,7 @@ var AnthropicProvider = class {
|
|
|
946
1092
|
finishReason
|
|
947
1093
|
};
|
|
948
1094
|
}
|
|
949
|
-
throw new
|
|
1095
|
+
throw new AIError(
|
|
950
1096
|
"SERVER_ERROR" /* SERVER_ERROR */,
|
|
951
1097
|
"No content generated from Anthropic API",
|
|
952
1098
|
void 0,
|
|
@@ -1100,6 +1246,12 @@ function calculateExponentialBackoff(params) {
|
|
|
1100
1246
|
}
|
|
1101
1247
|
|
|
1102
1248
|
// src/core/constants/identifier.constants.ts
|
|
1249
|
+
var MESSAGE_ID_PREFIX = "msg-";
|
|
1250
|
+
var CHAT_MESSAGE_ID_SUFFIX_LENGTH = 9;
|
|
1251
|
+
var CACHE_KEY_TYPE_COMPLETION = "completion";
|
|
1252
|
+
var CACHE_KEY_TYPE_CHAT = "chat";
|
|
1253
|
+
var USER_ROLE = "user";
|
|
1254
|
+
var ASSISTANT_ROLE = "assistant";
|
|
1103
1255
|
var QUEUE_PRIORITY_USER_REQUEST = 10;
|
|
1104
1256
|
var DEFAULT_RETRY_MAX_ATTEMPTS = 3;
|
|
1105
1257
|
var DEFAULT_RETRY_INITIAL_DELAY_MS = 1e3;
|
|
@@ -1151,7 +1303,7 @@ var RetryManager = class {
|
|
|
1151
1303
|
}
|
|
1152
1304
|
shouldRetry(error, options) {
|
|
1153
1305
|
if (options.shouldRetry) return options.shouldRetry(error);
|
|
1154
|
-
if (error instanceof
|
|
1306
|
+
if (error instanceof AIError) {
|
|
1155
1307
|
return isRetryableErrorType(error.type, DEFAULT_ERROR_CLASSIFICATION_RULE);
|
|
1156
1308
|
}
|
|
1157
1309
|
const lower = error.message.toLowerCase();
|
|
@@ -1273,6 +1425,27 @@ var RequestDeduplicator = class {
|
|
|
1273
1425
|
}
|
|
1274
1426
|
};
|
|
1275
1427
|
|
|
1428
|
+
// src/core/utils/api-key-validator.ts
|
|
1429
|
+
var DEFAULT_API_KEY_RULE = {
|
|
1430
|
+
minLength: 10
|
|
1431
|
+
};
|
|
1432
|
+
function validateApiKey(apiKey, rule = DEFAULT_API_KEY_RULE) {
|
|
1433
|
+
if (!apiKey) {
|
|
1434
|
+
return { valid: false, reason: "API key is required" };
|
|
1435
|
+
}
|
|
1436
|
+
const trimmed = apiKey.trim();
|
|
1437
|
+
if (trimmed.length === 0) {
|
|
1438
|
+
return { valid: false, reason: "API key cannot be empty" };
|
|
1439
|
+
}
|
|
1440
|
+
if (trimmed.length < rule.minLength) {
|
|
1441
|
+
return {
|
|
1442
|
+
valid: false,
|
|
1443
|
+
reason: `API key must be at least ${rule.minLength} characters`
|
|
1444
|
+
};
|
|
1445
|
+
}
|
|
1446
|
+
return { valid: true };
|
|
1447
|
+
}
|
|
1448
|
+
|
|
1276
1449
|
// src/providers/groq/groq.constants.ts
|
|
1277
1450
|
var GROQ_MODELS = {
|
|
1278
1451
|
LLAMA_3_1_8B_INSTANT: "llama-3.1-8b-instant",
|
|
@@ -1296,12 +1469,22 @@ var DEFAULT_GENERATION_CONFIG = {
|
|
|
1296
1469
|
maxTokens: 1024,
|
|
1297
1470
|
topP: 1
|
|
1298
1471
|
};
|
|
1472
|
+
var DEFAULT_MODELS = {
|
|
1473
|
+
TEXT: GROQ_MODELS.LLAMA_3_1_8B_INSTANT,
|
|
1474
|
+
FAST: GROQ_MODELS.LLAMA_3_1_8B_INSTANT,
|
|
1475
|
+
EXPERIMENTAL: GROQ_MODELS.GPT_OSS_20B
|
|
1476
|
+
};
|
|
1299
1477
|
var GROQ_BASE_URL = "https://api.groq.com/openai/v1";
|
|
1478
|
+
var DEFAULT_BASE_URL = GROQ_BASE_URL;
|
|
1300
1479
|
var CHAT_COMPLETIONS_PATH = "/chat/completions";
|
|
1480
|
+
var API_ENDPOINTS = {
|
|
1481
|
+
CHAT: CHAT_COMPLETIONS_PATH
|
|
1482
|
+
};
|
|
1301
1483
|
var GROQ_TIMEOUTS = {
|
|
1302
1484
|
DEFAULT: 3e4,
|
|
1303
1485
|
STREAMING: 6e4
|
|
1304
1486
|
};
|
|
1487
|
+
var TIMEOUTS = GROQ_TIMEOUTS;
|
|
1305
1488
|
var HTTP_HEADERS = {
|
|
1306
1489
|
CONTENT_TYPE: "Content-Type",
|
|
1307
1490
|
APPLICATION_JSON: "application/json",
|
|
@@ -1360,13 +1543,65 @@ var GroqChatRequestBuilder = class _GroqChatRequestBuilder {
|
|
|
1360
1543
|
}
|
|
1361
1544
|
};
|
|
1362
1545
|
|
|
1546
|
+
// src/providers/groq/groq-config-store.ts
|
|
1547
|
+
var GroqConfigStore = class {
|
|
1548
|
+
config = null;
|
|
1549
|
+
initialize(config) {
|
|
1550
|
+
const validation = validateApiKey(config.apiKey, { minLength: 1 });
|
|
1551
|
+
if (!validation.valid) {
|
|
1552
|
+
throw new GroqError("INVALID_API_KEY" /* INVALID_API_KEY */, validation.reason ?? "Invalid API key");
|
|
1553
|
+
}
|
|
1554
|
+
const normalized = {
|
|
1555
|
+
apiKey: config.apiKey.trim(),
|
|
1556
|
+
baseUrl: config.baseUrl ?? DEFAULT_BASE_URL,
|
|
1557
|
+
timeoutMs: config.timeoutMs ?? TIMEOUTS.DEFAULT,
|
|
1558
|
+
...config.models !== void 0 ? { models: config.models } : {},
|
|
1559
|
+
...config.textModel !== void 0 ? { textModel: config.textModel } : {}
|
|
1560
|
+
};
|
|
1561
|
+
this.config = normalized;
|
|
1562
|
+
return normalized;
|
|
1563
|
+
}
|
|
1564
|
+
get() {
|
|
1565
|
+
if (!this.config) {
|
|
1566
|
+
throw new GroqError(
|
|
1567
|
+
"MISSING_CONFIG" /* MISSING_CONFIG */,
|
|
1568
|
+
"Client not initialized. Call configureProvider first."
|
|
1569
|
+
);
|
|
1570
|
+
}
|
|
1571
|
+
return this.config;
|
|
1572
|
+
}
|
|
1573
|
+
getOrNull() {
|
|
1574
|
+
return this.config;
|
|
1575
|
+
}
|
|
1576
|
+
isInitialized() {
|
|
1577
|
+
return this.config !== null;
|
|
1578
|
+
}
|
|
1579
|
+
setApiKey(apiKey) {
|
|
1580
|
+
const current = this.get();
|
|
1581
|
+
const validation = validateApiKey(apiKey, { minLength: 1 });
|
|
1582
|
+
if (!validation.valid) {
|
|
1583
|
+
throw new GroqError("INVALID_API_KEY" /* INVALID_API_KEY */, validation.reason ?? "Invalid API key");
|
|
1584
|
+
}
|
|
1585
|
+
this.config = { ...current, apiKey: apiKey.trim() };
|
|
1586
|
+
}
|
|
1587
|
+
reset() {
|
|
1588
|
+
this.config = null;
|
|
1589
|
+
}
|
|
1590
|
+
};
|
|
1591
|
+
|
|
1363
1592
|
// src/providers/groq/groq-http-transport.ts
|
|
1364
1593
|
var GroqHttpTransport = class {
|
|
1365
|
-
constructor(
|
|
1366
|
-
this.
|
|
1594
|
+
constructor(configSource) {
|
|
1595
|
+
this.configSource = configSource;
|
|
1367
1596
|
}
|
|
1368
|
-
|
|
1597
|
+
configSource;
|
|
1369
1598
|
pendingAbortControllers = /* @__PURE__ */ new Set();
|
|
1599
|
+
getConfig() {
|
|
1600
|
+
if ("get" in this.configSource && typeof this.configSource.get === "function") {
|
|
1601
|
+
return this.configSource.get();
|
|
1602
|
+
}
|
|
1603
|
+
return this.configSource;
|
|
1604
|
+
}
|
|
1370
1605
|
cancelPendingRequests() {
|
|
1371
1606
|
for (const controller of this.pendingAbortControllers) {
|
|
1372
1607
|
controller.abort();
|
|
@@ -1381,7 +1616,7 @@ var GroqHttpTransport = class {
|
|
|
1381
1616
|
}
|
|
1382
1617
|
async streamChatCompletion(request, options = {}) {
|
|
1383
1618
|
const url = `${this.baseUrl()}${CHAT_COMPLETIONS_PATH}`;
|
|
1384
|
-
const timeout = this.
|
|
1619
|
+
const timeout = this.getConfig().timeoutMs ?? GROQ_TIMEOUTS.STREAMING;
|
|
1385
1620
|
const controller = new AbortController();
|
|
1386
1621
|
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
1387
1622
|
const onExternalAbort = () => controller.abort();
|
|
@@ -1396,7 +1631,7 @@ var GroqHttpTransport = class {
|
|
|
1396
1631
|
});
|
|
1397
1632
|
if (!response.ok) await this.throwFromErrorResponse(response);
|
|
1398
1633
|
if (!response.body) {
|
|
1399
|
-
throw new
|
|
1634
|
+
throw new AIError("NETWORK_ERROR" /* NETWORK_ERROR */, "Response body is null");
|
|
1400
1635
|
}
|
|
1401
1636
|
return response.body;
|
|
1402
1637
|
} catch (error) {
|
|
@@ -1408,11 +1643,11 @@ var GroqHttpTransport = class {
|
|
|
1408
1643
|
}
|
|
1409
1644
|
}
|
|
1410
1645
|
baseUrl() {
|
|
1411
|
-
return this.
|
|
1646
|
+
return this.getConfig().baseUrl ?? GROQ_BASE_URL;
|
|
1412
1647
|
}
|
|
1413
1648
|
async request(endpoint, body) {
|
|
1414
1649
|
const url = `${this.baseUrl()}${endpoint}`;
|
|
1415
|
-
const timeout = this.
|
|
1650
|
+
const timeout = this.getConfig().timeoutMs ?? GROQ_TIMEOUTS.DEFAULT;
|
|
1416
1651
|
const controller = new AbortController();
|
|
1417
1652
|
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
1418
1653
|
this.pendingAbortControllers.add(controller);
|
|
@@ -1435,7 +1670,7 @@ var GroqHttpTransport = class {
|
|
|
1435
1670
|
buildHeaders() {
|
|
1436
1671
|
return {
|
|
1437
1672
|
[HTTP_HEADERS.CONTENT_TYPE]: HTTP_HEADERS.APPLICATION_JSON,
|
|
1438
|
-
[HTTP_HEADERS.AUTHORIZATION]: `${HTTP_HEADERS.BEARER_PREFIX}${this.
|
|
1673
|
+
[HTTP_HEADERS.AUTHORIZATION]: `${HTTP_HEADERS.BEARER_PREFIX}${this.getConfig().apiKey}`
|
|
1439
1674
|
};
|
|
1440
1675
|
}
|
|
1441
1676
|
async throwFromErrorResponse(response) {
|
|
@@ -1444,7 +1679,7 @@ var GroqHttpTransport = class {
|
|
|
1444
1679
|
status: response.status,
|
|
1445
1680
|
url: response.url
|
|
1446
1681
|
};
|
|
1447
|
-
throw new
|
|
1682
|
+
throw new AIError(
|
|
1448
1683
|
classifyHttpStatus(response.status),
|
|
1449
1684
|
errorMessage,
|
|
1450
1685
|
void 0,
|
|
@@ -1452,24 +1687,24 @@ var GroqHttpTransport = class {
|
|
|
1452
1687
|
);
|
|
1453
1688
|
}
|
|
1454
1689
|
normalizeError(error) {
|
|
1455
|
-
if (error instanceof
|
|
1690
|
+
if (error instanceof AIError) return error;
|
|
1456
1691
|
if (error instanceof Error) {
|
|
1457
1692
|
if (error.name === "AbortError") {
|
|
1458
|
-
return new
|
|
1693
|
+
return new AIError(
|
|
1459
1694
|
"ABORT_ERROR" /* ABORT_ERROR */,
|
|
1460
1695
|
"Request was aborted by the client",
|
|
1461
1696
|
error
|
|
1462
1697
|
);
|
|
1463
1698
|
}
|
|
1464
1699
|
if (error.name === "TypeError" && error.message.toLowerCase().includes("network")) {
|
|
1465
|
-
return new
|
|
1700
|
+
return new AIError(
|
|
1466
1701
|
"NETWORK_ERROR" /* NETWORK_ERROR */,
|
|
1467
1702
|
"Network error: Unable to connect to the AI service",
|
|
1468
1703
|
error
|
|
1469
1704
|
);
|
|
1470
1705
|
}
|
|
1471
1706
|
}
|
|
1472
|
-
return new
|
|
1707
|
+
return new AIError(
|
|
1473
1708
|
"UNKNOWN_ERROR" /* UNKNOWN_ERROR */,
|
|
1474
1709
|
error instanceof Error ? error.message : "Unknown error occurred",
|
|
1475
1710
|
error instanceof Error ? error : void 0
|
|
@@ -1537,21 +1772,40 @@ var CompositeExecutor = class {
|
|
|
1537
1772
|
|
|
1538
1773
|
// src/providers/groq/groq-http-client.ts
|
|
1539
1774
|
var GroqHttpClient = class {
|
|
1775
|
+
configStore;
|
|
1540
1776
|
transport;
|
|
1541
1777
|
executor;
|
|
1542
|
-
constructor(
|
|
1543
|
-
|
|
1778
|
+
constructor(configOrDeps, maybeDeps = {}) {
|
|
1779
|
+
const isConfig = typeof configOrDeps === "object" && configOrDeps !== null && "apiKey" in configOrDeps && typeof configOrDeps.apiKey === "string";
|
|
1780
|
+
const deps = isConfig ? maybeDeps : configOrDeps ?? {};
|
|
1781
|
+
this.configStore = deps.configStore ?? new GroqConfigStore();
|
|
1782
|
+
if (isConfig) {
|
|
1783
|
+
this.configStore.initialize(configOrDeps);
|
|
1784
|
+
}
|
|
1785
|
+
this.transport = deps.transport ?? new GroqHttpTransport(this.configStore);
|
|
1544
1786
|
const retryManager2 = deps.retryManager ?? new RetryManager();
|
|
1545
1787
|
const queue = deps.queue ?? new RequestQueue();
|
|
1546
1788
|
const deduplicator = deps.deduplicator ?? new RequestDeduplicator();
|
|
1547
1789
|
this.executor = new CompositeExecutor([
|
|
1548
1790
|
new DedupeExecutor(deduplicator, {
|
|
1549
|
-
generateKey: (request) => deduplicatorKey(request,
|
|
1791
|
+
generateKey: (request) => deduplicatorKey(request, this.configStore.isInitialized() ? this.configStore.get().apiKey : "")
|
|
1550
1792
|
}),
|
|
1551
1793
|
new RetryExecutor(retryManager2),
|
|
1552
1794
|
new QueueExecutor(queue, QUEUE_PRIORITY_USER_REQUEST)
|
|
1553
1795
|
]);
|
|
1554
1796
|
}
|
|
1797
|
+
initialize(config) {
|
|
1798
|
+
this.configStore.initialize(config);
|
|
1799
|
+
}
|
|
1800
|
+
isInitialized() {
|
|
1801
|
+
return this.configStore.isInitialized();
|
|
1802
|
+
}
|
|
1803
|
+
getApiKey() {
|
|
1804
|
+
return this.configStore.get().apiKey;
|
|
1805
|
+
}
|
|
1806
|
+
setApiKey(apiKey) {
|
|
1807
|
+
this.configStore.setApiKey(apiKey);
|
|
1808
|
+
}
|
|
1555
1809
|
cancelPendingRequests() {
|
|
1556
1810
|
this.transport.cancelPendingRequests();
|
|
1557
1811
|
}
|
|
@@ -1564,6 +1818,10 @@ var GroqHttpClient = class {
|
|
|
1564
1818
|
streamChatCompletion(request, options) {
|
|
1565
1819
|
return this.transport.streamChatCompletion(request, options);
|
|
1566
1820
|
}
|
|
1821
|
+
reset() {
|
|
1822
|
+
this.transport.cancelPendingRequests();
|
|
1823
|
+
this.configStore.reset();
|
|
1824
|
+
}
|
|
1567
1825
|
};
|
|
1568
1826
|
function deduplicatorKey(request, apiKey) {
|
|
1569
1827
|
const { model, messages, stream: _stream, ...rest } = request;
|
|
@@ -1620,6 +1878,13 @@ async function consumeGroqStream(stream, callbacks) {
|
|
|
1620
1878
|
} catch {
|
|
1621
1879
|
return;
|
|
1622
1880
|
}
|
|
1881
|
+
const errorBody = chunk.error;
|
|
1882
|
+
if (errorBody) {
|
|
1883
|
+
throw new AIError(
|
|
1884
|
+
errorBody.message?.toLowerCase().includes("rate limit") ? "RATE_LIMIT_ERROR" /* RATE_LIMIT_ERROR */ : "SERVER_ERROR" /* SERVER_ERROR */,
|
|
1885
|
+
errorBody.message || "Groq stream failed mid-flight"
|
|
1886
|
+
);
|
|
1887
|
+
}
|
|
1623
1888
|
if (chunk.model) model = chunk.model;
|
|
1624
1889
|
const choice = chunk.choices?.[0];
|
|
1625
1890
|
const delta = choice?.delta?.content;
|
|
@@ -1634,13 +1899,6 @@ async function consumeGroqStream(stream, callbacks) {
|
|
|
1634
1899
|
if (tailUsage) {
|
|
1635
1900
|
usage = mapGroqUsage(tailUsage);
|
|
1636
1901
|
}
|
|
1637
|
-
const errorBody = chunk.error;
|
|
1638
|
-
if (errorBody) {
|
|
1639
|
-
throw new AIError2(
|
|
1640
|
-
"SERVER_ERROR" /* SERVER_ERROR */,
|
|
1641
|
-
errorBody.message || "Groq stream failed mid-flight"
|
|
1642
|
-
);
|
|
1643
|
-
}
|
|
1644
1902
|
};
|
|
1645
1903
|
try {
|
|
1646
1904
|
while (true) {
|
|
@@ -1654,8 +1912,8 @@ async function consumeGroqStream(stream, callbacks) {
|
|
|
1654
1912
|
buffer += decoder.decode();
|
|
1655
1913
|
if (buffer.length > 0) consumeLine(buffer);
|
|
1656
1914
|
} catch (error) {
|
|
1657
|
-
if (error instanceof
|
|
1658
|
-
throw new
|
|
1915
|
+
if (error instanceof AIError) throw error;
|
|
1916
|
+
throw new AIError(
|
|
1659
1917
|
"NETWORK_ERROR" /* NETWORK_ERROR */,
|
|
1660
1918
|
"Stream was interrupted",
|
|
1661
1919
|
error instanceof Error ? error : void 0
|
|
@@ -1713,7 +1971,7 @@ var GroqProvider = class {
|
|
|
1713
1971
|
const choice = response.choices?.[0];
|
|
1714
1972
|
const text = choice?.message?.content ?? "";
|
|
1715
1973
|
if (text.length === 0) {
|
|
1716
|
-
throw new
|
|
1974
|
+
throw new AIError(
|
|
1717
1975
|
"SERVER_ERROR" /* SERVER_ERROR */,
|
|
1718
1976
|
"No content generated from Groq API",
|
|
1719
1977
|
void 0,
|
|
@@ -1735,7 +1993,7 @@ var GroqProvider = class {
|
|
|
1735
1993
|
const messages = toGroqMessages(request.messages, request.systemPrompt);
|
|
1736
1994
|
const lastUserIndex = findLastIndex2(messages, (m) => m.role === "user");
|
|
1737
1995
|
if (lastUserIndex === -1) {
|
|
1738
|
-
throw new
|
|
1996
|
+
throw new AIError(
|
|
1739
1997
|
"UNKNOWN_ERROR" /* UNKNOWN_ERROR */,
|
|
1740
1998
|
"generateStructured requires at least one user message"
|
|
1741
1999
|
);
|
|
@@ -1749,7 +2007,7 @@ var GroqProvider = class {
|
|
|
1749
2007
|
const choice = response.choices?.[0];
|
|
1750
2008
|
const text = choice?.message?.content ?? "";
|
|
1751
2009
|
if (text.length === 0) {
|
|
1752
|
-
throw new
|
|
2010
|
+
throw new AIError(
|
|
1753
2011
|
"SERVER_ERROR" /* SERVER_ERROR */,
|
|
1754
2012
|
"No content generated from Groq API",
|
|
1755
2013
|
void 0,
|
|
@@ -1776,7 +2034,7 @@ var GroqProvider = class {
|
|
|
1776
2034
|
try {
|
|
1777
2035
|
const outcome = await consumeGroqStream(streamBody, callbacks);
|
|
1778
2036
|
if (outcome.text.length === 0) {
|
|
1779
|
-
throw new
|
|
2037
|
+
throw new AIError(
|
|
1780
2038
|
"SERVER_ERROR" /* SERVER_ERROR */,
|
|
1781
2039
|
"No content generated from Groq API",
|
|
1782
2040
|
void 0,
|
|
@@ -1907,7 +2165,9 @@ var GeminiProvider = class extends BaseProvider {
|
|
|
1907
2165
|
headers: this.authHeaders()
|
|
1908
2166
|
}
|
|
1909
2167
|
);
|
|
1910
|
-
|
|
2168
|
+
if (response.ok) return "healthy";
|
|
2169
|
+
if (response.status === 401 || response.status === 403) return "unavailable";
|
|
2170
|
+
return "degraded";
|
|
1911
2171
|
} catch {
|
|
1912
2172
|
return "unavailable";
|
|
1913
2173
|
}
|
|
@@ -1918,6 +2178,8 @@ var GeminiProvider = class extends BaseProvider {
|
|
|
1918
2178
|
async generate(request) {
|
|
1919
2179
|
try {
|
|
1920
2180
|
const model = request.model ?? this.resolveModel(request.tier ?? "balanced");
|
|
2181
|
+
const systemMessages = request.messages.filter((m) => m.role === "system");
|
|
2182
|
+
const systemPrompt = request.systemPrompt && request.systemPrompt.length > 0 ? request.systemPrompt : systemMessages.length > 0 ? systemMessages.map((m) => m.content).join("\n") : void 0;
|
|
1921
2183
|
const response = await this.withRetry(async () => {
|
|
1922
2184
|
return await this.fetchWithTimeout(
|
|
1923
2185
|
`${this.config.baseUrl}/models/${model}:generateContent`,
|
|
@@ -1925,24 +2187,30 @@ var GeminiProvider = class extends BaseProvider {
|
|
|
1925
2187
|
method: "POST",
|
|
1926
2188
|
headers: this.authHeaders(),
|
|
1927
2189
|
body: JSON.stringify({
|
|
1928
|
-
systemInstruction:
|
|
2190
|
+
systemInstruction: systemPrompt && systemPrompt.length > 0 ? { parts: [{ text: systemPrompt }] } : void 0,
|
|
1929
2191
|
contents: toGeminiContents(request.messages),
|
|
1930
2192
|
generationConfig: {
|
|
1931
|
-
temperature: request.temperature
|
|
1932
|
-
maxOutputTokens: request.maxTokens
|
|
1933
|
-
topP: request.topP
|
|
2193
|
+
temperature: request.temperature ?? 0.7,
|
|
2194
|
+
maxOutputTokens: request.maxTokens ?? 2048,
|
|
2195
|
+
topP: request.topP ?? 0.9
|
|
1934
2196
|
}
|
|
1935
2197
|
})
|
|
1936
2198
|
}
|
|
1937
2199
|
);
|
|
1938
2200
|
});
|
|
1939
2201
|
if (!response.ok) {
|
|
1940
|
-
|
|
2202
|
+
const message = await extractErrorMessageFromResponse(response);
|
|
2203
|
+
throw new AIError(
|
|
2204
|
+
classifyHttpStatus(response.status),
|
|
2205
|
+
`Gemini API error (${response.status}): ${message}`,
|
|
2206
|
+
void 0,
|
|
2207
|
+
{ status: response.status, model }
|
|
2208
|
+
);
|
|
1941
2209
|
}
|
|
1942
2210
|
const data = await response.json();
|
|
1943
|
-
const text = data.candidates[0]?.content?.parts?.map((part) => part.text ?? "").join("") ?? "";
|
|
2211
|
+
const text = data.candidates?.[0]?.content?.parts?.map((part) => part.text ?? "").join("") ?? "";
|
|
1944
2212
|
if (text.length === 0) {
|
|
1945
|
-
throw new
|
|
2213
|
+
throw new AIError("SERVER_ERROR" /* SERVER_ERROR */, "No content generated from Gemini API", void 0, { model });
|
|
1946
2214
|
}
|
|
1947
2215
|
return {
|
|
1948
2216
|
text,
|
|
@@ -1953,11 +2221,11 @@ var GeminiProvider = class extends BaseProvider {
|
|
|
1953
2221
|
} : void 0,
|
|
1954
2222
|
model,
|
|
1955
2223
|
providerId: this.id,
|
|
1956
|
-
finishReason: mapGeminiFinishReason(data.candidates[0]?.finishReason)
|
|
2224
|
+
finishReason: mapGeminiFinishReason(data.candidates?.[0]?.finishReason)
|
|
1957
2225
|
};
|
|
1958
2226
|
} catch (error) {
|
|
1959
|
-
if (error instanceof
|
|
1960
|
-
throw new
|
|
2227
|
+
if (error instanceof AIError) throw error;
|
|
2228
|
+
throw new AIError(
|
|
1961
2229
|
"UNKNOWN_ERROR" /* UNKNOWN_ERROR */,
|
|
1962
2230
|
`Gemini text generation failed: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
1963
2231
|
error instanceof Error ? error : void 0
|
|
@@ -1968,7 +2236,7 @@ var GeminiProvider = class extends BaseProvider {
|
|
|
1968
2236
|
const messages = [...request.messages];
|
|
1969
2237
|
const lastUserIndex = findLastIndex3(messages, (m) => m.role === "user");
|
|
1970
2238
|
if (lastUserIndex === -1) {
|
|
1971
|
-
throw new
|
|
2239
|
+
throw new AIError(
|
|
1972
2240
|
"UNKNOWN_ERROR" /* UNKNOWN_ERROR */,
|
|
1973
2241
|
"generateStructured requires at least one user message"
|
|
1974
2242
|
);
|
|
@@ -2005,12 +2273,18 @@ var GeminiProvider = class extends BaseProvider {
|
|
|
2005
2273
|
);
|
|
2006
2274
|
});
|
|
2007
2275
|
if (!response.ok) {
|
|
2008
|
-
|
|
2276
|
+
const message = await extractErrorMessageFromResponse(response);
|
|
2277
|
+
throw new AIError(
|
|
2278
|
+
classifyHttpStatus(response.status),
|
|
2279
|
+
`Gemini image generation error (${response.status}): ${message}`,
|
|
2280
|
+
void 0,
|
|
2281
|
+
{ status: response.status }
|
|
2282
|
+
);
|
|
2009
2283
|
}
|
|
2010
2284
|
const data = await response.json();
|
|
2011
|
-
const imageData = data.candidates[0]?.content?.parts[0]?.inlineData;
|
|
2285
|
+
const imageData = data.candidates?.[0]?.content?.parts?.[0]?.inlineData;
|
|
2012
2286
|
if (!imageData) {
|
|
2013
|
-
throw new
|
|
2287
|
+
throw new AIError("SERVER_ERROR" /* SERVER_ERROR */, "No image data in Gemini response");
|
|
2014
2288
|
}
|
|
2015
2289
|
const url = `data:${imageData.mimeType};base64,${imageData.data}`;
|
|
2016
2290
|
return {
|
|
@@ -2329,17 +2603,70 @@ var PrunaProvider = class {
|
|
|
2329
2603
|
async estimateCost(_request) {
|
|
2330
2604
|
return 1;
|
|
2331
2605
|
}
|
|
2332
|
-
async generateImage(
|
|
2333
|
-
|
|
2606
|
+
async generateImage(request) {
|
|
2607
|
+
let finalUrl;
|
|
2608
|
+
const model = this.config.models?.textToImage;
|
|
2609
|
+
for await (const stage of generateWithPruna(
|
|
2610
|
+
{ prompt: request.prompt, model, quality: request.quality },
|
|
2611
|
+
this.config.apiKey
|
|
2612
|
+
)) {
|
|
2613
|
+
if (stage.error) throw stage.error;
|
|
2614
|
+
if (stage.result?.url) finalUrl = stage.result.url;
|
|
2615
|
+
}
|
|
2616
|
+
if (!finalUrl) {
|
|
2617
|
+
throw new AIError("SERVER_ERROR" /* SERVER_ERROR */, "No image generated by Pruna AI");
|
|
2618
|
+
}
|
|
2619
|
+
return {
|
|
2620
|
+
id: Date.now().toString(),
|
|
2621
|
+
type: "image",
|
|
2622
|
+
url: finalUrl,
|
|
2623
|
+
metadata: { provider: this.id, model: model ?? "p-image" },
|
|
2624
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2625
|
+
};
|
|
2334
2626
|
}
|
|
2335
|
-
async generateVideo(
|
|
2336
|
-
|
|
2627
|
+
async generateVideo(request) {
|
|
2628
|
+
let finalUrl;
|
|
2629
|
+
const model = this.config.models?.textToVideo;
|
|
2630
|
+
for await (const stage of generateWithPruna(
|
|
2631
|
+
{ prompt: request.prompt, model, duration: request.duration, aspectRatio: request.aspectRatio },
|
|
2632
|
+
this.config.apiKey
|
|
2633
|
+
)) {
|
|
2634
|
+
if (stage.error) throw stage.error;
|
|
2635
|
+
if (stage.result?.url) finalUrl = stage.result.url;
|
|
2636
|
+
}
|
|
2637
|
+
if (!finalUrl) {
|
|
2638
|
+
throw new AIError("SERVER_ERROR" /* SERVER_ERROR */, "No video generated by Pruna AI");
|
|
2639
|
+
}
|
|
2640
|
+
return {
|
|
2641
|
+
id: Date.now().toString(),
|
|
2642
|
+
type: "video",
|
|
2643
|
+
url: finalUrl,
|
|
2644
|
+
metadata: { provider: this.id, model: model ?? "p-video" },
|
|
2645
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2646
|
+
};
|
|
2337
2647
|
}
|
|
2338
|
-
async imageToVideo(
|
|
2339
|
-
|
|
2648
|
+
async imageToVideo(request) {
|
|
2649
|
+
let finalUrl;
|
|
2650
|
+
for await (const stage of generateWithPruna(
|
|
2651
|
+
{ image: request.imageUrl, prompt: request.prompt, duration: request.duration, motion: request.motion },
|
|
2652
|
+
this.config.apiKey
|
|
2653
|
+
)) {
|
|
2654
|
+
if (stage.error) throw stage.error;
|
|
2655
|
+
if (stage.result?.url) finalUrl = stage.result.url;
|
|
2656
|
+
}
|
|
2657
|
+
if (!finalUrl) {
|
|
2658
|
+
throw new AIError("SERVER_ERROR" /* SERVER_ERROR */, "No video generated from image by Pruna AI");
|
|
2659
|
+
}
|
|
2660
|
+
return {
|
|
2661
|
+
id: Date.now().toString(),
|
|
2662
|
+
type: "video",
|
|
2663
|
+
url: finalUrl,
|
|
2664
|
+
metadata: { provider: this.id, model: "p-image-to-video" },
|
|
2665
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2666
|
+
};
|
|
2340
2667
|
}
|
|
2341
2668
|
async videoToVideo(_request) {
|
|
2342
|
-
throw new
|
|
2669
|
+
throw new AIError("CLIENT_ERROR" /* CLIENT_ERROR */, "Pruna AI provider does not support video-to-video conversion");
|
|
2343
2670
|
}
|
|
2344
2671
|
};
|
|
2345
2672
|
function createPrunaProvider(config) {
|
|
@@ -2371,6 +2698,504 @@ function createAI(config = {}) {
|
|
|
2371
2698
|
return factory;
|
|
2372
2699
|
}
|
|
2373
2700
|
|
|
2374
|
-
|
|
2701
|
+
// src/providers/groq/groq-messages.ts
|
|
2702
|
+
var createUserMessage = (content) => ({
|
|
2703
|
+
role: "user",
|
|
2704
|
+
content
|
|
2705
|
+
});
|
|
2706
|
+
var createAssistantMessage = (content) => ({
|
|
2707
|
+
role: "assistant",
|
|
2708
|
+
content
|
|
2709
|
+
});
|
|
2710
|
+
var createSystemMessage = (content) => ({
|
|
2711
|
+
role: "system",
|
|
2712
|
+
content
|
|
2713
|
+
});
|
|
2714
|
+
var createMessage = (role, content) => ({
|
|
2715
|
+
role,
|
|
2716
|
+
content
|
|
2717
|
+
});
|
|
2718
|
+
var promptToMessages = (prompt) => [
|
|
2719
|
+
createUserMessage(prompt)
|
|
2720
|
+
];
|
|
2721
|
+
var extractTextFromMessages = (messages) => messages.map((m) => m.content).join("\n\n");
|
|
2722
|
+
var formatMessagesForDisplay = (messages) => messages.map((m) => `[${m.role.toUpperCase()}]: ${m.content}`).join("\n\n---\n\n");
|
|
2723
|
+
|
|
2724
|
+
// src/providers/groq/groq-structured-json.ts
|
|
2725
|
+
async function requestStructuredJson(client, request) {
|
|
2726
|
+
const chatRequest = {
|
|
2727
|
+
model: request.model,
|
|
2728
|
+
messages: [createUserMessage(request.instructions)],
|
|
2729
|
+
temperature: request.temperature,
|
|
2730
|
+
max_tokens: request.maxTokens,
|
|
2731
|
+
response_format: { type: "json_object" },
|
|
2732
|
+
...request.reasoningEffort !== void 0 ? { reasoning_effort: request.reasoningEffort } : {}
|
|
2733
|
+
};
|
|
2734
|
+
const response = await client.postChatCompletion(chatRequest);
|
|
2735
|
+
const content = response.choices[0]?.message?.content ?? "";
|
|
2736
|
+
const candidate = extractJsonCandidate(content);
|
|
2737
|
+
try {
|
|
2738
|
+
return JSON.parse(candidate);
|
|
2739
|
+
} catch (err) {
|
|
2740
|
+
throw new GroqError(
|
|
2741
|
+
"UNKNOWN_ERROR" /* UNKNOWN_ERROR */,
|
|
2742
|
+
"model response contained no parsable json object",
|
|
2743
|
+
err instanceof Error ? err : void 0
|
|
2744
|
+
);
|
|
2745
|
+
}
|
|
2746
|
+
}
|
|
2747
|
+
|
|
2748
|
+
// src/providers/groq/text-generation.service.ts
|
|
2749
|
+
var STRUCTURED_OUTPUT_TEMPERATURE = 0.1;
|
|
2750
|
+
var TextGenerationService = class {
|
|
2751
|
+
constructor(httpClient, cache = new CacheManager(), defaultModel = DEFAULT_MODELS.TEXT) {
|
|
2752
|
+
this.httpClient = httpClient;
|
|
2753
|
+
this.cache = cache;
|
|
2754
|
+
this.defaultModel = defaultModel;
|
|
2755
|
+
}
|
|
2756
|
+
httpClient;
|
|
2757
|
+
cache;
|
|
2758
|
+
defaultModel;
|
|
2759
|
+
id = "groq-text-generation";
|
|
2760
|
+
name = "Groq Text Generation";
|
|
2761
|
+
capabilities = ["text"];
|
|
2762
|
+
jsonParser = new JsonResponseParser();
|
|
2763
|
+
generateCompletion(prompt, options = {}) {
|
|
2764
|
+
const model = options.model ?? this.defaultModel;
|
|
2765
|
+
return this.executeWithCache(
|
|
2766
|
+
{
|
|
2767
|
+
type: CACHE_KEY_TYPE_COMPLETION,
|
|
2768
|
+
model,
|
|
2769
|
+
prompt,
|
|
2770
|
+
config: options.generationConfig
|
|
2771
|
+
},
|
|
2772
|
+
async () => {
|
|
2773
|
+
const request = GroqChatRequestBuilder.for(model).withMessages([createUserMessage(prompt)]).withConfig(options.generationConfig).build();
|
|
2774
|
+
const response = await this.httpClient.postChatCompletion(request);
|
|
2775
|
+
return this.extractContent(response.choices[0]?.message?.content);
|
|
2776
|
+
}
|
|
2777
|
+
);
|
|
2778
|
+
}
|
|
2779
|
+
generateChatCompletion(messages, options = {}) {
|
|
2780
|
+
const model = options.model ?? this.defaultModel;
|
|
2781
|
+
return this.executeWithCache(
|
|
2782
|
+
{
|
|
2783
|
+
type: CACHE_KEY_TYPE_CHAT,
|
|
2784
|
+
model,
|
|
2785
|
+
messages: [...messages],
|
|
2786
|
+
config: options.generationConfig
|
|
2787
|
+
},
|
|
2788
|
+
async () => {
|
|
2789
|
+
const request = GroqChatRequestBuilder.for(model).withMessages(messages).withConfig(options.generationConfig).build();
|
|
2790
|
+
const response = await this.httpClient.postChatCompletion(request);
|
|
2791
|
+
return this.extractContent(response.choices[0]?.message?.content);
|
|
2792
|
+
}
|
|
2793
|
+
);
|
|
2794
|
+
}
|
|
2795
|
+
generateStructured(promptOrRequest, options) {
|
|
2796
|
+
if (typeof promptOrRequest === "string") {
|
|
2797
|
+
const model2 = options?.model ?? this.defaultModel;
|
|
2798
|
+
const jsonPrompt = StructuredPromptBuilder.for(promptOrRequest).withSchema(options?.schema).build();
|
|
2799
|
+
const request = GroqChatRequestBuilder.for(model2).withMessages([createUserMessage(jsonPrompt)]).withConfig({
|
|
2800
|
+
...options?.generationConfig,
|
|
2801
|
+
temperature: STRUCTURED_OUTPUT_TEMPERATURE
|
|
2802
|
+
}).build();
|
|
2803
|
+
return this.httpClient.postChatCompletion(request).then((response) => {
|
|
2804
|
+
const content = this.extractContent(response.choices[0]?.message?.content);
|
|
2805
|
+
return this.jsonParser.parse(content);
|
|
2806
|
+
});
|
|
2807
|
+
}
|
|
2808
|
+
const req = promptOrRequest;
|
|
2809
|
+
const model = req.model ?? (req.tier ? this.resolveModel(req.tier) : this.defaultModel);
|
|
2810
|
+
const schemaObj = req.schema;
|
|
2811
|
+
const messages = [];
|
|
2812
|
+
if (req.systemPrompt) {
|
|
2813
|
+
messages.push({ role: "system", content: req.systemPrompt });
|
|
2814
|
+
}
|
|
2815
|
+
for (const m of req.messages) {
|
|
2816
|
+
messages.push({ role: m.role, content: m.content });
|
|
2817
|
+
}
|
|
2818
|
+
const lastMsg = messages[messages.length - 1];
|
|
2819
|
+
if (lastMsg) {
|
|
2820
|
+
const structuredPrompt = StructuredPromptBuilder.for(lastMsg.content).withSchema(schemaObj).build();
|
|
2821
|
+
messages[messages.length - 1] = { ...lastMsg, content: structuredPrompt };
|
|
2822
|
+
}
|
|
2823
|
+
const groqReq = GroqChatRequestBuilder.for(model).withMessages(messages).withConfig({
|
|
2824
|
+
temperature: req.temperature ?? STRUCTURED_OUTPUT_TEMPERATURE,
|
|
2825
|
+
maxTokens: req.maxTokens
|
|
2826
|
+
}).build();
|
|
2827
|
+
return this.httpClient.postChatCompletion(groqReq).then((response) => {
|
|
2828
|
+
const content = this.extractContent(response.choices[0]?.message?.content);
|
|
2829
|
+
const parsed = this.jsonParser.parse(content);
|
|
2830
|
+
const genResult = {
|
|
2831
|
+
text: content,
|
|
2832
|
+
model: response.model,
|
|
2833
|
+
providerId: this.id,
|
|
2834
|
+
finishReason: response.choices[0]?.finish_reason ?? "stop",
|
|
2835
|
+
usage: response.usage ? {
|
|
2836
|
+
inputTokens: response.usage.prompt_tokens,
|
|
2837
|
+
outputTokens: response.usage.completion_tokens,
|
|
2838
|
+
totalTokens: response.usage.total_tokens
|
|
2839
|
+
} : void 0
|
|
2840
|
+
};
|
|
2841
|
+
return { value: parsed, result: genResult };
|
|
2842
|
+
});
|
|
2843
|
+
}
|
|
2844
|
+
streamCompletion(prompt, callbacks, options = {}) {
|
|
2845
|
+
return this.streamChatCompletion([createUserMessage(prompt)], callbacks, options);
|
|
2846
|
+
}
|
|
2847
|
+
async streamChatCompletion(messages, callbacks, options = {}) {
|
|
2848
|
+
const model = options.model ?? this.defaultModel;
|
|
2849
|
+
const request = GroqChatRequestBuilder.for(model).withMessages(messages).withConfig(options.generationConfig).withStream(true).build();
|
|
2850
|
+
const parser = new StreamingResponseParser(
|
|
2851
|
+
{
|
|
2852
|
+
onChunk: (chunk) => callbacks.onChunk?.(chunk)
|
|
2853
|
+
},
|
|
2854
|
+
extractContentFromSsePayload
|
|
2855
|
+
);
|
|
2856
|
+
try {
|
|
2857
|
+
const stream = await this.httpClient.streamChatCompletion(request, {
|
|
2858
|
+
signal: options.signal
|
|
2859
|
+
});
|
|
2860
|
+
const fullText = await parser.parse(stream);
|
|
2861
|
+
callbacks.onComplete?.(fullText);
|
|
2862
|
+
return fullText;
|
|
2863
|
+
} catch (error) {
|
|
2864
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
2865
|
+
callbacks.onError?.(err);
|
|
2866
|
+
throw err;
|
|
2867
|
+
}
|
|
2868
|
+
}
|
|
2869
|
+
clearCache() {
|
|
2870
|
+
this.cache.clear();
|
|
2871
|
+
}
|
|
2872
|
+
// --- ITextProvider implementation ---
|
|
2873
|
+
resolveModel(tier) {
|
|
2874
|
+
return GROQ_TIERS[tier] ?? this.defaultModel;
|
|
2875
|
+
}
|
|
2876
|
+
async generate(request) {
|
|
2877
|
+
const messages = [];
|
|
2878
|
+
if (request.systemPrompt) {
|
|
2879
|
+
messages.push({ role: "system", content: request.systemPrompt });
|
|
2880
|
+
}
|
|
2881
|
+
for (const m of request.messages) {
|
|
2882
|
+
messages.push({ role: m.role, content: m.content });
|
|
2883
|
+
}
|
|
2884
|
+
const model = request.model ?? (request.tier ? this.resolveModel(request.tier) : this.defaultModel);
|
|
2885
|
+
const groqReq = GroqChatRequestBuilder.for(model).withMessages(messages).withConfig({
|
|
2886
|
+
temperature: request.temperature,
|
|
2887
|
+
maxTokens: request.maxTokens
|
|
2888
|
+
}).build();
|
|
2889
|
+
const resp = await this.httpClient.postChatCompletion(groqReq);
|
|
2890
|
+
const content = resp.choices[0]?.message?.content ?? "";
|
|
2891
|
+
return {
|
|
2892
|
+
text: content,
|
|
2893
|
+
finishReason: resp.choices[0]?.finish_reason ?? "stop",
|
|
2894
|
+
model: resp.model,
|
|
2895
|
+
providerId: this.id,
|
|
2896
|
+
usage: resp.usage ? {
|
|
2897
|
+
inputTokens: resp.usage.prompt_tokens,
|
|
2898
|
+
outputTokens: resp.usage.completion_tokens,
|
|
2899
|
+
totalTokens: resp.usage.total_tokens
|
|
2900
|
+
} : void 0
|
|
2901
|
+
};
|
|
2902
|
+
}
|
|
2903
|
+
async stream(request, callbacks) {
|
|
2904
|
+
const messages = [];
|
|
2905
|
+
if (request.systemPrompt) {
|
|
2906
|
+
messages.push({ role: "system", content: request.systemPrompt });
|
|
2907
|
+
}
|
|
2908
|
+
for (const m of request.messages) {
|
|
2909
|
+
messages.push({ role: m.role, content: m.content });
|
|
2910
|
+
}
|
|
2911
|
+
const model = request.model ?? (request.tier ? this.resolveModel(request.tier) : this.defaultModel);
|
|
2912
|
+
const text = await this.streamChatCompletion(
|
|
2913
|
+
messages,
|
|
2914
|
+
{
|
|
2915
|
+
onChunk: callbacks.onChunk,
|
|
2916
|
+
onError: callbacks.onError
|
|
2917
|
+
},
|
|
2918
|
+
{
|
|
2919
|
+
model,
|
|
2920
|
+
signal: request.signal,
|
|
2921
|
+
generationConfig: {
|
|
2922
|
+
temperature: request.temperature,
|
|
2923
|
+
maxTokens: request.maxTokens
|
|
2924
|
+
}
|
|
2925
|
+
}
|
|
2926
|
+
);
|
|
2927
|
+
const result = {
|
|
2928
|
+
text,
|
|
2929
|
+
finishReason: "stop",
|
|
2930
|
+
model,
|
|
2931
|
+
providerId: this.id
|
|
2932
|
+
};
|
|
2933
|
+
callbacks.onComplete?.(result);
|
|
2934
|
+
return result;
|
|
2935
|
+
}
|
|
2936
|
+
async countTokens(text) {
|
|
2937
|
+
return Math.max(1, Math.ceil(text.length / 4));
|
|
2938
|
+
}
|
|
2939
|
+
async estimateCost(_request) {
|
|
2940
|
+
return 0;
|
|
2941
|
+
}
|
|
2942
|
+
async healthCheck() {
|
|
2943
|
+
try {
|
|
2944
|
+
await this.generateCompletion("ping", {
|
|
2945
|
+
generationConfig: { maxTokens: 1 }
|
|
2946
|
+
});
|
|
2947
|
+
return "healthy";
|
|
2948
|
+
} catch {
|
|
2949
|
+
return "unavailable";
|
|
2950
|
+
}
|
|
2951
|
+
}
|
|
2952
|
+
async executeWithCache(params, factory) {
|
|
2953
|
+
const key = this.cache.generateKey(params);
|
|
2954
|
+
const cached = this.cache.get(key);
|
|
2955
|
+
if (cached !== null) {
|
|
2956
|
+
return cached;
|
|
2957
|
+
}
|
|
2958
|
+
const result = await factory();
|
|
2959
|
+
this.cache.set(key, result);
|
|
2960
|
+
return result;
|
|
2961
|
+
}
|
|
2962
|
+
extractContent(content) {
|
|
2963
|
+
if (!content) {
|
|
2964
|
+
throw new GroqError(
|
|
2965
|
+
"UNKNOWN_ERROR" /* UNKNOWN_ERROR */,
|
|
2966
|
+
"No content generated from Groq API"
|
|
2967
|
+
);
|
|
2968
|
+
}
|
|
2969
|
+
return content;
|
|
2970
|
+
}
|
|
2971
|
+
};
|
|
2972
|
+
|
|
2973
|
+
// src/core/calculations/random-base36-suffix.ts
|
|
2974
|
+
var BASE_36_RADIX = 36;
|
|
2975
|
+
var DECIMAL_OFFSET = 2;
|
|
2976
|
+
function generateRandomBase36Suffix(length, random = Math.random) {
|
|
2977
|
+
if (length <= 0) return "";
|
|
2978
|
+
const raw = random().toString(BASE_36_RADIX).slice(DECIMAL_OFFSET);
|
|
2979
|
+
if (raw.length >= length) return raw.slice(0, length);
|
|
2980
|
+
let result = raw;
|
|
2981
|
+
while (result.length < length) {
|
|
2982
|
+
result += random().toString(BASE_36_RADIX).slice(DECIMAL_OFFSET);
|
|
2983
|
+
}
|
|
2984
|
+
return result.slice(0, length);
|
|
2985
|
+
}
|
|
2986
|
+
|
|
2987
|
+
// src/chat/utils/chat-id-generator.ts
|
|
2988
|
+
var ID_PART_DELIMITER = "-";
|
|
2989
|
+
var ChatIdGenerator = class {
|
|
2990
|
+
counter = 0;
|
|
2991
|
+
randomLength;
|
|
2992
|
+
constructor(randomLength = CHAT_MESSAGE_ID_SUFFIX_LENGTH) {
|
|
2993
|
+
this.randomLength = randomLength;
|
|
2994
|
+
}
|
|
2995
|
+
next(sender) {
|
|
2996
|
+
this.counter += 1;
|
|
2997
|
+
const random = generateRandomBase36Suffix(this.randomLength);
|
|
2998
|
+
return [
|
|
2999
|
+
MESSAGE_ID_PREFIX,
|
|
3000
|
+
Date.now(),
|
|
3001
|
+
sender,
|
|
3002
|
+
this.counter,
|
|
3003
|
+
random
|
|
3004
|
+
].join(ID_PART_DELIMITER);
|
|
3005
|
+
}
|
|
3006
|
+
};
|
|
3007
|
+
var chatIdGenerator = new ChatIdGenerator();
|
|
3008
|
+
|
|
3009
|
+
// src/chat/utils/message-formatter.ts
|
|
3010
|
+
var MessageFormatter = class {
|
|
3011
|
+
constructor(idGenerator = chatIdGenerator) {
|
|
3012
|
+
this.idGenerator = idGenerator;
|
|
3013
|
+
}
|
|
3014
|
+
idGenerator;
|
|
3015
|
+
/** Maps chat-domain history to neutral core messages; system senders are
|
|
3016
|
+
* dropped — the system prompt travels on the request's own slot. */
|
|
3017
|
+
toCoreMessages(messages) {
|
|
3018
|
+
const coreMessages = [];
|
|
3019
|
+
for (const message of messages) {
|
|
3020
|
+
if (message.sender === "system") continue;
|
|
3021
|
+
coreMessages.push({
|
|
3022
|
+
role: message.sender === "user" ? USER_ROLE : ASSISTANT_ROLE,
|
|
3023
|
+
content: message.content
|
|
3024
|
+
});
|
|
3025
|
+
}
|
|
3026
|
+
return coreMessages;
|
|
3027
|
+
}
|
|
3028
|
+
toChatMessage(content, sender) {
|
|
3029
|
+
return {
|
|
3030
|
+
id: this.idGenerator.next(sender),
|
|
3031
|
+
sender,
|
|
3032
|
+
content: content.trim(),
|
|
3033
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3034
|
+
type: "text"
|
|
3035
|
+
};
|
|
3036
|
+
}
|
|
3037
|
+
};
|
|
3038
|
+
var messageFormatter = new MessageFormatter();
|
|
3039
|
+
|
|
3040
|
+
// src/chat/utils/prompt-strategy.ts
|
|
3041
|
+
var DEFAULT_PROMPT_STRATEGY = {
|
|
3042
|
+
resolve: (config) => {
|
|
3043
|
+
if (config.systemPrompt && config.systemPrompt.length > 0) {
|
|
3044
|
+
return config.systemPrompt;
|
|
3045
|
+
}
|
|
3046
|
+
return void 0;
|
|
3047
|
+
}
|
|
3048
|
+
};
|
|
3049
|
+
|
|
3050
|
+
// src/chat/services/chat.service.ts
|
|
3051
|
+
var DEFAULT_CONFIG = {
|
|
3052
|
+
temperature: 0.8,
|
|
3053
|
+
maxTokens: 500
|
|
3054
|
+
};
|
|
3055
|
+
var ChatService = class {
|
|
3056
|
+
constructor(engine) {
|
|
3057
|
+
this.engine = engine;
|
|
3058
|
+
}
|
|
3059
|
+
engine;
|
|
3060
|
+
config = DEFAULT_CONFIG;
|
|
3061
|
+
promptStrategy = DEFAULT_PROMPT_STRATEGY;
|
|
3062
|
+
storage = null;
|
|
3063
|
+
initialize(config) {
|
|
3064
|
+
this.config = { ...DEFAULT_CONFIG, ...config };
|
|
3065
|
+
}
|
|
3066
|
+
updateConfig(config) {
|
|
3067
|
+
this.config = { ...this.config, ...config };
|
|
3068
|
+
}
|
|
3069
|
+
setPromptStrategy(strategy) {
|
|
3070
|
+
this.promptStrategy = strategy;
|
|
3071
|
+
}
|
|
3072
|
+
setStorage(storage) {
|
|
3073
|
+
this.storage = storage;
|
|
3074
|
+
}
|
|
3075
|
+
async getConversation(conversationId) {
|
|
3076
|
+
const storage = this.requireStorage();
|
|
3077
|
+
const messages = await storage.getMessages(conversationId);
|
|
3078
|
+
if (messages.length === 0) return null;
|
|
3079
|
+
const last = messages[messages.length - 1];
|
|
3080
|
+
const first = messages[0];
|
|
3081
|
+
const companionId = String(first.metadata?.["companionId"] ?? "unknown");
|
|
3082
|
+
const companionName = String(first.metadata?.["companionName"] ?? "Unknown");
|
|
3083
|
+
return {
|
|
3084
|
+
id: conversationId,
|
|
3085
|
+
companionId,
|
|
3086
|
+
companionName,
|
|
3087
|
+
lastMessage: last,
|
|
3088
|
+
messageCount: messages.length,
|
|
3089
|
+
updatedAt: last.timestamp
|
|
3090
|
+
};
|
|
3091
|
+
}
|
|
3092
|
+
async getConversations() {
|
|
3093
|
+
if (this.storage && "getConversations" in this.storage && typeof this.storage.getConversations === "function") {
|
|
3094
|
+
return this.storage.getConversations();
|
|
3095
|
+
}
|
|
3096
|
+
return [];
|
|
3097
|
+
}
|
|
3098
|
+
async sendMessage(input, signal) {
|
|
3099
|
+
const userMessage = {
|
|
3100
|
+
...messageFormatter.toChatMessage(input.text, "user"),
|
|
3101
|
+
type: input.type ?? "text",
|
|
3102
|
+
...input.imageUrl !== void 0 ? { imageUrl: input.imageUrl } : {}
|
|
3103
|
+
};
|
|
3104
|
+
const aiResponse = await this.generateAIResponse(
|
|
3105
|
+
input.companionId,
|
|
3106
|
+
input.text,
|
|
3107
|
+
[],
|
|
3108
|
+
signal
|
|
3109
|
+
);
|
|
3110
|
+
return { userMessage, aiResponse };
|
|
3111
|
+
}
|
|
3112
|
+
async generateAIResponse(companionId, userMessage, context = [], signal) {
|
|
3113
|
+
const result = await this.run({
|
|
3114
|
+
...this.buildRequest(userMessage, context, signal)
|
|
3115
|
+
});
|
|
3116
|
+
return {
|
|
3117
|
+
...messageFormatter.toChatMessage(result.text, "assistant"),
|
|
3118
|
+
metadata: {
|
|
3119
|
+
companionId,
|
|
3120
|
+
model: result.model,
|
|
3121
|
+
usage: {
|
|
3122
|
+
input_tokens: result.usage?.inputTokens,
|
|
3123
|
+
output_tokens: result.usage?.outputTokens,
|
|
3124
|
+
total_tokens: result.usage?.totalTokens
|
|
3125
|
+
},
|
|
3126
|
+
finishReason: result.finishReason
|
|
3127
|
+
}
|
|
3128
|
+
};
|
|
3129
|
+
}
|
|
3130
|
+
async generateStreamingResponse(userMessage, context, onChunk, signal) {
|
|
3131
|
+
const request = this.buildRequest(userMessage, context, signal);
|
|
3132
|
+
const result = await this.runStream(request, { onChunk });
|
|
3133
|
+
return result.text;
|
|
3134
|
+
}
|
|
3135
|
+
buildRequest(userMessage, context, signal) {
|
|
3136
|
+
const messages = messageFormatter.toCoreMessages(context);
|
|
3137
|
+
messages.push({ role: "user", content: userMessage });
|
|
3138
|
+
return {
|
|
3139
|
+
messages,
|
|
3140
|
+
systemPrompt: this.resolveSystemPrompt(),
|
|
3141
|
+
model: this.config.model,
|
|
3142
|
+
tier: this.config.tier,
|
|
3143
|
+
temperature: this.config.temperature,
|
|
3144
|
+
maxTokens: this.config.maxTokens,
|
|
3145
|
+
...signal !== void 0 ? { signal } : {}
|
|
3146
|
+
};
|
|
3147
|
+
}
|
|
3148
|
+
resolveSystemPrompt() {
|
|
3149
|
+
const prompt = this.promptStrategy.resolve(this.config);
|
|
3150
|
+
return prompt && prompt.length > 0 ? prompt : void 0;
|
|
3151
|
+
}
|
|
3152
|
+
async run(request) {
|
|
3153
|
+
if (this.engine instanceof ProviderFactory) {
|
|
3154
|
+
return this.engine.chat(request);
|
|
3155
|
+
}
|
|
3156
|
+
return this.engine.generate(request);
|
|
3157
|
+
}
|
|
3158
|
+
async runStream(request, callbacks) {
|
|
3159
|
+
if (this.engine instanceof ProviderFactory) {
|
|
3160
|
+
return this.engine.chatStream(request, callbacks);
|
|
3161
|
+
}
|
|
3162
|
+
return this.engine.stream(request, callbacks);
|
|
3163
|
+
}
|
|
3164
|
+
requireStorage() {
|
|
3165
|
+
if (!this.storage) {
|
|
3166
|
+
throw new AIError(
|
|
3167
|
+
"MISSING_CONFIG" /* MISSING_CONFIG */,
|
|
3168
|
+
"ChatService.getConversation requires setStorage() to be called first."
|
|
3169
|
+
);
|
|
3170
|
+
}
|
|
3171
|
+
return this.storage;
|
|
3172
|
+
}
|
|
3173
|
+
};
|
|
3174
|
+
|
|
3175
|
+
// src/providers/groq/groq-container.ts
|
|
3176
|
+
var container = null;
|
|
3177
|
+
function configureProvider(config) {
|
|
3178
|
+
const configStore = new GroqConfigStore();
|
|
3179
|
+
const transport = new GroqHttpTransport(configStore);
|
|
3180
|
+
const httpClient = new GroqHttpClient({ configStore, transport });
|
|
3181
|
+
httpClient.initialize(config);
|
|
3182
|
+
const textGeneration = new TextGenerationService(
|
|
3183
|
+
httpClient,
|
|
3184
|
+
new CacheManager(),
|
|
3185
|
+
config.textModel
|
|
3186
|
+
);
|
|
3187
|
+
const chat = new ChatService(textGeneration);
|
|
3188
|
+
container = { groqHttpClient: httpClient, textGeneration, chat };
|
|
3189
|
+
return container;
|
|
3190
|
+
}
|
|
3191
|
+
function getProvider() {
|
|
3192
|
+
return container;
|
|
3193
|
+
}
|
|
3194
|
+
function resetProvider() {
|
|
3195
|
+
container?.groqHttpClient.reset();
|
|
3196
|
+
container = null;
|
|
3197
|
+
}
|
|
3198
|
+
|
|
3199
|
+
export { ANTHROPIC_API_VERSION, ANTHROPIC_DEFAULT_MAX_TOKENS, ANTHROPIC_MODELS, ANTHROPIC_TIERS, API_ENDPOINTS, AnthropicHttpTransport, AnthropicProvider, AnthropicRequestBuilder, BaseProvider, DEFAULT_BASE_URL, DEFAULT_GENERATION_CONFIG, DEFAULT_MODELS, FalProvider, GEMINI_TIERS, GROQ_MODELS, GROQ_TIERS, GeminiProvider, GroqChatRequestBuilder, GroqConfigStore, GroqHttpClient, GroqHttpTransport, GroqProvider, ProviderError, ProviderFactory, PrunaProvider, TIMEOUTS, TextGenerationService, configureProvider, consumeAnthropicStream, consumeGroqStream, createAI, createAssistantMessage, createFalProvider, createGeminiProvider, createMessage, createPrunaProvider, createSystemMessage, createUserMessage, defaultEffortForModel, extractContentFromSsePayload, extractTextFromMessages, formatMessagesForDisplay, generateWithPruna, getProvider, isMediaProvider, isTextProvider, mapAnthropicStopReason, promptToMessages, requestStructuredJson, resetProvider, toGroqMessages };
|
|
2375
3200
|
//# sourceMappingURL=index.js.map
|
|
2376
3201
|
//# sourceMappingURL=index.js.map
|