Skip to content

Commit 95eb9fd

Browse files
dennysfredericcimarkpollack
authored andcommitted
Fix OpenAI image dimension handling
The OpenAiImageOptions class previously had inconsistent behavior when setting width and height properties. This change ensures that the size property is only computed when both dimensions are available, preventing invalid states in the API requests. Key changes: - Update setWidth/setHeight to only set size when both are non-null - Add test cases to verify null size when dimensions are incomplete - Improve code organization with proper import conventions This change aligns with OpenAI's API requirements for image generation where both dimensions must be specified for valid requests.
1 parent 0d122bb commit 95eb9fd

File tree

2 files changed

+72
-15
lines changed

2 files changed

+72
-15
lines changed

models/spring-ai-openai/src/main/java/org/springframework/ai/openai/OpenAiImageOptions.java

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,12 @@
1616

1717
package org.springframework.ai.openai;
1818

19-
import java.util.Objects;
20-
2119
import com.fasterxml.jackson.annotation.JsonInclude;
2220
import com.fasterxml.jackson.annotation.JsonProperty;
23-
2421
import org.springframework.ai.image.ImageOptions;
2522

23+
import java.util.Objects;
24+
2625
/**
2726
* OpenAI Image API options. OpenAiImageOptions.java
2827
*
@@ -48,12 +47,18 @@ public class OpenAiImageOptions implements ImageOptions {
4847

4948
/**
5049
* The width of the generated images. Must be one of 256, 512, or 1024 for dall-e-2.
50+
* This property is interconnected with the 'size' property - setting both width and height
51+
* will automatically compute and set the size in "widthxheight" format. Conversely,
52+
* setting a valid size string will parse and set the individual width and height values.
5153
*/
5254
@JsonProperty("size_width")
5355
private Integer width;
5456

5557
/**
5658
* The height of the generated images. Must be one of 256, 512, or 1024 for dall-e-2.
59+
* This property is interconnected with the 'size' property - setting both width and height
60+
* will automatically compute and set the size in "widthxheight" format. Conversely,
61+
* setting a valid size string will parse and set the individual width and height values.
5762
*/
5863
@JsonProperty("size_height")
5964
private Integer height;
@@ -76,6 +81,10 @@ public class OpenAiImageOptions implements ImageOptions {
7681
/**
7782
* The size of the generated images. Must be one of 256x256, 512x512, or 1024x1024 for
7883
* dall-e-2. Must be one of 1024x1024, 1792x1024, or 1024x1792 for dall-e-3 models.
84+
* This property is automatically computed when both width and height are set, following
85+
* the format "widthxheight". When setting this property directly, it must follow the
86+
* format "WxH" where W and H are valid integers. Invalid formats will result in null
87+
* width and height values.
7988
*/
8089
@JsonProperty("size")
8190
private String size;
@@ -142,9 +151,13 @@ public Integer getWidth() {
142151
}
143152
else if (this.size != null) {
144153
try {
145-
return Integer.parseInt(this.size.split("x")[0]);
154+
String[] dimensions = this.size.split("x");
155+
if (dimensions.length != 2) {
156+
return null;
157+
}
158+
return Integer.parseInt(dimensions[0]);
146159
}
147-
catch (NumberFormatException ex) {
160+
catch (Exception ex) {
148161
return null;
149162
}
150163
}
@@ -153,7 +166,9 @@ else if (this.size != null) {
153166

154167
public void setWidth(Integer width) {
155168
this.width = width;
156-
this.size = this.width + "x" + this.height;
169+
if (this.width != null && this.height != null) {
170+
this.size = this.width + "x" + this.height;
171+
}
157172
}
158173

159174
@Override
@@ -163,9 +178,13 @@ public Integer getHeight() {
163178
}
164179
else if (this.size != null) {
165180
try {
166-
return Integer.parseInt(this.size.split("x")[1]);
181+
String[] dimensions = this.size.split("x");
182+
if (dimensions.length != 2) {
183+
return null;
184+
}
185+
return Integer.parseInt(dimensions[1]);
167186
}
168-
catch (NumberFormatException ex) {
187+
catch (Exception ex) {
169188
return null;
170189
}
171190
}
@@ -174,7 +193,9 @@ else if (this.size != null) {
174193

175194
public void setHeight(Integer height) {
176195
this.height = height;
177-
this.size = this.width + "x" + this.height;
196+
if (this.width != null && this.height != null) {
197+
this.size = this.width + "x" + this.height;
198+
}
178199
}
179200

180201
@Override

models/spring-ai-openai/src/test/java/org/springframework/ai/openai/OpenAiImageOptionsTests.java

Lines changed: 42 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -58,20 +58,56 @@ void whenWidthAndHeightAreSet() {
5858
void whenWidthIsSet() {
5959
OpenAiImageOptions options = new OpenAiImageOptions();
6060
options.setWidth(1920);
61-
assertThat(options.getHeight()).isEqualTo(null);
61+
assertThat(options.getHeight()).isNull();
6262
assertThat(options.getWidth()).isEqualTo(1920);
63-
// This is because "setWidth()" computes "size" without checking for null values.
64-
assertThat(options.getSize()).isEqualTo("1920xnull");
63+
// 1920xnull is not a valid size, so "size" should be null.
64+
assertThat(options.getSize()).isNull();
6565
}
6666

6767
@Test
6868
void whenHeightIsSet() {
6969
OpenAiImageOptions options = new OpenAiImageOptions();
7070
options.setHeight(1080);
7171
assertThat(options.getHeight()).isEqualTo(1080);
72-
assertThat(options.getWidth()).isEqualTo(null);
73-
// This is because "setHeight()" computes "size" without checking for null values.
74-
assertThat(options.getSize()).isEqualTo("nullx1080");
72+
assertThat(options.getWidth()).isNull();
73+
// nullx1080 is not a valid size, so "size" should be null.
74+
assertThat(options.getSize()).isNull();
75+
}
76+
77+
@Test
78+
void whenInvalidSizeFormatIsSet() {
79+
OpenAiImageOptions options = new OpenAiImageOptions();
80+
options.setSize("invalid");
81+
assertThat(options.getHeight()).isNull();
82+
assertThat(options.getWidth()).isNull();
83+
assertThat(options.getSize()).isEqualTo("invalid");
84+
}
85+
86+
@Test
87+
void whenSizeWithInvalidNumbersIsSet() {
88+
OpenAiImageOptions options = new OpenAiImageOptions();
89+
options.setSize("axb");
90+
assertThat(options.getHeight()).isNull();
91+
assertThat(options.getWidth()).isNull();
92+
assertThat(options.getSize()).isEqualTo("axb");
93+
}
94+
95+
@Test
96+
void whenSizeWithMissingDimensionIsSet() {
97+
OpenAiImageOptions options = new OpenAiImageOptions();
98+
options.setSize("1024x");
99+
assertThat(options.getHeight()).isNull();
100+
assertThat(options.getWidth()).isNull();
101+
assertThat(options.getSize()).isEqualTo("1024x");
102+
}
103+
104+
@Test
105+
void whenSizeWithExtraSeparatorsIsSet() {
106+
OpenAiImageOptions options = new OpenAiImageOptions();
107+
options.setSize("1024x1024x1024");
108+
assertThat(options.getHeight()).isNull();
109+
assertThat(options.getWidth()).isNull();
110+
assertThat(options.getSize()).isEqualTo("1024x1024x1024");
75111
}
76112

77113
}

0 commit comments

Comments
 (0)