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