@broberg/ai-sdk 0.16.0 → 0.17.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/index.d.ts CHANGED
@@ -14,7 +14,7 @@ interface TierSpec {
14
14
  transport: Transport;
15
15
  }
16
16
  /** High-level capability a call exercises. Mirrors the capability layer (F5). */
17
- type Capability = "chat" | "vision" | "video" | "translate" | "image" | "embedding" | "transcribe" | "ocr" | "moderation" | "podcast" | "tts" | "trainStyle" | "mockup" | "design" | "extract" | "classify" | "rerank";
17
+ type Capability = "chat" | "vision" | "video" | "translate" | "image" | "animate" | "embedding" | "transcribe" | "ocr" | "moderation" | "podcast" | "tts" | "trainStyle" | "mockup" | "design" | "extract" | "classify" | "rerank";
18
18
  type Role = "system" | "user" | "assistant" | "tool";
19
19
  /** A piece of message content. Text everywhere; image parts feed vision. */
20
20
  type ContentPart = {
@@ -188,6 +188,30 @@ interface ImageResult {
188
188
  url: string;
189
189
  usage: Usage;
190
190
  }
191
+ /** Image-to-video generation (F024) — animate a still into a short clip. */
192
+ interface AnimateRequest {
193
+ /** Input image: a URL (passed through) or raw bytes (uploaded to fal storage). */
194
+ image: string | Uint8Array;
195
+ /** Motion/scene prompt, e.g. "the subject turns and smiles". */
196
+ prompt?: string;
197
+ /** Clip length in seconds (provider-dependent; Veo ≤ 8s). */
198
+ durationSec?: number;
199
+ /** e.g. "720p" / "1080p" (provider-dependent). */
200
+ resolution?: string;
201
+ spec: TierSpec;
202
+ }
203
+ interface AnimateResult {
204
+ /** URL to the generated video. For fal this is a public hosted URL; for the
205
+ * Gemini/Veo route it is Google's file URI (needs the API key to fetch — the
206
+ * bytes are also returned below, already downloaded). */
207
+ url: string;
208
+ /** The downloaded video bytes — set by providers whose result URL is auth-gated
209
+ * or short-lived (Gemini/Veo). Absent for providers that return a public URL (fal). */
210
+ bytes?: Uint8Array;
211
+ /** MIME type of `bytes` (e.g. "video/mp4"). */
212
+ mimeType?: string;
213
+ usage: Usage;
214
+ }
191
215
  /** Style/brand LoRA training (F021) — fal fal-ai/flux-lora-fast-training. */
192
216
  interface TrainStyleRequest {
193
217
  /** A hosted archive URL, or an array of image URLs the SDK zips in-memory. */
@@ -304,6 +328,8 @@ interface ProviderAdapter {
304
328
  chatStream?(req: ChatRequest): AsyncIterable<ChatStreamEvent>;
305
329
  vision?(req: ChatRequest): Promise<ChatResult>;
306
330
  image?(req: ImageRequest): Promise<ImageResult>;
331
+ /** Image-to-video generation (F024) — animate a still into a short clip. fal. */
332
+ animate?(req: AnimateRequest): Promise<AnimateResult>;
307
333
  /** Train a style/brand LoRA from images (F021). fal. */
308
334
  trainStyle?(req: TrainStyleRequest): Promise<TrainStyleResult>;
309
335
  embedding?(req: EmbeddingRequest): Promise<EmbeddingResult>;
@@ -417,12 +443,12 @@ declare const toolSchema: z.ZodObject<{
417
443
  parameters: z.ZodRecord<z.ZodString, z.ZodUnknown>;
418
444
  }, "strip", z.ZodTypeAny, {
419
445
  name: string;
420
- description: string;
421
446
  parameters: Record<string, unknown>;
447
+ description: string;
422
448
  }, {
423
449
  name: string;
424
- description: string;
425
450
  parameters: Record<string, unknown>;
451
+ description: string;
426
452
  }>;
427
453
  declare const messageSchema: z.ZodObject<{
428
454
  role: z.ZodEnum<["system", "user", "assistant", "tool"]>;
@@ -604,12 +630,12 @@ declare const chatInputSchema: z.ZodObject<{
604
630
  parameters: z.ZodRecord<z.ZodString, z.ZodUnknown>;
605
631
  }, "strip", z.ZodTypeAny, {
606
632
  name: string;
607
- description: string;
608
633
  parameters: Record<string, unknown>;
634
+ description: string;
609
635
  }, {
610
636
  name: string;
611
- description: string;
612
637
  parameters: Record<string, unknown>;
638
+ description: string;
613
639
  }>, "many">>;
614
640
  maxTokens: z.ZodOptional<z.ZodNumber>;
615
641
  temperature: z.ZodOptional<z.ZodNumber>;
@@ -637,8 +663,8 @@ declare const chatInputSchema: z.ZodObject<{
637
663
  }[] | undefined;
638
664
  tools?: {
639
665
  name: string;
640
- description: string;
641
666
  parameters: Record<string, unknown>;
667
+ description: string;
642
668
  }[] | undefined;
643
669
  temperature?: number | undefined;
644
670
  purpose?: string | undefined;
@@ -678,8 +704,8 @@ declare const chatInputSchema: z.ZodObject<{
678
704
  }[] | undefined;
679
705
  tools?: {
680
706
  name: string;
681
- description: string;
682
707
  parameters: Record<string, unknown>;
708
+ description: string;
683
709
  }[] | undefined;
684
710
  temperature?: number | undefined;
685
711
  purpose?: string | undefined;
@@ -1032,6 +1058,81 @@ declare const imageInputSchema: z.ZodObject<{
1032
1058
  safetyTolerance?: number | undefined;
1033
1059
  retryOnBlack?: boolean | undefined;
1034
1060
  }>;
1061
+ declare const animateInputSchema: z.ZodObject<{
1062
+ tier: z.ZodOptional<z.ZodEnum<["fast", "smart", "powerful", "cheap", "vision", "video", "embedding"]>>;
1063
+ override: z.ZodOptional<z.ZodObject<{
1064
+ provider: z.ZodOptional<z.ZodString>;
1065
+ model: z.ZodOptional<z.ZodString>;
1066
+ transport: z.ZodOptional<z.ZodEnum<["http", "subprocess"]>>;
1067
+ }, "strip", z.ZodTypeAny, {
1068
+ provider?: string | undefined;
1069
+ model?: string | undefined;
1070
+ transport?: "http" | "subprocess" | undefined;
1071
+ }, {
1072
+ provider?: string | undefined;
1073
+ model?: string | undefined;
1074
+ transport?: "http" | "subprocess" | undefined;
1075
+ }>>;
1076
+ fallback: z.ZodOptional<z.ZodArray<z.ZodUnion<[z.ZodEnum<["fast", "smart", "powerful", "cheap", "vision", "video", "embedding"]>, z.ZodObject<{
1077
+ provider: z.ZodString;
1078
+ model: z.ZodString;
1079
+ transport: z.ZodEnum<["http", "subprocess"]>;
1080
+ }, "strip", z.ZodTypeAny, {
1081
+ provider: string;
1082
+ model: string;
1083
+ transport: "http" | "subprocess";
1084
+ }, {
1085
+ provider: string;
1086
+ model: string;
1087
+ transport: "http" | "subprocess";
1088
+ }>]>, "many">>;
1089
+ purpose: z.ZodOptional<z.ZodString>;
1090
+ labels: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
1091
+ /** Input image: a URL or raw bytes (uploaded to fal storage). */
1092
+ image: z.ZodUnion<[z.ZodString, z.ZodType<Uint8Array<ArrayBuffer>, z.ZodTypeDef, Uint8Array<ArrayBuffer>>]>;
1093
+ /** Motion/scene prompt. */
1094
+ prompt: z.ZodOptional<z.ZodString>;
1095
+ /** Clip length in seconds (provider-dependent; Veo ≤ 8s). */
1096
+ durationSec: z.ZodOptional<z.ZodNumber>;
1097
+ /** e.g. "720p" / "1080p" (provider-dependent). */
1098
+ resolution: z.ZodOptional<z.ZodString>;
1099
+ }, "strip", z.ZodTypeAny, {
1100
+ image: string | Uint8Array<ArrayBuffer>;
1101
+ prompt?: string | undefined;
1102
+ resolution?: string | undefined;
1103
+ purpose?: string | undefined;
1104
+ tier?: "fast" | "smart" | "powerful" | "cheap" | "vision" | "video" | "embedding" | undefined;
1105
+ override?: {
1106
+ provider?: string | undefined;
1107
+ model?: string | undefined;
1108
+ transport?: "http" | "subprocess" | undefined;
1109
+ } | undefined;
1110
+ fallback?: ("fast" | "smart" | "powerful" | "cheap" | "vision" | "video" | "embedding" | {
1111
+ provider: string;
1112
+ model: string;
1113
+ transport: "http" | "subprocess";
1114
+ })[] | undefined;
1115
+ labels?: Record<string, string> | undefined;
1116
+ durationSec?: number | undefined;
1117
+ }, {
1118
+ image: string | Uint8Array<ArrayBuffer>;
1119
+ prompt?: string | undefined;
1120
+ resolution?: string | undefined;
1121
+ purpose?: string | undefined;
1122
+ tier?: "fast" | "smart" | "powerful" | "cheap" | "vision" | "video" | "embedding" | undefined;
1123
+ override?: {
1124
+ provider?: string | undefined;
1125
+ model?: string | undefined;
1126
+ transport?: "http" | "subprocess" | undefined;
1127
+ } | undefined;
1128
+ fallback?: ("fast" | "smart" | "powerful" | "cheap" | "vision" | "video" | "embedding" | {
1129
+ provider: string;
1130
+ model: string;
1131
+ transport: "http" | "subprocess";
1132
+ })[] | undefined;
1133
+ labels?: Record<string, string> | undefined;
1134
+ durationSec?: number | undefined;
1135
+ }>;
1035
1136
  declare const trainStyleInputSchema: z.ZodObject<{
1036
1137
  tier: z.ZodOptional<z.ZodEnum<["fast", "smart", "powerful", "cheap", "vision", "video", "embedding"]>>;
1037
1138
  override: z.ZodOptional<z.ZodObject<{
@@ -1591,6 +1692,7 @@ type VisionInput = z.infer<typeof visionInputSchema>;
1591
1692
  type VideoInput = z.infer<typeof videoInputSchema>;
1592
1693
  type TranslateInput = z.infer<typeof translateInputSchema>;
1593
1694
  type ImageInput = z.infer<typeof imageInputSchema>;
1695
+ type AnimateInput = z.infer<typeof animateInputSchema>;
1594
1696
  type TrainStyleInput = z.infer<typeof trainStyleInputSchema>;
1595
1697
  type EmbeddingInput = z.infer<typeof embeddingInputSchema>;
1596
1698
  type TranscribeInput = z.infer<typeof transcribeInputSchema>;
@@ -1610,6 +1712,8 @@ interface AiClient {
1610
1712
  video(input: VideoInput): Promise<ChatResult>;
1611
1713
  translate(input: TranslateInput): Promise<TranslateResult>;
1612
1714
  image(input: ImageInput): Promise<ImageResult>;
1715
+ /** Image-to-video (F024) — animate a still into a short clip → { url, usage }. fal. */
1716
+ animate(input: AnimateInput): Promise<AnimateResult>;
1613
1717
  /** Train a style/brand LoRA from images (F021) → { loraUrl, configUrl }. fal. */
1614
1718
  trainStyle(input: TrainStyleInput): Promise<TrainStyleResult>;
1615
1719
  embedding(input: EmbeddingInput): Promise<EmbeddingResult>;
@@ -1665,6 +1769,12 @@ declare function geminiAdapter(config?: {
1665
1769
  baseUrl?: string;
1666
1770
  fetch?: typeof fetch;
1667
1771
  pricePerImage?: number;
1772
+ /** Override the per-SECOND Veo video price (else estimate per model, 0 if unknown). */
1773
+ pricePerSecond?: number;
1774
+ /** Poll interval for Veo long-running operations (default 5s). */
1775
+ pollIntervalMs?: number;
1776
+ /** Deadline for a Veo job (default 300000 = 5 min). */
1777
+ videoTimeoutMs?: number;
1668
1778
  }): ProviderAdapter;
1669
1779
 
1670
1780
  declare function deepinfraAdapter(config?: {
@@ -1715,11 +1825,15 @@ interface FalAdapterConfig {
1715
1825
  timeoutMs?: number;
1716
1826
  /** Deadline for training jobs — they take minutes (default 600000 = 10 min). */
1717
1827
  trainTimeoutMs?: number;
1828
+ /** Deadline for video jobs — they take minutes (default 600000 = 10 min). */
1829
+ videoTimeoutMs?: number;
1718
1830
  fetch?: typeof fetch;
1719
1831
  /** Override the per-image USD price (else a built-in estimate per model, 0 if unknown). */
1720
1832
  pricePerImage?: number;
1721
1833
  /** Override the flat per-training USD price (else ~$2 estimate). */
1722
1834
  pricePerTraining?: number;
1835
+ /** Override the per-SECOND video USD price (else a built-in estimate per model, 0 if unknown). */
1836
+ pricePerSecond?: number;
1723
1837
  }
1724
1838
  declare function falAdapter(config?: FalAdapterConfig): ProviderAdapter;
1725
1839
 
@@ -1781,8 +1895,8 @@ declare const falStubAdapter: ProviderAdapter;
1781
1895
  * wires the live adapters. */
1782
1896
  declare const stubProviders: Record<string, ProviderAdapter>;
1783
1897
 
1784
- declare const VERSION: "0.16.0";
1785
- declare const SDK_TAG: "@broberg/ai-sdk@0.16.0";
1898
+ declare const VERSION: "0.17.0";
1899
+ declare const SDK_TAG: "@broberg/ai-sdk@0.17.0";
1786
1900
 
1787
1901
  /** Built-in defaults. Every entry is overridable via AiConfig.defaults or a
1788
1902
  * per-call override. Model IDs are current at scaffold time; callers pin their
package/dist/index.js CHANGED
@@ -870,6 +870,16 @@ function partsFrom(content) {
870
870
  return { inlineData: { mimeType: p.mimeType ?? "image/png", data } };
871
871
  });
872
872
  }
873
+ var VEO_PRICE_PER_SEC = {
874
+ "veo-3.1-generate-preview": 0.4,
875
+ // standard; 4K = 0.60
876
+ "veo-3.1-fast-generate-preview": 0.1,
877
+ // 720p; 1080p = 0.12, 4K = 0.30
878
+ "veo-3.1-lite-generate-preview": 0.05,
879
+ // 720p; 1080p = 0.08
880
+ "veo-3.0-generate-001": 0.4,
881
+ "veo-3.0-fast-generate-001": 0.1
882
+ };
873
883
  function geminiAdapter(config = {}) {
874
884
  const baseUrl = config.baseUrl ?? "https://generativelanguage.googleapis.com/v1beta";
875
885
  function resolveKey() {
@@ -1025,7 +1035,82 @@ function geminiAdapter(config = {}) {
1025
1035
  usage.costUsd = config.pricePerImage ?? GEMINI_IMAGE_PRICE_PER_IMAGE[req.spec.model] ?? 0;
1026
1036
  return { url, usage };
1027
1037
  }
1028
- return { name: "gemini", chat, chatStream, image, vision: chat };
1038
+ async function animate(req) {
1039
+ const apiKey = resolveKey();
1040
+ const fetchImpl = config.fetch ?? fetch;
1041
+ const pollIntervalMs = config.pollIntervalMs ?? 5e3;
1042
+ const deadline = Date.now() + (config.videoTimeoutMs ?? 3e5);
1043
+ const { data, mimeType } = await toInlineImage(req.image, fetchImpl);
1044
+ const parameters = {};
1045
+ if (req.durationSec !== void 0) parameters.durationSeconds = req.durationSec;
1046
+ if (req.resolution !== void 0) parameters.resolution = req.resolution;
1047
+ const body = {
1048
+ instances: [{ prompt: req.prompt ?? "", image: { bytesBase64Encoded: data, mimeType } }],
1049
+ ...Object.keys(parameters).length ? { parameters } : {}
1050
+ };
1051
+ const submit = await fetchImpl(
1052
+ `${baseUrl}/models/${req.spec.model}:predictLongRunning?key=${encodeURIComponent(apiKey)}`,
1053
+ { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify(body) }
1054
+ );
1055
+ if (!submit.ok) {
1056
+ throw new Error(`gemini animate ${submit.status}: ${(await submit.text().catch(() => "")).slice(0, 300)}`);
1057
+ }
1058
+ const op = await submit.json();
1059
+ if (!op.name) throw new Error("gemini animate: no operation name in submit response");
1060
+ let videoUri;
1061
+ for (; ; ) {
1062
+ const poll = await fetchImpl(`${baseUrl}/${op.name}?key=${encodeURIComponent(apiKey)}`, {
1063
+ headers: { "content-type": "application/json" }
1064
+ });
1065
+ if (!poll.ok) throw new Error(`gemini animate poll ${poll.status}`);
1066
+ const opData = await poll.json();
1067
+ if (opData.error) throw new Error(`gemini animate: ${opData.error.message ?? "operation error"}`);
1068
+ if (opData.done) {
1069
+ videoUri = opData.response?.generateVideoResponse?.generatedSamples?.[0]?.video?.uri;
1070
+ if (!videoUri) throw new Error(`gemini animate: done but no video uri: ${JSON.stringify(opData.response).slice(0, 300)}`);
1071
+ break;
1072
+ }
1073
+ if (Date.now() >= deadline) throw new Error("gemini animate: timed out");
1074
+ await new Promise((r) => setTimeout(r, pollIntervalMs));
1075
+ }
1076
+ const dl = await fetchImpl(videoUri.includes("key=") ? videoUri : `${videoUri}${videoUri.includes("?") ? "&" : "?"}key=${encodeURIComponent(apiKey)}`);
1077
+ if (!dl.ok) throw new Error(`gemini animate download ${dl.status}`);
1078
+ const bytes = new Uint8Array(await dl.arrayBuffer());
1079
+ const usage = freshUsage({
1080
+ provider: "gemini",
1081
+ model: req.spec.model,
1082
+ transport: "http",
1083
+ capability: "animate",
1084
+ inputTokens: 0,
1085
+ outputTokens: 0
1086
+ });
1087
+ const perSec = config.pricePerSecond ?? VEO_PRICE_PER_SEC[req.spec.model] ?? 0;
1088
+ usage.costUsd = perSec * (req.durationSec ?? 8);
1089
+ return { url: videoUri, bytes, mimeType: "video/mp4", usage };
1090
+ }
1091
+ return { name: "gemini", chat, chatStream, image, animate, vision: chat };
1092
+ }
1093
+ async function toInlineImage(image, fetchImpl) {
1094
+ if (typeof image !== "string") {
1095
+ return { data: Buffer.from(image).toString("base64"), mimeType: sniffMime(image) };
1096
+ }
1097
+ if (/^https?:\/\//i.test(image)) {
1098
+ const res = await fetchImpl(image);
1099
+ if (!res.ok) throw new Error(`gemini animate: failed to fetch image (${res.status})`);
1100
+ const bytes = new Uint8Array(await res.arrayBuffer());
1101
+ const mimeType2 = res.headers.get("content-type") ?? sniffMime(bytes);
1102
+ return { data: Buffer.from(bytes).toString("base64"), mimeType: mimeType2 };
1103
+ }
1104
+ const comma = image.startsWith("data:") ? image.indexOf(",") : -1;
1105
+ const b64 = comma >= 0 ? image.slice(comma + 1) : image;
1106
+ const mimeType = image.startsWith("data:") ? image.slice(5, image.indexOf(";")) : "image/png";
1107
+ return { data: b64, mimeType };
1108
+ }
1109
+ function sniffMime(b) {
1110
+ if (b[0] === 137 && b[1] === 80) return "image/png";
1111
+ if (b[0] === 71 && b[1] === 73) return "image/gif";
1112
+ if (b[0] === 82 && b[1] === 73 && b[8] === 87) return "image/webp";
1113
+ return "image/jpeg";
1029
1114
  }
1030
1115
  function mapGeminiFinish(reason) {
1031
1116
  switch (reason) {
@@ -1320,6 +1405,8 @@ var FAL_IMAGE_PRICE_ESTIMATE = {
1320
1405
  "fal-ai/flux-pro/v1.1": 0.04
1321
1406
  };
1322
1407
  var FAL_TRAIN_PRICE_ESTIMATE = 2;
1408
+ var FAL_VIDEO_PRICE_PER_SEC = {};
1409
+ var FAL_VIDEO_DEFAULT_SEC = 8;
1323
1410
  var sleep = (ms) => new Promise((r) => setTimeout(r, ms));
1324
1411
  function falAdapter(config = {}) {
1325
1412
  const doFetch = config.fetch ?? fetch;
@@ -1362,6 +1449,36 @@ function falAdapter(config = {}) {
1362
1449
  usage.costUsd = (config.pricePerImage ?? FAL_IMAGE_PRICE_ESTIMATE[req.spec.model] ?? 0) * calls;
1363
1450
  return { url, usage };
1364
1451
  }
1452
+ async function animate(req) {
1453
+ const apiKey = resolveKey();
1454
+ if (!apiKey) throw new Error("fal adapter: FAL_KEY not set");
1455
+ const headers = authHeaders(apiKey);
1456
+ const imageUrl = typeof req.image === "string" && /^https?:\/\//i.test(req.image) ? req.image : await uploadToFalStorage(toBytes(req.image), sniffImageType(req.image), "input", apiKey);
1457
+ const body = { image_url: imageUrl };
1458
+ if (req.prompt !== void 0) body.prompt = req.prompt;
1459
+ if (req.durationSec !== void 0) body.duration = req.durationSec;
1460
+ if (req.resolution !== void 0) body.resolution = req.resolution;
1461
+ const result = await queueResult(req.spec.model, headers, body, config.videoTimeoutMs ?? 6e5);
1462
+ const url = extractVideoUrl(result);
1463
+ if (!url) {
1464
+ throw new Error(
1465
+ `fal animate: no video url in result \u2014 keys [${Object.keys(
1466
+ result ?? {}
1467
+ ).join(", ")}]: ${JSON.stringify(result).slice(0, 500)}`
1468
+ );
1469
+ }
1470
+ const usage = freshUsage({
1471
+ provider: "fal",
1472
+ model: req.spec.model,
1473
+ transport: "http",
1474
+ capability: "animate",
1475
+ inputTokens: 0,
1476
+ outputTokens: 0
1477
+ });
1478
+ const perSec = config.pricePerSecond ?? FAL_VIDEO_PRICE_PER_SEC[req.spec.model] ?? 0;
1479
+ usage.costUsd = perSec * (req.durationSec ?? FAL_VIDEO_DEFAULT_SEC);
1480
+ return { url, usage };
1481
+ }
1365
1482
  async function trainStyle(req) {
1366
1483
  const apiKey = resolveKey();
1367
1484
  if (!apiKey) throw new Error("fal adapter: FAL_KEY not set");
@@ -1481,7 +1598,24 @@ function falAdapter(config = {}) {
1481
1598
  }
1482
1599
  return resultRes.json();
1483
1600
  }
1484
- return { name: "fal", image, trainStyle };
1601
+ return { name: "fal", image, animate, trainStyle };
1602
+ }
1603
+ function toBytes(img) {
1604
+ if (typeof img !== "string") return img;
1605
+ const b64 = img.startsWith("data:") ? img.slice(img.indexOf(",") + 1) : img;
1606
+ return new Uint8Array(Buffer.from(b64, "base64"));
1607
+ }
1608
+ function sniffImageType(img) {
1609
+ const b = toBytes(img);
1610
+ if (b[0] === 137 && b[1] === 80) return "image/png";
1611
+ if (b[0] === 71 && b[1] === 73) return "image/gif";
1612
+ if (b[0] === 82 && b[1] === 73 && b[8] === 87) return "image/webp";
1613
+ return "image/jpeg";
1614
+ }
1615
+ function extractVideoUrl(result) {
1616
+ const r = result;
1617
+ const root = r?.data ?? r?.response ?? r?.output ?? r;
1618
+ return urlOf(root?.video) ?? urlOf(root?.videos?.[0]) ?? deepFindUrl(root, (u) => /\.(mp4|webm|mov)(\?|$)/i.test(u));
1485
1619
  }
1486
1620
  function urlOf(v) {
1487
1621
  if (typeof v === "string") return v;
@@ -2019,6 +2153,17 @@ var imageInputSchema = z.object({
2019
2153
  retryOnBlack: z.boolean().optional(),
2020
2154
  ...callOptions
2021
2155
  });
2156
+ var animateInputSchema = z.object({
2157
+ /** Input image: a URL or raw bytes (uploaded to fal storage). */
2158
+ image: z.union([z.string(), z.instanceof(Uint8Array)]),
2159
+ /** Motion/scene prompt. */
2160
+ prompt: z.string().optional(),
2161
+ /** Clip length in seconds (provider-dependent; Veo ≤ 8s). */
2162
+ durationSec: z.number().positive().optional(),
2163
+ /** e.g. "720p" / "1080p" (provider-dependent). */
2164
+ resolution: z.string().optional(),
2165
+ ...callOptions
2166
+ });
2022
2167
  var trainStyleInputSchema = z.object({
2023
2168
  /** A hosted archive URL, or an array of image URLs the SDK zips in-memory. */
2024
2169
  images: z.union([z.string(), z.array(z.string())]),
@@ -2097,6 +2242,11 @@ var DEFAULT_TRAINSTYLE_SPEC = {
2097
2242
  model: "fal-ai/flux-lora-fast-training",
2098
2243
  transport: "http"
2099
2244
  };
2245
+ var DEFAULT_ANIMATE_SPEC = {
2246
+ provider: "gemini",
2247
+ model: "veo-3.1-generate-preview",
2248
+ transport: "http"
2249
+ };
2100
2250
  var DEFAULT_BFL_FINETUNE_SPEC = {
2101
2251
  provider: "bfl",
2102
2252
  model: "flux-pro-1.1-ultra-finetuned",
@@ -2383,6 +2533,30 @@ function createAI(config = {}) {
2383
2533
  }
2384
2534
  });
2385
2535
  },
2536
+ async animate(input) {
2537
+ input = animateInputSchema.parse(input);
2538
+ return runCapability({
2539
+ primary: { ...DEFAULT_ANIMATE_SPEC, ...input.override },
2540
+ fallback: input.fallback,
2541
+ capability: "animate",
2542
+ purpose: input.purpose,
2543
+ labels: input.labels,
2544
+ estIn: 0,
2545
+ // video cost is per-second, not token-based
2546
+ estOut: 0,
2547
+ invoke: async (spec) => {
2548
+ const adapter = pickProvider(spec.provider);
2549
+ if (!adapter.animate) throw new Error(`createAI: provider "${spec.provider}" does not support animate`);
2550
+ return adapter.animate({
2551
+ image: input.image,
2552
+ prompt: input.prompt,
2553
+ durationSec: input.durationSec,
2554
+ resolution: input.resolution,
2555
+ spec
2556
+ });
2557
+ }
2558
+ });
2559
+ },
2386
2560
  async trainStyle(input) {
2387
2561
  input = trainStyleInputSchema.parse(input);
2388
2562
  return runCapability({
@@ -2633,8 +2807,8 @@ var stubProviders = {
2633
2807
  };
2634
2808
 
2635
2809
  // src/version.ts
2636
- var VERSION = "0.16.0";
2637
- var SDK_TAG = "@broberg/ai-sdk@0.16.0";
2810
+ var VERSION = "0.17.0";
2811
+ var SDK_TAG = "@broberg/ai-sdk@0.17.0";
2638
2812
 
2639
2813
  // src/availability/refresh.ts
2640
2814
  var NOT_REFRESHED = { refreshed: false, checked: 0, markedUnavailable: [] };