Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand All @@ -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);
});
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
Expand Down Expand Up @@ -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()) {
Expand All @@ -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) {
Expand Down Expand Up @@ -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();
}
Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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`. | -
Expand All @@ -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 <<image-options>> to the `ImagePrompt` call.
Expand Down Expand Up @@ -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
Expand Down