Skip to content

Commit 48e0aeb

Browse files
authored
Merge branch 'main' into cosmos-db-entra-id-support
Signed-off-by: Theo van Kraay <[email protected]>
2 parents fd959fd + bda702e commit 48e0aeb

File tree

1,012 files changed

+14389
-4455
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

1,012 files changed

+14389
-4455
lines changed

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ One way to run integration tests on part of the code is to first do a quick comp
103103
```
104104
Then run the integration test for a specific module using the `-pl` option
105105
```shell
106-
./mvnw verify -Pintegration-tests -pl spring-ai-spring-boot-testcontainers
106+
./mvnw verify -Pintegration-tests -pl spring-ai-spring-boot-testcontainers
107107
```
108108

109109
### Documentation
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!--
3+
~ Copyright 2023-2025 the original author or authors.
4+
~
5+
~ Licensed under the Apache License, Version 2.0 (the "License");
6+
~ you may not use this file except in compliance with the License.
7+
~ You may obtain a copy of the License at
8+
~
9+
~ https://www.apache.org/licenses/LICENSE-2.0
10+
~
11+
~ Unless required by applicable law or agreed to in writing, software
12+
~ distributed under the License is distributed on an "AS IS" BASIS,
13+
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
~ See the License for the specific language governing permissions and
15+
~ limitations under the License.
16+
-->
17+
18+
<project xmlns="http://maven.apache.org/POM/4.0.0"
19+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
20+
<modelVersion>4.0.0</modelVersion>
21+
<parent>
22+
<groupId>org.springframework.ai</groupId>
23+
<artifactId>spring-ai-parent</artifactId>
24+
<version>1.0.0-SNAPSHOT</version>
25+
<relativePath>../../pom.xml</relativePath>
26+
</parent>
27+
<artifactId>spring-ai-advisors-vector-store</artifactId>
28+
<packaging>jar</packaging>
29+
<name>Spring AI Advisors</name>
30+
<description>Chat client advisors for Spring AI</description>
31+
<url>https://github.com/spring-projects/spring-ai</url>
32+
33+
<scm>
34+
<url>https://github.com/spring-projects/spring-ai</url>
35+
<connection>git://github.com/spring-projects/spring-ai.git</connection>
36+
<developerConnection>[email protected]:spring-projects/spring-ai.git</developerConnection>
37+
</scm>
38+
39+
<properties>
40+
<maven.compiler.target>17</maven.compiler.target>
41+
<maven.compiler.source>17</maven.compiler.source>
42+
</properties>
43+
44+
<dependencies>
45+
46+
<dependency>
47+
<groupId>org.springframework.ai</groupId>
48+
<artifactId>spring-ai-client-chat</artifactId>
49+
<version>${project.parent.version}</version>
50+
</dependency>
51+
52+
<dependency>
53+
<groupId>org.springframework.ai</groupId>
54+
<artifactId>spring-ai-vector-store</artifactId>
55+
<version>${project.parent.version}</version>
56+
</dependency>
57+
58+
<!-- test dependencies -->
59+
<dependency>
60+
<groupId>org.springframework.boot</groupId>
61+
<artifactId>spring-boot-starter-test</artifactId>
62+
<scope>test</scope>
63+
</dependency>
64+
</dependencies>
65+
66+
</project>

spring-ai-core/src/main/java/org/springframework/ai/chat/client/advisor/QuestionAnswerAdvisor.java renamed to advisors/spring-ai-advisors-vector-store/src/main/java/org/springframework/ai/chat/client/advisor/vectorstore/QuestionAnswerAdvisor.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
* limitations under the License.
1515
*/
1616

17-
package org.springframework.ai.chat.client.advisor;
17+
package org.springframework.ai.chat.client.advisor.vectorstore;
1818

1919
import java.util.HashMap;
2020
import java.util.List;

spring-ai-core/src/main/java/org/springframework/ai/chat/client/advisor/VectorStoreChatMemoryAdvisor.java renamed to advisors/spring-ai-advisors-vector-store/src/main/java/org/springframework/ai/chat/client/advisor/vectorstore/VectorStoreChatMemoryAdvisor.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
* limitations under the License.
1515
*/
1616

17-
package org.springframework.ai.chat.client.advisor;
17+
package org.springframework.ai.chat.client.advisor.vectorstore;
1818

1919
import java.util.HashMap;
2020
import java.util.List;
@@ -23,6 +23,7 @@
2323

2424
import reactor.core.publisher.Flux;
2525

26+
import org.springframework.ai.chat.client.advisor.AbstractChatMemoryAdvisor;
2627
import org.springframework.ai.chat.client.advisor.api.AdvisedRequest;
2728
import org.springframework.ai.chat.client.advisor.api.AdvisedResponse;
2829
import org.springframework.ai.chat.client.advisor.api.Advisor;

spring-ai-core/src/main/java/org/springframework/ai/observation/package-info.java renamed to advisors/spring-ai-advisors-vector-store/src/main/java/org/springframework/ai/chat/client/advisor/vectorstore/package-info.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,11 @@
1515
*/
1616

1717
/**
18-
* Provides classes for observing events in the system.
18+
* Spring AI chat client advisors package.
1919
*/
2020
@NonNullApi
2121
@NonNullFields
22-
package org.springframework.ai.observation;
22+
package org.springframework.ai.chat.client.advisor.vectorstore;
2323

2424
import org.springframework.lang.NonNullApi;
2525
import org.springframework.lang.NonNullFields;

spring-ai-core/src/test/java/org/springframework/ai/chat/client/advisor/QuestionAnswerAdvisorTests.java renamed to advisors/spring-ai-advisors-vector-store/src/test/java/org/springframework/ai/chat/client/advisor/vectorstore/QuestionAnswerAdvisorTests.java

+20-19
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,13 @@
1414
* limitations under the License.
1515
*/
1616

17-
package org.springframework.ai.chat.client.advisor;
17+
package org.springframework.ai.chat.client.advisor.vectorstore;
1818

1919
import java.time.Duration;
2020
import java.util.List;
2121
import java.util.Map;
2222

23+
import org.assertj.core.api.Assertions;
2324
import org.junit.jupiter.api.Test;
2425
import org.junit.jupiter.api.extension.ExtendWith;
2526
import org.mockito.ArgumentCaptor;
@@ -128,19 +129,19 @@ public Duration getTokensReset() {
128129
//formatter:on
129130

130131
// Ensure the metadata is correctly copied over
131-
assertThat(response.getMetadata().getModel()).isEqualTo("model1");
132-
assertThat(response.getMetadata().getId()).isEqualTo("678");
133-
assertThat(response.getMetadata().getRateLimit().getRequestsLimit()).isEqualTo(5L);
134-
assertThat(response.getMetadata().getRateLimit().getRequestsRemaining()).isEqualTo(6L);
135-
assertThat(response.getMetadata().getRateLimit().getRequestsReset()).isEqualTo(Duration.ofSeconds(7));
136-
assertThat(response.getMetadata().getRateLimit().getTokensLimit()).isEqualTo(8L);
137-
assertThat(response.getMetadata().getRateLimit().getTokensRemaining()).isEqualTo(8L);
138-
assertThat(response.getMetadata().getRateLimit().getTokensReset()).isEqualTo(Duration.ofSeconds(9));
139-
assertThat(response.getMetadata().getUsage().getPromptTokens()).isEqualTo(6L);
140-
assertThat(response.getMetadata().getUsage().getCompletionTokens()).isEqualTo(7L);
141-
assertThat(response.getMetadata().getUsage().getTotalTokens()).isEqualTo(6L + 7L);
142-
assertThat(response.getMetadata().get("key6").toString()).isEqualTo("value6");
143-
assertThat(response.getMetadata().get("key1").toString()).isEqualTo("value1");
132+
Assertions.assertThat(response.getMetadata().getModel()).isEqualTo("model1");
133+
Assertions.assertThat(response.getMetadata().getId()).isEqualTo("678");
134+
Assertions.assertThat(response.getMetadata().getRateLimit().getRequestsLimit()).isEqualTo(5L);
135+
Assertions.assertThat(response.getMetadata().getRateLimit().getRequestsRemaining()).isEqualTo(6L);
136+
Assertions.assertThat(response.getMetadata().getRateLimit().getRequestsReset()).isEqualTo(Duration.ofSeconds(7));
137+
Assertions.assertThat(response.getMetadata().getRateLimit().getTokensLimit()).isEqualTo(8L);
138+
Assertions.assertThat(response.getMetadata().getRateLimit().getTokensRemaining()).isEqualTo(8L);
139+
Assertions.assertThat(response.getMetadata().getRateLimit().getTokensReset()).isEqualTo(Duration.ofSeconds(9));
140+
Assertions.assertThat(response.getMetadata().getUsage().getPromptTokens()).isEqualTo(6L);
141+
Assertions.assertThat(response.getMetadata().getUsage().getCompletionTokens()).isEqualTo(7L);
142+
Assertions.assertThat(response.getMetadata().getUsage().getTotalTokens()).isEqualTo(6L + 7L);
143+
Assertions.assertThat(response.getMetadata().get("key6").toString()).isEqualTo("value6");
144+
Assertions.assertThat(response.getMetadata().get("key1").toString()).isEqualTo("value1");
144145

145146
String content = response.getResult().getOutput().getText();
146147

@@ -171,9 +172,9 @@ public Duration getTokensReset() {
171172
the user that you can't answer the question.
172173
""");
173174

174-
assertThat(this.vectorSearchCaptor.getValue().getFilterExpression()).isEqualTo(new FilterExpressionBuilder().eq("type", "Spring").build());
175-
assertThat(this.vectorSearchCaptor.getValue().getSimilarityThreshold()).isEqualTo(0.99d);
176-
assertThat(this.vectorSearchCaptor.getValue().getTopK()).isEqualTo(6);
175+
Assertions.assertThat(this.vectorSearchCaptor.getValue().getFilterExpression()).isEqualTo(new FilterExpressionBuilder().eq("type", "Spring").build());
176+
Assertions.assertThat(this.vectorSearchCaptor.getValue().getSimilarityThreshold()).isEqualTo(0.99d);
177+
Assertions.assertThat(this.vectorSearchCaptor.getValue().getTopK()).isEqualTo(6);
177178
}
178179

179180
@Test
@@ -201,7 +202,7 @@ public void qaAdvisorTakesUserTextParametersIntoAccountForSimilaritySearch() {
201202
var userPrompt = this.promptCaptor.getValue().getInstructions().get(0).getText();
202203
assertThat(userPrompt).doesNotContain(userTextTemplate);
203204
assertThat(userPrompt).contains(expectedQuery);
204-
assertThat(this.vectorSearchCaptor.getValue().getQuery()).isEqualTo(expectedQuery);
205+
Assertions.assertThat(this.vectorSearchCaptor.getValue().getQuery()).isEqualTo(expectedQuery);
205206
}
206207

207208
@Test
@@ -230,7 +231,7 @@ public void qaAdvisorTakesUserParameterizedUserMessagesIntoAccountForSimilarityS
230231
var userPrompt = this.promptCaptor.getValue().getInstructions().get(0).getText();
231232
assertThat(userPrompt).doesNotContain(userTextTemplate);
232233
assertThat(userPrompt).contains(expectedQuery);
233-
assertThat(this.vectorSearchCaptor.getValue().getQuery()).isEqualTo(expectedQuery);
234+
Assertions.assertThat(this.vectorSearchCaptor.getValue().getQuery()).isEqualTo(expectedQuery);
234235
}
235236

236237
}

auto-configurations/common/spring-ai-autoconfigure-retry/pom.xml

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
<modelVersion>4.0.0</modelVersion>
66
<parent>
77
<groupId>org.springframework.ai</groupId>
8-
<artifactId>spring-ai</artifactId>
8+
<artifactId>spring-ai-parent</artifactId>
99
<version>1.0.0-SNAPSHOT</version>
1010
<relativePath>../../../pom.xml</relativePath>
1111
</parent>

auto-configurations/mcp/spring-ai-autoconfigure-mcp-client/pom.xml

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
<modelVersion>4.0.0</modelVersion>
66
<parent>
77
<groupId>org.springframework.ai</groupId>
8-
<artifactId>spring-ai</artifactId>
8+
<artifactId>spring-ai-parent</artifactId>
99
<version>1.0.0-SNAPSHOT</version>
1010
<relativePath>../../../pom.xml</relativePath>
1111
</parent>

auto-configurations/mcp/spring-ai-autoconfigure-mcp-client/src/main/java/org/springframework/ai/mcp/client/autoconfigure/McpClientAutoConfiguration.java

-53
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,8 @@
2727
import org.springframework.ai.mcp.client.autoconfigure.configurer.McpAsyncClientConfigurer;
2828
import org.springframework.ai.mcp.client.autoconfigure.configurer.McpSyncClientConfigurer;
2929
import org.springframework.ai.mcp.client.autoconfigure.properties.McpClientCommonProperties;
30-
import org.springframework.ai.mcp.AsyncMcpToolCallbackProvider;
31-
import org.springframework.ai.mcp.SyncMcpToolCallbackProvider;
3230
import org.springframework.ai.mcp.customizer.McpAsyncClientCustomizer;
3331
import org.springframework.ai.mcp.customizer.McpSyncClientCustomizer;
34-
import org.springframework.ai.tool.ToolCallback;
35-
import org.springframework.ai.tool.ToolCallbackProvider;
3632
import org.springframework.beans.factory.ObjectProvider;
3733
import org.springframework.boot.autoconfigure.AutoConfiguration;
3834
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
@@ -179,36 +175,6 @@ public List<McpSyncClient> mcpSyncClients(McpSyncClientConfigurer mcpSyncClientC
179175
return mcpSyncClients;
180176
}
181177

182-
/**
183-
* Creates tool callbacks for all configured MCP clients.
184-
*
185-
* <p>
186-
* These callbacks enable integration with Spring AI's tool execution framework,
187-
* allowing MCP tools to be used as part of AI interactions.
188-
* @param mcpClientsProvider provider of MCP sync clients
189-
* @return list of tool callbacks for MCP integration
190-
*/
191-
@Bean
192-
@ConditionalOnProperty(prefix = McpClientCommonProperties.CONFIG_PREFIX, name = "type", havingValue = "SYNC",
193-
matchIfMissing = true)
194-
public ToolCallbackProvider toolCallbacks(ObjectProvider<List<McpSyncClient>> mcpClientsProvider) {
195-
List<McpSyncClient> mcpClients = mcpClientsProvider.stream().flatMap(List::stream).toList();
196-
return new SyncMcpToolCallbackProvider(mcpClients);
197-
}
198-
199-
/**
200-
* @deprecated replaced by {@link #toolCallbacks(ObjectProvider)} that returns a
201-
* {@link ToolCallbackProvider} instead of a list of {@link ToolCallback}
202-
*/
203-
@Deprecated
204-
@Bean
205-
@ConditionalOnProperty(prefix = McpClientCommonProperties.CONFIG_PREFIX, name = "type", havingValue = "SYNC",
206-
matchIfMissing = true)
207-
public List<ToolCallback> toolCallbacksDeprecated(ObjectProvider<List<McpSyncClient>> mcpClientsProvider) {
208-
List<McpSyncClient> mcpClients = mcpClientsProvider.stream().flatMap(List::stream).toList();
209-
return List.of(new SyncMcpToolCallbackProvider(mcpClients).getToolCallbacks());
210-
}
211-
212178
/**
213179
* Record class that implements {@link AutoCloseable} to ensure proper cleanup of MCP
214180
* clients.
@@ -292,25 +258,6 @@ public List<McpAsyncClient> mcpAsyncClients(McpAsyncClientConfigurer mcpSyncClie
292258
return mcpSyncClients;
293259
}
294260

295-
/**
296-
* @deprecated replaced by {@link #asyncToolCallbacks(ObjectProvider)} that returns a
297-
* {@link ToolCallbackProvider} instead of a list of {@link ToolCallback}
298-
*/
299-
@Deprecated
300-
@Bean
301-
@ConditionalOnProperty(prefix = McpClientCommonProperties.CONFIG_PREFIX, name = "type", havingValue = "ASYNC")
302-
public List<ToolCallback> asyncToolCallbacksDeprecated(ObjectProvider<List<McpAsyncClient>> mcpClientsProvider) {
303-
List<McpAsyncClient> mcpClients = mcpClientsProvider.stream().flatMap(List::stream).toList();
304-
return List.of(new AsyncMcpToolCallbackProvider(mcpClients).getToolCallbacks());
305-
}
306-
307-
@Bean
308-
@ConditionalOnProperty(prefix = McpClientCommonProperties.CONFIG_PREFIX, name = "type", havingValue = "ASYNC")
309-
public ToolCallbackProvider asyncToolCallbacks(ObjectProvider<List<McpAsyncClient>> mcpClientsProvider) {
310-
List<McpAsyncClient> mcpClients = mcpClientsProvider.stream().flatMap(List::stream).toList();
311-
return new AsyncMcpToolCallbackProvider(mcpClients);
312-
}
313-
314261
public record CloseableMcpAsyncClients(List<McpAsyncClient> clients) implements AutoCloseable {
315262
@Override
316263
public void close() {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
/*
2+
* Copyright 2025-2025 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.ai.mcp.client.autoconfigure;
18+
19+
import java.util.List;
20+
21+
import io.modelcontextprotocol.client.McpAsyncClient;
22+
import io.modelcontextprotocol.client.McpSyncClient;
23+
24+
import org.springframework.ai.mcp.AsyncMcpToolCallbackProvider;
25+
import org.springframework.ai.mcp.SyncMcpToolCallbackProvider;
26+
import org.springframework.ai.mcp.client.autoconfigure.properties.McpClientCommonProperties;
27+
import org.springframework.ai.tool.ToolCallbackProvider;
28+
import org.springframework.beans.factory.ObjectProvider;
29+
import org.springframework.boot.autoconfigure.AutoConfiguration;
30+
import org.springframework.boot.autoconfigure.condition.AllNestedConditions;
31+
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
32+
import org.springframework.boot.context.properties.EnableConfigurationProperties;
33+
import org.springframework.context.annotation.Bean;
34+
import org.springframework.context.annotation.Conditional;
35+
36+
/**
37+
*/
38+
@AutoConfiguration(after = { McpClientAutoConfiguration.class })
39+
@EnableConfigurationProperties(McpClientCommonProperties.class)
40+
@Conditional(McpToolCallbackAutoConfiguration.McpToolCallbackAutoconfigurationCondition.class)
41+
public class McpToolCallbackAutoConfiguration {
42+
43+
public static class McpToolCallbackAutoconfigurationCondition extends AllNestedConditions {
44+
45+
public McpToolCallbackAutoconfigurationCondition() {
46+
super(ConfigurationPhase.PARSE_CONFIGURATION);
47+
}
48+
49+
@ConditionalOnProperty(prefix = McpClientCommonProperties.CONFIG_PREFIX, name = "enabled", havingValue = "true",
50+
matchIfMissing = true)
51+
static class McpAutoConfigEnabled {
52+
53+
}
54+
55+
@ConditionalOnProperty(prefix = McpClientCommonProperties.CONFIG_PREFIX + ".toolcallback", name = "enabled",
56+
havingValue = "true", matchIfMissing = false)
57+
static class ToolCallbackProviderEnabled {
58+
59+
}
60+
61+
}
62+
63+
/**
64+
* Creates tool callbacks for all configured MCP clients.
65+
*
66+
* <p>
67+
* These callbacks enable integration with Spring AI's tool execution framework,
68+
* allowing MCP tools to be used as part of AI interactions.
69+
* @param syncMcpClients provider of MCP sync clients
70+
* @return list of tool callbacks for MCP integration
71+
*/
72+
@Bean
73+
@ConditionalOnProperty(prefix = McpClientCommonProperties.CONFIG_PREFIX, name = "type", havingValue = "SYNC",
74+
matchIfMissing = true)
75+
public ToolCallbackProvider mcpToolCallbacks(ObjectProvider<List<McpSyncClient>> syncMcpClients) {
76+
List<McpSyncClient> mcpClients = syncMcpClients.stream().flatMap(List::stream).toList();
77+
return new SyncMcpToolCallbackProvider(mcpClients);
78+
}
79+
80+
@Bean
81+
@ConditionalOnProperty(prefix = McpClientCommonProperties.CONFIG_PREFIX, name = "type", havingValue = "ASYNC")
82+
public ToolCallbackProvider mcpAsyncToolCallbacks(ObjectProvider<List<McpAsyncClient>> mcpClientsProvider) {
83+
List<McpAsyncClient> mcpClients = mcpClientsProvider.stream().flatMap(List::stream).toList();
84+
return new AsyncMcpToolCallbackProvider(mcpClients);
85+
}
86+
87+
}

auto-configurations/mcp/spring-ai-autoconfigure-mcp-client/src/main/java/org/springframework/ai/mcp/client/autoconfigure/NamedClientMcpTransport.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
*/
1616
package org.springframework.ai.mcp.client.autoconfigure;
1717

18-
import io.modelcontextprotocol.spec.ClientMcpTransport;
18+
import io.modelcontextprotocol.spec.McpClientTransport;
1919

2020
/**
2121
* A named MCP client transport. Usually created by the transport auto-configurations, but
@@ -26,6 +26,6 @@
2626
* @author Christian Tzolov
2727
* @since 1.0.0
2828
*/
29-
public record NamedClientMcpTransport(String name, ClientMcpTransport transport) {
29+
public record NamedClientMcpTransport(String name, McpClientTransport transport) {
3030

3131
}

0 commit comments

Comments
 (0)