From 6e7d513a23daf9e0a776edf7dc898b693a0832c7 Mon Sep 17 00:00:00 2001 From: Igor Bari Date: Mon, 15 Dec 2025 10:39:41 +0100 Subject: [PATCH] Add outputFormat and outputCompression to OpenAiSdkImageOptions Add support for outputFormat and outputCompression parameters in image generation using the OpenAI SDK. These options allow users to specify the output format (png, jpeg, webp) and compression level for generated images, which is supported by the gpt-image-1 model. Changes: * Add outputFormat and outputCompression fields to OpenAiSdkImageOptions * Add getters, setters, and builder methods for the new fields * Update equals, hashCode, and toString methods * Update toOpenAiImageGenerateParams to include the new parameters * Update Builder.from() and Builder.merge() to handle new fields * Add test coverage for the new properties * Update documentation with new properties and gpt-image-1 example Signed-off-by: Igor Bari --- .../OpenAiSdkImagePropertiesTests.java | 6 +- .../ai/openaisdk/OpenAiSdkImageOptions.java | 60 ++++++++++++++++++- .../pages/api/image/openai-sdk-image.adoc | 30 +++++++++- 3 files changed, 91 insertions(+), 5 deletions(-) diff --git a/auto-configurations/models/spring-ai-autoconfigure-model-openai-sdk/src/test/java/org/springframework/ai/model/openaisdk/autoconfigure/OpenAiSdkImagePropertiesTests.java b/auto-configurations/models/spring-ai-autoconfigure-model-openai-sdk/src/test/java/org/springframework/ai/model/openaisdk/autoconfigure/OpenAiSdkImagePropertiesTests.java index 60ddf53fe32..9f6637fbf4b 100644 --- a/auto-configurations/models/spring-ai-autoconfigure-model-openai-sdk/src/test/java/org/springframework/ai/model/openaisdk/autoconfigure/OpenAiSdkImagePropertiesTests.java +++ b/auto-configurations/models/spring-ai-autoconfigure-model-openai-sdk/src/test/java/org/springframework/ai/model/openaisdk/autoconfigure/OpenAiSdkImagePropertiesTests.java @@ -102,7 +102,9 @@ public void imageOptionsTest() { "spring.ai.openai-sdk.image.options.responseFormat=url", "spring.ai.openai-sdk.image.options.size=1024x1792", "spring.ai.openai-sdk.image.options.style=vivid", - "spring.ai.openai-sdk.image.options.user=userXYZ" + "spring.ai.openai-sdk.image.options.user=userXYZ", + "spring.ai.openai-sdk.image.options.outputFormat=jpeg", + "spring.ai.openai-sdk.image.options.outputCompression=75" ) // @formatter:on .withConfiguration(SpringAiTestAutoConfigurations.of(OpenAiSdkImageAutoConfiguration.class)) @@ -122,6 +124,8 @@ public void imageOptionsTest() { assertThat(imageProperties.getOptions().getSize()).isEqualTo("1024x1792"); assertThat(imageProperties.getOptions().getStyle()).isEqualTo("vivid"); assertThat(imageProperties.getOptions().getUser()).isEqualTo("userXYZ"); + assertThat(imageProperties.getOptions().getOutputFormat()).isEqualTo("jpeg"); + assertThat(imageProperties.getOptions().getOutputCompression()).isEqualTo(75); }); } diff --git a/models/spring-ai-openai-sdk/src/main/java/org/springframework/ai/openaisdk/OpenAiSdkImageOptions.java b/models/spring-ai-openai-sdk/src/main/java/org/springframework/ai/openaisdk/OpenAiSdkImageOptions.java index 711c1e2e9a5..062a071cdf8 100644 --- a/models/spring-ai-openai-sdk/src/main/java/org/springframework/ai/openaisdk/OpenAiSdkImageOptions.java +++ b/models/spring-ai-openai-sdk/src/main/java/org/springframework/ai/openaisdk/OpenAiSdkImageOptions.java @@ -82,6 +82,17 @@ public class OpenAiSdkImageOptions extends AbstractOpenAiSdkOptions implements I */ private String user; + /** + * The output format of the generated images. Must be one of png, jpeg, or webp. + */ + private String outputFormat; + + /** + * The compression level (0-100) for lossy formats like jpeg and webp. Only applies + * when outputFormat is jpeg or webp. + */ + private Integer outputCompression; + public static Builder builder() { return new Builder(); } @@ -160,6 +171,22 @@ public void setStyle(String style) { this.style = style; } + public String getOutputFormat() { + return this.outputFormat; + } + + public void setOutputFormat(String outputFormat) { + this.outputFormat = outputFormat; + } + + public Integer getOutputCompression() { + return this.outputCompression; + } + + public void setOutputCompression(Integer outputCompression) { + this.outputCompression = outputCompression; + } + @Override public boolean equals(Object o) { if (o == null || getClass() != o.getClass()) { @@ -169,20 +196,23 @@ public boolean equals(Object o) { return Objects.equals(this.n, that.n) && Objects.equals(this.width, that.width) && Objects.equals(this.height, that.height) && Objects.equals(this.quality, that.quality) && Objects.equals(this.responseFormat, that.responseFormat) && Objects.equals(this.size, that.size) - && Objects.equals(this.style, that.style) && Objects.equals(this.user, that.user); + && Objects.equals(this.style, that.style) && Objects.equals(this.user, that.user) + && Objects.equals(this.outputFormat, that.outputFormat) + && Objects.equals(this.outputCompression, that.outputCompression); } @Override public int hashCode() { return Objects.hash(this.n, this.width, this.height, this.quality, this.responseFormat, this.size, this.style, - this.user); + this.user, this.outputFormat, this.outputCompression); } @Override public String toString() { return "OpenAiSdkImageOptions{" + "n=" + this.n + ", width=" + this.width + ", height=" + this.height + ", quality='" + this.quality + '\'' + ", responseFormat='" + this.responseFormat + '\'' + ", size='" - + this.size + '\'' + ", style='" + this.style + '\'' + ", user='" + this.user + '\'' + '}'; + + this.size + '\'' + ", style='" + this.style + '\'' + ", user='" + this.user + '\'' + + ", outputFormat='" + this.outputFormat + '\'' + ", outputCompression=" + this.outputCompression + '}'; } public ImageGenerateParams toOpenAiImageGenerateParams(ImagePrompt imagePrompt) { @@ -220,6 +250,12 @@ else if (this.getModel() != null) { if (this.getUser() != null) { builder.user(this.getUser()); } + if (this.getOutputFormat() != null) { + builder.outputFormat(ImageGenerateParams.OutputFormat.of(this.getOutputFormat().toLowerCase())); + } + if (this.getOutputCompression() != null) { + builder.outputCompression(this.getOutputCompression().longValue()); + } return builder.build(); } @@ -256,6 +292,8 @@ public Builder from(OpenAiSdkImageOptions fromOptions) { this.options.setSize(fromOptions.getSize()); this.options.setStyle(fromOptions.getStyle()); this.options.setUser(fromOptions.getUser()); + this.options.setOutputFormat(fromOptions.getOutputFormat()); + this.options.setOutputCompression(fromOptions.getOutputCompression()); return this; } @@ -322,6 +360,12 @@ public Builder merge(ImageOptions from) { if (castFrom.getUser() != null) { this.options.setUser(castFrom.getUser()); } + if (castFrom.getOutputFormat() != null) { + this.options.setOutputFormat(castFrom.getOutputFormat()); + } + if (castFrom.getOutputCompression() != null) { + this.options.setOutputCompression(castFrom.getOutputCompression()); + } } return this; } @@ -421,6 +465,16 @@ public Builder style(String style) { return this; } + public Builder outputFormat(String outputFormat) { + this.options.setOutputFormat(outputFormat); + return this; + } + + public Builder outputCompression(Integer outputCompression) { + this.options.setOutputCompression(outputCompression); + return this; + } + public OpenAiSdkImageOptions build() { return this.options; } diff --git a/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/image/openai-sdk-image.adoc b/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/image/openai-sdk-image.adoc index ad7409d651b..1c5908b595a 100644 --- a/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/image/openai-sdk-image.adoc +++ b/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/image/openai-sdk-image.adoc @@ -191,7 +191,7 @@ The prefix `spring.ai.openai-sdk.image` is the property prefix for configuring t |==== | Property | Description | Default -| spring.ai.openai-sdk.image.options.model | The model to use for image generation. Available models: `dall-e-2`, `dall-e-3`. See the https://platform.openai.com/docs/models[models] page for more information. | `dall-e-3` +| spring.ai.openai-sdk.image.options.model | The model to use for image generation. Available models: `dall-e-2`, `dall-e-3`, `gpt-image-1`. See the https://platform.openai.com/docs/models[models] page for more information. | `dall-e-3` | spring.ai.openai-sdk.image.options.n | The number of images to generate. Must be between 1 and 10. For `dall-e-3`, only n=1 is supported. | - | spring.ai.openai-sdk.image.options.quality | The quality of the image that will be generated. `hd` creates images with finer details and greater consistency across the image. This parameter is only supported for `dall-e-3`. Available values: `standard`, `hd`. | - | spring.ai.openai-sdk.image.options.response-format | The format in which the generated images are returned. Must be one of `url` or `b64_json`. | - @@ -200,6 +200,8 @@ The prefix `spring.ai.openai-sdk.image` is the property prefix for configuring t | spring.ai.openai-sdk.image.options.height | The height of the generated images. Must be one of 256, 512, or 1024 for `dall-e-2`. | - | spring.ai.openai-sdk.image.options.style | The style of the generated images. Must be one of `vivid` or `natural`. Vivid causes the model to lean towards generating hyper-real and dramatic images. Natural causes the model to produce more natural, less hyper-real looking images. This parameter is only supported for `dall-e-3`. | - | spring.ai.openai-sdk.image.options.user | A unique identifier representing your end-user, which can help OpenAI to monitor and detect abuse. | - +| spring.ai.openai-sdk.image.options.output-format | The output format of the generated images. Must be one of `png`, `jpeg`, or `webp`. This parameter is supported for `gpt-image-1`. | - +| spring.ai.openai-sdk.image.options.output-compression | The compression level (0-100) for lossy formats like `jpeg` and `webp`. Lower values mean higher compression. This parameter is supported for `gpt-image-1`. | - |==== TIP: All properties prefixed with `spring.ai.openai-sdk.image.options` can be overridden at runtime by adding request-specific <> to the `ImagePrompt` call. @@ -229,6 +231,32 @@ ImageResponse response = imageModel.call( .build())); ---- +=== Using GPT Image 1 with Output Format Options + +The `gpt-image-1` model supports additional options for controlling the output format and compression level. This is useful when you need to generate images in specific formats like JPEG with controlled file sizes: + +[source,java] +---- +ImageResponse response = imageModel.call( + new ImagePrompt("A photorealistic landscape of mountains at sunset", + OpenAiSdkImageOptions.builder() + .model("gpt-image-1") + .N(1) + .width(1024) + .height(1024) + .outputFormat("jpeg") + .outputCompression(75) + .build())); +---- + +The `outputFormat` option accepts: + +* `png` - Lossless format, larger file size (default) +* `jpeg` - Lossy format, smaller file size with configurable compression +* `webp` - Modern format with good compression and quality balance + +The `outputCompression` option (0-100) controls the compression level for lossy formats (`jpeg` and `webp`). Lower values result in higher compression (smaller files, lower quality), while higher values preserve more quality (larger files). + TIP: In addition to the model specific https://github.com/spring-projects/spring-ai/blob/main/models/spring-ai-openai-sdk/src/main/java/org/springframework/ai/openaisdk/OpenAiSdkImageOptions.java[OpenAiSdkImageOptions] you can use a portable link:https://github.com/spring-projects/spring-ai/blob/main/spring-ai-model/src/main/java/org/springframework/ai/image/ImageOptions.java[ImageOptions] instance, created with the link:https://github.com/spring-projects/spring-ai/blob/main/spring-ai-model/src/main/java/org/springframework/ai/image/ImageOptionsBuilder.java[ImageOptionsBuilder#builder()]. == Sample Controller