climage 0.5.0 → 0.5.2

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.js CHANGED
@@ -624,6 +624,9 @@ var falProvider = {
624
624
 
625
625
  // src/providers/google.ts
626
626
  import { GoogleGenAI } from "@google/genai";
627
+ import { mkdtemp, readFile, rm } from "fs/promises";
628
+ import { tmpdir } from "os";
629
+ import { join } from "path";
627
630
  function getGeminiApiKey(env) {
628
631
  return env.GEMINI_API_KEY || env.GOOGLE_API_KEY || env.GOOGLE_GENAI_API_KEY;
629
632
  }
@@ -645,6 +648,7 @@ function log3(...args) {
645
648
  var MODEL_ALIASES = {
646
649
  "nano-banana": "gemini-2.5-flash-image",
647
650
  "nano-banana-pro": "gemini-3-pro-image-preview",
651
+ "nano-banana-2": "gemini-3.1-flash-image-preview",
648
652
  // Veo (video)
649
653
  veo2: "veo-2.0-generate-001",
650
654
  "veo-2": "veo-2.0-generate-001",
@@ -671,7 +675,26 @@ function imageToGoogleFormat(imageInput) {
671
675
  }
672
676
  return { fileUri: imageInput };
673
677
  }
674
- var GEMINI_IMAGE_MODELS = ["gemini-2.5-flash-image", "gemini-3-pro-image-preview"];
678
+ function imageToVeoFormat(imageInput) {
679
+ if (imageInput.startsWith("data:")) {
680
+ const parsed = parseDataUri(imageInput);
681
+ if (!parsed?.data) {
682
+ throw new Error("Failed to parse data URI for Veo image input");
683
+ }
684
+ return { imageBytes: parsed.data, mimeType: parsed.mimeType };
685
+ }
686
+ if (imageInput.startsWith("gs://")) {
687
+ return { gcsUri: imageInput };
688
+ }
689
+ throw new Error(
690
+ `Veo image inputs must be data: URIs or gs:// URIs (got ${imageInput.slice(0, 24)}...)`
691
+ );
692
+ }
693
+ var GEMINI_IMAGE_MODELS = [
694
+ "gemini-2.5-flash-image",
695
+ "gemini-3-pro-image-preview",
696
+ "gemini-3.1-flash-image-preview"
697
+ ];
675
698
  function resolveModel(model) {
676
699
  if (!model) return "gemini-2.5-flash-image";
677
700
  return MODEL_ALIASES[model] ?? model;
@@ -689,6 +712,31 @@ async function downloadBytes3(url) {
689
712
  log3(`Downloaded ${ab.byteLength} bytes in ${Date.now() - start}ms, type: ${ct}`);
690
713
  return { bytes: new Uint8Array(ab), mimeType: ct };
691
714
  }
715
+ async function downloadGeneratedVideo(ai, generatedVideo) {
716
+ const video = generatedVideo?.video;
717
+ if (video?.videoBytes) {
718
+ return {
719
+ bytes: new Uint8Array(Buffer.from(video.videoBytes, "base64")),
720
+ mimeType: video.mimeType
721
+ };
722
+ }
723
+ if (video?.uri && !video.uri.startsWith("gs://")) {
724
+ try {
725
+ return await downloadBytes3(video.uri);
726
+ } catch (err) {
727
+ log3("Direct video download failed, falling back to ai.files.download:", String(err));
728
+ }
729
+ }
730
+ const tempDir = await mkdtemp(join(tmpdir(), "climage-veo-"));
731
+ const downloadPath = join(tempDir, "video.mp4");
732
+ try {
733
+ await ai.files.download({ file: generatedVideo, downloadPath });
734
+ const buf = await readFile(downloadPath);
735
+ return { bytes: new Uint8Array(buf), mimeType: video?.mimeType ?? "video/mp4" };
736
+ } finally {
737
+ await rm(tempDir, { recursive: true, force: true });
738
+ }
739
+ }
692
740
  async function sleep2(ms) {
693
741
  await new Promise((r) => setTimeout(r, ms));
694
742
  }
@@ -754,15 +802,15 @@ async function generateWithVeo(ai, model, req) {
754
802
  const config = {
755
803
  numberOfVideos: req.n,
756
804
  ...req.aspectRatio ? { aspectRatio: req.aspectRatio } : {},
757
- // Add duration if specified (Veo 3.1 supports 4, 6, 8)
758
- ...req.duration !== void 0 ? { durationSeconds: String(req.duration) } : {}
805
+ // Add duration if specified (Veo supports 4-8 seconds depending on model)
806
+ ...req.duration !== void 0 ? { durationSeconds: req.duration } : {}
759
807
  };
760
808
  if (req.inputImages?.length && isVeo31Model(model)) {
761
809
  const referenceImages = req.inputImages.slice(0, 3).map((img) => {
762
- const imageData = imageToGoogleFormat(img);
810
+ const imageData = imageToVeoFormat(img);
763
811
  return {
764
812
  image: imageData,
765
- referenceType: "asset"
813
+ referenceType: "ASSET"
766
814
  };
767
815
  });
768
816
  config.referenceImages = referenceImages;
@@ -775,12 +823,12 @@ async function generateWithVeo(ai, model, req) {
775
823
  };
776
824
  const firstFrameImage = req.startFrame ?? (req.inputImages?.length === 1 ? req.inputImages[0] : void 0);
777
825
  if (firstFrameImage && isVeo31Model(model)) {
778
- const imageData = imageToGoogleFormat(firstFrameImage);
826
+ const imageData = imageToVeoFormat(firstFrameImage);
779
827
  generateParams.image = imageData;
780
828
  log3("Added first frame image");
781
829
  }
782
830
  if (req.endFrame && isVeo31Model(model)) {
783
- const lastFrameData = imageToGoogleFormat(req.endFrame);
831
+ const lastFrameData = imageToVeoFormat(req.endFrame);
784
832
  config.lastFrame = lastFrameData;
785
833
  log3("Added last frame for interpolation");
786
834
  }
@@ -810,26 +858,22 @@ async function generateWithVeo(ai, model, req) {
810
858
  for (let i = 0; i < Math.min(videos.length, req.n); i++) {
811
859
  const v = videos[i];
812
860
  log3(`Processing video ${i}:`, JSON.stringify(v).slice(0, 300));
813
- const uri = v?.video?.uri;
814
- if (!uri) {
815
- log3(`Video ${i} has no URI, skipping`);
861
+ if (!v?.video) {
862
+ log3(`Video ${i} has no video payload, skipping`);
816
863
  continue;
817
864
  }
818
- if (uri.startsWith("gs://")) {
819
- throw new Error(
820
- `Google Veo returned a gs:// URI (${uri}). Configure outputGcsUri / Vertex flow to fetch from GCS.`
821
- );
822
- }
823
- const { bytes, mimeType } = await downloadBytes3(uri);
824
- out.push({
865
+ const uri = v?.video?.uri;
866
+ const { bytes, mimeType } = await downloadGeneratedVideo(ai, v);
867
+ const item = {
825
868
  kind: "video",
826
869
  provider: "google",
827
870
  model,
828
871
  index: i,
829
- url: uri,
830
872
  bytes,
831
873
  ...mimeType !== void 0 ? { mimeType } : {}
832
- });
874
+ };
875
+ if (uri) item.url = uri;
876
+ out.push(item);
833
877
  }
834
878
  if (!out.length) throw new Error("Google Veo returned videos but none were downloadable");
835
879
  log3(`Successfully generated ${out.length} video(s)`);