vidspotai-shared 1.0.94 → 1.0.95

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.
@@ -676,12 +676,12 @@ export declare const VideoPlanSchema: z.ZodObject<{
676
676
  estimatedDurationMs: z.ZodOptional<z.ZodNumber>;
677
677
  sceneIndices: z.ZodDefault<z.ZodArray<z.ZodNumber>>;
678
678
  }, z.core.$strip>>>;
679
- script: z.ZodOptional<z.ZodObject<{
679
+ script: z.ZodOptional<z.ZodPipe<z.ZodTransform<unknown, unknown>, z.ZodObject<{
680
680
  fullText: z.ZodString;
681
681
  wordCount: z.ZodOptional<z.ZodNumber>;
682
682
  estimatedDurationMs: z.ZodOptional<z.ZodNumber>;
683
683
  wpm: z.ZodOptional<z.ZodNumber>;
684
- }, z.core.$strip>>;
684
+ }, z.core.$strip>>>;
685
685
  music: z.ZodOptional<z.ZodObject<{
686
686
  strategy: z.ZodEnum<{
687
687
  none: "none";
@@ -1 +1 @@
1
- {"version":3,"file":"videoPlan.schema.d.ts","sourceRoot":"","sources":["../../src/schemas/videoPlan.schema.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"videoPlan.schema.d.ts","sourceRoot":"","sources":["../../src/schemas/videoPlan.schema.ts"],"names":[],"mappings":"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"}
@@ -489,8 +489,18 @@ exports.VideoPlanSchema = zod_1.z.object({
489
489
  * Stream L (L-1) — full narration script for L1+ videos. Generated before
490
490
  * scenes in script-first authoring (L-3); scenes' voiceoverLines are slices
491
491
  * of `script.fullText`. Absent on scene-first (S1/S2/M1) plans.
492
+ *
493
+ * Coercion: the planner LLM repeatedly emits `script` as a bare narration
494
+ * string instead of `{ fullText }` (observed in prod 2026-06-30 — failed all
495
+ * 3 retries with "expected object, received string", killing the whole
496
+ * project). A string is unambiguous (it IS the full text), so we normalize it
497
+ * to `{ fullText }` at the schema boundary rather than burning retries on a
498
+ * shape the model won't reliably fix. `wordCount`/`estimatedDurationMs` stay
499
+ * optional and get backfilled downstream.
492
500
  */
493
- script: exports.ScriptSchema.optional(),
501
+ script: zod_1.z
502
+ .preprocess((v) => (typeof v === "string" ? { fullText: v } : v), exports.ScriptSchema)
503
+ .optional(),
494
504
  /** Music recommendation for the whole video. */
495
505
  music: zod_1.z
496
506
  .object({
@@ -332,12 +332,12 @@ declare const OutputSchema: z.ZodObject<{
332
332
  estimatedDurationMs: z.ZodOptional<z.ZodNumber>;
333
333
  sceneIndices: z.ZodDefault<z.ZodArray<z.ZodNumber>>;
334
334
  }, z.core.$strip>>>;
335
- script: z.ZodOptional<z.ZodObject<{
335
+ script: z.ZodOptional<z.ZodPipe<z.ZodTransform<unknown, unknown>, z.ZodObject<{
336
336
  fullText: z.ZodString;
337
337
  wordCount: z.ZodOptional<z.ZodNumber>;
338
338
  estimatedDurationMs: z.ZodOptional<z.ZodNumber>;
339
339
  wpm: z.ZodOptional<z.ZodNumber>;
340
- }, z.core.$strip>>;
340
+ }, z.core.$strip>>>;
341
341
  music: z.ZodOptional<z.ZodObject<{
342
342
  strategy: z.ZodEnum<{
343
343
  none: "none";
@@ -1 +1 @@
1
- {"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../../src/services/aiGen/helpers.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAC;AAE1D;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAGhE;AAED;;;;GAIG;AACH,wBAAgB,4BAA4B,IAAI,MAAM,EAAE,CAEvD;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAG3E;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,qBAAqB,GAAG,OAAO,CAqJrE"}
1
+ {"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../../src/services/aiGen/helpers.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAC;AAE1D;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAGhE;AAED;;;;GAIG;AACH,wBAAgB,4BAA4B,IAAI,MAAM,EAAE,CAEvD;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAG3E;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,qBAAqB,GAAG,OAAO,CAiKrE"}
@@ -5,6 +5,7 @@ exports.getModelsSupportingLastFrame = getModelsSupportingLastFrame;
5
5
  exports.modelDeclaresField = modelDeclaresField;
6
6
  exports.validateParams = validateParams;
7
7
  const aiModels_1 = require("../../globals/aiModels");
8
+ const errors_1 = require("../../utils/errors");
8
9
  /**
9
10
  * Whether a model supports first-last-frame (FLF) interpolation, i.e. accepts a
10
11
  * `lastFrameImageUrl`. The source of truth is the model config: a model declares
@@ -125,9 +126,18 @@ function validateParams(params) {
125
126
  }
126
127
  }
127
128
  }
128
- // 4. Fail fast if validation fails
129
+ // 4. Fail fast if validation fails.
130
+ // These are DETERMINISTIC input/capability mismatches (a required input the
131
+ // model needs is absent, an unsupported value/combo, etc.) — an expected
132
+ // user-input outcome, NOT a server fault. Throw a typed UserFacingError with
133
+ // CAPABILITY_MISMATCH so it (a) short-circuits the scene retry loop, (b) stays
134
+ // OFF the pager (errorPaging non-paging set) instead of alerting on-call with
135
+ // a raw "INTERNAL Validation failed" — e.g. a kling-motion-control job
136
+ // submitted without its required inputVideoUrl (2026-07-09). The job surfaces
137
+ // the friendly CAPABILITY_MISMATCH message; the technical detail stays on the
138
+ // scene for debugging.
129
139
  if (errors.length) {
130
- throw new Error(`INTERNAL Validation failed: ${errors.join("; ")}`);
140
+ throw new errors_1.UserFacingError(`Validation failed: ${errors.join("; ")}`, errors_1.USER_FACING_ERROR_CODES.CAPABILITY_MISMATCH);
131
141
  }
132
142
  return true;
133
143
  }
@@ -1 +1 @@
1
- {"version":3,"file":"kling.service.d.ts","sourceRoot":"","sources":["../../../../../src/services/aiGen/providers/kling/kling.service.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,wBAAwB,EAAE,MAAM,8BAA8B,CAAC;AACxE,OAAO,EACL,iBAAiB,EACjB,qBAAqB,EACrB,qBAAqB,EACrB,iBAAiB,EACjB,iBAAiB,EAClB,MAAM,UAAU,CAAC;AAsBlB;;;;;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;IAgCf,aAAa,CACjB,MAAM,EAAE,qBAAqB,GAC5B,OAAO,CAAC,qBAAqB,CAAC;IAoDjC;;;;OAIG;YACW,WAAW;YA6BX,iBAAiB;YA6BjB,qBAAqB;YAgCrB,eAAe;YAkDf,mBAAmB;YAgCnB,uBAAuB;IAwF/B,gBAAgB,CAAC,MAAM,EAAE,iBAAiB,GAAG,OAAO,CAAC,iBAAiB,CAAC;IAW7E,cAAc,IAAI,MAAM;IAexB,aAAa,CAAC,MAAM,EAAE,iBAAiB,GAAG,MAAM;CAGjD"}
1
+ {"version":3,"file":"kling.service.d.ts","sourceRoot":"","sources":["../../../../../src/services/aiGen/providers/kling/kling.service.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,wBAAwB,EAAE,MAAM,8BAA8B,CAAC;AACxE,OAAO,EACL,iBAAiB,EACjB,qBAAqB,EACrB,qBAAqB,EACrB,iBAAiB,EACjB,iBAAiB,EAClB,MAAM,UAAU,CAAC;AAsBlB;;;;;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;IAgCf,aAAa,CACjB,MAAM,EAAE,qBAAqB,GAC5B,OAAO,CAAC,qBAAqB,CAAC;IAoDjC;;;;OAIG;YACW,WAAW;YA6BX,iBAAiB;YA6BjB,qBAAqB;YAgCrB,eAAe;YAkDf,mBAAmB;YAgCnB,uBAAuB;IAqI/B,gBAAgB,CAAC,MAAM,EAAE,iBAAiB,GAAG,OAAO,CAAC,iBAAiB,CAAC;IAW7E,cAAc,IAAI,MAAM;IAexB,aAAa,CAAC,MAAM,EAAE,iBAAiB,GAAG,MAAM;CAGjD"}
@@ -219,25 +219,64 @@ class KlingService extends baseAiGenProvider_service_1.BaseAiGenProviderService
219
219
  // True rate-limit (HTTP 429 without the balance code) — worth retrying with backoff.
220
220
  const isRateLimit = httpStatus === 429 && !isInsufficientBalance;
221
221
  const isRetryable = (isTimeout || isNetworkError || isRateLimit) && !isInsufficientBalance;
222
- // Insufficient balance is a critical ops issue surface immediately to Slack.
222
+ // Insufficient balance is a billing/account fault an operator must act on
223
+ // (top up Kling). Classify as ACCOUNT_QUOTA_EXCEEDED so the shared paging
224
+ // policy (errorPaging.ts) pages ONCE at the scene boundary — consistent
225
+ // with the Veo billing-depletion path. The top-up detail is logged at
226
+ // warn (Loki only, no duplicate Slack page) so it stays searchable
227
+ // without the previous double-page (raw throw here + generic scene page).
223
228
  if (isInsufficientBalance) {
224
- logger_1.logger.error("Kling account balance too low — top up required to continue video generation", {
229
+ logger_1.logger.warn("Kling account balance too low — top up required to continue video generation", {
225
230
  endpoint,
226
231
  klingCode,
227
232
  klingMessage: err.response?.data?.message,
228
233
  httpStatus,
229
234
  });
230
- throw err;
235
+ throw new errors_1.UserFacingError("This video provider is temporarily unavailable. Please try a different model or retry shortly.", errors_1.USER_FACING_ERROR_CODES.ACCOUNT_QUOTA_EXCEEDED);
231
236
  }
232
- // Audio rejected by Kling pick the user message based on which 1201 variant it is.
237
+ // Kling code 1201 is a broad "input rejected" family a user-fixable
238
+ // input constraint, not a system fault. It fires for AUDIO (avatar /
239
+ // lip-sync `sound_file`) AND for IMAGE inputs (e.g. an oversized
240
+ // end-frame `image_tail` on a plain image2video — see the kling-v3.0
241
+ // end-frame case, 2026-07-09). So only blame audio when audio was
242
+ // actually part of the request; otherwise the message misleads the
243
+ // user (and whoever debugs the log) into hunting a non-existent audio
244
+ // clip. CAPABILITY_MISMATCH keeps it off the pager (errorPaging set).
233
245
  if (isAudioError) {
234
246
  const klingMsg = (err.response?.data?.message ?? "").toLowerCase();
235
- const userMessage = klingMsg.includes("size")
236
- ? "Your audio file is too large for this avatar generation. Please use a shorter audio clip and try again."
237
- : "Your audio format is not supported. Please upload an MP3 or WAV file and try again.";
238
- throw new errors_1.UserFacingError(userMessage);
247
+ const isSize = klingMsg.includes("size");
248
+ const hasAudioInput = endpoint.includes("avatar") ||
249
+ endpoint.includes("lip") ||
250
+ !!(body && typeof body === "object" && body.sound_file);
251
+ let userMessage;
252
+ if (hasAudioInput) {
253
+ userMessage = isSize
254
+ ? "Your audio clip is too large for this video. Please use a shorter audio clip and try again."
255
+ : "Your audio format is not supported. Please upload an MP3 or WAV file and try again.";
256
+ }
257
+ else {
258
+ userMessage = isSize
259
+ ? "One of your input images is too large for this model. Please use a smaller image (or remove the end-frame) and try again."
260
+ : "One of your inputs was rejected by this model. Please adjust your image or settings and try again.";
261
+ }
262
+ throw new errors_1.UserFacingError(userMessage, errors_1.USER_FACING_ERROR_CODES.CAPABILITY_MISMATCH);
239
263
  }
240
264
  if (!isRetryable || attempt === maxAttempts) {
265
+ // A rate-limit that exhausted its retries is an expected, un-actionable
266
+ // condition (Kling throughput saturation) — surface it as a non-paging
267
+ // UserFacingError rather than a raw axios 429, so it doesn't page
268
+ // on-call. (Genuine 1102 balance depletion is handled above and DOES
269
+ // page; ambiguous bare 429s land here and read as rate-limit, which is
270
+ // the safe default — rate limits shouldn't page.)
271
+ if (isRateLimit) {
272
+ logger_1.logger.warn("Kling rate-limited — retries exhausted", {
273
+ endpoint,
274
+ attempt,
275
+ maxAttempts,
276
+ httpStatus,
277
+ });
278
+ throw new errors_1.UserFacingError("This video provider is busy right now. Please retry in a little while.", errors_1.USER_FACING_ERROR_CODES.VIDEO_PROVIDER_RATE_LIMITED);
279
+ }
241
280
  logger_1.logger.error("Kling API request failed (non-retryable or max attempts reached)", {
242
281
  endpoint,
243
282
  attempt,
@@ -2,6 +2,14 @@ import { BaseAiGenProviderService } from "../baseAiGenProvider.service";
2
2
  import { CreditUsageParams, VideoGenerationParams, VideoGenerationResult, VideoStatusParams, VideoStatusResult } from "../types";
3
3
  export declare class PixVerseService extends BaseAiGenProviderService {
4
4
  private readonly baseUrl;
5
+ /**
6
+ * Uploads a source image to PixVerse and returns the integer `img_id` that
7
+ * /img/generate requires. The Upload-Image endpoint accepts either a binary
8
+ * `image` file or an `image_url` string (multipart/form-data) — we pass the
9
+ * URL we already have. Note: NO Content-Type header here; fetch sets the
10
+ * multipart boundary itself when the body is a FormData.
11
+ */
12
+ private uploadImage;
5
13
  generateVideo(params: VideoGenerationParams): Promise<VideoGenerationResult>;
6
14
  checkVideoStatus({ task, outputFilename, outputFilePath, }: VideoStatusParams): Promise<VideoStatusResult>;
7
15
  getCreditUsed(params: CreditUsageParams): number;
@@ -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;AA0FlB,qBAAa,eAAgB,SAAQ,wBAAwB;IAC3D,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAkD;IAKpE,aAAa,CACjB,MAAM,EAAE,qBAAqB,GAC5B,OAAO,CAAC,qBAAqB,CAAC;IAyK3B,gBAAgB,CAAC,EACrB,IAAI,EACJ,cAAc,EACd,cAAyB,GAC1B,EAAE,iBAAiB,GAAG,OAAO,CAAC,iBAAiB,CAAC;IA0FjD,aAAa,CAAC,MAAM,EAAE,iBAAiB,GAAG,MAAM;CAkDjD"}
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;AA0FlB,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;IAgL3B,gBAAgB,CAAC,EACrB,IAAI,EACJ,cAAc,EACd,cAAyB,GAC1B,EAAE,iBAAiB,GAAG,OAAO,CAAC,iBAAiB,CAAC;IA0FjD,aAAa,CAAC,MAAM,EAAE,iBAAiB,GAAG,MAAM;CAkDjD"}
@@ -82,6 +82,52 @@ class PixVerseService extends baseAiGenProvider_service_1.BaseAiGenProviderServi
82
82
  this.baseUrl = "https://app-api.pixverse.ai/openapi/v2/video";
83
83
  }
84
84
  // =========================================================
85
+ // UPLOAD IMAGE (image-to-video prerequisite)
86
+ // =========================================================
87
+ /**
88
+ * Uploads a source image to PixVerse and returns the integer `img_id` that
89
+ * /img/generate requires. The Upload-Image endpoint accepts either a binary
90
+ * `image` file or an `image_url` string (multipart/form-data) — we pass the
91
+ * URL we already have. Note: NO Content-Type header here; fetch sets the
92
+ * multipart boundary itself when the body is a FormData.
93
+ */
94
+ async uploadImage(imageUrl, traceId) {
95
+ const form = new FormData();
96
+ form.append("image_url", imageUrl);
97
+ return (0, transientRetry_1.withTransientRetry)("pixverse:uploadImage", async () => {
98
+ const resp = await fetch(`https://app-api.pixverse.ai/openapi/v2/image/upload`, {
99
+ method: "POST",
100
+ headers: {
101
+ "API-KEY": process.env.PIXVERSE_API_KEY,
102
+ "Ai-trace-id": traceId,
103
+ },
104
+ body: form,
105
+ });
106
+ if (!resp.ok) {
107
+ const errText = await resp.text();
108
+ throw classifyPixVerseHttpError(resp.status, errText, "uploadImage");
109
+ }
110
+ const data = await resp.json();
111
+ if (data.ErrCode !== undefined && data.ErrCode !== 0) {
112
+ throw classifyPixVerseApiError(data.ErrCode, data.ErrMsg ?? "", "uploadImage");
113
+ }
114
+ const imgId = data?.Resp?.img_id;
115
+ if (imgId === undefined || imgId === null) {
116
+ throw new Error("PixVerse image upload did not return img_id");
117
+ }
118
+ return Number(imgId);
119
+ }, {
120
+ onRetry: ({ attempt, maxAttempts, backoffMs, err }) => {
121
+ logger_1.logger.warn(`PixVerse uploadImage transient error — retrying`, {
122
+ attempt,
123
+ maxAttempts,
124
+ backoffMs,
125
+ message: err.message,
126
+ });
127
+ },
128
+ });
129
+ }
130
+ // =========================================================
85
131
  // GENERATE VIDEO
86
132
  // =========================================================
87
133
  async generateVideo(params) {
@@ -206,10 +252,17 @@ class PixVerseService extends baseAiGenProvider_service_1.BaseAiGenProviderServi
206
252
  if (params.lipSync.ttsSpeakerId)
207
253
  body.lip_sync_tts_speaker_id = params.lipSync.ttsSpeakerId;
208
254
  }
209
- // I2V switches endpoint and adds the image reference.
255
+ // I2V switches endpoint and adds the image reference. The /img/generate
256
+ // endpoint does NOT accept a raw image_url — it requires an integer
257
+ // `img_id` from the Upload-Image API (verified 2026-06-30 against
258
+ // https://docs.platform.pixverse.ai/image-to-video-generation-13016633e0).
259
+ // Sending image_url instead surfaced in prod as ErrCode 400017 "Either
260
+ // img_id or api_gen_img_ids must be provided." So we upload first, then
261
+ // pass the returned img_id. (Distinct from the /template/generate "effect"
262
+ // path above, which DOES take a customer_img_url directly.)
210
263
  const isImageToVideo = !!params.inputImageUrl;
211
264
  if (isImageToVideo) {
212
- body.image_url = params.inputImageUrl;
265
+ body.img_id = await this.uploadImage(params.inputImageUrl, traceId);
213
266
  }
214
267
  // I2V path is `/img/generate` — NOT `/image/generate`, which returns a bare
215
268
  // "404 page not found" (verified 2026-06-15 via PixVerse OpenAPI smoke test
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vidspotai-shared",
3
- "version": "1.0.94",
3
+ "version": "1.0.95",
4
4
  "main": "lib/index.js",
5
5
  "types": "lib/index.d.ts",
6
6
  "exports": {