@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/hooks/index.js
CHANGED
|
@@ -3,6 +3,17 @@ import { useMemo, useCallback, useState, useRef, useEffect } from 'react';
|
|
|
3
3
|
// src/hooks/useAIContent.ts
|
|
4
4
|
|
|
5
5
|
// src/core/errors/ai-error-types.ts
|
|
6
|
+
var AIErrorType = /* @__PURE__ */ ((AIErrorType2) => {
|
|
7
|
+
AIErrorType2["INVALID_API_KEY"] = "INVALID_API_KEY";
|
|
8
|
+
AIErrorType2["MISSING_CONFIG"] = "MISSING_CONFIG";
|
|
9
|
+
AIErrorType2["NETWORK_ERROR"] = "NETWORK_ERROR";
|
|
10
|
+
AIErrorType2["ABORT_ERROR"] = "ABORT_ERROR";
|
|
11
|
+
AIErrorType2["RATE_LIMIT_ERROR"] = "RATE_LIMIT_ERROR";
|
|
12
|
+
AIErrorType2["SERVER_ERROR"] = "SERVER_ERROR";
|
|
13
|
+
AIErrorType2["CLIENT_ERROR"] = "CLIENT_ERROR";
|
|
14
|
+
AIErrorType2["UNKNOWN_ERROR"] = "UNKNOWN_ERROR";
|
|
15
|
+
return AIErrorType2;
|
|
16
|
+
})(AIErrorType || {});
|
|
6
17
|
var USER_FRIENDLY_ERROR_MESSAGES = {
|
|
7
18
|
["INVALID_API_KEY" /* INVALID_API_KEY */]: "Please check your API key and try again.",
|
|
8
19
|
["MISSING_CONFIG" /* MISSING_CONFIG */]: "The provider is not configured. Please create a provider with createAI first.",
|
|
@@ -15,17 +26,80 @@ var USER_FRIENDLY_ERROR_MESSAGES = {
|
|
|
15
26
|
};
|
|
16
27
|
|
|
17
28
|
// src/core/errors/ai-error.ts
|
|
18
|
-
var
|
|
29
|
+
var KNOWN_ERROR_TYPES = new Set(Object.values(AIErrorType));
|
|
30
|
+
function mapCodeToType(code) {
|
|
31
|
+
switch (code) {
|
|
32
|
+
case "VALIDATION_ERROR":
|
|
33
|
+
case "CLIENT_ERROR":
|
|
34
|
+
return "CLIENT_ERROR" /* CLIENT_ERROR */;
|
|
35
|
+
case "INVALID_API_KEY":
|
|
36
|
+
return "INVALID_API_KEY" /* INVALID_API_KEY */;
|
|
37
|
+
case "MISSING_CONFIG":
|
|
38
|
+
case "PROVIDER_UNAVAILABLE":
|
|
39
|
+
return "MISSING_CONFIG" /* MISSING_CONFIG */;
|
|
40
|
+
case "PROVIDER_QUOTA_EXHAUSTED":
|
|
41
|
+
case "RATE_LIMIT_ERROR":
|
|
42
|
+
return "RATE_LIMIT_ERROR" /* RATE_LIMIT_ERROR */;
|
|
43
|
+
case "NETWORK_ERROR":
|
|
44
|
+
case "TIMEOUT":
|
|
45
|
+
return "NETWORK_ERROR" /* NETWORK_ERROR */;
|
|
46
|
+
case "ABORT_ERROR":
|
|
47
|
+
case "ABORTED":
|
|
48
|
+
return "ABORT_ERROR" /* ABORT_ERROR */;
|
|
49
|
+
case "SERVER_ERROR":
|
|
50
|
+
case "PROVIDER_ERROR":
|
|
51
|
+
case "NO_RESULT":
|
|
52
|
+
return "SERVER_ERROR" /* SERVER_ERROR */;
|
|
53
|
+
default:
|
|
54
|
+
return "UNKNOWN_ERROR" /* UNKNOWN_ERROR */;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
function mapMessageToCode(message, fallback = "UNKNOWN") {
|
|
58
|
+
const lower = message.toLowerCase();
|
|
59
|
+
if (lower.includes("validation")) return "VALIDATION_ERROR";
|
|
60
|
+
if (lower.includes("quota")) return "PROVIDER_QUOTA_EXHAUSTED";
|
|
61
|
+
if (lower.includes("unavailable")) return "PROVIDER_UNAVAILABLE";
|
|
62
|
+
if (lower.includes("timeout") || lower.includes("aborted")) {
|
|
63
|
+
return lower.includes("aborted") ? "ABORTED" : "TIMEOUT";
|
|
64
|
+
}
|
|
65
|
+
if (lower.includes("parse") || lower.includes("json")) return "PARSE_ERROR";
|
|
66
|
+
if (lower.includes("no result")) return "NO_RESULT";
|
|
67
|
+
if (lower.includes("network") || lower.includes("fetch")) return "NETWORK_ERROR";
|
|
68
|
+
return fallback;
|
|
69
|
+
}
|
|
70
|
+
var AIError = class _AIError extends Error {
|
|
19
71
|
type;
|
|
72
|
+
code;
|
|
20
73
|
cause;
|
|
21
74
|
metadata;
|
|
22
|
-
constructor(
|
|
23
|
-
|
|
75
|
+
constructor(typeOrMessage, messageOrCode, cause, metadata = {}) {
|
|
76
|
+
let effectiveType;
|
|
77
|
+
let effectiveMessage;
|
|
78
|
+
let effectiveCode;
|
|
79
|
+
if (KNOWN_ERROR_TYPES.has(typeOrMessage)) {
|
|
80
|
+
effectiveType = typeOrMessage;
|
|
81
|
+
effectiveMessage = messageOrCode ?? "";
|
|
82
|
+
effectiveCode = effectiveType;
|
|
83
|
+
} else {
|
|
84
|
+
effectiveMessage = typeOrMessage;
|
|
85
|
+
effectiveCode = messageOrCode ?? "UNKNOWN";
|
|
86
|
+
effectiveType = mapCodeToType(effectiveCode);
|
|
87
|
+
}
|
|
88
|
+
super(effectiveMessage);
|
|
24
89
|
this.name = "AIError";
|
|
25
|
-
this.type =
|
|
90
|
+
this.type = effectiveType;
|
|
91
|
+
this.code = effectiveCode;
|
|
26
92
|
this.cause = cause;
|
|
27
93
|
this.metadata = metadata;
|
|
28
94
|
}
|
|
95
|
+
static from(error, fallbackCode = "UNKNOWN") {
|
|
96
|
+
if (error instanceof _AIError) return error;
|
|
97
|
+
if (error instanceof Error) {
|
|
98
|
+
const code = mapMessageToCode(error.message, fallbackCode);
|
|
99
|
+
return new _AIError(error.message, code, error);
|
|
100
|
+
}
|
|
101
|
+
return new _AIError(String(error), fallbackCode, error);
|
|
102
|
+
}
|
|
29
103
|
toString() {
|
|
30
104
|
const statusPart = typeof this.metadata.status === "number" ? ` (HTTP ${this.metadata.status})` : "";
|
|
31
105
|
return `${this.name} [${this.type}]: ${this.message}${statusPart}`;
|
|
@@ -34,12 +108,21 @@ var AIError = class extends Error {
|
|
|
34
108
|
return {
|
|
35
109
|
name: this.name,
|
|
36
110
|
type: this.type,
|
|
111
|
+
code: this.code,
|
|
37
112
|
message: this.message,
|
|
38
113
|
metadata: this.metadata
|
|
39
114
|
};
|
|
40
115
|
}
|
|
41
116
|
};
|
|
42
117
|
|
|
118
|
+
// src/core/errors/groq-error.ts
|
|
119
|
+
var GroqError = class extends AIError {
|
|
120
|
+
constructor(type, message, cause, metadata = {}) {
|
|
121
|
+
super(type, message, cause, metadata);
|
|
122
|
+
this.name = "GroqError";
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
|
|
43
126
|
// src/core/errors/error-classifier.ts
|
|
44
127
|
var DEFAULT_RETRYABLE_TYPES = /* @__PURE__ */ new Set([
|
|
45
128
|
"NETWORK_ERROR" /* NETWORK_ERROR */,
|
|
@@ -222,12 +305,16 @@ var ProviderFactory = class {
|
|
|
222
305
|
let wake = null;
|
|
223
306
|
let finished = false;
|
|
224
307
|
let failure;
|
|
308
|
+
const controller = new AbortController();
|
|
309
|
+
const onAbort = () => controller.abort();
|
|
310
|
+
request.signal?.addEventListener("abort", onAbort);
|
|
311
|
+
const effectiveRequest = { ...request, signal: controller.signal };
|
|
225
312
|
const notify = () => {
|
|
226
313
|
const resolve = wake;
|
|
227
314
|
wake = null;
|
|
228
315
|
resolve?.();
|
|
229
316
|
};
|
|
230
|
-
const completion = this.chatStream(
|
|
317
|
+
const completion = this.chatStream(effectiveRequest, {
|
|
231
318
|
onChunk: (chunk) => {
|
|
232
319
|
queue.push(chunk);
|
|
233
320
|
notify();
|
|
@@ -243,18 +330,23 @@ var ProviderFactory = class {
|
|
|
243
330
|
notify();
|
|
244
331
|
}
|
|
245
332
|
);
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
333
|
+
try {
|
|
334
|
+
while (!finished || queue.length > 0) {
|
|
335
|
+
if (queue.length === 0 && !finished) {
|
|
336
|
+
await new Promise((resolve) => {
|
|
337
|
+
wake = resolve;
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
while (queue.length > 0) {
|
|
341
|
+
yield queue.shift();
|
|
342
|
+
}
|
|
343
|
+
if (failure) throw failure;
|
|
254
344
|
}
|
|
255
|
-
|
|
345
|
+
await completion;
|
|
346
|
+
} finally {
|
|
347
|
+
controller.abort();
|
|
348
|
+
request.signal?.removeEventListener("abort", onAbort);
|
|
256
349
|
}
|
|
257
|
-
await completion;
|
|
258
350
|
}
|
|
259
351
|
async generateJson(request) {
|
|
260
352
|
const effective = request.model || request.tier ? request : { ...request, tier: this.defaultTier };
|
|
@@ -401,15 +493,22 @@ var ProviderFactory = class {
|
|
|
401
493
|
// src/core/constants/protocol.constants.ts
|
|
402
494
|
var SSE_PREFIX = "data: ";
|
|
403
495
|
var SSE_DONE_MARKER = "[DONE]";
|
|
404
|
-
var JSON_CODE_BLOCK_PATTERN = /```json
|
|
496
|
+
var JSON_CODE_BLOCK_PATTERN = /```(?:json)?\s*\n?([\s\S]*?)\n?```/;
|
|
405
497
|
var JSON_OBJECT_PATTERN = /\{[\s\S]*\}/;
|
|
498
|
+
var JSON_ARRAY_PATTERN = /\[[\s\S]*\]/;
|
|
406
499
|
|
|
407
500
|
// src/core/parsers/sse-lines.ts
|
|
408
501
|
function isSseDataLine(line) {
|
|
409
|
-
return line.startsWith(SSE_PREFIX);
|
|
502
|
+
return line.startsWith(SSE_PREFIX) || line.startsWith("data:");
|
|
410
503
|
}
|
|
411
504
|
function extractSseDataPayload(line) {
|
|
412
|
-
|
|
505
|
+
if (line.startsWith(SSE_PREFIX)) {
|
|
506
|
+
return line.slice(SSE_PREFIX.length);
|
|
507
|
+
}
|
|
508
|
+
if (line.startsWith("data:")) {
|
|
509
|
+
return line.slice(5);
|
|
510
|
+
}
|
|
511
|
+
return line;
|
|
413
512
|
}
|
|
414
513
|
function isSseDoneMarker(payload) {
|
|
415
514
|
return payload === SSE_DONE_MARKER;
|
|
@@ -418,7 +517,7 @@ function isSseDoneMarker(payload) {
|
|
|
418
517
|
// src/core/parsers/json-content-extractor.ts
|
|
419
518
|
function extractJsonFromCodeFence(content) {
|
|
420
519
|
const match = content.match(JSON_CODE_BLOCK_PATTERN);
|
|
421
|
-
if (match && match[1]) return match[1];
|
|
520
|
+
if (match && match[1]) return match[1].trim();
|
|
422
521
|
return null;
|
|
423
522
|
}
|
|
424
523
|
function extractJsonObject(content) {
|
|
@@ -426,8 +525,27 @@ function extractJsonObject(content) {
|
|
|
426
525
|
if (match && match[0]) return match[0];
|
|
427
526
|
return null;
|
|
428
527
|
}
|
|
528
|
+
function extractJsonArray(content) {
|
|
529
|
+
const match = content.match(JSON_ARRAY_PATTERN);
|
|
530
|
+
if (match && match[0]) return match[0];
|
|
531
|
+
return null;
|
|
532
|
+
}
|
|
533
|
+
function repairCommonJsonDefects(text) {
|
|
534
|
+
return text.replace(/(:\s*-?\d+(?:\.\d+)?)\s*"/g, "$1").replace(/(:\s*"[^"]*")"/g, "$1");
|
|
535
|
+
}
|
|
429
536
|
function extractJsonCandidate(content) {
|
|
430
|
-
|
|
537
|
+
const fromFence = extractJsonFromCodeFence(content);
|
|
538
|
+
if (fromFence) return fromFence;
|
|
539
|
+
const objectIndex = content.indexOf("{");
|
|
540
|
+
const arrayIndex = content.indexOf("[");
|
|
541
|
+
if (objectIndex !== -1 && (arrayIndex === -1 || objectIndex < arrayIndex)) {
|
|
542
|
+
const obj = extractJsonObject(content);
|
|
543
|
+
if (obj) return obj;
|
|
544
|
+
} else if (arrayIndex !== -1) {
|
|
545
|
+
const arr = extractJsonArray(content);
|
|
546
|
+
if (arr) return arr;
|
|
547
|
+
}
|
|
548
|
+
return content.trim();
|
|
431
549
|
}
|
|
432
550
|
|
|
433
551
|
// src/core/parsers/json-response.parser.ts
|
|
@@ -436,12 +554,17 @@ var JsonResponseParser = class {
|
|
|
436
554
|
const candidate = extractJsonCandidate(rawContent);
|
|
437
555
|
try {
|
|
438
556
|
return JSON.parse(candidate);
|
|
439
|
-
} catch
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
557
|
+
} catch {
|
|
558
|
+
try {
|
|
559
|
+
return JSON.parse(repairCommonJsonDefects(candidate));
|
|
560
|
+
} catch (error) {
|
|
561
|
+
throw new AIError(
|
|
562
|
+
"UNKNOWN_ERROR" /* UNKNOWN_ERROR */,
|
|
563
|
+
"Failed to parse structured response as JSON",
|
|
564
|
+
error instanceof Error ? error : void 0,
|
|
565
|
+
{ rawContent }
|
|
566
|
+
);
|
|
567
|
+
}
|
|
445
568
|
}
|
|
446
569
|
}
|
|
447
570
|
};
|
|
@@ -755,11 +878,14 @@ async function consumeAnthropicStream(stream, callbacks) {
|
|
|
755
878
|
break;
|
|
756
879
|
case "message_stop":
|
|
757
880
|
break;
|
|
758
|
-
case "error":
|
|
881
|
+
case "error": {
|
|
882
|
+
const errType = event.error.type;
|
|
883
|
+
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 */;
|
|
759
884
|
throw new AIError(
|
|
760
|
-
|
|
885
|
+
mappedType,
|
|
761
886
|
event.error.message || "Anthropic stream failed mid-flight"
|
|
762
887
|
);
|
|
888
|
+
}
|
|
763
889
|
}
|
|
764
890
|
};
|
|
765
891
|
try {
|
|
@@ -1221,6 +1347,27 @@ var RequestDeduplicator = class {
|
|
|
1221
1347
|
}
|
|
1222
1348
|
};
|
|
1223
1349
|
|
|
1350
|
+
// src/core/utils/api-key-validator.ts
|
|
1351
|
+
var DEFAULT_API_KEY_RULE = {
|
|
1352
|
+
minLength: 10
|
|
1353
|
+
};
|
|
1354
|
+
function validateApiKey(apiKey, rule = DEFAULT_API_KEY_RULE) {
|
|
1355
|
+
if (!apiKey) {
|
|
1356
|
+
return { valid: false, reason: "API key is required" };
|
|
1357
|
+
}
|
|
1358
|
+
const trimmed = apiKey.trim();
|
|
1359
|
+
if (trimmed.length === 0) {
|
|
1360
|
+
return { valid: false, reason: "API key cannot be empty" };
|
|
1361
|
+
}
|
|
1362
|
+
if (trimmed.length < rule.minLength) {
|
|
1363
|
+
return {
|
|
1364
|
+
valid: false,
|
|
1365
|
+
reason: `API key must be at least ${rule.minLength} characters`
|
|
1366
|
+
};
|
|
1367
|
+
}
|
|
1368
|
+
return { valid: true };
|
|
1369
|
+
}
|
|
1370
|
+
|
|
1224
1371
|
// src/providers/groq/groq.constants.ts
|
|
1225
1372
|
var GROQ_MODELS = {
|
|
1226
1373
|
LLAMA_3_1_8B_INSTANT: "llama-3.1-8b-instant",
|
|
@@ -1237,11 +1384,13 @@ var DEFAULT_GENERATION_CONFIG = {
|
|
|
1237
1384
|
topP: 1
|
|
1238
1385
|
};
|
|
1239
1386
|
var GROQ_BASE_URL = "https://api.groq.com/openai/v1";
|
|
1387
|
+
var DEFAULT_BASE_URL = GROQ_BASE_URL;
|
|
1240
1388
|
var CHAT_COMPLETIONS_PATH = "/chat/completions";
|
|
1241
1389
|
var GROQ_TIMEOUTS = {
|
|
1242
1390
|
DEFAULT: 3e4,
|
|
1243
1391
|
STREAMING: 6e4
|
|
1244
1392
|
};
|
|
1393
|
+
var TIMEOUTS = GROQ_TIMEOUTS;
|
|
1245
1394
|
var HTTP_HEADERS = {
|
|
1246
1395
|
CONTENT_TYPE: "Content-Type",
|
|
1247
1396
|
APPLICATION_JSON: "application/json",
|
|
@@ -1300,13 +1449,65 @@ var GroqChatRequestBuilder = class _GroqChatRequestBuilder {
|
|
|
1300
1449
|
}
|
|
1301
1450
|
};
|
|
1302
1451
|
|
|
1452
|
+
// src/providers/groq/groq-config-store.ts
|
|
1453
|
+
var GroqConfigStore = class {
|
|
1454
|
+
config = null;
|
|
1455
|
+
initialize(config) {
|
|
1456
|
+
const validation = validateApiKey(config.apiKey, { minLength: 1 });
|
|
1457
|
+
if (!validation.valid) {
|
|
1458
|
+
throw new GroqError("INVALID_API_KEY" /* INVALID_API_KEY */, validation.reason ?? "Invalid API key");
|
|
1459
|
+
}
|
|
1460
|
+
const normalized = {
|
|
1461
|
+
apiKey: config.apiKey.trim(),
|
|
1462
|
+
baseUrl: config.baseUrl ?? DEFAULT_BASE_URL,
|
|
1463
|
+
timeoutMs: config.timeoutMs ?? TIMEOUTS.DEFAULT,
|
|
1464
|
+
...config.models !== void 0 ? { models: config.models } : {},
|
|
1465
|
+
...config.textModel !== void 0 ? { textModel: config.textModel } : {}
|
|
1466
|
+
};
|
|
1467
|
+
this.config = normalized;
|
|
1468
|
+
return normalized;
|
|
1469
|
+
}
|
|
1470
|
+
get() {
|
|
1471
|
+
if (!this.config) {
|
|
1472
|
+
throw new GroqError(
|
|
1473
|
+
"MISSING_CONFIG" /* MISSING_CONFIG */,
|
|
1474
|
+
"Client not initialized. Call configureProvider first."
|
|
1475
|
+
);
|
|
1476
|
+
}
|
|
1477
|
+
return this.config;
|
|
1478
|
+
}
|
|
1479
|
+
getOrNull() {
|
|
1480
|
+
return this.config;
|
|
1481
|
+
}
|
|
1482
|
+
isInitialized() {
|
|
1483
|
+
return this.config !== null;
|
|
1484
|
+
}
|
|
1485
|
+
setApiKey(apiKey) {
|
|
1486
|
+
const current = this.get();
|
|
1487
|
+
const validation = validateApiKey(apiKey, { minLength: 1 });
|
|
1488
|
+
if (!validation.valid) {
|
|
1489
|
+
throw new GroqError("INVALID_API_KEY" /* INVALID_API_KEY */, validation.reason ?? "Invalid API key");
|
|
1490
|
+
}
|
|
1491
|
+
this.config = { ...current, apiKey: apiKey.trim() };
|
|
1492
|
+
}
|
|
1493
|
+
reset() {
|
|
1494
|
+
this.config = null;
|
|
1495
|
+
}
|
|
1496
|
+
};
|
|
1497
|
+
|
|
1303
1498
|
// src/providers/groq/groq-http-transport.ts
|
|
1304
1499
|
var GroqHttpTransport = class {
|
|
1305
|
-
constructor(
|
|
1306
|
-
this.
|
|
1500
|
+
constructor(configSource) {
|
|
1501
|
+
this.configSource = configSource;
|
|
1307
1502
|
}
|
|
1308
|
-
|
|
1503
|
+
configSource;
|
|
1309
1504
|
pendingAbortControllers = /* @__PURE__ */ new Set();
|
|
1505
|
+
getConfig() {
|
|
1506
|
+
if ("get" in this.configSource && typeof this.configSource.get === "function") {
|
|
1507
|
+
return this.configSource.get();
|
|
1508
|
+
}
|
|
1509
|
+
return this.configSource;
|
|
1510
|
+
}
|
|
1310
1511
|
cancelPendingRequests() {
|
|
1311
1512
|
for (const controller of this.pendingAbortControllers) {
|
|
1312
1513
|
controller.abort();
|
|
@@ -1321,7 +1522,7 @@ var GroqHttpTransport = class {
|
|
|
1321
1522
|
}
|
|
1322
1523
|
async streamChatCompletion(request, options = {}) {
|
|
1323
1524
|
const url = `${this.baseUrl()}${CHAT_COMPLETIONS_PATH}`;
|
|
1324
|
-
const timeout = this.
|
|
1525
|
+
const timeout = this.getConfig().timeoutMs ?? GROQ_TIMEOUTS.STREAMING;
|
|
1325
1526
|
const controller = new AbortController();
|
|
1326
1527
|
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
1327
1528
|
const onExternalAbort = () => controller.abort();
|
|
@@ -1348,11 +1549,11 @@ var GroqHttpTransport = class {
|
|
|
1348
1549
|
}
|
|
1349
1550
|
}
|
|
1350
1551
|
baseUrl() {
|
|
1351
|
-
return this.
|
|
1552
|
+
return this.getConfig().baseUrl ?? GROQ_BASE_URL;
|
|
1352
1553
|
}
|
|
1353
1554
|
async request(endpoint, body) {
|
|
1354
1555
|
const url = `${this.baseUrl()}${endpoint}`;
|
|
1355
|
-
const timeout = this.
|
|
1556
|
+
const timeout = this.getConfig().timeoutMs ?? GROQ_TIMEOUTS.DEFAULT;
|
|
1356
1557
|
const controller = new AbortController();
|
|
1357
1558
|
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
1358
1559
|
this.pendingAbortControllers.add(controller);
|
|
@@ -1375,7 +1576,7 @@ var GroqHttpTransport = class {
|
|
|
1375
1576
|
buildHeaders() {
|
|
1376
1577
|
return {
|
|
1377
1578
|
[HTTP_HEADERS.CONTENT_TYPE]: HTTP_HEADERS.APPLICATION_JSON,
|
|
1378
|
-
[HTTP_HEADERS.AUTHORIZATION]: `${HTTP_HEADERS.BEARER_PREFIX}${this.
|
|
1579
|
+
[HTTP_HEADERS.AUTHORIZATION]: `${HTTP_HEADERS.BEARER_PREFIX}${this.getConfig().apiKey}`
|
|
1379
1580
|
};
|
|
1380
1581
|
}
|
|
1381
1582
|
async throwFromErrorResponse(response) {
|
|
@@ -1477,21 +1678,40 @@ var CompositeExecutor = class {
|
|
|
1477
1678
|
|
|
1478
1679
|
// src/providers/groq/groq-http-client.ts
|
|
1479
1680
|
var GroqHttpClient = class {
|
|
1681
|
+
configStore;
|
|
1480
1682
|
transport;
|
|
1481
1683
|
executor;
|
|
1482
|
-
constructor(
|
|
1483
|
-
|
|
1684
|
+
constructor(configOrDeps, maybeDeps = {}) {
|
|
1685
|
+
const isConfig = typeof configOrDeps === "object" && configOrDeps !== null && "apiKey" in configOrDeps && typeof configOrDeps.apiKey === "string";
|
|
1686
|
+
const deps = isConfig ? maybeDeps : configOrDeps ?? {};
|
|
1687
|
+
this.configStore = deps.configStore ?? new GroqConfigStore();
|
|
1688
|
+
if (isConfig) {
|
|
1689
|
+
this.configStore.initialize(configOrDeps);
|
|
1690
|
+
}
|
|
1691
|
+
this.transport = deps.transport ?? new GroqHttpTransport(this.configStore);
|
|
1484
1692
|
const retryManager2 = deps.retryManager ?? new RetryManager();
|
|
1485
1693
|
const queue = deps.queue ?? new RequestQueue();
|
|
1486
1694
|
const deduplicator = deps.deduplicator ?? new RequestDeduplicator();
|
|
1487
1695
|
this.executor = new CompositeExecutor([
|
|
1488
1696
|
new DedupeExecutor(deduplicator, {
|
|
1489
|
-
generateKey: (request) => deduplicatorKey(request,
|
|
1697
|
+
generateKey: (request) => deduplicatorKey(request, this.configStore.isInitialized() ? this.configStore.get().apiKey : "")
|
|
1490
1698
|
}),
|
|
1491
1699
|
new RetryExecutor(retryManager2),
|
|
1492
1700
|
new QueueExecutor(queue, QUEUE_PRIORITY_USER_REQUEST)
|
|
1493
1701
|
]);
|
|
1494
1702
|
}
|
|
1703
|
+
initialize(config) {
|
|
1704
|
+
this.configStore.initialize(config);
|
|
1705
|
+
}
|
|
1706
|
+
isInitialized() {
|
|
1707
|
+
return this.configStore.isInitialized();
|
|
1708
|
+
}
|
|
1709
|
+
getApiKey() {
|
|
1710
|
+
return this.configStore.get().apiKey;
|
|
1711
|
+
}
|
|
1712
|
+
setApiKey(apiKey) {
|
|
1713
|
+
this.configStore.setApiKey(apiKey);
|
|
1714
|
+
}
|
|
1495
1715
|
cancelPendingRequests() {
|
|
1496
1716
|
this.transport.cancelPendingRequests();
|
|
1497
1717
|
}
|
|
@@ -1504,6 +1724,10 @@ var GroqHttpClient = class {
|
|
|
1504
1724
|
streamChatCompletion(request, options) {
|
|
1505
1725
|
return this.transport.streamChatCompletion(request, options);
|
|
1506
1726
|
}
|
|
1727
|
+
reset() {
|
|
1728
|
+
this.transport.cancelPendingRequests();
|
|
1729
|
+
this.configStore.reset();
|
|
1730
|
+
}
|
|
1507
1731
|
};
|
|
1508
1732
|
function deduplicatorKey(request, apiKey) {
|
|
1509
1733
|
const { model, messages, stream: _stream, ...rest } = request;
|
|
@@ -1551,6 +1775,13 @@ async function consumeGroqStream(stream, callbacks) {
|
|
|
1551
1775
|
} catch {
|
|
1552
1776
|
return;
|
|
1553
1777
|
}
|
|
1778
|
+
const errorBody = chunk.error;
|
|
1779
|
+
if (errorBody) {
|
|
1780
|
+
throw new AIError(
|
|
1781
|
+
errorBody.message?.toLowerCase().includes("rate limit") ? "RATE_LIMIT_ERROR" /* RATE_LIMIT_ERROR */ : "SERVER_ERROR" /* SERVER_ERROR */,
|
|
1782
|
+
errorBody.message || "Groq stream failed mid-flight"
|
|
1783
|
+
);
|
|
1784
|
+
}
|
|
1554
1785
|
if (chunk.model) model = chunk.model;
|
|
1555
1786
|
const choice = chunk.choices?.[0];
|
|
1556
1787
|
const delta = choice?.delta?.content;
|
|
@@ -1565,13 +1796,6 @@ async function consumeGroqStream(stream, callbacks) {
|
|
|
1565
1796
|
if (tailUsage) {
|
|
1566
1797
|
usage = mapGroqUsage(tailUsage);
|
|
1567
1798
|
}
|
|
1568
|
-
const errorBody = chunk.error;
|
|
1569
|
-
if (errorBody) {
|
|
1570
|
-
throw new AIError(
|
|
1571
|
-
"SERVER_ERROR" /* SERVER_ERROR */,
|
|
1572
|
-
errorBody.message || "Groq stream failed mid-flight"
|
|
1573
|
-
);
|
|
1574
|
-
}
|
|
1575
1799
|
};
|
|
1576
1800
|
try {
|
|
1577
1801
|
while (true) {
|
|
@@ -1772,37 +1996,7 @@ function findLastIndex2(items, predicate) {
|
|
|
1772
1996
|
}
|
|
1773
1997
|
|
|
1774
1998
|
// src/content/domain/errors/AIErrors.ts
|
|
1775
|
-
var
|
|
1776
|
-
code;
|
|
1777
|
-
cause;
|
|
1778
|
-
constructor(message, code, cause) {
|
|
1779
|
-
super(message);
|
|
1780
|
-
this.name = "AIError";
|
|
1781
|
-
this.code = code;
|
|
1782
|
-
this.cause = cause;
|
|
1783
|
-
}
|
|
1784
|
-
static from(error, fallbackCode = "UNKNOWN") {
|
|
1785
|
-
if (error instanceof _AIError) return error;
|
|
1786
|
-
if (error instanceof Error) {
|
|
1787
|
-
return new _AIError(error.message, mapMessageToCode(error.message, fallbackCode), error);
|
|
1788
|
-
}
|
|
1789
|
-
return new _AIError(String(error), fallbackCode, error);
|
|
1790
|
-
}
|
|
1791
|
-
};
|
|
1792
|
-
function mapMessageToCode(message, fallback) {
|
|
1793
|
-
const lower = message.toLowerCase();
|
|
1794
|
-
if (lower.includes("validation")) return "VALIDATION_ERROR";
|
|
1795
|
-
if (lower.includes("quota")) return "PROVIDER_QUOTA_EXHAUSTED";
|
|
1796
|
-
if (lower.includes("unavailable")) return "PROVIDER_UNAVAILABLE";
|
|
1797
|
-
if (lower.includes("timeout") || lower.includes("aborted")) {
|
|
1798
|
-
return lower.includes("aborted") ? "ABORTED" : "TIMEOUT";
|
|
1799
|
-
}
|
|
1800
|
-
if (lower.includes("parse") || lower.includes("json")) return "PARSE_ERROR";
|
|
1801
|
-
if (lower.includes("no result")) return "NO_RESULT";
|
|
1802
|
-
if (lower.includes("network") || lower.includes("fetch")) return "NETWORK_ERROR";
|
|
1803
|
-
return fallback;
|
|
1804
|
-
}
|
|
1805
|
-
var ValidationError = class extends AIError2 {
|
|
1999
|
+
var ValidationError = class extends AIError {
|
|
1806
2000
|
constructor(message) {
|
|
1807
2001
|
super(message, "VALIDATION_ERROR");
|
|
1808
2002
|
this.name = "ValidationError";
|
|
@@ -1933,7 +2127,9 @@ var GeminiProvider = class extends BaseProvider {
|
|
|
1933
2127
|
headers: this.authHeaders()
|
|
1934
2128
|
}
|
|
1935
2129
|
);
|
|
1936
|
-
|
|
2130
|
+
if (response.ok) return "healthy";
|
|
2131
|
+
if (response.status === 401 || response.status === 403) return "unavailable";
|
|
2132
|
+
return "degraded";
|
|
1937
2133
|
} catch {
|
|
1938
2134
|
return "unavailable";
|
|
1939
2135
|
}
|
|
@@ -1944,6 +2140,8 @@ var GeminiProvider = class extends BaseProvider {
|
|
|
1944
2140
|
async generate(request) {
|
|
1945
2141
|
try {
|
|
1946
2142
|
const model = request.model ?? this.resolveModel(request.tier ?? "balanced");
|
|
2143
|
+
const systemMessages = request.messages.filter((m) => m.role === "system");
|
|
2144
|
+
const systemPrompt = request.systemPrompt && request.systemPrompt.length > 0 ? request.systemPrompt : systemMessages.length > 0 ? systemMessages.map((m) => m.content).join("\n") : void 0;
|
|
1947
2145
|
const response = await this.withRetry(async () => {
|
|
1948
2146
|
return await this.fetchWithTimeout(
|
|
1949
2147
|
`${this.config.baseUrl}/models/${model}:generateContent`,
|
|
@@ -1951,22 +2149,28 @@ var GeminiProvider = class extends BaseProvider {
|
|
|
1951
2149
|
method: "POST",
|
|
1952
2150
|
headers: this.authHeaders(),
|
|
1953
2151
|
body: JSON.stringify({
|
|
1954
|
-
systemInstruction:
|
|
2152
|
+
systemInstruction: systemPrompt && systemPrompt.length > 0 ? { parts: [{ text: systemPrompt }] } : void 0,
|
|
1955
2153
|
contents: toGeminiContents(request.messages),
|
|
1956
2154
|
generationConfig: {
|
|
1957
|
-
temperature: request.temperature
|
|
1958
|
-
maxOutputTokens: request.maxTokens
|
|
1959
|
-
topP: request.topP
|
|
2155
|
+
temperature: request.temperature ?? 0.7,
|
|
2156
|
+
maxOutputTokens: request.maxTokens ?? 2048,
|
|
2157
|
+
topP: request.topP ?? 0.9
|
|
1960
2158
|
}
|
|
1961
2159
|
})
|
|
1962
2160
|
}
|
|
1963
2161
|
);
|
|
1964
2162
|
});
|
|
1965
2163
|
if (!response.ok) {
|
|
1966
|
-
|
|
2164
|
+
const message = await extractErrorMessageFromResponse(response);
|
|
2165
|
+
throw new AIError(
|
|
2166
|
+
classifyHttpStatus(response.status),
|
|
2167
|
+
`Gemini API error (${response.status}): ${message}`,
|
|
2168
|
+
void 0,
|
|
2169
|
+
{ status: response.status, model }
|
|
2170
|
+
);
|
|
1967
2171
|
}
|
|
1968
2172
|
const data = await response.json();
|
|
1969
|
-
const text = data.candidates[0]?.content?.parts?.map((part) => part.text ?? "").join("") ?? "";
|
|
2173
|
+
const text = data.candidates?.[0]?.content?.parts?.map((part) => part.text ?? "").join("") ?? "";
|
|
1970
2174
|
if (text.length === 0) {
|
|
1971
2175
|
throw new AIError("SERVER_ERROR" /* SERVER_ERROR */, "No content generated from Gemini API", void 0, { model });
|
|
1972
2176
|
}
|
|
@@ -1979,7 +2183,7 @@ var GeminiProvider = class extends BaseProvider {
|
|
|
1979
2183
|
} : void 0,
|
|
1980
2184
|
model,
|
|
1981
2185
|
providerId: this.id,
|
|
1982
|
-
finishReason: mapGeminiFinishReason(data.candidates[0]?.finishReason)
|
|
2186
|
+
finishReason: mapGeminiFinishReason(data.candidates?.[0]?.finishReason)
|
|
1983
2187
|
};
|
|
1984
2188
|
} catch (error) {
|
|
1985
2189
|
if (error instanceof AIError) throw error;
|
|
@@ -2031,12 +2235,18 @@ var GeminiProvider = class extends BaseProvider {
|
|
|
2031
2235
|
);
|
|
2032
2236
|
});
|
|
2033
2237
|
if (!response.ok) {
|
|
2034
|
-
|
|
2238
|
+
const message = await extractErrorMessageFromResponse(response);
|
|
2239
|
+
throw new AIError(
|
|
2240
|
+
classifyHttpStatus(response.status),
|
|
2241
|
+
`Gemini image generation error (${response.status}): ${message}`,
|
|
2242
|
+
void 0,
|
|
2243
|
+
{ status: response.status }
|
|
2244
|
+
);
|
|
2035
2245
|
}
|
|
2036
2246
|
const data = await response.json();
|
|
2037
|
-
const imageData = data.candidates[0]?.content?.parts[0]?.inlineData;
|
|
2247
|
+
const imageData = data.candidates?.[0]?.content?.parts?.[0]?.inlineData;
|
|
2038
2248
|
if (!imageData) {
|
|
2039
|
-
throw new
|
|
2249
|
+
throw new AIError("SERVER_ERROR" /* SERVER_ERROR */, "No image data in Gemini response");
|
|
2040
2250
|
}
|
|
2041
2251
|
const url = `data:${imageData.mimeType};base64,${imageData.data}`;
|
|
2042
2252
|
return {
|
|
@@ -2465,17 +2675,70 @@ var PrunaProvider = class {
|
|
|
2465
2675
|
async estimateCost(_request) {
|
|
2466
2676
|
return 1;
|
|
2467
2677
|
}
|
|
2468
|
-
async generateImage(
|
|
2469
|
-
|
|
2678
|
+
async generateImage(request) {
|
|
2679
|
+
let finalUrl;
|
|
2680
|
+
const model = this.config.models?.textToImage;
|
|
2681
|
+
for await (const stage of generateWithPruna(
|
|
2682
|
+
{ prompt: request.prompt, model, quality: request.quality },
|
|
2683
|
+
this.config.apiKey
|
|
2684
|
+
)) {
|
|
2685
|
+
if (stage.error) throw stage.error;
|
|
2686
|
+
if (stage.result?.url) finalUrl = stage.result.url;
|
|
2687
|
+
}
|
|
2688
|
+
if (!finalUrl) {
|
|
2689
|
+
throw new AIError("SERVER_ERROR" /* SERVER_ERROR */, "No image generated by Pruna AI");
|
|
2690
|
+
}
|
|
2691
|
+
return {
|
|
2692
|
+
id: Date.now().toString(),
|
|
2693
|
+
type: "image",
|
|
2694
|
+
url: finalUrl,
|
|
2695
|
+
metadata: { provider: this.id, model: model ?? "p-image" },
|
|
2696
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2697
|
+
};
|
|
2470
2698
|
}
|
|
2471
|
-
async generateVideo(
|
|
2472
|
-
|
|
2699
|
+
async generateVideo(request) {
|
|
2700
|
+
let finalUrl;
|
|
2701
|
+
const model = this.config.models?.textToVideo;
|
|
2702
|
+
for await (const stage of generateWithPruna(
|
|
2703
|
+
{ prompt: request.prompt, model, duration: request.duration, aspectRatio: request.aspectRatio },
|
|
2704
|
+
this.config.apiKey
|
|
2705
|
+
)) {
|
|
2706
|
+
if (stage.error) throw stage.error;
|
|
2707
|
+
if (stage.result?.url) finalUrl = stage.result.url;
|
|
2708
|
+
}
|
|
2709
|
+
if (!finalUrl) {
|
|
2710
|
+
throw new AIError("SERVER_ERROR" /* SERVER_ERROR */, "No video generated by Pruna AI");
|
|
2711
|
+
}
|
|
2712
|
+
return {
|
|
2713
|
+
id: Date.now().toString(),
|
|
2714
|
+
type: "video",
|
|
2715
|
+
url: finalUrl,
|
|
2716
|
+
metadata: { provider: this.id, model: model ?? "p-video" },
|
|
2717
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2718
|
+
};
|
|
2473
2719
|
}
|
|
2474
|
-
async imageToVideo(
|
|
2475
|
-
|
|
2720
|
+
async imageToVideo(request) {
|
|
2721
|
+
let finalUrl;
|
|
2722
|
+
for await (const stage of generateWithPruna(
|
|
2723
|
+
{ image: request.imageUrl, prompt: request.prompt, duration: request.duration, motion: request.motion },
|
|
2724
|
+
this.config.apiKey
|
|
2725
|
+
)) {
|
|
2726
|
+
if (stage.error) throw stage.error;
|
|
2727
|
+
if (stage.result?.url) finalUrl = stage.result.url;
|
|
2728
|
+
}
|
|
2729
|
+
if (!finalUrl) {
|
|
2730
|
+
throw new AIError("SERVER_ERROR" /* SERVER_ERROR */, "No video generated from image by Pruna AI");
|
|
2731
|
+
}
|
|
2732
|
+
return {
|
|
2733
|
+
id: Date.now().toString(),
|
|
2734
|
+
type: "video",
|
|
2735
|
+
url: finalUrl,
|
|
2736
|
+
metadata: { provider: this.id, model: "p-image-to-video" },
|
|
2737
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2738
|
+
};
|
|
2476
2739
|
}
|
|
2477
2740
|
async videoToVideo(_request) {
|
|
2478
|
-
throw new AIError("
|
|
2741
|
+
throw new AIError("CLIENT_ERROR" /* CLIENT_ERROR */, "Pruna AI provider does not support video-to-video conversion");
|
|
2479
2742
|
}
|
|
2480
2743
|
};
|
|
2481
2744
|
|
|
@@ -3082,11 +3345,12 @@ var buildScriptResult = (parsed, duration) => {
|
|
|
3082
3345
|
if (!script) {
|
|
3083
3346
|
throw new Error("AI response missing script field");
|
|
3084
3347
|
}
|
|
3348
|
+
const wordCount = script.trim().length === 0 ? 0 : script.trim().split(/\s+/).length;
|
|
3085
3349
|
return {
|
|
3086
3350
|
...parsed,
|
|
3087
3351
|
script,
|
|
3088
3352
|
duration,
|
|
3089
|
-
wordCount
|
|
3353
|
+
wordCount
|
|
3090
3354
|
};
|
|
3091
3355
|
};
|
|
3092
3356
|
var VideoScriptService = class {
|
|
@@ -3281,7 +3545,6 @@ var SeoService = class {
|
|
|
3281
3545
|
analyzeKeywords(content, keywords) {
|
|
3282
3546
|
const validatedContent = validateContentBody(content);
|
|
3283
3547
|
const validatedKeywords = validateKeywordList(keywords);
|
|
3284
|
-
void countWordsInContent(validatedContent);
|
|
3285
3548
|
return validatedKeywords.map((keyword) => {
|
|
3286
3549
|
const count = countKeywordOccurrences(validatedContent, keyword);
|
|
3287
3550
|
const density = calculateKeywordDensity(validatedContent, keyword);
|
|
@@ -3376,68 +3639,20 @@ var ABTestService = class {
|
|
|
3376
3639
|
});
|
|
3377
3640
|
const scoreA = predictions[0]?.predictedEngagement ?? FALLBACK_ENGAGEMENT;
|
|
3378
3641
|
const scoreB = predictions[1]?.predictedEngagement ?? FALLBACK_ENGAGEMENT;
|
|
3642
|
+
const isTie = scoreA === scoreB;
|
|
3379
3643
|
const winner = selectHigherScoreVariantId(scoreA, scoreB, VARIANT_A_ID, VARIANT_B_ID);
|
|
3380
3644
|
const improvement = calculateImprovementPercentage(scoreA, scoreB);
|
|
3381
3645
|
return {
|
|
3382
3646
|
winner,
|
|
3383
3647
|
confidence: AB_TEST_DEFAULT_CONFIDENCE,
|
|
3384
|
-
improvement: `+${improvement}% engagement`,
|
|
3385
|
-
reasoning: winner === VARIANT_A_ID ? "Variant A has more engaging content" : "Variant B has more engaging content",
|
|
3648
|
+
improvement: isTie ? "0%" : `+${improvement}% engagement`,
|
|
3649
|
+
reasoning: isTie ? "Both variants have equal predicted engagement" : winner === VARIANT_A_ID ? "Variant A has more engaging content" : "Variant B has more engaging content",
|
|
3386
3650
|
recommendations: [...DEFAULT_RECOMMENDATIONS]
|
|
3387
3651
|
};
|
|
3388
3652
|
}
|
|
3389
3653
|
};
|
|
3390
3654
|
|
|
3391
|
-
// src/content/infrastructure/utils/jsonParser.ts
|
|
3392
|
-
function repairCommonJsonDefects(text) {
|
|
3393
|
-
return text.replace(/(:\s*-?\d+(?:\.\d+)?)\s*"/g, "$1").replace(/(:\s*"[^"]*")"/g, "$1");
|
|
3394
|
-
}
|
|
3395
|
-
function safeJSONParse(text, fallback, onError) {
|
|
3396
|
-
try {
|
|
3397
|
-
return JSON.parse(text);
|
|
3398
|
-
} catch (error) {
|
|
3399
|
-
try {
|
|
3400
|
-
return JSON.parse(repairCommonJsonDefects(text));
|
|
3401
|
-
} catch {
|
|
3402
|
-
}
|
|
3403
|
-
if (error instanceof SyntaxError) {
|
|
3404
|
-
const positionMatch = error.message.match(/position (\d+)/);
|
|
3405
|
-
const position = positionMatch ? parseInt(positionMatch[1], 10) : -1;
|
|
3406
|
-
if (position > 0) {
|
|
3407
|
-
text.substring(Math.max(0, position - 50), position + 50);
|
|
3408
|
-
}
|
|
3409
|
-
}
|
|
3410
|
-
return fallback;
|
|
3411
|
-
}
|
|
3412
|
-
}
|
|
3413
|
-
function extractJSON(text) {
|
|
3414
|
-
const codeBlockMatch = text.match(/```(?:json)?\s*([\s\S]*?)```/);
|
|
3415
|
-
if (codeBlockMatch && codeBlockMatch[1]) {
|
|
3416
|
-
const candidate = codeBlockMatch[1].trim();
|
|
3417
|
-
if (candidate.startsWith("{") || candidate.startsWith("[")) {
|
|
3418
|
-
return candidate;
|
|
3419
|
-
}
|
|
3420
|
-
}
|
|
3421
|
-
const objectMatch = text.match(/\{[\s\S]*\}/);
|
|
3422
|
-
if (objectMatch) {
|
|
3423
|
-
return objectMatch[0];
|
|
3424
|
-
}
|
|
3425
|
-
const arrayMatch = text.match(/\[[\s\S]*\]/);
|
|
3426
|
-
if (arrayMatch) {
|
|
3427
|
-
return arrayMatch[0];
|
|
3428
|
-
}
|
|
3429
|
-
return null;
|
|
3430
|
-
}
|
|
3431
|
-
function parseAIResponse(response, fallback, onError) {
|
|
3432
|
-
const json = extractJSON(response);
|
|
3433
|
-
if (!json) {
|
|
3434
|
-
return fallback;
|
|
3435
|
-
}
|
|
3436
|
-
return safeJSONParse(json, fallback);
|
|
3437
|
-
}
|
|
3438
|
-
|
|
3439
3655
|
// src/content/application/services/ImagePromptService.ts
|
|
3440
|
-
var JSON_ARRAY_PATTERN = /\[[\s\S]*\]/;
|
|
3441
3656
|
var ImagePromptService = class {
|
|
3442
3657
|
constructor(executor) {
|
|
3443
3658
|
this.executor = executor;
|
|
@@ -3451,10 +3666,11 @@ var ImagePromptService = class {
|
|
|
3451
3666
|
async generateImagePromptsForBlog(blogContent) {
|
|
3452
3667
|
const truncated = truncateForBlogPromptPreview(validateContentBody(blogContent));
|
|
3453
3668
|
const prompt = PromptBuilder.blogImagePrompts(truncated);
|
|
3454
|
-
const
|
|
3455
|
-
|
|
3456
|
-
|
|
3457
|
-
|
|
3669
|
+
const prompts = await this.executor.runJson(prompt, [], {
|
|
3670
|
+
maxTokens: 500,
|
|
3671
|
+
temperature: 0.8
|
|
3672
|
+
});
|
|
3673
|
+
return Array.isArray(prompts) ? prompts : [];
|
|
3458
3674
|
}
|
|
3459
3675
|
};
|
|
3460
3676
|
|
|
@@ -3498,22 +3714,27 @@ var ContentCalendarService = class {
|
|
|
3498
3714
|
const validatedNiche = validateTopicText(niche);
|
|
3499
3715
|
const validatedDays = validateCalendarDays(days);
|
|
3500
3716
|
const prompt = PromptBuilder.contentCalendar(validatedNiche, validatedDays);
|
|
3501
|
-
const data = await this.executor.runJson(
|
|
3502
|
-
|
|
3503
|
-
|
|
3504
|
-
|
|
3505
|
-
|
|
3717
|
+
const data = await this.executor.runJson(
|
|
3718
|
+
prompt,
|
|
3719
|
+
[],
|
|
3720
|
+
{
|
|
3721
|
+
maxTokens: estimateTokenBudgetForCalendarDays(validatedDays),
|
|
3722
|
+
temperature: 0.8
|
|
3723
|
+
}
|
|
3724
|
+
);
|
|
3725
|
+
const items = Array.isArray(data) ? data : Array.isArray(data?.calendar) ? data.calendar : Array.isArray(data?.entries) ? data.entries : [];
|
|
3726
|
+
return items.map(toCalendarEntry);
|
|
3506
3727
|
}
|
|
3507
3728
|
};
|
|
3508
3729
|
|
|
3509
3730
|
// src/content/application/services/ProviderServices.ts
|
|
3510
3731
|
var wrapProviderError = (operation, error) => {
|
|
3511
3732
|
const message = error instanceof Error ? error.message : "Unknown error";
|
|
3512
|
-
return new
|
|
3733
|
+
return new AIError(`${operation} failed: ${message}`, "PROVIDER_ERROR", error);
|
|
3513
3734
|
};
|
|
3514
3735
|
var ensureFactory = (factory, operation) => {
|
|
3515
3736
|
if (factory.getAllProviders().length === 0) {
|
|
3516
|
-
throw new
|
|
3737
|
+
throw new AIError(
|
|
3517
3738
|
`${operation} failed: No providers are registered. Provide a ProviderConfig with at least one enabled provider when constructing AIContentService.`,
|
|
3518
3739
|
"PROVIDER_UNAVAILABLE"
|
|
3519
3740
|
);
|
|
@@ -3550,6 +3771,54 @@ var MediaGenerationService = class {
|
|
|
3550
3771
|
}
|
|
3551
3772
|
};
|
|
3552
3773
|
|
|
3774
|
+
// src/content/infrastructure/utils/jsonParser.ts
|
|
3775
|
+
function repairCommonJsonDefects2(text) {
|
|
3776
|
+
return text.replace(/(:\s*-?\d+(?:\.\d+)?)\s*"/g, "$1").replace(/(:\s*"[^"]*")"/g, "$1");
|
|
3777
|
+
}
|
|
3778
|
+
function safeJSONParse(text, fallback, onError) {
|
|
3779
|
+
try {
|
|
3780
|
+
return JSON.parse(text);
|
|
3781
|
+
} catch (error) {
|
|
3782
|
+
try {
|
|
3783
|
+
return JSON.parse(repairCommonJsonDefects2(text));
|
|
3784
|
+
} catch {
|
|
3785
|
+
}
|
|
3786
|
+
if (error instanceof SyntaxError) {
|
|
3787
|
+
const positionMatch = error.message.match(/position (\d+)/);
|
|
3788
|
+
const position = positionMatch ? parseInt(positionMatch[1], 10) : -1;
|
|
3789
|
+
if (position > 0) {
|
|
3790
|
+
text.substring(Math.max(0, position - 50), position + 50);
|
|
3791
|
+
}
|
|
3792
|
+
}
|
|
3793
|
+
return fallback;
|
|
3794
|
+
}
|
|
3795
|
+
}
|
|
3796
|
+
function extractJSON(text) {
|
|
3797
|
+
const codeBlockMatch = text.match(/```(?:json)?\s*([\s\S]*?)```/);
|
|
3798
|
+
if (codeBlockMatch && codeBlockMatch[1]) {
|
|
3799
|
+
const candidate = codeBlockMatch[1].trim();
|
|
3800
|
+
if (candidate.startsWith("{") || candidate.startsWith("[")) {
|
|
3801
|
+
return candidate;
|
|
3802
|
+
}
|
|
3803
|
+
}
|
|
3804
|
+
const objectMatch = text.match(/\{[\s\S]*\}/);
|
|
3805
|
+
if (objectMatch) {
|
|
3806
|
+
return objectMatch[0];
|
|
3807
|
+
}
|
|
3808
|
+
const arrayMatch = text.match(/\[[\s\S]*\]/);
|
|
3809
|
+
if (arrayMatch) {
|
|
3810
|
+
return arrayMatch[0];
|
|
3811
|
+
}
|
|
3812
|
+
return null;
|
|
3813
|
+
}
|
|
3814
|
+
function parseAIResponse(response, fallback, onError) {
|
|
3815
|
+
const json = extractJSON(response);
|
|
3816
|
+
if (!json) {
|
|
3817
|
+
return fallback;
|
|
3818
|
+
}
|
|
3819
|
+
return safeJSONParse(json, fallback);
|
|
3820
|
+
}
|
|
3821
|
+
|
|
3553
3822
|
// src/content/application/services/GenerationExecutor.ts
|
|
3554
3823
|
var GenerationExecutor = class {
|
|
3555
3824
|
constructor(generator) {
|
|
@@ -3566,7 +3835,7 @@ var GenerationExecutor = class {
|
|
|
3566
3835
|
/** Usage-complete pass-through; requires a ProviderFactory engine. */
|
|
3567
3836
|
runChat(request) {
|
|
3568
3837
|
if (!(this.generator instanceof ProviderFactory)) {
|
|
3569
|
-
throw new
|
|
3838
|
+
throw new AIError(
|
|
3570
3839
|
"runChat requires a ProviderFactory engine \u2014 construct AIContentService with a provider config, not a custom ITextGenerator",
|
|
3571
3840
|
"PROVIDER_UNAVAILABLE"
|
|
3572
3841
|
);
|
|
@@ -3580,6 +3849,13 @@ var isTextGenerator = (value) => typeof value === "object" && value !== null &&
|
|
|
3580
3849
|
function toAIProviderConfig(config) {
|
|
3581
3850
|
const legacy = config;
|
|
3582
3851
|
const modern = config;
|
|
3852
|
+
const anthropic = modern.anthropic ?? (legacy.anthropic?.enabled ? {
|
|
3853
|
+
apiKey: legacy.anthropic.apiKey,
|
|
3854
|
+
baseUrl: legacy.anthropic.baseUrl,
|
|
3855
|
+
timeoutMs: legacy.anthropic.timeout,
|
|
3856
|
+
models: legacy.anthropic.models,
|
|
3857
|
+
effort: legacy.anthropic.effort
|
|
3858
|
+
} : void 0);
|
|
3583
3859
|
const groq = modern.groq ?? (legacy.groq?.enabled ? {
|
|
3584
3860
|
apiKey: legacy.groq.apiKey,
|
|
3585
3861
|
baseUrl: legacy.groq.baseUrl,
|
|
@@ -3594,7 +3870,7 @@ function toAIProviderConfig(config) {
|
|
|
3594
3870
|
priority: modern.priority,
|
|
3595
3871
|
fallbackEnabled: modern.fallbackEnabled,
|
|
3596
3872
|
defaultTier: modern.defaultTier,
|
|
3597
|
-
anthropic:
|
|
3873
|
+
...anthropic ? { anthropic } : {},
|
|
3598
3874
|
...groq ? { groq } : {},
|
|
3599
3875
|
...gemini ? { gemini } : {},
|
|
3600
3876
|
...fal ? { fal } : {},
|
|
@@ -3760,8 +4036,8 @@ function useAIContentContext(options) {
|
|
|
3760
4036
|
callbacksRef.current?.onProgress?.(value);
|
|
3761
4037
|
}, []);
|
|
3762
4038
|
const reportError = useCallback((err, fallbackMessage) => {
|
|
3763
|
-
const aiError =
|
|
3764
|
-
const finalError = aiError.message ? aiError : new
|
|
4039
|
+
const aiError = AIError.from(err, "UNKNOWN");
|
|
4040
|
+
const finalError = aiError.message ? aiError : new AIError(fallbackMessage, "UNKNOWN", err);
|
|
3765
4041
|
if (mountedRef.current) {
|
|
3766
4042
|
setError(finalError.message);
|
|
3767
4043
|
setErrorCode(finalError.code);
|
|
@@ -3787,7 +4063,7 @@ function useAIContentContext(options) {
|
|
|
3787
4063
|
}
|
|
3788
4064
|
return result;
|
|
3789
4065
|
} catch (err) {
|
|
3790
|
-
const aiError =
|
|
4066
|
+
const aiError = AIError.from(err, "UNKNOWN");
|
|
3791
4067
|
if (aiError.message) {
|
|
3792
4068
|
reportError(aiError, fallbackMessage);
|
|
3793
4069
|
} else {
|
|
@@ -4351,10 +4627,13 @@ function useWizardFlow(options) {
|
|
|
4351
4627
|
if (steps.length <= 1) return 0;
|
|
4352
4628
|
return Math.round(currentIndex / (steps.length - 1) * 100);
|
|
4353
4629
|
}, [currentIndex, steps.length]);
|
|
4630
|
+
const completedRef = useRef(false);
|
|
4354
4631
|
const navigateTo = useCallback((to) => {
|
|
4355
4632
|
setCurrentStep((prev) => {
|
|
4356
4633
|
if (prev === to) return prev;
|
|
4357
|
-
|
|
4634
|
+
queueMicrotask(() => {
|
|
4635
|
+
optionsRef.current.onStepChange?.(prev, to);
|
|
4636
|
+
});
|
|
4358
4637
|
return to;
|
|
4359
4638
|
});
|
|
4360
4639
|
}, []);
|
|
@@ -4395,13 +4674,17 @@ function useWizardFlow(options) {
|
|
|
4395
4674
|
[currentIndex, steps.length]
|
|
4396
4675
|
);
|
|
4397
4676
|
const reset = useCallback(() => {
|
|
4677
|
+
completedRef.current = false;
|
|
4398
4678
|
setCurrentStep(optionsRef.current.initialStep);
|
|
4399
4679
|
setWizardData({});
|
|
4400
4680
|
setIsProcessing(false);
|
|
4401
4681
|
}, []);
|
|
4402
4682
|
useEffect(() => {
|
|
4403
|
-
if (currentIndex === steps.length - 1 && steps.length > 0) {
|
|
4683
|
+
if (currentIndex === steps.length - 1 && steps.length > 0 && !completedRef.current) {
|
|
4684
|
+
completedRef.current = true;
|
|
4404
4685
|
optionsRef.current.onComplete?.(wizardData);
|
|
4686
|
+
} else if (currentIndex < steps.length - 1) {
|
|
4687
|
+
completedRef.current = false;
|
|
4405
4688
|
}
|
|
4406
4689
|
}, [currentIndex, steps.length, wizardData]);
|
|
4407
4690
|
return {
|
|
@@ -4630,21 +4913,17 @@ function useText(options = {}, deps) {
|
|
|
4630
4913
|
]
|
|
4631
4914
|
);
|
|
4632
4915
|
useEffect(() => {
|
|
4633
|
-
const controller = new AbortController();
|
|
4634
|
-
abortControllerRef.current = controller;
|
|
4635
4916
|
return () => {
|
|
4636
|
-
|
|
4917
|
+
abortControllerRef.current?.abort();
|
|
4637
4918
|
abortControllerRef.current = null;
|
|
4638
4919
|
};
|
|
4639
4920
|
}, []);
|
|
4640
4921
|
const runWithState = useCallback(
|
|
4641
4922
|
async (task) => {
|
|
4642
|
-
|
|
4643
|
-
|
|
4644
|
-
|
|
4645
|
-
}
|
|
4923
|
+
abortControllerRef.current?.abort();
|
|
4924
|
+
const controller = new AbortController();
|
|
4925
|
+
abortControllerRef.current = controller;
|
|
4646
4926
|
const { signal } = controller;
|
|
4647
|
-
if (signal.aborted) return null;
|
|
4648
4927
|
setIsLoading(true);
|
|
4649
4928
|
setError(null);
|
|
4650
4929
|
setResult(null);
|
|
@@ -4665,6 +4944,9 @@ function useText(options = {}, deps) {
|
|
|
4665
4944
|
if (!signal.aborted) {
|
|
4666
4945
|
setIsLoading(false);
|
|
4667
4946
|
}
|
|
4947
|
+
if (abortControllerRef.current === controller) {
|
|
4948
|
+
abortControllerRef.current = null;
|
|
4949
|
+
}
|
|
4668
4950
|
}
|
|
4669
4951
|
},
|
|
4670
4952
|
[]
|
|
@@ -4729,6 +5011,7 @@ function useText(options = {}, deps) {
|
|
|
4729
5011
|
);
|
|
4730
5012
|
const cancel = useCallback(() => {
|
|
4731
5013
|
abortControllerRef.current?.abort();
|
|
5014
|
+
abortControllerRef.current = null;
|
|
4732
5015
|
setIsLoading(false);
|
|
4733
5016
|
}, []);
|
|
4734
5017
|
const reset = useCallback(() => {
|
|
@@ -4871,10 +5154,8 @@ function useChat(options, deps) {
|
|
|
4871
5154
|
};
|
|
4872
5155
|
}, [conversationId, surfaceError]);
|
|
4873
5156
|
useEffect(() => {
|
|
4874
|
-
const controller = new AbortController();
|
|
4875
|
-
abortControllerRef.current = controller;
|
|
4876
5157
|
return () => {
|
|
4877
|
-
|
|
5158
|
+
abortControllerRef.current?.abort();
|
|
4878
5159
|
abortControllerRef.current = null;
|
|
4879
5160
|
};
|
|
4880
5161
|
}, []);
|
|
@@ -4915,9 +5196,11 @@ function useChat(options, deps) {
|
|
|
4915
5196
|
});
|
|
4916
5197
|
void persist(userMessage);
|
|
4917
5198
|
callbacksRef.current.onMessageSent?.(userMessage);
|
|
5199
|
+
let streamingPlaceholderId = null;
|
|
4918
5200
|
try {
|
|
4919
5201
|
if (streamEnabled) {
|
|
4920
5202
|
const placeholderId = chatIdGenerator.next("assistant");
|
|
5203
|
+
streamingPlaceholderId = placeholderId;
|
|
4921
5204
|
const placeholder = {
|
|
4922
5205
|
id: placeholderId,
|
|
4923
5206
|
sender: "assistant",
|
|
@@ -4936,7 +5219,8 @@ function useChat(options, deps) {
|
|
|
4936
5219
|
(m) => m.id === placeholderId ? { ...m, content: m.content + chunk } : m
|
|
4937
5220
|
)
|
|
4938
5221
|
);
|
|
4939
|
-
}
|
|
5222
|
+
},
|
|
5223
|
+
controller.signal
|
|
4940
5224
|
);
|
|
4941
5225
|
const finalMessage = {
|
|
4942
5226
|
...placeholder,
|
|
@@ -4953,13 +5237,18 @@ function useChat(options, deps) {
|
|
|
4953
5237
|
const aiResponse = await chatService.generateAIResponse(
|
|
4954
5238
|
conversationId,
|
|
4955
5239
|
trimmed,
|
|
4956
|
-
contextForAI
|
|
5240
|
+
contextForAI,
|
|
5241
|
+
controller.signal
|
|
4957
5242
|
);
|
|
4958
5243
|
setMessages((prev) => [...prev, aiResponse]);
|
|
4959
5244
|
void persist(aiResponse);
|
|
4960
5245
|
callbacksRef.current.onAIResponse?.(aiResponse);
|
|
4961
5246
|
return aiResponse;
|
|
4962
5247
|
} catch (err) {
|
|
5248
|
+
if (streamingPlaceholderId) {
|
|
5249
|
+
const failedId = streamingPlaceholderId;
|
|
5250
|
+
setMessages((prev) => prev.filter((m) => m.id !== failedId));
|
|
5251
|
+
}
|
|
4963
5252
|
surfaceError(err);
|
|
4964
5253
|
return null;
|
|
4965
5254
|
} finally {
|
|
@@ -4985,6 +5274,9 @@ function useChat(options, deps) {
|
|
|
4985
5274
|
const lastUserMessage = [...currentMessages].reverse().find((m) => m.sender === "user");
|
|
4986
5275
|
if (!lastUserMessage) return null;
|
|
4987
5276
|
const trimmed = currentMessages.length > 0 && currentMessages[currentMessages.length - 1].sender === "assistant" ? currentMessages.slice(0, -1) : currentMessages;
|
|
5277
|
+
const controller = new AbortController();
|
|
5278
|
+
abortControllerRef.current?.abort();
|
|
5279
|
+
abortControllerRef.current = controller;
|
|
4988
5280
|
setIsLoading(true);
|
|
4989
5281
|
setError(null);
|
|
4990
5282
|
setIsTyping(true);
|
|
@@ -4992,7 +5284,8 @@ function useChat(options, deps) {
|
|
|
4992
5284
|
const aiResponse = await chatService.generateAIResponse(
|
|
4993
5285
|
conversationId,
|
|
4994
5286
|
lastUserMessage.content,
|
|
4995
|
-
trimmed
|
|
5287
|
+
trimmed,
|
|
5288
|
+
controller.signal
|
|
4996
5289
|
);
|
|
4997
5290
|
setMessages((prev) => {
|
|
4998
5291
|
const lastIsAI = prev.length > 0 && prev[prev.length - 1].sender === "assistant";
|
|
@@ -5008,6 +5301,9 @@ function useChat(options, deps) {
|
|
|
5008
5301
|
} finally {
|
|
5009
5302
|
setIsLoading(false);
|
|
5010
5303
|
setIsTyping(false);
|
|
5304
|
+
if (abortControllerRef.current === controller) {
|
|
5305
|
+
abortControllerRef.current = null;
|
|
5306
|
+
}
|
|
5011
5307
|
}
|
|
5012
5308
|
}, [chatService, conversationId, isLoading, messages, persist, surfaceError]);
|
|
5013
5309
|
const clearMessages = useCallback(() => {
|
|
@@ -5040,6 +5336,161 @@ function useChat(options, deps) {
|
|
|
5040
5336
|
};
|
|
5041
5337
|
}
|
|
5042
5338
|
|
|
5043
|
-
|
|
5339
|
+
// src/providers/groq/groq-container.ts
|
|
5340
|
+
var container = null;
|
|
5341
|
+
function getProvider() {
|
|
5342
|
+
return container;
|
|
5343
|
+
}
|
|
5344
|
+
|
|
5345
|
+
// src/hooks/use-groq.hook.ts
|
|
5346
|
+
function useGroq(options = {}, deps) {
|
|
5347
|
+
const textGeneration = deps?.textGeneration ?? getProvider()?.textGeneration;
|
|
5348
|
+
if (!textGeneration) {
|
|
5349
|
+
throw new Error(
|
|
5350
|
+
"useGroq requires a TextGenerationService dependency. Provide it via the second argument or wire it through configureProvider()."
|
|
5351
|
+
);
|
|
5352
|
+
}
|
|
5353
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
5354
|
+
const [error, setError] = useState(null);
|
|
5355
|
+
const [result, setResult] = useState(null);
|
|
5356
|
+
const optionsRef = useRef(options);
|
|
5357
|
+
optionsRef.current = options;
|
|
5358
|
+
const abortControllerRef = useRef(null);
|
|
5359
|
+
const getOrCreateAbortController = useCallback(() => {
|
|
5360
|
+
if (!abortControllerRef.current || abortControllerRef.current.signal.aborted) {
|
|
5361
|
+
abortControllerRef.current = new AbortController();
|
|
5362
|
+
}
|
|
5363
|
+
return abortControllerRef.current;
|
|
5364
|
+
}, []);
|
|
5365
|
+
const stableConfig = useMemo(
|
|
5366
|
+
() => ({
|
|
5367
|
+
model: options.model,
|
|
5368
|
+
generationConfig: options.generationConfig
|
|
5369
|
+
}),
|
|
5370
|
+
[
|
|
5371
|
+
options.model,
|
|
5372
|
+
options.generationConfig?.temperature,
|
|
5373
|
+
options.generationConfig?.maxTokens,
|
|
5374
|
+
options.generationConfig?.topP
|
|
5375
|
+
]
|
|
5376
|
+
);
|
|
5377
|
+
useEffect(() => {
|
|
5378
|
+
abortControllerRef.current = new AbortController();
|
|
5379
|
+
return () => {
|
|
5380
|
+
abortControllerRef.current?.abort();
|
|
5381
|
+
abortControllerRef.current = null;
|
|
5382
|
+
};
|
|
5383
|
+
}, []);
|
|
5384
|
+
const runWithState = useCallback(
|
|
5385
|
+
async (task) => {
|
|
5386
|
+
const controller = getOrCreateAbortController();
|
|
5387
|
+
const { signal } = controller;
|
|
5388
|
+
if (signal.aborted) return null;
|
|
5389
|
+
setIsLoading(true);
|
|
5390
|
+
setError(null);
|
|
5391
|
+
setResult(null);
|
|
5392
|
+
const currentOptions = optionsRef.current;
|
|
5393
|
+
currentOptions.onStart?.();
|
|
5394
|
+
try {
|
|
5395
|
+
const value = await task(signal);
|
|
5396
|
+
return value;
|
|
5397
|
+
} catch (err) {
|
|
5398
|
+
if (signal.aborted) {
|
|
5399
|
+
return null;
|
|
5400
|
+
}
|
|
5401
|
+
const message = resolveUserFriendlyMessage(err);
|
|
5402
|
+
setError(message);
|
|
5403
|
+
currentOptions.onError?.(message);
|
|
5404
|
+
throw err;
|
|
5405
|
+
} finally {
|
|
5406
|
+
if (!signal.aborted) {
|
|
5407
|
+
setIsLoading(false);
|
|
5408
|
+
}
|
|
5409
|
+
}
|
|
5410
|
+
},
|
|
5411
|
+
[getOrCreateAbortController]
|
|
5412
|
+
);
|
|
5413
|
+
const generate = useCallback(
|
|
5414
|
+
(prompt, config) => runWithState(async () => {
|
|
5415
|
+
const response = await textGeneration.generateCompletion(prompt, {
|
|
5416
|
+
model: stableConfig.model,
|
|
5417
|
+
generationConfig: {
|
|
5418
|
+
...stableConfig.generationConfig,
|
|
5419
|
+
...config
|
|
5420
|
+
}
|
|
5421
|
+
});
|
|
5422
|
+
setResult(response);
|
|
5423
|
+
optionsRef.current.onSuccess?.(response);
|
|
5424
|
+
return response;
|
|
5425
|
+
}),
|
|
5426
|
+
[runWithState, textGeneration, stableConfig]
|
|
5427
|
+
);
|
|
5428
|
+
const generateJSON = useCallback(
|
|
5429
|
+
(prompt, config) => runWithState(async () => {
|
|
5430
|
+
const response = await textGeneration.generateStructured(prompt, {
|
|
5431
|
+
model: stableConfig.model,
|
|
5432
|
+
generationConfig: {
|
|
5433
|
+
...stableConfig.generationConfig,
|
|
5434
|
+
...config
|
|
5435
|
+
},
|
|
5436
|
+
...config?.schema !== void 0 ? { schema: config.schema } : {}
|
|
5437
|
+
});
|
|
5438
|
+
const json = JSON.stringify(response, null, 2);
|
|
5439
|
+
setResult(json);
|
|
5440
|
+
optionsRef.current.onSuccess?.(json);
|
|
5441
|
+
return response;
|
|
5442
|
+
}),
|
|
5443
|
+
[runWithState, textGeneration, stableConfig]
|
|
5444
|
+
);
|
|
5445
|
+
const stream = useCallback(
|
|
5446
|
+
(prompt, onChunk, config) => runWithState(async () => {
|
|
5447
|
+
const callbacks = {
|
|
5448
|
+
onChunk,
|
|
5449
|
+
onComplete: (text) => {
|
|
5450
|
+
setResult(text);
|
|
5451
|
+
optionsRef.current.onSuccess?.(text);
|
|
5452
|
+
}
|
|
5453
|
+
};
|
|
5454
|
+
return textGeneration.streamCompletion(
|
|
5455
|
+
prompt,
|
|
5456
|
+
callbacks,
|
|
5457
|
+
{
|
|
5458
|
+
model: stableConfig.model,
|
|
5459
|
+
generationConfig: {
|
|
5460
|
+
...stableConfig.generationConfig,
|
|
5461
|
+
...config
|
|
5462
|
+
}
|
|
5463
|
+
}
|
|
5464
|
+
);
|
|
5465
|
+
}),
|
|
5466
|
+
[runWithState, textGeneration, stableConfig]
|
|
5467
|
+
);
|
|
5468
|
+
const cancel = useCallback(() => {
|
|
5469
|
+
if (abortControllerRef.current) {
|
|
5470
|
+
abortControllerRef.current.abort();
|
|
5471
|
+
abortControllerRef.current = new AbortController();
|
|
5472
|
+
}
|
|
5473
|
+
setIsLoading(false);
|
|
5474
|
+
}, []);
|
|
5475
|
+
const reset = useCallback(() => {
|
|
5476
|
+
cancel();
|
|
5477
|
+
setError(null);
|
|
5478
|
+
setResult(null);
|
|
5479
|
+
}, [cancel]);
|
|
5480
|
+
const clearError = useCallback(() => setError(null), []);
|
|
5481
|
+
return {
|
|
5482
|
+
isLoading,
|
|
5483
|
+
error,
|
|
5484
|
+
result,
|
|
5485
|
+
generate,
|
|
5486
|
+
generateJSON,
|
|
5487
|
+
stream,
|
|
5488
|
+
cancel,
|
|
5489
|
+
reset,
|
|
5490
|
+
clearError
|
|
5491
|
+
};
|
|
5492
|
+
}
|
|
5493
|
+
|
|
5494
|
+
export { useAIContent, useAIGeneration, useBlogGenerator, useCacheStats, useChat, useContentWizard, useGroq, useImageWizard, useSocialContentGenerator, useText, useVideoWizard };
|
|
5044
5495
|
//# sourceMappingURL=index.js.map
|
|
5045
5496
|
//# sourceMappingURL=index.js.map
|