vidspotai-shared 1.0.100 → 1.0.101-dev.0
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/globals/aiModels/enums.d.ts +1 -0
- package/lib/globals/aiModels/enums.d.ts.map +1 -1
- package/lib/globals/aiModels/enums.js +3 -0
- package/lib/globals/aiModels/extendCapability.d.ts +26 -0
- package/lib/globals/aiModels/extendCapability.d.ts.map +1 -0
- package/lib/globals/aiModels/extendCapability.js +42 -0
- package/lib/globals/aiModels/index.d.ts +1 -0
- package/lib/globals/aiModels/index.d.ts.map +1 -1
- package/lib/globals/aiModels/index.js +1 -0
- package/lib/globals/aiModels/providers/google.d.ts.map +1 -1
- package/lib/globals/aiModels/providers/google.js +6 -9
- package/lib/globals/aiModels/providers/hostedAvatars.js +10 -10
- package/lib/globals/creditUnit.d.ts +20 -33
- package/lib/globals/creditUnit.d.ts.map +1 -1
- package/lib/globals/creditUnit.js +20 -33
- package/lib/globals/types.d.ts +34 -2
- package/lib/globals/types.d.ts.map +1 -1
- package/lib/globals/types.js +37 -0
- package/lib/models/demoVideoProject.model.d.ts +213 -0
- package/lib/models/demoVideoProject.model.d.ts.map +1 -1
- package/lib/models/index.d.ts +2 -0
- package/lib/models/index.d.ts.map +1 -1
- package/lib/models/index.js +2 -0
- package/lib/models/mediaUpload.model.d.ts +25 -0
- package/lib/models/mediaUpload.model.d.ts.map +1 -1
- package/lib/models/productConnection.model.d.ts +57 -0
- package/lib/models/productConnection.model.d.ts.map +1 -0
- package/lib/models/productConnection.model.js +7 -0
- package/lib/models/video.model.d.ts +11 -0
- package/lib/models/video.model.d.ts.map +1 -1
- package/lib/models/videoQaJob.model.d.ts +61 -0
- package/lib/models/videoQaJob.model.d.ts.map +1 -0
- package/lib/models/videoQaJob.model.js +16 -0
- package/lib/schemas/brandKit.schema.d.ts +3 -1
- package/lib/schemas/brandKit.schema.d.ts.map +1 -1
- package/lib/schemas/brandKit.schema.js +9 -0
- package/lib/schemas/brief.schema.d.ts +5 -4
- package/lib/schemas/brief.schema.d.ts.map +1 -1
- package/lib/schemas/brief.schema.js +23 -1
- package/lib/schemas/demoQuestion.schema.d.ts +24 -4
- package/lib/schemas/demoQuestion.schema.d.ts.map +1 -1
- package/lib/schemas/demoQuestion.schema.js +8 -2
- package/lib/schemas/demoSpec.schema.d.ts +40 -6
- package/lib/schemas/demoSpec.schema.d.ts.map +1 -1
- package/lib/schemas/demoSpec.schema.js +46 -1
- package/lib/schemas/project.schema.d.ts +33 -3
- package/lib/schemas/project.schema.d.ts.map +1 -1
- package/lib/schemas/project.schema.js +22 -0
- package/lib/schemas/social.schema.d.ts +1 -1
- package/lib/schemas/videoPlan.schema.d.ts +22 -20
- package/lib/schemas/videoPlan.schema.d.ts.map +1 -1
- package/lib/schemas/videoPlan.schema.js +19 -0
- package/lib/services/agent/editClassifier.d.ts +10 -10
- package/lib/services/agent/eval/types.d.ts +3 -2
- package/lib/services/agent/eval/types.d.ts.map +1 -1
- package/lib/services/agent/executor/core.d.ts.map +1 -1
- package/lib/services/agent/executor/core.js +11 -0
- package/lib/services/agent/planner/Planner.d.ts.map +1 -1
- package/lib/services/agent/planner/Planner.js +5 -0
- package/lib/services/agent/planner/overlayRegen.d.ts.map +1 -1
- package/lib/services/agent/planner/overlayRegen.js +5 -0
- package/lib/services/agent/planner/plannerMessages.d.ts.map +1 -1
- package/lib/services/agent/planner/plannerMessages.js +10 -1
- package/lib/services/agent/planner/scriptFirstPlanner.d.ts.map +1 -1
- package/lib/services/agent/planner/scriptFirstPlanner.js +2 -0
- package/lib/services/agent/regenCore.d.ts.map +1 -1
- package/lib/services/agent/regenCore.js +11 -1
- package/lib/services/agent/runHelpers.d.ts +8 -0
- package/lib/services/agent/runHelpers.d.ts.map +1 -1
- package/lib/services/agent/runHelpers.js +5 -1
- package/lib/services/agent/tools/chapterOutline.tool.d.ts +8 -0
- package/lib/services/agent/tools/chapterOutline.tool.d.ts.map +1 -1
- package/lib/services/agent/tools/chapterOutline.tool.js +21 -8
- package/lib/services/agent/tools/composeScene.tool.d.ts +32 -2
- package/lib/services/agent/tools/composeScene.tool.d.ts.map +1 -1
- package/lib/services/agent/tools/estimateCost.tool.d.ts +11 -1
- package/lib/services/agent/tools/estimateCost.tool.d.ts.map +1 -1
- package/lib/services/agent/tools/extendVideo.tool.d.ts +9 -3
- package/lib/services/agent/tools/extendVideo.tool.d.ts.map +1 -1
- package/lib/services/agent/tools/extendVideo.tool.js +12 -27
- package/lib/services/agent/tools/generateAvatarVideo.tool.d.ts +1 -1
- package/lib/services/agent/tools/generateScript.tool.d.ts +3 -2
- package/lib/services/agent/tools/generateScript.tool.d.ts.map +1 -1
- package/lib/services/agent/tools/generateScript.tool.js +16 -7
- package/lib/services/agent/tools/generateVoiceover.tool.d.ts +1 -1
- package/lib/services/agent/tools/planVideo.tool.d.ts +11 -8
- package/lib/services/agent/tools/planVideo.tool.d.ts.map +1 -1
- package/lib/services/agent/tools/render.tool.d.ts +11 -1
- package/lib/services/agent/tools/render.tool.d.ts.map +1 -1
- package/lib/services/agent/tools/searchStock.tool.d.ts +1 -1
- package/lib/services/agent/tools/searchUserLibrary.tool.d.ts +4 -4
- package/lib/services/aiGen/providers/alibaba/alibaba.d.ts +0 -6
- package/lib/services/aiGen/providers/alibaba/alibaba.d.ts.map +1 -1
- package/lib/services/aiGen/providers/alibaba/alibaba.js +5 -103
- package/lib/services/aiGen/providers/bytedance/bytedance.service.d.ts.map +1 -1
- package/lib/services/aiGen/providers/bytedance/bytedance.service.js +1 -9
- package/lib/services/aiGen/providers/google/google.service.d.ts.map +1 -1
- package/lib/services/aiGen/providers/google/google.service.js +26 -142
- package/lib/services/aiGen/providers/google/googleErrors.d.ts.map +1 -1
- package/lib/services/aiGen/providers/google/googleErrors.js +7 -17
- package/lib/services/aiGen/providers/kling/kling.service.d.ts.map +1 -1
- package/lib/services/aiGen/providers/kling/kling.service.js +22 -3
- package/lib/services/aiGen/providers/kling/klingStatus.d.ts +7 -0
- package/lib/services/aiGen/providers/kling/klingStatus.d.ts.map +1 -1
- package/lib/services/aiGen/providers/kling/klingStatus.js +41 -4
- package/lib/services/aiGen/providers/pixverse/pixverse.service.d.ts.map +1 -1
- package/lib/services/aiGen/providers/pixverse/pixverse.service.js +25 -0
- package/lib/services/aiGen/providers/types.d.ts +20 -0
- package/lib/services/aiGen/providers/types.d.ts.map +1 -1
- package/lib/services/avatarGen/avatarProviderFactory.d.ts.map +1 -1
- package/lib/services/avatarGen/avatarProviderFactory.js +3 -0
- package/lib/services/avatarGen/index.d.ts +1 -0
- package/lib/services/avatarGen/index.d.ts.map +1 -1
- package/lib/services/avatarGen/index.js +1 -0
- package/lib/services/avatarGen/providers/synthesiaAvatarProvider.d.ts +53 -0
- package/lib/services/avatarGen/providers/synthesiaAvatarProvider.d.ts.map +1 -0
- package/lib/services/avatarGen/providers/synthesiaAvatarProvider.js +176 -0
- package/lib/services/demo/demoAuthor.d.ts +7 -0
- package/lib/services/demo/demoAuthor.d.ts.map +1 -1
- package/lib/services/demo/demoAuthor.js +2 -0
- package/lib/services/demo/demoAuthorPrompt.d.ts +6 -0
- package/lib/services/demo/demoAuthorPrompt.d.ts.map +1 -1
- package/lib/services/demo/demoAuthorPrompt.js +24 -2
- package/lib/services/demo/demoCaptions.d.ts +69 -0
- package/lib/services/demo/demoCaptions.d.ts.map +1 -0
- package/lib/services/demo/demoCaptions.js +152 -0
- package/lib/services/demo/demoMusic.d.ts +8 -1
- package/lib/services/demo/demoMusic.d.ts.map +1 -1
- package/lib/services/demo/demoMusic.js +32 -16
- package/lib/services/demo/demoScriptPrompt.d.ts +2 -1
- package/lib/services/demo/demoScriptPrompt.d.ts.map +1 -1
- package/lib/services/demo/demoScriptPrompt.js +29 -2
- package/lib/services/demo/demoStockAssets.d.ts +29 -0
- package/lib/services/demo/demoStockAssets.d.ts.map +1 -0
- package/lib/services/demo/demoStockAssets.js +107 -0
- package/lib/services/demo/index.d.ts +3 -0
- package/lib/services/demo/index.d.ts.map +1 -1
- package/lib/services/demo/index.js +3 -0
- package/lib/services/demo/productConnection.service.d.ts +78 -0
- package/lib/services/demo/productConnection.service.d.ts.map +1 -0
- package/lib/services/demo/productConnection.service.js +125 -0
- package/lib/services/editor/planToProject.d.ts.map +1 -1
- package/lib/services/editor/planToProject.helpers.d.ts +1 -0
- package/lib/services/editor/planToProject.helpers.d.ts.map +1 -1
- package/lib/services/editor/planToProject.helpers.js +3 -0
- package/lib/services/editor/planToProject.js +39 -0
- package/lib/services/editor/projectToDesign.js +13 -1
- package/lib/services/firestore.service.d.ts +14 -0
- package/lib/services/firestore.service.d.ts.map +1 -1
- package/lib/services/firestore.service.js +16 -0
- package/lib/services/translation/index.d.ts +1 -0
- package/lib/services/translation/index.d.ts.map +1 -1
- package/lib/services/translation/index.js +8 -1
- package/lib/services/translation/outputLanguage.d.ts +75 -0
- package/lib/services/translation/outputLanguage.d.ts.map +1 -0
- package/lib/services/translation/outputLanguage.js +190 -0
- package/lib/services/translation/translation.service.d.ts +13 -2
- package/lib/services/translation/translation.service.d.ts.map +1 -1
- package/lib/services/translation/translation.service.js +73 -31
- package/lib/services/tts/index.d.ts +1 -0
- package/lib/services/tts/index.d.ts.map +1 -1
- package/lib/services/tts/index.js +1 -0
- package/lib/services/tts/providers/elevenlabs.service.d.ts +7 -0
- package/lib/services/tts/providers/elevenlabs.service.d.ts.map +1 -1
- package/lib/services/tts/providers/elevenlabs.service.js +69 -13
- package/lib/services/tts/types.d.ts +29 -0
- package/lib/services/tts/types.d.ts.map +1 -1
- package/lib/services/tts/wordTimings.d.ts +27 -0
- package/lib/services/tts/wordTimings.d.ts.map +1 -0
- package/lib/services/tts/wordTimings.js +57 -0
- package/lib/utils/billableDuration.d.ts +11 -0
- package/lib/utils/billableDuration.d.ts.map +1 -0
- package/lib/utils/billableDuration.js +91 -0
- package/lib/utils/index.d.ts +3 -0
- package/lib/utils/index.d.ts.map +1 -1
- package/lib/utils/index.js +3 -0
- package/lib/utils/mp4Duration.d.ts +38 -0
- package/lib/utils/mp4Duration.d.ts.map +1 -0
- package/lib/utils/mp4Duration.js +164 -0
- package/lib/utils/socialEntitlements.d.ts +51 -0
- package/lib/utils/socialEntitlements.d.ts.map +1 -0
- package/lib/utils/socialEntitlements.js +65 -0
- package/package.json +1 -1
|
@@ -4,7 +4,6 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.AlibabaService = void 0;
|
|
7
|
-
exports.resolveDashScopeSize = resolveDashScopeSize;
|
|
8
7
|
const axios_1 = __importDefault(require("axios"));
|
|
9
8
|
const types_1 = require("../../../../globals/types");
|
|
10
9
|
const baseAiGenProvider_service_1 = require("../baseAiGenProvider.service");
|
|
@@ -60,68 +59,6 @@ function isSyncMultimodalImageModel(modelId) {
|
|
|
60
59
|
function isAsyncWanImageModel(modelId) {
|
|
61
60
|
return modelId.startsWith("wan2.7-image") || modelId.startsWith("wan2.6-image");
|
|
62
61
|
}
|
|
63
|
-
/**
|
|
64
|
-
* DashScope's `size` parameter is "WIDTH*HEIGHT" in PIXELS — it is NOT an
|
|
65
|
-
* aspect-ratio string. We used to assign `parameters.size = params.aspectRatio`
|
|
66
|
-
* directly, so every request with an aspect ratio shipped `size: "9:16"` and
|
|
67
|
-
* came back `400 InvalidParameter` (observed in prod on qwen-image-2.0-pro,
|
|
68
|
-
* 2026-07-26). The 400 body was also swallowed (see `request`), which is why it
|
|
69
|
-
* only ever surfaced as the opaque "Request failed with status code 400".
|
|
70
|
-
*
|
|
71
|
-
* Two documented preset families, distinguished by their pixel budget:
|
|
72
|
-
* - qwen-image-2.x → 2048×2048-class (square preset 2048*2048)
|
|
73
|
-
* - everything else on this endpoint (max / plus / edit / wan2.7)
|
|
74
|
-
* → 1328×1328-class (square preset 1328*1328)
|
|
75
|
-
* Values below are the documented allowed presets, so they are guaranteed-valid
|
|
76
|
-
* rather than inferred. Ratios with no published preset (2:3, 3:2) are computed
|
|
77
|
-
* from the family's square-preset AREA and rounded to a multiple of 16, which
|
|
78
|
-
* keeps them inside the documented total-pixel range.
|
|
79
|
-
*/
|
|
80
|
-
const DASHSCOPE_SIZE_PRESETS = {
|
|
81
|
-
// qwen-image-2.x series
|
|
82
|
-
large: {
|
|
83
|
-
"1:1": "2048*2048",
|
|
84
|
-
"16:9": "2688*1536",
|
|
85
|
-
"9:16": "1536*2688",
|
|
86
|
-
"4:3": "2368*1728",
|
|
87
|
-
"3:4": "1728*2368",
|
|
88
|
-
},
|
|
89
|
-
// qwen-image-max / -plus / -edit, wan2.7-image
|
|
90
|
-
standard: {
|
|
91
|
-
"1:1": "1328*1328",
|
|
92
|
-
"16:9": "1664*928",
|
|
93
|
-
"9:16": "928*1664",
|
|
94
|
-
"4:3": "1472*1104",
|
|
95
|
-
"3:4": "1104*1472",
|
|
96
|
-
},
|
|
97
|
-
};
|
|
98
|
-
/** Round to the nearest multiple of 16 (DashScope dimensions are 16-aligned). */
|
|
99
|
-
function round16(n) {
|
|
100
|
-
return Math.max(16, Math.round(n / 16) * 16);
|
|
101
|
-
}
|
|
102
|
-
/**
|
|
103
|
-
* Map a canonical "W:H" aspect ratio to a DashScope pixel `size` string.
|
|
104
|
-
* Returns undefined for unparseable input so the caller simply omits `size`
|
|
105
|
-
* and lets DashScope apply its own default (better than sending a bad value).
|
|
106
|
-
*/
|
|
107
|
-
function resolveDashScopeSize(modelId, aspectRatio) {
|
|
108
|
-
const family = modelId.startsWith("qwen-image-2") ? "large" : "standard";
|
|
109
|
-
const preset = DASHSCOPE_SIZE_PRESETS[family][aspectRatio];
|
|
110
|
-
if (preset)
|
|
111
|
-
return preset;
|
|
112
|
-
// No published preset for this ratio — derive one at the family's area.
|
|
113
|
-
const [wStr, hStr] = aspectRatio.split(":");
|
|
114
|
-
const w = Number(wStr);
|
|
115
|
-
const h = Number(hStr);
|
|
116
|
-
if (!Number.isFinite(w) || !Number.isFinite(h) || w <= 0 || h <= 0) {
|
|
117
|
-
return undefined;
|
|
118
|
-
}
|
|
119
|
-
const squarePreset = DASHSCOPE_SIZE_PRESETS[family]["1:1"];
|
|
120
|
-
const side = Number(squarePreset.split("*")[0]);
|
|
121
|
-
const area = side * side;
|
|
122
|
-
const ratio = w / h;
|
|
123
|
-
return `${round16(Math.sqrt(area * ratio))}*${round16(Math.sqrt(area / ratio))}`;
|
|
124
|
-
}
|
|
125
62
|
class AlibabaService extends baseAiGenProvider_service_1.BaseAiGenProviderService {
|
|
126
63
|
constructor() {
|
|
127
64
|
super();
|
|
@@ -156,32 +93,8 @@ class AlibabaService extends baseAiGenProvider_service_1.BaseAiGenProviderServic
|
|
|
156
93
|
timeout: this.timeout,
|
|
157
94
|
data: method === "POST" ? body : undefined,
|
|
158
95
|
};
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
return res.data;
|
|
162
|
-
}
|
|
163
|
-
catch (err) {
|
|
164
|
-
// DashScope puts the real reason (code + message, e.g.
|
|
165
|
-
// "InvalidParameter: size is invalid") in the RESPONSE BODY. Axios's own
|
|
166
|
-
// message is only "Request failed with status code 400", so without this
|
|
167
|
-
// the actual cause was lost — a prod 400 on qwen-image-2.0-pro was
|
|
168
|
-
// undiagnosable until the body was surfaced (2026-07-26). Re-throw with
|
|
169
|
-
// the provider's code/message appended so it reaches Loki/Slack and the
|
|
170
|
-
// stored job errorMessage.
|
|
171
|
-
const axiosErr = err;
|
|
172
|
-
const data = axiosErr.response?.data;
|
|
173
|
-
if (data && (data.code || data.message)) {
|
|
174
|
-
logger_1.logger.error("Alibaba DashScope request failed", {
|
|
175
|
-
url,
|
|
176
|
-
status: axiosErr.response?.status,
|
|
177
|
-
code: data.code,
|
|
178
|
-
message: data.message,
|
|
179
|
-
requestId: data.request_id,
|
|
180
|
-
});
|
|
181
|
-
throw new Error(`DashScope ${axiosErr.response?.status ?? ""} ${data.code ?? ""}: ${data.message ?? axiosErr.message ?? "request failed"}`.trim());
|
|
182
|
-
}
|
|
183
|
-
throw err;
|
|
184
|
-
}
|
|
96
|
+
const res = await axios_1.default.request(config);
|
|
97
|
+
return res.data;
|
|
185
98
|
}
|
|
186
99
|
async generateVideo(params) {
|
|
187
100
|
(0, helpers_2.validateParams)(params);
|
|
@@ -397,13 +310,8 @@ class AlibabaService extends baseAiGenProvider_service_1.BaseAiGenProviderServic
|
|
|
397
310
|
parameters.prompt_extend = params.promptOptimizer;
|
|
398
311
|
if (params.seed !== undefined)
|
|
399
312
|
parameters.seed = params.seed;
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
if (params.aspectRatio) {
|
|
403
|
-
const size = resolveDashScopeSize(modelId, params.aspectRatio);
|
|
404
|
-
if (size)
|
|
405
|
-
parameters.size = size;
|
|
406
|
-
}
|
|
313
|
+
if (params.aspectRatio)
|
|
314
|
+
parameters.size = params.aspectRatio;
|
|
407
315
|
if (params.imageSize)
|
|
408
316
|
parameters.size = params.imageSize;
|
|
409
317
|
const result = await this.request({ model: modelId, input, parameters }, "POST", ENDPOINT_MULTIMODAL_SYNC, false);
|
|
@@ -424,13 +332,7 @@ class AlibabaService extends baseAiGenProvider_service_1.BaseAiGenProviderServic
|
|
|
424
332
|
input.negative_prompt = params.negativePrompt;
|
|
425
333
|
const parameters = {
|
|
426
334
|
n: params.numImages ?? 1,
|
|
427
|
-
|
|
428
|
-
...(params.aspectRatio
|
|
429
|
-
? (() => {
|
|
430
|
-
const size = resolveDashScopeSize(modelId, params.aspectRatio);
|
|
431
|
-
return size ? { size } : {};
|
|
432
|
-
})()
|
|
433
|
-
: {}),
|
|
335
|
+
...(params.aspectRatio ? { size: params.aspectRatio } : {}),
|
|
434
336
|
...(params.imageSize ? { size: params.imageSize } : {}),
|
|
435
337
|
...(params.seed !== undefined ? { seed: params.seed } : {}),
|
|
436
338
|
...(params.watermark !== undefined ? { watermark: params.watermark } : {}),
|
|
@@ -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,iBAAiB,EACjB,iBAAiB,EAClB,MAAM,UAAU,CAAC;AAUlB,qBAAa,gBAAiB,SAAQ,wBAAwB;IAC5D,OAAO,CAAC,QAAQ,CAAC,OAAO,CACsD;IAI9E,OAAO,CAAC,cAAc;IAStB,OAAO,CAAC,WAAW;IAIb,aAAa,CACjB,MAAM,EAAE,qBAAqB,GAC5B,OAAO,CAAC,qBAAqB,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,iBAAiB,EACjB,iBAAiB,EAClB,MAAM,UAAU,CAAC;AAUlB,qBAAa,gBAAiB,SAAQ,wBAAwB;IAC5D,OAAO,CAAC,QAAQ,CAAC,OAAO,CACsD;IAI9E,OAAO,CAAC,cAAc;IAStB,OAAO,CAAC,WAAW;IAIb,aAAa,CACjB,MAAM,EAAE,qBAAqB,GAC5B,OAAO,CAAC,qBAAqB,CAAC;IA4L3B,gBAAgB,CAAC,EACrB,IAAI,EACJ,cAAc,EACd,cAAyB,GAC1B,EAAE,iBAAiB,GAAG,OAAO,CAAC,iBAAiB,CAAC;IA4HjD,aAAa,CAAC,MAAM,EAAE,iBAAiB,GAAG,MAAM;CAyEjD"}
|
|
@@ -38,15 +38,7 @@ class ByteDanceService extends baseAiGenProvider_service_1.BaseAiGenProviderServ
|
|
|
38
38
|
const modelId = modelConfig.modelId;
|
|
39
39
|
const sanitizedPrompt = this.sanitizePrompt(params.prompt ?? "");
|
|
40
40
|
if (!sanitizedPrompt) {
|
|
41
|
-
|
|
42
|
-
// outside ASCII+CJK, so an empty result means the prompt was written in a
|
|
43
|
-
// script Seedance cannot accept (Cyrillic, Arabic, Devanagari, Thai, an
|
|
44
|
-
// all-emoji prompt, …). That is deterministic user input — it can never
|
|
45
|
-
// succeed on retry. Without a code, `isNonPagingCode(undefined)` is false
|
|
46
|
-
// and the scene-failure handler logged it at `error`, which pages Slack
|
|
47
|
-
// (see errorPaging.ts). PROMPT_INVALID is in NON_PAGING_ERROR_CODES, so
|
|
48
|
-
// this now stays a warn while still failing the scene cleanly.
|
|
49
|
-
throw new errors_1.UserFacingError("Your prompt could not be processed. Please use English or Chinese text and try again.", errors_1.USER_FACING_ERROR_CODES.PROMPT_INVALID);
|
|
41
|
+
throw new errors_1.UserFacingError("Your prompt could not be processed. Please use English or Chinese text and try again.");
|
|
50
42
|
}
|
|
51
43
|
const isV2 = this.isSeedance2(params.modelKey);
|
|
52
44
|
const content = [
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"google.service.d.ts","sourceRoot":"","sources":["../../../../../src/services/aiGen/providers/google/google.service.ts"],"names":[],"mappings":"AAiBA,OAAO,EAAE,wBAAwB,EAAE,MAAM,8BAA8B,CAAC;AACxE,OAAO,EACL,iBAAiB,EACjB,qBAAqB,EACrB,qBAAqB,EACrB,qBAAqB,EACrB,qBAAqB,EACrB,oBAAoB,EACpB,oBAAoB,EACpB,qBAAqB,EACrB,qBAAqB,EACrB,iBAAiB,EACjB,iBAAiB,EAClB,MAAM,UAAU,CAAC;AAkElB,qBAAa,aAAc,SAAQ,wBAAwB;IAKzD,OAAO,CAAC,EAAE,CAAc;IACxB,OAAO,CAAC,OAAO,CAAgB;IAC/B,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,kBAAkB,CAAK;;IAQ/C;;;;;;;;;OASG;IACG,YAAY,CAChB,MAAM,EAAE,oBAAoB,GAC3B,OAAO,CAAC,oBAAoB,CAAC;IAoChC;;;;;OAKG;IACH,OAAO,CAAC,eAAe;IAoCvB;;;;OAIG;YACW,kBAAkB;IAsC1B,aAAa,CACjB,MAAM,EAAE,qBAAqB,GAC5B,OAAO,CAAC,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"google.service.d.ts","sourceRoot":"","sources":["../../../../../src/services/aiGen/providers/google/google.service.ts"],"names":[],"mappings":"AAiBA,OAAO,EAAE,wBAAwB,EAAE,MAAM,8BAA8B,CAAC;AACxE,OAAO,EACL,iBAAiB,EACjB,qBAAqB,EACrB,qBAAqB,EACrB,qBAAqB,EACrB,qBAAqB,EACrB,oBAAoB,EACpB,oBAAoB,EACpB,qBAAqB,EACrB,qBAAqB,EACrB,iBAAiB,EACjB,iBAAiB,EAClB,MAAM,UAAU,CAAC;AAkElB,qBAAa,aAAc,SAAQ,wBAAwB;IAKzD,OAAO,CAAC,EAAE,CAAc;IACxB,OAAO,CAAC,OAAO,CAAgB;IAC/B,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,kBAAkB,CAAK;;IAQ/C;;;;;;;;;OASG;IACG,YAAY,CAChB,MAAM,EAAE,oBAAoB,GAC3B,OAAO,CAAC,oBAAoB,CAAC;IAoChC;;;;;OAKG;IACH,OAAO,CAAC,eAAe;IAoCvB;;;;OAIG;YACW,kBAAkB;IAsC1B,aAAa,CACjB,MAAM,EAAE,qBAAqB,GAC5B,OAAO,CAAC,qBAAqB,CAAC;IA0O3B,gBAAgB,CAAC,EACrB,IAAI,EACJ,cAAc,EACd,cAAyB,GAC1B,EAAE,iBAAiB,GAAG,OAAO,CAAC,iBAAiB,CAAC;IA8J3C,aAAa,CACjB,MAAM,EAAE,qBAAqB,GAC5B,OAAO,CAAC,qBAAqB,CAAC;YAiBnB,cAAc;IAwG5B;;;;;;OAMG;IACH;;;;OAIG;IACG,aAAa,CACjB,MAAM,EAAE,qBAAqB,GAC5B,OAAO,CAAC,qBAAqB,CAAC;IAIjC,aAAa,CAAC,EAAE,QAAQ,EAAE,QAAY,EAAE,UAAmB,EAAE,SAAiB,EAAE,SAAa,EAAE,SAAS,EAAE,WAAW,EAAE,EAAE,iBAAiB,GAAG,MAAM;CA8BpJ"}
|
|
@@ -197,54 +197,20 @@ class GoogleService extends baseAiGenProvider_service_1.BaseAiGenProviderService
|
|
|
197
197
|
const modelConfig = aiModels_1.aiModelConfigs[params.modelKey];
|
|
198
198
|
const modelId = modelConfig.modelId;
|
|
199
199
|
const isVeo3_1 = VEO_3_1_MODELS.has(params.modelKey);
|
|
200
|
-
//
|
|
201
|
-
//
|
|
202
|
-
//
|
|
203
|
-
//
|
|
204
|
-
//
|
|
205
|
-
|
|
206
|
-
const
|
|
207
|
-
|
|
208
|
-
(!!params.inputImageUrl ||
|
|
209
|
-
!!params.lastFrameImageUrl ||
|
|
210
|
-
(isVeo3_1 && refCount > 0));
|
|
211
|
-
// Gemini Veo cross-constraint: extension, first+last-frame interpolation
|
|
212
|
-
// AND reference images ALL require durationSeconds=8 (per the Veo API
|
|
213
|
-
// parameter table: `durationSeconds` "Must be '8' when using extension,
|
|
214
|
-
// reference images or with 1080p and 4k resolutions"). Sending any other
|
|
215
|
-
// duration returns INVALID_ARGUMENT 400 with no hint about which param
|
|
216
|
-
// caused it. Surface a typed UserFacingError so the user/frontend can
|
|
217
|
-
// correct the input instead of burning a provider call + opaque rejection.
|
|
218
|
-
const needsDuration8 = isExtension ||
|
|
219
|
-
!!params.lastFrameImageUrl ||
|
|
220
|
-
(isVeo3_1 && refCount > 0);
|
|
200
|
+
// Gemini Veo cross-constraint: first+last-frame interpolation AND
|
|
201
|
+
// reference images BOTH require durationSeconds=8. Sending any other
|
|
202
|
+
// duration returns INVALID_ARGUMENT 400 "Your use case is currently not
|
|
203
|
+
// supported." with no hint about which param caused it. Surface a
|
|
204
|
+
// typed UserFacingError so the user/frontend can correct the input
|
|
205
|
+
// instead of burning a provider call + opaque rejection.
|
|
206
|
+
const needsDuration8 = !!params.lastFrameImageUrl ||
|
|
207
|
+
(isVeo3_1 && (params.referenceImageUrls?.length ?? 0) > 0);
|
|
221
208
|
if (needsDuration8 && params.duration !== undefined && params.duration !== 8) {
|
|
222
|
-
const constraint =
|
|
223
|
-
? "
|
|
224
|
-
:
|
|
225
|
-
? "first-frame + last-frame interpolation"
|
|
226
|
-
: "reference images";
|
|
227
|
-
const remedy = isExtension
|
|
228
|
-
? "extend a shorter clip"
|
|
229
|
-
: `remove the ${params.lastFrameImageUrl ? "last-frame image" : "reference images"}`;
|
|
209
|
+
const constraint = params.lastFrameImageUrl
|
|
210
|
+
? "first-frame + last-frame interpolation"
|
|
211
|
+
: "reference images";
|
|
230
212
|
throw new errors_1.UserFacingError(`Google Veo requires an 8-second duration when using ${constraint}. ` +
|
|
231
|
-
`Please select 8s or ${
|
|
232
|
-
}
|
|
233
|
-
// Extension output is capped at 720p ("`720p` only for extension" in the
|
|
234
|
-
// same table). A 1080p/4k extend is a deterministic 400, so reject it here
|
|
235
|
-
// rather than paying for the round-trip. NOT coerced to 720p on purpose:
|
|
236
|
-
// resolution drives the credit price (cost.perResolution), so silently
|
|
237
|
-
// downgrading the output would bill the user for a tier we didn't deliver.
|
|
238
|
-
if (isExtension && params.resolution && params.resolution !== "720p") {
|
|
239
|
-
throw new errors_1.UserFacingError(`Google Veo only supports 720p when extending a video (requested ${params.resolution}). ` +
|
|
240
|
-
`Please select 720p to extend this clip.`, errors_1.USER_FACING_ERROR_CODES.CAPABILITY_MISMATCH);
|
|
241
|
-
}
|
|
242
|
-
// `lastFrame` is the END of an interpolation and is only meaningful with a
|
|
243
|
-
// START frame — the API spec states it "Must be used in combination with
|
|
244
|
-
// the `image` parameter." Sending it alone is a deterministic 400.
|
|
245
|
-
if (params.lastFrameImageUrl && !params.inputImageUrl) {
|
|
246
|
-
throw new errors_1.UserFacingError("Google Veo needs a starting image as well as a last-frame image to interpolate between them. " +
|
|
247
|
-
"Please add a first-frame image or remove the last-frame image.", errors_1.USER_FACING_ERROR_CODES.CAPABILITY_MISMATCH);
|
|
213
|
+
`Please select 8s or remove the ${params.lastFrameImageUrl ? "last-frame image" : "reference images"}.`, errors_1.USER_FACING_ERROR_CODES.CAPABILITY_MISMATCH);
|
|
248
214
|
}
|
|
249
215
|
const request = {
|
|
250
216
|
model: modelId,
|
|
@@ -262,25 +228,18 @@ class GoogleService extends baseAiGenProvider_service_1.BaseAiGenProviderService
|
|
|
262
228
|
// carried a seed. Like `generateAudio`, it is a Vertex-AI-only field.
|
|
263
229
|
// Restore this line if/when this client switches to Vertex (vertexai:true).
|
|
264
230
|
// ...(params.seed !== undefined ? { seed: params.seed } : {}),
|
|
265
|
-
// personGeneration on the Gemini API Veo backend is
|
|
266
|
-
//
|
|
267
|
-
//
|
|
268
|
-
//
|
|
269
|
-
//
|
|
270
|
-
//
|
|
271
|
-
//
|
|
272
|
-
//
|
|
273
|
-
//
|
|
274
|
-
//
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
// is that mode's single legal value, so omitting is always safe — it is
|
|
278
|
-
// what the overwhelming majority of our traffic already does).
|
|
279
|
-
...(params.personGeneration === "allow_all" ||
|
|
280
|
-
params.personGeneration === "allow_adult"
|
|
281
|
-
? isImageDriven
|
|
282
|
-
? { personGeneration: "allow_adult" }
|
|
283
|
-
: { personGeneration: "allow_all" }
|
|
231
|
+
// personGeneration on the Gemini API Veo backend: "allow_all" is the
|
|
232
|
+
// ONLY supported value for Veo 3.x (and it is also the default).
|
|
233
|
+
// "allow_adult" and "dont_allow" are Veo-2-only legacy values — sending
|
|
234
|
+
// either returns 400 "<value> for personGeneration is currently not
|
|
235
|
+
// supported", which is NOT matched by classifyGoogleApiError and so
|
|
236
|
+
// leaks to logger.error → Slack while failing the job. Since allow_all
|
|
237
|
+
// is the default, forward the param only when it's explicitly allow_all
|
|
238
|
+
// (a no-op that documents intent); drop any other requested value
|
|
239
|
+
// rather than hard-fail. Restoring allow_adult/dont_allow would require
|
|
240
|
+
// a Veo-2 model or an allowlisted Vertex project.
|
|
241
|
+
...(params.personGeneration === "allow_all"
|
|
242
|
+
? { personGeneration: "allow_all" }
|
|
284
243
|
: {}),
|
|
285
244
|
// negativePrompt support is MODEL-SPECIFIC on the Gemini API: veo-3.1 and
|
|
286
245
|
// veo-3.1-fast accept it, veo-3.1-lite returns 400 "negativePrompt isn't
|
|
@@ -364,36 +323,8 @@ class GoogleService extends baseAiGenProvider_service_1.BaseAiGenProviderService
|
|
|
364
323
|
const keyFailure = (0, googleErrors_1.classifyGoogleKeyHealth)(err);
|
|
365
324
|
// Not a fail-over-able failure (e.g. content rejection, bad params) →
|
|
366
325
|
// don't burn other accounts on it; surface as-is.
|
|
367
|
-
if (!keyFailure)
|
|
368
|
-
// Log WHAT WE SENT before rethrowing. Veo answers an unsupported
|
|
369
|
-
// parameter combination with a generic 400 ("Unsupported video
|
|
370
|
-
// generation request", "Your use case is currently not supported")
|
|
371
|
-
// that names neither the offending field nor its value — so without
|
|
372
|
-
// this line the Slack alert and the job doc are both dead ends, and
|
|
373
|
-
// the combo can only be guessed at (prod 2026-07-26). Shape only: no
|
|
374
|
-
// prompt text, no image bytes, no signed URLs.
|
|
375
|
-
logger_1.logger.warn("Google Veo: request rejected — submitted shape", {
|
|
376
|
-
modelKey: params.modelKey,
|
|
377
|
-
modelId,
|
|
378
|
-
aspectRatio: params.aspectRatio,
|
|
379
|
-
resolution: params.resolution,
|
|
380
|
-
durationSeconds: params.duration,
|
|
381
|
-
mode: isExtension
|
|
382
|
-
? "extension"
|
|
383
|
-
: isImageDriven
|
|
384
|
-
? "image-driven"
|
|
385
|
-
: "text-to-video",
|
|
386
|
-
hasInputImage: !!params.inputImageUrl,
|
|
387
|
-
hasLastFrame: !!params.lastFrameImageUrl,
|
|
388
|
-
referenceImageCount: refCount,
|
|
389
|
-
personGeneration: params.personGeneration ?? null,
|
|
390
|
-
hasNegativePrompt: !!params.negativePrompt,
|
|
391
|
-
enhancePrompt: params.promptOptimizer ?? null,
|
|
392
|
-
promptChars: params.prompt?.length ?? 0,
|
|
393
|
-
error: err instanceof Error ? err.message.slice(0, 500) : String(err),
|
|
394
|
-
});
|
|
326
|
+
if (!keyFailure)
|
|
395
327
|
throw err;
|
|
396
|
-
}
|
|
397
328
|
// Observability: log the RAW provider error once so we can see whether
|
|
398
329
|
// this was an RPM vs RPD limit and on which key (AI Studio's dashboard
|
|
399
330
|
// lags real-time, so this is our authoritative signal — Ammar 2026-06-20).
|
|
@@ -612,11 +543,6 @@ class GoogleService extends baseAiGenProvider_service_1.BaseAiGenProviderService
|
|
|
612
543
|
const isImagen = modelId.startsWith("imagen-");
|
|
613
544
|
const isNanoBanana = modelId.startsWith("gemini-") && modelId.includes("image");
|
|
614
545
|
const allBytes = [];
|
|
615
|
-
// Safety/RAI block reasons harvested from the response when it comes back
|
|
616
|
-
// WITHOUT image bytes. Google does not throw in this case — it returns a
|
|
617
|
-
// 200 with the images filtered out — so the reason is only available here.
|
|
618
|
-
// Mirrors the Veo video path (raiMediaFilteredReasons, above).
|
|
619
|
-
const blockReasons = [];
|
|
620
546
|
if (isImagen) {
|
|
621
547
|
const resp = await this.ai.models.generateImages({
|
|
622
548
|
model: modelId,
|
|
@@ -633,25 +559,13 @@ class GoogleService extends baseAiGenProvider_service_1.BaseAiGenProviderService
|
|
|
633
559
|
});
|
|
634
560
|
for (const gen of resp.generatedImages ?? []) {
|
|
635
561
|
const b64 = gen.image?.imageBytes;
|
|
636
|
-
if (!b64)
|
|
637
|
-
// An entry with no bytes carries the RAI filter reason instead.
|
|
638
|
-
const reason = gen.raiFilteredReason;
|
|
639
|
-
if (reason)
|
|
640
|
-
blockReasons.push(reason);
|
|
562
|
+
if (!b64)
|
|
641
563
|
continue;
|
|
642
|
-
}
|
|
643
564
|
allBytes.push({
|
|
644
565
|
bytes: Buffer.from(b64, "base64"),
|
|
645
566
|
mime: gen.image?.mimeType ?? "image/png",
|
|
646
567
|
});
|
|
647
568
|
}
|
|
648
|
-
// Prompt-level rejection: Imagen filters the whole request before
|
|
649
|
-
// producing any generatedImages entry, reporting it here instead.
|
|
650
|
-
const promptReason = resp
|
|
651
|
-
.positivePromptSafetyAttributes;
|
|
652
|
-
if (!allBytes.length && promptReason && !blockReasons.length) {
|
|
653
|
-
blockReasons.push("Prompt blocked by Google's safety filters");
|
|
654
|
-
}
|
|
655
569
|
}
|
|
656
570
|
else if (isNanoBanana) {
|
|
657
571
|
// Nano Banana (Gemini image-gen via generateContent) has no structured
|
|
@@ -693,41 +607,11 @@ class GoogleService extends baseAiGenProvider_service_1.BaseAiGenProviderService
|
|
|
693
607
|
}
|
|
694
608
|
}
|
|
695
609
|
}
|
|
696
|
-
// Gemini reports a safety stop two ways, neither of which throws:
|
|
697
|
-
// promptFeedback.blockReason (whole prompt rejected → candidates empty)
|
|
698
|
-
// and candidate.finishReason (generation halted: IMAGE_SAFETY,
|
|
699
|
-
// PROHIBITED_CONTENT, RECITATION, SPII).
|
|
700
|
-
const promptBlock = resp
|
|
701
|
-
.promptFeedback?.blockReason;
|
|
702
|
-
if (promptBlock)
|
|
703
|
-
blockReasons.push(promptBlock);
|
|
704
|
-
for (const cand of candidates) {
|
|
705
|
-
const finish = cand.finishReason;
|
|
706
|
-
if (finish && finish !== "STOP" && finish !== "MAX_TOKENS") {
|
|
707
|
-
blockReasons.push(finish);
|
|
708
|
-
}
|
|
709
|
-
}
|
|
710
610
|
}
|
|
711
611
|
else {
|
|
712
612
|
throw new Error(`Unsupported Google image modelId: ${modelId}`);
|
|
713
613
|
}
|
|
714
614
|
if (!allBytes.length) {
|
|
715
|
-
// A 200 with zero images is almost always a safety/RAI block, not a bug.
|
|
716
|
-
// Previously this threw a bare Error whose message wasn't JSON, so
|
|
717
|
-
// classifyGoogleApiError (which JSON.parses the message) returned null and
|
|
718
|
-
// the image queue logged it at `error` — paging Slack for what is really a
|
|
719
|
-
// user-content outcome. Emit a typed CONTENT_POLICY_VIOLATION when we have
|
|
720
|
-
// a block reason so it's logged at warn, and keep the reason for triage.
|
|
721
|
-
if (blockReasons.length) {
|
|
722
|
-
logger_1.logger.warn("Google image generation blocked by safety filters", {
|
|
723
|
-
modelKey: params.modelKey,
|
|
724
|
-
modelId,
|
|
725
|
-
reasons: blockReasons,
|
|
726
|
-
});
|
|
727
|
-
throw new errors_1.UserFacingError("Your prompt was flagged by Google's safety filters. Please rephrase and try again.", errors_1.USER_FACING_ERROR_CODES.CONTENT_POLICY_VIOLATION);
|
|
728
|
-
}
|
|
729
|
-
// No block reason at all — genuinely unexpected; keep it loud so a real
|
|
730
|
-
// platform/SDK regression isn't muted by the branch above.
|
|
731
615
|
throw new Error("Google image generation returned no image bytes");
|
|
732
616
|
}
|
|
733
617
|
const bucket = (0, firebase_1.getBucket)();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"googleErrors.d.ts","sourceRoot":"","sources":["../../../../../src/services/aiGen/providers/google/googleErrors.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,eAAe,EAGhB,MAAM,0BAA0B,CAAC;AAElC;;;;;;;;;GASG;AACH,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,GAAG,GAAG,eAAe,GAAG,IAAI,
|
|
1
|
+
{"version":3,"file":"googleErrors.d.ts","sourceRoot":"","sources":["../../../../../src/services/aiGen/providers/google/googleErrors.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,eAAe,EAGhB,MAAM,0BAA0B,CAAC;AAElC;;;;;;;;;GASG;AACH,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,GAAG,GAAG,eAAe,GAAG,IAAI,CAoJvE;AAED;;;+EAG+E;AAC/E,MAAM,WAAW,sBAAsB;IACrC,4DAA4D;IAC5D,IAAI,EAAE,SAAS,GAAG,MAAM,GAAG,OAAO,CAAC;IACnC,+EAA+E;IAC/E,MAAM,EAAE,MAAM,CAAC;IACf,mFAAmF;IACnF,UAAU,EAAE,MAAM,CAAC;CACpB;AAcD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,GAAG,GAAG,sBAAsB,GAAG,IAAI,CAuE/E"}
|
|
@@ -49,28 +49,18 @@ function classifyGoogleApiError(err) {
|
|
|
49
49
|
if (httpCode === 14 || /high demand/i.test(msg)) {
|
|
50
50
|
return new errors_1.UserFacingError((0, errors_1.friendlyMessageForCode)(errors_1.USER_FACING_ERROR_CODES.VIDEO_PROVIDER_HIGH_DEMAND), errors_1.USER_FACING_ERROR_CODES.VIDEO_PROVIDER_HIGH_DEMAND);
|
|
51
51
|
}
|
|
52
|
-
// INVALID_ARGUMENT 400 —
|
|
53
|
-
//
|
|
54
|
-
//
|
|
55
|
-
// - "Unsupported video generation request. Please check the documentation
|
|
56
|
-
// for supported usage: …/gemini-api/docs/video" (seen in prod on
|
|
57
|
-
// veo-3.1-fast, 2026-07-26 — recurring across many users)
|
|
52
|
+
// INVALID_ARGUMENT 400 — narrow match: only the specific "use case is
|
|
53
|
+
// currently not supported" string, which Veo returns when our request
|
|
54
|
+
// structure doesn't match the chosen model variant's capabilities.
|
|
58
55
|
// The pre-call guards above (duration=8 for lastFrame/refs) should
|
|
59
56
|
// prevent the known cases; if we still hit this it's a NEW combo we
|
|
60
57
|
// haven't profiled — surface as CAPABILITY_MISMATCH so the user gets a
|
|
61
58
|
// useful message, AND keep the raw provider text in the error so the
|
|
62
|
-
// next entry in PROD_FIX_LOG can identify which combo broke.
|
|
63
|
-
//
|
|
64
|
-
//
|
|
65
|
-
// UserFacingError, so the scene loop treats it as a candidate for the
|
|
66
|
-
// transient-retry path and logs it at `error` (which is what pages Slack —
|
|
67
|
-
// see errorPaging.ts). A deterministic 400 can never succeed on retry, so
|
|
68
|
-
// classifying it here both stops the wasted re-submits and moves it to
|
|
69
|
-
// `warn`, since CAPABILITY_MISMATCH is in NON_PAGING_ERROR_CODES.
|
|
70
|
-
// Generic 400s (other INVALID_ARGUMENT variants) still surface as `error`
|
|
71
|
-
// so a real platform bug isn't muted.
|
|
59
|
+
// next entry in PROD_FIX_LOG can identify which combo broke. Generic
|
|
60
|
+
// 400s (other INVALID_ARGUMENT variants) still surface as `error` so
|
|
61
|
+
// a real platform bug isn't muted.
|
|
72
62
|
if ((status === "INVALID_ARGUMENT" || httpCode === 400) &&
|
|
73
|
-
/use case is currently not supported
|
|
63
|
+
/use case is currently not supported/i.test(msg)) {
|
|
74
64
|
return new errors_1.UserFacingError(msg, errors_1.USER_FACING_ERROR_CODES.CAPABILITY_MISMATCH);
|
|
75
65
|
}
|
|
76
66
|
// Imagen + Nano-Banana surface Responsible-AI filter rejections as
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"kling.service.d.ts","sourceRoot":"","sources":["../../../../../src/services/aiGen/providers/kling/kling.service.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"kling.service.d.ts","sourceRoot":"","sources":["../../../../../src/services/aiGen/providers/kling/kling.service.ts"],"names":[],"mappings":"AAcA,OAAO,EAAE,wBAAwB,EAAE,MAAM,8BAA8B,CAAC;AACxE,OAAO,EACL,iBAAiB,EACjB,qBAAqB,EACrB,qBAAqB,EACrB,iBAAiB,EACjB,iBAAiB,EAClB,MAAM,UAAU,CAAC;AAyBlB;;;;;;;;;;;;;;GAcG;AACH,MAAM,MAAM,aAAa,GAAG,KAAK,GAAG,QAAQ,CAAC;AAE7C;;;;;GAKG;AACH,qBAAa,YAAa,SAAQ,wBAAwB;IACxD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAuC;IAC/D,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAW;IAC3C,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAU;IASxC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAkD;IAC9E,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAmD;;YAWlE,OAAO;IAiCf,aAAa,CACjB,MAAM,EAAE,qBAAqB,GAC5B,OAAO,CAAC,qBAAqB,CAAC;IA+EjC;;;;;;;;OAQG;YACW,kBAAkB;IAgChC;;;;OAIG;YACW,WAAW;YA6BX,iBAAiB;YA6BjB,qBAAqB;YAgCrB,eAAe;YAkDf,mBAAmB;YAgCnB,uBAAuB;IAsI/B,gBAAgB,CAAC,MAAM,EAAE,iBAAiB,GAAG,OAAO,CAAC,iBAAiB,CAAC;IAiB7E;;;;;;;OAOG;IACH,OAAO,CAAC,gBAAgB;IAiBxB,cAAc,IAAI,MAAM;IAexB,aAAa,CAAC,MAAM,EAAE,iBAAiB,GAAG,MAAM;CAGjD"}
|
|
@@ -64,6 +64,28 @@ class KlingService extends baseAiGenProvider_service_1.BaseAiGenProviderService
|
|
|
64
64
|
}
|
|
65
65
|
async generateVideo(params) {
|
|
66
66
|
(0, helpers_1.validateParams)(params);
|
|
67
|
+
// Extend is dispatched FIRST, and never silently downgrades.
|
|
68
|
+
//
|
|
69
|
+
// This check used to sit below the special-endpoint branches (avatar,
|
|
70
|
+
// lip-sync, motion-control, omni-video, turbo), so an extend request on any
|
|
71
|
+
// of those models fell into that model's own generate path and produced a
|
|
72
|
+
// BRAND-NEW clip while ignoring sourceTaskId — a silent wrong result, billed
|
|
73
|
+
// in full. kling-v3.0-turbo was the live example: it sits on the API 2.0
|
|
74
|
+
// /tasks surface, which has no video-extend equivalent at all.
|
|
75
|
+
//
|
|
76
|
+
// None of those endpoints accept a generic video-extend task, so the correct
|
|
77
|
+
// behaviour is to reject, not to reinterpret. The request schema already
|
|
78
|
+
// blocks this at the HTTP door; this is the provider-level backstop for the
|
|
79
|
+
// agent and any other in-process caller.
|
|
80
|
+
if (params.endpoint === "extend") {
|
|
81
|
+
if (!(0, aiModels_1.modelSupportsExtend)(params.modelKey)) {
|
|
82
|
+
throw new Error(`${params.modelKey} has no Kling video-extend endpoint — ` +
|
|
83
|
+
"avatar, lip-sync, motion-control, omni-video and v3.0-turbo tasks " +
|
|
84
|
+
"are not accepted by /v1/videos/video-extend. " +
|
|
85
|
+
`Extend-capable: ${[...aiModels_1.EXTEND_CAPABLE_MODELS].join(", ")}.`);
|
|
86
|
+
}
|
|
87
|
+
return this.extendVideo(params);
|
|
88
|
+
}
|
|
67
89
|
if (params.modelKey === "kling-avatar") {
|
|
68
90
|
return this.generateAvatarVideo(params);
|
|
69
91
|
}
|
|
@@ -80,9 +102,6 @@ class KlingService extends baseAiGenProvider_service_1.BaseAiGenProviderService
|
|
|
80
102
|
if (params.modelKey === "kling-v3.0-turbo") {
|
|
81
103
|
return this.generateTurboVideo(params);
|
|
82
104
|
}
|
|
83
|
-
if (params.endpoint === "extend") {
|
|
84
|
-
return this.extendVideo(params);
|
|
85
|
-
}
|
|
86
105
|
const modelId = aiModels_1.aiModelConfigs[params.modelKey]?.modelId;
|
|
87
106
|
if (!modelId)
|
|
88
107
|
throw new Error(`Unknown modelKey: ${params.modelKey}`);
|
|
@@ -6,6 +6,13 @@ import type { KlingAuthMode } from "./kling.service";
|
|
|
6
6
|
* service's bound requestWithTimeoutRetry) plus the submit timeout, so they
|
|
7
7
|
* reuse the JWT auth + IPv4-pinned agents without holding a class reference.
|
|
8
8
|
*/
|
|
9
|
+
/**
|
|
10
|
+
* Kling reports clip length as seconds-in-a-string ("5", "10.1"). Parse it
|
|
11
|
+
* defensively: a malformed or absent value must yield `undefined` (billing then
|
|
12
|
+
* falls back to the requested duration) rather than NaN, which would poison
|
|
13
|
+
* every downstream cost calculation. See VideoStatusResult.durationSec.
|
|
14
|
+
*/
|
|
15
|
+
export declare function parseKlingDurationSec(raw: unknown): number | undefined;
|
|
9
16
|
export interface KlingStatusDeps {
|
|
10
17
|
/** Bound KlingService.requestWithTimeoutRetry. */
|
|
11
18
|
request: <T>(endpoint: string, method: "POST" | "GET", body?: any, maxAttempts?: number, timeout?: number,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"klingStatus.d.ts","sourceRoot":"","sources":["../../../../../src/services/aiGen/providers/kling/klingStatus.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAChE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAGrD;;;;;GAKG;AACH,MAAM,WAAW,eAAe;IAC9B,kDAAkD;IAClD,OAAO,EAAE,CAAC,CAAC,EACT,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,GAAG,KAAK,EACtB,IAAI,CAAC,EAAE,GAAG,EACV,WAAW,CAAC,EAAE,MAAM,EACpB,OAAO,CAAC,EAAE,MAAM;IAChB,4EAA4E;IAC5E,QAAQ,CAAC,EAAE,aAAa,KACrB,OAAO,CAAC,CAAC,CAAC,CAAC;IAChB,oFAAoF;IACpF,eAAe,EAAE,MAAM,CAAC;CACzB;AA4ED;;;;;;;GAOG;AACH,wBAAsB,qBAAqB,CACzC,IAAI,EAAE,eAAe,EACrB,EAAE,IAAI,EAAE,cAAc,EAAE,cAAyB,EAAE,EAAE,iBAAiB,GACrE,OAAO,CAAC,iBAAiB,CAAC,
|
|
1
|
+
{"version":3,"file":"klingStatus.d.ts","sourceRoot":"","sources":["../../../../../src/services/aiGen/providers/kling/klingStatus.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAChE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAGrD;;;;;GAKG;AACH;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAItE;AAED,MAAM,WAAW,eAAe;IAC9B,kDAAkD;IAClD,OAAO,EAAE,CAAC,CAAC,EACT,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,GAAG,KAAK,EACtB,IAAI,CAAC,EAAE,GAAG,EACV,WAAW,CAAC,EAAE,MAAM,EACpB,OAAO,CAAC,EAAE,MAAM;IAChB,4EAA4E;IAC5E,QAAQ,CAAC,EAAE,aAAa,KACrB,OAAO,CAAC,CAAC,CAAC,CAAC;IAChB,oFAAoF;IACpF,eAAe,EAAE,MAAM,CAAC;CACzB;AA4ED;;;;;;;GAOG;AACH,wBAAsB,qBAAqB,CACzC,IAAI,EAAE,eAAe,EACrB,EAAE,IAAI,EAAE,cAAc,EAAE,cAAyB,EAAE,EAAE,iBAAiB,GACrE,OAAO,CAAC,iBAAiB,CAAC,CA4D5B;AAED,wBAAsB,uBAAuB,CAC3C,IAAI,EAAE,eAAe,EACrB,EAAE,IAAI,EAAE,cAAc,EAAE,cAAyB,EAAE,EAAE,iBAAiB,GACrE,OAAO,CAAC,iBAAiB,CAAC,CAgF5B;AAED,wBAAsB,sBAAsB,CAC1C,IAAI,EAAE,eAAe,EACrB,EAAE,IAAI,EAAE,cAAc,EAAE,cAAyB,EAAE,EAAE,iBAAiB,GACrE,OAAO,CAAC,iBAAiB,CAAC,CA2G5B"}
|
|
@@ -3,6 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.parseKlingDurationSec = parseKlingDurationSec;
|
|
6
7
|
exports.checkTurboVideoStatus = checkTurboVideoStatus;
|
|
7
8
|
exports.checkRegularVideoStatus = checkRegularVideoStatus;
|
|
8
9
|
exports.checkAvatarVideoStatus = checkAvatarVideoStatus;
|
|
@@ -12,6 +13,24 @@ const https_1 = __importDefault(require("https"));
|
|
|
12
13
|
const types_1 = require("../../../../globals/types");
|
|
13
14
|
const firebase_1 = require("../../../../libs/firebase");
|
|
14
15
|
const logger_1 = require("../../../../utils/logger");
|
|
16
|
+
/**
|
|
17
|
+
* Kling status polling + result stashing. Extracted from KlingService so the
|
|
18
|
+
* class stays a thin transport/dispatch layer. These take a `request` dep (the
|
|
19
|
+
* service's bound requestWithTimeoutRetry) plus the submit timeout, so they
|
|
20
|
+
* reuse the JWT auth + IPv4-pinned agents without holding a class reference.
|
|
21
|
+
*/
|
|
22
|
+
/**
|
|
23
|
+
* Kling reports clip length as seconds-in-a-string ("5", "10.1"). Parse it
|
|
24
|
+
* defensively: a malformed or absent value must yield `undefined` (billing then
|
|
25
|
+
* falls back to the requested duration) rather than NaN, which would poison
|
|
26
|
+
* every downstream cost calculation. See VideoStatusResult.durationSec.
|
|
27
|
+
*/
|
|
28
|
+
function parseKlingDurationSec(raw) {
|
|
29
|
+
if (typeof raw !== "string" && typeof raw !== "number")
|
|
30
|
+
return undefined;
|
|
31
|
+
const parsed = typeof raw === "number" ? raw : Number.parseFloat(raw);
|
|
32
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : undefined;
|
|
33
|
+
}
|
|
15
34
|
/**
|
|
16
35
|
* Download a finished Kling video and re-host it in GCS, returning a signed
|
|
17
36
|
* URL. Bounded retry + IPv4 pinning: Kling's CDN hostnames frequently resolve
|
|
@@ -102,7 +121,12 @@ async function checkTurboVideoStatus(deps, { task, outputFilename, outputFilePat
|
|
|
102
121
|
};
|
|
103
122
|
}
|
|
104
123
|
if (taskData.status === "succeeded") {
|
|
105
|
-
const
|
|
124
|
+
const videoOutput = taskData.outputs?.find((o) => o.type === "video");
|
|
125
|
+
const videoUrl = videoOutput?.url;
|
|
126
|
+
// Turbo is priced `perSecond * duration` with NO duration table, and accepts
|
|
127
|
+
// any length 3–15s, so a requested-vs-delivered mismatch bills wrong at
|
|
128
|
+
// every second of drift rather than being absorbed by a {5,10} table.
|
|
129
|
+
const durationSec = parseKlingDurationSec(videoOutput?.duration);
|
|
106
130
|
if (!videoUrl) {
|
|
107
131
|
return {
|
|
108
132
|
status: types_1.EVideoSceneStatus.FAILED,
|
|
@@ -110,7 +134,7 @@ async function checkTurboVideoStatus(deps, { task, outputFilename, outputFilePat
|
|
|
110
134
|
};
|
|
111
135
|
}
|
|
112
136
|
const signedUrl = await stashVideoToGcs(videoUrl, outputFilePath, outputFilename, deps.generateTimeout, { task, surface: "turbo" });
|
|
113
|
-
return { videoUrl: signedUrl, status: types_1.EVideoSceneStatus.COMPLETED };
|
|
137
|
+
return { videoUrl: signedUrl, status: types_1.EVideoSceneStatus.COMPLETED, durationSec };
|
|
114
138
|
}
|
|
115
139
|
if (taskData.status === "failed") {
|
|
116
140
|
return {
|
|
@@ -154,7 +178,13 @@ async function checkRegularVideoStatus(deps, { task, outputFilename, outputFileP
|
|
|
154
178
|
}
|
|
155
179
|
logger_1.logger.info("Kling checkVideoStatus polled", { task, durationMs: Date.now() - startTime, status: result.data.task_status });
|
|
156
180
|
if (result.data.task_status === "succeed") {
|
|
157
|
-
const
|
|
181
|
+
const video = result.data.task_result?.videos?.[0];
|
|
182
|
+
const videoUrl = video?.url;
|
|
183
|
+
// Kling reports how long the clip ACTUALLY is; this was previously read for
|
|
184
|
+
// `.url` only and the length discarded. Carrying it out lets billing
|
|
185
|
+
// reconcile on the delivered length instead of the requested one — the fix
|
|
186
|
+
// for extend overcharging (see VideoStatusResult.durationSec).
|
|
187
|
+
const durationSec = parseKlingDurationSec(video?.duration);
|
|
158
188
|
if (!videoUrl) {
|
|
159
189
|
return {
|
|
160
190
|
status: types_1.EVideoSceneStatus.FAILED,
|
|
@@ -177,6 +207,7 @@ async function checkRegularVideoStatus(deps, { task, outputFilename, outputFileP
|
|
|
177
207
|
return {
|
|
178
208
|
videoUrl: signedUrl,
|
|
179
209
|
status: types_1.EVideoSceneStatus.COMPLETED,
|
|
210
|
+
durationSec,
|
|
180
211
|
};
|
|
181
212
|
}
|
|
182
213
|
if (result.data.task_status === "failed") {
|
|
@@ -198,7 +229,12 @@ async function checkAvatarVideoStatus(deps, { task, outputFilename, outputFilePa
|
|
|
198
229
|
};
|
|
199
230
|
}
|
|
200
231
|
if (result.data.task_status === "succeed") {
|
|
201
|
-
const
|
|
232
|
+
const video = result.data.task_result?.videos?.[0];
|
|
233
|
+
const videoUrl = video?.url;
|
|
234
|
+
// Avatar/lip-sync length is driven by the driving AUDIO, not by a requested
|
|
235
|
+
// duration — and klingCredits bills it in 5s increments. Reporting the real
|
|
236
|
+
// length is what makes that calculation correct.
|
|
237
|
+
const durationSec = parseKlingDurationSec(video?.duration);
|
|
202
238
|
if (!videoUrl) {
|
|
203
239
|
return {
|
|
204
240
|
status: types_1.EVideoSceneStatus.FAILED,
|
|
@@ -246,6 +282,7 @@ async function checkAvatarVideoStatus(deps, { task, outputFilename, outputFilePa
|
|
|
246
282
|
return {
|
|
247
283
|
videoUrl: signedUrl,
|
|
248
284
|
status: types_1.EVideoSceneStatus.COMPLETED,
|
|
285
|
+
durationSec,
|
|
249
286
|
};
|
|
250
287
|
}
|
|
251
288
|
catch (err) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pixverse.service.d.ts","sourceRoot":"","sources":["../../../../../src/services/aiGen/providers/pixverse/pixverse.service.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,wBAAwB,EAAE,MAAM,8BAA8B,CAAC;AACxE,OAAO,EACL,iBAAiB,EACjB,qBAAqB,EACrB,qBAAqB,EACrB,iBAAiB,EACjB,iBAAiB,EAClB,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"pixverse.service.d.ts","sourceRoot":"","sources":["../../../../../src/services/aiGen/providers/pixverse/pixverse.service.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,wBAAwB,EAAE,MAAM,8BAA8B,CAAC;AACxE,OAAO,EACL,iBAAiB,EACjB,qBAAqB,EACrB,qBAAqB,EACrB,iBAAiB,EACjB,iBAAiB,EAClB,MAAM,UAAU,CAAC;AA+FlB,qBAAa,eAAgB,SAAQ,wBAAwB;IAC3D,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAkD;IAK1E;;;;;;OAMG;YACW,WAAW;IA6CnB,aAAa,CACjB,MAAM,EAAE,qBAAqB,GAC5B,OAAO,CAAC,qBAAqB,CAAC;IAyL3B,gBAAgB,CAAC,EACrB,IAAI,EACJ,cAAc,EACd,cAAyB,GAC1B,EAAE,iBAAiB,GAAG,OAAO,CAAC,iBAAiB,CAAC;IA+GjD,aAAa,CAAC,MAAM,EAAE,iBAAiB,GAAG,MAAM;CAkDjD"}
|