nano banana pro text to image

This commit is contained in:
Brandon Thomas committed 2026-05-10 02:13:25 -04:00
1 parent 7723341b6c
commit d78cca34f3
5 files changed
+235

No files matched your search

@@ -1,3 +1,4 @@
pub mod flux_1_dev_text_to_image;
pub mod flux_1_schnell_text_to_image;
pub mod nano_banana_2_text_to_image;
pub mod nano_banana_pro_text_to_image;
@@ -0,0 +1,123 @@
use crate::error::fal_error_plus::FalErrorPlus;
use crate::requests::api::image::text::nano_banana_pro_text_to_image::raw_request::{
NanoBananaProTextToImageInput, NanoBananaProTextToImageOutput,
};
use crate::requests::traits::fal_endpoint_trait::FalEndpoint;
#[derive(Clone, Debug)]
pub struct NanoBananaProTextToImageRequest {
/// Text prompt describing the image to generate.
pub prompt: String,
/// Number of images to generate.
pub num_images: NanoBananaProTextToImageNumImages,
/// Output resolution.
pub resolution: Option<NanoBananaProTextToImageResolution>,
/// Aspect ratio.
pub aspect_ratio: Option<NanoBananaProTextToImageAspectRatio>,
}
#[derive(Copy, Clone, Debug)]
pub enum NanoBananaProTextToImageNumImages {
One,
Two,
Three,
Four,
}
#[derive(Copy, Clone, Debug)]
pub enum NanoBananaProTextToImageResolution {
OneK,
TwoK,
FourK,
}
/// 21:9, 16:9, 3:2, 4:3, 5:4, 1:1, 4:5, 3:4, 2:3, 9:16
#[derive(Copy, Clone, Debug)]
pub enum NanoBananaProTextToImageAspectRatio {
OneByOne,
FiveByFour,
FourByThree,
ThreeByTwo,
SixteenByNine,
TwentyOneByNine,
FourByFive,
ThreeByFour,
TwoByThree,
NineBySixteen,
}
impl FalEndpoint for NanoBananaProTextToImageRequest {
const ENDPOINT: &str = "fal-ai/nano-banana-pro";
type RawRequest = NanoBananaProTextToImageInput;
type RawResponse = NanoBananaProTextToImageOutput;
fn to_raw_request(&self) -> Result<Self::RawRequest, FalErrorPlus> {
let num_images = match self.num_images {
NanoBananaProTextToImageNumImages::One => 1,
NanoBananaProTextToImageNumImages::Two => 2,
NanoBananaProTextToImageNumImages::Three => 3,
NanoBananaProTextToImageNumImages::Four => 4,
};
let resolution = self.resolution.map(|r| match r {
NanoBananaProTextToImageResolution::OneK => "1K",
NanoBananaProTextToImageResolution::TwoK => "2K",
NanoBananaProTextToImageResolution::FourK => "4K",
}.to_string());
let aspect_ratio = self.aspect_ratio.map(|ar| match ar {
NanoBananaProTextToImageAspectRatio::OneByOne => "1:1",
NanoBananaProTextToImageAspectRatio::FiveByFour => "5:4",
NanoBananaProTextToImageAspectRatio::FourByThree => "4:3",
NanoBananaProTextToImageAspectRatio::ThreeByTwo => "3:2",
NanoBananaProTextToImageAspectRatio::SixteenByNine => "16:9",
NanoBananaProTextToImageAspectRatio::TwentyOneByNine => "21:9",
NanoBananaProTextToImageAspectRatio::FourByFive => "4:5",
NanoBananaProTextToImageAspectRatio::ThreeByFour => "3:4",
NanoBananaProTextToImageAspectRatio::TwoByThree => "2:3",
NanoBananaProTextToImageAspectRatio::NineBySixteen => "9:16",
}.to_string());
Ok(Self::RawRequest {
prompt: self.prompt.clone(),
num_images: Some(num_images),
aspect_ratio,
resolution,
output_format: Some("png".to_string()),
})
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::creds::fal_api_key::FalApiKey;
use crate::requests::traits::fal_endpoint_trait::FalEndpoint;
use errors::AnyhowResult;
use std::fs::read_to_string;
#[tokio::test]
#[ignore] // manually test — requires real API key, incurs costs
async fn test_text_to_image() -> AnyhowResult<()> {
let secret = read_to_string("/Users/bt/Artcraft/credentials/fal_api_key.txt")?;
let api_key = FalApiKey::from_str(&secret);
let request = NanoBananaProTextToImageRequest {
prompt: "an anime girl riding on the back of a t-rex".to_string(),
num_images: NanoBananaProTextToImageNumImages::One,
resolution: Some(NanoBananaProTextToImageResolution::TwoK),
aspect_ratio: Some(NanoBananaProTextToImageAspectRatio::SixteenByNine),
};
let result = request.send_webhook_request(&api_key, "https://example.com/test").await?;
println!("Request ID: {:?}", result.request_id);
assert!(result.request_id.is_some());
Ok(())
}
// NB: Pricing tests are in cost.rs
}
@@ -0,0 +1,72 @@
use crate::requests::api::image::text::nano_banana_pro_text_to_image::api::{
NanoBananaProTextToImageNumImages, NanoBananaProTextToImageRequest,
NanoBananaProTextToImageResolution,
};
use crate::requests::traits::fal_request_cost_calculator_trait::{FalRequestCostCalculator, UsdCents};
impl FalRequestCostCalculator for NanoBananaProTextToImageRequest {
fn calculate_cost_in_cents(&self) -> UsdCents {
// Your request will cost $0.15 per image.
// 4K outputs will be charged at double the standard rate.
let base_cost = match self.resolution {
None => 15,
Some(NanoBananaProTextToImageResolution::OneK) => 15,
Some(NanoBananaProTextToImageResolution::TwoK) => 15,
Some(NanoBananaProTextToImageResolution::FourK) => 30,
};
let cost = match self.num_images {
NanoBananaProTextToImageNumImages::One => base_cost,
NanoBananaProTextToImageNumImages::Two => base_cost * 2,
NanoBananaProTextToImageNumImages::Three => base_cost * 3,
NanoBananaProTextToImageNumImages::Four => base_cost * 4,
};
cost as UsdCents
}
}
#[cfg(test)]
mod tests {
use super::*;
fn make_request(
num_images: NanoBananaProTextToImageNumImages,
resolution: Option<NanoBananaProTextToImageResolution>,
) -> NanoBananaProTextToImageRequest {
NanoBananaProTextToImageRequest {
prompt: "test".to_string(),
num_images,
resolution,
aspect_ratio: None,
}
}
#[test]
fn cost_default_resolution_one_image() {
assert_eq!(make_request(NanoBananaProTextToImageNumImages::One, None).calculate_cost_in_cents(), 15);
}
#[test]
fn cost_one_k_one_image() {
assert_eq!(make_request(NanoBananaProTextToImageNumImages::One, Some(NanoBananaProTextToImageResolution::OneK)).calculate_cost_in_cents(), 15);
}
#[test]
fn cost_two_k_one_image() {
assert_eq!(make_request(NanoBananaProTextToImageNumImages::One, Some(NanoBananaProTextToImageResolution::TwoK)).calculate_cost_in_cents(), 15);
}
#[test]
fn cost_four_k_one_image() {
assert_eq!(make_request(NanoBananaProTextToImageNumImages::One, Some(NanoBananaProTextToImageResolution::FourK)).calculate_cost_in_cents(), 30);
}
#[test]
fn cost_one_k_four_images() {
assert_eq!(make_request(NanoBananaProTextToImageNumImages::Four, Some(NanoBananaProTextToImageResolution::OneK)).calculate_cost_in_cents(), 60);
}
#[test]
fn cost_four_k_two_images() {
assert_eq!(make_request(NanoBananaProTextToImageNumImages::Two, Some(NanoBananaProTextToImageResolution::FourK)).calculate_cost_in_cents(), 60);
}
}
@@ -0,0 +1,3 @@
pub mod api;
mod raw_request;
mod cost;
@@ -0,0 +1,36 @@
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize, Default)]
pub struct NanoBananaProTextToImageInput {
pub prompt: String,
/// Options: auto, 21:9, 16:9, 3:2, 4:3, 5:4, 1:1, 4:5, 3:4, 2:3, 9:16
/// Default: "auto"
#[serde(skip_serializing_if = "Option::is_none")]
pub aspect_ratio: Option<String>,
/// "1K", "2K", "4K"
/// Default: "1K"
#[serde(skip_serializing_if = "Option::is_none")]
pub resolution: Option<String>,
/// 1 - 4
/// Default: 1
#[serde(skip_serializing_if = "Option::is_none")]
pub num_images: Option<u8>,
/// "jpeg", "png", "webp"
/// Default: "png"
#[serde(skip_serializing_if = "Option::is_none")]
pub output_format: Option<String>,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct NanoBananaProTextToImageFile {
pub url: String,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct NanoBananaProTextToImageOutput {
pub images: Vec<NanoBananaProTextToImageFile>,
}