Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
8415485
chore(deps): bump nacos3.version from 3.1.0 to 3.2.3
dependabot[bot] Aug 27, 2026
38fc85c
chore(deps): bump spring-boot.version from 4.1.0 to 4.1.1
dependabot[bot] Aug 27, 2026
c6bff2b
chore(deps): bump org.apache.maven.plugins:maven-site-plugin
dependabot[bot] Aug 27, 2026
837b111
chore(deps): bump org.apache.maven.plugins:maven-deploy-plugin
dependabot[bot] Aug 27, 2026
ad927fc
chore(deps): bump org.apache.maven.plugins:maven-checkstyle-plugin
dependabot[bot] Aug 27, 2026
0bba41d
chore(deps): bump io.modelcontextprotocol.sdk:mcp-bom
dependabot[bot] Aug 27, 2026
5773c59
chore(deps): bump org.codehaus.mojo:flatten-maven-plugin
dependabot[bot] Aug 27, 2026
9cc1dec
chore(deps): bump org.springframework.ai:spring-ai-bom
dependabot[bot] Aug 27, 2026
72d5deb
chore(deps-dev): bump org.apache.maven.plugins:maven-failsafe-plugin
dependabot[bot] Aug 27, 2026
f06994d
chore(deps): bump com.diffplug.spotless:spotless-maven-plugin
dependabot[bot] Aug 27, 2026
6100235
chore(deps): bump commons-io:commons-io from 2.21.0 to 2.22.0
dependabot[bot] Aug 27, 2026
25390fd
chore(deps-dev): bump org.springdoc:springdoc-openapi-maven-plugin
dependabot[bot] Aug 27, 2026
e1fb1cf
chore(deps): bump org.apache.maven.plugins:maven-jar-plugin
dependabot[bot] Aug 27, 2026
5e67978
chore(deps-dev): bump com.mycila:license-maven-plugin from 4.1 to 4.6
dependabot[bot] Aug 27, 2026
663bd22
chore(deps): bump org.apache.maven.plugins:maven-source-plugin
dependabot[bot] Aug 27, 2026
05fafd5
chore(deps): bump org.jspecify:jspecify from 1.0.0 to 1.0.1
dependabot[bot] Aug 27, 2026
cddc080
chore(deps): bump com.github.docker-java:docker-java-bom
dependabot[bot] Aug 27, 2026
047a4ae
chore(deps-dev): bump graalvm.polyglot.version from 24.2.1 to 24.2.2
dependabot[bot] Aug 27, 2026
d12ac09
chore(deps-dev): bump org.jacoco:jacoco-maven-plugin
dependabot[bot] Aug 27, 2026
e8f966d
fix(ci): use renamed extensions repository
yuluo-yx Aug 30, 2026
b8f1026
fix(agent): support Spring AI 2.0.1 media data
yuluo-yx Aug 30, 2026
5698881
fix(nacos): preserve Spring AI strict option
yuluo-yx Aug 30, 2026
cb379f7
fix(build): migrate license plugin configuration
yuluo-yx Aug 30, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,9 @@ private Map<String, Object> mediaToMap(Media media) {
}

Object data = media.getData();
if (data instanceof ToolMultimodalResult.MediaFormats formats) {
ToolMultimodalResult.MediaFormats formats = data instanceof String stringData
? ToolMultimodalResult.MediaFormats.deserialize(stringData) : null;
if (formats != null) {
handleMediaFormats(formats, mimeTypeStr, itemMap);
}
else if (data instanceof URI uri) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ public static Media mediaFromResource(Resource resource, MimeType mimeType) {
* @param base64 Base64-encoded data (raw string, not data URL; may be null)
*/
public static Media mediaFromUrlAndBase64(String url, String base64, MimeType mimeType) {
return Media.builder().mimeType(mimeType).data(new MediaFormats(url, base64)).build();
return Media.builder().mimeType(mimeType).data(new MediaFormats(url, base64).serialize()).build();
}

/**
Expand All @@ -132,14 +132,49 @@ public static Media mediaFromUrlAndBase64(String url, String base64, MimeType mi
* @param base64 Base64-encoded data (raw string, not data URL)
*/
public static Media mediaFromBase64(String base64, MimeType mimeType) {
return Media.builder().mimeType(mimeType).data(new MediaFormats(null, base64)).build();
return Media.builder().mimeType(mimeType).data(new MediaFormats(null, base64).serialize()).build();
}

/**
* Holds both URL and base64 when available. Used by {@link MultimodalToolCallResultConverter}
* to prefer the format matching OutputFormat without unnecessary conversion.
* Holds both URL and Base64 data so {@link MultimodalToolCallResultConverter}
* can select the requested output format without unnecessary downloads or encoding.
*
* <p>The length-prefixed string representation is accepted and preserved by Spring AI
* 2.0.1 while avoiding another Base64 encoding pass for potentially large payloads.
*/
public record MediaFormats(String url, String base64) {

private static final String PREFIX = "agentic-media-formats:";

private String serialize() {
String serializedUrl = this.url != null ? this.url : "";
String serializedBase64 = this.base64 != null ? this.base64 : "";
return PREFIX + serializedUrl.length() + ":" + serializedUrl + serializedBase64;
}

static MediaFormats deserialize(String data) {
if (data == null || !data.startsWith(PREFIX)) {
return null;
}
int lengthEnd = data.indexOf(':', PREFIX.length());
if (lengthEnd < 0) {
return null;
}
try {
int urlLength = Integer.parseInt(data.substring(PREFIX.length(), lengthEnd));
int urlStart = lengthEnd + 1;
int base64Start = urlStart + urlLength;
if (urlLength < 0 || base64Start > data.length()) {
return null;
}
String url = data.substring(urlStart, base64Start);
String base64 = data.substring(base64Start);
return new MediaFormats(url.isEmpty() ? null : url, base64.isEmpty() ? null : base64);
}
catch (NumberFormatException ex) {
return null;
}
}
}

public static class Builder {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/*
* 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
*
* https://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.github.agentic.spring.ai.graph.agent.tool.multimodal;

import org.junit.jupiter.api.Test;

import org.springframework.ai.content.Media;
import org.springframework.util.MimeTypeUtils;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;

class ToolMultimodalResultTest {

@Test
void preservesUrlAndBase64InSpringAiMedia() {
String url = "https://example.com/image.png";
String base64 = "aW1hZ2U=";

Media media = ToolMultimodalResult.mediaFromUrlAndBase64(url, base64, MimeTypeUtils.IMAGE_PNG);
String serialized = assertInstanceOf(String.class, media.getData());
ToolMultimodalResult.MediaFormats formats = ToolMultimodalResult.MediaFormats.deserialize(serialized);

assertNotNull(formats);
assertEquals(url, formats.url());
assertEquals(base64, formats.base64());
}

@Test
void convertsCombinedMediaToRequestedOutputFormat() {
String url = "https://example.com/image.png";
String base64 = "aW1hZ2U=";
ToolMultimodalResult result = ToolMultimodalResult.of("image",
ToolMultimodalResult.mediaFromUrlAndBase64(url, base64, MimeTypeUtils.IMAGE_PNG));

String urlJson = new MultimodalToolCallResultConverter(OutputFormat.url).convert(result,
ToolMultimodalResult.class);
String base64Json = new MultimodalToolCallResultConverter(OutputFormat.base64).convert(result,
ToolMultimodalResult.class);

assertTrue(urlJson.contains("\"url\":\"" + url + "\""));
assertTrue(base64Json.contains("\"data\":\"data:image/png;base64," + base64 + "\""));
}

@Test
void convertsBase64OnlyMediaWithoutUrl() {
String base64 = "YXVkaW8=";
ToolMultimodalResult result = ToolMultimodalResult.of("audio",
ToolMultimodalResult.mediaFromBase64(base64, MimeTypeUtils.parseMimeType("audio/mpeg")));

String json = new MultimodalToolCallResultConverter(OutputFormat.base64).convert(result,
ToolMultimodalResult.class);

assertTrue(json.contains("\"data\":\"data:audio/mpeg;base64," + base64 + "\""));
}
}
74 changes: 39 additions & 35 deletions agentic-spring-ai-bom/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -71,18 +71,18 @@
<!-- Plugin Versions -->
<maven-compiler-plugin.version>3.11.0</maven-compiler-plugin.version>
<maven-surefire-plugin.version>3.1.2</maven-surefire-plugin.version>
<maven-failsafe-plugin.version>3.1.2</maven-failsafe-plugin.version>
<maven-failsafe-plugin.version>3.5.6</maven-failsafe-plugin.version>
<maven-javadoc-plugin.version>3.5.0</maven-javadoc-plugin.version>
<maven-source-plugin.version>3.3.0</maven-source-plugin.version>
<jacoco-maven-plugin.version>0.8.10</jacoco-maven-plugin.version>
<flatten-maven-plugin.version>1.5.0</flatten-maven-plugin.version>
<maven-deploy-plugin.version>3.1.1</maven-deploy-plugin.version>
<maven-source-plugin.version>3.4.0</maven-source-plugin.version>
<jacoco-maven-plugin.version>0.8.15</jacoco-maven-plugin.version>
<flatten-maven-plugin.version>1.8.0</flatten-maven-plugin.version>
<maven-deploy-plugin.version>3.1.4</maven-deploy-plugin.version>
<asciidoctor-maven-plugin.version>2.2.3</asciidoctor-maven-plugin.version>
<maven-assembly-plugin.version>3.7.0</maven-assembly-plugin.version>
<maven-dependency-plugin.version>3.5.0</maven-dependency-plugin.version>
<maven-site-plugin.version>4.0.0-M13</maven-site-plugin.version>
<maven-site-plugin.version>4.0.0-M16</maven-site-plugin.version>
<maven-project-info-reports-plugin.version>3.4.5</maven-project-info-reports-plugin.version>
<maven-jar-plugin.version>3.3.0</maven-jar-plugin.version>
<maven-jar-plugin.version>3.5.1</maven-jar-plugin.version>
<spring-javaformat-maven-plugin.version>0.0.39</spring-javaformat-maven-plugin.version>

<!-- Maven Central Portal -->
Expand Down Expand Up @@ -238,7 +238,7 @@
<plugin>
<groupId>com.diffplug.spotless</groupId>
<artifactId>spotless-maven-plugin</artifactId>
<version>2.44.5</version>
<version>2.46.1</version>
<configuration>
<java>
<removeUnusedImports/>
Expand Down Expand Up @@ -273,7 +273,7 @@
<plugin>
<groupId>com.mycila</groupId>
<artifactId>license-maven-plugin</artifactId>
<version>4.1</version>
<version>4.6</version>
<executions>
<execution>
<phase>validate</phase>
Expand All @@ -289,32 +289,36 @@
<year>2024</year>
</properties>
<quiet>true</quiet>
<header>HEADER</header>
<excludes>
<exclude>**/.antlr/**</exclude>
<exclude>**/aot.factories</exclude>
<exclude>**/.sdkmanrc</exclude>
<exclude>**/*.adoc</exclude>
<exclude>**/*.puml</exclude>
<exclude>**/pom.xml</exclude>
<exclude>**/*.properties</exclude>
<exclude>**/*.yaml</exclude>
<exclude>**/*.yml</exclude>
<exclude>**/*.map</exclude>
<exclude>**/*.html</exclude>
<exclude>**/*.xhtml</exclude>
<exclude>**/*.jsp</exclude>
<exclude>**/*.js</exclude>
<exclude>**/*.css</exclude>
<exclude>**/*.txt</exclude>
<exclude>**/*.xjb</exclude>
<exclude>**/*.ftl</exclude>
<exclude>**/*.xsd</exclude>
<exclude>**/*.xml</exclude>
<exclude>**/*.sh</exclude>
<exclude>**/generated/**</exclude>
<exclude>**/Dockerfile</exclude>
</excludes>
<licenseSets>
<licenseSet>
<header>com/mycila/maven/plugin/license/templates/APACHE-2.txt</header>
<excludes>
<exclude>**/.antlr/**</exclude>
<exclude>**/aot.factories</exclude>
<exclude>**/.sdkmanrc</exclude>
<exclude>**/*.adoc</exclude>
<exclude>**/*.puml</exclude>
<exclude>**/pom.xml</exclude>
<exclude>**/*.properties</exclude>
<exclude>**/*.yaml</exclude>
<exclude>**/*.yml</exclude>
<exclude>**/*.map</exclude>
<exclude>**/*.html</exclude>
<exclude>**/*.xhtml</exclude>
<exclude>**/*.jsp</exclude>
<exclude>**/*.js</exclude>
<exclude>**/*.css</exclude>
<exclude>**/*.txt</exclude>
<exclude>**/*.xjb</exclude>
<exclude>**/*.ftl</exclude>
<exclude>**/*.xsd</exclude>
<exclude>**/*.xml</exclude>
<exclude>**/*.sh</exclude>
<exclude>**/generated/**</exclude>
<exclude>**/Dockerfile</exclude>
</excludes>
</licenseSet>
</licenseSets>
</configuration>
</plugin>

Expand Down
30 changes: 15 additions & 15 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -95,15 +95,15 @@
<maven.compiler.target>${java.version}</maven.compiler.target>

<!-- Spring version -->
<spring-ai.version>2.0.0</spring-ai.version>
<spring-ai.version>2.0.1</spring-ai.version>
<agentic-spring-ai-extensions.version>2.1.0-dev</agentic-spring-ai-extensions.version>
<spring-boot.version>4.1.0</spring-boot.version>
<spring-boot.version>4.1.1</spring-boot.version>

<!-- DashScope SDK Version -->
<dashscope-sdk-java.version>2.15.1</dashscope-sdk-java.version>

<!-- Nacos -->
<nacos3.version>3.1.0</nacos3.version>
<nacos3.version>3.2.3</nacos3.version>

<!-- Redis -->
<redisson.version>3.52.0</redisson.version>
Expand All @@ -117,17 +117,17 @@

<ojdbc-provider-oson.version>1.0.6</ojdbc-provider-oson.version>

<commons-io.version>2.21.0</commons-io.version>
<commons-io.version>2.22.0</commons-io.version>

<commons-lang3.version>3.20.0</commons-lang3.version>

<mcp.sdk.version>2.0.0</mcp.sdk.version>
<mcp.sdk.version>2.0.1</mcp.sdk.version>
<a2a-sdk.version>0.2.5.Beta2</a2a-sdk.version>

<!-- JSpecify Version -->
<jspecify.version>1.0.0</jspecify.version>
<jspecify.version>1.0.1</jspecify.version>

<graalvm.polyglot.version>24.2.1</graalvm.polyglot.version>
<graalvm.polyglot.version>24.2.2</graalvm.polyglot.version>

<!-- Unit Test Config -->
<surefireArgLine>-Xms512m -Xmx1024m</surefireArgLine>
Expand All @@ -136,26 +136,26 @@
<maven-enforcer-plugin.version>3.6.2</maven-enforcer-plugin.version>
<!-- The version (range) of the JDK we want to use to compile. -->
<compiler.jdk.version>[17,)</compiler.jdk.version>
<maven-jar-plugin.version>3.3.0</maven-jar-plugin.version>
<maven-site-plugin.version>4.0.0-M13</maven-site-plugin.version>
<maven-deploy-plugin.version>3.1.1</maven-deploy-plugin.version>
<maven-source-plugin.version>3.3.0</maven-source-plugin.version>
<maven-jar-plugin.version>3.5.1</maven-jar-plugin.version>
<maven-site-plugin.version>4.0.0-M16</maven-site-plugin.version>
<maven-deploy-plugin.version>3.1.4</maven-deploy-plugin.version>
<maven-source-plugin.version>3.4.0</maven-source-plugin.version>
<jacoco-maven-plugin.version>0.8.10</jacoco-maven-plugin.version>
<flatten-maven-plugin.version>1.5.0</flatten-maven-plugin.version>
<flatten-maven-plugin.version>1.8.0</flatten-maven-plugin.version>
<maven-javadoc-plugin.version>3.5.0</maven-javadoc-plugin.version>
<maven-failsafe-plugin.version>3.1.2</maven-failsafe-plugin.version>
<maven-surefire-plugin.version>3.1.2</maven-surefire-plugin.version>
<maven-compiler-plugin.version>3.14.0</maven-compiler-plugin.version>
<springdoc-openapi-maven-plugin.version>1.4</springdoc-openapi-maven-plugin.version>
<springdoc-openapi-maven-plugin.version>1.5</springdoc-openapi-maven-plugin.version>
<spring-javaformat-maven-plugin.version>0.0.39</spring-javaformat-maven-plugin.version>
<sorter-maven-plugin.version>1.0.1</sorter-maven-plugin.version>
<maven-gpg-plugin.version>3.2.8</maven-gpg-plugin.version>
<maven.deploy.skip>false</maven.deploy.skip>

<!-- CheckStyle Maven Plugin -->
<maven-checkstyle-plugin.version>3.5.0</maven-checkstyle-plugin.version>
<maven-checkstyle-plugin.version>3.6.0</maven-checkstyle-plugin.version>
<puppycrawl-tools-checkstyle.version>9.3</puppycrawl-tools-checkstyle.version>
<spotless.version>2.44.5</spotless.version>
<spotless.version>2.46.1</spotless.version>

<central.publishing.maven.version>0.11.0</central.publishing.maven.version>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@
</scm>

<properties>
<docker-java.version>3.5.3</docker-java.version>
<docker-java.version>3.7.1</docker-java.version>
</properties>

<dependencyManagement>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ private ObservationMetadataOpenAiChatOptions(OpenAiChatOptions options, Map<Stri
options.getLogprobs(), options.getTopLogprobs(), options.getMaxCompletionTokens(), options.getN(),
copyList(options.getOutputModalities()), options.getOutputAudio(), options.getResponseFormat(),
options.getStreamOptions(), options.getSeed(), options.getToolChoice(), options.getUser(),
options.getParallelToolCalls(), options.getStore(), copyMap(options.getMetadata()),
options.getParallelToolCalls(), options.getStore(), options.getStrict(), copyMap(options.getMetadata()),
options.getReasoningEffort(), options.getVerbosity(), options.getServiceTier(),
options.getPromptCacheKey(), copyMap(options.getExtraBody()));
this.observationMetadata = sharedObservationMetadata ? useObservationMetadata(observationMetadata)
Expand Down Expand Up @@ -148,6 +148,7 @@ private void copyFrom(OpenAiChatOptions options) {
this.user = options.getUser();
this.parallelToolCalls = options.getParallelToolCalls();
this.store = options.getStore();
this.strict = options.getStrict();
this.metadata = copyMap(options.getMetadata());
this.reasoningEffort = options.getReasoningEffort();
this.verbosity = options.getVerbosity();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
import static org.junit.jupiter.api.Assertions.assertTrue;

class ObservationMetadataOpenAiChatOptionsTest {

Expand All @@ -32,6 +33,7 @@ void mutateBuildPreservesObservationMetadata() {
OpenAiChatOptions options = ObservationMetadataOpenAiChatOptions.from(OpenAiChatOptions.builder()
.model("configured-model")
.temperature(0.3)
.strict(true)
.build(), Map.of("promptKey", "prompt-a", "promptVersion", "v1"));

OpenAiChatOptions mutatedOptions = options.mutate()
Expand All @@ -42,6 +44,7 @@ void mutateBuildPreservesObservationMetadata() {
mutatedOptions);
assertEquals("configured-model", mutatedOptions.getModel());
assertEquals(0.7, mutatedOptions.getTemperature());
assertTrue(mutatedOptions.getStrict());
assertEquals(Map.of("promptKey", "prompt-a", "promptVersion", "v1"),
observationOptions.getObservationMetadata());
}
Expand Down
6 changes: 3 additions & 3 deletions tools/github-actions/setup-extensions/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ runs:
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
repository: agentic-spring-ai/agentic-for-spring-ai-extensions
repository: agentic-spring-ai/agentic-spring-ai-extensions
ref: 3aa691dd3da2b75da3a4378306e85927a124b0d5
path: target/agentic-for-spring-ai-extensions
path: target/agentic-spring-ai-extensions
- name: Install required extension artifacts
shell: bash
run: |
Expand All @@ -38,5 +38,5 @@ runs:
)
extensions_module_list=$(IFS=,; echo "${extensions_modules[*]}")
mvn -B -DskipTests \
-f target/agentic-for-spring-ai-extensions/pom.xml \
-f target/agentic-spring-ai-extensions/pom.xml \
-pl "$extensions_module_list" -am install
Loading
Loading