@umituz/web-ai 1.0.0 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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 +393 -186
- 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 +393 -186
- package/dist/content/index.js.map +1 -1
- package/dist/core/index.cjs +112 -15
- package/dist/core/index.cjs.map +1 -1
- package/dist/core/index.d.cts +5 -33
- package/dist/core/index.d.ts +5 -33
- package/dist/core/index.js +111 -16
- package/dist/core/index.js.map +1 -1
- package/dist/{create-ai-DGZFBOA1.d.cts → create-ai-DOI9jD3v.d.cts} +4 -21
- package/dist/{create-ai-BhNaz86i.d.ts → create-ai-DXEoke8-.d.ts} +4 -21
- package/dist/hooks/index.cjs +359 -169
- package/dist/hooks/index.cjs.map +1 -1
- package/dist/hooks/index.d.cts +6 -6
- package/dist/hooks/index.d.ts +6 -6
- package/dist/hooks/index.js +359 -169
- 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 +245 -94
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +6 -6
- package/dist/index.d.ts +6 -6
- package/dist/index.js +244 -95
- 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 +281 -124
- package/dist/providers/index.cjs.map +1 -1
- package/dist/providers/index.d.cts +11 -12
- package/dist/providers/index.d.ts +11 -12
- package/dist/providers/index.js +281 -124
- package/dist/providers/index.js.map +1 -1
- package/dist/wizard/index.d.cts +1 -1
- package/dist/wizard/index.d.ts +1 -1
- package/package.json +4 -1
- package/dist/index-DjX9ZKuM.d.cts +0 -14
- package/dist/index-DxLvE588.d.ts +0 -14
package/dist/content/index.js
CHANGED
|
@@ -73,25 +73,49 @@ var VIDEO_DURATION_OPTIONS = VIDEO_DURATION_OPTIONS_SECONDS;
|
|
|
73
73
|
var getDimensionsForAspectRatio = resolveDimensionsForAspectRatio;
|
|
74
74
|
var getRecommendedQuality = recommendQualityForAspectRatio;
|
|
75
75
|
|
|
76
|
-
// src/
|
|
77
|
-
var
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
76
|
+
// src/core/errors/ai-error-types.ts
|
|
77
|
+
var AIErrorType = /* @__PURE__ */ ((AIErrorType2) => {
|
|
78
|
+
AIErrorType2["INVALID_API_KEY"] = "INVALID_API_KEY";
|
|
79
|
+
AIErrorType2["MISSING_CONFIG"] = "MISSING_CONFIG";
|
|
80
|
+
AIErrorType2["NETWORK_ERROR"] = "NETWORK_ERROR";
|
|
81
|
+
AIErrorType2["ABORT_ERROR"] = "ABORT_ERROR";
|
|
82
|
+
AIErrorType2["RATE_LIMIT_ERROR"] = "RATE_LIMIT_ERROR";
|
|
83
|
+
AIErrorType2["SERVER_ERROR"] = "SERVER_ERROR";
|
|
84
|
+
AIErrorType2["CLIENT_ERROR"] = "CLIENT_ERROR";
|
|
85
|
+
AIErrorType2["UNKNOWN_ERROR"] = "UNKNOWN_ERROR";
|
|
86
|
+
return AIErrorType2;
|
|
87
|
+
})(AIErrorType || {});
|
|
88
|
+
|
|
89
|
+
// src/core/errors/ai-error.ts
|
|
90
|
+
var KNOWN_ERROR_TYPES = new Set(Object.values(AIErrorType));
|
|
91
|
+
function mapCodeToType(code) {
|
|
92
|
+
switch (code) {
|
|
93
|
+
case "VALIDATION_ERROR":
|
|
94
|
+
case "CLIENT_ERROR":
|
|
95
|
+
return "CLIENT_ERROR" /* CLIENT_ERROR */;
|
|
96
|
+
case "INVALID_API_KEY":
|
|
97
|
+
return "INVALID_API_KEY" /* INVALID_API_KEY */;
|
|
98
|
+
case "MISSING_CONFIG":
|
|
99
|
+
case "PROVIDER_UNAVAILABLE":
|
|
100
|
+
return "MISSING_CONFIG" /* MISSING_CONFIG */;
|
|
101
|
+
case "PROVIDER_QUOTA_EXHAUSTED":
|
|
102
|
+
case "RATE_LIMIT_ERROR":
|
|
103
|
+
return "RATE_LIMIT_ERROR" /* RATE_LIMIT_ERROR */;
|
|
104
|
+
case "NETWORK_ERROR":
|
|
105
|
+
case "TIMEOUT":
|
|
106
|
+
return "NETWORK_ERROR" /* NETWORK_ERROR */;
|
|
107
|
+
case "ABORT_ERROR":
|
|
108
|
+
case "ABORTED":
|
|
109
|
+
return "ABORT_ERROR" /* ABORT_ERROR */;
|
|
110
|
+
case "SERVER_ERROR":
|
|
111
|
+
case "PROVIDER_ERROR":
|
|
112
|
+
case "NO_RESULT":
|
|
113
|
+
return "SERVER_ERROR" /* SERVER_ERROR */;
|
|
114
|
+
default:
|
|
115
|
+
return "UNKNOWN_ERROR" /* UNKNOWN_ERROR */;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
function mapMessageToCode(message, fallback = "UNKNOWN") {
|
|
95
119
|
const lower = message.toLowerCase();
|
|
96
120
|
if (lower.includes("validation")) return "VALIDATION_ERROR";
|
|
97
121
|
if (lower.includes("quota")) return "PROVIDER_QUOTA_EXHAUSTED";
|
|
@@ -104,37 +128,39 @@ function mapMessageToCode(message, fallback) {
|
|
|
104
128
|
if (lower.includes("network") || lower.includes("fetch")) return "NETWORK_ERROR";
|
|
105
129
|
return fallback;
|
|
106
130
|
}
|
|
107
|
-
var
|
|
108
|
-
constructor(message) {
|
|
109
|
-
super(message, "VALIDATION_ERROR");
|
|
110
|
-
this.name = "ValidationError";
|
|
111
|
-
}
|
|
112
|
-
};
|
|
113
|
-
var ProviderUnavailableError = class extends AIError {
|
|
114
|
-
constructor(providerId) {
|
|
115
|
-
super(`Provider ${providerId} is not available`, "PROVIDER_UNAVAILABLE");
|
|
116
|
-
this.name = "ProviderUnavailableError";
|
|
117
|
-
}
|
|
118
|
-
};
|
|
119
|
-
var ProviderQuotaExhaustedError = class extends AIError {
|
|
120
|
-
constructor(providerId) {
|
|
121
|
-
super(`Provider ${providerId} quota exhausted`, "PROVIDER_QUOTA_EXHAUSTED");
|
|
122
|
-
this.name = "ProviderQuotaExhaustedError";
|
|
123
|
-
}
|
|
124
|
-
};
|
|
125
|
-
|
|
126
|
-
// src/core/errors/ai-error.ts
|
|
127
|
-
var AIError2 = class extends Error {
|
|
131
|
+
var AIError = class _AIError extends Error {
|
|
128
132
|
type;
|
|
133
|
+
code;
|
|
129
134
|
cause;
|
|
130
135
|
metadata;
|
|
131
|
-
constructor(
|
|
132
|
-
|
|
136
|
+
constructor(typeOrMessage, messageOrCode, cause, metadata = {}) {
|
|
137
|
+
let effectiveType;
|
|
138
|
+
let effectiveMessage;
|
|
139
|
+
let effectiveCode;
|
|
140
|
+
if (KNOWN_ERROR_TYPES.has(typeOrMessage)) {
|
|
141
|
+
effectiveType = typeOrMessage;
|
|
142
|
+
effectiveMessage = messageOrCode ?? "";
|
|
143
|
+
effectiveCode = effectiveType;
|
|
144
|
+
} else {
|
|
145
|
+
effectiveMessage = typeOrMessage;
|
|
146
|
+
effectiveCode = messageOrCode ?? "UNKNOWN";
|
|
147
|
+
effectiveType = mapCodeToType(effectiveCode);
|
|
148
|
+
}
|
|
149
|
+
super(effectiveMessage);
|
|
133
150
|
this.name = "AIError";
|
|
134
|
-
this.type =
|
|
151
|
+
this.type = effectiveType;
|
|
152
|
+
this.code = effectiveCode;
|
|
135
153
|
this.cause = cause;
|
|
136
154
|
this.metadata = metadata;
|
|
137
155
|
}
|
|
156
|
+
static from(error, fallbackCode = "UNKNOWN") {
|
|
157
|
+
if (error instanceof _AIError) return error;
|
|
158
|
+
if (error instanceof Error) {
|
|
159
|
+
const code = mapMessageToCode(error.message, fallbackCode);
|
|
160
|
+
return new _AIError(error.message, code, error);
|
|
161
|
+
}
|
|
162
|
+
return new _AIError(String(error), fallbackCode, error);
|
|
163
|
+
}
|
|
138
164
|
toString() {
|
|
139
165
|
const statusPart = typeof this.metadata.status === "number" ? ` (HTTP ${this.metadata.status})` : "";
|
|
140
166
|
return `${this.name} [${this.type}]: ${this.message}${statusPart}`;
|
|
@@ -143,12 +169,33 @@ var AIError2 = class extends Error {
|
|
|
143
169
|
return {
|
|
144
170
|
name: this.name,
|
|
145
171
|
type: this.type,
|
|
172
|
+
code: this.code,
|
|
146
173
|
message: this.message,
|
|
147
174
|
metadata: this.metadata
|
|
148
175
|
};
|
|
149
176
|
}
|
|
150
177
|
};
|
|
151
178
|
|
|
179
|
+
// src/content/domain/errors/AIErrors.ts
|
|
180
|
+
var ValidationError = class extends AIError {
|
|
181
|
+
constructor(message) {
|
|
182
|
+
super(message, "VALIDATION_ERROR");
|
|
183
|
+
this.name = "ValidationError";
|
|
184
|
+
}
|
|
185
|
+
};
|
|
186
|
+
var ProviderUnavailableError = class extends AIError {
|
|
187
|
+
constructor(providerId) {
|
|
188
|
+
super(`Provider ${providerId} is not available`, "PROVIDER_UNAVAILABLE");
|
|
189
|
+
this.name = "ProviderUnavailableError";
|
|
190
|
+
}
|
|
191
|
+
};
|
|
192
|
+
var ProviderQuotaExhaustedError = class extends AIError {
|
|
193
|
+
constructor(providerId) {
|
|
194
|
+
super(`Provider ${providerId} quota exhausted`, "PROVIDER_QUOTA_EXHAUSTED");
|
|
195
|
+
this.name = "ProviderQuotaExhaustedError";
|
|
196
|
+
}
|
|
197
|
+
};
|
|
198
|
+
|
|
152
199
|
// src/core/errors/error-classifier.ts
|
|
153
200
|
var DEFAULT_RETRYABLE_TYPES = /* @__PURE__ */ new Set([
|
|
154
201
|
"NETWORK_ERROR" /* NETWORK_ERROR */,
|
|
@@ -291,7 +338,7 @@ var ProviderFactory = class {
|
|
|
291
338
|
}
|
|
292
339
|
}
|
|
293
340
|
}
|
|
294
|
-
const failure = new
|
|
341
|
+
const failure = new AIError(
|
|
295
342
|
"SERVER_ERROR" /* SERVER_ERROR */,
|
|
296
343
|
`All providers failed for capability 'text'`,
|
|
297
344
|
lastError instanceof Error ? lastError : void 0
|
|
@@ -305,12 +352,16 @@ var ProviderFactory = class {
|
|
|
305
352
|
let wake = null;
|
|
306
353
|
let finished = false;
|
|
307
354
|
let failure;
|
|
355
|
+
const controller = new AbortController();
|
|
356
|
+
const onAbort = () => controller.abort();
|
|
357
|
+
request.signal?.addEventListener("abort", onAbort);
|
|
358
|
+
const effectiveRequest = { ...request, signal: controller.signal };
|
|
308
359
|
const notify = () => {
|
|
309
360
|
const resolve = wake;
|
|
310
361
|
wake = null;
|
|
311
362
|
resolve?.();
|
|
312
363
|
};
|
|
313
|
-
const completion = this.chatStream(
|
|
364
|
+
const completion = this.chatStream(effectiveRequest, {
|
|
314
365
|
onChunk: (chunk) => {
|
|
315
366
|
queue.push(chunk);
|
|
316
367
|
notify();
|
|
@@ -326,18 +377,23 @@ var ProviderFactory = class {
|
|
|
326
377
|
notify();
|
|
327
378
|
}
|
|
328
379
|
);
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
380
|
+
try {
|
|
381
|
+
while (!finished || queue.length > 0) {
|
|
382
|
+
if (queue.length === 0 && !finished) {
|
|
383
|
+
await new Promise((resolve) => {
|
|
384
|
+
wake = resolve;
|
|
385
|
+
});
|
|
386
|
+
}
|
|
387
|
+
while (queue.length > 0) {
|
|
388
|
+
yield queue.shift();
|
|
389
|
+
}
|
|
390
|
+
if (failure) throw failure;
|
|
337
391
|
}
|
|
338
|
-
|
|
392
|
+
await completion;
|
|
393
|
+
} finally {
|
|
394
|
+
controller.abort();
|
|
395
|
+
request.signal?.removeEventListener("abort", onAbort);
|
|
339
396
|
}
|
|
340
|
-
await completion;
|
|
341
397
|
}
|
|
342
398
|
async generateJson(request) {
|
|
343
399
|
const effective = request.model || request.tier ? request : { ...request, tier: this.defaultTier };
|
|
@@ -445,7 +501,7 @@ var ProviderFactory = class {
|
|
|
445
501
|
}
|
|
446
502
|
}
|
|
447
503
|
}
|
|
448
|
-
throw new
|
|
504
|
+
throw new AIError(
|
|
449
505
|
"SERVER_ERROR" /* SERVER_ERROR */,
|
|
450
506
|
`All providers failed for capability '${capability}'`,
|
|
451
507
|
lastError instanceof Error ? lastError : void 0
|
|
@@ -474,7 +530,7 @@ var ProviderFactory = class {
|
|
|
474
530
|
}
|
|
475
531
|
}
|
|
476
532
|
noProviderError(capability) {
|
|
477
|
-
return new
|
|
533
|
+
return new AIError(
|
|
478
534
|
"MISSING_CONFIG" /* MISSING_CONFIG */,
|
|
479
535
|
`No provider registered for capability '${capability}' \u2014 create one with createAI() and include its config block`
|
|
480
536
|
);
|
|
@@ -484,15 +540,22 @@ var ProviderFactory = class {
|
|
|
484
540
|
// src/core/constants/protocol.constants.ts
|
|
485
541
|
var SSE_PREFIX = "data: ";
|
|
486
542
|
var SSE_DONE_MARKER = "[DONE]";
|
|
487
|
-
var JSON_CODE_BLOCK_PATTERN = /```json
|
|
543
|
+
var JSON_CODE_BLOCK_PATTERN = /```(?:json)?\s*\n?([\s\S]*?)\n?```/;
|
|
488
544
|
var JSON_OBJECT_PATTERN = /\{[\s\S]*\}/;
|
|
545
|
+
var JSON_ARRAY_PATTERN = /\[[\s\S]*\]/;
|
|
489
546
|
|
|
490
547
|
// src/core/parsers/sse-lines.ts
|
|
491
548
|
function isSseDataLine(line) {
|
|
492
|
-
return line.startsWith(SSE_PREFIX);
|
|
549
|
+
return line.startsWith(SSE_PREFIX) || line.startsWith("data:");
|
|
493
550
|
}
|
|
494
551
|
function extractSseDataPayload(line) {
|
|
495
|
-
|
|
552
|
+
if (line.startsWith(SSE_PREFIX)) {
|
|
553
|
+
return line.slice(SSE_PREFIX.length);
|
|
554
|
+
}
|
|
555
|
+
if (line.startsWith("data:")) {
|
|
556
|
+
return line.slice(5);
|
|
557
|
+
}
|
|
558
|
+
return line;
|
|
496
559
|
}
|
|
497
560
|
function isSseDoneMarker(payload) {
|
|
498
561
|
return payload === SSE_DONE_MARKER;
|
|
@@ -501,7 +564,7 @@ function isSseDoneMarker(payload) {
|
|
|
501
564
|
// src/core/parsers/json-content-extractor.ts
|
|
502
565
|
function extractJsonFromCodeFence(content) {
|
|
503
566
|
const match = content.match(JSON_CODE_BLOCK_PATTERN);
|
|
504
|
-
if (match && match[1]) return match[1];
|
|
567
|
+
if (match && match[1]) return match[1].trim();
|
|
505
568
|
return null;
|
|
506
569
|
}
|
|
507
570
|
function extractJsonObject(content) {
|
|
@@ -509,8 +572,27 @@ function extractJsonObject(content) {
|
|
|
509
572
|
if (match && match[0]) return match[0];
|
|
510
573
|
return null;
|
|
511
574
|
}
|
|
575
|
+
function extractJsonArray(content) {
|
|
576
|
+
const match = content.match(JSON_ARRAY_PATTERN);
|
|
577
|
+
if (match && match[0]) return match[0];
|
|
578
|
+
return null;
|
|
579
|
+
}
|
|
580
|
+
function repairCommonJsonDefects(text) {
|
|
581
|
+
return text.replace(/(:\s*-?\d+(?:\.\d+)?)\s*"/g, "$1").replace(/(:\s*"[^"]*")"/g, "$1");
|
|
582
|
+
}
|
|
512
583
|
function extractJsonCandidate(content) {
|
|
513
|
-
|
|
584
|
+
const fromFence = extractJsonFromCodeFence(content);
|
|
585
|
+
if (fromFence) return fromFence;
|
|
586
|
+
const objectIndex = content.indexOf("{");
|
|
587
|
+
const arrayIndex = content.indexOf("[");
|
|
588
|
+
if (objectIndex !== -1 && (arrayIndex === -1 || objectIndex < arrayIndex)) {
|
|
589
|
+
const obj = extractJsonObject(content);
|
|
590
|
+
if (obj) return obj;
|
|
591
|
+
} else if (arrayIndex !== -1) {
|
|
592
|
+
const arr = extractJsonArray(content);
|
|
593
|
+
if (arr) return arr;
|
|
594
|
+
}
|
|
595
|
+
return content.trim();
|
|
514
596
|
}
|
|
515
597
|
|
|
516
598
|
// src/core/parsers/json-response.parser.ts
|
|
@@ -519,12 +601,17 @@ var JsonResponseParser = class {
|
|
|
519
601
|
const candidate = extractJsonCandidate(rawContent);
|
|
520
602
|
try {
|
|
521
603
|
return JSON.parse(candidate);
|
|
522
|
-
} catch
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
604
|
+
} catch {
|
|
605
|
+
try {
|
|
606
|
+
return JSON.parse(repairCommonJsonDefects(candidate));
|
|
607
|
+
} catch (error) {
|
|
608
|
+
throw new AIError(
|
|
609
|
+
"UNKNOWN_ERROR" /* UNKNOWN_ERROR */,
|
|
610
|
+
"Failed to parse structured response as JSON",
|
|
611
|
+
error instanceof Error ? error : void 0,
|
|
612
|
+
{ rawContent }
|
|
613
|
+
);
|
|
614
|
+
}
|
|
528
615
|
}
|
|
529
616
|
}
|
|
530
617
|
};
|
|
@@ -650,7 +737,7 @@ var AnthropicHttpTransport = class {
|
|
|
650
737
|
});
|
|
651
738
|
if (!response.ok) await this.throwFromErrorResponse(response);
|
|
652
739
|
if (!response.body) {
|
|
653
|
-
throw new
|
|
740
|
+
throw new AIError("NETWORK_ERROR" /* NETWORK_ERROR */, "Response body is null");
|
|
654
741
|
}
|
|
655
742
|
return response.body;
|
|
656
743
|
} catch (error) {
|
|
@@ -683,7 +770,7 @@ var AnthropicHttpTransport = class {
|
|
|
683
770
|
status: response.status,
|
|
684
771
|
url: response.url
|
|
685
772
|
};
|
|
686
|
-
throw new
|
|
773
|
+
throw new AIError(
|
|
687
774
|
classifyHttpStatus(response.status),
|
|
688
775
|
errorMessage,
|
|
689
776
|
void 0,
|
|
@@ -691,24 +778,24 @@ var AnthropicHttpTransport = class {
|
|
|
691
778
|
);
|
|
692
779
|
}
|
|
693
780
|
normalizeError(error) {
|
|
694
|
-
if (error instanceof
|
|
781
|
+
if (error instanceof AIError) return error;
|
|
695
782
|
if (error instanceof Error) {
|
|
696
783
|
if (error.name === "AbortError") {
|
|
697
|
-
return new
|
|
784
|
+
return new AIError(
|
|
698
785
|
"ABORT_ERROR" /* ABORT_ERROR */,
|
|
699
786
|
"Request was aborted by the client",
|
|
700
787
|
error
|
|
701
788
|
);
|
|
702
789
|
}
|
|
703
790
|
if (error.name === "TypeError" && error.message.toLowerCase().includes("network")) {
|
|
704
|
-
return new
|
|
791
|
+
return new AIError(
|
|
705
792
|
"NETWORK_ERROR" /* NETWORK_ERROR */,
|
|
706
793
|
"Network error: Unable to connect to the AI service",
|
|
707
794
|
error
|
|
708
795
|
);
|
|
709
796
|
}
|
|
710
797
|
}
|
|
711
|
-
return new
|
|
798
|
+
return new AIError(
|
|
712
799
|
"UNKNOWN_ERROR" /* UNKNOWN_ERROR */,
|
|
713
800
|
error instanceof Error ? error.message : "Unknown error occurred",
|
|
714
801
|
error instanceof Error ? error : void 0
|
|
@@ -838,11 +925,14 @@ async function consumeAnthropicStream(stream, callbacks) {
|
|
|
838
925
|
break;
|
|
839
926
|
case "message_stop":
|
|
840
927
|
break;
|
|
841
|
-
case "error":
|
|
842
|
-
|
|
843
|
-
|
|
928
|
+
case "error": {
|
|
929
|
+
const errType = event.error.type;
|
|
930
|
+
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 */;
|
|
931
|
+
throw new AIError(
|
|
932
|
+
mappedType,
|
|
844
933
|
event.error.message || "Anthropic stream failed mid-flight"
|
|
845
934
|
);
|
|
935
|
+
}
|
|
846
936
|
}
|
|
847
937
|
};
|
|
848
938
|
try {
|
|
@@ -857,8 +947,8 @@ async function consumeAnthropicStream(stream, callbacks) {
|
|
|
857
947
|
buffer += decoder.decode();
|
|
858
948
|
if (buffer.length > 0) consumeLine(buffer);
|
|
859
949
|
} catch (error) {
|
|
860
|
-
if (error instanceof
|
|
861
|
-
throw new
|
|
950
|
+
if (error instanceof AIError) throw error;
|
|
951
|
+
throw new AIError(
|
|
862
952
|
"NETWORK_ERROR" /* NETWORK_ERROR */,
|
|
863
953
|
"Stream was interrupted",
|
|
864
954
|
error instanceof Error ? error : void 0
|
|
@@ -920,7 +1010,7 @@ var AnthropicProvider = class {
|
|
|
920
1010
|
const messages = [...request.messages];
|
|
921
1011
|
const lastUserIndex = findLastIndex(messages, (m) => m.role === "user");
|
|
922
1012
|
if (lastUserIndex === -1) {
|
|
923
|
-
throw new
|
|
1013
|
+
throw new AIError(
|
|
924
1014
|
"UNKNOWN_ERROR" /* UNKNOWN_ERROR */,
|
|
925
1015
|
"generateStructured requires at least one user message"
|
|
926
1016
|
);
|
|
@@ -975,7 +1065,7 @@ var AnthropicProvider = class {
|
|
|
975
1065
|
finishReason
|
|
976
1066
|
};
|
|
977
1067
|
}
|
|
978
|
-
throw new
|
|
1068
|
+
throw new AIError(
|
|
979
1069
|
"SERVER_ERROR" /* SERVER_ERROR */,
|
|
980
1070
|
"No content generated from Anthropic API",
|
|
981
1071
|
void 0,
|
|
@@ -1180,7 +1270,7 @@ var RetryManager = class {
|
|
|
1180
1270
|
}
|
|
1181
1271
|
shouldRetry(error, options) {
|
|
1182
1272
|
if (options.shouldRetry) return options.shouldRetry(error);
|
|
1183
|
-
if (error instanceof
|
|
1273
|
+
if (error instanceof AIError) {
|
|
1184
1274
|
return isRetryableErrorType(error.type, DEFAULT_ERROR_CLASSIFICATION_RULE);
|
|
1185
1275
|
}
|
|
1186
1276
|
const lower = error.message.toLowerCase();
|
|
@@ -1417,7 +1507,7 @@ var GroqHttpTransport = class {
|
|
|
1417
1507
|
});
|
|
1418
1508
|
if (!response.ok) await this.throwFromErrorResponse(response);
|
|
1419
1509
|
if (!response.body) {
|
|
1420
|
-
throw new
|
|
1510
|
+
throw new AIError("NETWORK_ERROR" /* NETWORK_ERROR */, "Response body is null");
|
|
1421
1511
|
}
|
|
1422
1512
|
return response.body;
|
|
1423
1513
|
} catch (error) {
|
|
@@ -1465,7 +1555,7 @@ var GroqHttpTransport = class {
|
|
|
1465
1555
|
status: response.status,
|
|
1466
1556
|
url: response.url
|
|
1467
1557
|
};
|
|
1468
|
-
throw new
|
|
1558
|
+
throw new AIError(
|
|
1469
1559
|
classifyHttpStatus(response.status),
|
|
1470
1560
|
errorMessage,
|
|
1471
1561
|
void 0,
|
|
@@ -1473,24 +1563,24 @@ var GroqHttpTransport = class {
|
|
|
1473
1563
|
);
|
|
1474
1564
|
}
|
|
1475
1565
|
normalizeError(error) {
|
|
1476
|
-
if (error instanceof
|
|
1566
|
+
if (error instanceof AIError) return error;
|
|
1477
1567
|
if (error instanceof Error) {
|
|
1478
1568
|
if (error.name === "AbortError") {
|
|
1479
|
-
return new
|
|
1569
|
+
return new AIError(
|
|
1480
1570
|
"ABORT_ERROR" /* ABORT_ERROR */,
|
|
1481
1571
|
"Request was aborted by the client",
|
|
1482
1572
|
error
|
|
1483
1573
|
);
|
|
1484
1574
|
}
|
|
1485
1575
|
if (error.name === "TypeError" && error.message.toLowerCase().includes("network")) {
|
|
1486
|
-
return new
|
|
1576
|
+
return new AIError(
|
|
1487
1577
|
"NETWORK_ERROR" /* NETWORK_ERROR */,
|
|
1488
1578
|
"Network error: Unable to connect to the AI service",
|
|
1489
1579
|
error
|
|
1490
1580
|
);
|
|
1491
1581
|
}
|
|
1492
1582
|
}
|
|
1493
|
-
return new
|
|
1583
|
+
return new AIError(
|
|
1494
1584
|
"UNKNOWN_ERROR" /* UNKNOWN_ERROR */,
|
|
1495
1585
|
error instanceof Error ? error.message : "Unknown error occurred",
|
|
1496
1586
|
error instanceof Error ? error : void 0
|
|
@@ -1632,6 +1722,13 @@ async function consumeGroqStream(stream, callbacks) {
|
|
|
1632
1722
|
} catch {
|
|
1633
1723
|
return;
|
|
1634
1724
|
}
|
|
1725
|
+
const errorBody = chunk.error;
|
|
1726
|
+
if (errorBody) {
|
|
1727
|
+
throw new AIError(
|
|
1728
|
+
errorBody.message?.toLowerCase().includes("rate limit") ? "RATE_LIMIT_ERROR" /* RATE_LIMIT_ERROR */ : "SERVER_ERROR" /* SERVER_ERROR */,
|
|
1729
|
+
errorBody.message || "Groq stream failed mid-flight"
|
|
1730
|
+
);
|
|
1731
|
+
}
|
|
1635
1732
|
if (chunk.model) model = chunk.model;
|
|
1636
1733
|
const choice = chunk.choices?.[0];
|
|
1637
1734
|
const delta = choice?.delta?.content;
|
|
@@ -1646,13 +1743,6 @@ async function consumeGroqStream(stream, callbacks) {
|
|
|
1646
1743
|
if (tailUsage) {
|
|
1647
1744
|
usage = mapGroqUsage(tailUsage);
|
|
1648
1745
|
}
|
|
1649
|
-
const errorBody = chunk.error;
|
|
1650
|
-
if (errorBody) {
|
|
1651
|
-
throw new AIError2(
|
|
1652
|
-
"SERVER_ERROR" /* SERVER_ERROR */,
|
|
1653
|
-
errorBody.message || "Groq stream failed mid-flight"
|
|
1654
|
-
);
|
|
1655
|
-
}
|
|
1656
1746
|
};
|
|
1657
1747
|
try {
|
|
1658
1748
|
while (true) {
|
|
@@ -1666,8 +1756,8 @@ async function consumeGroqStream(stream, callbacks) {
|
|
|
1666
1756
|
buffer += decoder.decode();
|
|
1667
1757
|
if (buffer.length > 0) consumeLine(buffer);
|
|
1668
1758
|
} catch (error) {
|
|
1669
|
-
if (error instanceof
|
|
1670
|
-
throw new
|
|
1759
|
+
if (error instanceof AIError) throw error;
|
|
1760
|
+
throw new AIError(
|
|
1671
1761
|
"NETWORK_ERROR" /* NETWORK_ERROR */,
|
|
1672
1762
|
"Stream was interrupted",
|
|
1673
1763
|
error instanceof Error ? error : void 0
|
|
@@ -1725,7 +1815,7 @@ var GroqProvider = class {
|
|
|
1725
1815
|
const choice = response.choices?.[0];
|
|
1726
1816
|
const text = choice?.message?.content ?? "";
|
|
1727
1817
|
if (text.length === 0) {
|
|
1728
|
-
throw new
|
|
1818
|
+
throw new AIError(
|
|
1729
1819
|
"SERVER_ERROR" /* SERVER_ERROR */,
|
|
1730
1820
|
"No content generated from Groq API",
|
|
1731
1821
|
void 0,
|
|
@@ -1747,7 +1837,7 @@ var GroqProvider = class {
|
|
|
1747
1837
|
const messages = toGroqMessages(request.messages, request.systemPrompt);
|
|
1748
1838
|
const lastUserIndex = findLastIndex2(messages, (m) => m.role === "user");
|
|
1749
1839
|
if (lastUserIndex === -1) {
|
|
1750
|
-
throw new
|
|
1840
|
+
throw new AIError(
|
|
1751
1841
|
"UNKNOWN_ERROR" /* UNKNOWN_ERROR */,
|
|
1752
1842
|
"generateStructured requires at least one user message"
|
|
1753
1843
|
);
|
|
@@ -1761,7 +1851,7 @@ var GroqProvider = class {
|
|
|
1761
1851
|
const choice = response.choices?.[0];
|
|
1762
1852
|
const text = choice?.message?.content ?? "";
|
|
1763
1853
|
if (text.length === 0) {
|
|
1764
|
-
throw new
|
|
1854
|
+
throw new AIError(
|
|
1765
1855
|
"SERVER_ERROR" /* SERVER_ERROR */,
|
|
1766
1856
|
"No content generated from Groq API",
|
|
1767
1857
|
void 0,
|
|
@@ -1788,7 +1878,7 @@ var GroqProvider = class {
|
|
|
1788
1878
|
try {
|
|
1789
1879
|
const outcome = await consumeGroqStream(streamBody, callbacks);
|
|
1790
1880
|
if (outcome.text.length === 0) {
|
|
1791
|
-
throw new
|
|
1881
|
+
throw new AIError(
|
|
1792
1882
|
"SERVER_ERROR" /* SERVER_ERROR */,
|
|
1793
1883
|
"No content generated from Groq API",
|
|
1794
1884
|
void 0,
|
|
@@ -1972,7 +2062,9 @@ var GeminiProvider = class extends BaseProvider {
|
|
|
1972
2062
|
headers: this.authHeaders()
|
|
1973
2063
|
}
|
|
1974
2064
|
);
|
|
1975
|
-
|
|
2065
|
+
if (response.ok) return "healthy";
|
|
2066
|
+
if (response.status === 401 || response.status === 403) return "unavailable";
|
|
2067
|
+
return "degraded";
|
|
1976
2068
|
} catch {
|
|
1977
2069
|
return "unavailable";
|
|
1978
2070
|
}
|
|
@@ -1983,6 +2075,8 @@ var GeminiProvider = class extends BaseProvider {
|
|
|
1983
2075
|
async generate(request) {
|
|
1984
2076
|
try {
|
|
1985
2077
|
const model = request.model ?? this.resolveModel(request.tier ?? "balanced");
|
|
2078
|
+
const systemMessages = request.messages.filter((m) => m.role === "system");
|
|
2079
|
+
const systemPrompt = request.systemPrompt && request.systemPrompt.length > 0 ? request.systemPrompt : systemMessages.length > 0 ? systemMessages.map((m) => m.content).join("\n") : void 0;
|
|
1986
2080
|
const response = await this.withRetry(async () => {
|
|
1987
2081
|
return await this.fetchWithTimeout(
|
|
1988
2082
|
`${this.config.baseUrl}/models/${model}:generateContent`,
|
|
@@ -1990,24 +2084,30 @@ var GeminiProvider = class extends BaseProvider {
|
|
|
1990
2084
|
method: "POST",
|
|
1991
2085
|
headers: this.authHeaders(),
|
|
1992
2086
|
body: JSON.stringify({
|
|
1993
|
-
systemInstruction:
|
|
2087
|
+
systemInstruction: systemPrompt && systemPrompt.length > 0 ? { parts: [{ text: systemPrompt }] } : void 0,
|
|
1994
2088
|
contents: toGeminiContents(request.messages),
|
|
1995
2089
|
generationConfig: {
|
|
1996
|
-
temperature: request.temperature
|
|
1997
|
-
maxOutputTokens: request.maxTokens
|
|
1998
|
-
topP: request.topP
|
|
2090
|
+
temperature: request.temperature ?? 0.7,
|
|
2091
|
+
maxOutputTokens: request.maxTokens ?? 2048,
|
|
2092
|
+
topP: request.topP ?? 0.9
|
|
1999
2093
|
}
|
|
2000
2094
|
})
|
|
2001
2095
|
}
|
|
2002
2096
|
);
|
|
2003
2097
|
});
|
|
2004
2098
|
if (!response.ok) {
|
|
2005
|
-
|
|
2099
|
+
const message = await extractErrorMessageFromResponse(response);
|
|
2100
|
+
throw new AIError(
|
|
2101
|
+
classifyHttpStatus(response.status),
|
|
2102
|
+
`Gemini API error (${response.status}): ${message}`,
|
|
2103
|
+
void 0,
|
|
2104
|
+
{ status: response.status, model }
|
|
2105
|
+
);
|
|
2006
2106
|
}
|
|
2007
2107
|
const data = await response.json();
|
|
2008
|
-
const text = data.candidates[0]?.content?.parts?.map((part) => part.text ?? "").join("") ?? "";
|
|
2108
|
+
const text = data.candidates?.[0]?.content?.parts?.map((part) => part.text ?? "").join("") ?? "";
|
|
2009
2109
|
if (text.length === 0) {
|
|
2010
|
-
throw new
|
|
2110
|
+
throw new AIError("SERVER_ERROR" /* SERVER_ERROR */, "No content generated from Gemini API", void 0, { model });
|
|
2011
2111
|
}
|
|
2012
2112
|
return {
|
|
2013
2113
|
text,
|
|
@@ -2018,11 +2118,11 @@ var GeminiProvider = class extends BaseProvider {
|
|
|
2018
2118
|
} : void 0,
|
|
2019
2119
|
model,
|
|
2020
2120
|
providerId: this.id,
|
|
2021
|
-
finishReason: mapGeminiFinishReason(data.candidates[0]?.finishReason)
|
|
2121
|
+
finishReason: mapGeminiFinishReason(data.candidates?.[0]?.finishReason)
|
|
2022
2122
|
};
|
|
2023
2123
|
} catch (error) {
|
|
2024
|
-
if (error instanceof
|
|
2025
|
-
throw new
|
|
2124
|
+
if (error instanceof AIError) throw error;
|
|
2125
|
+
throw new AIError(
|
|
2026
2126
|
"UNKNOWN_ERROR" /* UNKNOWN_ERROR */,
|
|
2027
2127
|
`Gemini text generation failed: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
2028
2128
|
error instanceof Error ? error : void 0
|
|
@@ -2033,7 +2133,7 @@ var GeminiProvider = class extends BaseProvider {
|
|
|
2033
2133
|
const messages = [...request.messages];
|
|
2034
2134
|
const lastUserIndex = findLastIndex3(messages, (m) => m.role === "user");
|
|
2035
2135
|
if (lastUserIndex === -1) {
|
|
2036
|
-
throw new
|
|
2136
|
+
throw new AIError(
|
|
2037
2137
|
"UNKNOWN_ERROR" /* UNKNOWN_ERROR */,
|
|
2038
2138
|
"generateStructured requires at least one user message"
|
|
2039
2139
|
);
|
|
@@ -2070,12 +2170,18 @@ var GeminiProvider = class extends BaseProvider {
|
|
|
2070
2170
|
);
|
|
2071
2171
|
});
|
|
2072
2172
|
if (!response.ok) {
|
|
2073
|
-
|
|
2173
|
+
const message = await extractErrorMessageFromResponse(response);
|
|
2174
|
+
throw new AIError(
|
|
2175
|
+
classifyHttpStatus(response.status),
|
|
2176
|
+
`Gemini image generation error (${response.status}): ${message}`,
|
|
2177
|
+
void 0,
|
|
2178
|
+
{ status: response.status }
|
|
2179
|
+
);
|
|
2074
2180
|
}
|
|
2075
2181
|
const data = await response.json();
|
|
2076
|
-
const imageData = data.candidates[0]?.content?.parts[0]?.inlineData;
|
|
2182
|
+
const imageData = data.candidates?.[0]?.content?.parts?.[0]?.inlineData;
|
|
2077
2183
|
if (!imageData) {
|
|
2078
|
-
throw new
|
|
2184
|
+
throw new AIError("SERVER_ERROR" /* SERVER_ERROR */, "No image data in Gemini response");
|
|
2079
2185
|
}
|
|
2080
2186
|
const url = `data:${imageData.mimeType};base64,${imageData.data}`;
|
|
2081
2187
|
return {
|
|
@@ -2454,6 +2560,41 @@ var FalProvider = class extends BaseProvider {
|
|
|
2454
2560
|
}
|
|
2455
2561
|
};
|
|
2456
2562
|
|
|
2563
|
+
// src/providers/pruna/pruna-generate.ts
|
|
2564
|
+
var prunaCorePromise = null;
|
|
2565
|
+
async function loadPrunaCore() {
|
|
2566
|
+
if (!prunaCorePromise) {
|
|
2567
|
+
const specifier = "@umituz/pruna-provider/core";
|
|
2568
|
+
prunaCorePromise = import(specifier);
|
|
2569
|
+
}
|
|
2570
|
+
return prunaCorePromise;
|
|
2571
|
+
}
|
|
2572
|
+
async function* generateWithPruna(request, apiKey, signal, onProgress) {
|
|
2573
|
+
const prunaCore = await loadPrunaCore();
|
|
2574
|
+
if (prunaCore.generateWithPruna) {
|
|
2575
|
+
yield* prunaCore.generateWithPruna(request, apiKey, signal, onProgress);
|
|
2576
|
+
return;
|
|
2577
|
+
}
|
|
2578
|
+
if (!prunaCore.submitPrediction || !prunaCore.pollForResult) {
|
|
2579
|
+
throw new Error("@umituz/pruna-provider/core does not export required functions");
|
|
2580
|
+
}
|
|
2581
|
+
const modelId = request.model || (request.image ? "p-image-edit" : "p-image");
|
|
2582
|
+
const prediction = await prunaCore.submitPrediction(modelId, request, apiKey, signal, onProgress);
|
|
2583
|
+
if (prediction.output && typeof prediction.output === "object" && "url" in prediction.output) {
|
|
2584
|
+
yield {
|
|
2585
|
+
stage: "predicting",
|
|
2586
|
+
result: { url: prediction.output.url, model: modelId }
|
|
2587
|
+
};
|
|
2588
|
+
return;
|
|
2589
|
+
}
|
|
2590
|
+
const pollUrl = prediction.status_url || prediction.generation_url;
|
|
2591
|
+
if (!pollUrl) {
|
|
2592
|
+
throw new Error("Pruna prediction response missing polling URL");
|
|
2593
|
+
}
|
|
2594
|
+
const resultUrl = await prunaCore.pollForResult(pollUrl, apiKey, 60, 2e3, signal, onProgress);
|
|
2595
|
+
yield { stage: "polling", result: { url: resultUrl, model: modelId } };
|
|
2596
|
+
}
|
|
2597
|
+
|
|
2457
2598
|
// src/providers/pruna/pruna.provider.ts
|
|
2458
2599
|
var PrunaProvider = class {
|
|
2459
2600
|
constructor(config) {
|
|
@@ -2469,17 +2610,70 @@ var PrunaProvider = class {
|
|
|
2469
2610
|
async estimateCost(_request) {
|
|
2470
2611
|
return 1;
|
|
2471
2612
|
}
|
|
2472
|
-
async generateImage(
|
|
2473
|
-
|
|
2613
|
+
async generateImage(request) {
|
|
2614
|
+
let finalUrl;
|
|
2615
|
+
const model = this.config.models?.textToImage;
|
|
2616
|
+
for await (const stage of generateWithPruna(
|
|
2617
|
+
{ prompt: request.prompt, model, quality: request.quality },
|
|
2618
|
+
this.config.apiKey
|
|
2619
|
+
)) {
|
|
2620
|
+
if (stage.error) throw stage.error;
|
|
2621
|
+
if (stage.result?.url) finalUrl = stage.result.url;
|
|
2622
|
+
}
|
|
2623
|
+
if (!finalUrl) {
|
|
2624
|
+
throw new AIError("SERVER_ERROR" /* SERVER_ERROR */, "No image generated by Pruna AI");
|
|
2625
|
+
}
|
|
2626
|
+
return {
|
|
2627
|
+
id: Date.now().toString(),
|
|
2628
|
+
type: "image",
|
|
2629
|
+
url: finalUrl,
|
|
2630
|
+
metadata: { provider: this.id, model: model ?? "p-image" },
|
|
2631
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2632
|
+
};
|
|
2474
2633
|
}
|
|
2475
|
-
async generateVideo(
|
|
2476
|
-
|
|
2634
|
+
async generateVideo(request) {
|
|
2635
|
+
let finalUrl;
|
|
2636
|
+
const model = this.config.models?.textToVideo;
|
|
2637
|
+
for await (const stage of generateWithPruna(
|
|
2638
|
+
{ prompt: request.prompt, model, duration: request.duration, aspectRatio: request.aspectRatio },
|
|
2639
|
+
this.config.apiKey
|
|
2640
|
+
)) {
|
|
2641
|
+
if (stage.error) throw stage.error;
|
|
2642
|
+
if (stage.result?.url) finalUrl = stage.result.url;
|
|
2643
|
+
}
|
|
2644
|
+
if (!finalUrl) {
|
|
2645
|
+
throw new AIError("SERVER_ERROR" /* SERVER_ERROR */, "No video generated by Pruna AI");
|
|
2646
|
+
}
|
|
2647
|
+
return {
|
|
2648
|
+
id: Date.now().toString(),
|
|
2649
|
+
type: "video",
|
|
2650
|
+
url: finalUrl,
|
|
2651
|
+
metadata: { provider: this.id, model: model ?? "p-video" },
|
|
2652
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2653
|
+
};
|
|
2477
2654
|
}
|
|
2478
|
-
async imageToVideo(
|
|
2479
|
-
|
|
2655
|
+
async imageToVideo(request) {
|
|
2656
|
+
let finalUrl;
|
|
2657
|
+
for await (const stage of generateWithPruna(
|
|
2658
|
+
{ image: request.imageUrl, prompt: request.prompt, duration: request.duration, motion: request.motion },
|
|
2659
|
+
this.config.apiKey
|
|
2660
|
+
)) {
|
|
2661
|
+
if (stage.error) throw stage.error;
|
|
2662
|
+
if (stage.result?.url) finalUrl = stage.result.url;
|
|
2663
|
+
}
|
|
2664
|
+
if (!finalUrl) {
|
|
2665
|
+
throw new AIError("SERVER_ERROR" /* SERVER_ERROR */, "No video generated from image by Pruna AI");
|
|
2666
|
+
}
|
|
2667
|
+
return {
|
|
2668
|
+
id: Date.now().toString(),
|
|
2669
|
+
type: "video",
|
|
2670
|
+
url: finalUrl,
|
|
2671
|
+
metadata: { provider: this.id, model: "p-image-to-video" },
|
|
2672
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2673
|
+
};
|
|
2480
2674
|
}
|
|
2481
2675
|
async videoToVideo(_request) {
|
|
2482
|
-
throw new
|
|
2676
|
+
throw new AIError("CLIENT_ERROR" /* CLIENT_ERROR */, "Pruna AI provider does not support video-to-video conversion");
|
|
2483
2677
|
}
|
|
2484
2678
|
};
|
|
2485
2679
|
|
|
@@ -3086,11 +3280,12 @@ var buildScriptResult = (parsed, duration) => {
|
|
|
3086
3280
|
if (!script) {
|
|
3087
3281
|
throw new Error("AI response missing script field");
|
|
3088
3282
|
}
|
|
3283
|
+
const wordCount = script.trim().length === 0 ? 0 : script.trim().split(/\s+/).length;
|
|
3089
3284
|
return {
|
|
3090
3285
|
...parsed,
|
|
3091
3286
|
script,
|
|
3092
3287
|
duration,
|
|
3093
|
-
wordCount
|
|
3288
|
+
wordCount
|
|
3094
3289
|
};
|
|
3095
3290
|
};
|
|
3096
3291
|
var VideoScriptService = class {
|
|
@@ -3285,7 +3480,6 @@ var SeoService = class {
|
|
|
3285
3480
|
analyzeKeywords(content, keywords) {
|
|
3286
3481
|
const validatedContent = validateContentBody(content);
|
|
3287
3482
|
const validatedKeywords = validateKeywordList(keywords);
|
|
3288
|
-
void countWordsInContent(validatedContent);
|
|
3289
3483
|
return validatedKeywords.map((keyword) => {
|
|
3290
3484
|
const count = countKeywordOccurrences(validatedContent, keyword);
|
|
3291
3485
|
const density = calculateKeywordDensity(validatedContent, keyword);
|
|
@@ -3380,68 +3574,20 @@ var ABTestService = class {
|
|
|
3380
3574
|
});
|
|
3381
3575
|
const scoreA = predictions[0]?.predictedEngagement ?? FALLBACK_ENGAGEMENT;
|
|
3382
3576
|
const scoreB = predictions[1]?.predictedEngagement ?? FALLBACK_ENGAGEMENT;
|
|
3577
|
+
const isTie = scoreA === scoreB;
|
|
3383
3578
|
const winner = selectHigherScoreVariantId(scoreA, scoreB, VARIANT_A_ID, VARIANT_B_ID);
|
|
3384
3579
|
const improvement = calculateImprovementPercentage(scoreA, scoreB);
|
|
3385
3580
|
return {
|
|
3386
3581
|
winner,
|
|
3387
3582
|
confidence: AB_TEST_DEFAULT_CONFIDENCE,
|
|
3388
|
-
improvement: `+${improvement}% engagement`,
|
|
3389
|
-
reasoning: winner === VARIANT_A_ID ? "Variant A has more engaging content" : "Variant B has more engaging content",
|
|
3583
|
+
improvement: isTie ? "0%" : `+${improvement}% engagement`,
|
|
3584
|
+
reasoning: isTie ? "Both variants have equal predicted engagement" : winner === VARIANT_A_ID ? "Variant A has more engaging content" : "Variant B has more engaging content",
|
|
3390
3585
|
recommendations: [...DEFAULT_RECOMMENDATIONS]
|
|
3391
3586
|
};
|
|
3392
3587
|
}
|
|
3393
3588
|
};
|
|
3394
3589
|
|
|
3395
|
-
// src/content/infrastructure/utils/jsonParser.ts
|
|
3396
|
-
function repairCommonJsonDefects(text) {
|
|
3397
|
-
return text.replace(/(:\s*-?\d+(?:\.\d+)?)\s*"/g, "$1").replace(/(:\s*"[^"]*")"/g, "$1");
|
|
3398
|
-
}
|
|
3399
|
-
function safeJSONParse(text, fallback, onError) {
|
|
3400
|
-
try {
|
|
3401
|
-
return JSON.parse(text);
|
|
3402
|
-
} catch (error) {
|
|
3403
|
-
try {
|
|
3404
|
-
return JSON.parse(repairCommonJsonDefects(text));
|
|
3405
|
-
} catch {
|
|
3406
|
-
}
|
|
3407
|
-
if (error instanceof SyntaxError) {
|
|
3408
|
-
const positionMatch = error.message.match(/position (\d+)/);
|
|
3409
|
-
const position = positionMatch ? parseInt(positionMatch[1], 10) : -1;
|
|
3410
|
-
if (position > 0) {
|
|
3411
|
-
text.substring(Math.max(0, position - 50), position + 50);
|
|
3412
|
-
}
|
|
3413
|
-
}
|
|
3414
|
-
return fallback;
|
|
3415
|
-
}
|
|
3416
|
-
}
|
|
3417
|
-
function extractJSON(text) {
|
|
3418
|
-
const codeBlockMatch = text.match(/```(?:json)?\s*([\s\S]*?)```/);
|
|
3419
|
-
if (codeBlockMatch && codeBlockMatch[1]) {
|
|
3420
|
-
const candidate = codeBlockMatch[1].trim();
|
|
3421
|
-
if (candidate.startsWith("{") || candidate.startsWith("[")) {
|
|
3422
|
-
return candidate;
|
|
3423
|
-
}
|
|
3424
|
-
}
|
|
3425
|
-
const objectMatch = text.match(/\{[\s\S]*\}/);
|
|
3426
|
-
if (objectMatch) {
|
|
3427
|
-
return objectMatch[0];
|
|
3428
|
-
}
|
|
3429
|
-
const arrayMatch = text.match(/\[[\s\S]*\]/);
|
|
3430
|
-
if (arrayMatch) {
|
|
3431
|
-
return arrayMatch[0];
|
|
3432
|
-
}
|
|
3433
|
-
return null;
|
|
3434
|
-
}
|
|
3435
|
-
function parseAIResponse(response, fallback, onError) {
|
|
3436
|
-
const json = extractJSON(response);
|
|
3437
|
-
if (!json) {
|
|
3438
|
-
return fallback;
|
|
3439
|
-
}
|
|
3440
|
-
return safeJSONParse(json, fallback);
|
|
3441
|
-
}
|
|
3442
|
-
|
|
3443
3590
|
// src/content/application/services/ImagePromptService.ts
|
|
3444
|
-
var JSON_ARRAY_PATTERN = /\[[\s\S]*\]/;
|
|
3445
3591
|
var ImagePromptService = class {
|
|
3446
3592
|
constructor(executor) {
|
|
3447
3593
|
this.executor = executor;
|
|
@@ -3455,10 +3601,11 @@ var ImagePromptService = class {
|
|
|
3455
3601
|
async generateImagePromptsForBlog(blogContent) {
|
|
3456
3602
|
const truncated = truncateForBlogPromptPreview(validateContentBody(blogContent));
|
|
3457
3603
|
const prompt = PromptBuilder.blogImagePrompts(truncated);
|
|
3458
|
-
const
|
|
3459
|
-
|
|
3460
|
-
|
|
3461
|
-
|
|
3604
|
+
const prompts = await this.executor.runJson(prompt, [], {
|
|
3605
|
+
maxTokens: 500,
|
|
3606
|
+
temperature: 0.8
|
|
3607
|
+
});
|
|
3608
|
+
return Array.isArray(prompts) ? prompts : [];
|
|
3462
3609
|
}
|
|
3463
3610
|
};
|
|
3464
3611
|
|
|
@@ -3502,11 +3649,16 @@ var ContentCalendarService = class {
|
|
|
3502
3649
|
const validatedNiche = validateTopicText(niche);
|
|
3503
3650
|
const validatedDays = validateCalendarDays(days);
|
|
3504
3651
|
const prompt = PromptBuilder.contentCalendar(validatedNiche, validatedDays);
|
|
3505
|
-
const data = await this.executor.runJson(
|
|
3506
|
-
|
|
3507
|
-
|
|
3508
|
-
|
|
3509
|
-
|
|
3652
|
+
const data = await this.executor.runJson(
|
|
3653
|
+
prompt,
|
|
3654
|
+
[],
|
|
3655
|
+
{
|
|
3656
|
+
maxTokens: estimateTokenBudgetForCalendarDays(validatedDays),
|
|
3657
|
+
temperature: 0.8
|
|
3658
|
+
}
|
|
3659
|
+
);
|
|
3660
|
+
const items = Array.isArray(data) ? data : Array.isArray(data?.calendar) ? data.calendar : Array.isArray(data?.entries) ? data.entries : [];
|
|
3661
|
+
return items.map(toCalendarEntry);
|
|
3510
3662
|
}
|
|
3511
3663
|
};
|
|
3512
3664
|
|
|
@@ -3554,6 +3706,54 @@ var MediaGenerationService = class {
|
|
|
3554
3706
|
}
|
|
3555
3707
|
};
|
|
3556
3708
|
|
|
3709
|
+
// src/content/infrastructure/utils/jsonParser.ts
|
|
3710
|
+
function repairCommonJsonDefects2(text) {
|
|
3711
|
+
return text.replace(/(:\s*-?\d+(?:\.\d+)?)\s*"/g, "$1").replace(/(:\s*"[^"]*")"/g, "$1");
|
|
3712
|
+
}
|
|
3713
|
+
function safeJSONParse(text, fallback, onError) {
|
|
3714
|
+
try {
|
|
3715
|
+
return JSON.parse(text);
|
|
3716
|
+
} catch (error) {
|
|
3717
|
+
try {
|
|
3718
|
+
return JSON.parse(repairCommonJsonDefects2(text));
|
|
3719
|
+
} catch {
|
|
3720
|
+
}
|
|
3721
|
+
if (error instanceof SyntaxError) {
|
|
3722
|
+
const positionMatch = error.message.match(/position (\d+)/);
|
|
3723
|
+
const position = positionMatch ? parseInt(positionMatch[1], 10) : -1;
|
|
3724
|
+
if (position > 0) {
|
|
3725
|
+
text.substring(Math.max(0, position - 50), position + 50);
|
|
3726
|
+
}
|
|
3727
|
+
}
|
|
3728
|
+
return fallback;
|
|
3729
|
+
}
|
|
3730
|
+
}
|
|
3731
|
+
function extractJSON(text) {
|
|
3732
|
+
const codeBlockMatch = text.match(/```(?:json)?\s*([\s\S]*?)```/);
|
|
3733
|
+
if (codeBlockMatch && codeBlockMatch[1]) {
|
|
3734
|
+
const candidate = codeBlockMatch[1].trim();
|
|
3735
|
+
if (candidate.startsWith("{") || candidate.startsWith("[")) {
|
|
3736
|
+
return candidate;
|
|
3737
|
+
}
|
|
3738
|
+
}
|
|
3739
|
+
const objectMatch = text.match(/\{[\s\S]*\}/);
|
|
3740
|
+
if (objectMatch) {
|
|
3741
|
+
return objectMatch[0];
|
|
3742
|
+
}
|
|
3743
|
+
const arrayMatch = text.match(/\[[\s\S]*\]/);
|
|
3744
|
+
if (arrayMatch) {
|
|
3745
|
+
return arrayMatch[0];
|
|
3746
|
+
}
|
|
3747
|
+
return null;
|
|
3748
|
+
}
|
|
3749
|
+
function parseAIResponse(response, fallback, onError) {
|
|
3750
|
+
const json = extractJSON(response);
|
|
3751
|
+
if (!json) {
|
|
3752
|
+
return fallback;
|
|
3753
|
+
}
|
|
3754
|
+
return safeJSONParse(json, fallback);
|
|
3755
|
+
}
|
|
3756
|
+
|
|
3557
3757
|
// src/content/application/services/GenerationExecutor.ts
|
|
3558
3758
|
var GenerationExecutor = class {
|
|
3559
3759
|
constructor(generator) {
|
|
@@ -3584,6 +3784,13 @@ var isTextGenerator = (value) => typeof value === "object" && value !== null &&
|
|
|
3584
3784
|
function toAIProviderConfig(config) {
|
|
3585
3785
|
const legacy = config;
|
|
3586
3786
|
const modern = config;
|
|
3787
|
+
const anthropic = modern.anthropic ?? (legacy.anthropic?.enabled ? {
|
|
3788
|
+
apiKey: legacy.anthropic.apiKey,
|
|
3789
|
+
baseUrl: legacy.anthropic.baseUrl,
|
|
3790
|
+
timeoutMs: legacy.anthropic.timeout,
|
|
3791
|
+
models: legacy.anthropic.models,
|
|
3792
|
+
effort: legacy.anthropic.effort
|
|
3793
|
+
} : void 0);
|
|
3587
3794
|
const groq = modern.groq ?? (legacy.groq?.enabled ? {
|
|
3588
3795
|
apiKey: legacy.groq.apiKey,
|
|
3589
3796
|
baseUrl: legacy.groq.baseUrl,
|
|
@@ -3598,7 +3805,7 @@ function toAIProviderConfig(config) {
|
|
|
3598
3805
|
priority: modern.priority,
|
|
3599
3806
|
fallbackEnabled: modern.fallbackEnabled,
|
|
3600
3807
|
defaultTier: modern.defaultTier,
|
|
3601
|
-
anthropic:
|
|
3808
|
+
...anthropic ? { anthropic } : {},
|
|
3602
3809
|
...groq ? { groq } : {},
|
|
3603
3810
|
...gemini ? { gemini } : {},
|
|
3604
3811
|
...fal ? { fal } : {},
|