Skip to content

Commit 17ba1fc

Browse files
ThomasVitaletzolov
authored andcommitted
Streamline ImageOptions
* Add style to option abstraction * Use abstraction in Observations directly instead of dedicated implementation * Clean-up the merge of runtime and default image options in OpenAI and Stability AI Related to #1148 Signed-off-by: Thomas Vitale <ThomasVitale@users.noreply.github.com>
1 parent 80007d4 commit 17ba1fc

File tree

17 files changed

+193
-381
lines changed

17 files changed

+193
-381
lines changed

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

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
* The configuration information for a image generation request.
1212
*
1313
* @author Benoit Moussaud
14+
* @author Thomas Vitale
1415
* @since 1.0.0 M1
1516
*/
1617
@JsonInclude(JsonInclude.Include.NON_NULL)
@@ -88,10 +89,15 @@ public class AzureOpenAiImageOptions implements ImageOptions {
8889
@JsonProperty("user")
8990
private String user;
9091

92+
@Override
9193
public Integer getN() {
9294
return n;
9395
}
9496

97+
public void setN(Integer n) {
98+
this.n = n;
99+
}
100+
95101
@Override
96102
public String getModel() {
97103
return model;
@@ -101,10 +107,7 @@ public void setModel(String model) {
101107
this.model = model;
102108
}
103109

104-
public void setN(Integer n) {
105-
this.n = n;
106-
}
107-
110+
@Override
108111
public Integer getWidth() {
109112
return width;
110113
}
@@ -114,6 +117,7 @@ public void setWidth(Integer width) {
114117
this.size = this.width + "x" + this.height;
115118
}
116119

120+
@Override
117121
public Integer getHeight() {
118122
return height;
119123
}
@@ -123,6 +127,7 @@ public void setHeight(Integer height) {
123127
this.size = this.width + "x" + this.height;
124128
}
125129

130+
@Override
126131
public String getResponseFormat() {
127132
return responseFormat;
128133
}
@@ -158,6 +163,7 @@ public void setQuality(String quality) {
158163
this.quality = quality;
159164
}
160165

166+
@Override
161167
public String getStyle() {
162168
return style;
163169
}

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

Lines changed: 24 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@
2929
import org.springframework.ai.image.observation.ImageModelObservationConvention;
3030
import org.springframework.ai.image.observation.ImageModelObservationContext;
3131
import org.springframework.ai.image.observation.ImageModelObservationDocumentation;
32-
import org.springframework.ai.image.observation.ImageModelRequestOptions;
3332
import org.springframework.ai.model.ModelOptionsUtils;
3433
import org.springframework.ai.observation.AiOperationMetadata;
3534
import org.springframework.ai.observation.conventions.AiOperationType;
@@ -40,7 +39,6 @@
4039
import org.springframework.http.ResponseEntity;
4140
import org.springframework.retry.support.RetryTemplate;
4241
import org.springframework.util.Assert;
43-
import org.springframework.util.StringUtils;
4442

4543
import java.util.List;
4644

@@ -128,12 +126,13 @@ public OpenAiImageModel(OpenAiImageApi openAiImageApi, OpenAiImageOptions option
128126

129127
@Override
130128
public ImageResponse call(ImagePrompt imagePrompt) {
131-
OpenAiImageApi.OpenAiImageRequest imageRequest = createRequest(imagePrompt);
129+
OpenAiImageOptions requestImageOptions = mergeOptions(imagePrompt.getOptions(), this.defaultOptions);
130+
OpenAiImageApi.OpenAiImageRequest imageRequest = createRequest(imagePrompt, requestImageOptions);
132131

133132
var observationContext = ImageModelObservationContext.builder()
134133
.imagePrompt(imagePrompt)
135134
.operationMetadata(buildOperationMetadata())
136-
.requestOptions(buildRequestOptions(imageRequest))
135+
.requestOptions(requestImageOptions)
137136
.build();
138137

139138
return ImageModelObservationDocumentation.IMAGE_MODEL_OPERATION
@@ -151,23 +150,14 @@ public ImageResponse call(ImagePrompt imagePrompt) {
151150
});
152151
}
153152

154-
private OpenAiImageApi.OpenAiImageRequest createRequest(ImagePrompt imagePrompt) {
153+
private OpenAiImageApi.OpenAiImageRequest createRequest(ImagePrompt imagePrompt,
154+
OpenAiImageOptions requestImageOptions) {
155155
String instructions = imagePrompt.getInstructions().get(0).getText();
156156

157157
OpenAiImageApi.OpenAiImageRequest imageRequest = new OpenAiImageApi.OpenAiImageRequest(instructions,
158158
OpenAiImageApi.DEFAULT_IMAGE_MODEL);
159159

160-
if (this.defaultOptions != null) {
161-
imageRequest = ModelOptionsUtils.merge(this.defaultOptions, imageRequest,
162-
OpenAiImageApi.OpenAiImageRequest.class);
163-
}
164-
165-
if (imagePrompt.getOptions() != null) {
166-
imageRequest = ModelOptionsUtils.merge(toOpenAiImageOptions(imagePrompt.getOptions()), imageRequest,
167-
OpenAiImageApi.OpenAiImageRequest.class);
168-
}
169-
170-
return imageRequest;
160+
return ModelOptionsUtils.merge(requestImageOptions, imageRequest, OpenAiImageApi.OpenAiImageRequest.class);
171161
}
172162

173163
private ImageResponse convertResponse(ResponseEntity<OpenAiImageApi.OpenAiImageResponse> imageResponseEntity,
@@ -188,44 +178,27 @@ private ImageResponse convertResponse(ResponseEntity<OpenAiImageApi.OpenAiImageR
188178
}
189179

190180
/**
191-
* Convert the {@link ImageOptions} into {@link OpenAiImageOptions}.
192-
* @param runtimeImageOptions the image options to use.
193-
* @return the converted {@link OpenAiImageOptions}.
181+
* Merge runtime and default {@link ImageOptions} to compute the final options to use
182+
* in the request.
194183
*/
195-
private OpenAiImageOptions toOpenAiImageOptions(ImageOptions runtimeImageOptions) {
196-
OpenAiImageOptions.Builder openAiImageOptionsBuilder = OpenAiImageOptions.builder();
197-
if (runtimeImageOptions != null) {
184+
private OpenAiImageOptions mergeOptions(ImageOptions runtimeOptions, OpenAiImageOptions defaultOptions) {
185+
if (runtimeOptions == null) {
186+
return defaultOptions;
187+
}
188+
189+
return OpenAiImageOptions.builder()
198190
// Handle portable image options
199-
if (runtimeImageOptions.getN() != null) {
200-
openAiImageOptionsBuilder.withN(runtimeImageOptions.getN());
201-
}
202-
if (runtimeImageOptions.getModel() != null) {
203-
openAiImageOptionsBuilder.withModel(runtimeImageOptions.getModel());
204-
}
205-
if (runtimeImageOptions.getResponseFormat() != null) {
206-
openAiImageOptionsBuilder.withResponseFormat(runtimeImageOptions.getResponseFormat());
207-
}
208-
if (runtimeImageOptions.getWidth() != null) {
209-
openAiImageOptionsBuilder.withWidth(runtimeImageOptions.getWidth());
210-
}
211-
if (runtimeImageOptions.getHeight() != null) {
212-
openAiImageOptionsBuilder.withHeight(runtimeImageOptions.getHeight());
213-
}
191+
.withModel(ModelOptionsUtils.mergeOption(runtimeOptions.getModel(), defaultOptions.getModel()))
192+
.withN(ModelOptionsUtils.mergeOption(runtimeOptions.getN(), defaultOptions.getN()))
193+
.withResponseFormat(ModelOptionsUtils.mergeOption(runtimeOptions.getResponseFormat(),
194+
defaultOptions.getResponseFormat()))
195+
.withWidth(ModelOptionsUtils.mergeOption(runtimeOptions.getWidth(), defaultOptions.getWidth()))
196+
.withHeight(ModelOptionsUtils.mergeOption(runtimeOptions.getHeight(), defaultOptions.getHeight()))
197+
.withStyle(ModelOptionsUtils.mergeOption(runtimeOptions.getStyle(), defaultOptions.getStyle()))
214198
// Handle OpenAI specific image options
215-
if (runtimeImageOptions instanceof OpenAiImageOptions) {
216-
OpenAiImageOptions runtimeOpenAiImageOptions = (OpenAiImageOptions) runtimeImageOptions;
217-
if (runtimeOpenAiImageOptions.getQuality() != null) {
218-
openAiImageOptionsBuilder.withQuality(runtimeOpenAiImageOptions.getQuality());
219-
}
220-
if (runtimeOpenAiImageOptions.getStyle() != null) {
221-
openAiImageOptionsBuilder.withStyle(runtimeOpenAiImageOptions.getStyle());
222-
}
223-
if (runtimeOpenAiImageOptions.getUser() != null) {
224-
openAiImageOptionsBuilder.withUser(runtimeOpenAiImageOptions.getUser());
225-
}
226-
}
227-
}
228-
return openAiImageOptionsBuilder.build();
199+
.withQuality(defaultOptions.getQuality())
200+
.withUser(defaultOptions.getUser())
201+
.build();
229202
}
230203

231204
private AiOperationMetadata buildOperationMetadata() {
@@ -235,17 +208,6 @@ private AiOperationMetadata buildOperationMetadata() {
235208
.build();
236209
}
237210

238-
private ImageModelRequestOptions buildRequestOptions(OpenAiImageApi.OpenAiImageRequest request) {
239-
return ImageModelRequestOptions.builder()
240-
.model(StringUtils.hasText(request.model()) ? request.model() : "unknown")
241-
.n(request.n())
242-
.width(request.size() != null ? Integer.parseInt(request.size().split("x")[0]) : null)
243-
.height(request.size() != null ? Integer.parseInt(request.size().split("x")[1]) : null)
244-
.responseFormat(request.responseFormat())
245-
.style(request.style())
246-
.build();
247-
}
248-
249211
/**
250212
* Use the provided convention for reporting observation data
251213
* @param observationConvention The provided convention

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,7 @@ public void setHeight(Integer height) {
208208
this.size = this.width + "x" + this.height;
209209
}
210210

211+
@Override
211212
public String getStyle() {
212213
return this.style;
213214
}

models/spring-ai-qianfan/src/main/java/org/springframework/ai/qianfan/QianFanImageOptions.java

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -164,16 +164,17 @@ public Integer getHeight() {
164164
return this.height;
165165
}
166166

167-
@Override
168-
public String getResponseFormat() {
169-
return null;
170-
}
171-
172167
public void setHeight(Integer height) {
173168
this.height = height;
174169
this.size = this.width + "x" + this.height;
175170
}
176171

172+
@Override
173+
public String getResponseFormat() {
174+
return null;
175+
}
176+
177+
@Override
177178
public String getStyle() {
178179
return this.style;
179180
}
@@ -190,18 +191,17 @@ public void setUser(String user) {
190191
this.user = user;
191192
}
192193

193-
public void setSize(String size) {
194-
this.size = size;
195-
}
196-
197194
public String getSize() {
198-
199195
if (this.size != null) {
200196
return this.size;
201197
}
202198
return (this.width != null && this.height != null) ? this.width + "x" + this.height : null;
203199
}
204200

201+
public void setSize(String size) {
202+
this.size = size;
203+
}
204+
205205
@Override
206206
public boolean equals(Object o) {
207207
if (this == o)

0 commit comments

Comments
 (0)