Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2023-2024 the original author or authors.
* Copyright 2023-2025 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.
Expand Down Expand Up @@ -331,6 +331,10 @@ public static Document getChatOptionsAdditionalModelRequestFields(ChatOptions de
attributes.remove("toolContext");
attributes.remove("functionCallbacks");

attributes.remove("toolCallbacks");
attributes.remove("toolNames");
attributes.remove("internalToolExecutionEnabled");

attributes.remove("temperature");
attributes.remove("topK");
attributes.remove("stopSequences");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -380,7 +380,8 @@ void multiModalityEmbeddedImage(String modelName) throws IOException {

@ParameterizedTest(name = "{0} : {displayName} ")
@ValueSource(strings = { "anthropic.claude-3-5-sonnet-20240620-v1:0" })
void multiModalityImageUrl(String modelName) throws IOException {
@Deprecated
void multiModalityImageUrl2(String modelName) throws IOException {

// TODO: add url method that wrapps the checked exception.
URL url = new URL("https://docs.spring.io/spring-ai/reference/_images/multimodal.test.png");
Expand All @@ -398,6 +399,26 @@ void multiModalityImageUrl(String modelName) throws IOException {
assertThat(response).containsAnyOf("bananas", "apple", "bowl", "basket", "fruit stand");
}

@ParameterizedTest(name = "{0} : {displayName} ")
@ValueSource(strings = { "anthropic.claude-3-5-sonnet-20240620-v1:0" })
void multiModalityImageUrl(String modelName) throws IOException {

// TODO: add url method that wrapps the checked exception.
URL url = new URL("https://docs.spring.io/spring-ai/reference/_images/multimodal.test.png");

// @formatter:off
String response = ChatClient.create(this.chatModel).prompt()
// TODO consider adding model(...) method to ChatClient as a shortcut to
.options(ToolCallingChatOptions.builder().model(modelName).build())
.user(u -> u.text("Explain what do you see on this picture?").media(MimeTypeUtils.IMAGE_PNG, url))
.call()
.content();
// @formatter:on

logger.info(response);
assertThat(response).containsAnyOf("bananas", "apple", "bowl", "basket", "fruit stand");
}

@Test
void streamingMultiModalityImageUrl() throws IOException {

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2023-2024 the original author or authors.
* Copyright 2023-2025 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.
Expand Down Expand Up @@ -38,10 +38,10 @@ public BedrockProxyChatModel bedrockConverseChatModel() {
String modelId = "anthropic.claude-3-5-sonnet-20240620-v1:0";

return BedrockProxyChatModel.builder()
.withCredentialsProvider(EnvironmentVariableCredentialsProvider.create())
.withRegion(Region.US_EAST_1)
.withTimeout(Duration.ofSeconds(120))
// .withRegion(Region.US_EAST_1)
.credentialsProvider(EnvironmentVariableCredentialsProvider.create())
.region(Region.US_EAST_1)
// .region(Region.US_EAST_1)
.timeout(Duration.ofSeconds(120))
.withDefaultOptions(FunctionCallingOptions.builder().model(modelId).build())
.build();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

import io.micrometer.observation.ObservationRegistry;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@
import org.springframework.ai.model.Media;
import org.springframework.ai.model.function.FunctionCallback;
import org.springframework.ai.model.function.FunctionCallingOptions;
import org.springframework.ai.model.tool.ToolCallingChatOptions;
import org.springframework.ai.tool.function.FunctionToolCallback;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.context.SpringBootTest;
Expand Down Expand Up @@ -244,8 +246,9 @@ void multiModalityTest() throws IOException {
"fruit stand");
}

@Deprecated
@Test
void functionCallTest() {
void functionCallTestDeprecated() {

UserMessage userMessage = new UserMessage(
"What's the weather like in San Francisco, Tokyo and Paris? Return the result in Celsius.");
Expand All @@ -269,6 +272,29 @@ void functionCallTest() {
assertThat(generation.getOutput().getText()).contains("30", "10", "15");
}

@Test
void functionCallTest() {

UserMessage userMessage = new UserMessage(
"What's the weather like in San Francisco, Tokyo and Paris? Return the result in Celsius.");

List<Message> messages = new ArrayList<>(List.of(userMessage));

var promptOptions = ToolCallingChatOptions.builder()
.toolCallbacks(List.of(FunctionToolCallback.builder("getCurrentWeather", new MockWeatherService())
.description("Get the weather in location. Return in 36°C format")
.inputType(MockWeatherService.Request.class)
.build()))
.build();

ChatResponse response = this.chatModel.call(new Prompt(messages, promptOptions));

logger.info("Response: {}", response);

Generation generation = response.getResult();
assertThat(generation.getOutput().getText()).contains("30", "10", "15");
}

@Test
void streamFunctionCallTest() {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import java.time.Duration;
import java.util.Set;

import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand All @@ -32,7 +33,6 @@
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.model.Media;
import org.springframework.ai.model.function.FunctionCallingOptions;
import org.springframework.ai.model.tool.ToolCallingChatOptions;
import org.springframework.ai.tool.function.FunctionToolCallback;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringBootConfiguration;
Expand All @@ -47,6 +47,7 @@
/**
* @author Christian Tzolov
*/
@Disabled
@SpringBootTest(classes = BedrockNovaChatClientIT.Config.class)
@RequiresAwsCredentials
public class BedrockNovaChatClientIT {
Expand Down Expand Up @@ -181,9 +182,9 @@ public BedrockProxyChatModel bedrockConverseChatModel() {
String modelId = "amazon.nova-pro-v1:0";

return BedrockProxyChatModel.builder()
.withCredentialsProvider(EnvironmentVariableCredentialsProvider.create())
.withRegion(Region.US_EAST_1)
.withTimeout(Duration.ofSeconds(120))
.credentialsProvider(EnvironmentVariableCredentialsProvider.create())
.region(Region.US_EAST_1)
.timeout(Duration.ofSeconds(120))
.withDefaultOptions(FunctionCallingOptions.builder().model(modelId).build())
.build();
}
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@
import org.springframework.ai.openai.api.OpenAiApi.ChatCompletionRequest.AudioParameters.Voice;
import org.springframework.ai.openai.api.tool.MockWeatherService;
import org.springframework.ai.openai.testutils.AbstractIT;
import org.springframework.ai.tool.function.FunctionToolCallback;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.core.convert.support.DefaultConversionService;
Expand Down Expand Up @@ -332,7 +333,8 @@ void beanStreamOutputConverterRecords() {
}

@Test
void functionCallTest() {
@Deprecated
void functionCallTestDeprecated() {

UserMessage userMessage = new UserMessage("What's the weather like in San Francisco, Tokyo, and Paris?");

Expand All @@ -356,6 +358,28 @@ void functionCallTest() {
assertThat(response.getResult().getOutput().getText()).containsAnyOf("15.0", "15");
}

@Test
void functionCallTest() {

UserMessage userMessage = new UserMessage("What's the weather like in San Francisco, Tokyo, and Paris?");

List<Message> messages = new ArrayList<>(List.of(userMessage));

var promptOptions = OpenAiChatOptions.builder()
.model(OpenAiApi.ChatModel.GPT_4_O.getValue())
.toolCallbacks(List.of(FunctionToolCallback.builder("getCurrentWeather", new MockWeatherService())
.description("Get the weather in location")
.inputType(MockWeatherService.Request.class)
.build()))
.build();

ChatResponse response = this.chatModel.call(new Prompt(messages, promptOptions));

logger.info("Response: {}", response);

assertThat(response.getResult().getOutput().getText()).contains("30", "10", "15");
}

@Test
void streamFunctionCallTest() {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,20 +90,19 @@ The prefix `spring.ai.bedrock.converse.chat` is the property prefix that configu

== Runtime Options [[chat-options]]

Use the portable `ChatOptions` or `FunctionCallingOptions` portable builders to create model configurations, such as temperature, maxToken, topP, etc.
Use the portable `ChatOptions` or `ToolCallingChatOptions` portable builders to create model configurations, such as temperature, maxToken, topP, etc.

On start-up, the default options can be configured with the `BedrockConverseProxyChatModel(api, options)` constructor or the `spring.ai.bedrock.converse.chat.options.*` properties.

At run-time you can override the default options by adding new, request specific, options to the `Prompt` call:

[source,java]
----
var options = FunctionCallingOptions.builder()
.withModel("anthropic.claude-3-5-sonnet-20240620-v1:0")
.withTemperature(0.6)
.withMaxTokens(300)
.withFunctionCallbacks(List.of(FunctionCallback.builder()
.function("getCurrentWeather", new WeatherService())
var options = ToolCallingChatOptions.builder()
.model("anthropic.claude-3-5-sonnet-20240620-v1:0")
.temperature(0.6)
.maxTokens(300)
.toolCallbacks(List.of(FunctionToolCallback.builder("getCurrentWeather", new WeatherService())
.description("Get the weather in location. Return temperature in 36°F or 36°C format. Use multi-turn if needed.")
.inputType(WeatherService.Request.class)
.build()))
Expand All @@ -118,7 +117,28 @@ String response = ChatClient.create(this.chatModel)

== Tool/Function Calling

The Bedrock Converse API supports function calling capabilities, allowing models to use tools during conversations. Here's an example of how to define and use functions:
The Bedrock Converse API supports tool calling capabilities, allowing models to use tools during conversations.
Here's an example of how to define and use @Tool based tools:

[source,java]
----

public class WeatherService {

@Tool(description = "Get the weather in location")
public String weatherByLocation(@ToolParam(description= "City or state name") String location) {
...
}
}

String response = ChatClient.create(this.chatModel)
.prompt("What's the weather like in Boston?")
.tools(new WeatherService())
.call()
.content();
----

You can use the java.util.function beans as tools as well:

[source,java]
----
Expand All @@ -130,12 +150,14 @@ public Function<Request, Response> weatherFunction() {

String response = ChatClient.create(this.chatModel)
.prompt("What's the weather like in Boston?")
.function("weatherFunction")
.tools("weatherFunction")
.inputType(Request.class)
.call()
.content();
----

Find more in xref:api/tools.adoc[Tools] documentation.

== Multimodal

Multimodality refers to a model's ability to simultaneously understand and process information from various sources, including text, images, video, pdf, doc, html, md and more data formats.
Expand Down
Loading