Skip to content

Add multiple reference image support to image edits - #1192

Open
adityasingh2400 wants to merge 2 commits into
openai:mainfrom
adityasingh2400:images-multi-edit
Open

Add multiple reference image support to image edits#1192
adityasingh2400 wants to merge 2 commits into
openai:mainfrom
adityasingh2400:images-multi-edit

Conversation

@adityasingh2400

Copy link
Copy Markdown
Contributor

Summary

The image edit endpoint accepts an array of reference images for the GPT image models, sent as repeated image[] multipart fields. The TypeSpec model already describes this with image: HttpPart<bytes | bytes[]>, and the platform documentation shows the multi-image curl form. However ImageClient only exposed single-image edit overloads, so there was no supported way to pass more than one reference image from .NET. This is the gap reported in #432.

This change adds convenience overloads on ImageClient that accept multiple reference images:

  • GenerateImageEdits(IEnumerable<Stream> images, IEnumerable<string> imageFilenames, string prompt, ImageEditOptions options = null, CancellationToken cancellationToken = default)
  • GenerateImageEdits(IEnumerable<string> imageFilePaths, string prompt, ImageEditOptions options = null, CancellationToken cancellationToken = default)
  • the matching ...Async variants

Root cause and fix

The hand-written ImageEditOptions.ToMultipartContent always wrote a single part named image. I added a sibling overload that writes one part per image under the image[] field name, which is what the service expects when several images are provided. The existing single-image path is unchanged and still uses the image field, so dall-e-2 and current callers are unaffected. The shared option fields (prompt, model, size, quality, and so on) were factored into a small private helper that both overloads call, to avoid duplicating that block.

The new client overloads validate that the image and filename collections are non-null, non-empty, and the same length before sending, and the file-path overload opens and disposes the file streams deterministically.

All of the changes are in hand-written customization code under OpenAI/src/Custom/Images. No generated files were modified. The public API listings under api/ were updated to include the new overloads.

Verification

  • Built OpenAI/src/OpenAI.csproj across netstandard2.0, net8.0, and net10.0 with no warnings or errors.
  • Added tests to ImagesMockTests and ran them: a serialization test that captures the outgoing multipart body and asserts both reference images are emitted under image[], deserialization tests for the stream and file-path overloads, an argument-validation test, and a cancellation-token test. All pass.
  • Ran dotnet format on the changed files with no further changes needed.

Fixes #432

Comment thread api/OpenAI.net10.0.cs Outdated
public virtual ClientResult<GeneratedImageCollection> GenerateImageEdits(string imageFilePath, string prompt, string maskFilePath, int imageCount, ImageEditOptions options = null);
public virtual Task<ClientResult> GenerateImageEditsAsync(BinaryContent content, string contentType, RequestOptions options = null);
public virtual Task<ClientResult<GeneratedImageCollection>> GenerateImageEditsAsync(IEnumerable<Stream> images, IEnumerable<string> imageFilenames, string prompt, ImageEditOptions options = null, CancellationToken cancellationToken = default);
public virtual Task<ClientResult<GeneratedImageCollection>> GenerateImageEditsAsync(IEnumerable<string> imageFilePaths, string prompt, ImageEditOptions options = null, CancellationToken cancellationToken = default);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since ImageClient is already stable, please add the [Experimental("OPENAI001")] attribute to the four new methods.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. All four new methods now carry [Experimental("OPENAI001")], and the regenerated API listings show the attribute on each of them.

Comment thread OpenAI/src/Custom/Images/ImageClient.cs Outdated
}

imageFilenameList = filenameList;
return imageList;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this method needs to return anything nor have out parameters.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, that was doing too much. ValidateImages is now void and takes the already-materialized lists, so it only validates. The callers do the null checks and the ToList() themselves, which also makes it obvious that the collections are enumerated exactly once. I dropped the out parameter from OpenImageFiles the same way, since the caller can materialize the path list before handing it over.

The image edit endpoint accepts an array of reference images for the GPT image
models, sent as repeated image[] multipart fields, but the .NET client only
exposed single-image overloads. This adds GenerateImageEdits and
GenerateImageEditsAsync overloads that take a collection of image streams plus
filenames, and a collection of image file paths, so callers can pass up to 16
reference images in a single request.

The new overloads validate that the image and filename collections are non-empty
and have matching lengths, then serialize each image under the image[] field name
expected by the service. The single-image overloads keep using the image field
name so dall-e-2 and existing callers are unaffected.

Fixes openai#432
@adityasingh2400

Copy link
Copy Markdown
Contributor Author

Rebased onto main and addressed both review points.

The rebase needed real work because the API listings were split by target framework and namespace in #1237 and #1268, so my edits to the old api/OpenAI.net*.cs files no longer applied. I dropped those and regenerated with scripts/Export-Api.ps1. The regeneration touches only the three OpenAI.Images.* listings and adds exactly the four new overloads, each with the [Experimental("OPENAI001")] attribute.

Verified locally: the library builds clean with 0 warnings and 0 errors across netstandard2.0, net8.0, and net10.0, and the Images tests pass 64 of 64.

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

Successfully merging this pull request may close these issues.

There doesn't seem to be a way to pass multiple reference images to a single generation

2 participants