Skip to content

CreateImageEditRequest forces an application/octet-stream content type for images #364

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
spantaleev opened this issue May 8, 2025 · 0 comments

Comments

@spantaleev
Copy link

I'm trying to use the Create image edit API with the gpt-image-1 model via client.images().create_edit() with a CreateImageEditRequest.

#363 is a blocker for multi-image use-cases, but even single images fail.

My code is something like this:

let image_input = ImageInput {
	source: InputSource::VecU8 {
		filename: "image.png".to_string(),
		vec: get_image_bytes(),
	},
};

let mut request_builder = CreateImageEditRequestArgs::default();

request_builder
	.image(image_input)
	.prompt("A surf propped up on the beach".to_owned())
	.size(DallE2ImageSize::S1024x1024)
	.model(ImageModel::Other("gpt-image-1".to_string()));


let request = request_builder.build()
	.map_err(|e| anyhow::anyhow!("Failed to build CreateImageEditRequest: {}", e))?;

let response = self.client.images().create_edit(request).await?;

This fails with:

invalid_request_error: Invalid file 'image': unsupported mimetype ('application/octet-stream'). Supported file formats are 'image/jpeg', 'image/png', and 'image/webp'. (param: image) (code: unsupported_file_mimetype)


The payload that's sent to /v1/images/edits looks like this:

Content-Disposition: form-data; name="image"; filename="image.png"
Content-Type: application/octet-stream
...

It seems like the relevant code is:

impl AsyncTryFrom<CreateImageEditRequest> for reqwest::multipart::Form {
type Error = OpenAIError;
async fn try_from(request: CreateImageEditRequest) -> Result<Self, Self::Error> {
let image_part = create_file_part(request.image.source).await?;
let mut form = reqwest::multipart::Form::new()
.part("image", image_part)
.text("prompt", request.prompt);

.. and:

/// Creates the part for the given file for multipart upload.
pub(crate) async fn create_file_part(
source: InputSource,
) -> Result<reqwest::multipart::Part, OpenAIError> {
let (stream, file_name) = match source {
InputSource::Path { path } => {
let file_name = path
.file_name()
.ok_or_else(|| {
OpenAIError::FileReadError(format!(
"cannot extract file name from {}",
path.display()
))
})?
.to_str()
.unwrap()
.to_string();
(
file_stream_body(InputSource::Path { path }).await?,
file_name,
)
}
InputSource::Bytes { filename, bytes } => (Body::from(bytes), filename),
InputSource::VecU8 { filename, vec } => (Body::from(vec), filename),
};
let file_part = reqwest::multipart::Part::stream(stream)
.file_name(file_name)
.mime_str("application/octet-stream")
.unwrap();
Ok(file_part)
}

It appears that create_file_part() hardcodes a mime type of application/octet-stream.

While this is probably OK for older models and some auto-detection may have been happening, the gpt-image-1 model appears to be picky.


I'm not sure what the best way to solve this is, but one possibility may be to add an additional field to each InputSource variant. Maybe something like:

 #[derive(Debug, Clone, PartialEq)]
 pub enum InputSource {
-   Path { path: PathBuf },
+   Path { path: PathBuf, mime_type: Option<String> },
-   Bytes { filename: String, bytes: Bytes },
+   Bytes { filename: String, bytes: Bytes, mime_type: Option<String> },
-   VecU8 { filename: String, vec: Vec<u8> },
+   VecU8 { filename: String, vec: Vec<u8>, mime_type: Option<String> },
 }

This could then be used in create_file_part().

spantaleev added a commit to etkecc/async-openai that referenced this issue May 10, 2025
spantaleev added a commit to etkecc/baibot that referenced this issue May 10, 2025
This is a huge patch which does some major refactoring like:

- renaming "Image Generation" to "Image Creation" in most places,
  to better match its new command (`!bai image create`)

- relocating image creation command (`!bai image` -> `!bai image create`),
  so it wouldn't conflict with the new image editing command (`!bai image edit`)

- introducing a new image editing command (`!bai image edit`), which
  is meant to work only with the OpenAI provider, but doesn't fully work yet
  due to 64bit/async-openai#364, though a next patch will fix it

- adding support for reading images off of Matrix conversations and forwarding them to
  text conversations. Works for OpenAI, but not for Anthropic yet
  (requires custom patches) and not for OpenAI-Compat (no support for
  images there)

- relocating some utils around (base64, mime)
spantaleev added a commit to etkecc/baibot that referenced this issue May 10, 2025
Related to:

- CreateImageEditRequest forces an application/octet-stream content type
  for images (64bit/async-openai#364)

- CreateImageEditRequest only deals with a single image
  (64bit/async-openai#363)

This is a continuation of 8f86289
and fixes the Image Editing feature for OpenAI.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant