This commit is contained in:
Brandon Thomas committed 2026-05-10 00:14:54 -04:00
1 parent 5d59aaeafe
commit 33a866a67d
5 files changed
+215

No files matched your search

@@ -0,0 +1,117 @@
use crate::error::fal_error_plus::FalErrorPlus;
use crate::requests::api::image::edit::flux_1_schnell_edit_image::raw_request::{
Flux1SchnellEditImageInput, Flux1SchnellEditImageOutput,
};
use crate::requests::traits::fal_endpoint_trait::FalEndpoint;
#[derive(Clone, Debug)]
pub struct Flux1SchnellEditImageRequest {
/// URL of the source image to edit.
pub image_url: String,
/// Number of images to generate.
pub num_images: Flux1SchnellEditImageNumImages,
/// Output image size.
pub image_size: Option<Flux1SchnellEditImageSize>,
}
#[derive(Copy, Clone, Debug)]
pub enum Flux1SchnellEditImageNumImages {
One,
Two,
Three,
Four,
}
#[derive(Copy, Clone, Debug)]
pub enum Flux1SchnellEditImageSize {
Square,
SquareHd,
LandscapeFourByThree,
LandscapeSixteenByNine,
PortraitThreeByFour,
PortraitNineBySixteen,
}
impl FalEndpoint for Flux1SchnellEditImageRequest {
const ENDPOINT: &str = "fal-ai/flux/schnell/redux";
type RawRequest = Flux1SchnellEditImageInput;
type RawResponse = Flux1SchnellEditImageOutput;
fn to_raw_request(&self) -> Result<Self::RawRequest, FalErrorPlus> {
let num_images = match self.num_images {
Flux1SchnellEditImageNumImages::One => 1,
Flux1SchnellEditImageNumImages::Two => 2,
Flux1SchnellEditImageNumImages::Three => 3,
Flux1SchnellEditImageNumImages::Four => 4,
};
let image_size = self.image_size.map(|s| match s {
Flux1SchnellEditImageSize::Square => "square",
Flux1SchnellEditImageSize::SquareHd => "square_hd",
Flux1SchnellEditImageSize::LandscapeFourByThree => "landscape_4_3",
Flux1SchnellEditImageSize::LandscapeSixteenByNine => "landscape_16_9",
Flux1SchnellEditImageSize::PortraitThreeByFour => "portrait_4_3",
Flux1SchnellEditImageSize::PortraitNineBySixteen => "portrait_16_9",
}.to_string());
Ok(Self::RawRequest {
image_url: self.image_url.clone(),
num_images: Some(num_images),
image_size,
enable_safety_checker: Some(false),
output_format: Some("png".to_string()),
..Default::default()
})
}
}
#[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;
use test_data::web::image_urls::GHOST_IMAGE_URL;
#[tokio::test]
#[ignore] // manually test — requires real API key, incurs costs
async fn test_single_image_no_size() -> AnyhowResult<()> {
let secret = read_to_string("/Users/bt/Artcraft/credentials/fal_api_key.txt")?;
let api_key = FalApiKey::from_str(&secret);
let request = Flux1SchnellEditImageRequest {
image_url: GHOST_IMAGE_URL.to_string(),
num_images: Flux1SchnellEditImageNumImages::One,
image_size: None,
};
let result = request.send_queue_request(&api_key).await?;
println!("Request ID: {}", result.request_id);
assert!(!result.request_id.is_empty());
Ok(())
}
#[tokio::test]
#[ignore] // manually test — requires real API key, incurs costs
async fn test_with_landscape_size() -> AnyhowResult<()> {
let secret = read_to_string("/Users/bt/Artcraft/credentials/fal_api_key.txt")?;
let api_key = FalApiKey::from_str(&secret);
let request = Flux1SchnellEditImageRequest {
image_url: GHOST_IMAGE_URL.to_string(),
num_images: Flux1SchnellEditImageNumImages::Two,
image_size: Some(Flux1SchnellEditImageSize::LandscapeSixteenByNine),
};
let result = request.send_queue_request(&api_key).await?;
println!("Request ID: {}", result.request_id);
assert!(!result.request_id.is_empty());
Ok(())
}
// NB: Pricing tests are in cost.rs
}
@@ -0,0 +1,53 @@
use crate::requests::api::image::edit::flux_1_schnell_edit_image::api::{
Flux1SchnellEditImageNumImages, Flux1SchnellEditImageRequest,
};
use crate::requests::traits::fal_request_cost_calculator_trait::{FalRequestCostCalculator, UsdCents};
impl FalRequestCostCalculator for Flux1SchnellEditImageRequest {
fn calculate_cost_in_cents(&self) -> UsdCents {
// Pricing: $0.003 per megapixel.
// For a 1024x1024 image (~1 MP), that's less than 1 cent per image.
// We round up to 1 cent per image.
let unit_cost = 1;
let cost = match self.num_images {
Flux1SchnellEditImageNumImages::One => unit_cost,
Flux1SchnellEditImageNumImages::Two => unit_cost * 2,
Flux1SchnellEditImageNumImages::Three => unit_cost * 3,
Flux1SchnellEditImageNumImages::Four => unit_cost * 4,
};
cost as UsdCents
}
}
#[cfg(test)]
mod tests {
use super::*;
fn make_request(num_images: Flux1SchnellEditImageNumImages) -> Flux1SchnellEditImageRequest {
Flux1SchnellEditImageRequest {
image_url: "https://example.com/image.jpg".to_string(),
num_images,
image_size: None,
}
}
#[test]
fn cost_one_image() {
assert_eq!(make_request(Flux1SchnellEditImageNumImages::One).calculate_cost_in_cents(), 1);
}
#[test]
fn cost_two_images() {
assert_eq!(make_request(Flux1SchnellEditImageNumImages::Two).calculate_cost_in_cents(), 2);
}
#[test]
fn cost_three_images() {
assert_eq!(make_request(Flux1SchnellEditImageNumImages::Three).calculate_cost_in_cents(), 3);
}
#[test]
fn cost_four_images() {
assert_eq!(make_request(Flux1SchnellEditImageNumImages::Four).calculate_cost_in_cents(), 4);
}
}
@@ -0,0 +1,3 @@
pub mod api;
mod raw_request;
mod cost;
@@ -0,0 +1,41 @@
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize, Default)]
pub struct Flux1SchnellEditImageInput {
pub image_url: String,
/// Options: square_hd, square, portrait_4_3, portrait_16_9, landscape_4_3, landscape_16_9
#[serde(skip_serializing_if = "Option::is_none")]
pub image_size: Option<String>,
/// 1 - 4
/// Default: 1
#[serde(skip_serializing_if = "Option::is_none")]
pub num_images: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub seed: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub num_inference_steps: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub enable_safety_checker: Option<bool>,
/// Options: jpeg, png
#[serde(skip_serializing_if = "Option::is_none")]
pub output_format: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub sync_mode: Option<bool>,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct Flux1SchnellEditImageFile {
pub url: String,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct Flux1SchnellEditImageOutput {
pub images: Vec<Flux1SchnellEditImageFile>,
}
@@ -1 +1,2 @@
pub mod flux_1_dev_edit_image;
pub mod flux_1_schnell_edit_image;