vidspotai-shared 1.0.126 → 1.0.128
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/lib/libs/storage/cdnUrl.d.ts +21 -0
- package/lib/libs/storage/cdnUrl.d.ts.map +1 -1
- package/lib/libs/storage/cdnUrl.js +41 -0
- package/lib/services/agent/sceneLayoutVision.d.ts.map +1 -1
- package/lib/services/agent/sceneLayoutVision.js +2 -1
- package/lib/services/agent/stitchedVideoCritic.gemini.d.ts.map +1 -1
- package/lib/services/agent/stitchedVideoCritic.gemini.js +3 -2
- package/lib/services/aiGen/aiGenFactory.service.d.ts.map +1 -1
- package/lib/services/aiGen/aiGenFactory.service.js +36 -7
- package/lib/services/aiGen/providers/bytedance/bytedance.service.d.ts.map +1 -1
- package/lib/services/aiGen/providers/bytedance/bytedance.service.js +38 -7
- package/lib/services/aiGen/providers/runway/runway.service.d.ts.map +1 -1
- package/lib/services/aiGen/providers/runway/runway.service.js +8 -0
- package/lib/services/avatarGen/avatarProviderFactory.d.ts.map +1 -1
- package/lib/services/avatarGen/avatarProviderFactory.js +22 -1
- package/lib/services/klingElements/klingElementLibrary.service.d.ts.map +1 -1
- package/lib/services/klingElements/klingElementLibrary.service.js +3 -1
- package/lib/services/outreachWebhooks.service.d.ts.map +1 -1
- package/lib/services/outreachWebhooks.service.js +4 -1
- package/lib/services/stock/realPersonSafety.d.ts.map +1 -1
- package/lib/services/stock/realPersonSafety.js +2 -1
- package/lib/services/translation/translation.service.d.ts.map +1 -1
- package/lib/services/translation/translation.service.js +60 -18
- package/package.json +1 -1
|
@@ -24,6 +24,27 @@ export type MediaVisibility = "public" | "private";
|
|
|
24
24
|
export declare function toCdnUrl(url: string, opts?: {
|
|
25
25
|
visibility?: MediaVisibility;
|
|
26
26
|
}): string;
|
|
27
|
+
/**
|
|
28
|
+
* Deep copy of `value` with every one of OUR storage URLs (at any depth, in
|
|
29
|
+
* plain objects and arrays) rewritten to its CDN form. For payloads that carry
|
|
30
|
+
* many media URLs to something that will dereference them — provider request
|
|
31
|
+
* params (start frames, reference images, input audio), an editor project
|
|
32
|
+
* handed to the Remotion renderer, etc. Since the R2-primary flip those URLs
|
|
33
|
+
* only resolve through the CDN; see `fetchOwnMedia` below.
|
|
34
|
+
*
|
|
35
|
+
* External URLs, CDN URLs and non-URL strings pass through unchanged, and
|
|
36
|
+
* non-plain objects (Buffer, Date, class instances) are kept by reference.
|
|
37
|
+
*/
|
|
38
|
+
export declare function toCdnUrlsDeep<T>(value: T, opts?: {
|
|
39
|
+
visibility?: MediaVisibility;
|
|
40
|
+
}): T;
|
|
41
|
+
/**
|
|
42
|
+
* `toCdnUrl` for every one of our storage URLs embedded in free text (a tool
|
|
43
|
+
* result line like "• Video: <url>", an email body). Other text is untouched.
|
|
44
|
+
*/
|
|
45
|
+
export declare function toCdnUrlsInText(text: string, opts?: {
|
|
46
|
+
visibility?: MediaVisibility;
|
|
47
|
+
}): string;
|
|
27
48
|
/**
|
|
28
49
|
* The sanctioned way to RE-DOWNLOAD a URL this backend itself minted via
|
|
29
50
|
* `mediaStorage.put()`/`putFromFile()`, from anywhere in the backend (a
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cdnUrl.d.ts","sourceRoot":"","sources":["../../../src/libs/storage/cdnUrl.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAeH,MAAM,MAAM,eAAe,GAAG,QAAQ,GAAG,SAAS,CAAC;AAEnD;;;;;;;;GAQG;AACH,wBAAgB,QAAQ,CACtB,GAAG,EAAE,MAAM,EACX,IAAI,CAAC,EAAE;IAAE,UAAU,CAAC,EAAE,eAAe,CAAA;CAAE,GACtC,MAAM,CAeR;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,QAAQ,CAAC,CAEhF;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAGvD"}
|
|
1
|
+
{"version":3,"file":"cdnUrl.d.ts","sourceRoot":"","sources":["../../../src/libs/storage/cdnUrl.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAeH,MAAM,MAAM,eAAe,GAAG,QAAQ,GAAG,SAAS,CAAC;AAEnD;;;;;;;;GAQG;AACH,wBAAgB,QAAQ,CACtB,GAAG,EAAE,MAAM,EACX,IAAI,CAAC,EAAE;IAAE,UAAU,CAAC,EAAE,eAAe,CAAA;CAAE,GACtC,MAAM,CAeR;AAQD;;;;;;;;;;GAUG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAC7B,KAAK,EAAE,CAAC,EACR,IAAI,GAAE;IAAE,UAAU,CAAC,EAAE,eAAe,CAAA;CAA8B,GACjE,CAAC,CASH;AAQD;;;GAGG;AACH,wBAAgB,eAAe,CAC7B,IAAI,EAAE,MAAM,EACZ,IAAI,GAAE;IAAE,UAAU,CAAC,EAAE,eAAe,CAAA;CAA8B,GACjE,MAAM,CAER;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,QAAQ,CAAC,CAEhF;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAGvD"}
|
|
@@ -14,6 +14,8 @@
|
|
|
14
14
|
*/
|
|
15
15
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
16
16
|
exports.toCdnUrl = toCdnUrl;
|
|
17
|
+
exports.toCdnUrlsDeep = toCdnUrlsDeep;
|
|
18
|
+
exports.toCdnUrlsInText = toCdnUrlsInText;
|
|
17
19
|
exports.fetchOwnMedia = fetchOwnMedia;
|
|
18
20
|
exports.publicCdnUrl = publicCdnUrl;
|
|
19
21
|
const FIREBASE_STORAGE_HOST = "https://firebasestorage.googleapis.com";
|
|
@@ -48,6 +50,45 @@ function toCdnUrl(url, opts) {
|
|
|
48
50
|
}
|
|
49
51
|
return url;
|
|
50
52
|
}
|
|
53
|
+
function isPlainObject(v) {
|
|
54
|
+
if (v === null || typeof v !== "object")
|
|
55
|
+
return false;
|
|
56
|
+
const proto = Object.getPrototypeOf(v);
|
|
57
|
+
return proto === Object.prototype || proto === null;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Deep copy of `value` with every one of OUR storage URLs (at any depth, in
|
|
61
|
+
* plain objects and arrays) rewritten to its CDN form. For payloads that carry
|
|
62
|
+
* many media URLs to something that will dereference them — provider request
|
|
63
|
+
* params (start frames, reference images, input audio), an editor project
|
|
64
|
+
* handed to the Remotion renderer, etc. Since the R2-primary flip those URLs
|
|
65
|
+
* only resolve through the CDN; see `fetchOwnMedia` below.
|
|
66
|
+
*
|
|
67
|
+
* External URLs, CDN URLs and non-URL strings pass through unchanged, and
|
|
68
|
+
* non-plain objects (Buffer, Date, class instances) are kept by reference.
|
|
69
|
+
*/
|
|
70
|
+
function toCdnUrlsDeep(value, opts = { visibility: "private" }) {
|
|
71
|
+
if (typeof value === "string")
|
|
72
|
+
return toCdnUrl(value, opts);
|
|
73
|
+
if (Array.isArray(value))
|
|
74
|
+
return value.map((v) => toCdnUrlsDeep(v, opts));
|
|
75
|
+
if (isPlainObject(value)) {
|
|
76
|
+
const out = {};
|
|
77
|
+
for (const [k, v] of Object.entries(value))
|
|
78
|
+
out[k] = toCdnUrlsDeep(v, opts);
|
|
79
|
+
return out;
|
|
80
|
+
}
|
|
81
|
+
return value;
|
|
82
|
+
}
|
|
83
|
+
const escapeRe = (s) => s.replace(/[.*+?^${}()|[\]\\/]/g, "\\$&");
|
|
84
|
+
const OWN_URL_IN_TEXT = new RegExp(`(?:${escapeRe(FIREBASE_STORAGE_HOST)}|${escapeRe(GCS_SIGNED_PREFIX)})[^\\s"'<>)\\]]+`, "g");
|
|
85
|
+
/**
|
|
86
|
+
* `toCdnUrl` for every one of our storage URLs embedded in free text (a tool
|
|
87
|
+
* result line like "• Video: <url>", an email body). Other text is untouched.
|
|
88
|
+
*/
|
|
89
|
+
function toCdnUrlsInText(text, opts = { visibility: "private" }) {
|
|
90
|
+
return text.replace(OWN_URL_IN_TEXT, (url) => toCdnUrl(url, opts));
|
|
91
|
+
}
|
|
51
92
|
/**
|
|
52
93
|
* The sanctioned way to RE-DOWNLOAD a URL this backend itself minted via
|
|
53
94
|
* `mediaStorage.put()`/`putFromFile()`, from anywhere in the backend (a
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sceneLayoutVision.d.ts","sourceRoot":"","sources":["../../../src/services/agent/sceneLayoutVision.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;
|
|
1
|
+
{"version":3,"file":"sceneLayoutVision.d.ts","sourceRoot":"","sources":["../../../src/services/agent/sceneLayoutVision.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAMH,MAAM,MAAM,gBAAgB,GAAG,QAAQ,GAAG,OAAO,GAAG,MAAM,GAAG,OAAO,CAAC;AAErE;;;;;;;;;GASG;AACH,MAAM,MAAM,aAAa,GAAG,QAAQ,GAAG,MAAM,GAAG,KAAK,GAAG,MAAM,CAAC;AAE/D,MAAM,WAAW,uBAAuB;IACtC,WAAW,EAAE,gBAAgB,CAAC;IAC9B,UAAU,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACrC,uEAAuE;IACvE,OAAO,EAAE;QACP,GAAG,EAAE,aAAa,CAAC;QACnB,MAAM,EAAE,aAAa,CAAC;QACtB,MAAM,EAAE,aAAa,CAAC;KACvB,CAAC;IACF,8CAA8C;IAC9C,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,uBAAuB;IACtC,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAgCD;;;GAGG;AACH,wBAAsB,mBAAmB,CACvC,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,uBAAuB,GAC9B,OAAO,CAAC,uBAAuB,GAAG,IAAI,CAAC,CAuDzC;AAED;;;;;;;;GAQG;AACH,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,uBAAuB,EAC/B,iBAAiB,EAAE,MAAM,EACzB,iBAAiB,EAAE,MAAM,GACxB,YAAY,GAAG,WAAW,GAAG,WAAW,GAAG,WAAW,GAAG,cAAc,GAAG,kBAAkB,CAqB9F;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,MAAM,GAAG,SAAS,GAC3B,KAAK,GAAG,QAAQ,GAAG,QAAQ,CAkB7B;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAC7B,IAAI,EAAE,aAAa,GAClB;IAAE,OAAO,EAAE,QAAQ,GAAG,MAAM,GAAG,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAWvD"}
|
|
@@ -27,6 +27,7 @@ exports.pickCompositionFromVision = pickCompositionFromVision;
|
|
|
27
27
|
exports.regionForPosition = regionForPosition;
|
|
28
28
|
exports.autoContrastFor = autoContrastFor;
|
|
29
29
|
const genai_1 = require("@google/genai");
|
|
30
|
+
const cdnUrl_1 = require("../../libs/storage/cdnUrl");
|
|
30
31
|
const logger_1 = require("../../utils/logger");
|
|
31
32
|
const DEFAULT_MODEL = "gemini-2.5-flash";
|
|
32
33
|
const CLASSIFY_PROMPT = [
|
|
@@ -68,7 +69,7 @@ async function classifySceneLayout(imageUrl, config) {
|
|
|
68
69
|
let bytes;
|
|
69
70
|
let mimeType;
|
|
70
71
|
try {
|
|
71
|
-
const r = await
|
|
72
|
+
const r = await (0, cdnUrl_1.fetchOwnMedia)(imageUrl, { signal: controller.signal });
|
|
72
73
|
if (!r.ok)
|
|
73
74
|
throw new Error(`fetch ${r.status} ${r.statusText}`);
|
|
74
75
|
bytes = Buffer.from(await r.arrayBuffer());
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stitchedVideoCritic.gemini.d.ts","sourceRoot":"","sources":["../../../src/services/agent/stitchedVideoCritic.gemini.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"stitchedVideoCritic.gemini.d.ts","sourceRoot":"","sources":["../../../src/services/agent/stitchedVideoCritic.gemini.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,OAAO,EACL,mBAAmB,EAEpB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,cAAc,EAAwB,MAAM,kBAAkB,CAAC;AAwBxE,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,sDAAsD;IACtD,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAOD,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,kBAAkB,IAKnD,MAAM;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,EAAE,CAAC,CAAC,OAAO,CAAC,mBAAmB,CAAC,CAAC;CAChD,KAAG,OAAO,CAAC,mBAAmB,CAAC,CA+FjC;AAED;;;;;GAKG;AACH,wBAAgB,gCAAgC,CAAC,MAAM,EAAE,kBAAkB,IAK3D,MAAM;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,EAAE,CAAC,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC;CAC3C,KAAG,OAAO,CAAC,cAAc,CAAC,CAwD5B"}
|
|
@@ -4,6 +4,7 @@ exports.buildGeminiCriticInvoker = buildGeminiCriticInvoker;
|
|
|
4
4
|
exports.buildGeminiPerSceneCriticInvoker = buildGeminiPerSceneCriticInvoker;
|
|
5
5
|
const genai_1 = require("@google/genai");
|
|
6
6
|
const zod_1 = require("zod");
|
|
7
|
+
const cdnUrl_1 = require("../../libs/storage/cdnUrl");
|
|
7
8
|
const logger_1 = require("../../utils/logger");
|
|
8
9
|
const stitchedVideoCritic_1 = require("./stitchedVideoCritic");
|
|
9
10
|
const perSceneCritic_1 = require("./perSceneCritic");
|
|
@@ -43,7 +44,7 @@ function buildGeminiCriticInvoker(config) {
|
|
|
43
44
|
const dlTimeout = setTimeout(() => controller.abort(), remain());
|
|
44
45
|
let buf;
|
|
45
46
|
try {
|
|
46
|
-
const r = await
|
|
47
|
+
const r = await (0, cdnUrl_1.fetchOwnMedia)(args.videoUrl, { signal: controller.signal });
|
|
47
48
|
if (!r.ok) {
|
|
48
49
|
throw new Error(`stitchedCritic.gemini: video fetch ${r.status} ${r.statusText}`);
|
|
49
50
|
}
|
|
@@ -136,7 +137,7 @@ function buildGeminiPerSceneCriticInvoker(config) {
|
|
|
136
137
|
const dlTimeout = setTimeout(() => controller.abort(), Math.max(1000, deadline - Date.now()));
|
|
137
138
|
let buf;
|
|
138
139
|
try {
|
|
139
|
-
const r = await
|
|
140
|
+
const r = await (0, cdnUrl_1.fetchOwnMedia)(args.videoUrl, { signal: controller.signal });
|
|
140
141
|
if (!r.ok)
|
|
141
142
|
throw new Error(`perSceneCritic.gemini: fetch ${r.status} ${r.statusText}`);
|
|
142
143
|
buf = Buffer.from(await r.arrayBuffer());
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"aiGenFactory.service.d.ts","sourceRoot":"","sources":["../../../src/services/aiGen/aiGenFactory.service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,wBAAwB,EAAE,MAAM,uCAAuC,CAAC;AACjF,OAAO,EACL,iBAAiB,EAEjB,eAAe,EACf,aAAa,EACb,cAAc,EACd,qBAAqB,EAErB,qBAAqB,EAErB,eAAe,EACf,WAAW,EACZ,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"aiGenFactory.service.d.ts","sourceRoot":"","sources":["../../../src/services/aiGen/aiGenFactory.service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,wBAAwB,EAAE,MAAM,uCAAuC,CAAC;AACjF,OAAO,EACL,iBAAiB,EAEjB,eAAe,EACf,aAAa,EACb,cAAc,EACd,qBAAqB,EAErB,qBAAqB,EAErB,eAAe,EACf,WAAW,EACZ,MAAM,wBAAwB,CAAC;AAiBhC,KAAK,uBAAuB,GAAG,UAAU,wBAAwB,CAAC;AASlE,eAAO,MAAM,gBAAgB,EAAE,OAAO,CACpC,MAAM,CAAC,eAAe,EAAE,uBAAuB,CAAC,CAiGjD,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,gBAAgB,EAAE,MAAM,CACnC,iBAAiB,EACjB,uBAAuB,CAsDxB,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,gBAAgB,EAAE,MAAM,CACnC,eAAe,EACf,uBAAuB,CAMxB,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,cAAc,EAAE,MAAM,CACjC,aAAa,EACb,uBAAuB,CAGxB,CAAC;AAEF,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,cAAc,EAAE,uBAAuB,CAsBzE,CAAC;AAEJ;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,uBAAuB,EAAE,MAAM,CAC1C,qBAAqB,EACrB,uBAAuB,CAGxB,CAAC;AAEF,eAAO,MAAM,uBAAuB,EAAE,MAAM,CAC1C,qBAAqB,EACrB,uBAAuB,CAGxB,CAAC;AA6BF,wBAAgB,8BAA8B,CAC5C,QAAQ,EAAE,qBAAqB,GAC9B,wBAAwB,CAM1B;AAED,wBAAgB,8BAA8B,CAC5C,QAAQ,EAAE,qBAAqB,GAC9B,wBAAwB,CAM1B;AAED,wBAAgB,uBAAuB,CACrC,QAAQ,EAAE,WAAW,GACpB,wBAAwB,CAmB1B"}
|
|
@@ -18,6 +18,7 @@ const elevenlabs_service_1 = require("./providers/elevenlabs/elevenlabs.service"
|
|
|
18
18
|
const anthropic_service_1 = require("./providers/anthropic/anthropic.service");
|
|
19
19
|
const falUpscale_service_1 = require("./providers/fal/falUpscale.service");
|
|
20
20
|
const xai_service_1 = require("./providers/xai/xai.service");
|
|
21
|
+
const cdnUrl_1 = require("../../libs/storage/cdnUrl");
|
|
21
22
|
// Partial, intentionally: hosted avatar engines (heygen-avatar, tavus-avatar)
|
|
22
23
|
// are NOT generic video models — they're handled by the dedicated avatar seam
|
|
23
24
|
// (getAvatarVideoProvider / IAvatarVideoProvider). getAiGenProviderService gates
|
|
@@ -82,6 +83,8 @@ exports.videoServicesMap = {
|
|
|
82
83
|
"kling-motion-control": kling_service_1.KlingService,
|
|
83
84
|
"kling-lip-sync": kling_service_1.KlingService,
|
|
84
85
|
// --- MiniMax ---
|
|
86
|
+
"minimax-h3": minimax_service_1.MinimaxService,
|
|
87
|
+
"minimax-h3-max": minimax_service_1.MinimaxService,
|
|
85
88
|
"minimax-hailuo-2.3-fast": minimax_service_1.MinimaxService,
|
|
86
89
|
"minimax-hailuo-2.3": minimax_service_1.MinimaxService,
|
|
87
90
|
"minimax-hailuo-02": minimax_service_1.MinimaxService,
|
|
@@ -229,36 +232,62 @@ exports.upscaleImageServicesMap = {
|
|
|
229
232
|
exports.upscaleVideoServicesMap = {
|
|
230
233
|
[aiModels_1.EUpscaleVideoModels.TOPAZ_VIDEO_PROTEUS]: falUpscale_service_1.FalUpscaleService, // was: TopazService
|
|
231
234
|
};
|
|
235
|
+
/**
|
|
236
|
+
* Rewrites our own storage URLs in every request's params to their CDN form
|
|
237
|
+
* before the provider sees them. Since the R2-primary flip (2026-09-22) a URL
|
|
238
|
+
* minted by `mediaStorage.put()` is GCS-shaped but its bytes live only in R2,
|
|
239
|
+
* so a provider dereferencing it (a generated image used as a start frame,
|
|
240
|
+
* TTS audio driving an avatar, an image being upscaled) got a 403. Done once
|
|
241
|
+
* here so every provider, current and future, is covered; providers that
|
|
242
|
+
* already call `toCdnUrl` themselves are unaffected (CDN URLs pass through).
|
|
243
|
+
*/
|
|
244
|
+
function withCdnInputs(service) {
|
|
245
|
+
const generateVideo = service.generateVideo.bind(service);
|
|
246
|
+
service.generateVideo = (params) => generateVideo((0, cdnUrl_1.toCdnUrlsDeep)(params));
|
|
247
|
+
const generateImage = service.generateImage.bind(service);
|
|
248
|
+
service.generateImage = (params) => generateImage((0, cdnUrl_1.toCdnUrlsDeep)(params));
|
|
249
|
+
const generateText = service.generateText.bind(service);
|
|
250
|
+
service.generateText = (params) => generateText((0, cdnUrl_1.toCdnUrlsDeep)(params));
|
|
251
|
+
const generateMusic = service.generateMusic.bind(service);
|
|
252
|
+
service.generateMusic = (params) => generateMusic((0, cdnUrl_1.toCdnUrlsDeep)(params));
|
|
253
|
+
const generateSoundEffects = service.generateSoundEffects.bind(service);
|
|
254
|
+
service.generateSoundEffects = (params) => generateSoundEffects((0, cdnUrl_1.toCdnUrlsDeep)(params));
|
|
255
|
+
const upscaleImage = service.upscaleImage.bind(service);
|
|
256
|
+
service.upscaleImage = (params) => upscaleImage((0, cdnUrl_1.toCdnUrlsDeep)(params));
|
|
257
|
+
const upscaleVideo = service.upscaleVideo.bind(service);
|
|
258
|
+
service.upscaleVideo = (params) => upscaleVideo((0, cdnUrl_1.toCdnUrlsDeep)(params));
|
|
259
|
+
return service;
|
|
260
|
+
}
|
|
232
261
|
function getUpscaleImageProviderService(modelKey) {
|
|
233
262
|
const Service = exports.upscaleImageServicesMap[modelKey];
|
|
234
263
|
if (!Service) {
|
|
235
264
|
throw new Error(`Unsupported upscale image model: ${modelKey}`);
|
|
236
265
|
}
|
|
237
|
-
return new Service();
|
|
266
|
+
return withCdnInputs(new Service());
|
|
238
267
|
}
|
|
239
268
|
function getUpscaleVideoProviderService(modelKey) {
|
|
240
269
|
const Service = exports.upscaleVideoServicesMap[modelKey];
|
|
241
270
|
if (!Service) {
|
|
242
271
|
throw new Error(`Unsupported upscale video model: ${modelKey}`);
|
|
243
272
|
}
|
|
244
|
-
return new Service();
|
|
273
|
+
return withCdnInputs(new Service());
|
|
245
274
|
}
|
|
246
275
|
function getAiGenProviderService(modelKey) {
|
|
247
276
|
const VideoService = exports.videoServicesMap[modelKey];
|
|
248
277
|
if (VideoService) {
|
|
249
|
-
return new VideoService();
|
|
278
|
+
return withCdnInputs(new VideoService());
|
|
250
279
|
}
|
|
251
280
|
if (modelKey in exports.imageServicesMap) {
|
|
252
|
-
return new exports.imageServicesMap[modelKey]();
|
|
281
|
+
return withCdnInputs(new exports.imageServicesMap[modelKey]());
|
|
253
282
|
}
|
|
254
283
|
if (modelKey in exports.musicServicesMap) {
|
|
255
|
-
return new exports.musicServicesMap[modelKey]();
|
|
284
|
+
return withCdnInputs(new exports.musicServicesMap[modelKey]());
|
|
256
285
|
}
|
|
257
286
|
if (modelKey in exports.sfxServicesMap) {
|
|
258
|
-
return new exports.sfxServicesMap[modelKey]();
|
|
287
|
+
return withCdnInputs(new exports.sfxServicesMap[modelKey]());
|
|
259
288
|
}
|
|
260
289
|
if (modelKey in exports.textServicesMap) {
|
|
261
|
-
return new exports.textServicesMap[modelKey]();
|
|
290
|
+
return withCdnInputs(new exports.textServicesMap[modelKey]());
|
|
262
291
|
}
|
|
263
292
|
throw new Error(`Unsupported AI generation model: ${modelKey}`);
|
|
264
293
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bytedance.service.d.ts","sourceRoot":"","sources":["../../../../../src/services/aiGen/providers/bytedance/bytedance.service.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,wBAAwB,EAAE,MAAM,8BAA8B,CAAC;AACxE,OAAO,EACL,iBAAiB,EACjB,qBAAqB,EACrB,qBAAqB,EACrB,qBAAqB,EACrB,qBAAqB,EACrB,iBAAiB,EACjB,iBAAiB,EAClB,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"bytedance.service.d.ts","sourceRoot":"","sources":["../../../../../src/services/aiGen/providers/bytedance/bytedance.service.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,wBAAwB,EAAE,MAAM,8BAA8B,CAAC;AACxE,OAAO,EACL,iBAAiB,EACjB,qBAAqB,EACrB,qBAAqB,EACrB,qBAAqB,EACrB,qBAAqB,EACrB,iBAAiB,EACjB,iBAAiB,EAClB,MAAM,UAAU,CAAC;AA8BlB;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAQlF;AAsBD,qBAAa,gBAAiB,SAAQ,wBAAwB;IAC5D,OAAO,CAAC,QAAQ,CAAC,OAAO,CACsD;IAQ9E,OAAO,CAAC,cAAc;IAYtB,OAAO,CAAC,WAAW;IAIb,aAAa,CACjB,MAAM,EAAE,qBAAqB,GAC5B,OAAO,CAAC,qBAAqB,CAAC;IA+Q3B,gBAAgB,CAAC,EACrB,IAAI,EACJ,cAAc,EACd,cAAyB,GAC1B,EAAE,iBAAiB,GAAG,OAAO,CAAC,iBAAiB,CAAC;IAkJjD;;;;;;;;;;;OAWG;IACG,aAAa,CAAC,MAAM,EAAE,qBAAqB,GAAG,OAAO,CAAC,qBAAqB,CAAC;IAyIlF,+EAA+E;YACjE,mBAAmB;IAoCjC;;;OAGG;YACW,eAAe;IAgD7B,aAAa,CAAC,MAAM,EAAE,iBAAiB,GAAG,MAAM;CAsFjD"}
|
|
@@ -16,6 +16,7 @@ const image_model_1 = require("../../../../models/image.model");
|
|
|
16
16
|
const helpers_2 = require("../../../../utils/helpers");
|
|
17
17
|
const errors_1 = require("../../../../utils/errors");
|
|
18
18
|
const logger_1 = require("../../../../utils/logger");
|
|
19
|
+
const transientRetry_1 = require("../../transientRetry");
|
|
19
20
|
const crypto_1 = __importDefault(require("crypto"));
|
|
20
21
|
const SEEDREAM_URL = "https://ark.ap-southeast.bytepluses.com/api/v3/images/generations";
|
|
21
22
|
/** Long-edge-equivalent of each `size` tier (tier² = target pixel count). */
|
|
@@ -197,13 +198,32 @@ class ByteDanceService extends baseAiGenProvider_service_1.BaseAiGenProviderServ
|
|
|
197
198
|
.digest("hex")
|
|
198
199
|
.slice(0, 16);
|
|
199
200
|
}
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
201
|
+
// ModelArk intermittently answers task creation with a 500
|
|
202
|
+
// InternalServiceError (prod 2026-10-06, several in an hour) that
|
|
203
|
+
// succeeds on resubmit. Retry 5xx only; 4xx stays permanent and 429 is
|
|
204
|
+
// left to the worker's rate limiter.
|
|
205
|
+
const resp = await (0, transientRetry_1.withTransientRetry)("bytedance:generateVideo", async () => {
|
|
206
|
+
const r = await fetch(this.baseUrl, {
|
|
207
|
+
method: "POST",
|
|
208
|
+
headers: {
|
|
209
|
+
"Content-Type": "application/json",
|
|
210
|
+
Authorization: `Bearer ${process.env.BYTEDANCE_API_KEY}`,
|
|
211
|
+
},
|
|
212
|
+
body: JSON.stringify(body),
|
|
213
|
+
});
|
|
214
|
+
if (r.status >= 500) {
|
|
215
|
+
throw new transientRetry_1.TransientHttpError(r.status, `ByteDance generateVideo failed (${r.status}): ${await r.text()}`);
|
|
216
|
+
}
|
|
217
|
+
return r;
|
|
218
|
+
}, {
|
|
219
|
+
baseBackoffMs: 2000,
|
|
220
|
+
onRetry: ({ attempt, maxAttempts, backoffMs, err }) => logger_1.logger.warn("Seedance: transient create error, retrying", {
|
|
221
|
+
modelKey: params.modelKey,
|
|
222
|
+
attempt,
|
|
223
|
+
maxAttempts,
|
|
224
|
+
backoffMs,
|
|
225
|
+
error: err.message.slice(0, 300),
|
|
226
|
+
}),
|
|
207
227
|
});
|
|
208
228
|
if (!resp.ok) {
|
|
209
229
|
const errText = await resp.text();
|
|
@@ -310,6 +330,17 @@ class ByteDanceService extends baseAiGenProvider_service_1.BaseAiGenProviderServ
|
|
|
310
330
|
errorMessage: JSON.stringify(data.error),
|
|
311
331
|
};
|
|
312
332
|
}
|
|
333
|
+
// ModelArk copies the input image to its own storage and transcodes it
|
|
334
|
+
// (`x-tos-process=image/format,jpg`); an image it can't decode fails
|
|
335
|
+
// asynchronously as InvalidParameter "Error while downloading: …ark-…
|
|
336
|
+
// status code: 400" (prod 2026-10-06, a user-uploaded PNG). That's the
|
|
337
|
+
// input file, not our system — tell the user to re-export it.
|
|
338
|
+
if (errCode === "InvalidParameter" && /error while downloading/i.test(errMessage)) {
|
|
339
|
+
return {
|
|
340
|
+
status: types_1.EVideoSceneStatus.FAILED,
|
|
341
|
+
errorMessage: "user_input_error: This model couldn't process your input image. Please re-save it as a standard JPEG or PNG (or use a different image) and try again.",
|
|
342
|
+
};
|
|
343
|
+
}
|
|
313
344
|
return {
|
|
314
345
|
status: types_1.EVideoSceneStatus.FAILED,
|
|
315
346
|
errorMessage: data.error
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runway.service.d.ts","sourceRoot":"","sources":["../../../../../src/services/aiGen/providers/runway/runway.service.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,wBAAwB,EAAE,MAAM,8BAA8B,CAAC;AACxE,OAAO,EACL,iBAAiB,EACjB,qBAAqB,EACrB,qBAAqB,EACrB,qBAAqB,EACrB,qBAAqB,EACrB,iBAAiB,EACjB,iBAAiB,EAClB,MAAM,UAAU,CAAC;AAWlB,qBAAa,aAAc,SAAQ,wBAAwB;IACzD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAqC;IAC7D,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;;YAUlB,OAAO;
|
|
1
|
+
{"version":3,"file":"runway.service.d.ts","sourceRoot":"","sources":["../../../../../src/services/aiGen/providers/runway/runway.service.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,wBAAwB,EAAE,MAAM,8BAA8B,CAAC;AACxE,OAAO,EACL,iBAAiB,EACjB,qBAAqB,EACrB,qBAAqB,EACrB,qBAAqB,EACrB,qBAAqB,EACrB,iBAAiB,EACjB,iBAAiB,EAClB,MAAM,UAAU,CAAC;AAWlB,qBAAa,aAAc,SAAQ,wBAAwB;IACzD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAqC;IAC7D,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;;YAUlB,OAAO;IA8Cf,aAAa,CACjB,MAAM,EAAE,qBAAqB,GAC5B,OAAO,CAAC,qBAAqB,CAAC;IAyJ3B,gBAAgB,CAAC,EACrB,IAAI,EACJ,cAAc,EACd,cAAyB,GAC1B,EAAE,iBAAiB,GAAG,OAAO,CAAC,iBAAiB,CAAC;IAuDjD,aAAa,CAAC,EACZ,QAAQ,EACR,QAAY,EACZ,SAAiB,EACjB,SAAa,EACb,WAAW,EACX,WAAW,EACX,UAAU,EACV,KAAK,EACL,aAAa,EACb,qBAAqB,EACrB,cAAkB,EAClB,cAAkB,GACnB,EAAE,iBAAiB,GAAG,MAAM;IAwD7B;;;;;;;;OAQG;IACG,aAAa,CACjB,MAAM,EAAE,qBAAqB,GAC5B,OAAO,CAAC,qBAAqB,CAAC;CA+ElC"}
|
|
@@ -41,6 +41,14 @@ class RunwayService extends baseAiGenProvider_service_1.BaseAiGenProviderService
|
|
|
41
41
|
validateStatus: () => true, // manually handle status
|
|
42
42
|
});
|
|
43
43
|
if (response.status < 200 || response.status >= 300) {
|
|
44
|
+
// Runway validates promptText length server-side (1000 chars on its
|
|
45
|
+
// native models; prod 2026-10-06 runway-gen4.5). That's the user's
|
|
46
|
+
// prompt, not a system fault — surface it like every other provider.
|
|
47
|
+
const issues = response.status === 400 && Array.isArray(response.data?.issues) ? response.data.issues : [];
|
|
48
|
+
const tooLong = issues.find((i) => i.code === "too_big" && Array.isArray(i.path) && i.path[0] === "promptText");
|
|
49
|
+
if (tooLong) {
|
|
50
|
+
throw new errors_1.UserFacingError(`Your prompt is too long for this model${tooLong.maximum ? ` (max ${tooLong.maximum} characters)` : ""}. Please shorten it and try again.`, errors_1.USER_FACING_ERROR_CODES.PROMPT_TOO_LONG);
|
|
51
|
+
}
|
|
44
52
|
throw new Error(`Runway API Error: ${response.status} ${response.statusText} - ${JSON.stringify(response.data)}`);
|
|
45
53
|
}
|
|
46
54
|
return response.data;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"avatarProviderFactory.d.ts","sourceRoot":"","sources":["../../../src/services/avatarGen/avatarProviderFactory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"avatarProviderFactory.d.ts","sourceRoot":"","sources":["../../../src/services/avatarGen/avatarProviderFactory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAEzD,OAAO,EACL,kBAAkB,EAClB,oBAAoB,EAGrB,MAAM,wBAAwB,CAAC;AAgDhC,oFAAoF;AACpF,wBAAgB,aAAa,CAAC,QAAQ,EAAE,eAAe,GAAG,OAAO,CAEhE;AAED,kFAAkF;AAClF,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,eAAe,GACxB,kBAAkB,GAAG,SAAS,CAEhC;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,eAAe,GAAG,OAAO,CAEtE;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,eAAe,GACxB,oBAAoB,CAMtB"}
|
|
@@ -5,6 +5,8 @@ exports.getAvatarCapabilities = getAvatarCapabilities;
|
|
|
5
5
|
exports.isHostedAvatarModel = isHostedAvatarModel;
|
|
6
6
|
exports.getAvatarVideoProvider = getAvatarVideoProvider;
|
|
7
7
|
const aiModels_1 = require("../../globals/aiModels");
|
|
8
|
+
const cdnUrl_1 = require("../../libs/storage/cdnUrl");
|
|
9
|
+
const IAvatarVideoProvider_1 = require("./IAvatarVideoProvider");
|
|
8
10
|
const heygenAvatarProvider_1 = require("./providers/heygenAvatarProvider");
|
|
9
11
|
const klingAvatarProvider_1 = require("./providers/klingAvatarProvider");
|
|
10
12
|
const synthesiaAvatarProvider_1 = require("./providers/synthesiaAvatarProvider");
|
|
@@ -53,5 +55,24 @@ function getAvatarVideoProvider(modelKey) {
|
|
|
53
55
|
if (!factory) {
|
|
54
56
|
throw new Error(`No avatar provider registered for model: ${modelKey}`);
|
|
55
57
|
}
|
|
56
|
-
return factory(modelKey);
|
|
58
|
+
return withCdnInputs(factory(modelKey));
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Same as the generic factory's `withCdnInputs`: our own media URLs in a
|
|
62
|
+
* request (face image, TTS audio, training/dub video) are GCS-shaped but
|
|
63
|
+
* R2-backed since 2026-09-22, so the engine fetching them gets a 403 unless
|
|
64
|
+
* they're rewritten to the CDN first.
|
|
65
|
+
*/
|
|
66
|
+
function withCdnInputs(provider) {
|
|
67
|
+
const generate = provider.generate.bind(provider);
|
|
68
|
+
provider.generate = (req) => generate((0, cdnUrl_1.toCdnUrlsDeep)(req));
|
|
69
|
+
if ((0, IAvatarVideoProvider_1.supportsReplica)(provider)) {
|
|
70
|
+
const createReplica = provider.createReplica.bind(provider);
|
|
71
|
+
provider.createReplica = (url, consent) => createReplica((0, cdnUrl_1.toCdnUrl)(url, { visibility: "private" }), consent);
|
|
72
|
+
}
|
|
73
|
+
if ((0, IAvatarVideoProvider_1.supportsDubbing)(provider)) {
|
|
74
|
+
const dub = provider.dub.bind(provider);
|
|
75
|
+
provider.dub = (url, locale) => dub((0, cdnUrl_1.toCdnUrl)(url, { visibility: "private" }), locale);
|
|
76
|
+
}
|
|
77
|
+
return provider;
|
|
57
78
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"klingElementLibrary.service.d.ts","sourceRoot":"","sources":["../../../src/services/klingElements/klingElementLibrary.service.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,kBAAkB,EAClB,gBAAgB,EAEhB,yBAAyB,EAC1B,MAAM,iCAAiC,CAAC;
|
|
1
|
+
{"version":3,"file":"klingElementLibrary.service.d.ts","sourceRoot":"","sources":["../../../src/services/klingElements/klingElementLibrary.service.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,kBAAkB,EAClB,gBAAgB,EAEhB,yBAAyB,EAC1B,MAAM,iCAAiC,CAAC;AAezC,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,yBAAyB,CAAC;CAC1C;AAED,MAAM,WAAW,uBAAuB;IACtC,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,6EAA6E;IAC7E,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,gEAAgE;IAChE,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAiBD;;;;GAIG;AACH,qBAAa,mBAAmB;WACjB,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,uBAAuB,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAsEhG,8EAA8E;WACjE,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,EAAE,CAAC;IA4BvE,gEAAgE;WACnD,OAAO,CAAC,GAAG,EAAE,kBAAkB,GAAG,OAAO,CAAC,kBAAkB,CAAC;WAsB7D,WAAW,IAAI,OAAO,CAAC,kBAAkB,EAAE,CAAC;WAS5C,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAelE;;;;OAIG;WACU,oBAAoB,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC;CAkBrG"}
|
|
@@ -4,6 +4,7 @@ exports.KlingElementLibrary = void 0;
|
|
|
4
4
|
const klingElement_model_1 = require("../../models/klingElement.model");
|
|
5
5
|
const logger_1 = require("../../utils/logger");
|
|
6
6
|
const errors_1 = require("../../utils/errors");
|
|
7
|
+
const cdnUrl_1 = require("../../libs/storage/cdnUrl");
|
|
7
8
|
const kling_service_1 = require("../aiGen/providers/kling/kling.service");
|
|
8
9
|
const firestore_service_1 = require("../firestore.service");
|
|
9
10
|
const TAG = "[KlingElementLibrary]";
|
|
@@ -68,7 +69,8 @@ class KlingElementLibrary {
|
|
|
68
69
|
},
|
|
69
70
|
}),
|
|
70
71
|
};
|
|
71
|
-
|
|
72
|
+
// Our own uploads are R2-backed since 2026-09-22 — Kling can only fetch them via the CDN.
|
|
73
|
+
const taskId = await new kling_service_1.KlingService().createElement((0, cdnUrl_1.toCdnUrlsDeep)(body));
|
|
72
74
|
const now = new Date().toISOString();
|
|
73
75
|
const doc = {
|
|
74
76
|
userId,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"outreachWebhooks.service.d.ts","sourceRoot":"","sources":["../../src/services/outreachWebhooks.service.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"outreachWebhooks.service.d.ts","sourceRoot":"","sources":["../../src/services/outreachWebhooks.service.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,oBAAoB,EAAE,MAAM,qCAAqC,CAAC;AAK3E;;;;;GAKG;AACH,MAAM,WAAW,yBAAyB;IACxC,cAAc,EAAE,MAAM,CAAC;IACvB,KAAK,EAAE,oBAAoB,CAAC;IAC5B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC9B,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;;GAQG;AACH,wBAAsB,4BAA4B,CAChD,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,oBAAoB,EAC3B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC5B,OAAO,CAAC,IAAI,CAAC,CAiCf"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.dispatchOutreachWebhookEvent = dispatchOutreachWebhookEvent;
|
|
4
|
+
const cdnUrl_1 = require("../libs/storage/cdnUrl");
|
|
4
5
|
const crypto_1 = require("crypto");
|
|
5
6
|
const types_1 = require("../globals/types");
|
|
6
7
|
const logger_1 = require("../utils/logger");
|
|
@@ -27,7 +28,9 @@ async function dispatchOutreachWebhookEvent(userId, event, data) {
|
|
|
27
28
|
await Promise.all(snap.docs.map((doc) => bullmq_service_1.bullmq.addJob(types_1.ERedisQueues.WEBHOOK_DELIVERY_JOBS, {
|
|
28
29
|
subscriptionId: doc.id,
|
|
29
30
|
event,
|
|
30
|
-
|
|
31
|
+
// Customers' servers fetch these media URLs; ours only resolve
|
|
32
|
+
// through the CDN since the R2-primary flip (2026-09-22).
|
|
33
|
+
data: (0, cdnUrl_1.toCdnUrlsDeep)(data),
|
|
31
34
|
deliveryId: (0, crypto_1.randomUUID)(),
|
|
32
35
|
}, { attempts: 5, backoff: { type: "exponential", delay: 5000 } })));
|
|
33
36
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"realPersonSafety.d.ts","sourceRoot":"","sources":["../../../src/services/stock/realPersonSafety.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;
|
|
1
|
+
{"version":3,"file":"realPersonSafety.d.ts","sourceRoot":"","sources":["../../../src/services/stock/realPersonSafety.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AASH,MAAM,WAAW,sBAAsB;IACrC,yCAAyC;IACzC,MAAM,EAAE,MAAM,CAAC;IACf,wDAAwD;IACxD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,yCAAyC;IACzC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,cAAc;IAC7B,uEAAuE;IACvE,OAAO,EAAE,OAAO,CAAC;IACjB,wEAAwE;IACxE,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,sCAAsC;IACtC,MAAM,EAAE,MAAM,CAAC;IACf,0EAA0E;IAC1E,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,6EAA6E;IAC7E,kBAAkB,EAAE,OAAO,CAAC;IAC5B,6BAA6B;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,2EAA2E;IAC3E,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,gBAAgB;IAC/B,0GAA0G;IAC1G,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,wEAAwE;IACxE,SAAS,EAAE,MAAM,EAAE,CAAC;CACrB;AAED,MAAM,WAAW,iBAAiB;IAChC,4DAA4D;IAC5D,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,cAAc,CAAC;IAC1B,UAAU,EAAE,iBAAiB,EAAE,CAAC;IAChC,gFAAgF;IAChF,kBAAkB,EAAE,OAAO,CAAC;IAC5B,6EAA6E;IAC7E,UAAU,EAAE,OAAO,CAAC;IACpB,mEAAmE;IACnE,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAuCD;;;;GAIG;AACH,wBAAsB,2BAA2B,CAC/C,KAAK,EAAE,MAAM,EAAE,EACf,MAAM,EAAE,sBAAsB,GAC7B,OAAO,CAAC,cAAc,CAAC,CA6CzB;AA4BD;;;;GAIG;AACH,wBAAsB,uBAAuB,CAC3C,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,sBAAsB,GAC7B,OAAO,CAAC,iBAAiB,CAAC,CAyD5B;AAED,MAAM,WAAW,aAAa;IAC5B,mEAAmE;IACnE,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;GAMG;AACH,wBAAsB,mBAAmB,CACvC,KAAK,EAAE,gBAAgB,EACvB,MAAM,EAAE,sBAAsB,EAC9B,IAAI,GAAE,aAAkB,GACvB,OAAO,CAAC,iBAAiB,CAAC,CA8C5B"}
|
|
@@ -32,6 +32,7 @@ exports.detectRealPersonNamesInText = detectRealPersonNamesInText;
|
|
|
32
32
|
exports.detectRealPersonInImage = detectRealPersonInImage;
|
|
33
33
|
exports.screenForRealPerson = screenForRealPerson;
|
|
34
34
|
const genai_1 = require("@google/genai");
|
|
35
|
+
const cdnUrl_1 = require("../../libs/storage/cdnUrl");
|
|
35
36
|
const logger_1 = require("../../utils/logger");
|
|
36
37
|
const DEFAULT_MODEL = "gemini-2.5-flash";
|
|
37
38
|
const DEFAULT_TIMEOUT_MS = 30000;
|
|
@@ -154,7 +155,7 @@ async function detectRealPersonInImage(imageUrl, config) {
|
|
|
154
155
|
const controller = new AbortController();
|
|
155
156
|
const t = setTimeout(() => controller.abort(), timeoutMs);
|
|
156
157
|
try {
|
|
157
|
-
const r = await
|
|
158
|
+
const r = await (0, cdnUrl_1.fetchOwnMedia)(imageUrl, { signal: controller.signal });
|
|
158
159
|
if (!r.ok)
|
|
159
160
|
throw new Error(`fetch ${r.status} ${r.statusText}`);
|
|
160
161
|
const bytes = Buffer.from(await r.arrayBuffer());
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"translation.service.d.ts","sourceRoot":"","sources":["../../../src/services/translation/translation.service.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAGlE;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,eAAO,MAAM,kBAAkB,uBAAuB,CAAC;AAiDvD;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,SAAkB,GAAG,MAAM,EAAE,CA8BjF;AA4ID;;;;;;GAMG;AACH,iBAAe,SAAS,CACtB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,cAAc,EACpB,EAAE,GAAE,cAAkC,EACtC,OAAO,GAAE,IAAI,GAAG,IAAW,GAC1B,OAAO,CAAC,MAAM,CAAC,CAiBjB;
|
|
1
|
+
{"version":3,"file":"translation.service.d.ts","sourceRoot":"","sources":["../../../src/services/translation/translation.service.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAGlE;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,eAAO,MAAM,kBAAkB,uBAAuB,CAAC;AAiDvD;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,SAAkB,GAAG,MAAM,EAAE,CA8BjF;AA4ID;;;;;;GAMG;AACH,iBAAe,SAAS,CACtB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,cAAc,EACpB,EAAE,GAAE,cAAkC,EACtC,OAAO,GAAE,IAAI,GAAG,IAAW,GAC1B,OAAO,CAAC,MAAM,CAAC,CAiBjB;AA+ID;;;;;;;;;;;;;;GAcG;AACH,iBAAe,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CA8ChE;AAOD,eAAO,MAAM,kBAAkB;;;CAG9B,CAAC;AAGF,qDAAqD;AACrD,eAAO,MAAM,qBAAqB,kBAAY,CAAC;AAC/C,0DAA0D;AAC1D,eAAO,MAAM,UAAU,uBAAiB,CAAC"}
|
|
@@ -276,33 +276,72 @@ async function translate(text, from, to = types_1.ELANGUAGE_CODE.en, version = "
|
|
|
276
276
|
});
|
|
277
277
|
return translated.join("");
|
|
278
278
|
}
|
|
279
|
-
|
|
279
|
+
// 2026-10-07: chunking fixed the long-prompt failures, but the two layers
|
|
280
|
+
// still ran strictly in sequence, so a primary that was merely SLOW (not down)
|
|
281
|
+
// burned up to 2 × primaryTimeoutFor() ≈ 17s before the LLM even started —
|
|
282
|
+
// prod paged "slow step >30s: getTranslationWrapper" at 30-40s for short `bg`
|
|
283
|
+
// / `ru` prompts. Hedge instead: if the primary hasn't answered within
|
|
284
|
+
// HEDGE_DELAY_MS (or fails sooner), start the LLM alongside it and take
|
|
285
|
+
// whichever succeeds first. Healthy primaries answer well inside the delay,
|
|
286
|
+
// so the LLM still only runs on the slow/failing tail.
|
|
287
|
+
const HEDGE_DELAY_MS = 3000;
|
|
288
|
+
/** Resolves with the first promise to fulfil; rejects with every error only
|
|
289
|
+
* once all of them have rejected (Promise.any without the ES2021 lib). */
|
|
290
|
+
function firstSuccess(promises) {
|
|
291
|
+
return new Promise((resolve, reject) => {
|
|
292
|
+
const errors = new Array(promises.length);
|
|
293
|
+
let pending = promises.length;
|
|
294
|
+
promises.forEach((p, i) => {
|
|
295
|
+
p.then(resolve, (err) => {
|
|
296
|
+
errors[i] = err;
|
|
297
|
+
if (--pending === 0)
|
|
298
|
+
reject(errors);
|
|
299
|
+
});
|
|
300
|
+
});
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
/** One chunk through primary, hedged with the LLM fallback. */
|
|
280
304
|
async function translateChunk(text, from, to, version) {
|
|
281
305
|
// ── Layer 1: primary Cloud Function ──────────────────────────────────────
|
|
282
306
|
let primaryErr;
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
307
|
+
const primary = (async () => {
|
|
308
|
+
for (let attempt = 1; attempt <= PRIMARY_MAX_ATTEMPTS; attempt++) {
|
|
309
|
+
try {
|
|
310
|
+
return await callPrimaryTranslate(text, from, to, version);
|
|
311
|
+
}
|
|
312
|
+
catch (err) {
|
|
313
|
+
primaryErr = err;
|
|
314
|
+
if (attempt < PRIMARY_MAX_ATTEMPTS) {
|
|
315
|
+
await new Promise((r) => setTimeout(r, 200 * attempt));
|
|
316
|
+
}
|
|
291
317
|
}
|
|
292
318
|
}
|
|
293
|
-
|
|
319
|
+
throw primaryErr;
|
|
320
|
+
})();
|
|
294
321
|
// ── Layer 2: LLM fallback (the common recovery path — WARN, not Slack) ────
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
322
|
+
// Starts on primary failure or after HEDGE_DELAY_MS, whichever comes first.
|
|
323
|
+
let startFallback;
|
|
324
|
+
const fallbackGate = new Promise((r) => (startFallback = r));
|
|
325
|
+
const hedgeTimer = setTimeout(() => startFallback(), HEDGE_DELAY_MS);
|
|
326
|
+
primary.catch(() => startFallback());
|
|
327
|
+
const fallback = fallbackGate.then(() => {
|
|
328
|
+
logger_1.logger.warn(primaryErr
|
|
329
|
+
? "translation: primary failed, falling back to LLM"
|
|
330
|
+
: "translation: primary slow, hedging with LLM", {
|
|
331
|
+
sourceLang: from,
|
|
332
|
+
targetLang: to,
|
|
333
|
+
primaryAttempts: PRIMARY_MAX_ATTEMPTS,
|
|
334
|
+
hedgeDelayMs: HEDGE_DELAY_MS,
|
|
335
|
+
fallbackModel: FALLBACK_MODEL,
|
|
336
|
+
err: primaryErr ? errMsg(primaryErr) : undefined,
|
|
337
|
+
});
|
|
338
|
+
return callLlmTranslate(text, from, to);
|
|
301
339
|
});
|
|
302
340
|
try {
|
|
303
|
-
return await
|
|
341
|
+
return await firstSuccess([primary, fallback]);
|
|
304
342
|
}
|
|
305
|
-
catch (
|
|
343
|
+
catch (errors) {
|
|
344
|
+
const [, fallbackErr] = errors;
|
|
306
345
|
// Both layers down — this is the only case worth a Slack alert.
|
|
307
346
|
logger_1.logger.error("translation failed on BOTH primary and LLM fallback", {
|
|
308
347
|
sourceLang: from,
|
|
@@ -314,6 +353,9 @@ async function translateChunk(text, from, to, version) {
|
|
|
314
353
|
e.cause = fallbackErr;
|
|
315
354
|
throw e;
|
|
316
355
|
}
|
|
356
|
+
finally {
|
|
357
|
+
clearTimeout(hedgeTimer);
|
|
358
|
+
}
|
|
317
359
|
}
|
|
318
360
|
/** One un-retried LLM detect attempt. */
|
|
319
361
|
async function callLlmDetectOnce(text) {
|