vidspotai-shared 1.0.124 → 1.0.126

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.
Files changed (153) hide show
  1. package/lib/globals/aiModels/enums.d.ts +38 -0
  2. package/lib/globals/aiModels/enums.d.ts.map +1 -1
  3. package/lib/globals/aiModels/enums.js +49 -1
  4. package/lib/globals/aiModels/index.d.ts.map +1 -1
  5. package/lib/globals/aiModels/index.js +2 -0
  6. package/lib/globals/aiModels/providers/alibaba.d.ts.map +1 -1
  7. package/lib/globals/aiModels/providers/alibaba.js +460 -53
  8. package/lib/globals/aiModels/providers/bytedance.d.ts.map +1 -1
  9. package/lib/globals/aiModels/providers/bytedance.js +221 -39
  10. package/lib/globals/aiModels/providers/google.d.ts.map +1 -1
  11. package/lib/globals/aiModels/providers/google.js +178 -32
  12. package/lib/globals/aiModels/providers/kling.d.ts.map +1 -1
  13. package/lib/globals/aiModels/providers/kling.js +86 -12
  14. package/lib/globals/aiModels/providers/minimax.d.ts.map +1 -1
  15. package/lib/globals/aiModels/providers/minimax.js +77 -0
  16. package/lib/globals/aiModels/providers/openai.d.ts.map +1 -1
  17. package/lib/globals/aiModels/providers/openai.js +12 -0
  18. package/lib/globals/aiModels/providers/pixverse.d.ts.map +1 -1
  19. package/lib/globals/aiModels/providers/pixverse.js +101 -28
  20. package/lib/globals/aiModels/providers/runway.d.ts.map +1 -1
  21. package/lib/globals/aiModels/providers/runway.js +450 -24
  22. package/lib/globals/aiModels/providers/xai.d.ts +4 -0
  23. package/lib/globals/aiModels/providers/xai.d.ts.map +1 -0
  24. package/lib/globals/aiModels/providers/xai.js +72 -0
  25. package/lib/globals/aiModels/tierHelpers.d.ts +10 -0
  26. package/lib/globals/aiModels/tierHelpers.d.ts.map +1 -1
  27. package/lib/globals/aiModels/tierHelpers.js +48 -7
  28. package/lib/globals/aiModels/types.d.ts +21 -0
  29. package/lib/globals/aiModels/types.d.ts.map +1 -1
  30. package/lib/models/image.model.d.ts +24 -0
  31. package/lib/models/image.model.d.ts.map +1 -1
  32. package/lib/models/image.model.js +12 -0
  33. package/lib/models/index.d.ts +1 -0
  34. package/lib/models/index.d.ts.map +1 -1
  35. package/lib/models/index.js +1 -0
  36. package/lib/models/klingElement.model.d.ts +52 -0
  37. package/lib/models/klingElement.model.d.ts.map +1 -0
  38. package/lib/models/klingElement.model.js +24 -0
  39. package/lib/models/user.model.d.ts +7 -1
  40. package/lib/models/user.model.d.ts.map +1 -1
  41. package/lib/models/video.model.d.ts +11 -0
  42. package/lib/models/video.model.d.ts.map +1 -1
  43. package/lib/schemas/videoPlan.schema.d.ts +88 -0
  44. package/lib/schemas/videoPlan.schema.d.ts.map +1 -1
  45. package/lib/schemas/videoPlan.schema.js +27 -1
  46. package/lib/services/agent/executor/visual.d.ts.map +1 -1
  47. package/lib/services/agent/executor/visual.js +40 -2
  48. package/lib/services/agent/mentionedRefs.d.ts.map +1 -1
  49. package/lib/services/agent/mentionedRefs.js +4 -1
  50. package/lib/services/agent/planner/structuralRules.d.ts.map +1 -1
  51. package/lib/services/agent/planner/structuralRules.js +8 -3
  52. package/lib/services/agent/poster/posterAgent.schema.d.ts +3 -3
  53. package/lib/services/agent/providerFallback/chains.d.ts +25 -0
  54. package/lib/services/agent/providerFallback/chains.d.ts.map +1 -1
  55. package/lib/services/agent/providerFallback/chains.js +59 -5
  56. package/lib/services/agent/repair/strategies/llmRepair.strategy.d.ts.map +1 -1
  57. package/lib/services/agent/repair/strategies/llmRepair.strategy.js +2 -0
  58. package/lib/services/agent/tools/animateImage.tool.d.ts +31 -0
  59. package/lib/services/agent/tools/animateImage.tool.d.ts.map +1 -1
  60. package/lib/services/agent/tools/animateImage.tool.js +10 -3
  61. package/lib/services/agent/tools/generateImage.tool.d.ts +8 -4
  62. package/lib/services/agent/tools/generateImage.tool.d.ts.map +1 -1
  63. package/lib/services/agent/tools/generateImage.tool.js +24 -7
  64. package/lib/services/agent/tools/generateVideo.tool.d.ts +32 -0
  65. package/lib/services/agent/tools/generateVideo.tool.d.ts.map +1 -1
  66. package/lib/services/agent/tools/generateVideo.tool.js +37 -9
  67. package/lib/services/agent/tools/planVideo.tool.d.ts +19 -0
  68. package/lib/services/agent/tools/planVideo.tool.d.ts.map +1 -1
  69. package/lib/services/aiGen/aiGenFactory.service.d.ts.map +1 -1
  70. package/lib/services/aiGen/aiGenFactory.service.js +40 -0
  71. package/lib/services/aiGen/canonicalAdapters/cameraControl.types.d.ts +6 -3
  72. package/lib/services/aiGen/canonicalAdapters/cameraControl.types.d.ts.map +1 -1
  73. package/lib/services/aiGen/canonicalAdapters/cameraControl.types.js +35 -0
  74. package/lib/services/aiGen/canonicalAdapters/cameraDirection.d.ts +36 -0
  75. package/lib/services/aiGen/canonicalAdapters/cameraDirection.d.ts.map +1 -0
  76. package/lib/services/aiGen/canonicalAdapters/cameraDirection.js +168 -0
  77. package/lib/services/aiGen/canonicalAdapters/index.d.ts +1 -0
  78. package/lib/services/aiGen/canonicalAdapters/index.d.ts.map +1 -1
  79. package/lib/services/aiGen/canonicalAdapters/index.js +1 -0
  80. package/lib/services/aiGen/canonicalAdapters/multiShot.types.d.ts +3 -2
  81. package/lib/services/aiGen/canonicalAdapters/multiShot.types.d.ts.map +1 -1
  82. package/lib/services/aiGen/helpers.d.ts +40 -0
  83. package/lib/services/aiGen/helpers.d.ts.map +1 -1
  84. package/lib/services/aiGen/helpers.js +48 -2
  85. package/lib/services/aiGen/index.d.ts +1 -0
  86. package/lib/services/aiGen/index.d.ts.map +1 -1
  87. package/lib/services/aiGen/index.js +3 -0
  88. package/lib/services/aiGen/providers/alibaba/alibaba.d.ts +13 -1
  89. package/lib/services/aiGen/providers/alibaba/alibaba.d.ts.map +1 -1
  90. package/lib/services/aiGen/providers/alibaba/alibaba.js +313 -33
  91. package/lib/services/aiGen/providers/bytedance/bytedance.service.d.ts +29 -1
  92. package/lib/services/aiGen/providers/bytedance/bytedance.service.d.ts.map +1 -1
  93. package/lib/services/aiGen/providers/bytedance/bytedance.service.js +371 -54
  94. package/lib/services/aiGen/providers/google/google.service.d.ts +16 -0
  95. package/lib/services/aiGen/providers/google/google.service.d.ts.map +1 -1
  96. package/lib/services/aiGen/providers/google/google.service.js +372 -116
  97. package/lib/services/aiGen/providers/google/googleKeyPool.d.ts +6 -0
  98. package/lib/services/aiGen/providers/google/googleKeyPool.d.ts.map +1 -1
  99. package/lib/services/aiGen/providers/google/googleKeyPool.js +8 -0
  100. package/lib/services/aiGen/providers/kling/kling.service.d.ts +17 -1
  101. package/lib/services/aiGen/providers/kling/kling.service.d.ts.map +1 -1
  102. package/lib/services/aiGen/providers/kling/kling.service.js +59 -3
  103. package/lib/services/aiGen/providers/kling/klingCredits.d.ts +1 -1
  104. package/lib/services/aiGen/providers/kling/klingCredits.d.ts.map +1 -1
  105. package/lib/services/aiGen/providers/kling/klingCredits.js +19 -1
  106. package/lib/services/aiGen/providers/kling/klingRequests.d.ts +37 -7
  107. package/lib/services/aiGen/providers/kling/klingRequests.d.ts.map +1 -1
  108. package/lib/services/aiGen/providers/kling/klingRequests.js +191 -13
  109. package/lib/services/aiGen/providers/kling/types.d.ts +78 -0
  110. package/lib/services/aiGen/providers/kling/types.d.ts.map +1 -1
  111. package/lib/services/aiGen/providers/minimax/minimax.service.d.ts +11 -2
  112. package/lib/services/aiGen/providers/minimax/minimax.service.d.ts.map +1 -1
  113. package/lib/services/aiGen/providers/minimax/minimax.service.js +168 -2
  114. package/lib/services/aiGen/providers/minimax/types.d.ts +63 -0
  115. package/lib/services/aiGen/providers/minimax/types.d.ts.map +1 -1
  116. package/lib/services/aiGen/providers/pixverse/pixverse.service.d.ts +12 -0
  117. package/lib/services/aiGen/providers/pixverse/pixverse.service.d.ts.map +1 -1
  118. package/lib/services/aiGen/providers/pixverse/pixverse.service.js +131 -17
  119. package/lib/services/aiGen/providers/runway/hostedModels.d.ts +84 -0
  120. package/lib/services/aiGen/providers/runway/hostedModels.d.ts.map +1 -0
  121. package/lib/services/aiGen/providers/runway/hostedModels.js +266 -0
  122. package/lib/services/aiGen/providers/runway/index.d.ts +1 -0
  123. package/lib/services/aiGen/providers/runway/index.d.ts.map +1 -1
  124. package/lib/services/aiGen/providers/runway/index.js +1 -0
  125. package/lib/services/aiGen/providers/runway/runway.service.d.ts +1 -1
  126. package/lib/services/aiGen/providers/runway/runway.service.d.ts.map +1 -1
  127. package/lib/services/aiGen/providers/runway/runway.service.js +118 -21
  128. package/lib/services/aiGen/providers/runway/types.d.ts +18 -3
  129. package/lib/services/aiGen/providers/runway/types.d.ts.map +1 -1
  130. package/lib/services/aiGen/providers/runway/types.js +31 -3
  131. package/lib/services/aiGen/providers/types.d.ts +55 -0
  132. package/lib/services/aiGen/providers/types.d.ts.map +1 -1
  133. package/lib/services/aiGen/providers/xai/xai.service.d.ts +19 -0
  134. package/lib/services/aiGen/providers/xai/xai.service.d.ts.map +1 -0
  135. package/lib/services/aiGen/providers/xai/xai.service.js +205 -0
  136. package/lib/services/firestore.service.d.ts +4 -0
  137. package/lib/services/firestore.service.d.ts.map +1 -1
  138. package/lib/services/firestore.service.js +5 -0
  139. package/lib/services/index.d.ts +2 -0
  140. package/lib/services/index.d.ts.map +1 -1
  141. package/lib/services/index.js +2 -0
  142. package/lib/services/klingElements/klingElementLibrary.service.d.ts +37 -0
  143. package/lib/services/klingElements/klingElementLibrary.service.d.ts.map +1 -0
  144. package/lib/services/klingElements/klingElementLibrary.service.js +179 -0
  145. package/lib/services/trialGuard/index.d.ts +2 -0
  146. package/lib/services/trialGuard/index.d.ts.map +1 -0
  147. package/lib/services/trialGuard/index.js +17 -0
  148. package/lib/services/trialGuard/trialSignals.d.ts +88 -0
  149. package/lib/services/trialGuard/trialSignals.d.ts.map +1 -0
  150. package/lib/services/trialGuard/trialSignals.js +160 -0
  151. package/lib/utils/helpers.d.ts.map +1 -1
  152. package/lib/utils/helpers.js +5 -1
  153. package/package.json +2 -1
@@ -3,6 +3,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.modelSupportsLastFrame = modelSupportsLastFrame;
4
4
  exports.getModelsSupportingLastFrame = getModelsSupportingLastFrame;
5
5
  exports.modelDeclaresField = modelDeclaresField;
6
+ exports.getInputVideoBilling = getInputVideoBilling;
7
+ exports.isModelRetired = isModelRetired;
6
8
  exports.validateParams = validateParams;
7
9
  const aiModels_1 = require("../../globals/aiModels");
8
10
  const errors_1 = require("../../utils/errors");
@@ -37,11 +39,50 @@ function modelDeclaresField(modelKey, field) {
37
39
  const cfg = aiModels_1.aiModelConfigs[modelKey];
38
40
  return !!cfg?.fields && field in cfg.fields;
39
41
  }
42
+ /**
43
+ * Input-video billing for getCreditUsed. Providers that bill input seconds
44
+ * (Wan 3.0, HappyHorse edit, MiniMax, Seedance 2.0) charge for EVERY video the
45
+ * request carries — the source clip and each reference clip — so both count.
46
+ * An unmeasured clip falls back to the output duration (the same conservative
47
+ * proxy the source clip always used). Every billing call site (precheck,
48
+ * upfront deduction, multi-scene true-up, reconcile) must go through this so
49
+ * the estimate and the reconcile agree.
50
+ */
51
+ function getInputVideoBilling(src) {
52
+ const numInputImages = (src.inputImageUrl ? 1 : 0) +
53
+ (src.lastFrameImageUrl ? 1 : 0) +
54
+ (src.referenceImageUrls?.length ?? 0);
55
+ const numInputAudios = src.referenceAudioUrls?.length ?? 0;
56
+ const fallback = src.duration ?? 0;
57
+ let seconds = 0;
58
+ let clips = 0;
59
+ if (src.inputVideoUrl) {
60
+ seconds += src.inputVideoDurationSec ?? fallback;
61
+ clips += 1;
62
+ }
63
+ (src.referenceVideoUrls ?? []).forEach((_, i) => {
64
+ seconds += src.referenceVideoDurationsSec?.[i] ?? fallback;
65
+ clips += 1;
66
+ });
67
+ // return clips ? { hasInputVideo: true, inputVideoDurationSec: seconds } : { hasInputVideo: false }; // REMOVED FOR v2 — now also counts input images
68
+ return clips
69
+ ? { hasInputVideo: true, inputVideoDurationSec: seconds, numInputImages, numInputAudios }
70
+ : { hasInputVideo: false, numInputImages, numInputAudios };
71
+ }
72
+ function isModelRetired(modelKey) {
73
+ const cfg = aiModels_1.aiModelConfigs[modelKey];
74
+ return !!cfg?.retired;
75
+ }
40
76
  function validateParams(params) {
41
77
  const modelConfig = aiModels_1.aiModelConfigs[params.modelKey];
42
78
  if (!modelConfig) {
43
79
  throw new Error(`Unsupported model: ${params.modelKey}`);
44
80
  }
81
+ // 0. Retired upstream — fail fast with a user-facing, non-paging error
82
+ // instead of a billed round-trip to a dead endpoint.
83
+ if (modelConfig.retired) {
84
+ throw new errors_1.UserFacingError(`Model ${params.modelKey} is no longer available: ${modelConfig.retired.reason} Please pick a different model.`, errors_1.USER_FACING_ERROR_CODES.CAPABILITY_MISMATCH);
85
+ }
45
86
  const errors = [];
46
87
  // 1. Field-level validation (required + allowedValues)
47
88
  for (const [field, rules] of Object.entries(modelConfig.fields)) {
@@ -84,8 +125,13 @@ function validateParams(params) {
84
125
  (!modelConfig.type.includes("image-to-video") || !params.inputImageUrl)) {
85
126
  errors.push(`Model ${params.modelKey} requires imageUrl for image-to-video mode`);
86
127
  }
87
- if (params.type === "video-to-video" &&
88
- (modelConfig.type.includes("video-to-video") || !params.inputVideoUrl)) {
128
+ // REMOVED FOR v2 — kept for reference: inverted — it rejected every
129
+ // video-to-video request on a model that DOES declare video-to-video.
130
+ // if (
131
+ // params.type === "video-to-video" &&
132
+ // (modelConfig.type.includes("video-to-video") || !params.inputVideoUrl)
133
+ // ) {
134
+ if (params.type === "video-to-video" && !params.inputVideoUrl) {
89
135
  errors.push(`Model ${params.modelKey} requires videoUrl for video-to-video mode`);
90
136
  }
91
137
  if (modelConfig.type.includes("text-to-video") && !params.prompt) {
@@ -3,4 +3,5 @@ export * from "./types";
3
3
  export * from "./helpers";
4
4
  export * from "./providers";
5
5
  export * from "./transientRetry";
6
+ export { applyCameraDirection } from "./canonicalAdapters/cameraDirection";
6
7
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/services/aiGen/index.ts"],"names":[],"mappings":"AAAA,cAAc,wBAAwB,CAAC;AACvC,cAAc,SAAS,CAAC;AACxB,cAAc,WAAW,CAAC;AAC1B,cAAc,aAAa,CAAC;AAC5B,cAAc,kBAAkB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/services/aiGen/index.ts"],"names":[],"mappings":"AAAA,cAAc,wBAAwB,CAAC;AACvC,cAAc,SAAS,CAAC;AACxB,cAAc,WAAW,CAAC;AAC1B,cAAc,aAAa,CAAC;AAC5B,cAAc,kBAAkB,CAAC;AACjC,OAAO,EAAE,oBAAoB,EAAE,MAAM,qCAAqC,CAAC"}
@@ -14,8 +14,11 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
14
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.applyCameraDirection = void 0;
17
18
  __exportStar(require("./aiGenFactory.service"), exports);
18
19
  __exportStar(require("./types"), exports);
19
20
  __exportStar(require("./helpers"), exports);
20
21
  __exportStar(require("./providers"), exports);
21
22
  __exportStar(require("./transientRetry"), exports);
23
+ var cameraDirection_1 = require("./canonicalAdapters/cameraDirection");
24
+ Object.defineProperty(exports, "applyCameraDirection", { enumerable: true, get: function () { return cameraDirection_1.applyCameraDirection; } });
@@ -1,5 +1,16 @@
1
1
  import { BaseAiGenProviderService } from "../baseAiGenProvider.service";
2
2
  import { CreditUsageParams, ImageGenerationParams, ImageGenerationResult, VideoGenerationParams, VideoGenerationResult, VideoStatusParams, VideoStatusResult } from "../types";
3
+ /**
4
+ * Build the `{input, parameters}` body for the media-array models. Mode is
5
+ * driven by the model id (HappyHorse ships one model per mode) and, for the
6
+ * all-in-one Wan 3.0, by which inputs are present. Reference order matters:
7
+ * the prompt cites media as "Image 1" / "Video 1" / "Audio 1" (Wan 3.0) or
8
+ * "[Image 1]" (HappyHorse) in array order, per type.
9
+ */
10
+ export declare function buildMediaArrayVideoBody(modelId: string, params: VideoGenerationParams): {
11
+ input: Record<string, unknown>;
12
+ parameters: Record<string, unknown>;
13
+ };
3
14
  /**
4
15
  * Map a canonical "W:H" aspect ratio to a DashScope pixel `size` string.
5
16
  * Returns undefined for unparseable input so the caller simply omits `size`
@@ -17,6 +28,7 @@ export declare class AlibabaService extends BaseAiGenProviderService {
17
28
  */
18
29
  private request;
19
30
  generateVideo(params: VideoGenerationParams): Promise<VideoGenerationResult>;
31
+ private submitVideo;
20
32
  checkVideoStatus({ task, outputFilename, outputFilePath, modelKey, }: VideoStatusParams): Promise<VideoStatusResult>;
21
33
  /**
22
34
  * DashScope image generation. Three dispatch paths (verified live 2026-06-06):
@@ -51,6 +63,6 @@ export declare class AlibabaService extends BaseAiGenProviderService {
51
63
  * and text2image/image-synthesis (legacy Qwen).
52
64
  */
53
65
  private generateImageAsync;
54
- getCreditUsed({ modelKey, resolution, aspectRatio, duration, multiClip, numImages, creditFloor, }: CreditUsageParams): number;
66
+ getCreditUsed({ modelKey, resolution, aspectRatio, duration, multiClip, numImages, creditFloor, hasInputVideo, inputVideoDurationSec, audio, numInputImages, }: CreditUsageParams): number;
55
67
  }
56
68
  //# sourceMappingURL=alibaba.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"alibaba.d.ts","sourceRoot":"","sources":["../../../../../src/services/aiGen/providers/alibaba/alibaba.ts"],"names":[],"mappings":"AAEA,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;AAoGlB;;;;GAIG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,MAAM,GAClB,MAAM,GAAG,SAAS,CAiBpB;AAED,qBAAa,cAAe,SAAQ,wBAAwB;IAE1D,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAwB;IAChD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;;IAiBjC;;;;OAIG;YACW,OAAO;IAwDf,aAAa,CACjB,MAAM,EAAE,qBAAqB,GAC5B,OAAO,CAAC,qBAAqB,CAAC;IA+E3B,gBAAgB,CAAC,EACrB,IAAI,EACJ,cAAc,EACd,cAAyB,EACzB,QAAQ,GACT,EAAE,iBAAiB,GAAG,OAAO,CAAC,iBAAiB,CAAC;IAqEjD;;;;;;;;;;;;;;;;;;;;;OAqBG;IACG,aAAa,CACjB,MAAM,EAAE,qBAAqB,GAC5B,OAAO,CAAC,qBAAqB,CAAC;IA0EjC;;;OAGG;YACW,iBAAiB;IA4C/B;;;OAGG;YACW,kBAAkB;IA6DhC,aAAa,CAAC,EACZ,QAAQ,EACR,UAAU,EACV,WAAW,EACX,QAAQ,EACR,SAAiB,EACjB,SAAa,EACb,WAAW,GACZ,EAAE,iBAAiB,GAAG,MAAM;CAoB9B"}
1
+ {"version":3,"file":"alibaba.d.ts","sourceRoot":"","sources":["../../../../../src/services/aiGen/providers/alibaba/alibaba.ts"],"names":[],"mappings":"AAEA,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;AAwFlB;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,qBAAqB,GAC5B;IAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,CAgJzE;AA2CD;;;;GAIG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,MAAM,GAClB,MAAM,GAAG,SAAS,CAmBpB;AAED,qBAAa,cAAe,SAAQ,wBAAwB;IAE1D,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAwB;IAChD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;;IAiBjC;;;;OAIG;YACW,OAAO;IAwDf,aAAa,CACjB,MAAM,EAAE,qBAAqB,GAC5B,OAAO,CAAC,qBAAqB,CAAC;YAyInB,WAAW;IAwBnB,gBAAgB,CAAC,EACrB,IAAI,EACJ,cAAc,EACd,cAAyB,EACzB,QAAQ,GACT,EAAE,iBAAiB,GAAG,OAAO,CAAC,iBAAiB,CAAC;IAqEjD;;;;;;;;;;;;;;;;;;;;;OAqBG;IACG,aAAa,CACjB,MAAM,EAAE,qBAAqB,GAC5B,OAAO,CAAC,qBAAqB,CAAC;IA6EjC;;;OAGG;YACW,iBAAiB;IAoD/B;;;OAGG;YACW,kBAAkB;IA6DhC,aAAa,CAAC,EACZ,QAAQ,EACR,UAAU,EACV,WAAW,EACX,QAAQ,EACR,SAAiB,EACjB,SAAa,EACb,WAAW,EACX,aAAa,EACb,qBAAqB,EACrB,KAAK,EACL,cAAc,GACf,EAAE,iBAAiB,GAAG,MAAM;CA4C9B"}
@@ -4,6 +4,7 @@ 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.buildMediaArrayVideoBody = buildMediaArrayVideoBody;
7
8
  exports.resolveDashScopeSize = resolveDashScopeSize;
8
9
  const axios_1 = __importDefault(require("axios"));
9
10
  const types_1 = require("../../../../globals/types");
@@ -29,7 +30,14 @@ const errors_1 = require("../../../../utils/errors");
29
30
  // S2V (wan2.2-s2v) is China-region only and cannot be reached with an intl
30
31
  // DashScope key; the model key is intentionally kept but rejected at submit
31
32
  // with a UserFacingError. Same for kolors-v2 (Kuaishou model, not Alibaba).
32
- const ALIBABA_BASE_URL = "https://dashscope-intl.aliyuncs.com/api/v1";
33
+ //
34
+ // Host: Alibaba now recommends the workspace domain
35
+ // https://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/api/v1 and says the
36
+ // DashScope domain gets no NEW features after 2026-09-30 (existing models keep
37
+ // working). ALIBABA_API_BASE_URL switches hosts without a code change; the
38
+ // same API key works on both.
39
+ // const ALIBABA_BASE_URL = "https://dashscope-intl.aliyuncs.com/api/v1"; // REMOVED FOR v2 — now env-overridable
40
+ const ALIBABA_BASE_URL = (process.env.ALIBABA_API_BASE_URL || "https://dashscope-intl.aliyuncs.com/api/v1").replace(/\/+$/, "");
33
41
  const ALIBABA_BASE = `${ALIBABA_BASE_URL}/services/aigc`;
34
42
  const ENDPOINT_VIDEO_ASYNC = `${ALIBABA_BASE}/video-generation/video-synthesis`;
35
43
  const ENDPOINT_IMAGE_T2I_ASYNC = `${ALIBABA_BASE}/text2image/image-synthesis`;
@@ -53,6 +61,7 @@ const INTL_UNAVAILABLE_MODEL_IDS = new Set([
53
61
  // - Legacy qwen-image, qwen-image-plus.
54
62
  function isSyncMultimodalImageModel(modelId) {
55
63
  return (modelId.startsWith("qwen-image-2") ||
64
+ modelId.startsWith("qwen-image-3") ||
56
65
  modelId.startsWith("qwen-image-max") ||
57
66
  modelId.startsWith("qwen-image-edit") ||
58
67
  modelId.startsWith("wan2.7-image"));
@@ -60,6 +69,165 @@ function isSyncMultimodalImageModel(modelId) {
60
69
  function isAsyncWanImageModel(modelId) {
61
70
  return modelId.startsWith("wan2.7-image") || modelId.startsWith("wan2.6-image");
62
71
  }
72
+ /**
73
+ * Wan 3.0, HappyHorse and Wan 2.7 r2v / videoedit take every input as
74
+ * `input.media[{type,url}]` and pick size with `resolution` ("1080P") +
75
+ * `ratio` instead of a pixel `size`. Only the Wan 2.7 pair take a negative prompt. HappyHorse also watermarks by DEFAULT, so we always
76
+ * send an explicit `watermark`.
77
+ */
78
+ function isMediaArrayVideoModel(modelId) {
79
+ // return modelId.startsWith("wan3") || modelId.startsWith("happyhorse"); // REMOVED FOR v2 — + Wan 2.7 r2v / videoedit
80
+ return (modelId.startsWith("wan3") ||
81
+ modelId.startsWith("happyhorse") ||
82
+ modelId.startsWith("wan2.7-r2v") ||
83
+ modelId.startsWith("wan2.7-videoedit"));
84
+ }
85
+ /**
86
+ * Build the `{input, parameters}` body for the media-array models. Mode is
87
+ * driven by the model id (HappyHorse ships one model per mode) and, for the
88
+ * all-in-one Wan 3.0, by which inputs are present. Reference order matters:
89
+ * the prompt cites media as "Image 1" / "Video 1" / "Audio 1" (Wan 3.0) or
90
+ * "[Image 1]" (HappyHorse) in array order, per type.
91
+ */
92
+ function buildMediaArrayVideoBody(modelId, params) {
93
+ const media = [];
94
+ const refImages = params.referenceImageUrls ?? [];
95
+ const refVideos = [
96
+ ...(params.inputVideoUrl ? [params.inputVideoUrl] : []),
97
+ ...(params.referenceVideoUrls ?? []),
98
+ ];
99
+ const refAudios = params.referenceAudioUrls ?? [];
100
+ const isHappyHorse = modelId.startsWith("happyhorse");
101
+ const isVideoEdit = modelId === "happyhorse-1.0-video-edit";
102
+ // const isHappyHorseI2V = modelId === "happyhorse-1.1-i2v"; // REMOVED FOR v2 — suffix covers 1.0 + 1.1
103
+ // HappyHorse ships one model per mode; the id suffix names it (-t2v/-i2v/-r2v).
104
+ const isHappyHorseI2V = isHappyHorse && modelId.endsWith("-i2v");
105
+ const isWan27R2V = modelId.startsWith("wan2.7-r2v");
106
+ const isWan27VideoEdit = modelId.startsWith("wan2.7-videoedit");
107
+ const hasFrames = !!params.inputImageUrl || !!params.lastFrameImageUrl;
108
+ const hasDocOrLink = !!params.referenceDocumentUrl || !!params.referenceWebPageUrl;
109
+ const hasRefs = refImages.length + refVideos.length + refAudios.length > 0 || hasDocOrLink;
110
+ if (isWan27R2V) {
111
+ // ≤1 first frame + 1–5 subjects (images + videos combined); each subject
112
+ // may carry a voice. Voices map in send order: videos first, then images.
113
+ const subjects = [
114
+ ...(params.referenceVideoUrls ?? []).map((url) => ({ type: "reference_video", url })),
115
+ ...refImages.map((url) => ({ type: "reference_image", url })),
116
+ ];
117
+ if (subjects.length < 1 || subjects.length > 5) {
118
+ throw new errors_1.UserFacingError(`${modelId} needs 1–5 reference images and videos in total (got ${subjects.length}).`, errors_1.USER_FACING_ERROR_CODES.CAPABILITY_MISMATCH);
119
+ }
120
+ if (refAudios.length > subjects.length) {
121
+ throw new errors_1.UserFacingError(`${modelId} takes one voice per reference subject: ${refAudios.length} voices for ${subjects.length} subjects.`, errors_1.USER_FACING_ERROR_CODES.CAPABILITY_MISMATCH);
122
+ }
123
+ if ((params.referenceVideoUrls?.length ?? 0) > 0 && (params.duration ?? 5) > 10) {
124
+ throw new errors_1.UserFacingError(`${modelId} is limited to 10s when a reference video is used.`, errors_1.USER_FACING_ERROR_CODES.CAPABILITY_MISMATCH);
125
+ }
126
+ if (params.inputImageUrl)
127
+ media.push({ type: "first_frame", url: params.inputImageUrl });
128
+ subjects.forEach((m, i) => media.push(refAudios[i] ? { ...m, reference_voice: refAudios[i] } : m));
129
+ }
130
+ else if (isWan27VideoEdit) {
131
+ if (!params.inputVideoUrl) {
132
+ throw new errors_1.UserFacingError(`${modelId} needs the video to edit (inputVideoUrl).`, errors_1.USER_FACING_ERROR_CODES.CAPABILITY_MISMATCH);
133
+ }
134
+ media.push({ type: "video", url: params.inputVideoUrl });
135
+ for (const url of refImages.slice(0, 4))
136
+ media.push({ type: "reference_image", url });
137
+ }
138
+ else if (isVideoEdit) {
139
+ if (!params.inputVideoUrl) {
140
+ throw new errors_1.UserFacingError(`${modelId} needs the video to edit (inputVideoUrl).`, errors_1.USER_FACING_ERROR_CODES.CAPABILITY_MISMATCH);
141
+ }
142
+ media.push({ type: "video", url: params.inputVideoUrl });
143
+ for (const url of refImages.slice(0, 5))
144
+ media.push({ type: "reference_image", url });
145
+ }
146
+ else if (isHappyHorseI2V) {
147
+ media.push({ type: "first_frame", url: params.inputImageUrl });
148
+ }
149
+ else if (isHappyHorse) {
150
+ // r2v: 1–9 reference images; t2v: no media.
151
+ for (const url of refImages.slice(0, 9))
152
+ media.push({ type: "reference_image", url });
153
+ }
154
+ else {
155
+ // Wan 3.0: first/last frame XOR references (the API rejects mixing them).
156
+ if (hasFrames && hasRefs) {
157
+ throw new errors_1.UserFacingError(`${modelId} can't combine first/last frame images with reference images, videos, audio, documents or web pages. Use one or the other.`, errors_1.USER_FACING_ERROR_CODES.CAPABILITY_MISMATCH);
158
+ }
159
+ if (params.referenceDocumentUrl && params.referenceWebPageUrl) {
160
+ throw new errors_1.UserFacingError(`${modelId} takes a reference document OR a web page, not both.`, errors_1.USER_FACING_ERROR_CODES.CAPABILITY_MISMATCH);
161
+ }
162
+ if (params.inputImageUrl)
163
+ media.push({ type: "first_frame", url: params.inputImageUrl });
164
+ if (params.lastFrameImageUrl)
165
+ media.push({ type: "last_frame", url: params.lastFrameImageUrl });
166
+ for (const url of refImages.slice(0, 10))
167
+ media.push({ type: "reference_image", url });
168
+ for (const url of refVideos.slice(0, 5))
169
+ media.push({ type: "reference_video", url });
170
+ for (const url of refAudios.slice(0, 5))
171
+ media.push({ type: "reference_audio", url });
172
+ if (params.referenceDocumentUrl)
173
+ media.push({ type: "file", url: params.referenceDocumentUrl });
174
+ if (params.referenceWebPageUrl)
175
+ media.push({ type: "link", url: params.referenceWebPageUrl });
176
+ }
177
+ const input = {};
178
+ if (params.prompt)
179
+ input.prompt = params.prompt;
180
+ if (media.length)
181
+ input.media = media;
182
+ // Wan 2.7 takes a negative prompt; Wan 3.0 / HappyHorse don't.
183
+ if ((isWan27R2V || isWan27VideoEdit) && params.negativePrompt) {
184
+ input.negative_prompt = params.negativePrompt;
185
+ }
186
+ const parameters = {
187
+ watermark: params.watermark ?? false,
188
+ };
189
+ if (params.resolution)
190
+ parameters.resolution = params.resolution.toUpperCase();
191
+ if (params.seed !== undefined)
192
+ parameters.seed = params.seed;
193
+ if (isWan27VideoEdit) {
194
+ // Output = the whole input clip by default; `duration` truncates from 0s,
195
+ // so it's only sent when we know it's shorter than the clip.
196
+ if (params.duration &&
197
+ params.inputVideoDurationSec !== undefined &&
198
+ params.duration < params.inputVideoDurationSec) {
199
+ parameters.duration = params.duration;
200
+ }
201
+ if (params.aspectRatio)
202
+ parameters.ratio = params.aspectRatio;
203
+ parameters.audio_setting = params.audio === false ? "origin" : "auto";
204
+ parameters.prompt_extend = params.promptOptimizer ?? true;
205
+ return { input, parameters };
206
+ }
207
+ if (isVideoEdit) {
208
+ // Output length follows the input clip; no duration/ratio. `audio: false`
209
+ // keeps the source audio, otherwise the model decides.
210
+ parameters.audio_setting = params.audio === false ? "origin" : "auto";
211
+ return { input, parameters };
212
+ }
213
+ parameters.duration = params.duration || 5;
214
+ // Frame-driven output follows the image, so ratio is only sent without a
215
+ // first frame (Wan 3.0 defaults to "adaptive"; HappyHorse i2v has no ratio).
216
+ if (params.aspectRatio && !params.inputImageUrl && !isHappyHorseI2V) {
217
+ parameters.ratio = params.aspectRatio;
218
+ }
219
+ if (isWan27R2V) {
220
+ // Always generates audio; no `audio` switch.
221
+ parameters.prompt_extend = params.promptOptimizer ?? true;
222
+ }
223
+ else if (!isHappyHorse) {
224
+ if (params.audio !== undefined)
225
+ parameters.audio = params.audio;
226
+ // Document / web-page inputs require prompt_extend=true (API rule).
227
+ parameters.prompt_extend = hasDocOrLink ? true : params.promptOptimizer ?? true;
228
+ }
229
+ return { input, parameters };
230
+ }
63
231
  /**
64
232
  * DashScope's `size` parameter is "WIDTH*HEIGHT" in PIXELS — it is NOT an
65
233
  * aspect-ratio string. We used to assign `parameters.size = params.aspectRatio`
@@ -105,7 +273,8 @@ function round16(n) {
105
273
  * and lets DashScope apply its own default (better than sending a bad value).
106
274
  */
107
275
  function resolveDashScopeSize(modelId, aspectRatio) {
108
- const family = modelId.startsWith("qwen-image-2") ? "large" : "standard";
276
+ // REMOVED FOR v2 — kept for reference: const family = modelId.startsWith("qwen-image-2") ? "large" : "standard";
277
+ const family = modelId.startsWith("qwen-image-2") || modelId.startsWith("qwen-image-3") ? "large" : "standard";
109
278
  const preset = DASHSCOPE_SIZE_PRESETS[family][aspectRatio];
110
279
  if (preset)
111
280
  return preset;
@@ -192,52 +361,131 @@ class AlibabaService extends baseAiGenProvider_service_1.BaseAiGenProviderServic
192
361
  if (INTL_UNAVAILABLE_MODEL_IDS.has(modelId)) {
193
362
  throw new errors_1.UserFacingError(`Model "${modelId}" is not available on the intl DashScope endpoint (dashscope-intl.aliyuncs.com). It is hosted only in the China region and requires a separate Beijing-region API key.`, errors_1.USER_FACING_ERROR_CODES.PROVIDER_AUTH_ERROR);
194
363
  }
195
- // T2V and I2V share a single endpoint. The model + presence of img_url
196
- // determines mode. S2V is unavailable on intl (gated above).
197
- const isI2V = !!params.inputImageUrl;
364
+ if (isMediaArrayVideoModel(modelId)) {
365
+ const { input, parameters } = buildMediaArrayVideoBody(modelId, params);
366
+ return this.submitVideo({ model: modelId, input, parameters });
367
+ }
368
+ // REMOVED FOR v2 — kept for reference. Sent a pixel `size` to wan2.7-t2v
369
+ // (which replaced it with resolution + ratio), never sent `resolution` for
370
+ // I2V (so every I2V ran at the 1080P default whatever we billed), and
371
+ // ignored first_clip / driving audio.
372
+ // // T2V and I2V share a single endpoint. The model + presence of img_url
373
+ // // determines mode. S2V is unavailable on intl (gated above).
374
+ // const isI2V = !!params.inputImageUrl;
375
+ // const input: Record<string, unknown> = {};
376
+ // if (params.prompt) input.prompt = params.prompt;
377
+ // if (params.negativePrompt) input.negative_prompt = params.negativePrompt;
378
+ // if (isI2V) {
379
+ // // wan2.7-i2v moved to a media[] array ({type,url}) instead of the
380
+ // // legacy img_url/last_frame_url fields — confirmed against Alibaba's
381
+ // // "Wan 2.7 - image-to-video API" reference (2026-08-20); wan2.6-i2v(-flash)
382
+ // // and older versions still use img_url/last_frame_url.
383
+ // if (modelId.startsWith("wan2.7")) {
384
+ // const media: Array<{ type: string; url: string }> = [
385
+ // { type: "first_frame", url: params.inputImageUrl! },
386
+ // ];
387
+ // if (params.lastFrameImageUrl) {
388
+ // media.push({ type: "last_frame", url: params.lastFrameImageUrl });
389
+ // }
390
+ // input.media = media;
391
+ // } else {
392
+ // input.img_url = params.inputImageUrl;
393
+ // // wan2.6-i2v supports first-last-frame interpolation
394
+ // if (params.lastFrameImageUrl) input.last_frame_url = params.lastFrameImageUrl;
395
+ // }
396
+ // }
397
+ // const parameters: Record<string, unknown> = {
398
+ // duration: params.duration || 5,
399
+ // prompt_extend: params.promptOptimizer ?? true,
400
+ // };
401
+ // // T2V requires explicit size; I2V derives dimensions from the input image.
402
+ // if (!isI2V) {
403
+ // const size = getAlibabaDimensions(params.resolution!, params.aspectRatio!);
404
+ // if (!size) {
405
+ // throw new Error(
406
+ // `Invalid resolution/aspect ratio combination: ${params.resolution} ${params.aspectRatio}`
407
+ // );
408
+ // }
409
+ // parameters.size = size;
410
+ // }
411
+ // if (params.seed !== undefined) parameters.seed = params.seed;
412
+ // if (params.watermark !== undefined) parameters.watermark = params.watermark;
413
+ // T2V and I2V share a single endpoint. The model + inputs determine mode.
414
+ const isWan27 = modelId.startsWith("wan2.7");
415
+ const isI2V = !!params.inputImageUrl || (isWan27 && !!params.inputVideoUrl);
416
+ const resolutionTier = params.resolution ? params.resolution.toUpperCase() : undefined;
417
+ if (modelId.startsWith("wan2.7-i2v") && !isI2V) {
418
+ throw new errors_1.UserFacingError(`${modelId} needs a first-frame image or a first video clip to continue.`, errors_1.USER_FACING_ERROR_CODES.CAPABILITY_MISMATCH);
419
+ }
198
420
  const input = {};
199
421
  if (params.prompt)
200
422
  input.prompt = params.prompt;
201
423
  if (params.negativePrompt)
202
424
  input.negative_prompt = params.negativePrompt;
203
- if (isI2V) {
204
- // wan2.7-i2v moved to a media[] array ({type,url}) instead of the
205
- // legacy img_url/last_frame_url fields — confirmed against Alibaba's
206
- // "Wan 2.7 - image-to-video API" reference (2026-08-20); wan2.6-i2v(-flash)
207
- // and older versions still use img_url/last_frame_url.
208
- if (modelId.startsWith("wan2.7")) {
209
- const media = [
210
- { type: "first_frame", url: params.inputImageUrl },
211
- ];
212
- if (params.lastFrameImageUrl) {
213
- media.push({ type: "last_frame", url: params.lastFrameImageUrl });
214
- }
215
- input.media = media;
216
- }
217
- else {
218
- input.img_url = params.inputImageUrl;
219
- // wan2.6-i2v supports first-last-frame interpolation
220
- if (params.lastFrameImageUrl)
221
- input.last_frame_url = params.lastFrameImageUrl;
222
- }
223
- }
224
425
  const parameters = {
225
426
  duration: params.duration || 5,
226
427
  prompt_extend: params.promptOptimizer ?? true,
227
428
  };
228
- // T2V requires explicit size; I2V derives dimensions from the input image.
229
- if (!isI2V) {
429
+ if (isI2V && isWan27) {
430
+ // wan2.7-i2v: everything in media[] ({type,url}); each type at most once.
431
+ // first_frame [+ last_frame] [+ driving_audio] or first_clip [+ last_frame]
432
+ // Output aspect follows the first frame / clip, so no ratio.
433
+ const media = [];
434
+ if (params.inputVideoUrl) {
435
+ media.push({ type: "first_clip", url: params.inputVideoUrl });
436
+ }
437
+ else {
438
+ media.push({ type: "first_frame", url: params.inputImageUrl });
439
+ }
440
+ if (params.lastFrameImageUrl) {
441
+ media.push({ type: "last_frame", url: params.lastFrameImageUrl });
442
+ }
443
+ if (params.inputAudioUrl && !params.inputVideoUrl) {
444
+ media.push({ type: "driving_audio", url: params.inputAudioUrl });
445
+ }
446
+ input.media = media;
447
+ if (resolutionTier)
448
+ parameters.resolution = resolutionTier;
449
+ }
450
+ else if (isI2V) {
451
+ // wan2.6 and older: img_url / last_frame_url, `resolution` tier.
452
+ input.img_url = params.inputImageUrl;
453
+ // REMOVED FOR v2 — the legacy I2V API has no last_frame_url (only wan2.7 does FLF):
454
+ // if (params.lastFrameImageUrl) input.last_frame_url = params.lastFrameImageUrl;
455
+ if (params.inputAudioUrl)
456
+ input.audio_url = params.inputAudioUrl;
457
+ if (resolutionTier)
458
+ parameters.resolution = resolutionTier;
459
+ }
460
+ else if (isWan27) {
461
+ // wan2.7-t2v: resolution + ratio replace the pixel `size`.
462
+ if (params.inputAudioUrl)
463
+ input.audio_url = params.inputAudioUrl;
464
+ if (resolutionTier)
465
+ parameters.resolution = resolutionTier;
466
+ if (params.aspectRatio)
467
+ parameters.ratio = params.aspectRatio;
468
+ }
469
+ else {
470
+ // wan2.6-t2v and older T2V: pixel `size` ("1920*1080").
471
+ if (params.inputAudioUrl)
472
+ input.audio_url = params.inputAudioUrl;
230
473
  const size = (0, helpers_1.getAlibabaDimensions)(params.resolution, params.aspectRatio);
231
474
  if (!size) {
232
475
  throw new Error(`Invalid resolution/aspect ratio combination: ${params.resolution} ${params.aspectRatio}`);
233
476
  }
234
477
  parameters.size = size;
235
478
  }
479
+ if (params.audio !== undefined && modelId === "wan2.6-i2v-flash") {
480
+ parameters.audio = params.audio; // silent video is half price
481
+ }
236
482
  if (params.seed !== undefined)
237
483
  parameters.seed = params.seed;
238
484
  if (params.watermark !== undefined)
239
485
  parameters.watermark = params.watermark;
240
- const body = { model: modelId, input, parameters };
486
+ return this.submitVideo({ model: modelId, input, parameters });
487
+ }
488
+ async submitVideo(body) {
241
489
  const result = await this.request(body, "POST", ENDPOINT_VIDEO_ASYNC, true);
242
490
  // DashScope returns { output: { task_id, ... } } on async submit.
243
491
  const taskId = result?.output?.task_id || result?.request_id;
@@ -341,7 +589,10 @@ class AlibabaService extends baseAiGenProvider_service_1.BaseAiGenProviderServic
341
589
  ...(params.inputImageUrls ?? []),
342
590
  ];
343
591
  const isEdit = modelId.startsWith("qwen-image-edit") ||
344
- (refs.length > 0 && (modelId.startsWith("wan2.7-image") || modelId.startsWith("qwen-image-2")));
592
+ (refs.length > 0 &&
593
+ (modelId.startsWith("wan2.7-image") ||
594
+ modelId.startsWith("qwen-image-2") ||
595
+ modelId.startsWith("qwen-image-3")));
345
596
  if (isEdit && !refs.length) {
346
597
  throw new Error(`${modelId}: edit/reference mode requires at least one input image`);
347
598
  }
@@ -406,6 +657,12 @@ class AlibabaService extends baseAiGenProvider_service_1.BaseAiGenProviderServic
406
657
  parameters.prompt_extend = params.promptOptimizer;
407
658
  if (params.seed !== undefined)
408
659
  parameters.seed = params.seed;
660
+ // Qwen-Image 2.x / 3.x return up to 6 images per call. `n` was never sent,
661
+ // so a multi-image request was billed for N and returned one.
662
+ if ((modelId.startsWith("qwen-image-2") || modelId.startsWith("qwen-image-3")) &&
663
+ (params.numImages ?? 1) > 1) {
664
+ parameters.n = Math.min(6, params.numImages);
665
+ }
409
666
  // `size` is WIDTH*HEIGHT pixels, never a ratio string — see
410
667
  // resolveDashScopeSize. An explicit imageSize (already in pixel form) wins.
411
668
  if (params.aspectRatio) {
@@ -475,11 +732,34 @@ class AlibabaService extends baseAiGenProvider_service_1.BaseAiGenProviderServic
475
732
  const urls = results.map((r) => r.url).filter((u) => !!u);
476
733
  return { urls, taskId };
477
734
  }
478
- getCreditUsed({ modelKey, resolution, aspectRatio, duration, multiClip = false, numImages = 1, creditFloor, }) {
735
+ getCreditUsed({ modelKey, resolution, aspectRatio, duration, multiClip = false, numImages = 1, creditFloor, hasInputVideo, inputVideoDurationSec, audio, numInputImages, }) {
479
736
  const modelConfig = aiModels_1.aiModelConfigs[modelKey];
480
- // Image-gen — flat per-image cost (Kolors / Qwen-Image).
737
+ // Wan 3.0 / HappyHorse — per second by output resolution, plus input
738
+ // video seconds where the model bills them (Wan 3.0 references, video edit).
739
+ const perSecond = modelConfig?.cost?.perSecondByResolution;
740
+ if (perSecond) {
741
+ const res = resolution && resolution.toLowerCase() in perSecond
742
+ ? resolution.toLowerCase()
743
+ : String(modelConfig.fields.resolution?.default ?? "");
744
+ const silent = modelConfig.cost?.silentPerSecondByResolution;
745
+ const rate = (audio === false ? silent?.[res] : undefined) ?? perSecond[res];
746
+ if (rate !== undefined) {
747
+ const seconds = duration ?? Number(modelConfig.fields.duration?.default ?? 5);
748
+ const videoInRate = modelConfig.cost?.inputVideoPerSecond?.[res] ?? 0;
749
+ // Some models cap billable input seconds (Wan 2.7 r2v: 5s total).
750
+ const maxIn = typeof modelConfig.cost?.inputVideoBillableMaxSec === "number"
751
+ ? modelConfig.cost.inputVideoBillableMaxSec
752
+ : Infinity;
753
+ // const videoIn = hasInputVideo ? videoInRate * (inputVideoDurationSec ?? 0) : 0; // REMOVED FOR v2 — uncapped
754
+ const videoIn = hasInputVideo ? videoInRate * Math.min(inputVideoDurationSec ?? 0, maxIn) : 0;
755
+ return (0, utils_1.getCreditsFromCost)(rate * seconds + videoIn, !multiClip);
756
+ }
757
+ }
758
+ // Image-gen — flat per-image cost (Kolors / Qwen-Image), plus input
759
+ // images where the model bills them (Qwen-Image 3.0: $0.003 each).
481
760
  if (modelConfig?.cost?.fixed !== undefined) {
482
- return (0, utils_1.getCreditsFromCost)(modelConfig.cost.fixed * Math.max(1, numImages), !multiClip, creditFloor);
761
+ const perInput = typeof modelConfig.cost.perInputImage === "number" ? modelConfig.cost.perInputImage : 0;
762
+ return (0, utils_1.getCreditsFromCost)(modelConfig.cost.fixed * Math.max(1, numImages) + perInput * (numInputImages ?? 0), !multiClip, creditFloor);
483
763
  }
484
764
  const dimensions = (0, helpers_1.getAlibabaDimensions)(resolution, aspectRatio);
485
765
  const cost = modelConfig.cost?.table?.[dimensions]?.[duration];
@@ -1,11 +1,39 @@
1
1
  import { BaseAiGenProviderService } from "../baseAiGenProvider.service";
2
- import { CreditUsageParams, VideoGenerationParams, VideoGenerationResult, VideoStatusParams, VideoStatusResult } from "../types";
2
+ import { CreditUsageParams, ImageGenerationParams, ImageGenerationResult, VideoGenerationParams, VideoGenerationResult, VideoStatusParams, VideoStatusResult } from "../types";
3
+ /**
4
+ * Explicit "WxH" for a ratio at a size tier: tier² pixels in the requested
5
+ * shape, floored to multiples of 16. Every tier's pixel count sits inside the
6
+ * range the models that offer it accept (e.g. 5.0 lite: [2560×1440, 4096²]),
7
+ * and flooring never drops below it, so any ratio in [1/16, 16] is valid —
8
+ * including ones a model's aspect list doesn't name (the agent's 4:5).
9
+ */
10
+ export declare function seedreamSize(aspectRatio: string, tier: string): string | undefined;
3
11
  export declare class ByteDanceService extends BaseAiGenProviderService {
4
12
  private readonly baseUrl;
5
13
  private sanitizePrompt;
6
14
  private isSeedance2;
7
15
  generateVideo(params: VideoGenerationParams): Promise<VideoGenerationResult>;
8
16
  checkVideoStatus({ task, outputFilename, outputFilePath, }: VideoStatusParams): Promise<VideoStatusResult>;
17
+ /**
18
+ * Seedream text-to-image / image-to-image. Three modes, each gated on the
19
+ * model declaring the field:
20
+ * - default: one request per output image, run in parallel, so numImages
21
+ * is exact.
22
+ * - `imageSet` (5.0 lite / 4.x): one `sequential_image_generation: "auto"`
23
+ * request returning a set of related images, at most numImages (the model
24
+ * picks the count; the worker refunds the unused share).
25
+ * - `layerDecomposition` (5.0 pro / flash): one input image → base image +
26
+ * up to 16 transparent PNG layers, returned with their stacking order and
27
+ * bounding boxes.
28
+ */
29
+ generateImage(params: ImageGenerationParams): Promise<ImageGenerationResult>;
30
+ /** Copy a layer decomposition's outputs into our storage, base image first. */
31
+ private storeSeedreamLayers;
32
+ /**
33
+ * One /images/generations call → its successful items (failed ones aren't
34
+ * billed) plus `usage.generated_images`, the count BytePlus bills.
35
+ */
36
+ private requestSeedream;
9
37
  getCreditUsed(params: CreditUsageParams): number;
10
38
  }
11
39
  //# sourceMappingURL=bytedance.service.d.ts.map
@@ -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;AAWlB,qBAAa,gBAAiB,SAAQ,wBAAwB;IAC5D,OAAO,CAAC,QAAQ,CAAC,OAAO,CACsD;IAI9E,OAAO,CAAC,cAAc;IAYtB,OAAO,CAAC,WAAW;IAIb,aAAa,CACjB,MAAM,EAAE,qBAAqB,GAC5B,OAAO,CAAC,qBAAqB,CAAC;IA2M3B,gBAAgB,CAAC,EACrB,IAAI,EACJ,cAAc,EACd,cAAyB,GAC1B,EAAE,iBAAiB,GAAG,OAAO,CAAC,iBAAiB,CAAC;IAqIjD,aAAa,CAAC,MAAM,EAAE,iBAAiB,GAAG,MAAM;CAyEjD"}
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;AA6BlB;;;;;;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;IAoP3B,gBAAgB,CAAC,EACrB,IAAI,EACJ,cAAc,EACd,cAAyB,GAC1B,EAAE,iBAAiB,GAAG,OAAO,CAAC,iBAAiB,CAAC;IAqIjD;;;;;;;;;;;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"}