@fungi.computer/darkroom 0.2.1 → 0.3.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.
@@ -18,6 +18,7 @@ export declare const mediaModelsSchema: z.ZodLazy<z.ZodArray<z.ZodReadonly<z.Zod
18
18
  readonly jpeg: "jpeg";
19
19
  readonly webp: "webp";
20
20
  }>>>;
21
+ maxPromptLength: z.ZodNumber;
21
22
  input: z.ZodLazy<z.ZodReadonly<z.ZodObject<{
22
23
  transport: z.ZodEnum<{
23
24
  readonly json: "json";
@@ -28,6 +29,7 @@ export declare const mediaModelsSchema: z.ZodLazy<z.ZodArray<z.ZodReadonly<z.Zod
28
29
  readonly own: "own";
29
30
  }>;
30
31
  maxDimension: z.ZodNullable<z.ZodNumber>;
32
+ maxDimensionDocumented: z.ZodBoolean;
31
33
  defaultWidth: z.ZodNumber;
32
34
  defaultHeight: z.ZodNumber;
33
35
  steps: z.ZodEnum<{
@@ -87,8 +89,8 @@ export declare const mediaModelsSchema: z.ZodLazy<z.ZodArray<z.ZodReadonly<z.Zod
87
89
  readonly ultrawide: "21:9";
88
90
  readonly ultratall: "9:21";
89
91
  }>;
92
+ supportsAudio: z.ZodBoolean;
90
93
  defaultGenerateAudio: z.ZodNullable<z.ZodBoolean>;
91
- maxPromptLength: z.ZodNumber;
92
94
  referenceRoles: z.ZodReadonly<z.ZodArray<z.ZodEnum<{
93
95
  readonly first: "first-frame";
94
96
  readonly last: "last-frame";
@@ -302,6 +304,7 @@ export declare const appMediaModelsOutcomeSchema: z.ZodUnion<readonly [z.ZodLazy
302
304
  readonly jpeg: "jpeg";
303
305
  readonly webp: "webp";
304
306
  }>>>;
307
+ maxPromptLength: z.ZodNumber;
305
308
  input: z.ZodLazy<z.ZodReadonly<z.ZodObject<{
306
309
  transport: z.ZodEnum<{
307
310
  readonly json: "json";
@@ -312,6 +315,7 @@ export declare const appMediaModelsOutcomeSchema: z.ZodUnion<readonly [z.ZodLazy
312
315
  readonly own: "own";
313
316
  }>;
314
317
  maxDimension: z.ZodNullable<z.ZodNumber>;
318
+ maxDimensionDocumented: z.ZodBoolean;
315
319
  defaultWidth: z.ZodNumber;
316
320
  defaultHeight: z.ZodNumber;
317
321
  steps: z.ZodEnum<{
@@ -371,8 +375,8 @@ export declare const appMediaModelsOutcomeSchema: z.ZodUnion<readonly [z.ZodLazy
371
375
  readonly ultrawide: "21:9";
372
376
  readonly ultratall: "9:21";
373
377
  }>;
378
+ supportsAudio: z.ZodBoolean;
374
379
  defaultGenerateAudio: z.ZodNullable<z.ZodBoolean>;
375
- maxPromptLength: z.ZodNumber;
376
380
  referenceRoles: z.ZodReadonly<z.ZodArray<z.ZodEnum<{
377
381
  readonly first: "first-frame";
378
382
  readonly last: "last-frame";
package/dist/browser.d.ts CHANGED
@@ -1,2 +1,3 @@
1
+ export { MAX_MEDIA_PROMPT_LENGTH } from "./model-catalog.js";
1
2
  export { mediaFailureSchema, readMediaFailure } from "./browser-failure.js";
2
3
  export type { MediaFailure, MediaGenerationDiagnostic, } from "./browser-failure.js";
package/dist/browser.js CHANGED
@@ -1 +1,2 @@
1
+ export { MAX_MEDIA_PROMPT_LENGTH } from "./model-catalog.js";
1
2
  export { mediaFailureSchema, readMediaFailure } from "./browser-failure.js";
@@ -17,7 +17,7 @@ export function estimate(settings, references) {
17
17
  params.steps = settings.steps;
18
18
  try {
19
19
  if (isGatewayModel(settings.model))
20
- return estimateGatewayMilliunits(settings);
20
+ return estimateGatewayMilliunits(settings, references);
21
21
  return estimateImageMilliunits(settings.model, params);
22
22
  }
23
23
  catch (cause) {
@@ -53,6 +53,29 @@ function workersAiInput(payload) {
53
53
  },
54
54
  };
55
55
  }
56
+ /**
57
+ * The one AI Gateway routing rule: a call keeps the host's gateway option only
58
+ * when its input is gateway-compatible.
59
+ * - A stream input never is. The binding's documented multipart input is a
60
+ * ReadableStream body
61
+ * (https://developers.cloudflare.com/changelog/2025-11-25-flux-2-dev-workers-ai/),
62
+ * and the gateway route rejects it: "AI Gateway does not support
63
+ * ReadableStreams yet".
64
+ * - An image or frame input never is. AI Gateway documents no request body
65
+ * limit (https://developers.cloudflare.com/ai-gateway/reference/limits/), yet
66
+ * it rejects a first-frame Seedance request (~700 KB data URI) with
67
+ * `7003: User Input Error` that succeeds on the direct binding.
68
+ * Metering stays correct either way: Media meters from its own estimate, not
69
+ * from gateway logs.
70
+ */
71
+ function routedOptions(options, signal, input) {
72
+ const compatible = !input.stream && input.images === 0;
73
+ if (compatible || options === undefined)
74
+ return signal === undefined ? options : { ...options, signal };
75
+ const { gateway, ...direct } = options;
76
+ void gateway;
77
+ return signal === undefined ? direct : { ...direct, signal };
78
+ }
56
79
  /** Retain the original provider failure alongside its bounded code and category. */
57
80
  function providerFailure(model, cause) {
58
81
  const provider = {
@@ -76,7 +99,10 @@ async function generateThroughGateway(ai, settings, references, signal, options,
76
99
  let providerValue;
77
100
  try {
78
101
  const input = gatewayInput(settings, references, outputFormat);
79
- providerValue = await ai.run(settings.model, input, signal === undefined ? options : { ...options, signal });
102
+ providerValue = await ai.run(settings.model, input, routedOptions(options, signal, {
103
+ stream: false,
104
+ images: references.length,
105
+ }));
80
106
  }
81
107
  catch (cause) {
82
108
  throw providerFailure(settings.model, cause);
@@ -121,7 +147,10 @@ export async function generate(ai, settings, references, signal, options, fetchR
121
147
  }
122
148
  let providerValue;
123
149
  try {
124
- providerValue = await ai.run(settings.model, workersAiInput(providerInput), signal === undefined ? options : { ...options, signal });
150
+ providerValue = await ai.run(settings.model, workersAiInput(providerInput), routedOptions(options, signal, {
151
+ stream: providerInput.transport === "multipart",
152
+ images: references.length,
153
+ }));
125
154
  }
126
155
  catch (cause) {
127
156
  throw providerFailure(settings.model, cause);
@@ -1,9 +1,8 @@
1
- import { getMediaModel, MediaSettingsError } from "./model-catalog.js";
1
+ import { getMediaModel, MAX_REFERENCE_COUNT, MediaSettingsError, } from "./model-catalog.js";
2
2
  import { neuronsToMilliunits } from "./neurons.js";
3
3
  import * as z from "zod";
4
4
  const TILE = 512;
5
5
  const MAX_DIMENSION = 4_096;
6
- const MAX_REFERENCES = 4;
7
6
  const imageParamsSchema = z.lazy(() => z
8
7
  .object({
9
8
  width: z.number().int().min(1).max(MAX_DIMENSION),
@@ -16,7 +15,7 @@ const imageParamsSchema = z.lazy(() => z
16
15
  height: z.number().int().min(1).max(MAX_DIMENSION),
17
16
  })
18
17
  .strict())
19
- .max(MAX_REFERENCES)
18
+ .max(MAX_REFERENCE_COUNT)
20
19
  .optional(),
21
20
  })
22
21
  .strict());
@@ -34,7 +33,7 @@ const checked = (modelId, params) => {
34
33
  if (model === undefined)
35
34
  throw new MediaSettingsError(`Image cost unknown for model "${modelId}"`);
36
35
  if (references.length > model.input.maxReferences)
37
- return invalid(`${modelId} does not accept references`);
36
+ return invalid(`${modelId} accepts at most ${String(model.input.maxReferences)} references`);
38
37
  const normalized = {
39
38
  width: candidate.width,
40
39
  height: candidate.height,
@@ -13,11 +13,20 @@ export type MediaUnits = Readonly<{
13
13
  /** Billing units for validated settings; video bills its requested length. */
14
14
  export declare function mediaUnits(settings: MediaGenerationSettings): MediaUnits;
15
15
  /** Milliunits (1 cent = 1,000) of provider cost, rounded up. */
16
- export declare function estimateGatewayMilliunits(settings: MediaGenerationSettings): number;
16
+ export declare function estimateGatewayMilliunits(settings: MediaGenerationSettings, references: readonly Readonly<{
17
+ width: number;
18
+ height: number;
19
+ }>[]): number;
17
20
  /** The provider JSON body for one validated gateway generation. */
18
21
  export declare function gatewayInput(settings: MediaGenerationSettings, references?: readonly MediaReferenceBytes[], outputFormat?: "png" | "jpeg" | "webp"): import("./video-providers/minimax.js").MinimaxGatewayInput | import("./video-providers/unified.js").UnifiedGatewayInput | {
19
22
  prompt: string;
20
23
  size: string;
21
24
  quality: string;
22
25
  output_format: "png" | "jpeg" | "webp";
26
+ } | {
27
+ images: string[];
28
+ prompt: string;
29
+ size: string;
30
+ quality: string;
31
+ output_format: "png" | "jpeg" | "webp";
23
32
  };
@@ -1,5 +1,6 @@
1
1
  import { getMediaModel, MediaSettingsError } from "./model-catalog.js";
2
2
  import { validateMediaGenerationSettings, estimateVideoMilliunits, } from "./index.js";
3
+ import { imageDataUri } from "./video-providers/reference-encoding.js";
3
4
  import { videoProviderAdapters, } from "./video-providers/index.js";
4
5
  export const isGatewayModel = (id) => id === "openai/gpt-image-2" || getMediaModel(id)?.kind === "video";
5
6
  /** Billing units for validated settings; video bills its requested length. */
@@ -28,33 +29,81 @@ const gptImageOutputTokens = new Map([
28
29
  ["1024x1536", 1_584],
29
30
  ["1536x1024", 1_568],
30
31
  ]);
31
- /** USD per million tokens: text input and image output. */
32
- const gptImageRates = { input: 5, outputImage: 30 };
32
+ /**
33
+ * USD per million tokens: text input, image input and image output, as
34
+ * Cloudflare publishes them for this exact model.
35
+ * https://developers.cloudflare.com/ai/models/openai/gpt-image-2/
36
+ */
37
+ const gptImageRates = { input: 5, inputImage: 8, outputImage: 30 };
38
+ /**
39
+ * Upper bound on the image input tokens one reference adds. OpenAI publishes
40
+ * no input-token rule for gpt-image-2, which always reads inputs at high
41
+ * fidelity, so this applies the GPT Image high-fidelity rule OpenAI does
42
+ * publish (images-vision guide, GPT Image 1): fit within 2048 px, cap the
43
+ * short side at 512 px, 65 base + 129 per 512 px tile, plus 4,160 extra
44
+ * tokens for a square image or 6,240 for any other aspect.
45
+ * https://developers.openai.com/api/docs/guides/images-vision
46
+ * Community measurements put gpt-image-2 at most 1,536 image tokens per
47
+ * input, so this bound over-bills rather than under-bills; lower it only
48
+ * against real gateway input-token counts.
49
+ */
50
+ function gptImageReferenceTokens(reference) {
51
+ const fit = Math.min(1, 2048 / Math.max(reference.width, reference.height));
52
+ const short = Math.min(reference.width, reference.height) * fit;
53
+ const scale = fit * Math.min(1, 512 / short);
54
+ const tiles = Math.ceil((reference.width * scale) / 512) *
55
+ Math.ceil((reference.height * scale) / 512);
56
+ const aspect = reference.width === reference.height ? 4_160 : 6_240;
57
+ return 65 + 129 * tiles + aspect;
58
+ }
33
59
  const sizeOf = (settings) => `${String(settings.width)}x${String(settings.height)}`;
34
60
  /** Milliunits (1 cent = 1,000) of provider cost, rounded up. */
35
- export function estimateGatewayMilliunits(settings) {
61
+ export function estimateGatewayMilliunits(settings, references) {
36
62
  if (settings.model === "openai/gpt-image-2") {
37
63
  const output = gptImageOutputTokens.get(sizeOf(settings));
38
64
  if (output === undefined)
39
65
  throw new MediaSettingsError("unsupported GPT Image 2 size");
40
- // A bound on prompt tokens: at most one token per character.
41
- const input = settings.prompt.length;
66
+ if (references.length !== (settings.references?.length ?? 0))
67
+ throw new MediaSettingsError("resolved references must match every admitted reference");
68
+ // A bound on prompt tokens: at most one token per character, plus one
69
+ // text token OpenAI is observed to add per input image.
70
+ const input = settings.prompt.length + references.length;
71
+ const inputImage = references.reduce((sum, reference) => sum + gptImageReferenceTokens(reference), 0);
42
72
  // tokens * USD/M * 100,000 milliunits/USD / 1,000,000 = tokens * rate / 10
43
- return Math.ceil((output * gptImageRates.outputImage + input * gptImageRates.input) / 10);
73
+ return Math.ceil((output * gptImageRates.outputImage +
74
+ input * gptImageRates.input +
75
+ inputImage * gptImageRates.inputImage) /
76
+ 10);
44
77
  }
45
78
  if (getMediaModel(settings.model)?.kind === "video")
46
79
  return estimateVideoMilliunits(settings);
47
80
  throw new MediaSettingsError(`${settings.model} is not a gateway model`);
48
81
  }
82
+ /**
83
+ * One `images[]` entry for `openai/gpt-image-2`: a base64 data URI of a png,
84
+ * jpeg or webp image (Cloudflare's published input schema).
85
+ * https://developers.cloudflare.com/ai/models/openai/gpt-image-2/
86
+ */
87
+ function gptImageReference(reference) {
88
+ if (reference.format === "gif")
89
+ throw new MediaSettingsError("openai/gpt-image-2 accepts png, jpeg or webp reference images, not gif");
90
+ return imageDataUri(reference);
91
+ }
49
92
  /** The provider JSON body for one validated gateway generation. */
50
93
  export function gatewayInput(settings, references = [], outputFormat = "png") {
51
- if (settings.model === "openai/gpt-image-2")
52
- return {
94
+ if (settings.model === "openai/gpt-image-2") {
95
+ const body = {
53
96
  prompt: settings.prompt,
54
97
  size: sizeOf(settings),
55
98
  quality: "medium",
56
99
  output_format: outputFormat,
57
100
  };
101
+ if (references.length !== (settings.references?.length ?? 0))
102
+ throw new MediaSettingsError("resolved references must match every admitted reference");
103
+ if (references.length === 0)
104
+ return body;
105
+ return { ...body, images: references.map(gptImageReference) };
106
+ }
58
107
  const checked = validateMediaGenerationSettings({
59
108
  ...settings,
60
109
  references: settings.references?.map((reference) => ({ ...reference })),
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import * as z from "zod";
2
2
  import { type MediaVideoReferenceRole, type MediaVideoModelId, type MediaVideoResolution, type MediaVideoAspectRatio } from "./model-catalog.js";
3
3
  export { mediaFailureStageSchema } from "./execution-error.js";
4
- export { getMediaModel, listMediaModels, mediaModelInputSchema, mediaModelSchema, mediaRequestedImageFormatSchema, mediaModels, mediaModelTierSchema, MediaSettingsError, mediaVideoModelIdSchema, mediaVideoResolutionSchema, mediaVideoAspectRatioSchema, mediaVideoReferenceRoleSchema, mediaVideoFormatSchema, mediaVideoSchema, } from "./model-catalog.js";
4
+ export { MAX_MEDIA_PROMPT_LENGTH, getMediaModel, MAX_REFERENCE_COUNT, listMediaModels, mediaModelInputSchema, mediaModelSchema, mediaRequestedImageFormatSchema, mediaModels, mediaModelTierSchema, MediaSettingsError, mediaVideoModelIdSchema, mediaVideoResolutionSchema, mediaVideoAspectRatioSchema, mediaVideoReferenceRoleSchema, mediaVideoFormatSchema, mediaVideoSchema, } from "./model-catalog.js";
5
5
  export type { MediaModel, MediaModelInput, MediaModelTier, MediaVideoModelId, MediaVideoResolution, MediaVideoAspectRatio, MediaVideoReferenceRole, MediaVideoModel, MediaVideoFormat, } from "./model-catalog.js";
6
6
  /** A reference file selected by the host, with its destination identity and path. */
7
7
  export type MediaReference = Readonly<{
@@ -80,8 +80,6 @@ export declare const viewDimensionSchema: z.ZodLazy<z.ZodNumber>;
80
80
  * a lossy JPEG, so a viewer can step the dimension down to meet a byte budget.
81
81
  */
82
82
  export type ImageViewFitter = (bytes: Uint8Array, maxDimension: number) => Promise<FittedReferenceImage>;
83
- /** Maximum reference files in one generation request. */
84
- export declare const MAX_REFERENCE_COUNT = 4;
85
83
  /** Maximum reference width and height sent to inference, in pixels. */
86
84
  export declare const MAX_REFERENCE_DIMENSION = 512;
87
85
  /** Maximum decoded bytes per reference image. */
package/dist/index.js CHANGED
@@ -1,16 +1,16 @@
1
1
  import * as z from "zod";
2
2
  import { reportCatchFailure } from "./failure.js";
3
- import { getMediaModel, MediaSettingsError, mediaVideoModelIdSchema, mediaVideoResolutionSchema, mediaVideoAspectRatioSchema, mediaVideoReferenceRoleSchema, } from "./model-catalog.js";
3
+ import { getMediaModel, MAX_REFERENCE_COUNT, MediaSettingsError, mediaVideoModelIdSchema, mediaVideoResolutionSchema, mediaVideoAspectRatioSchema, mediaVideoReferenceRoleSchema, MAX_MEDIA_PROMPT_LENGTH, } from "./model-catalog.js";
4
4
  export { mediaFailureStageSchema } from "./execution-error.js";
5
- export { getMediaModel, listMediaModels, mediaModelInputSchema, mediaModelSchema, mediaRequestedImageFormatSchema, mediaModels, mediaModelTierSchema, MediaSettingsError, mediaVideoModelIdSchema, mediaVideoResolutionSchema, mediaVideoAspectRatioSchema, mediaVideoReferenceRoleSchema, mediaVideoFormatSchema, mediaVideoSchema, } from "./model-catalog.js";
5
+ export { MAX_MEDIA_PROMPT_LENGTH, getMediaModel, MAX_REFERENCE_COUNT, listMediaModels, mediaModelInputSchema, mediaModelSchema, mediaRequestedImageFormatSchema, mediaModels, mediaModelTierSchema, MediaSettingsError, mediaVideoModelIdSchema, mediaVideoResolutionSchema, mediaVideoAspectRatioSchema, mediaVideoReferenceRoleSchema, mediaVideoFormatSchema, mediaVideoSchema, } from "./model-catalog.js";
6
6
  /** The long-edge bounds a viewer may ask for when fitting an image to look at. */
7
7
  export const MIN_VIEW_DIMENSION = 256;
8
8
  /** Maximum long-edge size accepted by an image viewer, in pixels. */
9
9
  export const MAX_VIEW_DIMENSION = 512;
10
10
  /** Decode a viewer long-edge size within the supported pixel bounds. */
11
11
  export const viewDimensionSchema = z.lazy(() => z.number().int().min(MIN_VIEW_DIMENSION).max(MAX_VIEW_DIMENSION));
12
- /** Maximum reference files in one generation request. */
13
- export const MAX_REFERENCE_COUNT = 4;
12
+ /** Reference images in one video request: every verified video model's catalog value. */
13
+ const MAX_VIDEO_REFERENCE_COUNT = 4;
14
14
  /** Maximum reference width and height sent to inference, in pixels. */
15
15
  export const MAX_REFERENCE_DIMENSION = 512;
16
16
  /** Maximum decoded bytes per reference image. */
@@ -42,8 +42,8 @@ export const mediaGenerationSettingsSchema = z
42
42
  .object({
43
43
  /** Provider catalog model identifier. */
44
44
  model: z.string().min(1).max(256),
45
- /** Generation prompt. */
46
- prompt: z.string().min(1).max(2_048),
45
+ /** Generation prompt; each model's `maxPromptLength` is enforced on top of this ceiling. */
46
+ prompt: z.string().min(1).max(MAX_MEDIA_PROMPT_LENGTH),
47
47
  /** Requested image width or nominal video selector, not decoded MP4 dimensions. */
48
48
  width: z.number().int().min(1).max(5_040),
49
49
  /** Requested image height or nominal video selector, not decoded MP4 dimensions. */
@@ -116,6 +116,8 @@ function normalizeGenerationSettings(input, normalize) {
116
116
  if (model === undefined)
117
117
  throw new MediaSettingsError(`unknown Media model "${modelId}"`);
118
118
  const prompt = requiredString(input.prompt, "prompt");
119
+ if (prompt.length > model.maxPromptLength)
120
+ throw new MediaSettingsError(`${model.id} prompt is ${String(prompt.length)} characters; its limit is ${String(model.maxPromptLength)}`);
119
121
  const maximumDimension = model.video === null ? 4_096 : 5_040;
120
122
  const width = boundedInteger(input.width, "width", 1, maximumDimension);
121
123
  const height = boundedInteger(input.height, "height", 1, maximumDimension);
@@ -142,8 +144,6 @@ function normalizeGenerationSettings(input, normalize) {
142
144
  durationSeconds = boundedInteger(input.durationSeconds ?? model.video.defaultSeconds, "durationSeconds", model.video.minSeconds, model.video.maxSeconds);
143
145
  if (!model.video.durations.includes(durationSeconds))
144
146
  throw new MediaSettingsError(`${model.id} accepts durationSeconds ${model.video.durations.join(", ")}`);
145
- if (prompt.length > model.video.maxPromptLength)
146
- throw new MediaSettingsError(`${model.id} prompt exceeds ${String(model.video.maxPromptLength)} characters`);
147
147
  }
148
148
  let steps;
149
149
  if (input.steps !== undefined && model.input.steps === "fixed")
@@ -194,20 +194,27 @@ function normalizeGenerationSettings(input, normalize) {
194
194
  role: reference.role ?? mediaVideoReferenceRoleSchema.enum.first,
195
195
  }));
196
196
  const roles = references.map((reference) => reference.role);
197
- if (roles.some((role) => role === undefined || !video.referenceRoles.includes(role)))
198
- throw new MediaSettingsError(`${model.id} does not accept this frame/reference role`);
197
+ const refused = roles.find((role) => role === undefined || !video.referenceRoles.includes(role));
198
+ if (refused !== undefined || roles.includes(undefined))
199
+ throw new MediaSettingsError(refused === "last-frame"
200
+ ? `${model.label} has no last frame`
201
+ : `${model.label} does not take ${String(refused)} images`);
199
202
  const first = roles.filter((role) => role === "first-frame").length;
200
203
  const last = roles.filter((role) => role === "last-frame").length;
201
204
  const referenceCount = roles.filter((role) => role === "reference").length;
202
- if (first > 1 ||
203
- last > 1 ||
204
- (last > 0 && first !== 1) ||
205
- (referenceCount > 0 && first + last > 0))
206
- throw new MediaSettingsError("video frame and reference modes cannot mix; last frame requires one first frame");
207
- if (referenceCount > video.maxReferenceImages ||
208
- (referenceCount > 0 &&
209
- !video.referenceResolutions.includes(format.resolution)))
210
- throw new MediaSettingsError(`${model.id} does not accept reference-image mode at this resolution/count`);
205
+ if (first > 1 || last > 1)
206
+ throw new MediaSettingsError(video.referenceRoles.includes("last-frame")
207
+ ? `${model.label} takes one first frame and one last frame`
208
+ : `${model.label} takes one first frame`);
209
+ if (last > 0 && first !== 1)
210
+ throw new MediaSettingsError("A last frame needs a first frame as well");
211
+ if (referenceCount > 0 && first + last > 0)
212
+ throw new MediaSettingsError("Frames and references cannot be mixed; use a first/last frame or references");
213
+ if (referenceCount > video.maxReferenceImages)
214
+ throw new MediaSettingsError(`${model.label} takes up to ${String(video.maxReferenceImages)} reference images`);
215
+ if (referenceCount > 0 &&
216
+ !video.referenceResolutions.includes(format.resolution))
217
+ throw new MediaSettingsError(`${model.label} takes reference images only at ${video.referenceResolutions.join(", ")}`);
211
218
  }
212
219
  }
213
220
  if (references !== undefined)
@@ -225,8 +232,8 @@ export const mediaVideoReferenceSchema = z.lazy(() => z.strictObject({
225
232
  export const mediaVideoSelectionSchema = z.lazy(() => z.strictObject({
226
233
  /** Verified catalog model identifier. */
227
234
  model: mediaVideoModelIdSchema,
228
- /** Trimmed prompt; Seedance allows 2,000 characters, Veo uses the retained host cap of 2,048. */
229
- prompt: z.string().min(1).max(2_048),
235
+ /** Trimmed prompt; the selected model's `maxPromptLength` is enforced on top of this ceiling. */
236
+ prompt: z.string().min(1).max(MAX_MEDIA_PROMPT_LENGTH),
230
237
  /** Model-specific resolution, default 720p. */
231
238
  resolution: mediaVideoResolutionSchema.optional(),
232
239
  /** Model-specific aspect, default 16:9. */
@@ -239,7 +246,7 @@ export const mediaVideoSelectionSchema = z.lazy(() => z.strictObject({
239
246
  /** Up to four references, with provider-specific roles and mode limits. */
240
247
  references: z
241
248
  .array(mediaVideoReferenceSchema)
242
- .max(MAX_REFERENCE_COUNT)
249
+ .max(MAX_VIDEO_REFERENCE_COUNT)
243
250
  .optional(),
244
251
  }));
245
252
  /** Validate supported model/size/duration/reference combinations before authority or billing admission. */
@@ -40,6 +40,7 @@ export declare const mediaModelInputSchema: z.ZodLazy<z.ZodReadonly<z.ZodObject<
40
40
  readonly own: "own";
41
41
  }>;
42
42
  maxDimension: z.ZodNullable<z.ZodNumber>;
43
+ maxDimensionDocumented: z.ZodBoolean;
43
44
  defaultWidth: z.ZodNumber;
44
45
  defaultHeight: z.ZodNumber;
45
46
  steps: z.ZodEnum<{
@@ -55,6 +56,11 @@ export declare const mediaModelInputSchema: z.ZodLazy<z.ZodReadonly<z.ZodObject<
55
56
  referenceField: z.ZodNullable<z.ZodString>;
56
57
  maxReferences: z.ZodNumber;
57
58
  }, z.core.$strict>>>;
59
+ /**
60
+ * Schema ceiling for reference lists: the largest per-model limit. Each model's
61
+ * own `maxReferences` / `maxReferenceImages` is enforced after parsing.
62
+ */
63
+ export declare const MAX_REFERENCE_COUNT = 16;
58
64
  /** Verified Unified AI video identifiers; no inferred family aliases. */
59
65
  export declare const mediaVideoModelIdSchema: z.ZodEnum<{
60
66
  /** Seedance 2.0. */
@@ -110,7 +116,7 @@ export type MediaVideoAspectRatio = z.output<typeof mediaVideoAspectRatioSchema>
110
116
  export declare const mediaVideoReferenceRoleSchema: z.ZodEnum<{
111
117
  /** First frame; required when a last frame is supplied. */
112
118
  readonly first: "first-frame";
113
- /** Last frame, supported by Seedance only. */
119
+ /** Last frame, supported by Seedance only (`last_frame_image`). */
114
120
  readonly last: "last-frame";
115
121
  /** One image in Seedance reference-image mode. */
116
122
  readonly reference: "reference";
@@ -226,12 +232,12 @@ export declare const mediaVideoSchema: z.ZodLazy<z.ZodReadonly<z.ZodObject<{
226
232
  /** Portrait ultrawide. */
227
233
  readonly ultratall: "9:21";
228
234
  }>;
235
+ supportsAudio: z.ZodBoolean;
229
236
  defaultGenerateAudio: z.ZodNullable<z.ZodBoolean>;
230
- maxPromptLength: z.ZodNumber;
231
237
  referenceRoles: z.ZodReadonly<z.ZodArray<z.ZodEnum<{
232
238
  /** First frame; required when a last frame is supplied. */
233
239
  readonly first: "first-frame";
234
- /** Last frame, supported by Seedance only. */
240
+ /** Last frame, supported by Seedance only (`last_frame_image`). */
235
241
  readonly last: "last-frame";
236
242
  /** One image in Seedance reference-image mode. */
237
243
  readonly reference: "reference";
@@ -268,6 +274,11 @@ export declare const mediaVideoSchema: z.ZodLazy<z.ZodReadonly<z.ZodObject<{
268
274
  }, z.core.$strict>>>;
269
275
  /** Immutable video capability and price metadata. */
270
276
  export type MediaVideoModel = z.output<typeof mediaVideoSchema>;
277
+ /**
278
+ * Structural ceiling for any prompt on the wire. Each model's own
279
+ * `maxPromptLength` is the real bound; no model may exceed this ceiling.
280
+ */
281
+ export declare const MAX_MEDIA_PROMPT_LENGTH = 32000;
271
282
  /** Public wire grammar for a catalog entry returned by the Media owner. */
272
283
  export declare const mediaModelSchema: z.ZodReadonly<z.ZodObject<{
273
284
  id: z.ZodString;
@@ -296,6 +307,7 @@ export declare const mediaModelSchema: z.ZodReadonly<z.ZodObject<{
296
307
  /** WebP image encoding. */
297
308
  readonly webp: "webp";
298
309
  }>>>;
310
+ maxPromptLength: z.ZodNumber;
299
311
  input: z.ZodLazy<z.ZodReadonly<z.ZodObject<{
300
312
  transport: z.ZodEnum<{
301
313
  /** JSON provider input. */
@@ -310,6 +322,7 @@ export declare const mediaModelSchema: z.ZodReadonly<z.ZodObject<{
310
322
  readonly own: "own";
311
323
  }>;
312
324
  maxDimension: z.ZodNullable<z.ZodNumber>;
325
+ maxDimensionDocumented: z.ZodBoolean;
313
326
  defaultWidth: z.ZodNumber;
314
327
  defaultHeight: z.ZodNumber;
315
328
  steps: z.ZodEnum<{
@@ -399,12 +412,12 @@ export declare const mediaModelSchema: z.ZodReadonly<z.ZodObject<{
399
412
  /** Portrait ultrawide. */
400
413
  readonly ultratall: "9:21";
401
414
  }>;
415
+ supportsAudio: z.ZodBoolean;
402
416
  defaultGenerateAudio: z.ZodNullable<z.ZodBoolean>;
403
- maxPromptLength: z.ZodNumber;
404
417
  referenceRoles: z.ZodReadonly<z.ZodArray<z.ZodEnum<{
405
418
  /** First frame; required when a last frame is supplied. */
406
419
  readonly first: "first-frame";
407
- /** Last frame, supported by Seedance only. */
420
+ /** Last frame, supported by Seedance only (`last_frame_image`). */
408
421
  readonly last: "last-frame";
409
422
  /** One image in Seedance reference-image mode. */
410
423
  readonly reference: "reference";
@@ -26,7 +26,11 @@ export const mediaModelTierSchema = z.enum({
26
26
  /** Media's provider catalog: Workers AI images plus gateway image and video models. */
27
27
  export const mediaModelInputSchema = z.lazy(() => z
28
28
  .strictObject({
29
- /** Provider input transport. */
29
+ /**
30
+ * Provider input transport. Multipart is the binding's documented
31
+ * ReadableStream body, which AI Gateway cannot carry, so multipart models
32
+ * run without the gateway option (see cloudflare/provider.ts).
33
+ */
30
34
  transport: z.enum({
31
35
  /** JSON provider input. */
32
36
  json: "json",
@@ -42,6 +46,8 @@ export const mediaModelInputSchema = z.lazy(() => z
42
46
  }),
43
47
  /** Maximum accepted dimension in pixels, or null when unbounded by this model. */
44
48
  maxDimension: z.number().int().positive().nullable(),
49
+ /** Whether the provider's own page documents `maxDimension`; false for host caps. */
50
+ maxDimensionDocumented: z.boolean(),
45
51
  /** Default output width in pixels. */
46
52
  defaultWidth: z.number().int().positive(),
47
53
  /** Default output height in pixels. */
@@ -65,6 +71,11 @@ export const mediaModelInputSchema = z.lazy(() => z
65
71
  maxReferences: z.number().int().nonnegative(),
66
72
  })
67
73
  .readonly());
74
+ /**
75
+ * Schema ceiling for reference lists: the largest per-model limit. Each model's
76
+ * own `maxReferences` / `maxReferenceImages` is enforced after parsing.
77
+ */
78
+ export const MAX_REFERENCE_COUNT = 16;
68
79
  /** Verified Unified AI video identifiers; no inferred family aliases. */
69
80
  export const mediaVideoModelIdSchema = z.enum({
70
81
  /** Seedance 2.0. */
@@ -114,7 +125,7 @@ export const mediaVideoAspectRatioSchema = z.enum({
114
125
  export const mediaVideoReferenceRoleSchema = z.enum({
115
126
  /** First frame; required when a last frame is supplied. */
116
127
  first: "first-frame",
117
- /** Last frame, supported by Seedance only. */
128
+ /** Last frame, supported by Seedance only (`last_frame_image`). */
118
129
  last: "last-frame",
119
130
  /** One image in Seedance reference-image mode. */
120
131
  reference: "reference",
@@ -158,10 +169,10 @@ export const mediaVideoSchema = z.lazy(() => z
158
169
  defaultResolution: mediaVideoResolutionSchema,
159
170
  /** Aspect selected when omitted. */
160
171
  defaultAspectRatio: mediaVideoAspectRatioSchema,
172
+ /** Whether the model documents a `generate_audio` choice. */
173
+ supportsAudio: z.boolean(),
161
174
  /** Audio default, or null when not documented and left to the provider. */
162
175
  defaultGenerateAudio: z.boolean().nullable(),
163
- /** Prompt bound: provider bound for Seedance, retained host bound for Veo. */
164
- maxPromptLength: z.number().int().positive(),
165
176
  /** Frame or reference roles this provider accepts. */
166
177
  referenceRoles: z.array(mediaVideoReferenceRoleSchema).readonly(),
167
178
  /** Reference-image mode count; first/last frame mode has its own grammar. */
@@ -183,6 +194,13 @@ export const mediaVideoSchema = z.lazy(() => z
183
194
  .readonly(),
184
195
  })
185
196
  .readonly());
197
+ /**
198
+ * Structural ceiling for any prompt on the wire. Each model's own
199
+ * `maxPromptLength` is the real bound; no model may exceed this ceiling.
200
+ */
201
+ export const MAX_MEDIA_PROMPT_LENGTH = 32_000;
202
+ /** Prompt cap kept for models whose provider documents no prompt limit. */
203
+ const UNDOCUMENTED_PROMPT_LENGTH = 2_048;
186
204
  /** Public wire grammar for a catalog entry returned by the Media owner. */
187
205
  export const mediaModelSchema = z
188
206
  .strictObject({
@@ -201,6 +219,11 @@ export const mediaModelSchema = z
201
219
  tier: mediaModelTierSchema,
202
220
  /** Verified provider encodings; an empty list refuses pinned formats. */
203
221
  outputFormats: z.array(mediaRequestedImageFormatSchema).max(3).readonly(),
222
+ /**
223
+ * Maximum prompt length in characters for this model: the provider's
224
+ * documented limit, or the retained 2,048 host cap where none is documented.
225
+ */
226
+ maxPromptLength: z.number().int().positive().max(MAX_MEDIA_PROMPT_LENGTH),
204
227
  /** Model-native input options and reference limits. */
205
228
  input: mediaModelInputSchema,
206
229
  /**
@@ -271,8 +294,12 @@ function videoModel(id, label, tier, provider, rates) {
271
294
  formats,
272
295
  defaultResolution: "720p",
273
296
  defaultAspectRatio: "16:9",
297
+ // Seedance documents `generate_audio` without a default; Veo documents
298
+ // `generate_audio` with default true.
299
+ // https://developers.cloudflare.com/ai/models/bytedance/seedance-2.0/
300
+ // https://developers.cloudflare.com/ai/models/google/veo-3.1/
301
+ supportsAudio: true,
274
302
  defaultGenerateAudio: seedance ? null : true,
275
- maxPromptLength: seedance ? 2000 : 2048,
276
303
  referenceRoles: Object.freeze(seedance
277
304
  ? mediaVideoReferenceRoleSchema.options
278
305
  : [mediaVideoReferenceRoleSchema.enum.first]),
@@ -292,6 +319,10 @@ function videoModel(id, label, tier, provider, rates) {
292
319
  id,
293
320
  label,
294
321
  tier,
322
+ // Seedance: the provider's own 2,000-character bound (retained from the
323
+ // earlier video contract).
324
+ // Veo documents no character limit; keep the retained 2,048 host cap.
325
+ maxPromptLength: seedance ? 2_000 : UNDOCUMENTED_PROMPT_LENGTH,
295
326
  kind: "video",
296
327
  video,
297
328
  sizes: Object.freeze(formats.map(({ width, height }) => `${String(width)}x${String(height)}`)),
@@ -299,6 +330,7 @@ function videoModel(id, label, tier, provider, rates) {
299
330
  transport: "json",
300
331
  dimensions: "own",
301
332
  maxDimension: Math.max(...formats.map(({ width, height }) => Math.max(width, height))),
333
+ maxDimensionDocumented: false,
302
334
  defaultWidth: 1280,
303
335
  defaultHeight: 720,
304
336
  steps: "fixed",
@@ -358,10 +390,15 @@ function minimaxVideoModel() {
358
390
  // Not merely undocumented: the schema has no audio field at all, so the
359
391
  // dispatcher refuses an explicit generateAudio choice outright.
360
392
  defaultGenerateAudio: null,
361
- maxPromptLength: 7000,
362
- referenceRoles: Object.freeze(mediaVideoReferenceRoleSchema.options),
363
- // The host-wide MAX_REFERENCE_COUNT (4) already governs every call; the
364
- // vendor page documents up to nine, which this catalog cannot reach.
393
+ supportsAudio: false,
394
+ // The vendor page documents `first_frame` and `reference_image` roles only;
395
+ // it names no last frame.
396
+ referenceRoles: Object.freeze([
397
+ mediaVideoReferenceRoleSchema.enum.first,
398
+ mediaVideoReferenceRoleSchema.enum.reference,
399
+ ]),
400
+ // The vendor page documents up to nine; this stays at the conservative
401
+ // verified value of four until the gateway schema is confirmed.
365
402
  maxReferenceImages: 4,
366
403
  referenceResolutions: Object.freeze(resolutions),
367
404
  prices: Object.freeze([
@@ -382,6 +419,8 @@ function minimaxVideoModel() {
382
419
  id: mediaVideoModelIdSchema.enum.minimaxH3Max,
383
420
  label: "MiniMax H3 Max",
384
421
  tier: "balanced",
422
+ // 7,000 characters per the vendor page cited above.
423
+ maxPromptLength: 7_000,
385
424
  kind: "video",
386
425
  video,
387
426
  sizes: Object.freeze(formats.map(({ width, height }) => `${String(width)}x${String(height)}`)),
@@ -389,6 +428,7 @@ function minimaxVideoModel() {
389
428
  transport: "json",
390
429
  dimensions: "own",
391
430
  maxDimension: Math.max(...formats.map(({ width, height }) => Math.max(width, height))),
431
+ maxDimensionDocumented: false,
392
432
  defaultWidth: defaultFormat.width,
393
433
  defaultHeight: defaultFormat.height,
394
434
  steps: "fixed",
@@ -404,6 +444,8 @@ function minimaxVideoModel() {
404
444
  const models = Object.freeze([
405
445
  freezeModel({
406
446
  id: "@cf/black-forest-labs/flux-1-schnell",
447
+ // 2,048 characters: input schema maxLength at developers.cloudflare.com/workers-ai/models/flux-1-schnell/.
448
+ maxPromptLength: 2_048,
407
449
  outputFormats: Object.freeze(["jpeg"]),
408
450
  tier: "draft",
409
451
  label: "Flux 1 Schnell — cheap/fast sketches",
@@ -411,6 +453,7 @@ const models = Object.freeze([
411
453
  transport: "json",
412
454
  dimensions: "none",
413
455
  maxDimension: null,
456
+ maxDimensionDocumented: false,
414
457
  defaultWidth: 1024,
415
458
  defaultHeight: 768,
416
459
  steps: "steps",
@@ -422,12 +465,15 @@ const models = Object.freeze([
422
465
  }),
423
466
  freezeModel({
424
467
  id: "@cf/black-forest-labs/flux-2-klein-4b",
468
+ // Workers AI documents no prompt maxLength for this model; keep the 2,048 host cap.
469
+ maxPromptLength: UNDOCUMENTED_PROMPT_LENGTH,
425
470
  tier: "balanced",
426
471
  label: "Flux 2 Klein 4b — balanced default",
427
472
  input: {
428
473
  transport: "multipart",
429
474
  dimensions: "own",
430
475
  maxDimension: 1920,
476
+ maxDimensionDocumented: false,
431
477
  defaultWidth: 1024,
432
478
  defaultHeight: 768,
433
479
  steps: "fixed",
@@ -439,12 +485,15 @@ const models = Object.freeze([
439
485
  }),
440
486
  freezeModel({
441
487
  id: "@cf/black-forest-labs/flux-2-klein-9b",
488
+ // Workers AI documents no prompt maxLength for this model; keep the 2,048 host cap.
489
+ maxPromptLength: UNDOCUMENTED_PROMPT_LENGTH,
442
490
  tier: "premium",
443
491
  label: "Flux 2 Klein 9b — higher quality flux-2",
444
492
  input: {
445
493
  transport: "multipart",
446
494
  dimensions: "own",
447
495
  maxDimension: 1920,
496
+ maxDimensionDocumented: false,
448
497
  defaultWidth: 1024,
449
498
  defaultHeight: 768,
450
499
  steps: "fixed",
@@ -456,12 +505,15 @@ const models = Object.freeze([
456
505
  }),
457
506
  freezeModel({
458
507
  id: "@cf/black-forest-labs/flux-2-dev",
508
+ // Workers AI documents no prompt maxLength for this model; keep the 2,048 host cap.
509
+ maxPromptLength: UNDOCUMENTED_PROMPT_LENGTH,
459
510
  tier: "max",
460
511
  label: "Flux 2 Dev — best detailed quality, expensive",
461
512
  input: {
462
513
  transport: "multipart",
463
514
  dimensions: "own",
464
515
  maxDimension: 1920,
516
+ maxDimensionDocumented: false,
465
517
  defaultWidth: 1024,
466
518
  defaultHeight: 768,
467
519
  steps: "steps",
@@ -473,12 +525,16 @@ const models = Object.freeze([
473
525
  }),
474
526
  freezeModel({
475
527
  id: "@cf/leonardo/lucid-origin",
528
+ // Workers AI documents no prompt maxLength for this model; keep the 2,048 host cap.
529
+ maxPromptLength: UNDOCUMENTED_PROMPT_LENGTH,
476
530
  tier: "premium",
477
531
  label: "Leonardo Lucid Origin — premium prompt adherence",
478
532
  input: {
479
533
  transport: "json",
480
534
  dimensions: "own",
535
+ // https://developers.cloudflare.com/workers-ai/models/lucid-origin/ (width/height maximum)
481
536
  maxDimension: 2500,
537
+ maxDimensionDocumented: true,
482
538
  defaultWidth: 1120,
483
539
  defaultHeight: 1120,
484
540
  steps: "num_steps",
@@ -490,12 +546,16 @@ const models = Object.freeze([
490
546
  }),
491
547
  freezeModel({
492
548
  id: "@cf/leonardo/phoenix-1.0",
549
+ // Workers AI documents no prompt maxLength for this model; keep the 2,048 host cap.
550
+ maxPromptLength: UNDOCUMENTED_PROMPT_LENGTH,
493
551
  tier: "balanced",
494
552
  label: "Leonardo Phoenix 1.0 — best prompt adherence",
495
553
  input: {
496
554
  transport: "json",
497
555
  dimensions: "own",
556
+ // https://developers.cloudflare.com/workers-ai/models/phoenix-1.0/ (width/height range)
498
557
  maxDimension: 2048,
558
+ maxDimensionDocumented: true,
499
559
  defaultWidth: 1024,
500
560
  defaultHeight: 1024,
501
561
  steps: "num_steps",
@@ -509,6 +569,8 @@ const models = Object.freeze([
509
569
  // image model and ByteDance's video model return a result URL, not bytes.
510
570
  freezeModel({
511
571
  id: "openai/gpt-image-2",
572
+ // 32,000 characters: OpenAI's Images API reference gives GPT image models a 32000-character prompt limit.
573
+ maxPromptLength: 32_000,
512
574
  outputFormats: Object.freeze(["png", "jpeg", "webp"]),
513
575
  tier: "premium",
514
576
  label: "GPT Image 2 — strong text and layout, premium",
@@ -517,13 +579,19 @@ const models = Object.freeze([
517
579
  transport: "json",
518
580
  dimensions: "own",
519
581
  maxDimension: 1536,
582
+ maxDimensionDocumented: false,
520
583
  defaultWidth: 1024,
521
584
  defaultHeight: 1024,
522
585
  steps: "fixed",
523
586
  maxSteps: null,
524
587
  defaultSteps: null,
525
- referenceField: null,
526
- maxReferences: 0,
588
+ // Cloudflare documents `images[]` (base64 strings or data URIs, 1-16
589
+ // entries, routed to OpenAI's /v1/images/edits) for this exact model:
590
+ // https://developers.cloudflare.com/ai/models/openai/gpt-image-2/
591
+ // OpenAI's own page shows multipart `image[]` without stating a cap, so
592
+ // 16 is Cloudflare's published schema limit, the route Prints calls.
593
+ referenceField: "images",
594
+ maxReferences: 16,
527
595
  },
528
596
  }),
529
597
  videoModel(mediaVideoModelIdSchema.enum.seedance, "Seedance 2.0", "balanced", "seedance", [
@@ -1,6 +1,6 @@
1
1
  import type { VideoProviderAdapter } from "./types.js";
2
2
  /** MiniMax's documented `content[]` image role, distinct from our neutral one. */
3
- export type MinimaxReferenceRole = "first_frame" | "last_frame" | "reference_image";
3
+ export type MinimaxReferenceRole = "first_frame" | "reference_image";
4
4
  /** One MiniMax `content[]` entry, exactly as its schema documents it. */
5
5
  export type MinimaxContentItem = {
6
6
  type: "text";
@@ -13,7 +13,6 @@ const minimaxResolutionWire = {
13
13
  /** Our neutral reference role to MiniMax's documented `content[]` role. */
14
14
  const minimaxRoleWire = {
15
15
  "first-frame": "first_frame",
16
- "last-frame": "last_frame",
17
16
  reference: "reference_image",
18
17
  };
19
18
  /** MiniMax's documented task wrapper: `{task: {status, content: {url}}}`,
@@ -34,7 +33,9 @@ export const minimaxVideoAdapter = {
34
33
  ];
35
34
  references.forEach((image, index) => {
36
35
  const role = checked.references?.[index]?.role;
37
- const wireRole = role === undefined ? undefined : minimaxRoleWire[role];
36
+ const wireRole = role === undefined || role === "last-frame"
37
+ ? undefined
38
+ : minimaxRoleWire[role];
38
39
  if (wireRole === undefined)
39
40
  throw new MediaSettingsError("video reference role was not admitted");
40
41
  content.push({
@@ -31,6 +31,7 @@ const mediaModelsSchema: ZodLazy<ZodArray<ZodReadonly<ZodObject<{
31
31
  jpeg: "jpeg";
32
32
  webp: "webp";
33
33
  }>>>;
34
+ maxPromptLength: ZodNumber;
34
35
  input: ZodLazy<ZodReadonly<ZodObject<{
35
36
  transport: ZodEnum<{
36
37
  json: ...;
@@ -64,7 +65,6 @@ const mediaModelsSchema: ZodLazy<ZodArray<ZodReadonly<ZodObject<{
64
65
  defaultResolution: ZodEnum<...>;
65
66
  defaultAspectRatio: ZodEnum<...>;
66
67
  defaultGenerateAudio: ZodNullable<...>;
67
- maxPromptLength: ZodNumber;
68
68
  referenceRoles: ZodReadonly<...>;
69
69
  maxReferenceImages: ZodNumber;
70
70
  referenceResolutions: ZodReadonly<...>;
@@ -367,6 +367,7 @@ const appMediaModelsOutcomeSchema: ZodUnion<readonly [ZodLazy<ZodArray<ZodReadon
367
367
  jpeg: ...;
368
368
  webp: ...;
369
369
  }>>>;
370
+ maxPromptLength: ZodNumber;
370
371
  input: ZodLazy<ZodReadonly<ZodObject<{
371
372
  transport: ...;
372
373
  dimensions: ...;
@@ -156,3 +156,9 @@ Read and decode one Media host failure at the browser HTTP boundary.
156
156
  `"AiInternalError"` \| `"TypeError"` \| `"AbortError"` \| `"TimeoutError"` \|
157
157
  `"Error"`; `code`: `number` \| `null`; \}\> \| `Readonly`\<\{ `category`:
158
158
  `"result_download"`; `status`: `number`; \}\>; \} \| `null`\>
159
+
160
+ ## References
161
+
162
+ ### MAX\_MEDIA\_PROMPT\_LENGTH
163
+
164
+ Re-exports [MAX_MEDIA_PROMPT_LENGTH](index.md#max_media_prompt_length)
package/docs/api/index.md CHANGED
@@ -1101,7 +1101,6 @@ const mediaVideoSchema: ZodLazy<ZodReadonly<ZodObject<{
1101
1101
  ultratall: "9:21";
1102
1102
  }>;
1103
1103
  defaultGenerateAudio: ZodNullable<ZodBoolean>;
1104
- maxPromptLength: ZodNumber;
1105
1104
  referenceRoles: ZodReadonly<ZodArray<ZodEnum<{
1106
1105
  first: "first-frame";
1107
1106
  last: "last-frame";
@@ -1133,6 +1132,17 @@ Verified video parameters and provider costs, checked 2026-10-11.
1133
1132
 
1134
1133
  ---
1135
1134
 
1135
+ ### MAX\_MEDIA\_PROMPT\_LENGTH
1136
+
1137
+ ```ts
1138
+ const MAX_MEDIA_PROMPT_LENGTH: 32000 = 32_000;
1139
+ ```
1140
+
1141
+ Structural ceiling for any prompt on the wire. Each model's own
1142
+ `maxPromptLength` is the real bound; no model may exceed this ceiling.
1143
+
1144
+ ---
1145
+
1136
1146
  ### mediaModelSchema
1137
1147
 
1138
1148
  ```ts
@@ -1154,6 +1164,7 @@ const mediaModelSchema: ZodReadonly<ZodObject<{
1154
1164
  jpeg: "jpeg";
1155
1165
  webp: "webp";
1156
1166
  }>>>;
1167
+ maxPromptLength: ZodNumber;
1157
1168
  input: ZodLazy<ZodReadonly<ZodObject<{
1158
1169
  transport: ZodEnum<{
1159
1170
  json: "json";
@@ -1205,7 +1216,6 @@ const mediaModelSchema: ZodReadonly<ZodObject<{
1205
1216
  ultratall: "9:21";
1206
1217
  }>;
1207
1218
  defaultGenerateAudio: ZodNullable<ZodBoolean>;
1208
- maxPromptLength: ZodNumber;
1209
1219
  referenceRoles: ZodReadonly<ZodArray<ZodEnum<...>>>;
1210
1220
  maxReferenceImages: ZodNumber;
1211
1221
  referenceResolutions: ZodReadonly<ZodArray<ZodEnum<...>>>;
@@ -1290,19 +1300,19 @@ Validate caller settings and apply retained model defaults.
1290
1300
 
1291
1301
  #### Parameters
1292
1302
 
1293
- | Parameter | Type | Description |
1294
- | ---------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------- |
1295
- | `raw` | \{ `model`: `string`; `prompt`: `string`; `width`: `number`; `height`: `number`; `steps?`: `number`; `durationSeconds?`: `number`; `resolution?`: `"480p"` \| `"720p"` \| `"768p"` \| `"1080p"` \| `"4k"`; `aspectRatio?`: `"16:9"` \| `"4:3"` \| `"1:1"` \| `"3:4"` \| `"9:16"` \| `"21:9"` \| `"9:21"`; `generateAudio?`: `boolean`; `references?`: `object`[]; \} | - |
1296
- | `raw.model` | `string` | Provider catalog model identifier. |
1297
- | `raw.prompt` | `string` | Generation prompt. |
1298
- | `raw.width` | `number` | Requested image width or nominal video selector, not decoded MP4 dimensions. |
1299
- | `raw.height` | `number` | Requested image height or nominal video selector, not decoded MP4 dimensions. |
1300
- | `raw.steps?` | `number` | Model-specific inference step count. |
1301
- | `raw.durationSeconds?` | `number` | Requested video length in whole seconds. |
1302
- | `raw.resolution?` | `"480p"` \| `"720p"` \| `"768p"` \| `"1080p"` \| `"4k"` | Video resolution; must match the nominal width/height selector. |
1303
- | `raw.aspectRatio?` | `"16:9"` \| `"4:3"` \| `"1:1"` \| `"3:4"` \| `"9:16"` \| `"21:9"` \| `"9:21"` | Video aspect; must match the nominal width/height selector. |
1304
- | `raw.generateAudio?` | `boolean` | Video audio selection. |
1305
- | `raw.references?` | `object`[] | Host-authorized reference images. |
1303
+ | Parameter | Type | Description |
1304
+ | ---------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------- |
1305
+ | `raw` | \{ `model`: `string`; `prompt`: `string`; `width`: `number`; `height`: `number`; `steps?`: `number`; `durationSeconds?`: `number`; `resolution?`: `"480p"` \| `"720p"` \| `"768p"` \| `"1080p"` \| `"4k"`; `aspectRatio?`: `"16:9"` \| `"4:3"` \| `"1:1"` \| `"3:4"` \| `"9:16"` \| `"21:9"` \| `"9:21"`; `generateAudio?`: `boolean`; `references?`: `object`[]; \} | - |
1306
+ | `raw.model` | `string` | Provider catalog model identifier. |
1307
+ | `raw.prompt` | `string` | Generation prompt; each model's `maxPromptLength` is enforced on top of this ceiling. |
1308
+ | `raw.width` | `number` | Requested image width or nominal video selector, not decoded MP4 dimensions. |
1309
+ | `raw.height` | `number` | Requested image height or nominal video selector, not decoded MP4 dimensions. |
1310
+ | `raw.steps?` | `number` | Model-specific inference step count. |
1311
+ | `raw.durationSeconds?` | `number` | Requested video length in whole seconds. |
1312
+ | `raw.resolution?` | `"480p"` \| `"720p"` \| `"768p"` \| `"1080p"` \| `"4k"` | Video resolution; must match the nominal width/height selector. |
1313
+ | `raw.aspectRatio?` | `"16:9"` \| `"4:3"` \| `"1:1"` \| `"3:4"` \| `"9:16"` \| `"21:9"` \| `"9:21"` | Video aspect; must match the nominal width/height selector. |
1314
+ | `raw.generateAudio?` | `boolean` | Video audio selection. |
1315
+ | `raw.references?` | `object`[] | Host-authorized reference images. |
1306
1316
 
1307
1317
  #### Returns
1308
1318
 
@@ -1322,19 +1332,19 @@ Validate path-only selections without inventing a Computer identity.
1322
1332
 
1323
1333
  #### Parameters
1324
1334
 
1325
- | Parameter | Type | Description |
1326
- | ---------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------- |
1327
- | `raw` | \{ `model`: `string`; `prompt`: `string`; `width`: `number`; `height`: `number`; `steps?`: `number`; `durationSeconds?`: `number`; `resolution?`: `"480p"` \| `"720p"` \| `"768p"` \| `"1080p"` \| `"4k"`; `aspectRatio?`: `"16:9"` \| `"4:3"` \| `"1:1"` \| `"3:4"` \| `"9:16"` \| `"21:9"` \| `"9:21"`; `generateAudio?`: `boolean`; `references?`: `object`[]; \} | - |
1328
- | `raw.model` | `string` | Provider catalog model identifier. |
1329
- | `raw.prompt` | `string` | Generation prompt. |
1330
- | `raw.width` | `number` | Requested image width or nominal video selector, not decoded MP4 dimensions. |
1331
- | `raw.height` | `number` | Requested image height or nominal video selector, not decoded MP4 dimensions. |
1332
- | `raw.steps?` | `number` | Model-specific inference step count. |
1333
- | `raw.durationSeconds?` | `number` | Requested video length in whole seconds. |
1334
- | `raw.resolution?` | `"480p"` \| `"720p"` \| `"768p"` \| `"1080p"` \| `"4k"` | Video resolution; must match the nominal width/height selector. |
1335
- | `raw.aspectRatio?` | `"16:9"` \| `"4:3"` \| `"1:1"` \| `"3:4"` \| `"9:16"` \| `"21:9"` \| `"9:21"` | Video aspect; must match the nominal width/height selector. |
1336
- | `raw.generateAudio?` | `boolean` | Video audio selection. |
1337
- | `raw.references?` | `object`[] | Host-authorized reference images. |
1335
+ | Parameter | Type | Description |
1336
+ | ---------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------- |
1337
+ | `raw` | \{ `model`: `string`; `prompt`: `string`; `width`: `number`; `height`: `number`; `steps?`: `number`; `durationSeconds?`: `number`; `resolution?`: `"480p"` \| `"720p"` \| `"768p"` \| `"1080p"` \| `"4k"`; `aspectRatio?`: `"16:9"` \| `"4:3"` \| `"1:1"` \| `"3:4"` \| `"9:16"` \| `"21:9"` \| `"9:21"`; `generateAudio?`: `boolean`; `references?`: `object`[]; \} | - |
1338
+ | `raw.model` | `string` | Provider catalog model identifier. |
1339
+ | `raw.prompt` | `string` | Generation prompt; each model's `maxPromptLength` is enforced on top of this ceiling. |
1340
+ | `raw.width` | `number` | Requested image width or nominal video selector, not decoded MP4 dimensions. |
1341
+ | `raw.height` | `number` | Requested image height or nominal video selector, not decoded MP4 dimensions. |
1342
+ | `raw.steps?` | `number` | Model-specific inference step count. |
1343
+ | `raw.durationSeconds?` | `number` | Requested video length in whole seconds. |
1344
+ | `raw.resolution?` | `"480p"` \| `"720p"` \| `"768p"` \| `"1080p"` \| `"4k"` | Video resolution; must match the nominal width/height selector. |
1345
+ | `raw.aspectRatio?` | `"16:9"` \| `"4:3"` \| `"1:1"` \| `"3:4"` \| `"9:16"` \| `"21:9"` \| `"9:21"` | Video aspect; must match the nominal width/height selector. |
1346
+ | `raw.generateAudio?` | `boolean` | Video audio selection. |
1347
+ | `raw.references?` | `object`[] | Host-authorized reference images. |
1338
1348
 
1339
1349
  #### Returns
1340
1350
 
@@ -1358,7 +1368,7 @@ or billing admission.
1358
1368
  | ---------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
1359
1369
  | `raw` | \{ `model`: \| `"bytedance/seedance-2.0"` \| `"bytedance/seedance-2.0-fast"` \| `"bytedance/seedance-2.0-mini"` \| `"google/veo-3.1"` \| `"google/veo-3.1-fast"` \| `"minimax/h3-max"`; `prompt`: `string`; `resolution?`: `"480p"` \| `"720p"` \| `"768p"` \| `"1080p"` \| `"4k"`; `aspectRatio?`: `"16:9"` \| `"4:3"` \| `"1:1"` \| `"3:4"` \| `"9:16"` \| `"21:9"` \| `"9:21"`; `durationSeconds?`: `number`; `generateAudio?`: `boolean`; `references?`: `object`[]; \} | - |
1360
1370
  | `raw.model` | \| `"bytedance/seedance-2.0"` \| `"bytedance/seedance-2.0-fast"` \| `"bytedance/seedance-2.0-mini"` \| `"google/veo-3.1"` \| `"google/veo-3.1-fast"` \| `"minimax/h3-max"` | Verified catalog model identifier. |
1361
- | `raw.prompt` | `string` | Trimmed prompt; Seedance allows 2,000 characters, Veo uses the retained host cap of 2,048. |
1371
+ | `raw.prompt` | `string` | Trimmed prompt; the selected model's `maxPromptLength` is enforced on top of this ceiling. |
1362
1372
  | `raw.resolution?` | `"480p"` \| `"720p"` \| `"768p"` \| `"1080p"` \| `"4k"` | Model-specific resolution, default 720p. |
1363
1373
  | `raw.aspectRatio?` | `"16:9"` \| `"4:3"` \| `"1:1"` \| `"3:4"` \| `"9:16"` \| `"21:9"` \| `"9:21"` | Model-specific aspect, default 16:9. |
1364
1374
  | `raw.durationSeconds?` | `number` | Whole seconds; Seedance 4–12, Veo exactly 4/6/8, MiniMax H3 Max 5–15. |
@@ -1531,6 +1541,7 @@ function listMediaModels(): readonly Readonly<{
1531
1541
  kind: "image" | "video";
1532
1542
  tier: "draft" | "balanced" | "premium" | "max";
1533
1543
  outputFormats: readonly ("png" | "jpeg" | "webp")[];
1544
+ maxPromptLength: number;
1534
1545
  input: Readonly<{
1535
1546
  transport: "json" | "multipart";
1536
1547
  dimensions: "none" | "own";
@@ -1560,7 +1571,6 @@ function listMediaModels(): readonly Readonly<{
1560
1571
  defaultAspectRatio:
1561
1572
  "16:9" | "4:3" | "1:1" | "3:4" | "9:16" | "21:9" | "9:21";
1562
1573
  defaultGenerateAudio: boolean | null;
1563
- maxPromptLength: number;
1564
1574
  referenceRoles: readonly ("first-frame" | "last-frame" | "reference")[];
1565
1575
  maxReferenceImages: number;
1566
1576
  referenceResolutions: readonly (
@@ -1581,26 +1591,26 @@ List all image and video models in catalog order.
1581
1591
 
1582
1592
  readonly `Readonly`\<\{ `id`: `string`; `label`: `string`; `kind`: `"image"` \|
1583
1593
  `"video"`; `tier`: `"draft"` \| `"balanced"` \| `"premium"` \| `"max"`;
1584
- `outputFormats`: readonly (`"png"` \| `"jpeg"` \| `"webp"`)[]; `input`:
1585
- `Readonly`\<\{ `transport`: `"json"` \| `"multipart"`; `dimensions`: `"none"` \|
1586
- `"own"`; `maxDimension`: `number` \| `null`; `defaultWidth`: `number`;
1587
- `defaultHeight`: `number`; `steps`: `"steps"` \| `"num_steps"` \| `"fixed"`;
1588
- `maxSteps`: `number` \| `null`; `defaultSteps`: `number` \| `null`;
1589
- `referenceField`: `string` \| `null`; `maxReferences`: `number`; \}\>; `sizes`:
1590
- readonly `string`[] \| `null`; `video`: \| `Readonly`\<\{ `minSeconds`:
1591
- `number`; `maxSeconds`: `number`; `defaultSeconds`: `number`; `durations`:
1592
- readonly `number`[]; `provider`: `"seedance"` \| `"veo"` \| `"minimax"`;
1593
- `formats`: readonly `Readonly`\<\{ `resolution`: `"480p"` \| `"720p"` \|
1594
- `"768p"` \| `"1080p"` \| `"4k"`; `aspectRatio`: `"16:9"` \| `"4:3"` \| `"1:1"`
1595
- \| `"3:4"` \| `"9:16"` \| `"21:9"` \| `"9:21"`; `width`: `number`; `height`:
1596
- `number`; \}\>[]; `defaultResolution`: `"480p"` \| `"720p"` \| `"768p"` \|
1597
- `"1080p"` \| `"4k"`; `defaultAspectRatio`: `"16:9"` \| `"4:3"` \| `"1:1"` \|
1598
- `"3:4"` \| `"9:16"` \| `"21:9"` \| `"9:21"`; `defaultGenerateAudio`: `boolean`
1599
- \| `null`; `maxPromptLength`: `number`; `referenceRoles`: readonly
1600
- (`"first-frame"` \| `"last-frame"` \| `"reference"`)[]; `maxReferenceImages`:
1601
- `number`; `referenceResolutions`: readonly (`"480p"` \| `"720p"` \| `"768p"` \|
1602
- `"1080p"` \| `"4k"`)[]; `prices`: readonly `Readonly`\<\{ `resolution`: `"480p"`
1603
- \| `"720p"` \| `"768p"` \| `"1080p"` \| `"4k"`; `silentMilliunitsPerSecond`:
1594
+ `outputFormats`: readonly (`"png"` \| `"jpeg"` \| `"webp"`)[];
1595
+ `maxPromptLength`: `number`; `input`: `Readonly`\<\{ `transport`: `"json"` \|
1596
+ `"multipart"`; `dimensions`: `"none"` \| `"own"`; `maxDimension`: `number` \|
1597
+ `null`; `defaultWidth`: `number`; `defaultHeight`: `number`; `steps`: `"steps"`
1598
+ \| `"num_steps"` \| `"fixed"`; `maxSteps`: `number` \| `null`; `defaultSteps`:
1599
+ `number` \| `null`; `referenceField`: `string` \| `null`; `maxReferences`:
1600
+ `number`; \}\>; `sizes`: readonly `string`[] \| `null`; `video`: \|
1601
+ `Readonly`\<\{ `minSeconds`: `number`; `maxSeconds`: `number`; `defaultSeconds`:
1602
+ `number`; `durations`: readonly `number`[]; `provider`: `"seedance"` \| `"veo"`
1603
+ \| `"minimax"`; `formats`: readonly `Readonly`\<\{ `resolution`: `"480p"` \|
1604
+ `"720p"` \| `"768p"` \| `"1080p"` \| `"4k"`; `aspectRatio`: `"16:9"` \| `"4:3"`
1605
+ \| `"1:1"` \| `"3:4"` \| `"9:16"` \| `"21:9"` \| `"9:21"`; `width`: `number`;
1606
+ `height`: `number`; \}\>[]; `defaultResolution`: `"480p"` \| `"720p"` \|
1607
+ `"768p"` \| `"1080p"` \| `"4k"`; `defaultAspectRatio`: `"16:9"` \| `"4:3"` \|
1608
+ `"1:1"` \| `"3:4"` \| `"9:16"` \| `"21:9"` \| `"9:21"`; `defaultGenerateAudio`:
1609
+ `boolean` \| `null`; `referenceRoles`: readonly (`"first-frame"` \|
1610
+ `"last-frame"` \| `"reference"`)[]; `maxReferenceImages`: `number`;
1611
+ `referenceResolutions`: readonly (`"480p"` \| `"720p"` \| `"768p"` \| `"1080p"`
1612
+ \| `"4k"`)[]; `prices`: readonly `Readonly`\<\{ `resolution`: `"480p"` \|
1613
+ `"720p"` \| `"768p"` \| `"1080p"` \| `"4k"`; `silentMilliunitsPerSecond`:
1604
1614
  `number`; `audioMilliunitsPerSecond`: `number`; \}\>[]; \}\> \| `null`; \}\>[]
1605
1615
 
1606
1616
  ---
@@ -1615,6 +1625,7 @@ function getMediaModel(id):
1615
1625
  kind: "image" | "video";
1616
1626
  tier: "draft" | "balanced" | "premium" | "max";
1617
1627
  outputFormats: readonly ("png" | "jpeg" | "webp")[];
1628
+ maxPromptLength: number;
1618
1629
  input: Readonly<{
1619
1630
  transport: "json" | "multipart";
1620
1631
  dimensions: "none" | "own";
@@ -1645,7 +1656,6 @@ function getMediaModel(id):
1645
1656
  defaultAspectRatio:
1646
1657
  "16:9" | "4:3" | "1:1" | "3:4" | "9:16" | "21:9" | "9:21";
1647
1658
  defaultGenerateAudio: boolean | null;
1648
- maxPromptLength: number;
1649
1659
  referenceRoles: readonly ("first-frame" | "last-frame" | "reference")[];
1650
1660
  maxReferenceImages: number;
1651
1661
  referenceResolutions: readonly (
@@ -1673,26 +1683,26 @@ Find a model by provider identifier, returning undefined when it is absent.
1673
1683
 
1674
1684
  \| `Readonly`\<\{ `id`: `string`; `label`: `string`; `kind`: `"image"` \|
1675
1685
  `"video"`; `tier`: `"draft"` \| `"balanced"` \| `"premium"` \| `"max"`;
1676
- `outputFormats`: readonly (`"png"` \| `"jpeg"` \| `"webp"`)[]; `input`:
1677
- `Readonly`\<\{ `transport`: `"json"` \| `"multipart"`; `dimensions`: `"none"` \|
1678
- `"own"`; `maxDimension`: `number` \| `null`; `defaultWidth`: `number`;
1679
- `defaultHeight`: `number`; `steps`: `"steps"` \| `"num_steps"` \| `"fixed"`;
1680
- `maxSteps`: `number` \| `null`; `defaultSteps`: `number` \| `null`;
1681
- `referenceField`: `string` \| `null`; `maxReferences`: `number`; \}\>; `sizes`:
1682
- readonly `string`[] \| `null`; `video`: \| `Readonly`\<\{ `minSeconds`:
1683
- `number`; `maxSeconds`: `number`; `defaultSeconds`: `number`; `durations`:
1684
- readonly `number`[]; `provider`: `"seedance"` \| `"veo"` \| `"minimax"`;
1685
- `formats`: readonly `Readonly`\<\{ `resolution`: `"480p"` \| `"720p"` \|
1686
- `"768p"` \| `"1080p"` \| `"4k"`; `aspectRatio`: `"16:9"` \| `"4:3"` \| `"1:1"`
1687
- \| `"3:4"` \| `"9:16"` \| `"21:9"` \| `"9:21"`; `width`: `number`; `height`:
1688
- `number`; \}\>[]; `defaultResolution`: `"480p"` \| `"720p"` \| `"768p"` \|
1689
- `"1080p"` \| `"4k"`; `defaultAspectRatio`: `"16:9"` \| `"4:3"` \| `"1:1"` \|
1690
- `"3:4"` \| `"9:16"` \| `"21:9"` \| `"9:21"`; `defaultGenerateAudio`: `boolean`
1691
- \| `null`; `maxPromptLength`: `number`; `referenceRoles`: readonly
1692
- (`"first-frame"` \| `"last-frame"` \| `"reference"`)[]; `maxReferenceImages`:
1693
- `number`; `referenceResolutions`: readonly (`"480p"` \| `"720p"` \| `"768p"` \|
1694
- `"1080p"` \| `"4k"`)[]; `prices`: readonly `Readonly`\<\{ `resolution`: `"480p"`
1695
- \| `"720p"` \| `"768p"` \| `"1080p"` \| `"4k"`; `silentMilliunitsPerSecond`:
1686
+ `outputFormats`: readonly (`"png"` \| `"jpeg"` \| `"webp"`)[];
1687
+ `maxPromptLength`: `number`; `input`: `Readonly`\<\{ `transport`: `"json"` \|
1688
+ `"multipart"`; `dimensions`: `"none"` \| `"own"`; `maxDimension`: `number` \|
1689
+ `null`; `defaultWidth`: `number`; `defaultHeight`: `number`; `steps`: `"steps"`
1690
+ \| `"num_steps"` \| `"fixed"`; `maxSteps`: `number` \| `null`; `defaultSteps`:
1691
+ `number` \| `null`; `referenceField`: `string` \| `null`; `maxReferences`:
1692
+ `number`; \}\>; `sizes`: readonly `string`[] \| `null`; `video`: \|
1693
+ `Readonly`\<\{ `minSeconds`: `number`; `maxSeconds`: `number`; `defaultSeconds`:
1694
+ `number`; `durations`: readonly `number`[]; `provider`: `"seedance"` \| `"veo"`
1695
+ \| `"minimax"`; `formats`: readonly `Readonly`\<\{ `resolution`: `"480p"` \|
1696
+ `"720p"` \| `"768p"` \| `"1080p"` \| `"4k"`; `aspectRatio`: `"16:9"` \| `"4:3"`
1697
+ \| `"1:1"` \| `"3:4"` \| `"9:16"` \| `"21:9"` \| `"9:21"`; `width`: `number`;
1698
+ `height`: `number`; \}\>[]; `defaultResolution`: `"480p"` \| `"720p"` \|
1699
+ `"768p"` \| `"1080p"` \| `"4k"`; `defaultAspectRatio`: `"16:9"` \| `"4:3"` \|
1700
+ `"1:1"` \| `"3:4"` \| `"9:16"` \| `"21:9"` \| `"9:21"`; `defaultGenerateAudio`:
1701
+ `boolean` \| `null`; `referenceRoles`: readonly (`"first-frame"` \|
1702
+ `"last-frame"` \| `"reference"`)[]; `maxReferenceImages`: `number`;
1703
+ `referenceResolutions`: readonly (`"480p"` \| `"720p"` \| `"768p"` \| `"1080p"`
1704
+ \| `"4k"`)[]; `prices`: readonly `Readonly`\<\{ `resolution`: `"480p"` \|
1705
+ `"720p"` \| `"768p"` \| `"1080p"` \| `"4k"`; `silentMilliunitsPerSecond`:
1696
1706
  `number`; `audioMilliunitsPerSecond`: `number`; \}\>[]; \}\> \| `null`; \}\> \|
1697
1707
  `undefined`
1698
1708
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fungi.computer/darkroom",
3
- "version": "0.2.1",
3
+ "version": "0.3.0",
4
4
  "author": "Levi <levi@technomancy.dev>",
5
5
  "description": "Media generation, durable output retention and provenance with host-owned authority and billing",
6
6
  "type": "module",
@@ -59,7 +59,7 @@
59
59
  "exifreader": "4.46.0",
60
60
  "@fungi.computer/ts-webp": "0.1.6",
61
61
  "@xmldom/xmldom": "0.9.12",
62
- "@fungi.computer/agar": "0.3.1",
62
+ "@fungi.computer/agar": "0.4.0",
63
63
  "@fungi.computer/capnweb": "0.12.0"
64
64
  },
65
65
  "devDependencies": {