#[cfg(all(feature = "aliyun", feature = "image"))]
mod aliyun;
#[cfg(all(feature = "openai", feature = "image"))]
mod openai_compat;
use async_trait::async_trait;
use crate::config::Provider;
use crate::config::ProviderConfig;
use crate::error::{Error, Result};
#[derive(Debug, Clone, Copy)]
pub enum ImageSize {
Square512,
Square1024,
Landscape,
Portrait,
}
#[derive(Debug, Clone)]
pub enum ImageOutput {
Url(String),
Bytes(Vec<u8>),
}
#[async_trait]
pub trait ImageProvider: Send + Sync {
async fn generate(&self, prompt: &str, size: ImageSize) -> Result<ImageOutput>;
}
pub(crate) fn create(config: &ProviderConfig) -> Result<Box<dyn ImageProvider>> {
match config.provider {
#[cfg(feature = "openai")]
Provider::OpenAI => Ok(Box::new(openai_compat::OpenaiCompatImage::new(config)?)),
#[cfg(not(feature = "openai"))]
Provider::OpenAI => Err(Error::ProviderDisabled("openai".to_string())),
#[cfg(feature = "aliyun")]
Provider::Aliyun => Ok(Box::new(aliyun::AliyunQwenImage::new(config)?)),
#[cfg(not(feature = "aliyun"))]
Provider::Aliyun => Err(Error::ProviderDisabled("aliyun".to_string())),
Provider::Ollama => Err(Error::Unsupported {
provider: config.provider.to_string(),
capability: "image",
}),
#[cfg(feature = "anthropic")]
Provider::Anthropic => Err(Error::Unsupported {
provider: config.provider.to_string(),
capability: "image",
}),
#[cfg(not(feature = "anthropic"))]
Provider::Anthropic => Err(Error::ProviderDisabled("anthropic".to_string())),
#[cfg(feature = "google")]
Provider::Google => Err(Error::Unsupported {
provider: config.provider.to_string(),
capability: "image",
}),
#[cfg(not(feature = "google"))]
Provider::Google => Err(Error::ProviderDisabled("google".to_string())),
Provider::Zhipu => Err(Error::Unsupported {
provider: config.provider.to_string(),
capability: "image",
}),
}
}
#[cfg(test)]
mod factory_tests {
use super::create;
use crate::config::{Provider, ProviderConfig};
use crate::error::Error;
#[cfg(feature = "ollama")]
#[test]
fn ollama_is_unsupported() {
let cfg = ProviderConfig::new(Provider::Ollama, "k", "http://localhost/v1", "m");
match create(&cfg) {
Err(Error::Unsupported {
provider,
capability,
}) => {
assert_eq!(provider, "ollama");
assert_eq!(capability, "image");
}
Ok(_) => panic!("expected error"),
Err(e) => panic!("expected Unsupported, got {:?}", e),
}
}
#[cfg(feature = "zhipu")]
#[test]
fn zhipu_is_unsupported() {
let cfg = ProviderConfig::new(Provider::Zhipu, "k", "https://x/v1", "m");
match create(&cfg) {
Err(Error::Unsupported {
provider,
capability,
}) => {
assert_eq!(provider, "zhipu");
assert_eq!(capability, "image");
}
Ok(_) => panic!("expected error"),
Err(e) => panic!("expected Unsupported, got {:?}", e),
}
}
#[cfg(feature = "anthropic")]
#[test]
fn anthropic_is_unsupported() {
let cfg = ProviderConfig::new(Provider::Anthropic, "k", "https://x/v1", "m");
match create(&cfg) {
Err(Error::Unsupported {
provider,
capability,
}) => {
assert_eq!(provider, "anthropic");
assert_eq!(capability, "image");
}
Ok(_) => panic!("expected error"),
Err(e) => panic!("expected Unsupported, got {:?}", e),
}
}
#[cfg(not(feature = "anthropic"))]
#[test]
fn anthropic_disabled_without_anthropic_feature() {
let cfg = ProviderConfig::new(
Provider::Anthropic,
"k",
"https://api.anthropic.com/v1",
"claude-3-opus",
);
match create(&cfg) {
Err(Error::ProviderDisabled(s)) => assert_eq!(s, "anthropic"),
Ok(_) => panic!("expected error"),
Err(e) => panic!("expected ProviderDisabled, got {:?}", e),
}
}
#[cfg(feature = "google")]
#[test]
fn google_is_unsupported() {
let cfg = ProviderConfig::new(Provider::Google, "k", "https://x/v1", "m");
match create(&cfg) {
Err(Error::Unsupported {
provider,
capability,
}) => {
assert_eq!(provider, "google");
assert_eq!(capability, "image");
}
Ok(_) => panic!("expected error"),
Err(e) => panic!("expected Unsupported, got {:?}", e),
}
}
#[cfg(not(feature = "google"))]
#[test]
fn google_disabled_without_google_feature() {
let cfg = ProviderConfig::new(
Provider::Google,
"k",
"https://generativelanguage.googleapis.com/v1beta",
"gemini-2.0-flash",
);
match create(&cfg) {
Err(Error::ProviderDisabled(s)) => assert_eq!(s, "google"),
Ok(_) => panic!("expected error"),
Err(e) => panic!("expected ProviderDisabled, got {:?}", e),
}
}
#[cfg(not(feature = "openai"))]
#[test]
fn openai_disabled_without_openai_feature() {
let cfg = ProviderConfig::new(
Provider::OpenAI,
"k",
"https://api.openai.com/v1",
"dall-e-3",
);
match create(&cfg) {
Err(Error::ProviderDisabled(s)) => assert_eq!(s, "openai"),
Ok(_) => panic!("expected error"),
Err(e) => panic!("expected ProviderDisabled, got {:?}", e),
}
}
#[cfg(not(feature = "aliyun"))]
#[test]
fn aliyun_disabled_without_aliyun_feature() {
let cfg = ProviderConfig::new(
Provider::Aliyun,
"k",
"https://dashscope.aliyuncs.com/api/v1",
"qwen-image-plus",
);
match create(&cfg) {
Err(Error::ProviderDisabled(s)) => assert_eq!(s, "aliyun"),
Ok(_) => panic!("expected error"),
Err(e) => panic!("expected ProviderDisabled, got {:?}", e),
}
}
}