@orangecatai/adgen-canvas 0.0.40 → 0.1.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/dist/dev/chunk-2K6ZAAHB.js +1535 -0
- package/dist/dev/chunk-2K6ZAAHB.js.map +7 -0
- package/dist/dev/{chunk-JWNDQ5YS.js → chunk-2Q5Z4UTF.js} +2 -2
- package/dist/dev/{chunk-JWNDQ5YS.js.map → chunk-2Q5Z4UTF.js.map} +1 -1
- package/dist/dev/{chunk-LIELFD5T.js → chunk-3WIRZ6S7.js} +6 -1
- package/dist/dev/chunk-3WIRZ6S7.js.map +7 -0
- package/dist/dev/{chunk-XWMZCKIQ.js → chunk-7XZBXIDZ.js} +1300 -1252
- package/dist/dev/chunk-7XZBXIDZ.js.map +7 -0
- package/dist/dev/chunk-AKA4GZ3R.js +62 -0
- package/dist/dev/chunk-AKA4GZ3R.js.map +7 -0
- package/dist/dev/chunk-LWUV2KR2.js +27 -0
- package/dist/dev/chunk-LWUV2KR2.js.map +7 -0
- package/dist/dev/chunk-T57LHRZV.js +2167 -0
- package/dist/dev/chunk-T57LHRZV.js.map +7 -0
- package/dist/dev/components/auto-resize/nativeResizeAgent-5XWJJ74W.js +1074 -0
- package/dist/dev/components/auto-resize/nativeResizeAgent-5XWJJ74W.js.map +7 -0
- package/dist/dev/data/{image-CHTDICZP.js → image-MTDE3WXB.js} +4 -3
- package/dist/dev/index.css +618 -27
- package/dist/dev/index.css.map +3 -3
- package/dist/dev/index.js +6121 -7617
- package/dist/dev/index.js.map +4 -4
- package/dist/dev/locales/{en-VCZQBJ2N.js → en-3S2H3QGO.js} +2 -2
- package/dist/dev/model-capabilities.js +47 -0
- package/dist/dev/model-capabilities.js.map +7 -0
- package/dist/dev/subset-shared.chunk.js +1 -1
- package/dist/dev/subset-worker.chunk.js +1 -1
- package/dist/prod/chunk-6YYSQB5E.js +1 -0
- package/dist/prod/{chunk-CLQPYXMS.js → chunk-PIM2DC2L.js} +3 -3
- package/dist/prod/chunk-POCDVKZK.js +142 -0
- package/dist/prod/chunk-PWBUVPCU.js +1 -0
- package/dist/prod/{chunk-VASPG36O.js → chunk-R4DNAEVN.js} +1 -1
- package/dist/prod/chunk-U2I2OXQ7.js +4 -0
- package/dist/prod/chunk-UR7GPHHO.js +1 -0
- package/dist/prod/components/auto-resize/nativeResizeAgent-45UNA3LO.js +5 -0
- package/dist/prod/data/image-U4RMOYQQ.js +1 -0
- package/dist/prod/index.css +1 -1
- package/dist/prod/index.js +68 -297
- package/dist/prod/locales/{en-ZZURI326.js → en-Z3CKUL4S.js} +1 -1
- package/dist/prod/model-capabilities.js +1 -0
- package/dist/prod/subset-shared.chunk.js +1 -1
- package/dist/prod/subset-worker.chunk.js +1 -1
- package/dist/types/common/src/utils.d.ts +4 -1
- package/dist/types/element/src/index.d.ts +3 -0
- package/dist/types/element/src/newElement.d.ts +2 -1
- package/dist/types/element/src/textHtml.d.ts +14 -0
- package/dist/types/element/src/textLayout.d.ts +97 -0
- package/dist/types/element/src/textStyleRanges.d.ts +62 -0
- package/dist/types/element/src/textWrapping.d.ts +27 -0
- package/dist/types/element/src/types.d.ts +35 -0
- package/dist/types/excalidraw/actions/actionFrame.d.ts +1 -0
- package/dist/types/excalidraw/actions/actionLinearEditor.d.ts +1 -0
- package/dist/types/excalidraw/actions/actionProperties.d.ts +50 -0
- package/dist/types/excalidraw/actions/index.d.ts +1 -1
- package/dist/types/excalidraw/actions/types.d.ts +1 -1
- package/dist/types/excalidraw/components/AIChatPanel.d.ts +18 -2
- package/dist/types/excalidraw/components/App.d.ts +6 -0
- package/dist/types/excalidraw/components/FontSizeInput.d.ts +19 -0
- package/dist/types/excalidraw/components/ImageGeneratorPanel.d.ts +946 -45
- package/dist/types/excalidraw/components/VideoGeneratorPanel.d.ts +67 -53
- package/dist/types/excalidraw/components/ai-chat/ActivityTimeline.d.ts +18 -0
- package/dist/types/excalidraw/components/ai-chat/activitySteps.d.ts +106 -0
- package/dist/types/excalidraw/components/ai-chat/agentLoop.d.ts +66 -3
- package/dist/types/excalidraw/components/ai-chat/canvasTools.d.ts +23 -8
- package/dist/types/excalidraw/components/ai-chat/htmlToExcalidraw.d.ts +3 -0
- package/dist/types/excalidraw/components/ai-chat/openRouterStream.d.ts +13 -0
- package/dist/types/excalidraw/components/auto-resize/ResizeActivityCard.d.ts +7 -0
- package/dist/types/excalidraw/components/auto-resize/autoResizeEngine.d.ts +5 -2
- package/dist/types/excalidraw/components/auto-resize/autoResizeStore.d.ts +23 -0
- package/dist/types/excalidraw/components/auto-resize/imageBounds.d.ts +11 -0
- package/dist/types/excalidraw/components/auto-resize/nativeResizeAgent.d.ts +5 -0
- package/dist/types/excalidraw/components/auto-resize/resizeFitting.d.ts +7 -0
- package/dist/types/excalidraw/components/auto-resize/resizeLayout.d.ts +33 -0
- package/dist/types/excalidraw/components/icons.d.ts +6 -0
- package/dist/types/excalidraw/components/template-builder/TemplateBuilderTray.d.ts +1 -0
- package/dist/types/excalidraw/index.d.ts +9 -0
- package/dist/types/excalidraw/modelCapabilities/index.d.ts +17 -0
- package/dist/types/excalidraw/modelCapabilities/registry.image.d.ts +2 -0
- package/dist/types/excalidraw/modelCapabilities/registry.video.d.ts +2 -0
- package/dist/types/excalidraw/modelCapabilities/shared.d.ts +24 -0
- package/dist/types/excalidraw/modelCapabilities/types.d.ts +276 -0
- package/dist/types/excalidraw/modelCapabilities/validate.d.ts +48 -0
- package/dist/types/excalidraw/types.d.ts +4 -4
- package/dist/types/excalidraw/utils/imageApi.d.ts +3 -0
- package/dist/types/excalidraw/utils/referenceBudget.d.ts +12 -23
- package/dist/types/excalidraw/utils/videoApi.d.ts +21 -1
- package/dist/types/excalidraw/wysiwyg/richTextEditable.d.ts +36 -0
- package/dist/types/excalidraw/wysiwyg/textSelectionStyles.d.ts +32 -0
- package/dist/types/excalidraw/wysiwyg/textWysiwyg.d.ts +6 -2
- package/package.json +10 -4
- package/dist/dev/chunk-LIELFD5T.js.map +0 -7
- package/dist/dev/chunk-XWMZCKIQ.js.map +0 -7
- package/dist/prod/chunk-QG2AQCPN.js +0 -4
- package/dist/prod/data/image-ZEYF7NUD.js +0 -1
- /package/dist/dev/data/{image-CHTDICZP.js.map → image-MTDE3WXB.js.map} +0 -0
- /package/dist/dev/locales/{en-VCZQBJ2N.js.map → en-3S2H3QGO.js.map} +0 -0
|
@@ -0,0 +1,1535 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ADAPTIVE_ASPECT_RATIO,
|
|
3
|
+
MAX_USER_REFERENCE_IMAGES,
|
|
4
|
+
RESOLUTION_RANK
|
|
5
|
+
} from "./chunk-LWUV2KR2.js";
|
|
6
|
+
|
|
7
|
+
// modelCapabilities/shared.ts
|
|
8
|
+
var MB = 1024 * 1024;
|
|
9
|
+
var NO_FRAMES = {
|
|
10
|
+
start: { supported: false, maxImages: 0 },
|
|
11
|
+
end: { supported: false, maxImages: 0 },
|
|
12
|
+
allowsStartAndEndTogether: false,
|
|
13
|
+
endFrameRequiresStartFrame: false
|
|
14
|
+
};
|
|
15
|
+
var unchecked = {
|
|
16
|
+
maxImageBytes: null,
|
|
17
|
+
minEdgePx: null,
|
|
18
|
+
maxEdgePx: null,
|
|
19
|
+
inputPixels: null,
|
|
20
|
+
inputAspectRatioRange: null
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
// modelCapabilities/registry.image.ts
|
|
24
|
+
var GEMINI_IMAGE_MIMES = [
|
|
25
|
+
"image/png",
|
|
26
|
+
"image/jpeg",
|
|
27
|
+
"image/webp",
|
|
28
|
+
"image/heic",
|
|
29
|
+
"image/heif"
|
|
30
|
+
];
|
|
31
|
+
var OPENAI_IMAGE_MIMES = ["image/png", "image/webp", "image/jpeg"];
|
|
32
|
+
var SEEDREAM_IMAGE_MIMES = [
|
|
33
|
+
"image/jpeg",
|
|
34
|
+
"image/png",
|
|
35
|
+
"image/webp",
|
|
36
|
+
"image/bmp",
|
|
37
|
+
"image/tiff",
|
|
38
|
+
"image/gif",
|
|
39
|
+
"image/heic",
|
|
40
|
+
"image/heif"
|
|
41
|
+
];
|
|
42
|
+
var XAI_IMAGE_MIMES = ["image/jpeg", "image/png", "image/webp"];
|
|
43
|
+
var GEMINI_IMAGE_DOCS = [
|
|
44
|
+
"https://ai.google.dev/gemini-api/docs/image-generation",
|
|
45
|
+
"https://ai.google.dev/gemini-api/docs/image-understanding"
|
|
46
|
+
];
|
|
47
|
+
var GEMINI_ASPECT_RATIOS = [
|
|
48
|
+
"21:9",
|
|
49
|
+
"16:9",
|
|
50
|
+
"4:3",
|
|
51
|
+
"3:2",
|
|
52
|
+
"1:1",
|
|
53
|
+
"9:16",
|
|
54
|
+
"3:4",
|
|
55
|
+
"2:3",
|
|
56
|
+
"5:4",
|
|
57
|
+
"4:5",
|
|
58
|
+
"4:1",
|
|
59
|
+
"1:4",
|
|
60
|
+
"8:1",
|
|
61
|
+
"1:8"
|
|
62
|
+
];
|
|
63
|
+
var GEMINI_ASSETS = {
|
|
64
|
+
imageMimeTypes: GEMINI_IMAGE_MIMES,
|
|
65
|
+
// No PER-FILE cap is documented. The 20 MB figure is the documented
|
|
66
|
+
// aggregate inline-request cap, which necessarily bounds a single image too,
|
|
67
|
+
// so this is a derivation rather than a guess.
|
|
68
|
+
maxImageBytes: 20 * MB,
|
|
69
|
+
minEdgePx: null,
|
|
70
|
+
maxEdgePx: null,
|
|
71
|
+
inputPixels: null,
|
|
72
|
+
inputAspectRatioRange: null,
|
|
73
|
+
maxRequestBytes: 20 * MB
|
|
74
|
+
};
|
|
75
|
+
var GEMINI_UNKNOWN_NOTES = {
|
|
76
|
+
"controls.aspectRatios": "Docs state 14 supported ratios but enumerate only the newly added ones. Keeping all 14; verify against a live call before trusting the tail.",
|
|
77
|
+
"assets.minEdgePx": "No input dimension floor documented. Left unchecked rather than inventing one, which would reject valid images.",
|
|
78
|
+
"assets.maxEdgePx": "No input dimension ceiling documented; docs only note that large images are tiled into 768x768 tiles."
|
|
79
|
+
};
|
|
80
|
+
var geminiFlashImage = {
|
|
81
|
+
modelId: "google/gemini-3.1-flash-image",
|
|
82
|
+
modality: "image",
|
|
83
|
+
verified: {
|
|
84
|
+
sources: [
|
|
85
|
+
...GEMINI_IMAGE_DOCS,
|
|
86
|
+
"https://ai.google.dev/gemini-api/docs/models/gemini-3.1-flash-image"
|
|
87
|
+
],
|
|
88
|
+
checkedOn: "2026-09-11"
|
|
89
|
+
},
|
|
90
|
+
unknownFields: [
|
|
91
|
+
"controls.aspectRatios",
|
|
92
|
+
"assets.minEdgePx",
|
|
93
|
+
"assets.maxEdgePx"
|
|
94
|
+
],
|
|
95
|
+
unknownNotes: GEMINI_UNKNOWN_NOTES,
|
|
96
|
+
references: {
|
|
97
|
+
supported: true,
|
|
98
|
+
grouping: "flat",
|
|
99
|
+
// "Up to 10 images of objects" + "Up to 4 images of characters".
|
|
100
|
+
maxImages: 14,
|
|
101
|
+
maxVideos: 0,
|
|
102
|
+
maxAudios: 0,
|
|
103
|
+
acceptsImageAndVideoReferences: false,
|
|
104
|
+
canCombineWithStartFrame: false,
|
|
105
|
+
canCombineWithEndFrame: false
|
|
106
|
+
},
|
|
107
|
+
frames: NO_FRAMES,
|
|
108
|
+
assets: GEMINI_ASSETS,
|
|
109
|
+
controls: {
|
|
110
|
+
aspectRatioMode: "settable",
|
|
111
|
+
aspectRatios: GEMINI_ASPECT_RATIOS,
|
|
112
|
+
// "support for 0.5K, 2K and 4K, default 1K"
|
|
113
|
+
resolutions: ["0.5K", "1K", "2K", "4K"],
|
|
114
|
+
audio: "none",
|
|
115
|
+
supportsNegativePrompt: false,
|
|
116
|
+
supportsSeed: false
|
|
117
|
+
},
|
|
118
|
+
constraints: []
|
|
119
|
+
};
|
|
120
|
+
var geminiFlashLiteImage = {
|
|
121
|
+
...geminiFlashImage,
|
|
122
|
+
modelId: "google/gemini-3.1-flash-lite-image",
|
|
123
|
+
verified: {
|
|
124
|
+
sources: [
|
|
125
|
+
...GEMINI_IMAGE_DOCS,
|
|
126
|
+
"https://ai.google.dev/gemini-api/docs/models/gemini-3.1-flash-lite-image"
|
|
127
|
+
],
|
|
128
|
+
checkedOn: "2026-09-11"
|
|
129
|
+
},
|
|
130
|
+
references: {
|
|
131
|
+
...geminiFlashImage.references,
|
|
132
|
+
// "Up to 14 images of objects". Unlike Flash, no character-reference
|
|
133
|
+
// capability is documented for Flash-Lite.
|
|
134
|
+
maxImages: 14
|
|
135
|
+
},
|
|
136
|
+
controls: {
|
|
137
|
+
...geminiFlashImage.controls,
|
|
138
|
+
// "Optimized for lower resolutions of 1K (1024x1024px)";
|
|
139
|
+
// "(Note: 2K and 4K are unsupported)".
|
|
140
|
+
resolutions: ["1K"]
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
var OPENAI_IMAGE_DOCS = [
|
|
144
|
+
"https://developers.openai.com/api/reference/python/resources/images/methods/edit",
|
|
145
|
+
"https://developers.openai.com/api/docs/guides/image-prompting",
|
|
146
|
+
"https://developers.openai.com/api/docs/guides/image-generation"
|
|
147
|
+
];
|
|
148
|
+
var OPENAI_ASPECT_RATIOS = [
|
|
149
|
+
"21:9",
|
|
150
|
+
"16:9",
|
|
151
|
+
"4:3",
|
|
152
|
+
"3:2",
|
|
153
|
+
"1:1",
|
|
154
|
+
"9:16",
|
|
155
|
+
"3:4",
|
|
156
|
+
"2:3"
|
|
157
|
+
];
|
|
158
|
+
var gptImage25Flare = {
|
|
159
|
+
modelId: "openai/gpt-image-2.5-flare",
|
|
160
|
+
modality: "image",
|
|
161
|
+
verified: { sources: OPENAI_IMAGE_DOCS, checkedOn: "2026-09-11" },
|
|
162
|
+
unknownFields: ["assets.maxRequestBytes"],
|
|
163
|
+
unknownNotes: {
|
|
164
|
+
"assets.maxRequestBytes": "Only the per-image 50 MB and per-mask 4 MB caps are documented; no whole-request cap."
|
|
165
|
+
},
|
|
166
|
+
references: {
|
|
167
|
+
supported: true,
|
|
168
|
+
grouping: "flat",
|
|
169
|
+
// "You can provide up to 16 images."
|
|
170
|
+
maxImages: 16,
|
|
171
|
+
maxVideos: 0,
|
|
172
|
+
maxAudios: 0,
|
|
173
|
+
acceptsImageAndVideoReferences: false,
|
|
174
|
+
canCombineWithStartFrame: false,
|
|
175
|
+
canCombineWithEndFrame: false
|
|
176
|
+
},
|
|
177
|
+
frames: NO_FRAMES,
|
|
178
|
+
assets: {
|
|
179
|
+
imageMimeTypes: OPENAI_IMAGE_MIMES,
|
|
180
|
+
// "each image should be a png, webp, or jpg file less than 50MB"
|
|
181
|
+
maxImageBytes: 50 * MB,
|
|
182
|
+
minEdgePx: null,
|
|
183
|
+
maxEdgePx: null,
|
|
184
|
+
inputPixels: null,
|
|
185
|
+
inputAspectRatioRange: null,
|
|
186
|
+
maxRequestBytes: null
|
|
187
|
+
},
|
|
188
|
+
controls: {
|
|
189
|
+
aspectRatioMode: "settable",
|
|
190
|
+
aspectRatios: OPENAI_ASPECT_RATIOS,
|
|
191
|
+
resolutions: ["1K", "2K"],
|
|
192
|
+
audio: "none",
|
|
193
|
+
supportsNegativePrompt: false,
|
|
194
|
+
supportsSeed: false,
|
|
195
|
+
outputPixels: { min: 655360, max: 8294400 },
|
|
196
|
+
outputEdge: { max: 3840, multipleOf: 16 },
|
|
197
|
+
maxOutputEdgeRatio: 3
|
|
198
|
+
},
|
|
199
|
+
constraints: []
|
|
200
|
+
};
|
|
201
|
+
var gptImage25Sunburst = {
|
|
202
|
+
...gptImage25Flare,
|
|
203
|
+
modelId: "openai/gpt-image-2.5-sunburst"
|
|
204
|
+
};
|
|
205
|
+
var SEEDREAM_DOCS = [
|
|
206
|
+
"https://docs.byteplus.com/en/docs/ModelArk/1541523",
|
|
207
|
+
"https://docs.volcengine.com/docs/82379/1541523"
|
|
208
|
+
];
|
|
209
|
+
var SEEDREAM_ASSETS = {
|
|
210
|
+
imageMimeTypes: SEEDREAM_IMAGE_MIMES,
|
|
211
|
+
// "Size: Up to 30 MB"
|
|
212
|
+
maxImageBytes: 30 * MB,
|
|
213
|
+
// "Width and height (px): > 14"
|
|
214
|
+
minEdgePx: 15,
|
|
215
|
+
maxEdgePx: null,
|
|
216
|
+
// "Total pixels: [196, 6000x6000 (36,000,000)]" -- the product is bounded,
|
|
217
|
+
// not either edge individually.
|
|
218
|
+
inputPixels: { min: 196, max: 36e6 },
|
|
219
|
+
// "Aspect ratio (width / height): [1/16, 16]"
|
|
220
|
+
inputAspectRatioRange: [1 / 16, 16],
|
|
221
|
+
maxRequestBytes: null
|
|
222
|
+
};
|
|
223
|
+
var SEEDREAM_ASPECT_RATIOS = [
|
|
224
|
+
"21:9",
|
|
225
|
+
"16:9",
|
|
226
|
+
"4:3",
|
|
227
|
+
"3:2",
|
|
228
|
+
"1:1",
|
|
229
|
+
"9:16",
|
|
230
|
+
"3:4",
|
|
231
|
+
"2:3",
|
|
232
|
+
"5:4",
|
|
233
|
+
"4:5"
|
|
234
|
+
];
|
|
235
|
+
var seedream50Lite = {
|
|
236
|
+
modelId: "bytedance-seed/seedream-5-0-lite",
|
|
237
|
+
modality: "image",
|
|
238
|
+
verified: { sources: SEEDREAM_DOCS, checkedOn: "2026-09-11" },
|
|
239
|
+
unknownFields: ["assets.maxEdgePx", "assets.maxRequestBytes"],
|
|
240
|
+
unknownNotes: {
|
|
241
|
+
"assets.maxEdgePx": "Docs bound total pixels rather than either edge; see assets.inputPixels.",
|
|
242
|
+
"assets.maxRequestBytes": "No whole-request cap documented for the image API."
|
|
243
|
+
},
|
|
244
|
+
references: {
|
|
245
|
+
supported: true,
|
|
246
|
+
grouping: "flat",
|
|
247
|
+
// "Seedream 5.0 lite, 4.5, and 4.0 support up to 14 reference images."
|
|
248
|
+
maxImages: 14,
|
|
249
|
+
maxVideos: 0,
|
|
250
|
+
maxAudios: 0,
|
|
251
|
+
acceptsImageAndVideoReferences: false,
|
|
252
|
+
canCombineWithStartFrame: false,
|
|
253
|
+
canCombineWithEndFrame: false
|
|
254
|
+
},
|
|
255
|
+
frames: NO_FRAMES,
|
|
256
|
+
assets: SEEDREAM_ASSETS,
|
|
257
|
+
controls: {
|
|
258
|
+
aspectRatioMode: "settable",
|
|
259
|
+
aspectRatios: SEEDREAM_ASPECT_RATIOS,
|
|
260
|
+
// Lite and Pro have DISJOINT resolution ladders -- they are not drop-in
|
|
261
|
+
// substitutes for one another.
|
|
262
|
+
resolutions: ["2K", "3K", "4K"],
|
|
263
|
+
audio: "none",
|
|
264
|
+
supportsNegativePrompt: false,
|
|
265
|
+
supportsSeed: false,
|
|
266
|
+
// "Total pixels range: [2560x1440(3,686,400), 4096x4096(16,777,216)]"
|
|
267
|
+
outputPixels: { min: 3686400, max: 16777216 }
|
|
268
|
+
},
|
|
269
|
+
constraints: [],
|
|
270
|
+
providerCaveats: [
|
|
271
|
+
"Input reference images plus generated images must total 15 or fewer."
|
|
272
|
+
]
|
|
273
|
+
};
|
|
274
|
+
var seedream50Pro = {
|
|
275
|
+
...seedream50Lite,
|
|
276
|
+
modelId: "bytedance-seed/seedream-5-0-pro",
|
|
277
|
+
references: {
|
|
278
|
+
...seedream50Lite.references,
|
|
279
|
+
// "Seedream 5.0 pro supports up to 10 reference images."
|
|
280
|
+
maxImages: 10
|
|
281
|
+
},
|
|
282
|
+
controls: {
|
|
283
|
+
...seedream50Lite.controls,
|
|
284
|
+
resolutions: ["1K", "1.5K", "2K"],
|
|
285
|
+
// "Total pixels range: [1280x720(921,600), 2048x2048x1.1025(4,624,220)]"
|
|
286
|
+
outputPixels: { min: 921600, max: 4624220 }
|
|
287
|
+
},
|
|
288
|
+
providerCaveats: [
|
|
289
|
+
"Layer decomposition accepts exactly one input image and yields at most 16 layers.",
|
|
290
|
+
"Sequential image generation and streaming are not supported on Pro."
|
|
291
|
+
]
|
|
292
|
+
};
|
|
293
|
+
var grokImagineImage20 = {
|
|
294
|
+
modelId: "x-ai/grok-imagine-image-2.0",
|
|
295
|
+
modality: "image",
|
|
296
|
+
verified: {
|
|
297
|
+
sources: [
|
|
298
|
+
"https://docs.x.ai/developers/rest-api-reference/inference/images",
|
|
299
|
+
"https://docs.x.ai/developers/model-capabilities/images/multi-image-editing"
|
|
300
|
+
],
|
|
301
|
+
checkedOn: "2026-09-11"
|
|
302
|
+
},
|
|
303
|
+
unknownFields: [
|
|
304
|
+
"assets.maxImageBytes",
|
|
305
|
+
"assets.minEdgePx",
|
|
306
|
+
"assets.maxEdgePx",
|
|
307
|
+
"assets.inputPixels"
|
|
308
|
+
],
|
|
309
|
+
unknownNotes: {
|
|
310
|
+
"assets.maxImageBytes": "xAI's documented 20 MiB / jpg-png limit is scoped to CHAT vision models and contradicts Imagine's documented WebP support, so it is deliberately not carried over. Left unchecked; the transport guard binds first anyway.",
|
|
311
|
+
"assets.minEdgePx": "No input dimension floor documented.",
|
|
312
|
+
"assets.maxEdgePx": "No input dimension ceiling documented.",
|
|
313
|
+
"assets.inputPixels": "No input pixel bound documented."
|
|
314
|
+
},
|
|
315
|
+
references: {
|
|
316
|
+
supported: true,
|
|
317
|
+
grouping: "flat",
|
|
318
|
+
// "Multi-image editing supports up to 5 source images in a single request"
|
|
319
|
+
maxImages: 5,
|
|
320
|
+
maxVideos: 0,
|
|
321
|
+
maxAudios: 0,
|
|
322
|
+
acceptsImageAndVideoReferences: false,
|
|
323
|
+
canCombineWithStartFrame: false,
|
|
324
|
+
canCombineWithEndFrame: false
|
|
325
|
+
},
|
|
326
|
+
frames: NO_FRAMES,
|
|
327
|
+
assets: {
|
|
328
|
+
imageMimeTypes: XAI_IMAGE_MIMES,
|
|
329
|
+
...unchecked,
|
|
330
|
+
maxRequestBytes: null
|
|
331
|
+
},
|
|
332
|
+
controls: {
|
|
333
|
+
aspectRatioMode: "settable",
|
|
334
|
+
aspectRatios: [
|
|
335
|
+
"16:9",
|
|
336
|
+
"4:3",
|
|
337
|
+
"3:2",
|
|
338
|
+
"1:1",
|
|
339
|
+
"9:16",
|
|
340
|
+
"3:4",
|
|
341
|
+
"2:3",
|
|
342
|
+
"5:4",
|
|
343
|
+
"4:5"
|
|
344
|
+
],
|
|
345
|
+
resolutions: ["1K", "2K"],
|
|
346
|
+
audio: "none",
|
|
347
|
+
supportsNegativePrompt: false,
|
|
348
|
+
supportsSeed: false
|
|
349
|
+
},
|
|
350
|
+
constraints: [],
|
|
351
|
+
providerCaveats: [
|
|
352
|
+
"The `images[]` array is mutually exclusive with the single `image` field.",
|
|
353
|
+
"By default the output ratio follows the first input image unless aspect_ratio is set."
|
|
354
|
+
]
|
|
355
|
+
};
|
|
356
|
+
var maiImage26Flash = {
|
|
357
|
+
modelId: "microsoft/mai-image-2.6-flash",
|
|
358
|
+
modality: "image",
|
|
359
|
+
verified: {
|
|
360
|
+
sources: [
|
|
361
|
+
"https://learn.microsoft.com/en-us/azure/foundry/foundry-models/how-to/use-foundry-models-mai-image",
|
|
362
|
+
"https://microsoft.ai/pdf/MAI-Image-2.6-Model-Card.pdf"
|
|
363
|
+
],
|
|
364
|
+
checkedOn: "2026-09-11"
|
|
365
|
+
},
|
|
366
|
+
unknownFields: [
|
|
367
|
+
"references.maxImages",
|
|
368
|
+
"assets.maxImageBytes",
|
|
369
|
+
"assets.minEdgePx",
|
|
370
|
+
"assets.maxEdgePx",
|
|
371
|
+
"assets.inputPixels"
|
|
372
|
+
],
|
|
373
|
+
unknownNotes: {
|
|
374
|
+
"references.maxImages": "The edits endpoint documents a single `image` multipart field and no multi-image cap. Conservative default of 1 -- the tightest limit of any registered model, and the reason Quick Edit must not spend the slot on a user reference.",
|
|
375
|
+
"assets.maxImageBytes": "No input file size cap documented.",
|
|
376
|
+
"assets.minEdgePx": "The 768 px floor is documented for output width/height, not for input images.",
|
|
377
|
+
"assets.maxEdgePx": "No input dimension ceiling documented.",
|
|
378
|
+
"assets.inputPixels": "No input pixel bound documented."
|
|
379
|
+
},
|
|
380
|
+
references: {
|
|
381
|
+
supported: true,
|
|
382
|
+
grouping: "flat",
|
|
383
|
+
maxImages: 1,
|
|
384
|
+
maxVideos: 0,
|
|
385
|
+
maxAudios: 0,
|
|
386
|
+
acceptsImageAndVideoReferences: false,
|
|
387
|
+
canCombineWithStartFrame: false,
|
|
388
|
+
canCombineWithEndFrame: false
|
|
389
|
+
},
|
|
390
|
+
frames: NO_FRAMES,
|
|
391
|
+
assets: {
|
|
392
|
+
imageMimeTypes: ["image/jpeg", "image/png"],
|
|
393
|
+
...unchecked,
|
|
394
|
+
maxRequestBytes: null
|
|
395
|
+
},
|
|
396
|
+
controls: {
|
|
397
|
+
aspectRatioMode: "settable",
|
|
398
|
+
// 21:9 is deliberately absent: at the documented 768 px per-side minimum
|
|
399
|
+
// it would need 1792x768 = 1,376,256 pixels, over the 1,048,576 cap.
|
|
400
|
+
aspectRatios: ["1:1", "16:9", "3:2", "4:3", "3:4", "2:3", "9:16"],
|
|
401
|
+
resolutions: ["1K"],
|
|
402
|
+
audio: "none",
|
|
403
|
+
supportsNegativePrompt: false,
|
|
404
|
+
supportsSeed: false,
|
|
405
|
+
// Microsoft's two first-party docs conflict: Learn says 1,048,576 and
|
|
406
|
+
// documents the 400 that enforces it; the model card PDF says 2,359,296.
|
|
407
|
+
// Take the lower, demonstrably enforced figure.
|
|
408
|
+
outputPixels: { min: 768 * 768, max: 1048576 },
|
|
409
|
+
outputEdge: { min: 768 }
|
|
410
|
+
},
|
|
411
|
+
constraints: [],
|
|
412
|
+
providerCaveats: [
|
|
413
|
+
"Output is always PNG, and exactly one image per request.",
|
|
414
|
+
"Learn documents a 1,048,576 output pixel cap; the model card PDF claims 2,359,296. The lower figure is enforced."
|
|
415
|
+
]
|
|
416
|
+
};
|
|
417
|
+
var IMAGE_MODEL_CAPABILITIES = [
|
|
418
|
+
geminiFlashImage,
|
|
419
|
+
geminiFlashLiteImage,
|
|
420
|
+
gptImage25Flare,
|
|
421
|
+
gptImage25Sunburst,
|
|
422
|
+
seedream50Lite,
|
|
423
|
+
seedream50Pro,
|
|
424
|
+
grokImagineImage20,
|
|
425
|
+
maiImage26Flash
|
|
426
|
+
];
|
|
427
|
+
|
|
428
|
+
// modelCapabilities/registry.video.ts
|
|
429
|
+
var VEO_DOCS = ["https://ai.google.dev/gemini-api/docs/veo"];
|
|
430
|
+
var VEO_ASSET_UNKNOWNS = [
|
|
431
|
+
"assets.imageMimeTypes",
|
|
432
|
+
"assets.maxImageBytes",
|
|
433
|
+
"assets.minEdgePx",
|
|
434
|
+
"assets.maxEdgePx",
|
|
435
|
+
"assets.inputPixels",
|
|
436
|
+
"assets.inputAspectRatioRange"
|
|
437
|
+
];
|
|
438
|
+
var VEO_ASSETS = {
|
|
439
|
+
// Empty list means "do not check" -- see `unchecked` in ./shared.
|
|
440
|
+
imageMimeTypes: [],
|
|
441
|
+
...unchecked,
|
|
442
|
+
maxRequestBytes: null
|
|
443
|
+
};
|
|
444
|
+
var veo31Fast = {
|
|
445
|
+
modelId: "google/veo-3.1-fast",
|
|
446
|
+
modality: "video",
|
|
447
|
+
verified: {
|
|
448
|
+
sources: [
|
|
449
|
+
...VEO_DOCS,
|
|
450
|
+
"https://ai.google.dev/gemini-api/docs/models/veo-3.1-generate-preview"
|
|
451
|
+
],
|
|
452
|
+
checkedOn: "2026-09-11"
|
|
453
|
+
},
|
|
454
|
+
unknownFields: [
|
|
455
|
+
"references.canCombineWithStartFrame",
|
|
456
|
+
"references.canCombineWithEndFrame",
|
|
457
|
+
"controls.supportsNegativePrompt",
|
|
458
|
+
...VEO_ASSET_UNKNOWNS
|
|
459
|
+
],
|
|
460
|
+
unknownNotes: {
|
|
461
|
+
"references.canCombineWithStartFrame": "Google documents neither permission nor prohibition. The guide establishes that referenceImages accepts up to 3, that lastFrame requires image, and that duration must be 8 when using reference images -- but never whether references and frames may coexist. Defaulted to `false` so the combination is blocked as REFERENCE_FRAME_COMBINATION_UNVERIFIED rather than discovered by the provider after charging.",
|
|
462
|
+
"references.canCombineWithEndFrame": "Same gap as canCombineWithStartFrame.",
|
|
463
|
+
"controls.supportsNegativePrompt": "Absent from the Gemini API Veo parameter table, but Vertex accepts negativePrompt inside the google-vertex `parameters` block, which is how the host forwards it today. Defaulted to supported so validation does not reject a working passthrough; confirm against the Vertex reference.",
|
|
464
|
+
"assets.imageMimeTypes": "Not stated in the Gemini API Veo guide; the Vertex AI pages do not serve their body.",
|
|
465
|
+
"assets.maxImageBytes": "Not stated.",
|
|
466
|
+
"assets.minEdgePx": "Not stated.",
|
|
467
|
+
"assets.maxEdgePx": "Not stated.",
|
|
468
|
+
"assets.inputPixels": "Not stated.",
|
|
469
|
+
"assets.inputAspectRatioRange": "Not stated."
|
|
470
|
+
},
|
|
471
|
+
references: {
|
|
472
|
+
supported: true,
|
|
473
|
+
grouping: "flat",
|
|
474
|
+
// "up to three asset images of a single person, character, or product"
|
|
475
|
+
maxImages: 3,
|
|
476
|
+
maxVideos: 0,
|
|
477
|
+
maxAudios: 0,
|
|
478
|
+
acceptsImageAndVideoReferences: false,
|
|
479
|
+
canCombineWithStartFrame: false,
|
|
480
|
+
canCombineWithEndFrame: false
|
|
481
|
+
},
|
|
482
|
+
frames: {
|
|
483
|
+
start: { supported: true, maxImages: 1 },
|
|
484
|
+
end: { supported: true, maxImages: 1 },
|
|
485
|
+
allowsStartAndEndTogether: true,
|
|
486
|
+
// "Must be used in combination with the `image` parameter"
|
|
487
|
+
endFrameRequiresStartFrame: true
|
|
488
|
+
},
|
|
489
|
+
assets: VEO_ASSETS,
|
|
490
|
+
controls: {
|
|
491
|
+
aspectRatioMode: "settable",
|
|
492
|
+
aspectRatios: ["16:9", "9:16"],
|
|
493
|
+
resolutions: ["720p", "1080p", "4K"],
|
|
494
|
+
durationsSeconds: [4, 6, 8],
|
|
495
|
+
audio: "always-on",
|
|
496
|
+
supportsNegativePrompt: true,
|
|
497
|
+
// "It doesn't guarantee determinism, but slightly improves it"
|
|
498
|
+
supportsSeed: true
|
|
499
|
+
},
|
|
500
|
+
constraints: [
|
|
501
|
+
{
|
|
502
|
+
whenAny: [
|
|
503
|
+
{ kind: "hasReferences" },
|
|
504
|
+
{ kind: "resolutionIn", values: ["1080p", "4K"] }
|
|
505
|
+
],
|
|
506
|
+
requires: { kind: "durationMustBe", seconds: 8 },
|
|
507
|
+
message: "Veo 3.1 requires an 8 second duration when using reference images, 1080p or 4K."
|
|
508
|
+
}
|
|
509
|
+
],
|
|
510
|
+
providerCaveats: [
|
|
511
|
+
"personGeneration is restricted to allow_adult for image-to-video, interpolation and reference-image modes.",
|
|
512
|
+
"Video extension is 720p only."
|
|
513
|
+
]
|
|
514
|
+
};
|
|
515
|
+
var veo31Lite = {
|
|
516
|
+
modelId: "google/veo-3.1-lite",
|
|
517
|
+
modality: "video",
|
|
518
|
+
verified: {
|
|
519
|
+
sources: [
|
|
520
|
+
...VEO_DOCS,
|
|
521
|
+
"https://ai.google.dev/gemini-api/docs/models/veo-3.1-lite-generate-preview"
|
|
522
|
+
],
|
|
523
|
+
checkedOn: "2026-09-11"
|
|
524
|
+
},
|
|
525
|
+
unknownFields: [
|
|
526
|
+
"controls.supportsSeed",
|
|
527
|
+
"controls.supportsNegativePrompt",
|
|
528
|
+
...VEO_ASSET_UNKNOWNS
|
|
529
|
+
],
|
|
530
|
+
unknownNotes: {
|
|
531
|
+
"controls.supportsNegativePrompt": "Same Vertex passthrough as Veo 3.1 Fast \u2014 see that record's note.",
|
|
532
|
+
"controls.supportsSeed": "The guide's seed note refers to 'Veo 3 models' generically and is not confirmed per-model for Lite. Defaulted to unsupported so we never send a parameter the model may reject.",
|
|
533
|
+
"assets.imageMimeTypes": "Not stated.",
|
|
534
|
+
"assets.maxImageBytes": "Not stated.",
|
|
535
|
+
"assets.minEdgePx": "Not stated.",
|
|
536
|
+
"assets.maxEdgePx": "Not stated.",
|
|
537
|
+
"assets.inputPixels": "Not stated.",
|
|
538
|
+
"assets.inputAspectRatioRange": "Not stated."
|
|
539
|
+
},
|
|
540
|
+
references: {
|
|
541
|
+
// `referenceImages` is explicitly `n/a` for Veo 3.1 Lite.
|
|
542
|
+
supported: false,
|
|
543
|
+
grouping: "flat",
|
|
544
|
+
maxImages: 0,
|
|
545
|
+
maxVideos: 0,
|
|
546
|
+
maxAudios: 0,
|
|
547
|
+
acceptsImageAndVideoReferences: false,
|
|
548
|
+
canCombineWithStartFrame: false,
|
|
549
|
+
canCombineWithEndFrame: false
|
|
550
|
+
},
|
|
551
|
+
frames: veo31Fast.frames,
|
|
552
|
+
assets: VEO_ASSETS,
|
|
553
|
+
controls: {
|
|
554
|
+
aspectRatioMode: "settable",
|
|
555
|
+
aspectRatios: ["16:9", "9:16"],
|
|
556
|
+
// "Veo 3.1 Lite Preview does not support 4K outputs or Extension."
|
|
557
|
+
resolutions: ["720p", "1080p"],
|
|
558
|
+
durationsSeconds: [4, 6, 8],
|
|
559
|
+
audio: "always-on",
|
|
560
|
+
supportsNegativePrompt: true,
|
|
561
|
+
supportsSeed: false
|
|
562
|
+
},
|
|
563
|
+
constraints: [
|
|
564
|
+
{
|
|
565
|
+
whenAny: [{ kind: "resolutionIn", values: ["1080p"] }],
|
|
566
|
+
requires: { kind: "durationMustBe", seconds: 8 },
|
|
567
|
+
message: "Veo 3.1 Lite requires an 8 second duration for 1080p output."
|
|
568
|
+
}
|
|
569
|
+
]
|
|
570
|
+
};
|
|
571
|
+
var KLING_DOCS = [
|
|
572
|
+
"https://klingai.com/document-api/api/video/3-0-omni/image-to-video",
|
|
573
|
+
"https://klingai.com/document-api/api/video/3-0-omni/elements",
|
|
574
|
+
"https://klingai.com/document-api/guides/capability-map/video"
|
|
575
|
+
];
|
|
576
|
+
var klingBase = {
|
|
577
|
+
modality: "video",
|
|
578
|
+
verified: { sources: KLING_DOCS, checkedOn: "2026-09-11" },
|
|
579
|
+
unknownFields: ["assets.maxEdgePx", "assets.inputPixels"],
|
|
580
|
+
unknownNotes: {
|
|
581
|
+
"assets.maxEdgePx": "Only the 300 px floor is documented; no ceiling is stated.",
|
|
582
|
+
"assets.inputPixels": "No input pixel bound documented."
|
|
583
|
+
},
|
|
584
|
+
references: {
|
|
585
|
+
supported: true,
|
|
586
|
+
// Kling references are nested SUBJECTS, not a flat list: each Element is
|
|
587
|
+
// built from "at least 1 frontal reference image" plus "1-3 other
|
|
588
|
+
// reference images" that must differ from it.
|
|
589
|
+
grouping: "elements",
|
|
590
|
+
maxGroups: 3,
|
|
591
|
+
imagesPerGroup: { min: 2, max: 4 },
|
|
592
|
+
maxImages: 12,
|
|
593
|
+
maxVideos: 0,
|
|
594
|
+
maxAudios: 0,
|
|
595
|
+
acceptsImageAndVideoReferences: false,
|
|
596
|
+
canCombineWithStartFrame: true,
|
|
597
|
+
canCombineWithEndFrame: true
|
|
598
|
+
},
|
|
599
|
+
frames: {
|
|
600
|
+
start: { supported: true, maxImages: 1 },
|
|
601
|
+
end: { supported: true, maxImages: 1 },
|
|
602
|
+
allowsStartAndEndTogether: true,
|
|
603
|
+
// "Supports first-frame-to-video and first-and-last-frame-to-video
|
|
604
|
+
// generation; last-frame-only video generation is not supported."
|
|
605
|
+
endFrameRequiresStartFrame: true
|
|
606
|
+
},
|
|
607
|
+
assets: {
|
|
608
|
+
imageMimeTypes: ["image/jpeg", "image/png"],
|
|
609
|
+
// 50 MB on the current API; the legacy /v1/videos/image2video endpoint
|
|
610
|
+
// caps at 10 MB.
|
|
611
|
+
maxImageBytes: 50 * MB,
|
|
612
|
+
minEdgePx: 300,
|
|
613
|
+
maxEdgePx: null,
|
|
614
|
+
inputPixels: null,
|
|
615
|
+
// "The image aspect ratio must be between 1:2.5 and 2.5:1."
|
|
616
|
+
inputAspectRatioRange: [0.4, 2.5],
|
|
617
|
+
maxRequestBytes: null
|
|
618
|
+
},
|
|
619
|
+
constraints: [],
|
|
620
|
+
providerCaveats: [
|
|
621
|
+
"Image-to-video has no aspect_ratio parameter; the output ratio follows the first frame.",
|
|
622
|
+
"Prompt length must not exceed 3072 characters.",
|
|
623
|
+
"Multi-image reference-to-video (as distinct from Elements) is not supported on Kling 3.0."
|
|
624
|
+
]
|
|
625
|
+
};
|
|
626
|
+
var KLING_CONTROLS = {
|
|
627
|
+
// Text-to-video accepts an explicit ratio; image-to-video has no such
|
|
628
|
+
// parameter at all, so the control must disappear once a frame is attached.
|
|
629
|
+
aspectRatioMode: "notApplicableWithFrames",
|
|
630
|
+
aspectRatios: ["16:9", "9:16", "1:1"],
|
|
631
|
+
minDurationSeconds: 3,
|
|
632
|
+
maxDurationSeconds: 15,
|
|
633
|
+
// `audio` defaults to `off` on Kling, unlike every other toggle model here.
|
|
634
|
+
audio: "toggle-default-off",
|
|
635
|
+
// The current API folds negative guidance into `prompt`; the legacy
|
|
636
|
+
// image2video endpoint documents a `negative_prompt` field (max 2500
|
|
637
|
+
// chars). Either way the provider accepts negative guidance, so rejecting
|
|
638
|
+
// it here would be wrong.
|
|
639
|
+
supportsNegativePrompt: true,
|
|
640
|
+
supportsSeed: false
|
|
641
|
+
};
|
|
642
|
+
var klingV3Pro = {
|
|
643
|
+
...klingBase,
|
|
644
|
+
modelId: "kwaivgi/kling-v3.0-pro",
|
|
645
|
+
controls: { ...KLING_CONTROLS, resolutions: ["1080p", "4K"] }
|
|
646
|
+
};
|
|
647
|
+
var klingV3Std = {
|
|
648
|
+
...klingBase,
|
|
649
|
+
modelId: "kwaivgi/kling-v3.0-std",
|
|
650
|
+
controls: { ...KLING_CONTROLS, resolutions: ["720p"] }
|
|
651
|
+
};
|
|
652
|
+
var SEEDANCE_DOCS = [
|
|
653
|
+
"https://docs.byteplus.com/en/docs/ModelArk/1520757",
|
|
654
|
+
"https://docs.byteplus.com/en/docs/ModelArk/2607688"
|
|
655
|
+
];
|
|
656
|
+
var SEEDANCE_ASSETS = {
|
|
657
|
+
imageMimeTypes: [
|
|
658
|
+
"image/jpeg",
|
|
659
|
+
"image/png",
|
|
660
|
+
"image/webp",
|
|
661
|
+
"image/bmp",
|
|
662
|
+
"image/tiff",
|
|
663
|
+
"image/gif",
|
|
664
|
+
"image/heic",
|
|
665
|
+
"image/heif"
|
|
666
|
+
],
|
|
667
|
+
// "Single image must be less than 30 MB"
|
|
668
|
+
maxImageBytes: 30 * MB,
|
|
669
|
+
// "Width and height (px): [300, 6000]"
|
|
670
|
+
minEdgePx: 300,
|
|
671
|
+
maxEdgePx: 6e3,
|
|
672
|
+
inputPixels: null,
|
|
673
|
+
// "Aspect ratio (width/height): [0.4, 2.5]"
|
|
674
|
+
inputAspectRatioRange: [0.4, 2.5],
|
|
675
|
+
// "Request body size does not exceed 64 MB."
|
|
676
|
+
maxRequestBytes: 64 * MB
|
|
677
|
+
};
|
|
678
|
+
var SEEDANCE_CAVEATS = [
|
|
679
|
+
"Reference images and videos containing real human faces are rejected. Use model-generated faces, preset digital characters (asset://), or authorized assets.",
|
|
680
|
+
"The first and last frame images may be identical; mismatched ratios crop the last frame to the first."
|
|
681
|
+
];
|
|
682
|
+
var seedance20Mini = {
|
|
683
|
+
modelId: "bytedance/seedance-2.0-mini",
|
|
684
|
+
modality: "video",
|
|
685
|
+
verified: { sources: SEEDANCE_DOCS, checkedOn: "2026-09-11" },
|
|
686
|
+
unknownFields: ["controls.supportsNegativePrompt"],
|
|
687
|
+
unknownNotes: {
|
|
688
|
+
"controls.supportsNegativePrompt": "The word 'negative' does not appear anywhere in the video generation API reference. Defaulted to unsupported."
|
|
689
|
+
},
|
|
690
|
+
references: {
|
|
691
|
+
supported: true,
|
|
692
|
+
grouping: "flat",
|
|
693
|
+
// "Dreamina Seedance 2.0 series (1-9 images)"
|
|
694
|
+
maxImages: 9,
|
|
695
|
+
maxVideos: 3,
|
|
696
|
+
maxAudios: 3,
|
|
697
|
+
// "15 (9 images + 3 videos + 3 audios)"
|
|
698
|
+
maxTotalAssets: 15,
|
|
699
|
+
acceptsImageAndVideoReferences: true,
|
|
700
|
+
// Documented prohibition, NOT a conservative default.
|
|
701
|
+
canCombineWithStartFrame: false,
|
|
702
|
+
canCombineWithEndFrame: false
|
|
703
|
+
},
|
|
704
|
+
frames: {
|
|
705
|
+
start: { supported: true, maxImages: 1 },
|
|
706
|
+
end: { supported: true, maxImages: 1 },
|
|
707
|
+
allowsStartAndEndTogether: true,
|
|
708
|
+
// Only "first frame" and "first and last frames" are documented modes;
|
|
709
|
+
// there is no last-frame-only mode.
|
|
710
|
+
endFrameRequiresStartFrame: true
|
|
711
|
+
},
|
|
712
|
+
assets: SEEDANCE_ASSETS,
|
|
713
|
+
controls: {
|
|
714
|
+
// "ratio must be adaptive" once a first frame is supplied.
|
|
715
|
+
aspectRatioMode: "forcedAdaptiveWithFrames",
|
|
716
|
+
// 9:21 is NOT supported, despite previously appearing in our registry.
|
|
717
|
+
aspectRatios: ["16:9", "4:3", "1:1", "3:4", "9:16", "21:9", "adaptive"],
|
|
718
|
+
resolutions: ["480p", "720p"],
|
|
719
|
+
minDurationSeconds: 4,
|
|
720
|
+
maxDurationSeconds: 15,
|
|
721
|
+
supportsAutoDuration: true,
|
|
722
|
+
audio: "toggle-default-on",
|
|
723
|
+
supportsNegativePrompt: false,
|
|
724
|
+
// `seed` exists on the shared video API but its "Supported models" list
|
|
725
|
+
// names only Seedance 1.5 pro and 1.0 pro -- 2.x is deliberately absent.
|
|
726
|
+
supportsSeed: false
|
|
727
|
+
},
|
|
728
|
+
constraints: [],
|
|
729
|
+
providerCaveats: [
|
|
730
|
+
...SEEDANCE_CAVEATS,
|
|
731
|
+
"Audio-only references are not supported; an image or video reference is required."
|
|
732
|
+
]
|
|
733
|
+
};
|
|
734
|
+
var seedance25 = {
|
|
735
|
+
...seedance20Mini,
|
|
736
|
+
modelId: "bytedance/seedance-2.5",
|
|
737
|
+
references: {
|
|
738
|
+
...seedance20Mini.references,
|
|
739
|
+
// "Dreamina Seedance 2.5 (1-30 images)"
|
|
740
|
+
maxImages: 30,
|
|
741
|
+
maxVideos: 10,
|
|
742
|
+
maxAudios: 10,
|
|
743
|
+
// "50 (30 images + 10 video clips + 10 audio clips)"
|
|
744
|
+
maxTotalAssets: 50
|
|
745
|
+
},
|
|
746
|
+
controls: {
|
|
747
|
+
...seedance20Mini.controls,
|
|
748
|
+
resolutions: ["480p", "720p", "1080p"],
|
|
749
|
+
maxDurationSeconds: 30
|
|
750
|
+
},
|
|
751
|
+
providerCaveats: SEEDANCE_CAVEATS
|
|
752
|
+
};
|
|
753
|
+
var grokImagineVideo15 = {
|
|
754
|
+
modelId: "x-ai/grok-imagine-video-1.5",
|
|
755
|
+
modality: "video",
|
|
756
|
+
verified: {
|
|
757
|
+
sources: [
|
|
758
|
+
"https://docs.x.ai/developers/rest-api-reference/inference/videos",
|
|
759
|
+
"https://docs.x.ai/developers/model-capabilities/video",
|
|
760
|
+
"https://docs.x.ai/developers/model-capabilities/video/reference-to-video"
|
|
761
|
+
],
|
|
762
|
+
checkedOn: "2026-09-11"
|
|
763
|
+
},
|
|
764
|
+
unknownFields: [
|
|
765
|
+
"references.maxImages",
|
|
766
|
+
"assets.maxImageBytes",
|
|
767
|
+
"assets.minEdgePx",
|
|
768
|
+
"assets.maxEdgePx",
|
|
769
|
+
"assets.inputPixels"
|
|
770
|
+
],
|
|
771
|
+
unknownNotes: {
|
|
772
|
+
"references.maxImages": "The schema caps reference_audios at 3 entries but states no cap for reference_images. Defaulted to 3 to match the documented audio cap and the docs' own examples. The 5-image figure in xAI's docs is scoped to IMAGE editing, not video, and is deliberately not carried over.",
|
|
773
|
+
"assets.maxImageBytes": "Not stated for video inputs.",
|
|
774
|
+
"assets.minEdgePx": "Not stated.",
|
|
775
|
+
"assets.maxEdgePx": "Not stated.",
|
|
776
|
+
"assets.inputPixels": "Not stated."
|
|
777
|
+
},
|
|
778
|
+
references: {
|
|
779
|
+
supported: true,
|
|
780
|
+
grouping: "flat",
|
|
781
|
+
maxImages: 3,
|
|
782
|
+
maxVideos: 0,
|
|
783
|
+
// "Max 3 voices per request"
|
|
784
|
+
maxAudios: 3,
|
|
785
|
+
acceptsImageAndVideoReferences: false,
|
|
786
|
+
// The ONLY registered model that explicitly permits this.
|
|
787
|
+
canCombineWithStartFrame: true,
|
|
788
|
+
canCombineWithEndFrame: true
|
|
789
|
+
},
|
|
790
|
+
frames: {
|
|
791
|
+
start: { supported: true, maxImages: 1 },
|
|
792
|
+
end: { supported: true, maxImages: 1 },
|
|
793
|
+
allowsStartAndEndTogether: true,
|
|
794
|
+
// "last_frame only | Pinned last frame. The model generates the opening
|
|
795
|
+
// and lands on the pinned image." -- a documented standalone mode.
|
|
796
|
+
endFrameRequiresStartFrame: false
|
|
797
|
+
},
|
|
798
|
+
assets: {
|
|
799
|
+
imageMimeTypes: ["image/jpeg", "image/png", "image/webp"],
|
|
800
|
+
...unchecked,
|
|
801
|
+
maxRequestBytes: null
|
|
802
|
+
},
|
|
803
|
+
controls: {
|
|
804
|
+
aspectRatioMode: "settable",
|
|
805
|
+
aspectRatios: ["1:1", "16:9", "9:16", "4:3", "3:4", "3:2", "2:3"],
|
|
806
|
+
resolutions: ["480p", "720p", "1080p"],
|
|
807
|
+
minDurationSeconds: 1,
|
|
808
|
+
maxDurationSeconds: 15,
|
|
809
|
+
audio: "toggle-default-on",
|
|
810
|
+
supportsNegativePrompt: false,
|
|
811
|
+
supportsSeed: false
|
|
812
|
+
},
|
|
813
|
+
constraints: [
|
|
814
|
+
{
|
|
815
|
+
whenAny: [{ kind: "hasReferences" }],
|
|
816
|
+
requires: { kind: "resolutionAtMost", value: "720p" },
|
|
817
|
+
message: "Grok Imagine Video caps reference-to-video at 720p; 1080p is available only for text-to-video and image-to-video."
|
|
818
|
+
}
|
|
819
|
+
],
|
|
820
|
+
providerCaveats: [
|
|
821
|
+
"Prompt is optional whenever image, reference_images or last_frame is supplied.",
|
|
822
|
+
"The legacy grok-imagine-video model rejects last_frame and rejects combining image with reference inputs; 1.5 does not."
|
|
823
|
+
]
|
|
824
|
+
};
|
|
825
|
+
var WAN_DOCS = [
|
|
826
|
+
"https://help.aliyun.com/zh/model-studio/wan3-video-generation-api-reference"
|
|
827
|
+
];
|
|
828
|
+
var wanBase = {
|
|
829
|
+
modality: "video",
|
|
830
|
+
unknownFields: ["frames.endFrameRequiresStartFrame", "controls.supportsNegativePrompt"],
|
|
831
|
+
unknownNotes: {
|
|
832
|
+
"frames.endFrameRequiresStartFrame": "The docs enumerate the permitted material combinations and warn that 'passing other combinations returns an error', but a last-frame-only combination is not among those listed. Defaulted to requiring a start frame rather than sending an unlisted combination.",
|
|
833
|
+
"controls.supportsNegativePrompt": "No negative_prompt field exists on the API reference; the adjacent knob is prompt_extend. Defaulted to unsupported."
|
|
834
|
+
},
|
|
835
|
+
references: {
|
|
836
|
+
supported: true,
|
|
837
|
+
grouping: "flat",
|
|
838
|
+
// "reference_image: reference image. At most 10."
|
|
839
|
+
maxImages: 10,
|
|
840
|
+
maxVideos: 5,
|
|
841
|
+
maxAudios: 5,
|
|
842
|
+
acceptsImageAndVideoReferences: true,
|
|
843
|
+
// Documented prohibition, quoted at the top of this file.
|
|
844
|
+
canCombineWithStartFrame: false,
|
|
845
|
+
canCombineWithEndFrame: false
|
|
846
|
+
},
|
|
847
|
+
frames: {
|
|
848
|
+
start: { supported: true, maxImages: 1 },
|
|
849
|
+
end: { supported: true, maxImages: 1 },
|
|
850
|
+
allowsStartAndEndTogether: true,
|
|
851
|
+
endFrameRequiresStartFrame: true
|
|
852
|
+
},
|
|
853
|
+
assets: {
|
|
854
|
+
imageMimeTypes: ["image/jpeg", "image/png", "image/bmp", "image/webp"],
|
|
855
|
+
// "File size: no more than 20MB."
|
|
856
|
+
maxImageBytes: 20 * MB,
|
|
857
|
+
// "Resolution: single side [240, 8000] pixels."
|
|
858
|
+
minEdgePx: 240,
|
|
859
|
+
maxEdgePx: 8e3,
|
|
860
|
+
inputPixels: null,
|
|
861
|
+
// "Aspect ratio: no more than 8:1."
|
|
862
|
+
inputAspectRatioRange: [1 / 8, 8],
|
|
863
|
+
maxRequestBytes: null
|
|
864
|
+
},
|
|
865
|
+
controls: {
|
|
866
|
+
aspectRatioMode: "settable",
|
|
867
|
+
aspectRatios: ["adaptive", "16:9", "4:3", "1:1", "3:4", "9:16"],
|
|
868
|
+
resolutions: ["480p", "720p", "1080p"],
|
|
869
|
+
minDurationSeconds: 2,
|
|
870
|
+
maxDurationSeconds: 30,
|
|
871
|
+
supportsAutoDuration: true,
|
|
872
|
+
audio: "toggle-default-on",
|
|
873
|
+
supportsNegativePrompt: false,
|
|
874
|
+
// "seed integer (optional) random seed, used to reproduce results."
|
|
875
|
+
supportsSeed: true
|
|
876
|
+
},
|
|
877
|
+
constraints: [],
|
|
878
|
+
providerCaveats: [
|
|
879
|
+
"Input PNGs must not carry an alpha channel.",
|
|
880
|
+
"Input aspect ratio must not exceed 8:1.",
|
|
881
|
+
"With a video input, input duration plus output duration must not exceed 30 seconds.",
|
|
882
|
+
"reference_image, reference_video and reference_audio combine freely with each other, but never with a frame."
|
|
883
|
+
]
|
|
884
|
+
};
|
|
885
|
+
var wan30Prime = {
|
|
886
|
+
...wanBase,
|
|
887
|
+
modelId: "alibaba/wan-3.0-prime",
|
|
888
|
+
verified: {
|
|
889
|
+
sources: [
|
|
890
|
+
...WAN_DOCS,
|
|
891
|
+
"https://www.alibabacloud.com/help/zh/model-studio/wan3-0-video-prime"
|
|
892
|
+
],
|
|
893
|
+
checkedOn: "2026-09-11"
|
|
894
|
+
}
|
|
895
|
+
};
|
|
896
|
+
var wan30 = {
|
|
897
|
+
...wanBase,
|
|
898
|
+
modelId: "alibaba/wan-3.0",
|
|
899
|
+
verified: {
|
|
900
|
+
sources: [
|
|
901
|
+
...WAN_DOCS,
|
|
902
|
+
"https://www.alibabacloud.com/help/zh/model-studio/wan3-0-video"
|
|
903
|
+
],
|
|
904
|
+
checkedOn: "2026-09-11"
|
|
905
|
+
}
|
|
906
|
+
};
|
|
907
|
+
var VIDEO_MODEL_CAPABILITIES = [
|
|
908
|
+
veo31Fast,
|
|
909
|
+
veo31Lite,
|
|
910
|
+
klingV3Pro,
|
|
911
|
+
klingV3Std,
|
|
912
|
+
seedance20Mini,
|
|
913
|
+
seedance25,
|
|
914
|
+
grokImagineVideo15,
|
|
915
|
+
wan30Prime,
|
|
916
|
+
wan30
|
|
917
|
+
];
|
|
918
|
+
|
|
919
|
+
// modelCapabilities/validate.ts
|
|
920
|
+
var AUTO_DURATION = -1;
|
|
921
|
+
var asList = (value) => {
|
|
922
|
+
if (!value) {
|
|
923
|
+
return [];
|
|
924
|
+
}
|
|
925
|
+
return Array.isArray(value) ? value : [value];
|
|
926
|
+
};
|
|
927
|
+
function normalizeInputs(raw) {
|
|
928
|
+
const startFrames = asList(raw.startFrame);
|
|
929
|
+
const endFrames = asList(raw.endFrame);
|
|
930
|
+
return {
|
|
931
|
+
startFrame: startFrames[0] ?? null,
|
|
932
|
+
endFrame: endFrames[0] ?? null,
|
|
933
|
+
startFrameCount: startFrames.length,
|
|
934
|
+
endFrameCount: endFrames.length,
|
|
935
|
+
referenceImages: raw.referenceImages ? [...raw.referenceImages] : [],
|
|
936
|
+
referenceVideos: raw.referenceVideos ? [...raw.referenceVideos] : [],
|
|
937
|
+
referenceAudios: raw.referenceAudios ? [...raw.referenceAudios] : [],
|
|
938
|
+
referenceGroups: raw.referenceGroups ? [...raw.referenceGroups] : [],
|
|
939
|
+
sourceImage: raw.sourceImage ?? null,
|
|
940
|
+
brandLogo: raw.brandLogo ?? null,
|
|
941
|
+
hostReferences: raw.hostReferences ? [...raw.hostReferences] : []
|
|
942
|
+
};
|
|
943
|
+
}
|
|
944
|
+
function groupImages(group) {
|
|
945
|
+
return [group.frontal, ...group.additional].filter(Boolean);
|
|
946
|
+
}
|
|
947
|
+
function providerCountedImages(caps, inputs) {
|
|
948
|
+
const grouped = caps.references.grouping === "elements" ? inputs.referenceGroups.flatMap(groupImages) : [];
|
|
949
|
+
return [
|
|
950
|
+
...inputs.referenceImages,
|
|
951
|
+
...grouped,
|
|
952
|
+
...inputs.sourceImage ? [inputs.sourceImage] : [],
|
|
953
|
+
...inputs.brandLogo ? [inputs.brandLogo] : [],
|
|
954
|
+
...inputs.hostReferences
|
|
955
|
+
];
|
|
956
|
+
}
|
|
957
|
+
function effectiveUserReferenceLimit(caps, reserved, globalLimit = MAX_USER_REFERENCE_IMAGES) {
|
|
958
|
+
if (!caps.references.supported) {
|
|
959
|
+
return 0;
|
|
960
|
+
}
|
|
961
|
+
return Math.max(0, Math.min(globalLimit, caps.references.maxImages - reserved));
|
|
962
|
+
}
|
|
963
|
+
var hasBytes = (asset) => asset.dataUrl !== "";
|
|
964
|
+
function validateAsset(caps, asset, field) {
|
|
965
|
+
const errors = [];
|
|
966
|
+
const { assets } = caps;
|
|
967
|
+
if (!hasBytes(asset)) {
|
|
968
|
+
return errors;
|
|
969
|
+
}
|
|
970
|
+
if (assets.imageMimeTypes.length > 0 && asset.mimeType && !assets.imageMimeTypes.includes(asset.mimeType.toLowerCase())) {
|
|
971
|
+
errors.push({
|
|
972
|
+
code: "UNSUPPORTED_MEDIA_TYPE",
|
|
973
|
+
modelId: caps.modelId,
|
|
974
|
+
field,
|
|
975
|
+
message: `${caps.modelId} does not accept ${asset.mimeType} input images.`,
|
|
976
|
+
allowed: assets.imageMimeTypes,
|
|
977
|
+
received: asset.mimeType
|
|
978
|
+
});
|
|
979
|
+
}
|
|
980
|
+
if (assets.maxImageBytes != null && (asset.bytes ?? 0) > assets.maxImageBytes) {
|
|
981
|
+
errors.push({
|
|
982
|
+
code: "REQUEST_TOO_LARGE",
|
|
983
|
+
modelId: caps.modelId,
|
|
984
|
+
field,
|
|
985
|
+
message: `${field} is larger than the ${Math.round(
|
|
986
|
+
assets.maxImageBytes / (1024 * 1024)
|
|
987
|
+
)} MB per-image limit for ${caps.modelId}.`,
|
|
988
|
+
allowed: assets.maxImageBytes,
|
|
989
|
+
received: asset.bytes
|
|
990
|
+
});
|
|
991
|
+
}
|
|
992
|
+
const { widthPx: w, heightPx: h } = asset;
|
|
993
|
+
if (w == null || h == null || w <= 0 || h <= 0) {
|
|
994
|
+
return errors;
|
|
995
|
+
}
|
|
996
|
+
const shortest = Math.min(w, h);
|
|
997
|
+
const longest = Math.max(w, h);
|
|
998
|
+
if (assets.minEdgePx != null && shortest < assets.minEdgePx) {
|
|
999
|
+
errors.push({
|
|
1000
|
+
code: "UNSUPPORTED_DIMENSIONS",
|
|
1001
|
+
modelId: caps.modelId,
|
|
1002
|
+
field,
|
|
1003
|
+
message: `${field} is ${w}x${h}; ${caps.modelId} requires at least ${assets.minEdgePx}px on each side.`,
|
|
1004
|
+
allowed: assets.minEdgePx,
|
|
1005
|
+
received: `${w}x${h}`
|
|
1006
|
+
});
|
|
1007
|
+
}
|
|
1008
|
+
if (assets.maxEdgePx != null && longest > assets.maxEdgePx) {
|
|
1009
|
+
errors.push({
|
|
1010
|
+
code: "UNSUPPORTED_DIMENSIONS",
|
|
1011
|
+
modelId: caps.modelId,
|
|
1012
|
+
field,
|
|
1013
|
+
message: `${field} is ${w}x${h}; ${caps.modelId} allows at most ${assets.maxEdgePx}px on any side.`,
|
|
1014
|
+
allowed: assets.maxEdgePx,
|
|
1015
|
+
received: `${w}x${h}`
|
|
1016
|
+
});
|
|
1017
|
+
}
|
|
1018
|
+
if (assets.inputPixels) {
|
|
1019
|
+
const pixels = w * h;
|
|
1020
|
+
if (pixels < assets.inputPixels.min || pixels > assets.inputPixels.max) {
|
|
1021
|
+
errors.push({
|
|
1022
|
+
code: "UNSUPPORTED_DIMENSIONS",
|
|
1023
|
+
modelId: caps.modelId,
|
|
1024
|
+
field,
|
|
1025
|
+
message: `${field} is ${pixels.toLocaleString()} pixels; ${caps.modelId} accepts ${assets.inputPixels.min.toLocaleString()}-${assets.inputPixels.max.toLocaleString()}.`,
|
|
1026
|
+
received: `${w}x${h}`
|
|
1027
|
+
});
|
|
1028
|
+
}
|
|
1029
|
+
}
|
|
1030
|
+
if (assets.inputAspectRatioRange) {
|
|
1031
|
+
const [min, max] = assets.inputAspectRatioRange;
|
|
1032
|
+
const ratio = w / h;
|
|
1033
|
+
if (ratio < min || ratio > max) {
|
|
1034
|
+
errors.push({
|
|
1035
|
+
code: "UNSUPPORTED_DIMENSIONS",
|
|
1036
|
+
modelId: caps.modelId,
|
|
1037
|
+
field,
|
|
1038
|
+
message: `${field} has a ${ratio.toFixed(2)}:1 aspect ratio; ${caps.modelId} accepts ${min}-${max}.`,
|
|
1039
|
+
received: `${w}x${h}`
|
|
1040
|
+
});
|
|
1041
|
+
}
|
|
1042
|
+
}
|
|
1043
|
+
return errors;
|
|
1044
|
+
}
|
|
1045
|
+
function validateFrames(caps, inputs) {
|
|
1046
|
+
const errors = [];
|
|
1047
|
+
const { frames, modelId } = caps;
|
|
1048
|
+
const hasStart = inputs.startFrame != null;
|
|
1049
|
+
const hasEnd = inputs.endFrame != null;
|
|
1050
|
+
if (hasStart && !frames.start.supported) {
|
|
1051
|
+
errors.push({
|
|
1052
|
+
code: "START_FRAME_NOT_SUPPORTED",
|
|
1053
|
+
modelId,
|
|
1054
|
+
field: "startFrame",
|
|
1055
|
+
message: `${modelId} does not accept a start frame.`
|
|
1056
|
+
});
|
|
1057
|
+
}
|
|
1058
|
+
if (hasEnd && !frames.end.supported) {
|
|
1059
|
+
errors.push({
|
|
1060
|
+
code: "END_FRAME_NOT_SUPPORTED",
|
|
1061
|
+
modelId,
|
|
1062
|
+
field: "endFrame",
|
|
1063
|
+
message: `${modelId} does not accept an end frame.`
|
|
1064
|
+
});
|
|
1065
|
+
}
|
|
1066
|
+
if (inputs.startFrameCount > frames.start.maxImages) {
|
|
1067
|
+
errors.push({
|
|
1068
|
+
code: "TOO_MANY_START_FRAMES",
|
|
1069
|
+
modelId,
|
|
1070
|
+
field: "startFrame",
|
|
1071
|
+
message: `${modelId} accepts ${frames.start.maxImages} start frame image(s).`,
|
|
1072
|
+
allowed: frames.start.maxImages,
|
|
1073
|
+
received: inputs.startFrameCount
|
|
1074
|
+
});
|
|
1075
|
+
}
|
|
1076
|
+
if (inputs.endFrameCount > frames.end.maxImages) {
|
|
1077
|
+
errors.push({
|
|
1078
|
+
code: "TOO_MANY_END_FRAMES",
|
|
1079
|
+
modelId,
|
|
1080
|
+
field: "endFrame",
|
|
1081
|
+
message: `${modelId} accepts ${frames.end.maxImages} end frame image(s).`,
|
|
1082
|
+
allowed: frames.end.maxImages,
|
|
1083
|
+
received: inputs.endFrameCount
|
|
1084
|
+
});
|
|
1085
|
+
}
|
|
1086
|
+
if (hasEnd && frames.end.supported && !hasStart && frames.endFrameRequiresStartFrame) {
|
|
1087
|
+
errors.push({
|
|
1088
|
+
code: "END_FRAME_REQUIRES_START_FRAME",
|
|
1089
|
+
modelId,
|
|
1090
|
+
field: "endFrame",
|
|
1091
|
+
message: `${modelId} only accepts an end frame alongside a start frame.`
|
|
1092
|
+
});
|
|
1093
|
+
}
|
|
1094
|
+
if (hasStart && hasEnd && !frames.allowsStartAndEndTogether) {
|
|
1095
|
+
errors.push({
|
|
1096
|
+
code: "START_AND_END_FRAME_NOT_SUPPORTED_TOGETHER",
|
|
1097
|
+
modelId,
|
|
1098
|
+
message: `${modelId} cannot take a start frame and an end frame in the same job.`
|
|
1099
|
+
});
|
|
1100
|
+
}
|
|
1101
|
+
return errors;
|
|
1102
|
+
}
|
|
1103
|
+
function validateReferences(caps, inputs) {
|
|
1104
|
+
const errors = [];
|
|
1105
|
+
const { references: refs, modelId } = caps;
|
|
1106
|
+
const isElements = refs.grouping === "elements";
|
|
1107
|
+
const flatCount = inputs.referenceImages.length;
|
|
1108
|
+
const groupCount = inputs.referenceGroups.length;
|
|
1109
|
+
const countedImages = providerCountedImages(caps, inputs);
|
|
1110
|
+
const userSuppliedCount = flatCount + inputs.referenceGroups.flatMap(groupImages).length;
|
|
1111
|
+
if (!refs.supported && userSuppliedCount > 0) {
|
|
1112
|
+
errors.push({
|
|
1113
|
+
code: "REFERENCES_NOT_SUPPORTED",
|
|
1114
|
+
modelId,
|
|
1115
|
+
field: "referenceImages",
|
|
1116
|
+
message: `${modelId} does not accept reference images.`,
|
|
1117
|
+
received: userSuppliedCount
|
|
1118
|
+
});
|
|
1119
|
+
return errors;
|
|
1120
|
+
}
|
|
1121
|
+
if (isElements && flatCount > 0) {
|
|
1122
|
+
errors.push({
|
|
1123
|
+
code: "REFERENCES_NOT_SUPPORTED",
|
|
1124
|
+
modelId,
|
|
1125
|
+
field: "referenceImages",
|
|
1126
|
+
message: `${modelId} takes reference subjects (each a frontal image plus other angles), not a flat reference list.`,
|
|
1127
|
+
received: flatCount
|
|
1128
|
+
});
|
|
1129
|
+
}
|
|
1130
|
+
if (!isElements && groupCount > 0) {
|
|
1131
|
+
errors.push({
|
|
1132
|
+
code: "REFERENCES_NOT_SUPPORTED",
|
|
1133
|
+
modelId,
|
|
1134
|
+
field: "referenceGroups",
|
|
1135
|
+
message: `${modelId} does not support grouped reference subjects.`,
|
|
1136
|
+
received: groupCount
|
|
1137
|
+
});
|
|
1138
|
+
}
|
|
1139
|
+
if (isElements) {
|
|
1140
|
+
if (refs.maxGroups != null && groupCount > refs.maxGroups) {
|
|
1141
|
+
errors.push({
|
|
1142
|
+
code: "TOO_MANY_REFERENCE_GROUPS",
|
|
1143
|
+
modelId,
|
|
1144
|
+
field: "referenceGroups",
|
|
1145
|
+
message: `${modelId} accepts at most ${refs.maxGroups} reference subjects.`,
|
|
1146
|
+
allowed: refs.maxGroups,
|
|
1147
|
+
received: groupCount
|
|
1148
|
+
});
|
|
1149
|
+
}
|
|
1150
|
+
if (refs.imagesPerGroup) {
|
|
1151
|
+
const { min, max } = refs.imagesPerGroup;
|
|
1152
|
+
inputs.referenceGroups.forEach((group, index) => {
|
|
1153
|
+
const size = groupImages(group).length;
|
|
1154
|
+
if (size < min || size > max) {
|
|
1155
|
+
errors.push({
|
|
1156
|
+
code: "INVALID_REFERENCE_GROUP_SIZE",
|
|
1157
|
+
modelId,
|
|
1158
|
+
field: `referenceGroups[${index}]`,
|
|
1159
|
+
message: `${group.label ?? `Subject ${index + 1}`} has ${size} image(s); ${modelId} needs a frontal image plus ${min - 1}-${max - 1} other angles.`,
|
|
1160
|
+
allowed: max,
|
|
1161
|
+
received: size
|
|
1162
|
+
});
|
|
1163
|
+
}
|
|
1164
|
+
});
|
|
1165
|
+
}
|
|
1166
|
+
}
|
|
1167
|
+
if (countedImages.length > refs.maxImages) {
|
|
1168
|
+
errors.push({
|
|
1169
|
+
code: "TOO_MANY_REFERENCE_IMAGES",
|
|
1170
|
+
modelId,
|
|
1171
|
+
field: "referenceImages",
|
|
1172
|
+
message: `${modelId} accepts ${refs.maxImages} reference image(s); this request has ${countedImages.length}.`,
|
|
1173
|
+
allowed: refs.maxImages,
|
|
1174
|
+
received: countedImages.length
|
|
1175
|
+
});
|
|
1176
|
+
}
|
|
1177
|
+
if (inputs.referenceVideos.length > refs.maxVideos) {
|
|
1178
|
+
errors.push({
|
|
1179
|
+
code: "TOO_MANY_REFERENCE_VIDEOS",
|
|
1180
|
+
modelId,
|
|
1181
|
+
field: "referenceVideos",
|
|
1182
|
+
message: `${modelId} accepts ${refs.maxVideos} reference video(s).`,
|
|
1183
|
+
allowed: refs.maxVideos,
|
|
1184
|
+
received: inputs.referenceVideos.length
|
|
1185
|
+
});
|
|
1186
|
+
}
|
|
1187
|
+
if (inputs.referenceAudios.length > refs.maxAudios) {
|
|
1188
|
+
errors.push({
|
|
1189
|
+
code: "TOO_MANY_REFERENCE_AUDIOS",
|
|
1190
|
+
modelId,
|
|
1191
|
+
field: "referenceAudios",
|
|
1192
|
+
message: `${modelId} accepts ${refs.maxAudios} reference audio clip(s).`,
|
|
1193
|
+
allowed: refs.maxAudios,
|
|
1194
|
+
received: inputs.referenceAudios.length
|
|
1195
|
+
});
|
|
1196
|
+
}
|
|
1197
|
+
if (refs.maxTotalAssets != null) {
|
|
1198
|
+
const total = countedImages.length + inputs.referenceVideos.length + inputs.referenceAudios.length;
|
|
1199
|
+
if (total > refs.maxTotalAssets) {
|
|
1200
|
+
errors.push({
|
|
1201
|
+
code: "TOO_MANY_REFERENCE_ASSETS",
|
|
1202
|
+
modelId,
|
|
1203
|
+
message: `${modelId} accepts ${refs.maxTotalAssets} reference assets in total; this request has ${total}.`,
|
|
1204
|
+
allowed: refs.maxTotalAssets,
|
|
1205
|
+
received: total
|
|
1206
|
+
});
|
|
1207
|
+
}
|
|
1208
|
+
}
|
|
1209
|
+
return errors;
|
|
1210
|
+
}
|
|
1211
|
+
function validateCombination(caps, inputs) {
|
|
1212
|
+
const { references: refs, modelId, unknownFields } = caps;
|
|
1213
|
+
const hasRefs = inputs.referenceImages.length > 0 || inputs.referenceGroups.length > 0 || inputs.referenceVideos.length > 0 || inputs.referenceAudios.length > 0;
|
|
1214
|
+
if (!hasRefs) {
|
|
1215
|
+
return [];
|
|
1216
|
+
}
|
|
1217
|
+
const errors = [];
|
|
1218
|
+
const blocked = (slot, unknownPath) => {
|
|
1219
|
+
const isUnverified = unknownFields.includes(unknownPath);
|
|
1220
|
+
return {
|
|
1221
|
+
code: isUnverified ? "REFERENCE_FRAME_COMBINATION_UNVERIFIED" : "FRAME_REFERENCE_COMBINATION_NOT_SUPPORTED",
|
|
1222
|
+
modelId,
|
|
1223
|
+
field: slot,
|
|
1224
|
+
message: isUnverified ? `Combining reference images with a ${slot === "startFrame" ? "start" : "end"} frame is not documented for ${modelId}, so it is blocked rather than risking a failed paid request. Use references or frames, not both.` : `${modelId} cannot combine reference images with a ${slot === "startFrame" ? "start" : "end"} frame \u2014 the provider documents these as mutually exclusive.`
|
|
1225
|
+
};
|
|
1226
|
+
};
|
|
1227
|
+
if (inputs.startFrame && !refs.canCombineWithStartFrame) {
|
|
1228
|
+
errors.push(
|
|
1229
|
+
blocked("startFrame", "references.canCombineWithStartFrame")
|
|
1230
|
+
);
|
|
1231
|
+
}
|
|
1232
|
+
if (inputs.endFrame && !refs.canCombineWithEndFrame) {
|
|
1233
|
+
errors.push(blocked("endFrame", "references.canCombineWithEndFrame"));
|
|
1234
|
+
}
|
|
1235
|
+
if (inputs.referenceVideos.length > 0 && inputs.referenceImages.length > 0 && !refs.acceptsImageAndVideoReferences) {
|
|
1236
|
+
errors.push({
|
|
1237
|
+
code: "FRAME_REFERENCE_COMBINATION_NOT_SUPPORTED",
|
|
1238
|
+
modelId,
|
|
1239
|
+
message: `${modelId} cannot take image references and video references in the same request.`
|
|
1240
|
+
});
|
|
1241
|
+
}
|
|
1242
|
+
return errors;
|
|
1243
|
+
}
|
|
1244
|
+
var resolutionRank = (value) => RESOLUTION_RANK[value.toLowerCase()];
|
|
1245
|
+
function validateControls(caps, inputs, controls) {
|
|
1246
|
+
const errors = [];
|
|
1247
|
+
const { controls: cfg, modelId } = caps;
|
|
1248
|
+
const hasFrame = inputs.startFrame != null || inputs.endFrame != null;
|
|
1249
|
+
if (controls.aspectRatio != null) {
|
|
1250
|
+
const mode = cfg.aspectRatioMode;
|
|
1251
|
+
if (mode === "notApplicable" || mode === "notApplicableWithFrames" && hasFrame) {
|
|
1252
|
+
errors.push({
|
|
1253
|
+
code: "UNSUPPORTED_CONTROL",
|
|
1254
|
+
modelId,
|
|
1255
|
+
field: "aspectRatio",
|
|
1256
|
+
message: `${modelId} has no aspect ratio setting when a frame is supplied \u2014 the output follows the frame.`,
|
|
1257
|
+
received: controls.aspectRatio
|
|
1258
|
+
});
|
|
1259
|
+
} else if (mode === "forcedAdaptiveWithFrames" && hasFrame && controls.aspectRatio !== ADAPTIVE_ASPECT_RATIO) {
|
|
1260
|
+
errors.push({
|
|
1261
|
+
code: "UNSUPPORTED_CONTROL",
|
|
1262
|
+
modelId,
|
|
1263
|
+
field: "aspectRatio",
|
|
1264
|
+
message: `${modelId} requires an adaptive aspect ratio when a frame is supplied; it derives the ratio from the first frame.`,
|
|
1265
|
+
allowed: [ADAPTIVE_ASPECT_RATIO],
|
|
1266
|
+
received: controls.aspectRatio
|
|
1267
|
+
});
|
|
1268
|
+
} else if (!cfg.aspectRatios.includes(controls.aspectRatio)) {
|
|
1269
|
+
errors.push({
|
|
1270
|
+
code: "UNSUPPORTED_CONTROL",
|
|
1271
|
+
modelId,
|
|
1272
|
+
field: "aspectRatio",
|
|
1273
|
+
message: `${modelId} does not support a ${controls.aspectRatio} aspect ratio.`,
|
|
1274
|
+
allowed: cfg.aspectRatios,
|
|
1275
|
+
received: controls.aspectRatio
|
|
1276
|
+
});
|
|
1277
|
+
}
|
|
1278
|
+
}
|
|
1279
|
+
if (controls.resolution != null && !cfg.resolutions.some(
|
|
1280
|
+
(r) => r.toLowerCase() === controls.resolution.toLowerCase()
|
|
1281
|
+
)) {
|
|
1282
|
+
errors.push({
|
|
1283
|
+
code: "UNSUPPORTED_CONTROL",
|
|
1284
|
+
modelId,
|
|
1285
|
+
field: "resolution",
|
|
1286
|
+
message: `${modelId} does not support ${controls.resolution} output.`,
|
|
1287
|
+
allowed: cfg.resolutions,
|
|
1288
|
+
received: controls.resolution
|
|
1289
|
+
});
|
|
1290
|
+
}
|
|
1291
|
+
const duration = controls.durationSeconds;
|
|
1292
|
+
if (duration != null) {
|
|
1293
|
+
if (duration === AUTO_DURATION) {
|
|
1294
|
+
if (!cfg.supportsAutoDuration) {
|
|
1295
|
+
errors.push({
|
|
1296
|
+
code: "UNSUPPORTED_CONTROL",
|
|
1297
|
+
modelId,
|
|
1298
|
+
field: "durationSeconds",
|
|
1299
|
+
message: `${modelId} requires an explicit duration.`,
|
|
1300
|
+
received: duration
|
|
1301
|
+
});
|
|
1302
|
+
}
|
|
1303
|
+
} else if (cfg.durationsSeconds) {
|
|
1304
|
+
if (!cfg.durationsSeconds.includes(duration)) {
|
|
1305
|
+
errors.push({
|
|
1306
|
+
code: "UNSUPPORTED_CONTROL",
|
|
1307
|
+
modelId,
|
|
1308
|
+
field: "durationSeconds",
|
|
1309
|
+
message: `${modelId} supports ${cfg.durationsSeconds.join(
|
|
1310
|
+
", "
|
|
1311
|
+
)} second durations.`,
|
|
1312
|
+
allowed: cfg.durationsSeconds.map(String),
|
|
1313
|
+
received: duration
|
|
1314
|
+
});
|
|
1315
|
+
}
|
|
1316
|
+
} else {
|
|
1317
|
+
const min = cfg.minDurationSeconds;
|
|
1318
|
+
const max = cfg.maxDurationSeconds;
|
|
1319
|
+
if (min != null && duration < min || max != null && duration > max) {
|
|
1320
|
+
errors.push({
|
|
1321
|
+
code: "UNSUPPORTED_CONTROL",
|
|
1322
|
+
modelId,
|
|
1323
|
+
field: "durationSeconds",
|
|
1324
|
+
message: `${modelId} supports durations between ${min} and ${max} seconds.`,
|
|
1325
|
+
received: duration
|
|
1326
|
+
});
|
|
1327
|
+
}
|
|
1328
|
+
}
|
|
1329
|
+
}
|
|
1330
|
+
if (controls.seed != null && !cfg.supportsSeed) {
|
|
1331
|
+
errors.push({
|
|
1332
|
+
code: "UNSUPPORTED_CONTROL",
|
|
1333
|
+
modelId,
|
|
1334
|
+
field: "seed",
|
|
1335
|
+
message: `${modelId} does not document a seed parameter.`,
|
|
1336
|
+
received: controls.seed
|
|
1337
|
+
});
|
|
1338
|
+
}
|
|
1339
|
+
if (controls.negativePrompt && !cfg.supportsNegativePrompt) {
|
|
1340
|
+
errors.push({
|
|
1341
|
+
code: "UNSUPPORTED_CONTROL",
|
|
1342
|
+
modelId,
|
|
1343
|
+
field: "negativePrompt",
|
|
1344
|
+
message: `${modelId} does not document a negative prompt parameter. Put negative guidance in the prompt instead.`
|
|
1345
|
+
});
|
|
1346
|
+
}
|
|
1347
|
+
return errors;
|
|
1348
|
+
}
|
|
1349
|
+
function conditionHolds(condition, inputs, controls) {
|
|
1350
|
+
switch (condition.kind) {
|
|
1351
|
+
case "hasReferences":
|
|
1352
|
+
return inputs.referenceImages.length > 0 || inputs.referenceGroups.length > 0 || inputs.referenceVideos.length > 0;
|
|
1353
|
+
case "hasStartFrame":
|
|
1354
|
+
return inputs.startFrame != null;
|
|
1355
|
+
case "hasEndFrame":
|
|
1356
|
+
return inputs.endFrame != null;
|
|
1357
|
+
case "hasAnyFrame":
|
|
1358
|
+
return inputs.startFrame != null || inputs.endFrame != null;
|
|
1359
|
+
case "resolutionIn":
|
|
1360
|
+
return controls.resolution != null && condition.values.some(
|
|
1361
|
+
(v) => v.toLowerCase() === controls.resolution.toLowerCase()
|
|
1362
|
+
);
|
|
1363
|
+
default:
|
|
1364
|
+
return false;
|
|
1365
|
+
}
|
|
1366
|
+
}
|
|
1367
|
+
function validateConstraints(caps, inputs, controls) {
|
|
1368
|
+
const errors = [];
|
|
1369
|
+
for (const constraint of caps.constraints) {
|
|
1370
|
+
const fires = constraint.whenAny.some(
|
|
1371
|
+
(c) => conditionHolds(c, inputs, controls)
|
|
1372
|
+
);
|
|
1373
|
+
if (!fires) {
|
|
1374
|
+
continue;
|
|
1375
|
+
}
|
|
1376
|
+
const { requires } = constraint;
|
|
1377
|
+
if (requires.kind === "durationMustBe" && controls.durationSeconds != null && controls.durationSeconds !== requires.seconds) {
|
|
1378
|
+
errors.push({
|
|
1379
|
+
code: "CONTROL_CONSTRAINT_VIOLATED",
|
|
1380
|
+
modelId: caps.modelId,
|
|
1381
|
+
field: "durationSeconds",
|
|
1382
|
+
message: constraint.message,
|
|
1383
|
+
allowed: requires.seconds,
|
|
1384
|
+
received: controls.durationSeconds
|
|
1385
|
+
});
|
|
1386
|
+
}
|
|
1387
|
+
if (requires.kind === "resolutionAtMost" && controls.resolution != null) {
|
|
1388
|
+
const cap = resolutionRank(requires.value);
|
|
1389
|
+
const requested = resolutionRank(controls.resolution);
|
|
1390
|
+
if (cap != null && requested != null && requested > cap) {
|
|
1391
|
+
errors.push({
|
|
1392
|
+
code: "CONTROL_CONSTRAINT_VIOLATED",
|
|
1393
|
+
modelId: caps.modelId,
|
|
1394
|
+
field: "resolution",
|
|
1395
|
+
message: constraint.message,
|
|
1396
|
+
allowed: requires.value,
|
|
1397
|
+
received: controls.resolution
|
|
1398
|
+
});
|
|
1399
|
+
}
|
|
1400
|
+
}
|
|
1401
|
+
if (requires.kind === "aspectRatioMustBe" && controls.aspectRatio != null && controls.aspectRatio !== requires.value) {
|
|
1402
|
+
errors.push({
|
|
1403
|
+
code: "CONTROL_CONSTRAINT_VIOLATED",
|
|
1404
|
+
modelId: caps.modelId,
|
|
1405
|
+
field: "aspectRatio",
|
|
1406
|
+
message: constraint.message,
|
|
1407
|
+
allowed: requires.value,
|
|
1408
|
+
received: controls.aspectRatio
|
|
1409
|
+
});
|
|
1410
|
+
}
|
|
1411
|
+
}
|
|
1412
|
+
return errors;
|
|
1413
|
+
}
|
|
1414
|
+
function requiredDurationFor(caps, inputs, controls) {
|
|
1415
|
+
if (!caps) {
|
|
1416
|
+
return null;
|
|
1417
|
+
}
|
|
1418
|
+
for (const constraint of caps.constraints) {
|
|
1419
|
+
if (constraint.requires.kind !== "durationMustBe") {
|
|
1420
|
+
continue;
|
|
1421
|
+
}
|
|
1422
|
+
if (constraint.whenAny.some((c) => conditionHolds(c, inputs, controls))) {
|
|
1423
|
+
return constraint.requires.seconds;
|
|
1424
|
+
}
|
|
1425
|
+
}
|
|
1426
|
+
return null;
|
|
1427
|
+
}
|
|
1428
|
+
function maxResolutionFor(caps, inputs, controls) {
|
|
1429
|
+
if (!caps) {
|
|
1430
|
+
return null;
|
|
1431
|
+
}
|
|
1432
|
+
for (const constraint of caps.constraints) {
|
|
1433
|
+
if (constraint.requires.kind !== "resolutionAtMost") {
|
|
1434
|
+
continue;
|
|
1435
|
+
}
|
|
1436
|
+
if (constraint.whenAny.some((c) => conditionHolds(c, inputs, controls))) {
|
|
1437
|
+
return constraint.requires.value;
|
|
1438
|
+
}
|
|
1439
|
+
}
|
|
1440
|
+
return null;
|
|
1441
|
+
}
|
|
1442
|
+
function allowedResolutionsFor(caps, inputs, controls) {
|
|
1443
|
+
if (!caps) {
|
|
1444
|
+
return [];
|
|
1445
|
+
}
|
|
1446
|
+
const cap = maxResolutionFor(caps, inputs, controls);
|
|
1447
|
+
if (!cap) {
|
|
1448
|
+
return caps.controls.resolutions;
|
|
1449
|
+
}
|
|
1450
|
+
const ceiling = resolutionRank(cap);
|
|
1451
|
+
if (ceiling == null) {
|
|
1452
|
+
return caps.controls.resolutions;
|
|
1453
|
+
}
|
|
1454
|
+
return caps.controls.resolutions.filter((r) => {
|
|
1455
|
+
const rank = resolutionRank(r);
|
|
1456
|
+
return rank == null || rank <= ceiling;
|
|
1457
|
+
});
|
|
1458
|
+
}
|
|
1459
|
+
function validateInputs(caps, inputs, controls) {
|
|
1460
|
+
if (!caps) {
|
|
1461
|
+
return [
|
|
1462
|
+
{
|
|
1463
|
+
code: "UNKNOWN_MODEL_CAPABILITIES",
|
|
1464
|
+
modelId: "",
|
|
1465
|
+
message: "This model has no verified input capability record, so the request cannot be validated or sent."
|
|
1466
|
+
}
|
|
1467
|
+
];
|
|
1468
|
+
}
|
|
1469
|
+
const assetErrors = [];
|
|
1470
|
+
const named = [
|
|
1471
|
+
[inputs.startFrame, "startFrame"],
|
|
1472
|
+
[inputs.endFrame, "endFrame"],
|
|
1473
|
+
[inputs.sourceImage, "sourceImage"],
|
|
1474
|
+
[inputs.brandLogo, "brandLogo"]
|
|
1475
|
+
];
|
|
1476
|
+
for (const [asset, field] of named) {
|
|
1477
|
+
if (asset) {
|
|
1478
|
+
assetErrors.push(...validateAsset(caps, asset, field));
|
|
1479
|
+
}
|
|
1480
|
+
}
|
|
1481
|
+
inputs.referenceImages.forEach((asset, i) => {
|
|
1482
|
+
assetErrors.push(...validateAsset(caps, asset, `referenceImages[${i}]`));
|
|
1483
|
+
});
|
|
1484
|
+
inputs.referenceGroups.forEach((group, gi) => {
|
|
1485
|
+
groupImages(group).forEach((asset, i) => {
|
|
1486
|
+
assetErrors.push(
|
|
1487
|
+
...validateAsset(caps, asset, `referenceGroups[${gi}][${i}]`)
|
|
1488
|
+
);
|
|
1489
|
+
});
|
|
1490
|
+
});
|
|
1491
|
+
return [
|
|
1492
|
+
...assetErrors,
|
|
1493
|
+
...validateFrames(caps, inputs),
|
|
1494
|
+
...validateReferences(caps, inputs),
|
|
1495
|
+
...validateCombination(caps, inputs),
|
|
1496
|
+
...validateControls(caps, inputs, controls),
|
|
1497
|
+
...validateConstraints(caps, inputs, controls)
|
|
1498
|
+
];
|
|
1499
|
+
}
|
|
1500
|
+
|
|
1501
|
+
// modelCapabilities/index.ts
|
|
1502
|
+
var ALL_MODEL_CAPABILITIES = [
|
|
1503
|
+
...IMAGE_MODEL_CAPABILITIES,
|
|
1504
|
+
...VIDEO_MODEL_CAPABILITIES
|
|
1505
|
+
];
|
|
1506
|
+
var BY_ID = new Map(
|
|
1507
|
+
ALL_MODEL_CAPABILITIES.map((caps) => [caps.modelId, caps])
|
|
1508
|
+
);
|
|
1509
|
+
function getCapabilities(modelId) {
|
|
1510
|
+
if (!modelId || typeof modelId !== "string") {
|
|
1511
|
+
return null;
|
|
1512
|
+
}
|
|
1513
|
+
const base = modelId.endsWith(":online") ? modelId.slice(0, -":online".length) : modelId;
|
|
1514
|
+
return BY_ID.get(base) ?? null;
|
|
1515
|
+
}
|
|
1516
|
+
function registeredModelIds() {
|
|
1517
|
+
return [...BY_ID.keys()];
|
|
1518
|
+
}
|
|
1519
|
+
|
|
1520
|
+
export {
|
|
1521
|
+
IMAGE_MODEL_CAPABILITIES,
|
|
1522
|
+
VIDEO_MODEL_CAPABILITIES,
|
|
1523
|
+
AUTO_DURATION,
|
|
1524
|
+
normalizeInputs,
|
|
1525
|
+
groupImages,
|
|
1526
|
+
effectiveUserReferenceLimit,
|
|
1527
|
+
requiredDurationFor,
|
|
1528
|
+
maxResolutionFor,
|
|
1529
|
+
allowedResolutionsFor,
|
|
1530
|
+
validateInputs,
|
|
1531
|
+
ALL_MODEL_CAPABILITIES,
|
|
1532
|
+
getCapabilities,
|
|
1533
|
+
registeredModelIds
|
|
1534
|
+
};
|
|
1535
|
+
//# sourceMappingURL=chunk-2K6ZAAHB.js.map
|