Fix sticker generation for newer GPT image models
Sticker generation was failing when using newer GPT image models (gpt-image-1, gpt-image-1-mini, gpt-image-1.5). The issue occurred because stickers requested 256x256 size, but these models only support 1024x1024, 1536x1024, 1024x1536, and auto. To reproduce, send `!bai sticker Something` to an agent configured with a GPT image model. The error was: invalid_request_error: Invalid value: '256x256'. Supported values are: '1024x1024', '1024x1536', '1536x1024', and 'auto'. (param: size) (code: invalid_value) The fix replaces the hardcoded 256x256 size override with a `smallest_size_possible` flag, letting each provider determine the appropriate sticker size based on the model being used. The `openai_compat` provider still defaults to requesting 256x256 in all cases (regardless of model name).
This commit is contained in:
@@ -2,7 +2,7 @@ use mxlink::mime;
|
|||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct ImageGenerationParams {
|
pub struct ImageGenerationParams {
|
||||||
pub size_override: Option<String>,
|
pub smallest_size_possible: bool,
|
||||||
|
|
||||||
pub cheaper_model_switching_allowed: bool,
|
pub cheaper_model_switching_allowed: bool,
|
||||||
|
|
||||||
@@ -10,8 +10,8 @@ pub struct ImageGenerationParams {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ImageGenerationParams {
|
impl ImageGenerationParams {
|
||||||
pub fn with_size_override(mut self, value: Option<String>) -> Self {
|
pub fn with_smallest_size_possible(mut self, value: bool) -> Self {
|
||||||
self.size_override = value;
|
self.smallest_size_possible = value;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -31,12 +31,9 @@ use crate::{
|
|||||||
use crate::{
|
use crate::{
|
||||||
agent::{
|
agent::{
|
||||||
AgentPurpose,
|
AgentPurpose,
|
||||||
provider::{
|
provider::entity::{
|
||||||
entity::{
|
ImageEditResult, ImageGenerationResult, ImageSource, PingResult,
|
||||||
ImageEditResult, ImageGenerationResult, ImageSource, PingResult,
|
TextToSpeechParams, TextToSpeechResult,
|
||||||
TextToSpeechParams, TextToSpeechResult,
|
|
||||||
},
|
|
||||||
openai::utils::convert_string_to_enum,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
strings,
|
strings,
|
||||||
@@ -312,10 +309,11 @@ impl ControllerTrait for Controller {
|
|||||||
image_generation_config.quality.clone()
|
image_generation_config.quality.clone()
|
||||||
};
|
};
|
||||||
|
|
||||||
let size = params
|
let size = if params.smallest_size_possible {
|
||||||
.size_override
|
Some(get_sticker_size(&model))
|
||||||
.map(|s| convert_string_to_enum::<async_openai::types::images::ImageSize>(&s).unwrap())
|
} else {
|
||||||
.or(image_generation_config.size);
|
image_generation_config.size
|
||||||
|
};
|
||||||
|
|
||||||
let response_format = match model.clone() {
|
let response_format = match model.clone() {
|
||||||
ImageModel::DallE2 => Some(ImageResponseFormat::B64Json),
|
ImageModel::DallE2 => Some(ImageResponseFormat::B64Json),
|
||||||
@@ -638,3 +636,17 @@ fn audio_mime_type_to_file_name(mime_type: &mxlink::mime::Mime) -> Option<String
|
|||||||
|
|
||||||
Some(format!("audio.{}", file_extension))
|
Some(format!("audio.{}", file_extension))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the smallest supported size for stickers based on what the image model supports.
|
||||||
|
fn get_sticker_size(model: &ImageModel) -> async_openai::types::images::ImageSize {
|
||||||
|
use async_openai::types::images::ImageSize;
|
||||||
|
|
||||||
|
match model {
|
||||||
|
ImageModel::DallE2 => ImageSize::S256x256,
|
||||||
|
ImageModel::DallE3 => ImageSize::S1024x1024,
|
||||||
|
ImageModel::GptImage1 => ImageSize::S1024x1024,
|
||||||
|
ImageModel::GptImage1Mini => ImageSize::S1024x1024,
|
||||||
|
ImageModel::GptImage1dot5 => ImageSize::S1024x1024,
|
||||||
|
ImageModel::Other(_) => ImageSize::S1024x1024,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
use async_openai::types::{
|
use async_openai::types::responses::{
|
||||||
responses::{
|
EasyInputContent, EasyInputMessage, ImageDetail, InputContent, InputImageContent, InputItem,
|
||||||
EasyInputContent, EasyInputMessage, ImageDetail, InputContent, InputImageContent,
|
InputParam, MessageType, Role,
|
||||||
InputItem, InputParam, MessageType, Role,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::conversation::llm::{
|
use crate::conversation::llm::{
|
||||||
@@ -48,19 +46,3 @@ pub fn convert_llm_messages_to_openai_response_input(
|
|||||||
|
|
||||||
InputParam::Items(items)
|
InputParam::Items(items)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn convert_string_to_enum<T>(value: &str) -> Result<T, String>
|
|
||||||
where
|
|
||||||
T: serde::de::DeserializeOwned,
|
|
||||||
{
|
|
||||||
// This is a hacky way to construct an enum from the string we have.
|
|
||||||
let enum_result: serde_json::Result<T> = serde_json::from_str(&format!("\"{}\"", value));
|
|
||||||
match enum_result {
|
|
||||||
Ok(enum_result) => Ok(enum_result),
|
|
||||||
Err(err) => {
|
|
||||||
tracing::debug!(?err, "Failed to parse into enum");
|
|
||||||
|
|
||||||
Err(format!("The value ({}) is not supported.", value))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ use etke_openai_api_rust::chat::{ChatApi, ChatBody};
|
|||||||
use etke_openai_api_rust::images::{ImagesApi, ImagesBody};
|
use etke_openai_api_rust::images::{ImagesApi, ImagesBody};
|
||||||
use etke_openai_api_rust::{Auth, Message, OpenAI};
|
use etke_openai_api_rust::{Auth, Message, OpenAI};
|
||||||
|
|
||||||
|
const SMALLEST_IMAGE_SIZE: &str = "256x256";
|
||||||
|
|
||||||
use super::super::ControllerTrait;
|
use super::super::ControllerTrait;
|
||||||
use crate::utils::base64::base64_decode;
|
use crate::utils::base64::base64_decode;
|
||||||
use crate::{
|
use crate::{
|
||||||
@@ -303,9 +305,11 @@ impl ControllerTrait for Controller {
|
|||||||
// when they span multiple lines.
|
// when they span multiple lines.
|
||||||
let prompt = prompt.replace("\n", " ");
|
let prompt = prompt.replace("\n", " ");
|
||||||
|
|
||||||
let size: Option<String> = params
|
let size: Option<String> = if params.smallest_size_possible {
|
||||||
.size_override
|
Some(SMALLEST_IMAGE_SIZE.to_owned())
|
||||||
.or_else(|| image_generation_config.size.clone());
|
} else {
|
||||||
|
image_generation_config.size.clone()
|
||||||
|
};
|
||||||
|
|
||||||
let request = ImagesBody {
|
let request = ImagesBody {
|
||||||
model: Some(image_generation_config.model_id.to_owned()),
|
model: Some(image_generation_config.model_id.to_owned()),
|
||||||
|
|||||||
@@ -12,9 +12,6 @@ use crate::strings;
|
|||||||
use crate::utils::mime::get_file_extension;
|
use crate::utils::mime::get_file_extension;
|
||||||
use crate::{Bot, entity::MessageContext};
|
use crate::{Bot, entity::MessageContext};
|
||||||
|
|
||||||
// We may make this configurable (per room, etc.) in the future, but for now it's hardcoded.
|
|
||||||
const STICKER_SIZE: &str = "256x256";
|
|
||||||
|
|
||||||
pub async fn handle_image(
|
pub async fn handle_image(
|
||||||
bot: &Bot,
|
bot: &Bot,
|
||||||
matrix_link: MatrixLink,
|
matrix_link: MatrixLink,
|
||||||
@@ -177,7 +174,7 @@ pub async fn handle_sticker(
|
|||||||
);
|
);
|
||||||
|
|
||||||
let params = ImageGenerationParams::default()
|
let params = ImageGenerationParams::default()
|
||||||
.with_size_override(Some(STICKER_SIZE.to_owned()))
|
.with_smallest_size_possible(true)
|
||||||
.with_cheaper_model_switching_allowed(true)
|
.with_cheaper_model_switching_allowed(true)
|
||||||
.with_cheaper_quality_switching_allowed(true);
|
.with_cheaper_quality_switching_allowed(true);
|
||||||
|
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ To create a sticker, send a command like `%command_prefix% sticker A huge bowl o
|
|||||||
|
|
||||||
The difference from **creating images** is that the bot will:
|
The difference from **creating images** is that the bot will:
|
||||||
|
|
||||||
- create a smaller-resolution image (`256x256`) - smaller/quicker, but still good enough for a sticker
|
- create a smaller-resolution image (as small as the model allows) - smaller/quicker, but still good enough for a sticker
|
||||||
- potentially switch to a different (cheaper or otherwise more suitable) model, if available
|
- potentially switch to a different (cheaper or otherwise more suitable) model, if available
|
||||||
- post the image directly to the room (as a reply to your message), without starting a threaded conversation
|
- post the image directly to the room (as a reply to your message), without starting a threaded conversation
|
||||||
"#;
|
"#;
|
||||||
|
|||||||
Reference in New Issue
Block a user