@ai-sdk/black-forest-labs 1.0.49 → 1.0.51

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.
@@ -2,12 +2,10 @@ import type { ImageModelV3, SharedV3Warning } from '@ai-sdk/provider';
2
2
  import {
3
3
  combineHeaders,
4
4
  createBinaryResponseHandler,
5
- createJsonErrorResponseHandler,
6
5
  createJsonResponseHandler,
7
6
  createStatusCodeErrorResponseHandler,
8
7
  delay,
9
8
  getFromApi,
10
- isSameOrigin,
11
9
  lazySchema,
12
10
  parseProviderOptions,
13
11
  postJsonToApi,
@@ -18,6 +16,10 @@ import {
18
16
  type FetchFunction,
19
17
  } from '@ai-sdk/provider-utils';
20
18
  import { z } from 'zod/v4';
19
+ import {
20
+ bflFailedResponseHandler,
21
+ isTrustedUrl,
22
+ } from './black-forest-labs-api';
21
23
  import {
22
24
  type BlackForestLabsAspectRatio,
23
25
  type BlackForestLabsImageModelId,
@@ -342,29 +344,6 @@ export class BlackForestLabsImageModel implements ImageModelV3 {
342
344
  }
343
345
  }
344
346
 
345
- /**
346
- * Black Forest Labs returns response-supplied URLs (polling and delivery) on
347
- * sibling cluster hosts of the API origin (e.g. `api.us1.bfl.ai` for a base
348
- * URL on `api.bfl.ai`), so a strict same-origin check against the configured
349
- * base URL is not enough. Credentials may also be sent to any https host under
350
- * the official `bfl.ai` domain.
351
- */
352
- function isTrustedUrl(url: string, baseUrl: string): boolean {
353
- if (isSameOrigin(url, baseUrl)) {
354
- return true;
355
- }
356
-
357
- try {
358
- const { protocol, hostname } = new URL(url);
359
- return (
360
- protocol === 'https:' &&
361
- (hostname === 'bfl.ai' || hostname.endsWith('.bfl.ai'))
362
- );
363
- } catch {
364
- return false;
365
- }
366
- }
367
-
368
347
  export const blackForestLabsImageModelOptionsSchema = lazySchema(() =>
369
348
  zodSchema(
370
349
  z.object({
@@ -477,29 +456,3 @@ const bflPollSchema = z
477
456
  status: (v.status ?? v.state)!,
478
457
  result: v.result,
479
458
  }));
480
-
481
- const bflErrorSchema = z.object({
482
- message: z.string().optional(),
483
- detail: z.any().optional(),
484
- });
485
-
486
- const bflFailedResponseHandler = createJsonErrorResponseHandler({
487
- errorSchema: bflErrorSchema,
488
- errorToMessage: error =>
489
- bflErrorToMessage(error) ?? 'Unknown Black Forest Labs error',
490
- });
491
-
492
- function bflErrorToMessage(error: unknown): string | undefined {
493
- const parsed = bflErrorSchema.safeParse(error);
494
- if (!parsed.success) return undefined;
495
- const { message, detail } = parsed.data;
496
- if (typeof detail === 'string') return detail;
497
- if (detail != null) {
498
- try {
499
- return JSON.stringify(detail);
500
- } catch {
501
- // ignore
502
- }
503
- }
504
- return message;
505
- }
@@ -1,5 +1,6 @@
1
1
  import {
2
2
  NoSuchModelError,
3
+ type Experimental_VideoModelV3,
3
4
  type ImageModelV3,
4
5
  type ProviderV3,
5
6
  } from '@ai-sdk/provider';
@@ -11,6 +12,8 @@ import {
11
12
  } from '@ai-sdk/provider-utils';
12
13
  import { BlackForestLabsImageModel } from './black-forest-labs-image-model';
13
14
  import type { BlackForestLabsImageModelId } from './black-forest-labs-image-settings';
15
+ import { BlackForestLabsVideoModel } from './black-forest-labs-video-model';
16
+ import type { BlackForestLabsVideoModelId } from './black-forest-labs-video-settings';
14
17
  import { VERSION } from './version';
15
18
 
16
19
  export interface BlackForestLabsProviderSettings {
@@ -36,12 +39,14 @@ export interface BlackForestLabsProviderSettings {
36
39
  fetch?: FetchFunction;
37
40
 
38
41
  /**
39
- * Poll interval in milliseconds between status checks. Defaults to 500ms.
42
+ * Poll interval in milliseconds between status checks. Defaults to 500ms for
43
+ * images and 2s for video.
40
44
  */
41
45
  pollIntervalMillis?: number;
42
46
 
43
47
  /**
44
- * Overall timeout in milliseconds for polling before giving up. Defaults to 60s.
48
+ * Overall timeout in milliseconds for polling before giving up. Defaults to
49
+ * 60s for images and 10 minutes for video.
45
50
  */
46
51
  pollTimeoutMillis?: number;
47
52
  }
@@ -57,6 +62,16 @@ export interface BlackForestLabsProvider extends ProviderV3 {
57
62
  */
58
63
  imageModel(modelId: BlackForestLabsImageModelId): ImageModelV3;
59
64
 
65
+ /**
66
+ * Creates a model for video generation.
67
+ */
68
+ video(modelId: BlackForestLabsVideoModelId): Experimental_VideoModelV3;
69
+
70
+ /**
71
+ * Creates a model for video generation.
72
+ */
73
+ videoModel(modelId: BlackForestLabsVideoModelId): Experimental_VideoModelV3;
74
+
60
75
  /**
61
76
  * @deprecated Use `embeddingModel` instead.
62
77
  */
@@ -92,6 +107,16 @@ export function createBlackForestLabs(
92
107
  pollTimeoutMillis: options.pollTimeoutMillis,
93
108
  });
94
109
 
110
+ const createVideoModel = (modelId: BlackForestLabsVideoModelId) =>
111
+ new BlackForestLabsVideoModel(modelId, {
112
+ provider: 'black-forest-labs.video',
113
+ baseURL: baseURL ?? defaultBaseURL,
114
+ headers: getHeaders,
115
+ fetch: options.fetch,
116
+ pollIntervalMillis: options.pollIntervalMillis,
117
+ pollTimeoutMillis: options.pollTimeoutMillis,
118
+ });
119
+
95
120
  const embeddingModel = (modelId: string) => {
96
121
  throw new NoSuchModelError({
97
122
  modelId,
@@ -103,6 +128,8 @@ export function createBlackForestLabs(
103
128
  specificationVersion: 'v3',
104
129
  imageModel: createImageModel,
105
130
  image: createImageModel,
131
+ videoModel: createVideoModel,
132
+ video: createVideoModel,
106
133
  languageModel: (modelId: string) => {
107
134
  throw new NoSuchModelError({
108
135
  modelId,
@@ -0,0 +1,92 @@
1
+ import {
2
+ lazySchema,
3
+ zodSchema,
4
+ type InferSchema,
5
+ } from '@ai-sdk/provider-utils';
6
+ import { z } from 'zod/v4';
7
+ import {
8
+ blackForestLabsVideoAspectRatios,
9
+ blackForestLabsVideoResolutions,
10
+ } from './black-forest-labs-video-settings';
11
+
12
+ const blackForestLabsTimedVideoKeyframeSchema = z.tuple([
13
+ z.number().min(0).max(20),
14
+ z.string(),
15
+ ]);
16
+
17
+ export const blackForestLabsVideoKeyframesSchema = z.union([
18
+ z.array(z.string()).min(1).max(10),
19
+ z
20
+ .array(blackForestLabsTimedVideoKeyframeSchema)
21
+ .min(1)
22
+ .max(10)
23
+ .refine(
24
+ keyframes =>
25
+ keyframes.every(
26
+ (keyframe, index) =>
27
+ index === 0 || keyframe[0] > keyframes[index - 1][0],
28
+ ),
29
+ { message: 'Timed keyframes must be in chronological order.' },
30
+ ),
31
+ ]);
32
+
33
+ export const blackForestLabsVideoProviderOptions = z.object({
34
+ /**
35
+ * Output resolution tier. Takes precedence over the top-level `resolution`,
36
+ * which is expressed as `{width}x{height}` and has to be mapped onto a tier.
37
+ */
38
+ resolution: z.enum(blackForestLabsVideoResolutions).optional(),
39
+
40
+ /**
41
+ * Aspect ratio of the generated video. Takes precedence over the top-level
42
+ * `aspectRatio`, and unlike it can be set to `auto`.
43
+ */
44
+ aspectRatio: z.enum(blackForestLabsVideoAspectRatios).optional(),
45
+
46
+ /**
47
+ * Keyframes for image-to-video generation, for the shapes the top-level
48
+ * `image` and `frameImages` options cannot express: more than two images, or
49
+ * images pinned to a specific second. Takes precedence over both.
50
+ *
51
+ * One image opens the clip, two open and close it, and with more the extras
52
+ * are spaced evenly in between. Three or more plain images require an
53
+ * explicit `duration`.
54
+ */
55
+ keyframes: blackForestLabsVideoKeyframesSchema.optional(),
56
+
57
+ /**
58
+ * Moderation strictness from 0 (strictest) to 4. Defaults to 2. Sexual
59
+ * content is capped at 3 and hate content at 2 regardless of the request,
60
+ * and any request carrying conditioning media is capped at 2.
61
+ */
62
+ safetyTolerance: z.number().int().min(0).max(4).optional(),
63
+
64
+ /**
65
+ * Render a fast, lower-quality preview instead of the finished video.
66
+ * Defaults to `false`.
67
+ */
68
+ draft: z.boolean().optional(),
69
+
70
+ /**
71
+ * Encrypted draft-cache bundle from a prior `draft` generation, which
72
+ * switches the request to draft-enhance mode: the bundle is replayed at full
73
+ * quality.
74
+ *
75
+ * Either the base64-encoded `.bin` downloaded from the draft's `draftCache`
76
+ * URL, or that URL itself while it is still in its expiry window.
77
+ */
78
+ draftCache: z.string().optional(),
79
+
80
+ /**
81
+ * Model version to pin. Only `latest` is available today.
82
+ */
83
+ version: z.literal('latest').optional(),
84
+ });
85
+
86
+ export const blackForestLabsVideoModelOptionsSchema = lazySchema(() =>
87
+ zodSchema(blackForestLabsVideoProviderOptions),
88
+ );
89
+
90
+ export type BlackForestLabsVideoModelOptions = InferSchema<
91
+ typeof blackForestLabsVideoModelOptionsSchema
92
+ >;