Skip to content

Commit be3976a

Browse files
author
kevin
committed
feat: add new parameters for image generation and multimodal conversation
1 parent 36ac21e commit be3976a

16 files changed

Lines changed: 292 additions & 20 deletions

File tree

.dev_tools/run_ci.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ mvn package
2626

2727
if [ $? -ne 0 ]; then
2828
echo "mvn package failed, please check if any unittest is failed!"
29-
exit -1
29+
exit 1
3030
fi
3131

32-
echo "CI passed."
32+
echo "CI passed."

README.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -173,4 +173,3 @@ public class Main {
173173
```
174174

175175
The `call` method accepts a `GenerationParam`, and returns a `GenerationResult`, you can also catch the exception with a try-catch block.
176-

lint.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
java -jar .dev_tools/google-java-format-1.7-all-deps.jar -i $(find . -type f -name "*.java" | grep "./*/src/.*java")
1+
java -jar .dev_tools/google-java-format-1.7-all-deps.jar -i $(find . -type f -name "*.java" | grep "./*/src/.*java")

src/main/java/com/alibaba/dashscope/aigc/completion/ChatCompletionParam.java

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,45 @@ public class ChatCompletionParam extends FlattenHalfDuplexParamBase {
8080
@SerializedName("parallel_tool_calls")
8181
private Boolean parallelToolCalls;
8282

83+
/**
84+
* Whether to preserve thinking/reasoning content in the response. When enabled, the model will
85+
* include reasoning process in the output.
86+
*/
87+
@SerializedName("preserve_thinking")
88+
private Boolean preserveThinking;
89+
90+
/**
91+
* Controls the reasoning effort level for models that support it. Possible values: "low",
92+
* "medium", "high"
93+
*/
94+
@SerializedName("reasoning_effort")
95+
private String reasoningEffort;
96+
97+
/**
98+
* The maximum number of tokens to generate for completion. This is an alternative to max_tokens
99+
* following OpenAI's newer API convention.
100+
*/
101+
@SerializedName("max_completion_tokens")
102+
private Integer maxCompletionTokens;
103+
104+
/**
105+
* Whether to stream tool calls as they are generated. When true, tool calls will be sent
106+
* incrementally rather than all at once.
107+
*/
108+
@SerializedName("tool_stream")
109+
private Boolean toolStream;
110+
111+
/**
112+
* Enable high resolution image processing for vision-language models. Improves image
113+
* understanding quality at the cost of more tokens.
114+
*/
115+
@SerializedName("vl_high_resolution_images")
116+
private Boolean vlHighResolutionImages;
117+
118+
/** Enable hardware-accelerated image output for vision-language models. */
119+
@SerializedName("vl_enable_image_hw_output")
120+
private Boolean vlEnableImageHwOutput;
121+
83122
private String user;
84123

85124
@Override
@@ -142,6 +181,24 @@ public JsonObject getHttpBody() {
142181
if (parallelToolCalls != null) {
143182
requestObject.addProperty("parallel_tool_calls", parallelToolCalls);
144183
}
184+
if (preserveThinking != null) {
185+
requestObject.addProperty("preserve_thinking", preserveThinking);
186+
}
187+
if (reasoningEffort != null) {
188+
requestObject.addProperty("reasoning_effort", reasoningEffort);
189+
}
190+
if (maxCompletionTokens != null) {
191+
requestObject.addProperty("max_completion_tokens", maxCompletionTokens);
192+
}
193+
if (toolStream != null) {
194+
requestObject.addProperty("tool_stream", toolStream);
195+
}
196+
if (vlHighResolutionImages != null) {
197+
requestObject.addProperty("vl_high_resolution_images", vlHighResolutionImages);
198+
}
199+
if (vlEnableImageHwOutput != null) {
200+
requestObject.addProperty("vl_enable_image_hw_output", vlEnableImageHwOutput);
201+
}
145202
if (user != null) {
146203
requestObject.addProperty("user", user);
147204
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package com.alibaba.dashscope.aigc.imagegeneration;
2+
3+
import lombok.AllArgsConstructor;
4+
import lombok.Builder;
5+
import lombok.Data;
6+
import lombok.NoArgsConstructor;
7+
8+
/** Color palette configuration for image generation. */
9+
@Data
10+
@Builder
11+
@NoArgsConstructor
12+
@AllArgsConstructor
13+
public class ColorPalette {
14+
15+
/** Hex color code, e.g., "#FF5733" */
16+
private String hex;
17+
18+
/** Ratio of the color in the palette, value range: [0.0, 1.0] */
19+
private Double ratio;
20+
}

src/main/java/com/alibaba/dashscope/aigc/imagegeneration/ImageGenerationParam.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,15 @@ public class ImageGenerationParam extends HalfDuplexServiceParam {
8383
*/
8484
@Builder.Default private List<List<List<Integer>>> bboxList = null;
8585

86+
/**
87+
* Thinking mode for image generation. Controls the level of reasoning and planning in the
88+
* generation process.
89+
*/
90+
private String thinkingMode;
91+
92+
/** Color palette configuration for controlling color distribution in generated images. */
93+
private List<ColorPalette> colorPalette;
94+
8695
@Override
8796
public JsonObject getHttpBody() {
8897
JsonObject requestObject = new JsonObject();
@@ -167,6 +176,14 @@ public Map<String, Object> getParameters() {
167176
params.put("bbox_list", bboxList);
168177
}
169178

179+
if (thinkingMode != null) {
180+
params.put("thinking_mode", thinkingMode);
181+
}
182+
183+
if (colorPalette != null) {
184+
params.put("color_palette", colorPalette);
185+
}
186+
170187
params.putAll(parameters);
171188
return params;
172189
}

src/main/java/com/alibaba/dashscope/aigc/imagesynthesis/ImageSynthesisParam.java

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import com.alibaba.dashscope.utils.JsonUtils;
1313
import com.alibaba.dashscope.utils.PreprocessInputImage;
1414
import com.google.gson.JsonObject;
15+
import com.google.gson.annotations.SerializedName;
1516
import java.nio.ByteBuffer;
1617
import java.util.ArrayList;
1718
import java.util.HashMap;
@@ -58,6 +59,27 @@ public class ImageSynthesisParam extends HalfDuplexServiceParam {
5859

5960
@Builder.Default private Boolean watermark = null;
6061

62+
/**
63+
* Controls the similarity between the output image and the reference image (垫图). Value range:
64+
* [0.0, 1.0]. Higher values mean the generated image is more similar to the reference image.
65+
*/
66+
@Builder.Default private Float refStrength = null;
67+
68+
/**
69+
* The mode for generating images based on the reference image (垫图). Supported modes: - "repaint"
70+
* (default): Generate image based on the content of the reference image. - "refonly": Generate
71+
* image based on the style of the reference image.
72+
*/
73+
@Builder.Default private String refMode = null;
74+
75+
/**
76+
* The color used to fill the masked area before inpainting. Format: hex color code, e.g.,
77+
* "#FFFFFF" or "white", "black", etc.
78+
*/
79+
@SerializedName("mask_color")
80+
@Builder.Default
81+
private String maskColor = null;
82+
6183
@Override
6284
public JsonObject getInput() {
6385
JsonObject jsonObject = new JsonObject();
@@ -124,6 +146,18 @@ public Map<String, Object> getParameters() {
124146
params.put(WATERMARK, watermark);
125147
}
126148

149+
if (refStrength != null) {
150+
params.put("ref_strength", refStrength);
151+
}
152+
153+
if (refMode != null) {
154+
params.put("ref_mode", refMode);
155+
}
156+
157+
if (maskColor != null) {
158+
params.put("mask_color", maskColor);
159+
}
160+
127161
params.putAll(super.getParameters());
128162
return params;
129163
}

src/main/java/com/alibaba/dashscope/aigc/imagesynthesis/SketchImageSynthesisParam.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,18 @@ public class SketchImageSynthesisParam extends HalfDuplexServiceParam {
3636
@lombok.NonNull
3737
private String sketchImageUrl;
3838

39+
@SerializedName("style")
40+
@Builder.Default
41+
private String style = null;
42+
43+
@SerializedName("sketch_extraction")
44+
@Builder.Default
45+
private Boolean sketchExtraction = null;
46+
47+
@SerializedName("sketch_color")
48+
@Builder.Default
49+
private String sketchColor = null;
50+
3951
@lombok.NonNull private String prompt;
4052

4153
@Override
@@ -61,6 +73,15 @@ public Map<String, Object> getParameters() {
6173
if (realisticness != null) {
6274
params.put(REALISTICNESS, realisticness);
6375
}
76+
if (style != null) {
77+
params.put("style", style);
78+
}
79+
if (sketchExtraction != null) {
80+
params.put("sketch_extraction", sketchExtraction);
81+
}
82+
if (sketchColor != null) {
83+
params.put("sketch_color", sketchColor);
84+
}
6485
params.putAll(super.getParameters());
6586
return params;
6687
}

src/main/java/com/alibaba/dashscope/aigc/multimodalconversation/MultiModalConversationParam.java

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ public class MultiModalConversationParam extends HalfDuplexServiceParam {
103103
* apple
104104
* </pre>
105105
*/
106-
@Builder.Default private Boolean incrementalOutput;
106+
@Builder.Default private Boolean incrementalOutput = false;
107107

108108
/** Output format of the model including "text" and "audio". Default value: ["text"] */
109109
private List<String> modalities;
@@ -162,6 +162,24 @@ public class MultiModalConversationParam extends HalfDuplexServiceParam {
162162
/** thinking budget */
163163
private Integer thinkingBudget;
164164

165+
/** stop words or token ids to stop generation */
166+
@Singular("stopString")
167+
private List<String> stopStrings;
168+
169+
@Singular private List<List<Integer>> stopTokens;
170+
171+
/**
172+
* whether to return log probabilities of output tokens, supported for qwen-vl-ocr-2025-04-13 and
173+
* later
174+
*/
175+
private Boolean logprobs;
176+
177+
/**
178+
* number of top candidate tokens to return log probabilities for, range [0,5], only effective
179+
* when logprobs is true
180+
*/
181+
private Integer topLogprobs;
182+
165183
@Override
166184
public JsonObject getHttpBody() {
167185
JsonObject requestObject = new JsonObject();
@@ -318,6 +336,20 @@ public Map<String, Object> getParameters() {
318336
params.put("thinking_budget", thinkingBudget);
319337
}
320338

339+
if (stopStrings != null && !stopStrings.isEmpty()) {
340+
params.put(ApiKeywords.STOP, stopStrings);
341+
} else if (stopTokens != null && !stopTokens.isEmpty()) {
342+
params.put(ApiKeywords.STOP, stopTokens);
343+
}
344+
345+
if (logprobs != null) {
346+
params.put("logprobs", logprobs);
347+
}
348+
349+
if (topLogprobs != null) {
350+
params.put("top_logprobs", topLogprobs);
351+
}
352+
321353
params.putAll(parameters);
322354
return params;
323355
}

src/test/java/com/alibaba/dashscope/TestImageSynthesis.java

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,4 +109,96 @@ public void testImageSynthesisUsageMore()
109109
String requestBody = request.getBody().readUtf8();
110110
assertEquals(expectRequestBody, requestBody);
111111
}
112+
113+
@Test
114+
public void testImageSynthesisWithRefStrengthAndRefMode()
115+
throws ApiException, NoApiKeyException, IOException, InterruptedException,
116+
InputRequiredException {
117+
String responseBody =
118+
"{\"request_id\":\"40\",\"output\":{\"task_id\":\"e5\",\"task_status\":\"SUCCEEDED\",\"results\":[{\"url\":\"https://ref1\"}],\"task_metrics\":{\"TOTAL\":1,\"SUCCEEDED\":1,\"FAILED\":0}},\"usage\":{\"image_count\":1}}";
119+
server.enqueue(
120+
new MockResponse()
121+
.setBody(responseBody)
122+
.setHeader("content-type", MEDIA_TYPE_APPLICATION_JSON));
123+
int port = server.getPort();
124+
ImageSynthesis is = new ImageSynthesis();
125+
ImageSynthesisParam param =
126+
ImageSynthesisParam.builder()
127+
.model(ImageSynthesis.Models.WANX_2_1_IMAGEEDIT)
128+
.n(1)
129+
.prompt("参考图像生成")
130+
.refStrength(0.8f)
131+
.refMode("repaint")
132+
.images(java.util.Arrays.asList("https://example.com/ref.png"))
133+
.build();
134+
Constants.baseHttpApiUrl = String.format("http://127.0.0.1:%s", port);
135+
ImageSynthesisResult result = is.asyncCall(param);
136+
RecordedRequest request = server.takeRequest();
137+
String requestBody = request.getBody().readUtf8();
138+
log.info("Request body with ref params: {}", requestBody);
139+
// 验证 ref_strength 和 ref_mode 参数被正确序列化
140+
assertEquals(true, requestBody.contains("\"ref_strength\":0.8"));
141+
assertEquals(true, requestBody.contains("\"ref_mode\":\"repaint\""));
142+
}
143+
144+
@Test
145+
public void testImageSynthesisWithMaskColor()
146+
throws ApiException, NoApiKeyException, IOException, InterruptedException,
147+
InputRequiredException {
148+
String responseBody =
149+
"{\"request_id\":\"41\",\"output\":{\"task_id\":\"e6\",\"task_status\":\"SUCCEEDED\",\"results\":[{\"url\":\"https://mask1\"}],\"task_metrics\":{\"TOTAL\":1,\"SUCCEEDED\":1,\"FAILED\":0}},\"usage\":{\"image_count\":1}}";
150+
server.enqueue(
151+
new MockResponse()
152+
.setBody(responseBody)
153+
.setHeader("content-type", MEDIA_TYPE_APPLICATION_JSON));
154+
int port = server.getPort();
155+
ImageSynthesis is = new ImageSynthesis();
156+
ImageSynthesisParam param =
157+
ImageSynthesisParam.builder()
158+
.model(ImageSynthesis.Models.WANX_2_1_IMAGEEDIT)
159+
.n(1)
160+
.prompt("局部重绘")
161+
.function("description_edit_with_mask")
162+
.baseImageUrl("https://www.xxx.cn/base.png")
163+
.maskImageUrl("https://www.xxx.cn/mask.png")
164+
.maskColor("#FFFFFF")
165+
.build();
166+
Constants.baseHttpApiUrl = String.format("http://127.0.0.1:%s", port);
167+
ImageSynthesisResult result = is.asyncCall(param);
168+
RecordedRequest request = server.takeRequest();
169+
String requestBody = request.getBody().readUtf8();
170+
log.info("Request body with mask_color: {}", requestBody);
171+
// 验证 mask_color 参数被正确序列化
172+
assertEquals(true, requestBody.contains("\"mask_color\":\"#FFFFFF\""));
173+
}
174+
175+
@Test
176+
public void testImageSynthesisWithPromptExtendAndWatermark()
177+
throws ApiException, NoApiKeyException, IOException, InterruptedException,
178+
InputRequiredException {
179+
String responseBody =
180+
"{\"request_id\":\"42\",\"output\":{\"task_id\":\"e7\",\"task_status\":\"SUCCEEDED\",\"results\":[{\"url\":\"https://extend1\"}],\"task_metrics\":{\"TOTAL\":1,\"SUCCEEDED\":1,\"FAILED\":0}},\"usage\":{\"image_count\":1}}";
181+
server.enqueue(
182+
new MockResponse()
183+
.setBody(responseBody)
184+
.setHeader("content-type", MEDIA_TYPE_APPLICATION_JSON));
185+
int port = server.getPort();
186+
ImageSynthesis is = new ImageSynthesis();
187+
ImageSynthesisParam param =
188+
ImageSynthesisParam.builder()
189+
.model(ImageSynthesis.Models.WANX_2_1_IMAGEEDIT)
190+
.n(1)
191+
.prompt("简单提示词")
192+
.promptExtend(true)
193+
.watermark(false)
194+
.build();
195+
Constants.baseHttpApiUrl = String.format("http://127.0.0.1:%s", port);
196+
ImageSynthesisResult result = is.asyncCall(param);
197+
RecordedRequest request = server.takeRequest();
198+
String requestBody = request.getBody().readUtf8();
199+
log.info("Request body with prompt_extend and watermark: {}", requestBody);
200+
// 验证 prompt_extend 和 watermark 参数被正确序列化
201+
assertEquals(true, requestBody.contains("\"prompt_extend\":true"));
202+
assertEquals(true, requestBody.contains("\"watermark\":false"));
203+
}
112204
}

0 commit comments

Comments
 (0)