From 72d42307cf63b4304f823f2b7d47b94e62cec7ff Mon Sep 17 00:00:00 2001 From: guanxu <1510424541@qq.com> Date: Sat, 11 Apr 2026 22:31:01 +0800 Subject: [PATCH 1/2] feat(core): Add image and video block parameters for user message content --- .../dashscope/DashScopeMediaConverter.java | 15 +- .../dashscope/dto/DashScopeContentPart.java | 85 +++++ .../agentscope/core/message/ImageBlock.java | 69 +++- .../agentscope/core/message/VideoBlock.java | 148 +++++++- .../DashScopeMediaConverterTest.java | 351 ++++++++++++++++++ .../dto/DashScopeDtoSerializationTest.java | 88 +++++ 6 files changed, 750 insertions(+), 6 deletions(-) create mode 100644 agentscope-core/src/test/java/io/agentscope/core/formatter/dashscope/DashScopeMediaConverterTest.java diff --git a/agentscope-core/src/main/java/io/agentscope/core/formatter/dashscope/DashScopeMediaConverter.java b/agentscope-core/src/main/java/io/agentscope/core/formatter/dashscope/DashScopeMediaConverter.java index e9c275a16..77fdabf11 100644 --- a/agentscope-core/src/main/java/io/agentscope/core/formatter/dashscope/DashScopeMediaConverter.java +++ b/agentscope-core/src/main/java/io/agentscope/core/formatter/dashscope/DashScopeMediaConverter.java @@ -81,7 +81,11 @@ public String convertImageBlockToUrl(ImageBlock imageBlock) throws Exception { public DashScopeContentPart convertImageBlockToContentPart(ImageBlock imageBlock) throws Exception { String imageUrl = convertImageBlockToUrl(imageBlock); - return DashScopeContentPart.image(imageUrl); + return DashScopeContentPart.builder() + .image(imageUrl) + .minPixels(imageBlock.getMinPixels()) + .maxPixels(imageBlock.getMaxPixels()) + .build(); } /** @@ -130,7 +134,14 @@ public String convertVideoBlockToUrl(VideoBlock videoBlock) throws Exception { public DashScopeContentPart convertVideoBlockToContentPart(VideoBlock videoBlock) throws Exception { String videoUrl = convertVideoBlockToUrl(videoBlock); - return DashScopeContentPart.video(videoUrl); + return DashScopeContentPart.builder() + .video(videoUrl) + .fps(videoBlock.getFps()) + .maxFrames(videoBlock.getMaxFrames()) + .minPixels(videoBlock.getMinPixels()) + .maxPixels(videoBlock.getMaxPixels()) + .totalPixels(videoBlock.getTotalPixels()) + .build(); } /** diff --git a/agentscope-core/src/main/java/io/agentscope/core/formatter/dashscope/dto/DashScopeContentPart.java b/agentscope-core/src/main/java/io/agentscope/core/formatter/dashscope/dto/DashScopeContentPart.java index 202af305c..d2c7915a9 100644 --- a/agentscope-core/src/main/java/io/agentscope/core/formatter/dashscope/dto/DashScopeContentPart.java +++ b/agentscope-core/src/main/java/io/agentscope/core/formatter/dashscope/dto/DashScopeContentPart.java @@ -59,6 +59,26 @@ public class DashScopeContentPart { @JsonProperty("video") private Object video; + /** Frames per second. The value range is [0.1, 10], and the default value is 2.0. */ + @JsonProperty("fps") + private Float fps; + + /** The maximum number of frames captured in the video. */ + @JsonProperty("max_frames") + private Integer maxFrames; + + /** Used to set the minimum pixel threshold for input image or video frames. */ + @JsonProperty("min_pixels") + private Integer minPixels; + + /** Used to set the maximum pixel threshold for input image or video frames. */ + @JsonProperty("max_pixels") + private Integer maxPixels; + + /** Used to limit the total pixels of all frames extracted from the video (single image pixels × total frames). */ + @JsonProperty("total_pixels") + private Integer totalPixels; + public DashScopeContentPart() {} public String getText() { @@ -93,6 +113,46 @@ public void setVideo(Object video) { this.video = video; } + public Float getFps() { + return fps; + } + + public void setFps(Float fps) { + this.fps = fps; + } + + public Integer getMaxFrames() { + return maxFrames; + } + + public void setMaxFrames(Integer maxFrames) { + this.maxFrames = maxFrames; + } + + public Integer getMinPixels() { + return minPixels; + } + + public void setMinPixels(Integer minPixels) { + this.minPixels = minPixels; + } + + public Integer getMaxPixels() { + return maxPixels; + } + + public void setMaxPixels(Integer maxPixels) { + this.maxPixels = maxPixels; + } + + public Integer getTotalPixels() { + return totalPixels; + } + + public void setTotalPixels(Integer totalPixels) { + this.totalPixels = totalPixels; + } + /** * Get video as URL string. * @@ -205,6 +265,31 @@ public Builder video(Object video) { return this; } + public Builder fps(Float fps) { + part.setFps(fps); + return this; + } + + public Builder maxFrames(Integer maxFrames) { + part.setMaxFrames(maxFrames); + return this; + } + + public Builder minPixels(Integer minPixels) { + part.setMinPixels(minPixels); + return this; + } + + public Builder maxPixels(Integer maxPixels) { + part.setMaxPixels(maxPixels); + return this; + } + + public Builder totalPixels(Integer totalPixels) { + part.setTotalPixels(totalPixels); + return this; + } + public DashScopeContentPart build() { return part; } diff --git a/agentscope-core/src/main/java/io/agentscope/core/message/ImageBlock.java b/agentscope-core/src/main/java/io/agentscope/core/message/ImageBlock.java index bf967b0c0..42bab547d 100644 --- a/agentscope-core/src/main/java/io/agentscope/core/message/ImageBlock.java +++ b/agentscope-core/src/main/java/io/agentscope/core/message/ImageBlock.java @@ -36,15 +36,36 @@ public final class ImageBlock extends ContentBlock { private final Source source; + private final Integer minPixels; + + private final Integer maxPixels; + + /** + * Creates a new image block with source. + * + * @param source The image source (URL or Base64) + * @throws NullPointerException if source is null + */ + public ImageBlock(@JsonProperty("source") Source source) { + this(source, null, null); + } + /** * Creates a new image block for JSON deserialization. * * @param source The image source (URL or Base64) + * @param minPixels Used to set the minimum pixel threshold for input image. + * @param maxPixels Used to set the maximum pixel threshold for input image. * @throws NullPointerException if source is null */ @JsonCreator - public ImageBlock(@JsonProperty("source") Source source) { + private ImageBlock( + @JsonProperty("source") Source source, + @JsonProperty("min_pixels") Integer minPixels, + @JsonProperty("max_pixels") Integer maxPixels) { this.source = Objects.requireNonNull(source, "source cannot be null"); + this.minPixels = minPixels; + this.maxPixels = maxPixels; } /** @@ -56,6 +77,24 @@ public Source getSource() { return source; } + /** + * Gets the minimum pixel threshold for input image. + * + * @return The minimum pixel threshold + */ + public Integer getMinPixels() { + return minPixels; + } + + /** + * Gets the maximum pixel threshold for input image. + * + * @return The maximum pixel threshold + */ + public Integer getMaxPixels() { + return maxPixels; + } + /** * Creates a new builder for constructing ImageBlock instances. * @@ -72,6 +111,10 @@ public static class Builder { private Source source; + private Integer minPixels; + + private Integer maxPixels; + /** * Sets the source for the image. * @@ -83,6 +126,28 @@ public Builder source(Source source) { return this; } + /** + * Sets the minimum pixel threshold for input image frames. + * + * @param minPixels The minimum pixel threshold + * @return This builder for chaining + */ + public Builder minPixels(Integer minPixels) { + this.minPixels = minPixels; + return this; + } + + /** + * Sets the maximum pixel threshold for input image frames. + * + * @param maxPixels The maximum pixel threshold + * @return This builder for chaining + */ + public Builder maxPixels(Integer maxPixels) { + this.maxPixels = maxPixels; + return this; + } + /** * Builds a new ImageBlock with the configured source. * @@ -90,7 +155,7 @@ public Builder source(Source source) { * @throws NullPointerException if source is null */ public ImageBlock build() { - return new ImageBlock(source); + return new ImageBlock(source, minPixels, maxPixels); } } } diff --git a/agentscope-core/src/main/java/io/agentscope/core/message/VideoBlock.java b/agentscope-core/src/main/java/io/agentscope/core/message/VideoBlock.java index 10a3844e9..c725fa3e2 100644 --- a/agentscope-core/src/main/java/io/agentscope/core/message/VideoBlock.java +++ b/agentscope-core/src/main/java/io/agentscope/core/message/VideoBlock.java @@ -35,14 +35,49 @@ public final class VideoBlock extends ContentBlock { private final Source source; + private final Float fps; + + private final Integer maxFrames; + + private final Integer minPixels; + + private final Integer maxPixels; + + private final Integer totalPixels; + + /** + * Creates a new video block with source. + * + * @param source The video source (URL or Base64) + */ + public VideoBlock(@JsonProperty("source") Source source) { + this(source, null, null, null, null, null); + } + /** * Creates a new video block for JSON deserialization. * * @param source The video source (URL or Base64) + * @param fps The frames per second. The value range is [0.1, 10], and the default value is 2.0. + * @param maxFrames The maximum number of frames captured in the video. + * @param minPixels Used to set the minimum pixel threshold for input video frames. + * @param maxPixels Used to set the maximum pixel threshold for input video frames. + * @param totalPixels Used to limit the total pixels of all frames extracted from the video (single image pixels × total frames). */ @JsonCreator - public VideoBlock(@JsonProperty("source") Source source) { + private VideoBlock( + @JsonProperty("source") Source source, + @JsonProperty("fps") Float fps, + @JsonProperty("max_frames") Integer maxFrames, + @JsonProperty("min_pixels") Integer minPixels, + @JsonProperty("max_pixels") Integer maxPixels, + @JsonProperty("total_pixels") Integer totalPixels) { this.source = source; + this.fps = fps; + this.maxFrames = maxFrames; + this.minPixels = minPixels; + this.maxPixels = maxPixels; + this.totalPixels = totalPixels; } /** @@ -54,6 +89,50 @@ public Source getSource() { return source; } + /** + * Gets the frames per second (FPS) of the video. + * + * @return The frames per second value + */ + public Float getFps() { + return fps; + } + + /** + * Gets the maximum number of frames to capture from the video. + * + * @return The maximum number of frames + */ + public Integer getMaxFrames() { + return maxFrames; + } + + /** + * Gets the minimum pixel threshold for input video frames. + * + * @return The minimum pixel threshold + */ + public Integer getMinPixels() { + return minPixels; + } + + /** + * Gets the maximum pixel threshold for input video frames. + * + * @return The maximum pixel threshold + */ + public Integer getMaxPixels() { + return maxPixels; + } + + /** + * Gets the total pixels of all frames extracted from the video (single image pixels × total frames). + * @return The total pixels + */ + public Integer getTotalPixels() { + return totalPixels; + } + /** * Creates a new builder for constructing VideoBlock instances. * @@ -70,6 +149,16 @@ public static class Builder { private Source source; + private Float fps; + + private Integer maxFrames; + + private Integer minPixels; + + private Integer maxPixels; + + private Integer totalPixels; + /** * Sets the source for the video content. * @@ -81,13 +170,68 @@ public Builder source(Source source) { return this; } + /** + * Sets the frames per second (FPS) of the video. + * + * @param fps The frames per second value + * @return This builder for chaining + */ + public Builder fps(Float fps) { + this.fps = fps; + return this; + } + + /** + * Sets the maximum number of frames to capture from the video. + * + * @param maxFrames The maximum number of frames + * @return This builder for chaining + */ + public Builder maxFrames(Integer maxFrames) { + this.maxFrames = maxFrames; + return this; + } + + /** + * Sets the minimum pixel threshold for input video frames. + * + * @param minPixels The minimum pixel threshold + * @return This builder for chaining + */ + public Builder minPixels(Integer minPixels) { + this.minPixels = minPixels; + return this; + } + + /** + * Sets the maximum pixel threshold for input video frames. + * + * @param maxPixels The maximum pixel threshold + * @return This builder for chaining + */ + public Builder maxPixels(Integer maxPixels) { + this.maxPixels = maxPixels; + return this; + } + + /** + * Sets the total pixels of all frames extracted from the video (single image pixels × total frames). + * + * @param totalPixels The total pixels + * @return This builder for chaining + */ + public Builder totalPixels(Integer totalPixels) { + this.totalPixels = totalPixels; + return this; + } + /** * Builds a new VideoBlock with the configured source. * * @return A new VideoBlock instance */ public VideoBlock build() { - return new VideoBlock(source); + return new VideoBlock(source, fps, maxFrames, minPixels, maxPixels, totalPixels); } } } diff --git a/agentscope-core/src/test/java/io/agentscope/core/formatter/dashscope/DashScopeMediaConverterTest.java b/agentscope-core/src/test/java/io/agentscope/core/formatter/dashscope/DashScopeMediaConverterTest.java new file mode 100644 index 000000000..a7e5a461d --- /dev/null +++ b/agentscope-core/src/test/java/io/agentscope/core/formatter/dashscope/DashScopeMediaConverterTest.java @@ -0,0 +1,351 @@ +/* + * Copyright 2024-2026 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.agentscope.core.formatter.dashscope; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; + +import io.agentscope.core.formatter.dashscope.dto.DashScopeContentPart; +import io.agentscope.core.message.Base64Source; +import io.agentscope.core.message.ImageBlock; +import io.agentscope.core.message.URLSource; +import io.agentscope.core.message.VideoBlock; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; + +/** Unit tests for {@link DashScopeMediaConverter}. */ +@Tag("unit") +class DashScopeMediaConverterTest { + + private final DashScopeMediaConverter converter = new DashScopeMediaConverter(); + + @Test + void testConvertImageBlockToContentPartWithMinPixels() throws Exception { + ImageBlock imageBlock = + ImageBlock.builder() + .source(URLSource.builder().url("https://example.com/image.png").build()) + .minPixels(256) + .build(); + + DashScopeContentPart contentPart = converter.convertImageBlockToContentPart(imageBlock); + + assertEquals("https://example.com/image.png", contentPart.getImage()); + assertEquals(256, contentPart.getMinPixels()); + assertNull(contentPart.getMaxPixels()); + } + + @Test + void testConvertImageBlockToContentPartWithMaxPixels() throws Exception { + ImageBlock imageBlock = + ImageBlock.builder() + .source(URLSource.builder().url("https://example.com/image.png").build()) + .maxPixels(1048576) + .build(); + + DashScopeContentPart contentPart = converter.convertImageBlockToContentPart(imageBlock); + + assertEquals("https://example.com/image.png", contentPart.getImage()); + assertEquals(1048576, contentPart.getMaxPixels()); + assertNull(contentPart.getMinPixels()); + } + + @Test + void testConvertImageBlockToContentPartWithBothPixels() throws Exception { + ImageBlock imageBlock = + ImageBlock.builder() + .source(URLSource.builder().url("https://example.com/image.png").build()) + .minPixels(256) + .maxPixels(1048576) + .build(); + + DashScopeContentPart contentPart = converter.convertImageBlockToContentPart(imageBlock); + + assertEquals("https://example.com/image.png", contentPart.getImage()); + assertEquals(256, contentPart.getMinPixels()); + assertEquals(1048576, contentPart.getMaxPixels()); + } + + @Test + void testConvertImageBlockToContentPartWithBase64Source() throws Exception { + ImageBlock imageBlock = + ImageBlock.builder() + .source( + Base64Source.builder() + .mediaType("image/png") + .data("iVBORw0KGgo...") + .build()) + .minPixels(128) + .maxPixels(512000) + .build(); + + DashScopeContentPart contentPart = converter.convertImageBlockToContentPart(imageBlock); + + assertEquals("data:image/png;base64,iVBORw0KGgo...", contentPart.getImage()); + assertEquals(128, contentPart.getMinPixels()); + assertEquals(512000, contentPart.getMaxPixels()); + } + + @Test + void testConvertImageBlockToContentPartWithoutPixels() throws Exception { + ImageBlock imageBlock = + ImageBlock.builder() + .source(URLSource.builder().url("https://example.com/image.png").build()) + .build(); + + DashScopeContentPart contentPart = converter.convertImageBlockToContentPart(imageBlock); + + assertEquals("https://example.com/image.png", contentPart.getImage()); + assertNull(contentPart.getMinPixels()); + assertNull(contentPart.getMaxPixels()); + } + + @Test + void testConvertVideoBlockToContentPartWithFps() throws Exception { + VideoBlock videoBlock = + VideoBlock.builder() + .source(URLSource.builder().url("https://example.com/video.mp4").build()) + .fps(2.5f) + .build(); + + DashScopeContentPart contentPart = converter.convertVideoBlockToContentPart(videoBlock); + + assertEquals("https://example.com/video.mp4", contentPart.getVideoAsString()); + assertEquals(2.5f, contentPart.getFps()); + assertNull(contentPart.getMaxFrames()); + } + + @Test + void testConvertVideoBlockToContentPartWithMaxFrames() throws Exception { + VideoBlock videoBlock = + VideoBlock.builder() + .source(URLSource.builder().url("https://example.com/video.mp4").build()) + .maxFrames(32) + .build(); + + DashScopeContentPart contentPart = converter.convertVideoBlockToContentPart(videoBlock); + + assertEquals("https://example.com/video.mp4", contentPart.getVideoAsString()); + assertEquals(32, contentPart.getMaxFrames()); + assertNull(contentPart.getFps()); + } + + @Test + void testConvertVideoBlockToContentPartWithMinPixels() throws Exception { + VideoBlock videoBlock = + VideoBlock.builder() + .source(URLSource.builder().url("https://example.com/video.mp4").build()) + .minPixels(256) + .build(); + + DashScopeContentPart contentPart = converter.convertVideoBlockToContentPart(videoBlock); + + assertEquals("https://example.com/video.mp4", contentPart.getVideoAsString()); + assertEquals(256, contentPart.getMinPixels()); + } + + @Test + void testConvertVideoBlockToContentPartWithMaxPixels() throws Exception { + VideoBlock videoBlock = + VideoBlock.builder() + .source(URLSource.builder().url("https://example.com/video.mp4").build()) + .maxPixels(1048576) + .build(); + + DashScopeContentPart contentPart = converter.convertVideoBlockToContentPart(videoBlock); + + assertEquals("https://example.com/video.mp4", contentPart.getVideoAsString()); + assertEquals(1048576, contentPart.getMaxPixels()); + } + + @Test + void testConvertVideoBlockToContentPartWithTotalPixels() throws Exception { + VideoBlock videoBlock = + VideoBlock.builder() + .source(URLSource.builder().url("https://example.com/video.mp4").build()) + .totalPixels(2097152) + .build(); + + DashScopeContentPart contentPart = converter.convertVideoBlockToContentPart(videoBlock); + + assertEquals("https://example.com/video.mp4", contentPart.getVideoAsString()); + assertEquals(2097152, contentPart.getTotalPixels()); + } + + @Test + void testConvertVideoBlockToContentPartWithAllParameters() throws Exception { + VideoBlock videoBlock = + VideoBlock.builder() + .source(URLSource.builder().url("https://example.com/video.mp4").build()) + .fps(2.0f) + .maxFrames(16) + .minPixels(256) + .maxPixels(512000) + .totalPixels(8192000) + .build(); + + DashScopeContentPart contentPart = converter.convertVideoBlockToContentPart(videoBlock); + + assertEquals("https://example.com/video.mp4", contentPart.getVideoAsString()); + assertEquals(2.0f, contentPart.getFps()); + assertEquals(16, contentPart.getMaxFrames()); + assertEquals(256, contentPart.getMinPixels()); + assertEquals(512000, contentPart.getMaxPixels()); + assertEquals(8192000, contentPart.getTotalPixels()); + } + + @Test + void testConvertVideoBlockToContentPartWithBase64Source() throws Exception { + VideoBlock videoBlock = + VideoBlock.builder() + .source( + Base64Source.builder() + .mediaType("video/mp4") + .data("AAAAIGZ0eXBpc29tAAACAGlzb21pc28y...") + .build()) + .fps(1.5f) + .maxFrames(8) + .minPixels(128) + .maxPixels(256000) + .totalPixels(2048000) + .build(); + + DashScopeContentPart contentPart = converter.convertVideoBlockToContentPart(videoBlock); + + assertEquals( + "data:video/mp4;base64,AAAAIGZ0eXBpc29tAAACAGlzb21pc28y...", + contentPart.getVideoAsString()); + assertEquals(1.5f, contentPart.getFps()); + assertEquals(8, contentPart.getMaxFrames()); + assertEquals(128, contentPart.getMinPixels()); + assertEquals(256000, contentPart.getMaxPixels()); + assertEquals(2048000, contentPart.getTotalPixels()); + } + + @Test + void testConvertVideoBlockToContentPartWithoutParameters() throws Exception { + VideoBlock videoBlock = + VideoBlock.builder() + .source(URLSource.builder().url("https://example.com/video.mp4").build()) + .build(); + + DashScopeContentPart contentPart = converter.convertVideoBlockToContentPart(videoBlock); + + assertEquals("https://example.com/video.mp4", contentPart.getVideoAsString()); + assertNull(contentPart.getFps()); + assertNull(contentPart.getMaxFrames()); + assertNull(contentPart.getMinPixels()); + assertNull(contentPart.getMaxPixels()); + assertNull(contentPart.getTotalPixels()); + } + + @Test + void testImageBlockBuilderWithAllParameters() { + ImageBlock imageBlock = + ImageBlock.builder() + .source(URLSource.builder().url("https://example.com/image.png").build()) + .minPixels(128) + .maxPixels(1048576) + .build(); + + assertEquals( + "https://example.com/image.png", ((URLSource) imageBlock.getSource()).getUrl()); + assertEquals(128, imageBlock.getMinPixels()); + assertEquals(1048576, imageBlock.getMaxPixels()); + } + + @Test + void testVideoBlockBuilderWithAllParameters() { + VideoBlock videoBlock = + VideoBlock.builder() + .source(URLSource.builder().url("https://example.com/video.mp4").build()) + .fps(3.0f) + .maxFrames(24) + .minPixels(256) + .maxPixels(512000) + .totalPixels(12288000) + .build(); + + assertEquals( + "https://example.com/video.mp4", ((URLSource) videoBlock.getSource()).getUrl()); + assertEquals(3.0f, videoBlock.getFps()); + assertEquals(24, videoBlock.getMaxFrames()); + assertEquals(256, videoBlock.getMinPixels()); + assertEquals(512000, videoBlock.getMaxPixels()); + assertEquals(12288000, videoBlock.getTotalPixels()); + } + + @Test + void testDashScopeContentPartBuilderWithAllParameters() { + DashScopeContentPart contentPart = + DashScopeContentPart.builder() + .image("https://example.com/image.png") + .fps(2.0f) + .maxFrames(16) + .minPixels(128) + .maxPixels(512000) + .totalPixels(8192000) + .build(); + + assertEquals("https://example.com/image.png", contentPart.getImage()); + assertEquals(2.0f, contentPart.getFps()); + assertEquals(16, contentPart.getMaxFrames()); + assertEquals(128, contentPart.getMinPixels()); + assertEquals(512000, contentPart.getMaxPixels()); + assertEquals(8192000, contentPart.getTotalPixels()); + } + + @Test + void testImageBlockSourceNotNull() { + try { + ImageBlock.builder().source(null).build(); + } catch (NullPointerException e) { + assertNotNull(e); + return; + } + } + + @Test + void testVideoBlockSourceNotNull() { + try { + VideoBlock.builder().source(null).build(); + } catch (NullPointerException e) { + assertNotNull(e); + return; + } + } + + @Test + void testImageBlockDefaultConstructorNullPixels() { + ImageBlock imageBlock = + new ImageBlock(URLSource.builder().url("https://example.com/image.png").build()); + + assertNull(imageBlock.getMinPixels()); + assertNull(imageBlock.getMaxPixels()); + } + + @Test + void testVideoBlockDefaultConstructorNullParameters() { + VideoBlock videoBlock = + new VideoBlock(URLSource.builder().url("https://example.com/video.mp4").build()); + + assertNull(videoBlock.getFps()); + assertNull(videoBlock.getMaxFrames()); + assertNull(videoBlock.getMinPixels()); + assertNull(videoBlock.getMaxPixels()); + assertNull(videoBlock.getTotalPixels()); + } +} diff --git a/agentscope-core/src/test/java/io/agentscope/core/formatter/dashscope/dto/DashScopeDtoSerializationTest.java b/agentscope-core/src/test/java/io/agentscope/core/formatter/dashscope/dto/DashScopeDtoSerializationTest.java index d0497895b..b580fa7cd 100644 --- a/agentscope-core/src/test/java/io/agentscope/core/formatter/dashscope/dto/DashScopeDtoSerializationTest.java +++ b/agentscope-core/src/test/java/io/agentscope/core/formatter/dashscope/dto/DashScopeDtoSerializationTest.java @@ -359,4 +359,92 @@ void testDashScopeParametersBuilder() { assertEquals(0.5, params.getFrequencyPenalty()); assertEquals(0.3, params.getPresencePenalty()); } + + @Test + void testDashScopeContentPartWithImagePixels() throws Exception { + DashScopeContentPart imagePart = + DashScopeContentPart.builder() + .image("https://example.com/image.jpg") + .minPixels(256) + .maxPixels(1048576) + .build(); + + String json = jsonCodec.toJson(imagePart); + assertTrue(json.contains("image")); + assertTrue(json.contains("min_pixels")); + assertTrue(json.contains("max_pixels")); + + DashScopeContentPart deserialized = jsonCodec.fromJson(json, DashScopeContentPart.class); + assertEquals("https://example.com/image.jpg", deserialized.getImage()); + assertEquals(256, deserialized.getMinPixels()); + assertEquals(1048576, deserialized.getMaxPixels()); + } + + @Test + void testDashScopeContentPartWithVideoParameters() throws Exception { + DashScopeContentPart videoPart = + DashScopeContentPart.builder() + .video("https://example.com/video.mp4") + .fps(2.5f) + .maxFrames(32) + .minPixels(128) + .maxPixels(512000) + .totalPixels(8192000) + .build(); + + String json = jsonCodec.toJson(videoPart); + assertTrue(json.contains("video")); + assertTrue(json.contains("fps")); + assertTrue(json.contains("max_frames")); + assertTrue(json.contains("min_pixels")); + assertTrue(json.contains("max_pixels")); + assertTrue(json.contains("total_pixels")); + + DashScopeContentPart deserialized = jsonCodec.fromJson(json, DashScopeContentPart.class); + assertEquals("https://example.com/video.mp4", deserialized.getVideoAsString()); + assertEquals(2.5f, deserialized.getFps()); + assertEquals(32, deserialized.getMaxFrames()); + assertEquals(128, deserialized.getMinPixels()); + assertEquals(512000, deserialized.getMaxPixels()); + assertEquals(8192000, deserialized.getTotalPixels()); + } + + @Test + void testDashScopeContentPartBuilderMethods() { + DashScopeContentPart part = + DashScopeContentPart.builder() + .text("Hello") + .image("https://example.com/image.png") + .audio("https://example.com/audio.wav") + .video("https://example.com/video.mp4") + .fps(2.0f) + .maxFrames(16) + .minPixels(256) + .maxPixels(512000) + .totalPixels(8192000) + .build(); + + assertEquals("Hello", part.getText()); + assertEquals("https://example.com/image.png", part.getImage()); + assertEquals("https://example.com/audio.wav", part.getAudio()); + assertEquals("https://example.com/video.mp4", part.getVideoAsString()); + assertEquals(2.0f, part.getFps()); + assertEquals(16, part.getMaxFrames()); + assertEquals(256, part.getMinPixels()); + assertEquals(512000, part.getMaxPixels()); + assertEquals(8192000, part.getTotalPixels()); + } + + @Test + void testDashScopeContentPartNullParameters() { + DashScopeContentPart part = + DashScopeContentPart.builder().image("https://example.com/image.png").build(); + + assertEquals("https://example.com/image.png", part.getImage()); + assertNull(part.getFps()); + assertNull(part.getMaxFrames()); + assertNull(part.getMinPixels()); + assertNull(part.getMaxPixels()); + assertNull(part.getTotalPixels()); + } } From 88c58a20a2c38e459f2e0f85a777a122e7ecc186 Mon Sep 17 00:00:00 2001 From: guanxu <1510424541@qq.com> Date: Sat, 11 Apr 2026 23:41:04 +0800 Subject: [PATCH 2/2] feat(core): Set JsonInclude.Include.NON_NULL for image and video block --- .../src/main/java/io/agentscope/core/message/ImageBlock.java | 2 ++ .../src/main/java/io/agentscope/core/message/VideoBlock.java | 2 ++ 2 files changed, 4 insertions(+) diff --git a/agentscope-core/src/main/java/io/agentscope/core/message/ImageBlock.java b/agentscope-core/src/main/java/io/agentscope/core/message/ImageBlock.java index 42bab547d..b57866f02 100644 --- a/agentscope-core/src/main/java/io/agentscope/core/message/ImageBlock.java +++ b/agentscope-core/src/main/java/io/agentscope/core/message/ImageBlock.java @@ -16,6 +16,7 @@ package io.agentscope.core.message; import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; import java.util.Objects; @@ -32,6 +33,7 @@ * need to process visual information from images, diagrams, screenshots, * or other visual content. */ +@JsonInclude(JsonInclude.Include.NON_NULL) public final class ImageBlock extends ContentBlock { private final Source source; diff --git a/agentscope-core/src/main/java/io/agentscope/core/message/VideoBlock.java b/agentscope-core/src/main/java/io/agentscope/core/message/VideoBlock.java index c725fa3e2..09dc51cc9 100644 --- a/agentscope-core/src/main/java/io/agentscope/core/message/VideoBlock.java +++ b/agentscope-core/src/main/java/io/agentscope/core/message/VideoBlock.java @@ -16,6 +16,7 @@ package io.agentscope.core.message; import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; /** @@ -31,6 +32,7 @@ * or analyze video content such as presentations, tutorials, surveillance footage, * or other visual media that includes motion and temporal elements. */ +@JsonInclude(JsonInclude.Include.NON_NULL) public final class VideoBlock extends ContentBlock { private final Source source;