Skip to content

Commit 0527909

Browse files
committed
Merge branch 'main' into aws-bedrock-converse
2 parents 0fffb5a + 067a33d commit 0527909

File tree

298 files changed

+10817
-2553
lines changed

Some content is hidden

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

298 files changed

+10817
-2553
lines changed

.editorconfig

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
root = true
2+
3+
[*.{adoc,bat,groovy,html,java,js,jsp,kt,kts,md,properties,py,rb,sh,sql,svg,txt,xml,xsd}]
4+
charset = utf-8
5+
6+
[*.{groovy,java,kt,kts,xml,xsd}]
7+
indent_style = tab
8+
indent_size = 4
9+
continuation_indent_size = 8
10+
end_of_line = lf

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ Spring AI supports many AI models. For an overview see here. Specific models c
8989
* OpenAI
9090
* Azure OpenAI
9191
* Amazon Bedrock (Anthropic, Llama, Cohere, Titan, Jurassic2, Mistral)
92-
* HuggingFace
92+
* Hugging Face
9393
* Google VertexAI (PaLM2, Gemini)
9494
* Mistral AI
9595
* Stability AI
@@ -163,7 +163,7 @@ Though the `DocumentWriter` interface isn't exclusively for Vector Database writ
163163

164164
**Vector Stores:** Vector Databases are instrumental in incorporating your data with AI models.
165165
They ascertain which document sections the AI should use for generating responses.
166-
Examples of Vector Databases include Chroma, Postgres, Pinecone, Qdrant, Weaviate, Mongo Atlas, and Redis. Spring AI's `VectorStore` abstraction permits effortless transitions between database implementations.
166+
Examples of Vector Databases include Chroma, Oracle, Postgres, Pinecone, Qdrant, Weaviate, Mongo Atlas, and Redis. Spring AI's `VectorStore` abstraction permits effortless transitions between database implementations.
167167

168168

169169

document-readers/tika-reader/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
</scm>
2222

2323
<properties>
24-
<tika.version>2.9.0</tika.version>
24+
<tika.version>2.9.2</tika.version>
2525
</properties>
2626

2727
<dependencies>

models/spring-ai-anthropic/src/main/java/org/springframework/ai/anthropic/AnthropicChatModel.java

Lines changed: 9 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@
4141
import org.springframework.ai.anthropic.metadata.AnthropicChatResponseMetadata;
4242
import org.springframework.ai.chat.model.ChatResponse;
4343
import org.springframework.ai.chat.model.Generation;
44-
import org.springframework.ai.chat.model.StreamingChatModel;
4544
import org.springframework.ai.chat.messages.MessageType;
4645
import org.springframework.ai.chat.metadata.ChatGenerationMetadata;
4746
import org.springframework.ai.chat.prompt.ChatOptions;
@@ -59,11 +58,12 @@
5958
* The {@link ChatModel} implementation for the Anthropic service.
6059
*
6160
* @author Christian Tzolov
61+
* @author luocongqiu
6262
* @since 1.0.0
6363
*/
6464
public class AnthropicChatModel extends
6565
AbstractFunctionCallSupport<AnthropicApi.RequestMessage, AnthropicApi.ChatCompletionRequest, ResponseEntity<AnthropicApi.ChatCompletion>>
66-
implements ChatModel, StreamingChatModel {
66+
implements ChatModel {
6767

6868
private static final Logger logger = LoggerFactory.getLogger(AnthropicChatModel.class);
6969

@@ -81,7 +81,7 @@ public class AnthropicChatModel extends
8181
/**
8282
* The default options used for the chat completion requests.
8383
*/
84-
private AnthropicChatOptions defaultOptions;
84+
private final AnthropicChatOptions defaultOptions;
8585

8686
/**
8787
* The retry template used to retry the OpenAI API calls.
@@ -280,20 +280,14 @@ ChatCompletionRequest createRequest(Prompt prompt, boolean stream) {
280280
systemPrompt, this.defaultOptions.getMaxTokens(), this.defaultOptions.getTemperature(), stream);
281281

282282
if (prompt.getOptions() != null) {
283-
if (prompt.getOptions() instanceof ChatOptions runtimeOptions) {
284-
AnthropicChatOptions updatedRuntimeOptions = ModelOptionsUtils.copyToTarget(runtimeOptions,
285-
ChatOptions.class, AnthropicChatOptions.class);
283+
AnthropicChatOptions updatedRuntimeOptions = ModelOptionsUtils.copyToTarget(prompt.getOptions(),
284+
ChatOptions.class, AnthropicChatOptions.class);
286285

287-
Set<String> promptEnabledFunctions = this.handleFunctionCallbackConfigurations(updatedRuntimeOptions,
288-
IS_RUNTIME_CALL);
289-
functionsForThisRequest.addAll(promptEnabledFunctions);
286+
Set<String> promptEnabledFunctions = this.handleFunctionCallbackConfigurations(updatedRuntimeOptions,
287+
IS_RUNTIME_CALL);
288+
functionsForThisRequest.addAll(promptEnabledFunctions);
290289

291-
request = ModelOptionsUtils.merge(updatedRuntimeOptions, request, ChatCompletionRequest.class);
292-
}
293-
else {
294-
throw new IllegalArgumentException("Prompt options are not of type ChatOptions: "
295-
+ prompt.getOptions().getClass().getSimpleName());
296-
}
290+
request = ModelOptionsUtils.merge(updatedRuntimeOptions, request, ChatCompletionRequest.class);
297291
}
298292

299293
if (this.defaultOptions != null) {

models/spring-ai-anthropic/src/main/java/org/springframework/ai/anthropic/AnthropicChatOptions.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import com.fasterxml.jackson.annotation.JsonInclude.Include;
2626
import com.fasterxml.jackson.annotation.JsonProperty;
2727

28+
import org.springframework.ai.anthropic.api.AnthropicApi;
2829
import org.springframework.ai.anthropic.api.AnthropicApi.ChatCompletionRequest;
2930
import org.springframework.ai.chat.prompt.ChatOptions;
3031
import org.springframework.ai.model.function.FunctionCallback;
@@ -90,6 +91,11 @@ public Builder withModel(String model) {
9091
return this;
9192
}
9293

94+
public Builder withModel(AnthropicApi.ChatModel model) {
95+
this.options.model = model.getValue();
96+
return this;
97+
}
98+
9399
public Builder withMaxTokens(Integer maxTokens) {
94100
this.options.maxTokens = maxTokens;
95101
return this;

models/spring-ai-anthropic/src/test/java/org/springframework/ai/anthropic/AnthropicChatModelIT.java

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -202,10 +202,11 @@ void functionCallTest() {
202202
List<Message> messages = new ArrayList<>(List.of(userMessage));
203203

204204
var promptOptions = AnthropicChatOptions.builder()
205-
.withModel(AnthropicApi.ChatModel.CLAUDE_3_OPUS.getValue())
205+
.withModel(AnthropicApi.ChatModel.CLAUDE_3_OPUS)
206206
.withFunctionCallbacks(List.of(FunctionCallbackWrapper.builder(new MockWeatherService())
207207
.withName("getCurrentWeather")
208-
.withDescription("Get the weather in location. Return temperature in 36°F or 36°C format.")
208+
.withDescription(
209+
"Get the weather in location. Return temperature in 36°F or 36°C format. Use multi-turn if needed.")
209210
.build()))
210211
.build();
211212

@@ -214,9 +215,7 @@ void functionCallTest() {
214215
logger.info("Response: {}", response);
215216

216217
Generation generation = response.getResult();
217-
assertThat(generation.getOutput().getContent()).containsAnyOf("30.0", "30");
218-
assertThat(generation.getOutput().getContent()).containsAnyOf("10.0", "10");
219-
assertThat(generation.getOutput().getContent()).containsAnyOf("15.0", "15");
218+
assertThat(generation.getOutput().getContent()).contains("30", "10", "15");
220219
}
221220

222221
}

models/spring-ai-azure-openai/src/main/java/org/springframework/ai/azure/openai/AzureOpenAiChatModel.java

Lines changed: 41 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,16 @@
2020
import com.azure.ai.openai.models.ChatCompletions;
2121
import com.azure.ai.openai.models.ChatCompletionsFunctionToolCall;
2222
import com.azure.ai.openai.models.ChatCompletionsFunctionToolDefinition;
23+
import com.azure.ai.openai.models.ChatCompletionsJsonResponseFormat;
2324
import com.azure.ai.openai.models.ChatCompletionsOptions;
25+
import com.azure.ai.openai.models.ChatCompletionsResponseFormat;
26+
import com.azure.ai.openai.models.ChatCompletionsTextResponseFormat;
2427
import com.azure.ai.openai.models.ChatCompletionsToolCall;
2528
import com.azure.ai.openai.models.ChatCompletionsToolDefinition;
29+
import com.azure.ai.openai.models.ChatMessageContentItem;
30+
import com.azure.ai.openai.models.ChatMessageImageContentItem;
31+
import com.azure.ai.openai.models.ChatMessageImageUrl;
32+
import com.azure.ai.openai.models.ChatMessageTextContentItem;
2633
import com.azure.ai.openai.models.ChatRequestAssistantMessage;
2734
import com.azure.ai.openai.models.ChatRequestMessage;
2835
import com.azure.ai.openai.models.ChatRequestSystemMessage;
@@ -32,23 +39,18 @@
3239
import com.azure.ai.openai.models.ContentFilterResultsForPrompt;
3340
import com.azure.ai.openai.models.FunctionCall;
3441
import com.azure.ai.openai.models.FunctionDefinition;
35-
import com.azure.ai.openai.models.ChatCompletionsJsonResponseFormat;
36-
import com.azure.ai.openai.models.ChatCompletionsTextResponseFormat;
37-
import com.azure.ai.openai.models.ChatCompletionsResponseFormat;
3842
import com.azure.core.util.BinaryData;
3943
import com.azure.core.util.IterableStream;
4044
import org.slf4j.Logger;
4145
import org.slf4j.LoggerFactory;
42-
4346
import org.springframework.ai.azure.openai.metadata.AzureOpenAiChatResponseMetadata;
44-
import org.springframework.ai.chat.model.ChatModel;
45-
import org.springframework.ai.chat.model.ChatResponse;
46-
import org.springframework.ai.chat.model.Generation;
47-
import org.springframework.ai.chat.model.StreamingChatModel;
4847
import org.springframework.ai.chat.messages.Message;
4948
import org.springframework.ai.chat.metadata.ChatGenerationMetadata;
5049
import org.springframework.ai.chat.metadata.PromptMetadata;
5150
import org.springframework.ai.chat.metadata.PromptMetadata.PromptFilterMetadata;
51+
import org.springframework.ai.chat.model.ChatModel;
52+
import org.springframework.ai.chat.model.ChatResponse;
53+
import org.springframework.ai.chat.model.Generation;
5254
import org.springframework.ai.chat.prompt.ChatOptions;
5355
import org.springframework.ai.chat.prompt.Prompt;
5456
import org.springframework.ai.model.ModelOptionsUtils;
@@ -58,6 +60,7 @@
5860
import org.springframework.util.CollectionUtils;
5961
import reactor.core.publisher.Flux;
6062

63+
import java.util.ArrayList;
6164
import java.util.Collections;
6265
import java.util.HashSet;
6366
import java.util.List;
@@ -74,12 +77,13 @@
7477
* @author John Blum
7578
* @author Christian Tzolov
7679
* @author Grogdunn
80+
* @author Benoit Moussaud
81+
* @author luocongqiu
7782
* @see ChatModel
7883
* @see com.azure.ai.openai.OpenAIClient
7984
*/
80-
public class AzureOpenAiChatModel
81-
extends AbstractFunctionCallSupport<ChatRequestMessage, ChatCompletionsOptions, ChatCompletions>
82-
implements ChatModel, StreamingChatModel {
85+
public class AzureOpenAiChatModel extends
86+
AbstractFunctionCallSupport<ChatRequestMessage, ChatCompletionsOptions, ChatCompletions> implements ChatModel {
8387

8488
private static final String DEFAULT_DEPLOYMENT_NAME = "gpt-35-turbo";
8589

@@ -88,14 +92,14 @@ public class AzureOpenAiChatModel
8892
private final Logger logger = LoggerFactory.getLogger(getClass());
8993

9094
/**
91-
* The configuration information for a chat completions request.
95+
* The {@link OpenAIClient} used to interact with the Azure OpenAI service.
9296
*/
93-
private AzureOpenAiChatOptions defaultOptions;
97+
private final OpenAIClient openAIClient;
9498

9599
/**
96-
* The {@link OpenAIClient} used to interact with the Azure OpenAI service.
100+
* The configuration information for a chat completions request.
97101
*/
98-
private final OpenAIClient openAIClient;
102+
private AzureOpenAiChatOptions defaultOptions;
99103

100104
public AzureOpenAiChatModel(OpenAIClient microsoftOpenAiClient) {
101105
this(microsoftOpenAiClient,
@@ -143,8 +147,7 @@ public ChatResponse call(Prompt prompt) {
143147
ChatCompletions chatCompletions = this.callWithFunctionSupport(options);
144148
logger.trace("Azure ChatCompletions: {}", chatCompletions);
145149

146-
List<Generation> generations = chatCompletions.getChoices()
147-
.stream()
150+
List<Generation> generations = nullSafeList(chatCompletions.getChoices()).stream()
148151
.map(choice -> new Generation(choice.getMessage().getContent())
149152
.withGenerationMetadata(generateChoiceMetadata(choice)))
150153
.toList();
@@ -229,24 +232,17 @@ ChatCompletionsOptions toAzureChatCompletionsOptions(Prompt prompt) {
229232
}
230233

231234
if (prompt.getOptions() != null) {
232-
if (prompt.getOptions() instanceof ChatOptions runtimeOptions) {
233-
AzureOpenAiChatOptions updatedRuntimeOptions = ModelOptionsUtils.copyToTarget(runtimeOptions,
234-
ChatOptions.class, AzureOpenAiChatOptions.class);
235-
// JSON merge doesn't due to Azure OpenAI service bug:
236-
// https://github.com/Azure/azure-sdk-for-java/issues/38183
237-
// options = ModelOptionsUtils.merge(runtimeOptions, options,
238-
// ChatCompletionsOptions.class);
239-
options = merge(updatedRuntimeOptions, options);
240-
241-
Set<String> promptEnabledFunctions = this.handleFunctionCallbackConfigurations(updatedRuntimeOptions,
242-
IS_RUNTIME_CALL);
243-
functionsForThisRequest.addAll(promptEnabledFunctions);
235+
AzureOpenAiChatOptions updatedRuntimeOptions = ModelOptionsUtils.copyToTarget(prompt.getOptions(),
236+
ChatOptions.class, AzureOpenAiChatOptions.class);
237+
// JSON merge doesn't due to Azure OpenAI service bug:
238+
// https://github.com/Azure/azure-sdk-for-java/issues/38183
239+
// options = ModelOptionsUtils.merge(runtimeOptions, options,
240+
// ChatCompletionsOptions.class);
241+
options = merge(updatedRuntimeOptions, options);
244242

245-
}
246-
else {
247-
throw new IllegalArgumentException("Prompt options are not of type ChatCompletionsOptions:"
248-
+ prompt.getOptions().getClass().getSimpleName());
249-
}
243+
Set<String> promptEnabledFunctions = this.handleFunctionCallbackConfigurations(updatedRuntimeOptions,
244+
IS_RUNTIME_CALL);
245+
functionsForThisRequest.addAll(promptEnabledFunctions);
250246
}
251247

252248
// Add the enabled functions definitions to the request's tools parameter.
@@ -278,7 +274,17 @@ private ChatRequestMessage fromSpringAiMessage(Message message) {
278274

279275
switch (message.getMessageType()) {
280276
case USER:
281-
return new ChatRequestUserMessage(message.getContent());
277+
// https://github.com/Azure/azure-sdk-for-java/blob/main/sdk/openai/azure-ai-openai/README.md#text-completions-with-images
278+
List<ChatMessageContentItem> items = new ArrayList<>();
279+
items.add(new ChatMessageTextContentItem(message.getContent()));
280+
if (!CollectionUtils.isEmpty(message.getMedia())) {
281+
items.addAll(message.getMedia()
282+
.stream()
283+
.map(media -> new ChatMessageImageContentItem(
284+
new ChatMessageImageUrl(media.getData().toString())))
285+
.toList());
286+
}
287+
return new ChatRequestUserMessage(items);
282288
case SYSTEM:
283289
return new ChatRequestSystemMessage(message.getContent());
284290
case ASSISTANT:

0 commit comments

Comments
 (0)