From be3976a49ac521cb350e94776fb6f0447d788f16 Mon Sep 17 00:00:00 2001 From: kevin Date: Tue, 7 Jul 2026 14:00:06 +0800 Subject: [PATCH 01/31] feat: add new parameters for image generation and multimodal conversation --- .dev_tools/run_ci.sh | 4 +- README.md | 1 - lint.sh | 2 +- .../aigc/completion/ChatCompletionParam.java | 57 ++++++++++++ .../aigc/imagegeneration/ColorPalette.java | 20 ++++ .../imagegeneration/ImageGenerationParam.java | 17 ++++ .../imagesynthesis/ImageSynthesisParam.java | 34 +++++++ .../SketchImageSynthesisParam.java | 21 +++++ .../MultiModalConversationParam.java | 34 ++++++- .../alibaba/dashscope/TestImageSynthesis.java | 92 +++++++++++++++++++ src/test/resources/assistant.json | 2 +- src/test/resources/messages.json | 2 +- src/test/resources/mismatch.jsonl | 14 +-- src/test/resources/runs.json | 2 +- src/test/resources/stream_runs.json | 8 +- src/test/resources/threads.json | 2 +- 16 files changed, 292 insertions(+), 20 deletions(-) create mode 100644 src/main/java/com/alibaba/dashscope/aigc/imagegeneration/ColorPalette.java diff --git a/.dev_tools/run_ci.sh b/.dev_tools/run_ci.sh index 08877d00..a2338057 100644 --- a/.dev_tools/run_ci.sh +++ b/.dev_tools/run_ci.sh @@ -26,7 +26,7 @@ mvn package if [ $? -ne 0 ]; then echo "mvn package failed, please check if any unittest is failed!" - exit -1 + exit 1 fi -echo "CI passed." \ No newline at end of file +echo "CI passed." diff --git a/README.md b/README.md index 4dc376a2..bee11a6a 100644 --- a/README.md +++ b/README.md @@ -173,4 +173,3 @@ public class Main { ``` The `call` method accepts a `GenerationParam`, and returns a `GenerationResult`, you can also catch the exception with a try-catch block. - diff --git a/lint.sh b/lint.sh index 68e79f1a..97b28c6e 100755 --- a/lint.sh +++ b/lint.sh @@ -1 +1 @@ -java -jar .dev_tools/google-java-format-1.7-all-deps.jar -i $(find . -type f -name "*.java" | grep "./*/src/.*java") \ No newline at end of file +java -jar .dev_tools/google-java-format-1.7-all-deps.jar -i $(find . -type f -name "*.java" | grep "./*/src/.*java") diff --git a/src/main/java/com/alibaba/dashscope/aigc/completion/ChatCompletionParam.java b/src/main/java/com/alibaba/dashscope/aigc/completion/ChatCompletionParam.java index 8204042b..84cafab7 100644 --- a/src/main/java/com/alibaba/dashscope/aigc/completion/ChatCompletionParam.java +++ b/src/main/java/com/alibaba/dashscope/aigc/completion/ChatCompletionParam.java @@ -80,6 +80,45 @@ public class ChatCompletionParam extends FlattenHalfDuplexParamBase { @SerializedName("parallel_tool_calls") private Boolean parallelToolCalls; + /** + * Whether to preserve thinking/reasoning content in the response. When enabled, the model will + * include reasoning process in the output. + */ + @SerializedName("preserve_thinking") + private Boolean preserveThinking; + + /** + * Controls the reasoning effort level for models that support it. Possible values: "low", + * "medium", "high" + */ + @SerializedName("reasoning_effort") + private String reasoningEffort; + + /** + * The maximum number of tokens to generate for completion. This is an alternative to max_tokens + * following OpenAI's newer API convention. + */ + @SerializedName("max_completion_tokens") + private Integer maxCompletionTokens; + + /** + * Whether to stream tool calls as they are generated. When true, tool calls will be sent + * incrementally rather than all at once. + */ + @SerializedName("tool_stream") + private Boolean toolStream; + + /** + * Enable high resolution image processing for vision-language models. Improves image + * understanding quality at the cost of more tokens. + */ + @SerializedName("vl_high_resolution_images") + private Boolean vlHighResolutionImages; + + /** Enable hardware-accelerated image output for vision-language models. */ + @SerializedName("vl_enable_image_hw_output") + private Boolean vlEnableImageHwOutput; + private String user; @Override @@ -142,6 +181,24 @@ public JsonObject getHttpBody() { if (parallelToolCalls != null) { requestObject.addProperty("parallel_tool_calls", parallelToolCalls); } + if (preserveThinking != null) { + requestObject.addProperty("preserve_thinking", preserveThinking); + } + if (reasoningEffort != null) { + requestObject.addProperty("reasoning_effort", reasoningEffort); + } + if (maxCompletionTokens != null) { + requestObject.addProperty("max_completion_tokens", maxCompletionTokens); + } + if (toolStream != null) { + requestObject.addProperty("tool_stream", toolStream); + } + if (vlHighResolutionImages != null) { + requestObject.addProperty("vl_high_resolution_images", vlHighResolutionImages); + } + if (vlEnableImageHwOutput != null) { + requestObject.addProperty("vl_enable_image_hw_output", vlEnableImageHwOutput); + } if (user != null) { requestObject.addProperty("user", user); } diff --git a/src/main/java/com/alibaba/dashscope/aigc/imagegeneration/ColorPalette.java b/src/main/java/com/alibaba/dashscope/aigc/imagegeneration/ColorPalette.java new file mode 100644 index 00000000..85850c1b --- /dev/null +++ b/src/main/java/com/alibaba/dashscope/aigc/imagegeneration/ColorPalette.java @@ -0,0 +1,20 @@ +package com.alibaba.dashscope.aigc.imagegeneration; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** Color palette configuration for image generation. */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class ColorPalette { + + /** Hex color code, e.g., "#FF5733" */ + private String hex; + + /** Ratio of the color in the palette, value range: [0.0, 1.0] */ + private Double ratio; +} diff --git a/src/main/java/com/alibaba/dashscope/aigc/imagegeneration/ImageGenerationParam.java b/src/main/java/com/alibaba/dashscope/aigc/imagegeneration/ImageGenerationParam.java index 683414a3..19522ac7 100644 --- a/src/main/java/com/alibaba/dashscope/aigc/imagegeneration/ImageGenerationParam.java +++ b/src/main/java/com/alibaba/dashscope/aigc/imagegeneration/ImageGenerationParam.java @@ -83,6 +83,15 @@ public class ImageGenerationParam extends HalfDuplexServiceParam { */ @Builder.Default private List>> bboxList = null; + /** + * Thinking mode for image generation. Controls the level of reasoning and planning in the + * generation process. + */ + private String thinkingMode; + + /** Color palette configuration for controlling color distribution in generated images. */ + private List colorPalette; + @Override public JsonObject getHttpBody() { JsonObject requestObject = new JsonObject(); @@ -167,6 +176,14 @@ public Map getParameters() { params.put("bbox_list", bboxList); } + if (thinkingMode != null) { + params.put("thinking_mode", thinkingMode); + } + + if (colorPalette != null) { + params.put("color_palette", colorPalette); + } + params.putAll(parameters); return params; } diff --git a/src/main/java/com/alibaba/dashscope/aigc/imagesynthesis/ImageSynthesisParam.java b/src/main/java/com/alibaba/dashscope/aigc/imagesynthesis/ImageSynthesisParam.java index 66ac2454..ce147545 100644 --- a/src/main/java/com/alibaba/dashscope/aigc/imagesynthesis/ImageSynthesisParam.java +++ b/src/main/java/com/alibaba/dashscope/aigc/imagesynthesis/ImageSynthesisParam.java @@ -12,6 +12,7 @@ import com.alibaba.dashscope.utils.JsonUtils; import com.alibaba.dashscope.utils.PreprocessInputImage; import com.google.gson.JsonObject; +import com.google.gson.annotations.SerializedName; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.HashMap; @@ -58,6 +59,27 @@ public class ImageSynthesisParam extends HalfDuplexServiceParam { @Builder.Default private Boolean watermark = null; + /** + * Controls the similarity between the output image and the reference image (垫图). Value range: + * [0.0, 1.0]. Higher values mean the generated image is more similar to the reference image. + */ + @Builder.Default private Float refStrength = null; + + /** + * The mode for generating images based on the reference image (垫图). Supported modes: - "repaint" + * (default): Generate image based on the content of the reference image. - "refonly": Generate + * image based on the style of the reference image. + */ + @Builder.Default private String refMode = null; + + /** + * The color used to fill the masked area before inpainting. Format: hex color code, e.g., + * "#FFFFFF" or "white", "black", etc. + */ + @SerializedName("mask_color") + @Builder.Default + private String maskColor = null; + @Override public JsonObject getInput() { JsonObject jsonObject = new JsonObject(); @@ -124,6 +146,18 @@ public Map getParameters() { params.put(WATERMARK, watermark); } + if (refStrength != null) { + params.put("ref_strength", refStrength); + } + + if (refMode != null) { + params.put("ref_mode", refMode); + } + + if (maskColor != null) { + params.put("mask_color", maskColor); + } + params.putAll(super.getParameters()); return params; } diff --git a/src/main/java/com/alibaba/dashscope/aigc/imagesynthesis/SketchImageSynthesisParam.java b/src/main/java/com/alibaba/dashscope/aigc/imagesynthesis/SketchImageSynthesisParam.java index 010c07f4..4def1049 100644 --- a/src/main/java/com/alibaba/dashscope/aigc/imagesynthesis/SketchImageSynthesisParam.java +++ b/src/main/java/com/alibaba/dashscope/aigc/imagesynthesis/SketchImageSynthesisParam.java @@ -36,6 +36,18 @@ public class SketchImageSynthesisParam extends HalfDuplexServiceParam { @lombok.NonNull private String sketchImageUrl; + @SerializedName("style") + @Builder.Default + private String style = null; + + @SerializedName("sketch_extraction") + @Builder.Default + private Boolean sketchExtraction = null; + + @SerializedName("sketch_color") + @Builder.Default + private String sketchColor = null; + @lombok.NonNull private String prompt; @Override @@ -61,6 +73,15 @@ public Map getParameters() { if (realisticness != null) { params.put(REALISTICNESS, realisticness); } + if (style != null) { + params.put("style", style); + } + if (sketchExtraction != null) { + params.put("sketch_extraction", sketchExtraction); + } + if (sketchColor != null) { + params.put("sketch_color", sketchColor); + } params.putAll(super.getParameters()); return params; } diff --git a/src/main/java/com/alibaba/dashscope/aigc/multimodalconversation/MultiModalConversationParam.java b/src/main/java/com/alibaba/dashscope/aigc/multimodalconversation/MultiModalConversationParam.java index 202bfa53..2ab9490e 100644 --- a/src/main/java/com/alibaba/dashscope/aigc/multimodalconversation/MultiModalConversationParam.java +++ b/src/main/java/com/alibaba/dashscope/aigc/multimodalconversation/MultiModalConversationParam.java @@ -103,7 +103,7 @@ public class MultiModalConversationParam extends HalfDuplexServiceParam { * apple * */ - @Builder.Default private Boolean incrementalOutput; + @Builder.Default private Boolean incrementalOutput = false; /** Output format of the model including "text" and "audio". Default value: ["text"] */ private List modalities; @@ -162,6 +162,24 @@ public class MultiModalConversationParam extends HalfDuplexServiceParam { /** thinking budget */ private Integer thinkingBudget; + /** stop words or token ids to stop generation */ + @Singular("stopString") + private List stopStrings; + + @Singular private List> stopTokens; + + /** + * whether to return log probabilities of output tokens, supported for qwen-vl-ocr-2025-04-13 and + * later + */ + private Boolean logprobs; + + /** + * number of top candidate tokens to return log probabilities for, range [0,5], only effective + * when logprobs is true + */ + private Integer topLogprobs; + @Override public JsonObject getHttpBody() { JsonObject requestObject = new JsonObject(); @@ -318,6 +336,20 @@ public Map getParameters() { params.put("thinking_budget", thinkingBudget); } + if (stopStrings != null && !stopStrings.isEmpty()) { + params.put(ApiKeywords.STOP, stopStrings); + } else if (stopTokens != null && !stopTokens.isEmpty()) { + params.put(ApiKeywords.STOP, stopTokens); + } + + if (logprobs != null) { + params.put("logprobs", logprobs); + } + + if (topLogprobs != null) { + params.put("top_logprobs", topLogprobs); + } + params.putAll(parameters); return params; } diff --git a/src/test/java/com/alibaba/dashscope/TestImageSynthesis.java b/src/test/java/com/alibaba/dashscope/TestImageSynthesis.java index bc1ecee3..7741cea8 100644 --- a/src/test/java/com/alibaba/dashscope/TestImageSynthesis.java +++ b/src/test/java/com/alibaba/dashscope/TestImageSynthesis.java @@ -109,4 +109,96 @@ public void testImageSynthesisUsageMore() String requestBody = request.getBody().readUtf8(); assertEquals(expectRequestBody, requestBody); } + + @Test + public void testImageSynthesisWithRefStrengthAndRefMode() + throws ApiException, NoApiKeyException, IOException, InterruptedException, + InputRequiredException { + String responseBody = + "{\"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}}"; + server.enqueue( + new MockResponse() + .setBody(responseBody) + .setHeader("content-type", MEDIA_TYPE_APPLICATION_JSON)); + int port = server.getPort(); + ImageSynthesis is = new ImageSynthesis(); + ImageSynthesisParam param = + ImageSynthesisParam.builder() + .model(ImageSynthesis.Models.WANX_2_1_IMAGEEDIT) + .n(1) + .prompt("参考图像生成") + .refStrength(0.8f) + .refMode("repaint") + .images(java.util.Arrays.asList("https://example.com/ref.png")) + .build(); + Constants.baseHttpApiUrl = String.format("http://127.0.0.1:%s", port); + ImageSynthesisResult result = is.asyncCall(param); + RecordedRequest request = server.takeRequest(); + String requestBody = request.getBody().readUtf8(); + log.info("Request body with ref params: {}", requestBody); + // 验证 ref_strength 和 ref_mode 参数被正确序列化 + assertEquals(true, requestBody.contains("\"ref_strength\":0.8")); + assertEquals(true, requestBody.contains("\"ref_mode\":\"repaint\"")); + } + + @Test + public void testImageSynthesisWithMaskColor() + throws ApiException, NoApiKeyException, IOException, InterruptedException, + InputRequiredException { + String responseBody = + "{\"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}}"; + server.enqueue( + new MockResponse() + .setBody(responseBody) + .setHeader("content-type", MEDIA_TYPE_APPLICATION_JSON)); + int port = server.getPort(); + ImageSynthesis is = new ImageSynthesis(); + ImageSynthesisParam param = + ImageSynthesisParam.builder() + .model(ImageSynthesis.Models.WANX_2_1_IMAGEEDIT) + .n(1) + .prompt("局部重绘") + .function("description_edit_with_mask") + .baseImageUrl("https://www.xxx.cn/base.png") + .maskImageUrl("https://www.xxx.cn/mask.png") + .maskColor("#FFFFFF") + .build(); + Constants.baseHttpApiUrl = String.format("http://127.0.0.1:%s", port); + ImageSynthesisResult result = is.asyncCall(param); + RecordedRequest request = server.takeRequest(); + String requestBody = request.getBody().readUtf8(); + log.info("Request body with mask_color: {}", requestBody); + // 验证 mask_color 参数被正确序列化 + assertEquals(true, requestBody.contains("\"mask_color\":\"#FFFFFF\"")); + } + + @Test + public void testImageSynthesisWithPromptExtendAndWatermark() + throws ApiException, NoApiKeyException, IOException, InterruptedException, + InputRequiredException { + String responseBody = + "{\"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}}"; + server.enqueue( + new MockResponse() + .setBody(responseBody) + .setHeader("content-type", MEDIA_TYPE_APPLICATION_JSON)); + int port = server.getPort(); + ImageSynthesis is = new ImageSynthesis(); + ImageSynthesisParam param = + ImageSynthesisParam.builder() + .model(ImageSynthesis.Models.WANX_2_1_IMAGEEDIT) + .n(1) + .prompt("简单提示词") + .promptExtend(true) + .watermark(false) + .build(); + Constants.baseHttpApiUrl = String.format("http://127.0.0.1:%s", port); + ImageSynthesisResult result = is.asyncCall(param); + RecordedRequest request = server.takeRequest(); + String requestBody = request.getBody().readUtf8(); + log.info("Request body with prompt_extend and watermark: {}", requestBody); + // 验证 prompt_extend 和 watermark 参数被正确序列化 + assertEquals(true, requestBody.contains("\"prompt_extend\":true")); + assertEquals(true, requestBody.contains("\"watermark\":false")); + } } diff --git a/src/test/resources/assistant.json b/src/test/resources/assistant.json index c89d6cd5..2b55b086 100644 --- a/src/test/resources/assistant.json +++ b/src/test/resources/assistant.json @@ -161,4 +161,4 @@ "last_id": "file_2", "has_more": false } -} \ No newline at end of file +} diff --git a/src/test/resources/messages.json b/src/test/resources/messages.json index 141c3160..37a9cd6d 100644 --- a/src/test/resources/messages.json +++ b/src/test/resources/messages.json @@ -98,4 +98,4 @@ "created_at": 1699061776, "message_id": "msg_1" } -} \ No newline at end of file +} diff --git a/src/test/resources/mismatch.jsonl b/src/test/resources/mismatch.jsonl index ba8bf4b6..ef0cc609 100644 --- a/src/test/resources/mismatch.jsonl +++ b/src/test/resources/mismatch.jsonl @@ -1,7 +1,7 @@ -{"HumanAnswer": "2个读音  一个是xi 一个是qian", -"Python": [24918, 18947, 57553, 78685, 22441, 22441, 108075, 144767, 144155, 22441, 108075, 145529, 144155, 144127, 144270], +{"HumanAnswer": "2个读音  一个是xi 一个是qian", +"Python": [24918, 18947, 57553, 78685, 22441, 22441, 108075, 144767, 144155, 22441, 108075, 145529, 144155, 144127, 144270], "Java": [24918, 18947, 57553, 78685, 43429, 108075, 144767, 144155, 22441, 108075, 145529, 144155, 144127, 144270]} -{"HumanAnswer": "炸腐竹·油温要适中·炸后浸温水 +{"HumanAnswer": "炸腐竹·油温要适中·炸后浸温水   要腐竹炸得刚好而不焦黑,秘诀是当生油烧至六成熟时转慢火,然后放下腐竹,迅速地翻动,让腐竹全身炸透,呈现白泡,并即投入温水中浸透,再切段。炸时如油温过热,会将腐竹炸焦,变深黄色或黑色,如油温不够热,则不会起白泡。 ", "Python": [100893, 100298, 102045, 13935, 99318, 99416, 30534, 99340, 15946, 13935, 100893, 33447, 101595, 99416, 52510, 2529, 22441, 22441, 30534, 100298, 102045, 100893, 49828, 109586, 105145, 100479, 56652, 3837, 114928, 20412, 39165, 21287, 99318, 100228, 56137, 99566, 102226, 13343, 46670, 99843, 79599, 3837, 101889, 106162, 100298, 102045, 3837, 104015, 29490, 100712, 27733, 3837, 99258, 100298, 102045, 105418, 100893, 99844, 3837, 104401, 99243, 100454, 90395, 91676, 100709, 99416, 107629, 101595, 99844, 3837, 87256, 99322, 37474, 1773, 100893, 13343, 29524, 99318, 99416, 38182, 99259, 3837, 36993, 44063, 100298, 102045, 100893, 100479, 3837, 74040, 99194, 106140, 57191, 104723, 3837, 29524, 99318, 99416, 104222, 99259, 95053, 99670, 71618, 99243, 100454, 1773, 220], "Java": [100893, 100298, 102045, 13935, 99318, 99416, 30534, 99340, 15946, 13935, 100893, 33447, 101595, 99416, 52510, 2529, 43429, 30534, 100298, 102045, 100893, 49828, 109586, 105145, 100479, 56652, 3837, 114928, 20412, 39165, 21287, 99318, 100228, 56137, 99566, 102226, 13343, 46670, 99843, 79599, 3837, 101889, 106162, 100298, 102045, 3837, 104015, 29490, 100712, 27733, 3837, 99258, 100298, 102045, 105418, 100893, 99844, 3837, 104401, 99243, 100454, 90395, 91676, 100709, 99416, 107629, 101595, 99844, 3837, 87256, 99322, 37474, 1773, 100893, 13343, 29524, 99318, 99416, 38182, 99259, 3837, 36993, 44063, 100298, 102045, 100893, 100479, 3837, 74040, 99194, 106140, 57191, 104723, 3837, 29524, 99318, 99416, 104222, 99259, 95053, 99670, 71618, 99243, 100454, 1773, 220]} {"HumanAnswer": "  你好,癫痫病频频发作容易造成癫痫患者的脑发育障碍、组织结构毁坏、代谢异常,必然也影响其所有心理发育的领域,对于脑器质性损害伴发的癫痫,往往伴有智力损害及行为问题。一般癫痫发生愈早,程度愈重,对心理、精神行为的影响也愈大。 ", "Python": [22441, 22441, 108386, 3837, 113282, 111972, 107253, 100047, 101090, 103440, 108723, 99931, 106115, 102544, 5373, 99877, 100166, 100882, 100250, 5373, 103225, 70633, 3837, 102309, 74763, 99564, 41146, 55338, 100438, 106115, 9370, 100650, 3837, 100002, 99931, 31548, 99178, 33071, 105252, 99595, 28291, 9370, 103440, 3837, 101207, 115563, 107941, 105252, 81217, 101070, 86119, 1773, 100141, 103440, 99726, 100939, 99391, 3837, 100069, 100939, 29258, 3837, 32664, 100438, 5373, 100150, 101070, 104126, 74763, 100939, 26288, 37265], "Java": [43429, 108386, 3837, 113282, 111972, 107253, 100047, 101090, 103440, 108723, 99931, 106115, 102544, 5373, 99877, 100166, 100882, 100250, 5373, 103225, 70633, 3837, 102309, 74763, 99564, 41146, 55338, 100438, 106115, 9370, 100650, 3837, 100002, 99931, 31548, 99178, 33071, 105252, 99595, 28291, 9370, 103440, 3837, 101207, 115563, 107941, 105252, 81217, 101070, 86119, 1773, 100141, 103440, 99726, 100939, 99391, 3837, 100069, 100939, 29258, 3837, 32664, 100438, 5373, 100150, 101070, 104126, 74763, 100939, 26288, 37265]} @@ -10,7 +10,7 @@ ", "Python": [85336, 116350, 104160, 39907, 99604, 99999, 97480, 36993, 99518, 102478, 3837, 102716, 104160, 18830, 319, 22441, 16, 5373, 99950, 100553, 24339, 9909, 99393, 99451, 24339, 7552, 85336, 116350, 220, 22441, 22441, 17, 5373, 29767, 101953, 24339, 85336, 116350, 220, 22441, 18, 5373, 105608, 24339, 85336, 116350, 220, 22441, 22441, 19, 5373, 99759, 64952, 32320, 99368, 24339, 85336, 116350, 220, 22441, 20, 5373, 47815, 76837, 99322, 29767, 24339, 85336, 116350, 220, 100700, 99559, 30440, 118393, 100849, 105157, 104316, 31139, 319], "Java": [85336, 116350, 104160, 39907, 99604, 99999, 97480, 36993, 99518, 102478, 3837, 102716, 104160, 18830, 319, 22441, 16, 5373, 99950, 100553, 24339, 9909, 99393, 99451, 24339, 7552, 85336, 116350, 220, 43429, 17, 5373, 29767, 101953, 24339, 85336, 116350, 220, 22441, 18, 5373, 105608, 24339, 85336, 116350, 220, 43429, 19, 5373, 99759, 64952, 32320, 99368, 24339, 85336, 116350, 220, 22441, 20, 5373, 47815, 76837, 99322, 29767, 24339, 85336, 116350, 220, 100700, 99559, 30440, 118393, 100849, 105157, 104316, 31139, 319]} {"HumanAnswer": "  你好,过敏性鼻炎喷嚏、鼻痒、流涕和鼻堵是最常见的四大症状。目前临床上主要是应用内窥镜下等离子低温消融术,选择性地进行筛前神经、翼管神经阻断术,降低鼻腔副交感神经的兴奋性,消除过敏区域神经对外界各种刺激,如气候、灰尘、花粉等的敏感性,过敏性鼻炎就可以自然而愈,不易复发。 ", "Python": [22441, 22441, 108386, 11, 107270, 33071, 102228, 100439, 102125, 121875, 5373, 102228, 108681, 5373, 88653, 119392, 33108, 102228, 101476, 104890, 102716, 104978, 101368, 1773, 100004, 99635, 101717, 104312, 99892, 31843, 112808, 100811, 16872, 49567, 108510, 109878, 32320, 99368, 99216, 3837, 50404, 33071, 29490, 71817, 101737, 24562, 102398, 5373, 101401, 35551, 102398, 100318, 63789, 99216, 3837, 101134, 102228, 102504, 99310, 38109, 98650, 102398, 9370, 105487, 33071, 3837, 105109, 107270, 101065, 102398, 102330, 97120, 100646, 104313, 3837, 29524, 102730, 5373, 114267, 5373, 99232, 99742, 49567, 9370, 105521, 33071, 3837, 107270, 33071, 102228, 100439, 102003, 117536, 100939, 3837, 106921, 111574, 8997], "Java": [43429, 108386, 11, 107270, 33071, 102228, 100439, 102125, 121875, 5373, 102228, 108681, 5373, 88653, 119392, 33108, 102228, 101476, 104890, 102716, 104978, 101368, 1773, 100004, 99635, 101717, 104312, 99892, 31843, 112808, 100811, 16872, 49567, 108510, 109878, 32320, 99368, 99216, 3837, 50404, 33071, 29490, 71817, 101737, 24562, 102398, 5373, 101401, 35551, 102398, 100318, 63789, 99216, 3837, 101134, 102228, 102504, 99310, 38109, 98650, 102398, 9370, 105487, 33071, 3837, 105109, 107270, 101065, 102398, 102330, 97120, 100646, 104313, 3837, 29524, 102730, 5373, 114267, 5373, 99232, 99742, 49567, 9370, 105521, 33071, 3837, 107270, 33071, 102228, 100439, 102003, 117536, 100939, 3837, 106921, 111574, 8997]} -{"HumanAnswer": "  新生婴儿应在出生后一个月内,到父亲或母亲户口所在地的户口登记机关(辖区派出所)办理出生入户登记。 +{"HumanAnswer": "  新生婴儿应在出生后一个月内,到父亲或母亲户口所在地的户口登记机关(辖区派出所)办理出生入户登记。   办理时需提供以下材料:   1、小孩的出生医学证明   2、父母的户口簿、身份证和结婚证。", "Python": [22441, 22441, 102900, 102833, 110391, 102246, 33447, 105033, 31843, 3837, 26939, 102037, 57191, 102079, 109200, 108176, 9370, 109200, 104141, 100776, 9909, 103022, 106644, 7552, 103990, 102246, 111981, 104141, 1773, 2529, 22441, 22441, 103990, 13343, 58362, 99553, 87752, 100643, 5122, 319, 22441, 22441, 16, 5373, 104902, 9370, 102246, 104316, 104022, 319, 22441, 22441, 17, 5373, 103953, 9370, 109200, 113649, 5373, 105765, 33108, 104388, 33477, 1773], "Java": [43429, 102900, 102833, 110391, 102246, 33447, 105033, 31843, 3837, 26939, 102037, 57191, 102079, 109200, 108176, 9370, 109200, 104141, 100776, 9909, 103022, 106644, 7552, 103990, 102246, 111981, 104141, 1773, 2529, 43429, 103990, 13343, 58362, 99553, 87752, 100643, 5122, 319, 43429, 16, 5373, 104902, 9370, 102246, 104316, 104022, 319, 43429, 17, 5373, 103953, 9370, 109200, 113649, 5373, 105765, 33108, 104388, 33477, 1773]} @@ -59,7 +59,7 @@ {"HumanAnswer": "1.及时治疗:主要是改善脑血液循环。可以在医生的指导下使用扩血管药物和银杏叶制剂等。  2.早期预防:对脑供血不足的防治重点在脑血管方面,尤其是血脂和低密度脂蛋白增高要对症处理。  3.合理饮食:平时多吃新鲜蔬菜(如洋葱、西红柿等)、水果、鱼、黑木耳、少量醋、干红葡萄酒等,可以起抗氧化作用,延缓脑动脉硬化的发生。  4.适当的户外活动:如快走、慢跑、散步等,每次30-40分钟,每周至少5天,或者打打太极拳、垂钓、登山等。  5.保持良好的心态和健康用脑:平时看看电视、报纸;做些手工劳作或家务事;也可以参加一些文体活动,如唱歌、跳舞、书法、打球等,调制性情,增强脑的思维活动;避免情绪激动和过度疲劳。", "Python": [16, 58883, 100667, 101899, 5122, 104312, 104009, 99931, 116417, 1773, 104964, 103998, 9370, 109767, 37029, 99834, 102890, 104459, 33108, 99660, 108202, 100027, 115031, 49567, 1773, 22441, 22441, 17, 58883, 105184, 104332, 5122, 32664, 99931, 83744, 99389, 102004, 9370, 102681, 99887, 18493, 99931, 102890, 99522, 3837, 104033, 118112, 33108, 99285, 106651, 100553, 102835, 117015, 30534, 32664, 99769, 54542, 1773, 22441, 22441, 18, 58883, 100745, 104579, 5122, 104325, 108851, 104838, 104451, 7, 29524, 117956, 5373, 117700, 49567, 8, 5373, 104618, 5373, 100655, 5373, 56652, 116690, 5373, 108521, 106960, 5373, 99251, 99425, 107358, 49567, 3837, 73670, 71618, 118283, 100154, 3837, 99771, 99982, 99931, 109826, 99927, 105302, 99726, 1773, 22441, 22441, 19, 58883, 109776, 105813, 99600, 5122, 29524, 99234, 99314, 5373, 99843, 99803, 5373, 111261, 49567, 3837, 102173, 18, 15, 12, 19, 15, 83031, 3837, 106854, 104102, 20, 35727, 3837, 100631, 75437, 75437, 116674, 5373, 102566, 103429, 5373, 112357, 49567, 1773, 22441, 22441, 20, 58883, 100662, 104205, 106479, 33108, 99722, 11622, 99931, 5122, 104325, 101997, 100234, 5373, 107574, 24968, 99190, 97084, 107171, 99697, 19403, 57191, 114791, 29826, 24968, 104047, 101061, 101883, 111048, 99600, 3837, 29524, 109283, 5373, 112084, 5373, 102702, 5373, 117248, 49567, 3837, 47872, 43316, 33071, 39374, 3837, 101138, 99931, 9370, 102141, 99600, 24968, 101153, 104405, 105694, 33108, 105831, 108351, 1773], "Java": [16, 58883, 100667, 101899, 5122, 104312, 104009, 99931, 116417, 1773, 104964, 103998, 9370, 109767, 37029, 99834, 102890, 104459, 33108, 99660, 108202, 100027, 115031, 49567, 1773, 43429, 17, 58883, 105184, 104332, 5122, 32664, 99931, 83744, 99389, 102004, 9370, 102681, 99887, 18493, 99931, 102890, 99522, 3837, 104033, 118112, 33108, 99285, 106651, 100553, 102835, 117015, 30534, 32664, 99769, 54542, 1773, 43429, 18, 58883, 100745, 104579, 5122, 104325, 108851, 104838, 104451, 7, 29524, 117956, 5373, 117700, 49567, 8, 5373, 104618, 5373, 100655, 5373, 56652, 116690, 5373, 108521, 106960, 5373, 99251, 99425, 107358, 49567, 3837, 73670, 71618, 118283, 100154, 3837, 99771, 99982, 99931, 109826, 99927, 105302, 99726, 1773, 43429, 19, 58883, 109776, 105813, 99600, 5122, 29524, 99234, 99314, 5373, 99843, 99803, 5373, 111261, 49567, 3837, 102173, 18, 15, 12, 19, 15, 83031, 3837, 106854, 104102, 20, 35727, 3837, 100631, 75437, 75437, 116674, 5373, 102566, 103429, 5373, 112357, 49567, 1773, 43429, 20, 58883, 100662, 104205, 106479, 33108, 99722, 11622, 99931, 5122, 104325, 101997, 100234, 5373, 107574, 24968, 99190, 97084, 107171, 99697, 19403, 57191, 114791, 29826, 24968, 104047, 101061, 101883, 111048, 99600, 3837, 29524, 109283, 5373, 112084, 5373, 102702, 5373, 117248, 49567, 3837, 47872, 43316, 33071, 39374, 3837, 101138, 99931, 9370, 102141, 99600, 24968, 101153, 104405, 105694, 33108, 105831, 108351, 1773]} {"HumanAnswer": "您好:肠道检查包括: (1)粪便检查: -  (2)直肠指诊: +  (2)直肠指诊:   (3)乙状结肠镜检查:   (4)钡灌肠X线检查:   (5)纤维结肠镜检查 @@ -93,6 +93,6 @@ {"HumanAnswer": "不合法  属于盗用   协议也是无效的   不受保护", "Python": [16530, 101431, 4102, 4102, 100409, 100591, 11622, 9238, 4102, 101136, 100000, 107128, 9370, 9238, 4102, 107524, 100153], "Java": [16530, 101431, 9238, 100409, 100591, 11622, 45393, 101136, 100000, 107128, 9370, 45393, 107524, 100153]} {"HumanAnswer": "您好  一般是可以的,具体的咨询社保部门", "Python": [111308, 4102, 4102, 100141, 105903, 9370, 3837, 108247, 100703, 106732, 99667], "Java": [111308, 9238, 100141, 105903, 9370, 3837, 108247, 100703, 106732, 99667]} {"HumanAnswer": "针对你的问题答复如下:    《民法通则》第六十六条 没有代理权、超越代理权或者代理权终止后的行为,只有经过被代理人的追认,被代理人才承担民事责任。未经追认的行为,由行为人承担民事责任。本人知道他人以本人名义实施民事行为而不作否认表示的,视为同意。以上答复,如果满意,敬请采纳,并给予评价。", "Python": [101092, 103929, 86119, 110463, 104506, 5122, 45393, 4102, 26940, 69721, 24339, 31935, 46448, 25067, 102651, 117602, 4102, 80443, 101259, 40981, 5373, 104840, 101259, 40981, 100631, 101259, 40981, 107660, 33447, 104796, 3837, 101043, 101897, 99250, 101259, 103947, 99626, 28951, 3837, 99250, 101259, 101050, 102016, 108669, 99531, 1773, 102887, 99626, 28951, 104796, 3837, 67071, 101070, 17340, 102016, 108669, 99531, 1773, 102043, 99392, 104287, 23031, 102043, 107765, 100355, 108669, 101070, 105145, 19403, 109433, 51463, 9370, 3837, 103097, 101185, 1773, 70589, 110463, 3837, 62244, 100545, 3837, 114131, 115250, 90395, 102062, 103964, 1773], "Java": [101092, 103929, 86119, 110463, 104506, 5122, 19070, 26940, 69721, 24339, 31935, 46448, 25067, 102651, 117602, 4102, 80443, 101259, 40981, 5373, 104840, 101259, 40981, 100631, 101259, 40981, 107660, 33447, 104796, 3837, 101043, 101897, 99250, 101259, 103947, 99626, 28951, 3837, 99250, 101259, 101050, 102016, 108669, 99531, 1773, 102887, 99626, 28951, 104796, 3837, 67071, 101070, 17340, 102016, 108669, 99531, 1773, 102043, 99392, 104287, 23031, 102043, 107765, 100355, 108669, 101070, 105145, 19403, 109433, 51463, 9370, 3837, 103097, 101185, 1773, 70589, 110463, 3837, 62244, 100545, 3837, 114131, 115250, 90395, 102062, 103964, 1773]} -{"HumanAnswer": "你在网上的棋牌室花了人民币多少钱啊  你说的跑路是什么意思啊", -"Python": [107740, 31139, 101913, 101386, 99478, 108225, 104142, 105957, 103924, 4102, 4102, 105665, 9370, 99803, 45995, 102021, 100313, 103924], +{"HumanAnswer": "你在网上的棋牌室花了人民币多少钱啊  你说的跑路是什么意思啊", +"Python": [107740, 31139, 101913, 101386, 99478, 108225, 104142, 105957, 103924, 4102, 4102, 105665, 9370, 99803, 45995, 102021, 100313, 103924], "Java": [107740, 31139, 101913, 101386, 99478, 108225, 104142, 105957, 103924, 9238, 105665, 9370, 99803, 45995, 102021, 100313, 103924]} diff --git a/src/test/resources/runs.json b/src/test/resources/runs.json index c2977189..742d4094 100644 --- a/src/test/resources/runs.json +++ b/src/test/resources/runs.json @@ -482,4 +482,4 @@ "response_format": "auto", "tool_choice": "auto" } -} \ No newline at end of file +} diff --git a/src/test/resources/stream_runs.json b/src/test/resources/stream_runs.json index 98ac44fc..12096721 100644 --- a/src/test/resources/stream_runs.json +++ b/src/test/resources/stream_runs.json @@ -55,7 +55,7 @@ { "event": "done", "data": "[DONE]" - } + } ], "create_stream_thread_and_run_response": [ {"event": "thread.created", @@ -120,7 +120,7 @@ { "event": "done", "data": "[DONE]" - } + } ], "create_stream_require_action_function_1": [ { @@ -182,7 +182,7 @@ { "event": "done", "data": "[DONE]" - } + } ], "create_stream_require_action_function_2": [ { @@ -292,4 +292,4 @@ "data": "[DONE]" } ] -} \ No newline at end of file +} diff --git a/src/test/resources/threads.json b/src/test/resources/threads.json index 863b37ef..aafdd3c5 100644 --- a/src/test/resources/threads.json +++ b/src/test/resources/threads.json @@ -34,4 +34,4 @@ "object": "thread" }] } -} \ No newline at end of file +} From 7d11faedfd953ce2323b7ab3e16ffb25e89ff5ae Mon Sep 17 00:00:00 2001 From: "zhansheng.lzs" Date: Tue, 7 Jul 2026 15:04:32 +0800 Subject: [PATCH 02/31] Add NOTICE file declaring third-party licenses Lists EPL-2.0 (JUnit Jupiter, JUnit Pioneer) and other third-party components used in test scope to address license compliance. Co-Authored-By: Claude Opus 4.7 --- NOTICE | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 NOTICE diff --git a/NOTICE b/NOTICE new file mode 100644 index 00000000..6c4eb808 --- /dev/null +++ b/NOTICE @@ -0,0 +1,39 @@ +DashScope SDK for Java +Copyright 2023-2026 Alibaba Cloud Computing Ltd. + +This product includes software developed at +The Apache Software Foundation (https://www.apache.org/). + +Third-Party Software +-------------------- + +This project uses the following third-party components in test scope only +(not included in the distributed artifact): + +JUnit Jupiter (https://junit.org/junit5/) + License: Eclipse Public License 2.0 (EPL-2.0) + Copyright: JUnit team + +JUnit Pioneer (https://junit-pioneer.org/) + License: Eclipse Public License 2.0 (EPL-2.0) + Copyright: JUnit Pioneer team + +OkHttp (https://square.github.io/okhttp/) + License: Apache License 2.0 + Copyright: Square, Inc. + +Gson (https://github.com/google/gson) + License: Apache License 2.0 + Copyright: Google Inc. + +Jackson (https://github.com/FasterXML/jackson) + License: Apache License 2.0 + Copyright: FasterXML, LLC + +SLF4J (https://www.slf4j.org/) + License: MIT License + Copyright: QOS.ch + +Lombok (https://projectlombok.org/) + License: MIT License + Copyright: Reinier Zwitserloot, Roel Spilker From 650672a7fe9d3f92012c827de69f94257d4f8f7c Mon Sep 17 00:00:00 2001 From: "zhansheng.lzs" Date: Tue, 7 Jul 2026 15:28:03 +0800 Subject: [PATCH 03/31] fix: remove hardcoded URL in TingWu to support DASHSCOPE_HTTP_BASE_URL env Replaced hardcoded DEFAULT_BASE_HTTP_URL with taskGroup/task/function based URL construction, allowing base URL override via environment variable DASHSCOPE_HTTP_BASE_URL for custom endpoints. Co-Authored-By: Claude Opus 4.7 --- .../com/alibaba/dashscope/multimodal/tingwu/TingWu.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/alibaba/dashscope/multimodal/tingwu/TingWu.java b/src/main/java/com/alibaba/dashscope/multimodal/tingwu/TingWu.java index fc865c05..236bc00f 100644 --- a/src/main/java/com/alibaba/dashscope/multimodal/tingwu/TingWu.java +++ b/src/main/java/com/alibaba/dashscope/multimodal/tingwu/TingWu.java @@ -12,15 +12,15 @@ public final class TingWu { private final SynchronizeHalfDuplexApi syncApi; private final ApiServiceOption serviceOption; - private final String DEFAULT_BASE_HTTP_URL = - "https://dashscope.aliyuncs.com/api/v1/services/aigc/multimodal-generation/generation"; private ApiServiceOption defaultApiServiceOption() { return ApiServiceOption.builder() .protocol(Protocol.HTTP) .httpMethod(HttpMethod.POST) - .isService(false) - .baseHttpUrl(DEFAULT_BASE_HTTP_URL) + .isService(true) + .taskGroup(TaskGroup.AIGC.getValue()) + .task(Task.MULTIMODAL_GENERATION.getValue()) + .function(Function.GENERATION.getValue()) .build(); } From 3f460b3beac2002f6531905886bfbb5d3ffe362a Mon Sep 17 00:00:00 2001 From: "zhansheng.lzs" Date: Tue, 7 Jul 2026 15:30:28 +0800 Subject: [PATCH 04/31] fix: remove hardcoded URL in TingWuUsage sample Use default TingWu constructor instead of hardcoding the base URL, consistent with the TingWu.java fix for environment variable support. Co-Authored-By: Claude Opus 4.7 --- samples/TingWuUsage.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/samples/TingWuUsage.java b/samples/TingWuUsage.java index 0aa78862..f7b2059b 100644 --- a/samples/TingWuUsage.java +++ b/samples/TingWuUsage.java @@ -25,9 +25,8 @@ public static Map buildCreateTask(String appId) { public static void main(String[] args) { try { - String baseWebsocketApiUrl = "https://dashscope.aliyuncs.com/api/v1/services/aigc/multimodal-generation/generation"; // 创建任务 - TingWu tingwu = new TingWu(Protocol.HTTP.getValue(),baseWebsocketApiUrl); + TingWu tingwu = new TingWu(); TingWuParam param = TingWuParam.builder(). model("tingwu-automotive-service-inspection") .input(buildCreateTask("123456")) From d2449e96beeed46b1489f28d89b32a50e82efd22 Mon Sep 17 00:00:00 2001 From: kevin Date: Tue, 7 Jul 2026 15:34:13 +0800 Subject: [PATCH 05/31] feat: validate mutual exclusivity of stopStrings and stopTokens --- .../dashscope/aigc/conversation/ConversationParam.java | 6 ++++++ .../alibaba/dashscope/aigc/generation/GenerationParam.java | 6 ++++++ .../multimodalconversation/MultiModalConversationParam.java | 6 ++++++ 3 files changed, 18 insertions(+) diff --git a/src/main/java/com/alibaba/dashscope/aigc/conversation/ConversationParam.java b/src/main/java/com/alibaba/dashscope/aigc/conversation/ConversationParam.java index 3d386844..d454741c 100644 --- a/src/main/java/com/alibaba/dashscope/aigc/conversation/ConversationParam.java +++ b/src/main/java/com/alibaba/dashscope/aigc/conversation/ConversationParam.java @@ -166,6 +166,12 @@ public Map getParameters() { if (maxTokens != null) { params.put(MAX_TOKENS, maxTokens); } + if (stopStrings != null + && !stopStrings.isEmpty() + && stopTokens != null + && !stopTokens.isEmpty()) { + throw new IllegalArgumentException("Only one of stopStrings or stopTokens can be specified."); + } if (stopStrings != null && !stopStrings.isEmpty()) { params.put(STOP, stopStrings); } else if (stopTokens != null && !stopTokens.isEmpty()) { diff --git a/src/main/java/com/alibaba/dashscope/aigc/generation/GenerationParam.java b/src/main/java/com/alibaba/dashscope/aigc/generation/GenerationParam.java index 17269c5a..f19c45cd 100644 --- a/src/main/java/com/alibaba/dashscope/aigc/generation/GenerationParam.java +++ b/src/main/java/com/alibaba/dashscope/aigc/generation/GenerationParam.java @@ -210,6 +210,12 @@ public Map getParameters() { if (maxTokens != null) { params.put(MAX_TOKENS, maxTokens); } + if (stopStrings != null + && !stopStrings.isEmpty() + && stopTokens != null + && !stopTokens.isEmpty()) { + throw new IllegalArgumentException("Only one of stopStrings or stopTokens can be specified."); + } if (stopStrings != null && !stopStrings.isEmpty()) { params.put(STOP, stopStrings); } else if (stopTokens != null && !stopTokens.isEmpty()) { diff --git a/src/main/java/com/alibaba/dashscope/aigc/multimodalconversation/MultiModalConversationParam.java b/src/main/java/com/alibaba/dashscope/aigc/multimodalconversation/MultiModalConversationParam.java index 2ab9490e..7e38625e 100644 --- a/src/main/java/com/alibaba/dashscope/aigc/multimodalconversation/MultiModalConversationParam.java +++ b/src/main/java/com/alibaba/dashscope/aigc/multimodalconversation/MultiModalConversationParam.java @@ -336,6 +336,12 @@ public Map getParameters() { params.put("thinking_budget", thinkingBudget); } + if (stopStrings != null + && !stopStrings.isEmpty() + && stopTokens != null + && !stopTokens.isEmpty()) { + throw new IllegalArgumentException("Only one of stopStrings or stopTokens can be specified."); + } if (stopStrings != null && !stopStrings.isEmpty()) { params.put(ApiKeywords.STOP, stopStrings); } else if (stopTokens != null && !stopTokens.isEmpty()) { From 000f3db2e0b7df0d7987184ef494cf8f193cf9a6 Mon Sep 17 00:00:00 2001 From: kevin Date: Tue, 7 Jul 2026 16:01:31 +0800 Subject: [PATCH 06/31] feat: validate mutual exclusivity of stopStrings and stopTokens Add IllegalArgumentException when both stopStrings and stopTokens are configured simultaneously in ConversationParam, GenerationParam, and MultiModalConversationParam to prevent silent parameter override. Previously, stopTokens was silently ignored when both parameters were set due to if-else structure. Now the SDK throws a clear error to help users debug their configuration. --- .pre-commit-config.yaml | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 .pre-commit-config.yaml diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 00000000..a5468300 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,38 @@ +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.3.0 + hooks: + - id: trailing-whitespace + exclude: ^(tests/legacy|samples)/ + - id: check-yaml + exclude: | + (?x)^( + meta.yaml + | tests/legacy/ + | samples/ + )$ + - id: check-xml + exclude: ^(tests/legacy|samples)/ + - id: check-json + exclude: ^(tests/legacy|samples)/ + - id: detect-private-key + exclude: ^(tests/legacy|samples)/ + - id: end-of-file-fixer + exclude: ^(tests/legacy|samples)/ + - id: check-merge-conflict + exclude: ^(tests/legacy|samples)/ + - id: check-added-large-files + args: ['--maxkb=1024'] + exclude: ^(tests/legacy|samples)/ + - id: no-commit-to-branch + args: ['--branch', 'main', '--branch', 'master'] + + # Google Java Format for Java code formatting + - repo: local + hooks: + - id: google-java-format + name: Google Java Format + entry: bash lint.sh + language: system + files: \.java$ + pass_filenames: false From cc1a48716b3f72f1b8ce9c84fcf3cd96a67b994e Mon Sep 17 00:00:00 2001 From: kevin Lu Date: Tue, 7 Jul 2026 16:11:42 +0800 Subject: [PATCH 07/31] Delete .pre-commit-config.yaml --- .pre-commit-config.yaml | 38 -------------------------------------- 1 file changed, 38 deletions(-) delete mode 100644 .pre-commit-config.yaml diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml deleted file mode 100644 index a5468300..00000000 --- a/.pre-commit-config.yaml +++ /dev/null @@ -1,38 +0,0 @@ -repos: - - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.3.0 - hooks: - - id: trailing-whitespace - exclude: ^(tests/legacy|samples)/ - - id: check-yaml - exclude: | - (?x)^( - meta.yaml - | tests/legacy/ - | samples/ - )$ - - id: check-xml - exclude: ^(tests/legacy|samples)/ - - id: check-json - exclude: ^(tests/legacy|samples)/ - - id: detect-private-key - exclude: ^(tests/legacy|samples)/ - - id: end-of-file-fixer - exclude: ^(tests/legacy|samples)/ - - id: check-merge-conflict - exclude: ^(tests/legacy|samples)/ - - id: check-added-large-files - args: ['--maxkb=1024'] - exclude: ^(tests/legacy|samples)/ - - id: no-commit-to-branch - args: ['--branch', 'main', '--branch', 'master'] - - # Google Java Format for Java code formatting - - repo: local - hooks: - - id: google-java-format - name: Google Java Format - entry: bash lint.sh - language: system - files: \.java$ - pass_filenames: false From a2e45658a384a0cb6f1d392e65c105ca9fc6940b Mon Sep 17 00:00:00 2001 From: kevin Date: Thu, 9 Jul 2026 15:22:37 +0800 Subject: [PATCH 08/31] chore: bump version to 2.22.25 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 14f0b2a2..ebf308ab 100644 --- a/pom.xml +++ b/pom.xml @@ -40,7 +40,7 @@ DashScope Java SDK com.alibaba dashscope-sdk-java - 2.22.24 + 2.22.25 8 From 7f4a930eddd9072fd47e47ee514a80611938322f Mon Sep 17 00:00:00 2001 From: kevin Date: Fri, 10 Jul 2026 10:53:19 +0800 Subject: [PATCH 09/31] fix: remove trailing whitespace and apply reasoningContent/content mutual exclusion rule --- .../dashscope/aigc/generation/Generation.java | 19 +++++++++++-------- .../MultiModalConversation.java | 13 ++++++------- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/src/main/java/com/alibaba/dashscope/aigc/generation/Generation.java b/src/main/java/com/alibaba/dashscope/aigc/generation/Generation.java index d96c6188..2d7811f0 100644 --- a/src/main/java/com/alibaba/dashscope/aigc/generation/Generation.java +++ b/src/main/java/com/alibaba/dashscope/aigc/generation/Generation.java @@ -351,19 +351,20 @@ private GenerationResult mergeSingleResponse( if (currentContent != null && !currentContent.isEmpty()) { accumulated.content.append(currentContent); } - // Always set the accumulated content if we have any - if (accumulated.content.length() > 0) { - choice.getMessage().setContent(accumulated.content.toString()); - } // Handle reasoning_content accumulation String currentReasoningContent = choice.getMessage().getReasoningContent(); if (currentReasoningContent != null && !currentReasoningContent.isEmpty()) { accumulated.reasoningContent.append(currentReasoningContent); } - // Always set the accumulated reasoning_content if we have any + + // Apply mutual exclusion rule: when reasoningContent exists, content should be empty if (accumulated.reasoningContent.length() > 0) { choice.getMessage().setReasoningContent(accumulated.reasoningContent.toString()); + // Clear content when reasoningContent is present + choice.getMessage().setContent(null); + } else if (accumulated.content.length() > 0) { + choice.getMessage().setContent(accumulated.content.toString()); } // Handle tool_calls accumulation @@ -474,11 +475,13 @@ private GenerationResult mergeSingleResponse( com.alibaba.dashscope.common.Message message = new com.alibaba.dashscope.common.Message(); message.setRole("assistant"); - if (data.content.length() > 0) { - message.setContent(data.content.toString()); - } + // Apply mutual exclusion rule: when reasoningContent exists, content should be empty if (data.reasoningContent.length() > 0) { message.setReasoningContent(data.reasoningContent.toString()); + // Clear content when reasoningContent is present + message.setContent(null); + } else if (data.content.length() > 0) { + message.setContent(data.content.toString()); } if (!data.toolCalls.isEmpty()) { message.setToolCalls(data.toolCalls); diff --git a/src/main/java/com/alibaba/dashscope/aigc/multimodalconversation/MultiModalConversation.java b/src/main/java/com/alibaba/dashscope/aigc/multimodalconversation/MultiModalConversation.java index a46a0081..4fc4ee4c 100644 --- a/src/main/java/com/alibaba/dashscope/aigc/multimodalconversation/MultiModalConversation.java +++ b/src/main/java/com/alibaba/dashscope/aigc/multimodalconversation/MultiModalConversation.java @@ -328,22 +328,21 @@ private MultiModalConversationResult mergeSingleResponse( if (currentContent != null && !currentContent.isEmpty()) { mergeTextContent(currentContent, accumulated); } - // Always set the accumulated content if we have any - if (!accumulated.content.isEmpty()) { - choice.getMessage().setContent(accumulated.content); - } // Handle reasoning_content accumulation String currentReasoningContent = choice.getMessage().getReasoningContent(); if (currentReasoningContent != null && !currentReasoningContent.isEmpty()) { accumulated.reasoningContent.append(currentReasoningContent); } - // Always set the accumulated reasoning_content if we have any + + // Apply mutual exclusion rule: when reasoningContent exists, content should be empty if (accumulated.reasoningContent.length() > 0) { choice.getMessage().setReasoningContent(accumulated.reasoningContent.toString()); + // Clear content when reasoningContent is present + choice.getMessage().setContent(null); + } else if (!accumulated.content.isEmpty()) { + choice.getMessage().setContent(accumulated.content); } - - // Handle tool_calls accumulation List currentToolCalls = choice.getMessage().getToolCalls(); if (currentToolCalls != null && !currentToolCalls.isEmpty()) { mergeToolCalls(currentToolCalls, accumulated.toolCalls); From 2c640e146b424d9c062a7aec2ea89c48c0f74f73 Mon Sep 17 00:00:00 2001 From: kevin Date: Fri, 10 Jul 2026 11:46:10 +0800 Subject: [PATCH 10/31] feat: support quark_search and code_interpreter tool calls in MultiModalMessageAdapter --- .../common/MultiModalMessageAdapter.java | 161 ++++++++++++++---- 1 file changed, 126 insertions(+), 35 deletions(-) diff --git a/src/main/java/com/alibaba/dashscope/common/MultiModalMessageAdapter.java b/src/main/java/com/alibaba/dashscope/common/MultiModalMessageAdapter.java index 9327ea6c..6717f643 100644 --- a/src/main/java/com/alibaba/dashscope/common/MultiModalMessageAdapter.java +++ b/src/main/java/com/alibaba/dashscope/common/MultiModalMessageAdapter.java @@ -13,9 +13,12 @@ import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonWriter; import java.io.IOException; +import java.math.BigDecimal; +import java.math.BigInteger; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; +import java.util.HashMap; import java.util.List; import java.util.Map; @@ -37,14 +40,18 @@ private void writeValue(JsonWriter out, Object value) throws IOException { out.nullValue(); } else if (value instanceof String) { out.value((String) value); - } else if (value instanceof Integer) { - out.value((Integer) value); - } else if (value instanceof Long) { - out.value((Long) value); - } else if (value instanceof Double) { - out.value((Double) value); - } else if (value instanceof Float) { - out.value((Float) value); + } else if (value instanceof Integer || value instanceof Long) { + out.value(((Number) value).longValue()); + } else if (value instanceof Float || value instanceof Double) { + out.value(((Number) value).doubleValue()); + } else if (value instanceof Short || value instanceof Byte) { + out.value(((Number) value).intValue()); + } else if (value instanceof BigDecimal) { + // Use jsonValue to write as JSON number, not string + out.jsonValue(((BigDecimal) value).toPlainString()); + } else if (value instanceof BigInteger) { + // Use jsonValue to write as JSON number, not string + out.jsonValue(((BigInteger) value).toString()); } else if (value instanceof Boolean) { out.value((Boolean) value); } else if (value instanceof Character) { @@ -59,8 +66,9 @@ private void writeValue(JsonWriter out, Object value) throws IOException { } else if (value instanceof Map) { writeMapObject(out, (Map) value); } else { - // Fallback for other types - out.value(value.toString()); + // For unsupported types, serialize using Gson to JSON string + String jsonStr = JsonUtils.toJson(value); + out.jsonValue(jsonStr); } } @@ -95,8 +103,8 @@ private void writeToolCallBase(JsonWriter writer, ToolCallBase toolCallBase) thr } writer.endObject(); } else if (toolCallBase instanceof ToolCallCodeInterpreter) { - // For ToolCallCodeInterpreter no extra fields besides id and type - // Any additional fields specific to this should be written here. + // ToolCallCodeInterpreter only has id, type, and index fields + // id and type are already written above, index is handled in common fields } writer.endObject(); @@ -113,10 +121,13 @@ private ToolCallFunction convertToCallFunction(LinkedTreeMap too callFunction.setName(fc.get("name").toString()); } if (fc.containsKey("arguments")) { - callFunction.setArguments(fc.get("arguments").toString()); + Object args = fc.get("arguments"); + callFunction.setArguments(args instanceof String ? (String) args : JsonUtils.toJson(args)); } if (fc.containsKey("output")) { - callFunction.setOutput(fc.get("output").toString()); + Object output = fc.get("output"); + callFunction.setOutput( + output instanceof String ? (String) output : JsonUtils.toJson(output)); } functionCall.setFunction(callFunction); } @@ -133,18 +144,89 @@ private ToolCallFunction convertToCallFunction(LinkedTreeMap too return functionCall; } + // Convert LinkedTreeMap to ToolCallQuarkSearch + @SuppressWarnings("unchecked") + private ToolCallQuarkSearch convertToQuarkSearch(LinkedTreeMap toolCall) { + ToolCallQuarkSearch quarkSearch = new ToolCallQuarkSearch(); + if (toolCall.containsKey("quark_search")) { + LinkedTreeMap qs = + (LinkedTreeMap) toolCall.get("quark_search"); + Map searchParams = new HashMap<>(); + for (Map.Entry entry : qs.entrySet()) { + Object val = entry.getValue(); + if (val instanceof String) { + searchParams.put(entry.getKey(), (String) val); + } else { + // For non-string types, serialize to JSON string + searchParams.put(entry.getKey(), JsonUtils.toJson(val)); + } + } + quarkSearch.setQuarkSearch(searchParams); + } + quarkSearch.setType(toolCall.get("type").toString()); + if (toolCall.containsKey("id")) { + quarkSearch.setId(toolCall.get("id").toString()); + } + if (toolCall.containsKey("index")) { + Object indexObj = toolCall.get("index"); + if (indexObj instanceof Number) { + quarkSearch.setIndex(((Number) indexObj).intValue()); + } + } + return quarkSearch; + } + + // Convert LinkedTreeMap to ToolCallCodeInterpreter + @SuppressWarnings("unchecked") + private ToolCallCodeInterpreter convertToCodeInterpreter(LinkedTreeMap toolCall) { + ToolCallCodeInterpreter codeInterpreter = new ToolCallCodeInterpreter(); + codeInterpreter.setType(toolCall.get("type").toString()); + if (toolCall.containsKey("id")) { + codeInterpreter.setId(toolCall.get("id").toString()); + } + if (toolCall.containsKey("index")) { + Object indexObj = toolCall.get("index"); + if (indexObj instanceof Number) { + codeInterpreter.setIndex(((Number) indexObj).intValue()); + } + } + return codeInterpreter; + } + + // Generic method to convert LinkedTreeMap to appropriate ToolCallBase subclass + @SuppressWarnings("unchecked") + private ToolCallBase convertToToolCall(LinkedTreeMap toolCall) { + if (!toolCall.containsKey("type")) { + throw new IllegalArgumentException("Tool call must contain 'type' field"); + } + + String type = toolCall.get("type").toString(); + switch (type) { + case "function": + return convertToCallFunction(toolCall); + case "quark_search": + return convertToQuarkSearch(toolCall); + case "code_interpreter": + return convertToCodeInterpreter(toolCall); + default: + throw new IllegalArgumentException("Unknown tool call type: " + type); + } + } + @Override public void write(JsonWriter out, MultiModalMessage value) throws IOException { out.beginObject(); out.name(ApiKeywords.ROLE); out.value(value.getRole()); - out.name(ApiKeywords.CONTENT); - out.beginArray(); - for (Map item : value.getContent()) { - writeMapObject(out, item); + if (value.getContent() != null) { + out.name(ApiKeywords.CONTENT); + out.beginArray(); + for (Map item : value.getContent()) { + writeMapObject(out, item); + } + out.endArray(); } - out.endArray(); if (value.getAnnotations() != null) { out.name(ApiKeywords.ANNOTATIONS); @@ -164,8 +246,7 @@ public void write(JsonWriter out, MultiModalMessage value) throws IOException { out.name(ApiKeywords.TOOL_CALLS); out.beginArray(); List toolCalls = value.getToolCalls(); - for (ToolCallBase tc : - JsonUtils.fromJson(JsonUtils.toJson(toolCalls), ToolCallBase[].class)) { + for (ToolCallBase tc : toolCalls) { writeToolCallBase(out, tc); } out.endArray(); @@ -199,20 +280,37 @@ public MultiModalMessage read(JsonReader in) throws IOException { Object content = objectMap.get(ApiKeywords.CONTENT); if (content instanceof String) { msg.setContent(Arrays.asList(Collections.singletonMap("text", (String) content))); - } else { + } else if (content instanceof List) { msg.setContent((List>) content); + } else { + throw new IllegalArgumentException( + "Content must be String or List, got: " + + (content != null ? content.getClass().getName() : "null")); } objectMap.remove(ApiKeywords.CONTENT); } if (objectMap.containsKey(ApiKeywords.ANNOTATIONS)) { - msg.setAnnotations((List>) objectMap.get(ApiKeywords.ANNOTATIONS)); + Object annotations = objectMap.get(ApiKeywords.ANNOTATIONS); + if (annotations instanceof List) { + msg.setAnnotations((List>) annotations); + } else { + throw new IllegalArgumentException( + "Annotations must be List, got: " + + (annotations != null ? annotations.getClass().getName() : "null")); + } objectMap.remove(ApiKeywords.ANNOTATIONS); } if (objectMap.containsKey(ApiKeywords.REASONING_CONTENT)) { - String reasoningContent = (String) objectMap.get(ApiKeywords.REASONING_CONTENT); - msg.setReasoningContent(reasoningContent); + Object reasoningContent = objectMap.get(ApiKeywords.REASONING_CONTENT); + if (reasoningContent instanceof String) { + msg.setReasoningContent((String) reasoningContent); + } else { + throw new IllegalArgumentException( + "Reasoning content must be String, got: " + + (reasoningContent != null ? reasoningContent.getClass().getName() : "null")); + } objectMap.remove(ApiKeywords.REASONING_CONTENT); } @@ -223,22 +321,15 @@ public MultiModalMessage read(JsonReader in) throws IOException { // Check if need conversion for function type boolean needConversion = false; if (!toolCallsList.isEmpty() && toolCallsList.get(0) instanceof LinkedTreeMap) { - LinkedTreeMap firstToolCall = - (LinkedTreeMap) toolCallsList.get(0); - if (firstToolCall.containsKey("type")) { - String type = firstToolCall.get("type").toString(); - if (type.equals("function")) { - needConversion = true; - } - } + needConversion = true; } if (needConversion) { - // Convert LinkedTreeMap to ToolCallFunction + // Convert LinkedTreeMap to appropriate ToolCallBase subclass msg.toolCalls = new ArrayList(); List toolCalls = (List) toolCallsObj; for (LinkedTreeMap toolCall : toolCalls) { - msg.toolCalls.add(convertToCallFunction(toolCall)); + msg.toolCalls.add(convertToToolCall(toolCall)); } } else { // Use original method for non-function types From f9f5fecf3db34496bf5b3cf50c03884729af2237 Mon Sep 17 00:00:00 2001 From: kevin Date: Fri, 10 Jul 2026 14:30:40 +0800 Subject: [PATCH 11/31] fix: allow reasoning_content and content to coexist in streaming responses --- .../dashscope/aigc/generation/Generation.java | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/main/java/com/alibaba/dashscope/aigc/generation/Generation.java b/src/main/java/com/alibaba/dashscope/aigc/generation/Generation.java index 2d7811f0..6c16cae4 100644 --- a/src/main/java/com/alibaba/dashscope/aigc/generation/Generation.java +++ b/src/main/java/com/alibaba/dashscope/aigc/generation/Generation.java @@ -358,12 +358,11 @@ private GenerationResult mergeSingleResponse( accumulated.reasoningContent.append(currentReasoningContent); } - // Apply mutual exclusion rule: when reasoningContent exists, content should be empty + // Accumulate both reasoning_content and content independently if (accumulated.reasoningContent.length() > 0) { choice.getMessage().setReasoningContent(accumulated.reasoningContent.toString()); - // Clear content when reasoningContent is present - choice.getMessage().setContent(null); - } else if (accumulated.content.length() > 0) { + } + if (accumulated.content.length() > 0) { choice.getMessage().setContent(accumulated.content.toString()); } @@ -475,12 +474,11 @@ private GenerationResult mergeSingleResponse( com.alibaba.dashscope.common.Message message = new com.alibaba.dashscope.common.Message(); message.setRole("assistant"); - // Apply mutual exclusion rule: when reasoningContent exists, content should be empty + // Set both reasoning_content and content independently if (data.reasoningContent.length() > 0) { message.setReasoningContent(data.reasoningContent.toString()); - // Clear content when reasoningContent is present - message.setContent(null); - } else if (data.content.length() > 0) { + } + if (data.content.length() > 0) { message.setContent(data.content.toString()); } if (!data.toolCalls.isEmpty()) { From 2112b1daef0378471436023a8242d9105d9aabdd Mon Sep 17 00:00:00 2001 From: kevin Date: Fri, 10 Jul 2026 15:23:38 +0800 Subject: [PATCH 12/31] fix: allow reasoning_content and content to coexist in streaming responses --- .../multimodalconversation/MultiModalConversation.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/alibaba/dashscope/aigc/multimodalconversation/MultiModalConversation.java b/src/main/java/com/alibaba/dashscope/aigc/multimodalconversation/MultiModalConversation.java index 4fc4ee4c..c34fff03 100644 --- a/src/main/java/com/alibaba/dashscope/aigc/multimodalconversation/MultiModalConversation.java +++ b/src/main/java/com/alibaba/dashscope/aigc/multimodalconversation/MultiModalConversation.java @@ -335,12 +335,12 @@ private MultiModalConversationResult mergeSingleResponse( accumulated.reasoningContent.append(currentReasoningContent); } - // Apply mutual exclusion rule: when reasoningContent exists, content should be empty + // Set both reasoning_content and content (they are not mutually exclusive) if (accumulated.reasoningContent.length() > 0) { choice.getMessage().setReasoningContent(accumulated.reasoningContent.toString()); - // Clear content when reasoningContent is present - choice.getMessage().setContent(null); - } else if (!accumulated.content.isEmpty()) { + } + // Always set accumulated content if available + if (!accumulated.content.isEmpty()) { choice.getMessage().setContent(accumulated.content); } List currentToolCalls = choice.getMessage().getToolCalls(); From 45b40009414d6bb0f2df8e247908a8597f0ad410 Mon Sep 17 00:00:00 2001 From: kevin Date: Fri, 10 Jul 2026 16:17:14 +0800 Subject: [PATCH 13/31] fix: prevent automatic incremental_output injection to avoid response merging issues --- .../dashscope/aigc/conversation/ConversationParam.java | 2 +- .../multimodalconversation/MultiModalConversationParam.java | 4 ++-- src/main/java/com/alibaba/dashscope/app/ApplicationParam.java | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/alibaba/dashscope/aigc/conversation/ConversationParam.java b/src/main/java/com/alibaba/dashscope/aigc/conversation/ConversationParam.java index d454741c..a2e92191 100644 --- a/src/main/java/com/alibaba/dashscope/aigc/conversation/ConversationParam.java +++ b/src/main/java/com/alibaba/dashscope/aigc/conversation/ConversationParam.java @@ -88,7 +88,7 @@ public static class ResultFormat { * apple * */ - @Builder.Default private Boolean incrementalOutput = false; + private Boolean incrementalOutput = null; /* * Maximum tokens to generate. */ diff --git a/src/main/java/com/alibaba/dashscope/aigc/multimodalconversation/MultiModalConversationParam.java b/src/main/java/com/alibaba/dashscope/aigc/multimodalconversation/MultiModalConversationParam.java index 7e38625e..2b969abc 100644 --- a/src/main/java/com/alibaba/dashscope/aigc/multimodalconversation/MultiModalConversationParam.java +++ b/src/main/java/com/alibaba/dashscope/aigc/multimodalconversation/MultiModalConversationParam.java @@ -91,7 +91,7 @@ public class MultiModalConversationParam extends HalfDuplexServiceParam { /** * Used to control the streaming output mode. If true, the subsequent output will include the * previously input content by default. Otherwise, the subsequent output will not include the - * previously output content. Default: false eg(false): + * previously output content. Default: null eg(false): * *
    * I
@@ -103,7 +103,7 @@ public class MultiModalConversationParam extends HalfDuplexServiceParam {
    * apple
    * 
*/ - @Builder.Default private Boolean incrementalOutput = false; + private Boolean incrementalOutput = null; /** Output format of the model including "text" and "audio". Default value: ["text"] */ private List modalities; diff --git a/src/main/java/com/alibaba/dashscope/app/ApplicationParam.java b/src/main/java/com/alibaba/dashscope/app/ApplicationParam.java index 143d0739..6eb12338 100644 --- a/src/main/java/com/alibaba/dashscope/app/ApplicationParam.java +++ b/src/main/java/com/alibaba/dashscope/app/ApplicationParam.java @@ -89,9 +89,9 @@ public class ApplicationParam extends HalfDuplexParamBase { /** * Used to control the streaming output mode. If true, the subsequent output will include the * previously input content by default. Otherwise, the subsequent output will not include the - * previously output content. Default: false. + * previously output content. Default: null. */ - @Builder.Default private Boolean incrementalOutput = false; + private Boolean incrementalOutput = null; /** * Long term memory id is used to store long term context summary between end users and assistant. From 6f4c8400d13c7f4c477a61042e7e57830f3e2f55 Mon Sep 17 00:00:00 2001 From: kevin Date: Fri, 10 Jul 2026 17:31:01 +0800 Subject: [PATCH 14/31] fix ConversationParam.java --- .../dashscope/aigc/conversation/ConversationParam.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/alibaba/dashscope/aigc/conversation/ConversationParam.java b/src/main/java/com/alibaba/dashscope/aigc/conversation/ConversationParam.java index a2e92191..929b0f89 100644 --- a/src/main/java/com/alibaba/dashscope/aigc/conversation/ConversationParam.java +++ b/src/main/java/com/alibaba/dashscope/aigc/conversation/ConversationParam.java @@ -177,7 +177,14 @@ public Map getParameters() { } else if (stopTokens != null && !stopTokens.isEmpty()) { params.put(STOP, stopTokens); } - params.putAll(parameters); + if (parameters != null) { + parameters.forEach( + (k, v) -> { + if (v != null) { + params.put(k, v); + } + }); + } return params; } From 1714e88591e57e87d7e61f7c2753a9f412083da9 Mon Sep 17 00:00:00 2001 From: kevin Date: Fri, 10 Jul 2026 18:14:03 +0800 Subject: [PATCH 15/31] fix GenerationStreamCall.java --- samples/GenerationStreamCall.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/samples/GenerationStreamCall.java b/samples/GenerationStreamCall.java index 4aadd8e4..43092eb9 100644 --- a/samples/GenerationStreamCall.java +++ b/samples/GenerationStreamCall.java @@ -41,7 +41,12 @@ public static void streamCall() .repetitionPenalty((float) 1.0) .topK(50) .build(); - System.out.println(param.getHttpBody().toString()); + JsonObject body = param.getHttpBody(); + if (body != null) { + System.out.println(body.toString()); + } else { + System.out.println("param body is null"); + } Generation generation = new Generation(); Flowable flowable = generation.streamCall(param); flowable.blockingForEach(message -> { From b84fb587177086e0db8b246ce05904ad7ebcf5cf Mon Sep 17 00:00:00 2001 From: kevin Date: Fri, 10 Jul 2026 19:46:43 +0800 Subject: [PATCH 16/31] fix: prevent NPE when parameters map is null and improve stream error logging --- .../java/com/alibaba/dashscope/base/HalfDuplexServiceParam.java | 2 +- .../dashscope/protocol/okhttp/OkHttpWebSocketClient.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/alibaba/dashscope/base/HalfDuplexServiceParam.java b/src/main/java/com/alibaba/dashscope/base/HalfDuplexServiceParam.java index f44f0401..90900078 100644 --- a/src/main/java/com/alibaba/dashscope/base/HalfDuplexServiceParam.java +++ b/src/main/java/com/alibaba/dashscope/base/HalfDuplexServiceParam.java @@ -25,7 +25,7 @@ public String getModel() { @Override public Map getParameters() { - return parameters; + return parameters != null ? parameters : new HashMap<>(); } @Override diff --git a/src/main/java/com/alibaba/dashscope/protocol/okhttp/OkHttpWebSocketClient.java b/src/main/java/com/alibaba/dashscope/protocol/okhttp/OkHttpWebSocketClient.java index c91bd363..9133a9ee 100644 --- a/src/main/java/com/alibaba/dashscope/protocol/okhttp/OkHttpWebSocketClient.java +++ b/src/main/java/com/alibaba/dashscope/protocol/okhttp/OkHttpWebSocketClient.java @@ -694,7 +694,7 @@ protected void executeStreamRequest(FullDuplexRequest req) { } }, err -> { - log.error(StringUtils.format("Get stream data error!")); + log.error(StringUtils.format("Get stream data error: %s", err.getMessage())); if (responseEmitter != null && !responseEmitter.isCancelled()) { responseEmitter.onError(err); } From 4e8b6bf8e77e2ab593fc642beeda79c322f4e3b5 Mon Sep 17 00:00:00 2001 From: kevin Date: Mon, 13 Jul 2026 11:48:11 +0800 Subject: [PATCH 17/31] fix: initialize parameters and headers maps in HalfDuplexParamBase to prevent NPE --- .../java/com/alibaba/dashscope/base/HalfDuplexParamBase.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/alibaba/dashscope/base/HalfDuplexParamBase.java b/src/main/java/com/alibaba/dashscope/base/HalfDuplexParamBase.java index dd7e6427..2998162f 100644 --- a/src/main/java/com/alibaba/dashscope/base/HalfDuplexParamBase.java +++ b/src/main/java/com/alibaba/dashscope/base/HalfDuplexParamBase.java @@ -6,6 +6,7 @@ import com.alibaba.dashscope.utils.GsonExclude; import com.google.gson.JsonObject; import java.nio.ByteBuffer; +import java.util.HashMap; import java.util.Map; import lombok.Builder; import lombok.Data; @@ -27,7 +28,7 @@ public abstract class HalfDuplexParamBase { @GsonExclude @Builder.Default private Boolean enableEncrypt = false; /** The extra parameters. */ - @GsonExclude @Singular protected Map parameters; + @GsonExclude @Builder.Default protected Map parameters = new HashMap<>(); public abstract String getModel(); /** @@ -38,7 +39,7 @@ public abstract class HalfDuplexParamBase { public abstract Map getParameters(); /** The custom http header. */ - @GsonExclude @Singular protected Map headers; + @GsonExclude @Builder.Default protected Map headers = new HashMap<>(); /** * The custom http header. From 541ad3b69f4d05a36cff4d4a53bbcf03153d7314 Mon Sep 17 00:00:00 2001 From: kevin Date: Mon, 13 Jul 2026 11:50:07 +0800 Subject: [PATCH 18/31] fix: initialize parameters and headers maps in HalfDuplexParamBase to prevent NPE --- .../java/com/alibaba/dashscope/base/HalfDuplexParamBase.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/com/alibaba/dashscope/base/HalfDuplexParamBase.java b/src/main/java/com/alibaba/dashscope/base/HalfDuplexParamBase.java index 2998162f..858c3e0e 100644 --- a/src/main/java/com/alibaba/dashscope/base/HalfDuplexParamBase.java +++ b/src/main/java/com/alibaba/dashscope/base/HalfDuplexParamBase.java @@ -10,7 +10,6 @@ import java.util.Map; import lombok.Builder; import lombok.Data; -import lombok.Singular; import lombok.experimental.SuperBuilder; /** The user input and parameter. */ From d667ac8e886a2e30a09a1c534e7da8a07fa3df55 Mon Sep 17 00:00:00 2001 From: kevin Date: Mon, 13 Jul 2026 11:56:14 +0800 Subject: [PATCH 19/31] fix: initialize parameters and headers maps in HalfDuplexParamBase to prevent NPE --- .../java/com/alibaba/dashscope/base/HalfDuplexParamBase.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/alibaba/dashscope/base/HalfDuplexParamBase.java b/src/main/java/com/alibaba/dashscope/base/HalfDuplexParamBase.java index 858c3e0e..3f4e74f1 100644 --- a/src/main/java/com/alibaba/dashscope/base/HalfDuplexParamBase.java +++ b/src/main/java/com/alibaba/dashscope/base/HalfDuplexParamBase.java @@ -10,6 +10,7 @@ import java.util.Map; import lombok.Builder; import lombok.Data; +import lombok.Singular; import lombok.experimental.SuperBuilder; /** The user input and parameter. */ @@ -27,7 +28,7 @@ public abstract class HalfDuplexParamBase { @GsonExclude @Builder.Default private Boolean enableEncrypt = false; /** The extra parameters. */ - @GsonExclude @Builder.Default protected Map parameters = new HashMap<>(); + @GsonExclude @Singular protected Map parameters = new HashMap<>(); public abstract String getModel(); /** @@ -38,7 +39,7 @@ public abstract class HalfDuplexParamBase { public abstract Map getParameters(); /** The custom http header. */ - @GsonExclude @Builder.Default protected Map headers = new HashMap<>(); + @GsonExclude @Singular protected Map headers = new HashMap<>(); /** * The custom http header. From 6e83e47a4e8c8cc7391a7dce2c5f23d41d629363 Mon Sep 17 00:00:00 2001 From: kevin Date: Mon, 13 Jul 2026 14:55:39 +0800 Subject: [PATCH 20/31] Revert "fix: initialize parameters and headers maps in HalfDuplexParamBase to prevent NPE" This reverts commit d667ac8e886a2e30a09a1c534e7da8a07fa3df55. --- .../java/com/alibaba/dashscope/base/HalfDuplexParamBase.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/alibaba/dashscope/base/HalfDuplexParamBase.java b/src/main/java/com/alibaba/dashscope/base/HalfDuplexParamBase.java index 3f4e74f1..858c3e0e 100644 --- a/src/main/java/com/alibaba/dashscope/base/HalfDuplexParamBase.java +++ b/src/main/java/com/alibaba/dashscope/base/HalfDuplexParamBase.java @@ -10,7 +10,6 @@ import java.util.Map; import lombok.Builder; import lombok.Data; -import lombok.Singular; import lombok.experimental.SuperBuilder; /** The user input and parameter. */ @@ -28,7 +27,7 @@ public abstract class HalfDuplexParamBase { @GsonExclude @Builder.Default private Boolean enableEncrypt = false; /** The extra parameters. */ - @GsonExclude @Singular protected Map parameters = new HashMap<>(); + @GsonExclude @Builder.Default protected Map parameters = new HashMap<>(); public abstract String getModel(); /** @@ -39,7 +38,7 @@ public abstract class HalfDuplexParamBase { public abstract Map getParameters(); /** The custom http header. */ - @GsonExclude @Singular protected Map headers = new HashMap<>(); + @GsonExclude @Builder.Default protected Map headers = new HashMap<>(); /** * The custom http header. From 380f226800819c9d0de415437f9862f7b9cb5848 Mon Sep 17 00:00:00 2001 From: kevin Date: Mon, 13 Jul 2026 14:55:39 +0800 Subject: [PATCH 21/31] Revert "fix: initialize parameters and headers maps in HalfDuplexParamBase to prevent NPE" This reverts commit 541ad3b69f4d05a36cff4d4a53bbcf03153d7314. --- .../java/com/alibaba/dashscope/base/HalfDuplexParamBase.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/com/alibaba/dashscope/base/HalfDuplexParamBase.java b/src/main/java/com/alibaba/dashscope/base/HalfDuplexParamBase.java index 858c3e0e..2998162f 100644 --- a/src/main/java/com/alibaba/dashscope/base/HalfDuplexParamBase.java +++ b/src/main/java/com/alibaba/dashscope/base/HalfDuplexParamBase.java @@ -10,6 +10,7 @@ import java.util.Map; import lombok.Builder; import lombok.Data; +import lombok.Singular; import lombok.experimental.SuperBuilder; /** The user input and parameter. */ From d170341d481646067c7fe5e1177edb0bb30637cd Mon Sep 17 00:00:00 2001 From: kevin Date: Mon, 13 Jul 2026 14:55:39 +0800 Subject: [PATCH 22/31] Revert "fix: initialize parameters and headers maps in HalfDuplexParamBase to prevent NPE" This reverts commit 4e8b6bf8e77e2ab593fc642beeda79c322f4e3b5. --- .../java/com/alibaba/dashscope/base/HalfDuplexParamBase.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/alibaba/dashscope/base/HalfDuplexParamBase.java b/src/main/java/com/alibaba/dashscope/base/HalfDuplexParamBase.java index 2998162f..dd7e6427 100644 --- a/src/main/java/com/alibaba/dashscope/base/HalfDuplexParamBase.java +++ b/src/main/java/com/alibaba/dashscope/base/HalfDuplexParamBase.java @@ -6,7 +6,6 @@ import com.alibaba.dashscope.utils.GsonExclude; import com.google.gson.JsonObject; import java.nio.ByteBuffer; -import java.util.HashMap; import java.util.Map; import lombok.Builder; import lombok.Data; @@ -28,7 +27,7 @@ public abstract class HalfDuplexParamBase { @GsonExclude @Builder.Default private Boolean enableEncrypt = false; /** The extra parameters. */ - @GsonExclude @Builder.Default protected Map parameters = new HashMap<>(); + @GsonExclude @Singular protected Map parameters; public abstract String getModel(); /** @@ -39,7 +38,7 @@ public abstract class HalfDuplexParamBase { public abstract Map getParameters(); /** The custom http header. */ - @GsonExclude @Builder.Default protected Map headers = new HashMap<>(); + @GsonExclude @Singular protected Map headers; /** * The custom http header. From e1d7b1ea5c4104fb5e439e04e17c607508d26ee2 Mon Sep 17 00:00:00 2001 From: kevin Date: Mon, 13 Jul 2026 14:55:39 +0800 Subject: [PATCH 23/31] Revert "fix: prevent NPE when parameters map is null and improve stream error logging" This reverts commit b84fb587177086e0db8b246ce05904ad7ebcf5cf. --- .../java/com/alibaba/dashscope/base/HalfDuplexServiceParam.java | 2 +- .../dashscope/protocol/okhttp/OkHttpWebSocketClient.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/alibaba/dashscope/base/HalfDuplexServiceParam.java b/src/main/java/com/alibaba/dashscope/base/HalfDuplexServiceParam.java index 90900078..f44f0401 100644 --- a/src/main/java/com/alibaba/dashscope/base/HalfDuplexServiceParam.java +++ b/src/main/java/com/alibaba/dashscope/base/HalfDuplexServiceParam.java @@ -25,7 +25,7 @@ public String getModel() { @Override public Map getParameters() { - return parameters != null ? parameters : new HashMap<>(); + return parameters; } @Override diff --git a/src/main/java/com/alibaba/dashscope/protocol/okhttp/OkHttpWebSocketClient.java b/src/main/java/com/alibaba/dashscope/protocol/okhttp/OkHttpWebSocketClient.java index 9133a9ee..c91bd363 100644 --- a/src/main/java/com/alibaba/dashscope/protocol/okhttp/OkHttpWebSocketClient.java +++ b/src/main/java/com/alibaba/dashscope/protocol/okhttp/OkHttpWebSocketClient.java @@ -694,7 +694,7 @@ protected void executeStreamRequest(FullDuplexRequest req) { } }, err -> { - log.error(StringUtils.format("Get stream data error: %s", err.getMessage())); + log.error(StringUtils.format("Get stream data error!")); if (responseEmitter != null && !responseEmitter.isCancelled()) { responseEmitter.onError(err); } From d74d76fde0cb7b50f22181e296287b1304fdcbc5 Mon Sep 17 00:00:00 2001 From: kevin Date: Mon, 13 Jul 2026 14:55:39 +0800 Subject: [PATCH 24/31] Revert "fix GenerationStreamCall.java" This reverts commit 1714e88591e57e87d7e61f7c2753a9f412083da9. --- samples/GenerationStreamCall.java | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/samples/GenerationStreamCall.java b/samples/GenerationStreamCall.java index 43092eb9..4aadd8e4 100644 --- a/samples/GenerationStreamCall.java +++ b/samples/GenerationStreamCall.java @@ -41,12 +41,7 @@ public static void streamCall() .repetitionPenalty((float) 1.0) .topK(50) .build(); - JsonObject body = param.getHttpBody(); - if (body != null) { - System.out.println(body.toString()); - } else { - System.out.println("param body is null"); - } + System.out.println(param.getHttpBody().toString()); Generation generation = new Generation(); Flowable flowable = generation.streamCall(param); flowable.blockingForEach(message -> { From 07c84a48b9df3087562d54b9873dc6db3933f859 Mon Sep 17 00:00:00 2001 From: kevin Date: Mon, 13 Jul 2026 14:55:50 +0800 Subject: [PATCH 25/31] Revert "fix: prevent automatic incremental_output injection to avoid response merging issues" This reverts commit 45b40009414d6bb0f2df8e247908a8597f0ad410. --- .../dashscope/aigc/conversation/ConversationParam.java | 2 +- .../multimodalconversation/MultiModalConversationParam.java | 4 ++-- src/main/java/com/alibaba/dashscope/app/ApplicationParam.java | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/alibaba/dashscope/aigc/conversation/ConversationParam.java b/src/main/java/com/alibaba/dashscope/aigc/conversation/ConversationParam.java index 929b0f89..9da8470e 100644 --- a/src/main/java/com/alibaba/dashscope/aigc/conversation/ConversationParam.java +++ b/src/main/java/com/alibaba/dashscope/aigc/conversation/ConversationParam.java @@ -88,7 +88,7 @@ public static class ResultFormat { * apple * */ - private Boolean incrementalOutput = null; + @Builder.Default private Boolean incrementalOutput = false; /* * Maximum tokens to generate. */ diff --git a/src/main/java/com/alibaba/dashscope/aigc/multimodalconversation/MultiModalConversationParam.java b/src/main/java/com/alibaba/dashscope/aigc/multimodalconversation/MultiModalConversationParam.java index 2b969abc..7e38625e 100644 --- a/src/main/java/com/alibaba/dashscope/aigc/multimodalconversation/MultiModalConversationParam.java +++ b/src/main/java/com/alibaba/dashscope/aigc/multimodalconversation/MultiModalConversationParam.java @@ -91,7 +91,7 @@ public class MultiModalConversationParam extends HalfDuplexServiceParam { /** * Used to control the streaming output mode. If true, the subsequent output will include the * previously input content by default. Otherwise, the subsequent output will not include the - * previously output content. Default: null eg(false): + * previously output content. Default: false eg(false): * *
    * I
@@ -103,7 +103,7 @@ public class MultiModalConversationParam extends HalfDuplexServiceParam {
    * apple
    * 
*/ - private Boolean incrementalOutput = null; + @Builder.Default private Boolean incrementalOutput = false; /** Output format of the model including "text" and "audio". Default value: ["text"] */ private List modalities; diff --git a/src/main/java/com/alibaba/dashscope/app/ApplicationParam.java b/src/main/java/com/alibaba/dashscope/app/ApplicationParam.java index 6eb12338..143d0739 100644 --- a/src/main/java/com/alibaba/dashscope/app/ApplicationParam.java +++ b/src/main/java/com/alibaba/dashscope/app/ApplicationParam.java @@ -89,9 +89,9 @@ public class ApplicationParam extends HalfDuplexParamBase { /** * Used to control the streaming output mode. If true, the subsequent output will include the * previously input content by default. Otherwise, the subsequent output will not include the - * previously output content. Default: null. + * previously output content. Default: false. */ - private Boolean incrementalOutput = null; + @Builder.Default private Boolean incrementalOutput = false; /** * Long term memory id is used to store long term context summary between end users and assistant. From 0b383f69bb506b66a5e84bd780fbb6e63517d266 Mon Sep 17 00:00:00 2001 From: kevin Date: Mon, 13 Jul 2026 14:55:50 +0800 Subject: [PATCH 26/31] Revert "fix: allow reasoning_content and content to coexist in streaming responses" This reverts commit 2112b1daef0378471436023a8242d9105d9aabdd. --- .../multimodalconversation/MultiModalConversation.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/alibaba/dashscope/aigc/multimodalconversation/MultiModalConversation.java b/src/main/java/com/alibaba/dashscope/aigc/multimodalconversation/MultiModalConversation.java index c34fff03..4fc4ee4c 100644 --- a/src/main/java/com/alibaba/dashscope/aigc/multimodalconversation/MultiModalConversation.java +++ b/src/main/java/com/alibaba/dashscope/aigc/multimodalconversation/MultiModalConversation.java @@ -335,12 +335,12 @@ private MultiModalConversationResult mergeSingleResponse( accumulated.reasoningContent.append(currentReasoningContent); } - // Set both reasoning_content and content (they are not mutually exclusive) + // Apply mutual exclusion rule: when reasoningContent exists, content should be empty if (accumulated.reasoningContent.length() > 0) { choice.getMessage().setReasoningContent(accumulated.reasoningContent.toString()); - } - // Always set accumulated content if available - if (!accumulated.content.isEmpty()) { + // Clear content when reasoningContent is present + choice.getMessage().setContent(null); + } else if (!accumulated.content.isEmpty()) { choice.getMessage().setContent(accumulated.content); } List currentToolCalls = choice.getMessage().getToolCalls(); From c5bb4e0b09402a8fc058cc65fe52cbf8b1b8475a Mon Sep 17 00:00:00 2001 From: kevin Date: Mon, 13 Jul 2026 15:41:28 +0800 Subject: [PATCH 27/31] =?UTF-8?q?fix:=20propagate=20streamData=20error=20t?= =?UTF-8?q?o=20responseEmitter=20and=20avoid=20duplicate=20URL=20path?= =?UTF-8?q?=E6=8B=BC=E6=8E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/alibaba/dashscope/protocol/HalfDuplexRequest.java | 7 ++++++- .../dashscope/protocol/okhttp/OkHttpWebSocketClient.java | 7 ++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/alibaba/dashscope/protocol/HalfDuplexRequest.java b/src/main/java/com/alibaba/dashscope/protocol/HalfDuplexRequest.java index b47b1e0f..5d40d9b2 100644 --- a/src/main/java/com/alibaba/dashscope/protocol/HalfDuplexRequest.java +++ b/src/main/java/com/alibaba/dashscope/protocol/HalfDuplexRequest.java @@ -59,7 +59,12 @@ public String getHttpUrl() { if (baseUrl.endsWith("/")) { baseUrl = baseUrl.substring(0, baseUrl.length() - 1); } - return baseUrl + serviceOption.httpUrl(); + String path = serviceOption.httpUrl(); + // Avoid duplicate path when baseUrl already contains the full path + if (path != null && !path.isEmpty() && baseUrl.endsWith(path)) { + return baseUrl; + } + return baseUrl + path; } public boolean isSecurityCheck() { diff --git a/src/main/java/com/alibaba/dashscope/protocol/okhttp/OkHttpWebSocketClient.java b/src/main/java/com/alibaba/dashscope/protocol/okhttp/OkHttpWebSocketClient.java index c91bd363..333a0dfc 100644 --- a/src/main/java/com/alibaba/dashscope/protocol/okhttp/OkHttpWebSocketClient.java +++ b/src/main/java/com/alibaba/dashscope/protocol/okhttp/OkHttpWebSocketClient.java @@ -740,8 +740,13 @@ private void joinSendFuture(CompletableFuture future) { future.cancel(true); future.join(); } - } catch (CancellationException | CompletionException ex) { + } catch (CancellationException ex) { + log.error("Sending streaming data cancelled", ex.getMessage()); + } catch (CompletionException ex) { log.error("Sending streaming data exception", ex.getMessage()); + if (responseEmitter != null && !responseEmitter.isCancelled()) { + responseEmitter.onError(ex.getCause() != null ? ex.getCause() : ex); + } } } From 72506668b7f5adc7164ca072e9afe471eb742d88 Mon Sep 17 00:00:00 2001 From: kevin Date: Mon, 13 Jul 2026 17:02:05 +0800 Subject: [PATCH 28/31] fix: allow reasoning_content and content to coexist in MultiModalConversation stream responses --- .../multimodalconversation/MultiModalConversation.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/alibaba/dashscope/aigc/multimodalconversation/MultiModalConversation.java b/src/main/java/com/alibaba/dashscope/aigc/multimodalconversation/MultiModalConversation.java index 4fc4ee4c..0759bc93 100644 --- a/src/main/java/com/alibaba/dashscope/aigc/multimodalconversation/MultiModalConversation.java +++ b/src/main/java/com/alibaba/dashscope/aigc/multimodalconversation/MultiModalConversation.java @@ -335,12 +335,11 @@ private MultiModalConversationResult mergeSingleResponse( accumulated.reasoningContent.append(currentReasoningContent); } - // Apply mutual exclusion rule: when reasoningContent exists, content should be empty + // Accumulate both reasoning_content and content independently if (accumulated.reasoningContent.length() > 0) { choice.getMessage().setReasoningContent(accumulated.reasoningContent.toString()); - // Clear content when reasoningContent is present - choice.getMessage().setContent(null); - } else if (!accumulated.content.isEmpty()) { + } + if (!accumulated.content.isEmpty()) { choice.getMessage().setContent(accumulated.content); } List currentToolCalls = choice.getMessage().getToolCalls(); From 3878c1a95be42bba284581eaf55c66fd11b40bd0 Mon Sep 17 00:00:00 2001 From: kevin Date: Mon, 13 Jul 2026 18:25:37 +0800 Subject: [PATCH 29/31] feat: support real incremental output in MultiModalConversation streaming --- .../multimodalconversation/MultiModalConversation.java | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/alibaba/dashscope/aigc/multimodalconversation/MultiModalConversation.java b/src/main/java/com/alibaba/dashscope/aigc/multimodalconversation/MultiModalConversation.java index 0759bc93..d0c2d87a 100644 --- a/src/main/java/com/alibaba/dashscope/aigc/multimodalconversation/MultiModalConversation.java +++ b/src/main/java/com/alibaba/dashscope/aigc/multimodalconversation/MultiModalConversation.java @@ -280,19 +280,24 @@ private void preprocessInput(MultiModalConversationParam param) /** * Modifies the parameters for internal streaming optimization. If incrementalOutput is false, * modifies the MultiModalConversationParam object to set incrementalOutput to true for internal - * streaming optimization. + * streaming optimization. If incrementalOutput is true or null, keeps the original value to + * return real incremental content. * * @param param The parameter object to modify - * @return true if the parameter was modified, false otherwise + * @return true if the parameter was modified (needs local merge), false otherwise (real + * incremental) */ private boolean modifyIncrementalOutput(MultiModalConversationParam param) { Boolean incrementalOutput = param.getIncrementalOutput(); + // Only modify when user explicitly sets incrementalOutput to false + // If user sets true or null, respect their choice and return real incremental content if (ParamUtils.shouldModifyIncrementalOutput(param.getModel()) && Boolean.FALSE.equals(incrementalOutput)) { // Modify the MultiModalConversationParam object to enable incremental output param.setIncrementalOutput(true); return true; } + // User wants real incremental content (incrementalOutput=true or null) return false; } From c07a264679ff4bf0713936cbd735a6808294951d Mon Sep 17 00:00:00 2001 From: kevin Date: Mon, 13 Jul 2026 19:04:44 +0800 Subject: [PATCH 30/31] feat: support explicit incremental/full mode selection in MultiModalConversation --- .../MultiModalConversationParam.java | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/src/main/java/com/alibaba/dashscope/aigc/multimodalconversation/MultiModalConversationParam.java b/src/main/java/com/alibaba/dashscope/aigc/multimodalconversation/MultiModalConversationParam.java index 7e38625e..e87ae23e 100644 --- a/src/main/java/com/alibaba/dashscope/aigc/multimodalconversation/MultiModalConversationParam.java +++ b/src/main/java/com/alibaba/dashscope/aigc/multimodalconversation/MultiModalConversationParam.java @@ -92,18 +92,24 @@ public class MultiModalConversationParam extends HalfDuplexServiceParam { * Used to control the streaming output mode. If true, the subsequent output will include the * previously input content by default. Otherwise, the subsequent output will not include the * previously output content. Default: false eg(false): + /** + * Used to control the streaming output mode. If true, each chunk contains only incremental + * content without accumulation. If false, SDK accumulates chunks locally and returns full + * content in each chunk. Must be explicitly set - no default value. * *
-   * I
-   * I like
-   * I like apple
-   * when true:
-   * I
-   * like
-   * apple
+   * incrementalOutput=true (incremental):
+   * Chunk1: reasoning="让我先分析", content=[]
+   * Chunk2: reasoning="看起来是羊", content=[{text="这张图片"}]
+   * Chunk3: reasoning="", content=[{text="和一辆车"}]
+   *
+   * incrementalOutput=false (full):
+   * Chunk1: reasoning="让我先分析", content=[]
+   * Chunk2: reasoning="让我先分析看起来是羊", content=[{text="这张图片"}]
+   * Chunk3: reasoning="让我先分析看起来是羊", content=[{text="这张图片和一辆车"}]
    * 
*/ - @Builder.Default private Boolean incrementalOutput = false; + private Boolean incrementalOutput; /** Output format of the model including "text" and "audio". Default value: ["text"] */ private List modalities; From 3faa65d1713db61eafc0512528189e5bb399ce18 Mon Sep 17 00:00:00 2001 From: kevin Date: Mon, 13 Jul 2026 19:06:58 +0800 Subject: [PATCH 31/31] feat: support explicit incremental/full mode selection in MultiModalConversation --- .../MultiModalConversationParam.java | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/alibaba/dashscope/aigc/multimodalconversation/MultiModalConversationParam.java b/src/main/java/com/alibaba/dashscope/aigc/multimodalconversation/MultiModalConversationParam.java index e87ae23e..2825c89a 100644 --- a/src/main/java/com/alibaba/dashscope/aigc/multimodalconversation/MultiModalConversationParam.java +++ b/src/main/java/com/alibaba/dashscope/aigc/multimodalconversation/MultiModalConversationParam.java @@ -91,11 +91,10 @@ public class MultiModalConversationParam extends HalfDuplexServiceParam { /** * Used to control the streaming output mode. If true, the subsequent output will include the * previously input content by default. Otherwise, the subsequent output will not include the - * previously output content. Default: false eg(false): - /** - * Used to control the streaming output mode. If true, each chunk contains only incremental - * content without accumulation. If false, SDK accumulates chunks locally and returns full - * content in each chunk. Must be explicitly set - no default value. + * previously output content. Default: false eg(false): /** Used to control the streaming output + * mode. If true, each chunk contains only incremental content without accumulation. If false, SDK + * accumulates chunks locally and returns full content in each chunk. Must be explicitly set - no + * default value. * *
    * incrementalOutput=true (incremental):