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