1
1
/*
2
- * Copyright 2023-2024 the original author or authors.
2
+ * Copyright 2023-2025 the original author or authors.
3
3
*
4
4
* Licensed under the Apache License, Version 2.0 (the "License");
5
5
* you may not use this file except in compliance with the License.
39
39
import org .springframework .ai .openai .metadata .OpenAiImageGenerationMetadata ;
40
40
import org .springframework .ai .retry .RetryUtils ;
41
41
import org .springframework .http .ResponseEntity ;
42
- import org .springframework .lang .Nullable ;
43
42
import org .springframework .retry .support .RetryTemplate ;
44
43
import org .springframework .util .Assert ;
45
44
@@ -127,13 +126,16 @@ public OpenAiImageModel(OpenAiImageApi openAiImageApi, OpenAiImageOptions option
127
126
128
127
@ Override
129
128
public ImageResponse call (ImagePrompt imagePrompt ) {
130
- OpenAiImageOptions requestImageOptions = mergeOptions (imagePrompt .getOptions (), this .defaultOptions );
131
- OpenAiImageApi .OpenAiImageRequest imageRequest = createRequest (imagePrompt , requestImageOptions );
129
+ // Before moving any further, build the final request ImagePrompt,
130
+ // merging runtime and default options.
131
+ ImagePrompt requestImagePrompt = buildRequestImagePrompt (imagePrompt );
132
+
133
+ OpenAiImageApi .OpenAiImageRequest imageRequest = createRequest (requestImagePrompt );
132
134
133
135
var observationContext = ImageModelObservationContext .builder ()
134
136
.imagePrompt (imagePrompt )
135
137
.provider (OpenAiApiConstants .PROVIDER_NAME )
136
- .requestOptions (requestImageOptions )
138
+ .requestOptions (requestImagePrompt . getOptions () )
137
139
.build ();
138
140
139
141
return ImageModelObservationDocumentation .IMAGE_MODEL_OPERATION
@@ -151,14 +153,14 @@ public ImageResponse call(ImagePrompt imagePrompt) {
151
153
});
152
154
}
153
155
154
- private OpenAiImageApi .OpenAiImageRequest createRequest (ImagePrompt imagePrompt ,
155
- OpenAiImageOptions requestImageOptions ) {
156
+ private OpenAiImageApi .OpenAiImageRequest createRequest (ImagePrompt imagePrompt ) {
156
157
String instructions = imagePrompt .getInstructions ().get (0 ).getText ();
158
+ OpenAiImageOptions imageOptions = (OpenAiImageOptions ) imagePrompt .getOptions ();
157
159
158
160
OpenAiImageApi .OpenAiImageRequest imageRequest = new OpenAiImageApi .OpenAiImageRequest (instructions ,
159
161
OpenAiImageApi .DEFAULT_IMAGE_MODEL );
160
162
161
- return ModelOptionsUtils .merge (requestImageOptions , imageRequest , OpenAiImageApi .OpenAiImageRequest .class );
163
+ return ModelOptionsUtils .merge (imageOptions , imageRequest , OpenAiImageApi .OpenAiImageRequest .class );
162
164
}
163
165
164
166
private ImageResponse convertResponse (ResponseEntity <OpenAiImageApi .OpenAiImageResponse > imageResponseEntity ,
@@ -179,31 +181,29 @@ private ImageResponse convertResponse(ResponseEntity<OpenAiImageApi.OpenAiImageR
179
181
return new ImageResponse (imageGenerationList , openAiImageResponseMetadata );
180
182
}
181
183
182
- /**
183
- * Merge runtime and default {@link ImageOptions} to compute the final options to use
184
- * in the request.
185
- */
186
- private OpenAiImageOptions mergeOptions (@ Nullable ImageOptions runtimeOptions , OpenAiImageOptions defaultOptions ) {
187
- var runtimeOptionsForProvider = ModelOptionsUtils .copyToTarget (runtimeOptions , ImageOptions .class ,
188
- OpenAiImageOptions .class );
189
-
190
- if (runtimeOptionsForProvider == null ) {
191
- return defaultOptions ;
184
+ private ImagePrompt buildRequestImagePrompt (ImagePrompt imagePrompt ) {
185
+ // Process runtime options
186
+ OpenAiImageOptions runtimeOptions = null ;
187
+ if (imagePrompt .getOptions () != null ) {
188
+ runtimeOptions = ModelOptionsUtils .copyToTarget (imagePrompt .getOptions (), ImageOptions .class ,
189
+ OpenAiImageOptions .class );
192
190
}
193
191
194
- return OpenAiImageOptions .builder ()
192
+ OpenAiImageOptions requestOptions = runtimeOptions == null ? this . defaultOptions : OpenAiImageOptions .builder ()
195
193
// Handle portable image options
196
- .model (ModelOptionsUtils .mergeOption (runtimeOptionsForProvider .getModel (), defaultOptions .getModel ()))
197
- .N (ModelOptionsUtils .mergeOption (runtimeOptionsForProvider .getN (), defaultOptions .getN ()))
198
- .responseFormat (ModelOptionsUtils .mergeOption (runtimeOptionsForProvider .getResponseFormat (),
194
+ .model (ModelOptionsUtils .mergeOption (runtimeOptions .getModel (), defaultOptions .getModel ()))
195
+ .N (ModelOptionsUtils .mergeOption (runtimeOptions .getN (), defaultOptions .getN ()))
196
+ .responseFormat (ModelOptionsUtils .mergeOption (runtimeOptions .getResponseFormat (),
199
197
defaultOptions .getResponseFormat ()))
200
- .width (ModelOptionsUtils .mergeOption (runtimeOptionsForProvider .getWidth (), defaultOptions .getWidth ()))
201
- .height (ModelOptionsUtils .mergeOption (runtimeOptionsForProvider .getHeight (), defaultOptions .getHeight ()))
202
- .style (ModelOptionsUtils .mergeOption (runtimeOptionsForProvider .getStyle (), defaultOptions .getStyle ()))
198
+ .width (ModelOptionsUtils .mergeOption (runtimeOptions .getWidth (), defaultOptions .getWidth ()))
199
+ .height (ModelOptionsUtils .mergeOption (runtimeOptions .getHeight (), defaultOptions .getHeight ()))
200
+ .style (ModelOptionsUtils .mergeOption (runtimeOptions .getStyle (), defaultOptions .getStyle ()))
203
201
// Handle OpenAI specific image options
204
- .quality (ModelOptionsUtils .mergeOption (runtimeOptionsForProvider .getQuality (), defaultOptions .getQuality ()))
205
- .user (ModelOptionsUtils .mergeOption (runtimeOptionsForProvider .getUser (), defaultOptions .getUser ()))
202
+ .quality (ModelOptionsUtils .mergeOption (runtimeOptions .getQuality (), defaultOptions .getQuality ()))
203
+ .user (ModelOptionsUtils .mergeOption (runtimeOptions .getUser (), defaultOptions .getUser ()))
206
204
.build ();
205
+
206
+ return new ImagePrompt (imagePrompt .getInstructions (), requestOptions );
207
207
}
208
208
209
209
/**
0 commit comments