From 6a195ee9fe15bf7d549cb85f91ace6b2d3f1687c Mon Sep 17 00:00:00 2001 From: Christian Tzolov Date: Mon, 25 Nov 2024 11:51:35 +0100 Subject: [PATCH] refactor(core): extract common function callback builder functionality Extracts shared function callback builder functionality into DefaultCommonCallbackInvokingSpec base class, reducing code duplication across builder implementations. Makes FunctionInvokingSpec and MethodInvokingSpec extend CommonCallbackInvokingSpec for better code organization. Also fixes function/description builder order in Anthropic tests. - Introduced a common base class for function callback builders to centralize shared logic - Standardized the order of method chaining for function and description in multiple AI model test classes - Refactored test cases across various AI model integrations - Corrected builder method order from .description().function() to .function().description() and .description().method() to .method().description() - Updated multiple test files to consistently use .function() before .description() - Updated documentation examples to reflect new builder method order - Modified DefaultFunctionCallbackResolver to maintain new builder method order - Updated DefaultChatClient and ChatClient test classes to reflect new builder pattern - Simplified callback specification by removing parent spec reference - Removed cascading getter logic for description, schema type, and other properties - Minor adjustments to function callback builder and invoking specs --- .../ai/anthropic/AnthropicChatModelIT.java | 4 +- .../client/AnthropicChatClientIT.java | 4 +- ...lientMethodInvokingFunctionCallbackIT.java | 12 +- .../AzureOpenAiChatModelFunctionCallIT.java | 8 +- .../converse/BedrockConverseChatClientIT.java | 30 ++--- .../BedrockConverseUsageAggregationTests.java | 2 +- .../converse/BedrockProxyChatModelIT.java | 4 +- .../BedrockConverseChatModelMain2.java | 2 +- .../BedrockConverseChatModelMain3.java | 2 +- .../minimax/ChatCompletionRequestTests.java | 6 +- .../ai/mistralai/MistralAiChatClientIT.java | 12 +- .../ai/mistralai/MistralAiChatModelIT.java | 4 +- .../MoonshotChatModelFunctionCallingIT.java | 4 +- .../OllamaChatModelFunctionCallingIT.java | 4 +- .../ai/openai/ChatCompletionRequestTests.java | 6 +- .../OpenAiChatModelFunctionCallingIT.java | 8 +- .../ai/openai/chat/OpenAiChatModelIT.java | 4 +- .../chat/client/OpenAiChatClientIT.java | 6 +- ...lientMethodInvokingFunctionCallbackIT.java | 12 +- ...enAiChatClientMultipleFunctionCallsIT.java | 10 +- .../chat/proxy/GroqWithOpenAiChatModelIT.java | 4 +- .../proxy/MistralWithOpenAiChatModelIT.java | 4 +- .../proxy/NvidiaWithOpenAiChatModelIT.java | 4 +- .../proxy/OllamaWithOpenAiChatModelIT.java | 4 +- .../PerplexityWithOpenAiChatModelIT.java | 4 +- .../gemini/CreateGeminiRequestTests.java | 6 +- ...texAiGeminiChatModelFunctionCallingIT.java | 12 +- .../zhipuai/ChatCompletionRequestTests.java | 6 +- .../ai/zhipuai/chat/ZhiPuAiChatModelIT.java | 4 +- .../ai/chat/client/DefaultChatClient.java | 2 +- .../DefaultCommonCallbackInvokingSpec.java | 115 +++++++++++++++++ .../DefaultFunctionCallbackBuilder.java | 118 +++--------------- .../DefaultFunctionCallbackResolver.java | 14 +-- .../ai/model/function/FunctionCallback.java | 70 ++++++----- .../ai/chat/ChatBuilderTests.java | 2 +- .../ai/chat/client/ChatClientTest.java | 4 +- .../chat/client/DefaultChatClientTests.java | 6 +- .../DefaultFunctionCallbackBuilderTests.java | 78 +++++++++--- .../MethodInvokingFunctionCallbackTests.java | 6 +- .../ROOT/pages/api/chat/bedrock-converse.adoc | 2 +- .../functions/anthropic-chat-functions.adoc | 2 +- .../azure-open-ai-chat-functions.adoc | 4 +- .../functions/minimax-chat-functions.adoc | 4 +- .../functions/mistralai-chat-functions.adoc | 4 +- .../functions/moonshot-chat-functions.adoc | 4 +- .../chat/functions/ollama-chat-functions.adoc | 4 +- .../chat/functions/openai-chat-functions.adoc | 4 +- .../vertexai-gemini-chat-functions.adoc | 4 +- .../functions/zhipuai-chat-functions.adoc | 4 +- .../ROOT/pages/api/function-callback.adoc | 14 +-- .../modules/ROOT/pages/api/functions.adoc | 16 +-- .../FunctionCallWithPromptFunctionIT.java | 2 +- .../FunctionCallWithFunctionWrapperIT.java | 2 +- .../FunctionCallWithPromptFunctionIT.java | 2 +- .../FunctionCallWithPromptFunctionIT.java | 2 +- .../minimax/FunctionCallbackInPromptIT.java | 4 +- .../minimax/MiniMaxFunctionCallbackIT.java | 2 +- .../mistralai/tool/PaymentStatusPromptIT.java | 2 +- .../tool/WeatherServicePromptIT.java | 4 +- .../tool/FunctionCallbackInPromptIT.java | 4 +- .../tool/MoonshotFunctionCallbackIT.java | 2 +- .../tool/FunctionCallbackInPromptIT.java | 4 +- .../ollama/tool/OllamaFunctionCallbackIT.java | 2 +- .../tool/FunctionCallbackInPrompt2IT.java | 6 +- .../tool/FunctionCallbackInPromptIT.java | 4 +- .../tool/OpenAiFunctionCallback2IT.java | 2 +- .../openai/tool/OpenAiFunctionCallbackIT.java | 2 +- .../FunctionCallWithFunctionWrapperIT.java | 2 +- .../FunctionCallWithPromptFunctionIT.java | 2 +- .../tool/FunctionCallbackInPromptIT.java | 4 +- .../tool/ZhipuAiFunctionCallbackIT.java | 2 +- .../ollama/tool/FunctionCallbackKotlinIT.kt | 2 +- 72 files changed, 408 insertions(+), 323 deletions(-) create mode 100644 spring-ai-core/src/main/java/org/springframework/ai/model/function/DefaultCommonCallbackInvokingSpec.java diff --git a/models/spring-ai-anthropic/src/test/java/org/springframework/ai/anthropic/AnthropicChatModelIT.java b/models/spring-ai-anthropic/src/test/java/org/springframework/ai/anthropic/AnthropicChatModelIT.java index dd63502ee..0777acf5b 100644 --- a/models/spring-ai-anthropic/src/test/java/org/springframework/ai/anthropic/AnthropicChatModelIT.java +++ b/models/spring-ai-anthropic/src/test/java/org/springframework/ai/anthropic/AnthropicChatModelIT.java @@ -276,9 +276,9 @@ class AnthropicChatModelIT { var promptOptions = AnthropicChatOptions.builder() .withModel(AnthropicApi.ChatModel.CLAUDE_3_OPUS.getName()) .withFunctionCallbacks(List.of(FunctionCallback.builder() + .function("getCurrentWeather", new MockWeatherService()) .description( "Get the weather in location. Return temperature in 36°F or 36°C format. Use multi-turn if needed.") - .function("getCurrentWeather", new MockWeatherService()) .inputType(MockWeatherService.Request.class) .build())) .build(); @@ -304,9 +304,9 @@ class AnthropicChatModelIT { var promptOptions = AnthropicChatOptions.builder() .withModel(AnthropicApi.ChatModel.CLAUDE_3_5_SONNET.getName()) .withFunctionCallbacks(List.of(FunctionCallback.builder() + .function("getCurrentWeather", new MockWeatherService()) .description( "Get the weather in location. Return temperature in 36°F or 36°C format. Use multi-turn if needed.") - .function("getCurrentWeather", new MockWeatherService()) .inputType(MockWeatherService.Request.class) .build())) .build(); diff --git a/models/spring-ai-anthropic/src/test/java/org/springframework/ai/anthropic/client/AnthropicChatClientIT.java b/models/spring-ai-anthropic/src/test/java/org/springframework/ai/anthropic/client/AnthropicChatClientIT.java index 7a5ff4475..f6d1a8891 100644 --- a/models/spring-ai-anthropic/src/test/java/org/springframework/ai/anthropic/client/AnthropicChatClientIT.java +++ b/models/spring-ai-anthropic/src/test/java/org/springframework/ai/anthropic/client/AnthropicChatClientIT.java @@ -250,8 +250,8 @@ class AnthropicChatClientIT { // @formatter:off String response = ChatClient.builder(this.chatModel) .defaultFunctions(FunctionCallback.builder() - .description("Get the weather in location") .function("getCurrentWeather", new MockWeatherService()) + .description("Get the weather in location") .inputType(MockWeatherService.Request.class) .build()) .defaultUser(u -> u.text("What's the weather like in San Francisco, Tokyo, and Paris? Use Celsius.")) @@ -273,8 +273,8 @@ class AnthropicChatClientIT { Flux response = ChatClient.create(this.chatModel).prompt() .user("What's the weather like in San Francisco, Tokyo, and Paris? Use Celsius.") .functions(FunctionCallback.builder() - .description("Get the weather in location") .function("getCurrentWeather", new MockWeatherService()) + .description("Get the weather in location") .inputType(MockWeatherService.Request.class) .build()) .stream() diff --git a/models/spring-ai-anthropic/src/test/java/org/springframework/ai/anthropic/client/AnthropicChatClientMethodInvokingFunctionCallbackIT.java b/models/spring-ai-anthropic/src/test/java/org/springframework/ai/anthropic/client/AnthropicChatClientMethodInvokingFunctionCallbackIT.java index fbea3c91a..2fcee182d 100644 --- a/models/spring-ai-anthropic/src/test/java/org/springframework/ai/anthropic/client/AnthropicChatClientMethodInvokingFunctionCallbackIT.java +++ b/models/spring-ai-anthropic/src/test/java/org/springframework/ai/anthropic/client/AnthropicChatClientMethodInvokingFunctionCallbackIT.java @@ -80,8 +80,8 @@ class AnthropicChatClientMethodInvokingFunctionCallbackIT { String response = ChatClient.create(this.chatModel).prompt() .user("What's the weather like in San Francisco, Tokyo, and Paris? Use Celsius.") .functions(FunctionCallback.builder() - .description("Get the weather in location") .method("getWeatherStatic", String.class, Unit.class) + .description("Get the weather in location") .targetClass(TestFunctionClass.class) .build()) .call() @@ -102,8 +102,8 @@ class AnthropicChatClientMethodInvokingFunctionCallbackIT { String response = ChatClient.create(this.chatModel).prompt() .user("Turn light on in the living room.") .functions(FunctionCallback.builder() - .description("Turn light on in the living room.") .method("turnLight", String.class, boolean.class) + .description("Turn light on in the living room.") .targetObject(targetObject) .build()) .call() @@ -125,8 +125,8 @@ class AnthropicChatClientMethodInvokingFunctionCallbackIT { String response = ChatClient.create(this.chatModel).prompt() .user("What's the weather like in San Francisco, Tokyo, and Paris? Use Celsius.") .functions(FunctionCallback.builder() - .description("Get the weather in location") .method("getWeatherNonStatic", String.class, Unit.class) + .description("Get the weather in location") .targetObject(targetObject) .build()) .call() @@ -147,8 +147,8 @@ class AnthropicChatClientMethodInvokingFunctionCallbackIT { String response = ChatClient.create(this.chatModel).prompt() .user("What's the weather like in San Francisco, Tokyo, and Paris? Use Celsius.") .functions(FunctionCallback.builder() - .description("Get the weather in location") .method("getWeatherWithContext", String.class, Unit.class, ToolContext.class) + .description("Get the weather in location") .targetObject(targetObject) .build()) .toolContext(Map.of("tool", "value")) @@ -174,8 +174,8 @@ class AnthropicChatClientMethodInvokingFunctionCallbackIT { assertThatThrownBy(() -> ChatClient.create(this.chatModel).prompt() .user("What's the weather like in San Francisco, Tokyo, and Paris? Use Celsius.") .functions(FunctionCallback.builder() - .description("Get the weather in location") .method("getWeatherNonStatic", String.class, Unit.class) + .description("Get the weather in location") .targetObject(targetObject) .build()) .toolContext(Map.of("tool", "value")) @@ -195,8 +195,8 @@ class AnthropicChatClientMethodInvokingFunctionCallbackIT { String response = ChatClient.create(this.chatModel).prompt() .user("Turn light on in the living room.") .functions(FunctionCallback.builder() - .description("Can turn lights on in the Living Room") .method("turnLivingRoomLightOn") + .description("Can turn lights on in the Living Room") .targetObject(targetObject) .build()) .call() diff --git a/models/spring-ai-azure-openai/src/test/java/org/springframework/ai/azure/openai/function/AzureOpenAiChatModelFunctionCallIT.java b/models/spring-ai-azure-openai/src/test/java/org/springframework/ai/azure/openai/function/AzureOpenAiChatModelFunctionCallIT.java index 33e9ef27a..8adb93fed 100644 --- a/models/spring-ai-azure-openai/src/test/java/org/springframework/ai/azure/openai/function/AzureOpenAiChatModelFunctionCallIT.java +++ b/models/spring-ai-azure-openai/src/test/java/org/springframework/ai/azure/openai/function/AzureOpenAiChatModelFunctionCallIT.java @@ -70,8 +70,8 @@ class AzureOpenAiChatModelFunctionCallIT { var promptOptions = AzureOpenAiChatOptions.builder() .withDeploymentName(this.selectedModel) .withFunctionCallbacks(List.of(FunctionCallback.builder() - .description("Get the current weather in a given location") .function("getCurrentWeather", new MockWeatherService()) + .description("Get the current weather in a given location") .inputType(MockWeatherService.Request.class) .build())) .build(); @@ -94,8 +94,8 @@ class AzureOpenAiChatModelFunctionCallIT { var promptOptions = AzureOpenAiChatOptions.builder() .withDeploymentName(this.selectedModel) .withFunctionCallbacks(List.of(FunctionCallback.builder() - .description("Get the current weather in a given location") .function("getCurrentWeather", new MockWeatherService()) + .description("Get the current weather in a given location") .inputType(MockWeatherService.Request.class) .build())) .build(); @@ -116,8 +116,8 @@ class AzureOpenAiChatModelFunctionCallIT { var promptOptions = AzureOpenAiChatOptions.builder() .withDeploymentName(this.selectedModel) .withFunctionCallbacks(List.of(FunctionCallback.builder() - .description("Get the current weather in a given location") .function("getCurrentWeather", new MockWeatherService()) + .description("Get the current weather in a given location") .inputType(MockWeatherService.Request.class) .build())) .build(); @@ -153,8 +153,8 @@ class AzureOpenAiChatModelFunctionCallIT { var promptOptions = AzureOpenAiChatOptions.builder() .withDeploymentName(this.selectedModel) .withFunctionCallbacks(List.of(FunctionCallback.builder() - .description("Get the current weather in a given location") .function("getCurrentWeather", new MockWeatherService()) + .description("Get the current weather in a given location") .inputType(MockWeatherService.Request.class) .build())) .build(); diff --git a/models/spring-ai-bedrock-converse/src/test/java/org/springframework/ai/bedrock/converse/BedrockConverseChatClientIT.java b/models/spring-ai-bedrock-converse/src/test/java/org/springframework/ai/bedrock/converse/BedrockConverseChatClientIT.java index f49cc87eb..5317e31a7 100644 --- a/models/spring-ai-bedrock-converse/src/test/java/org/springframework/ai/bedrock/converse/BedrockConverseChatClientIT.java +++ b/models/spring-ai-bedrock-converse/src/test/java/org/springframework/ai/bedrock/converse/BedrockConverseChatClientIT.java @@ -214,10 +214,10 @@ class BedrockConverseChatClientIT { String response = ChatClient.create(this.chatModel) .prompt("What's the weather like in San Francisco, Tokyo, and Paris? Return the temperature in Celsius.") .functions(FunctionCallback.builder() - .description("Get the weather in location") - .function("getCurrentWeather", new MockWeatherService()) - .inputType(MockWeatherService.Request.class) - .build()) + .function("getCurrentWeather", new MockWeatherService()) + .description("Get the weather in location") + .inputType(MockWeatherService.Request.class) + .build()) .call() .content(); // @formatter:on @@ -234,10 +234,10 @@ class BedrockConverseChatClientIT { ChatResponse response = ChatClient.create(this.chatModel) .prompt("What's the weather like in San Francisco, Tokyo, and Paris? Return the temperature in Celsius.") .functions(FunctionCallback.builder() - .description("Get the weather in location") - .function("getCurrentWeather", new MockWeatherService()) - .inputType(MockWeatherService.Request.class) - .build()) + .function("getCurrentWeather", new MockWeatherService()) + .description("Get the weather in location") + .inputType(MockWeatherService.Request.class) + .build()) .call() .chatResponse(); // @formatter:on @@ -269,10 +269,10 @@ class BedrockConverseChatClientIT { String response = ChatClient.create(this.chatModel) .prompt("What's the weather like in San Francisco, Tokyo, and Paris? Return the temperature in Celsius.") .functions(FunctionCallback.builder() - .description("Get the weather in location") - .function("getCurrentWeather", new MockWeatherService()) - .inputType(MockWeatherService.Request.class) - .build()) + .function("getCurrentWeather", new MockWeatherService()) + .description("Get the weather in location") + .inputType(MockWeatherService.Request.class) + .build()) .advisors(new SimpleLoggerAdvisor()) .call() .content(); @@ -289,8 +289,8 @@ class BedrockConverseChatClientIT { // @formatter:off String response = ChatClient.builder(this.chatModel) .defaultFunctions(FunctionCallback.builder() - .description("Get the weather in location") .function("getCurrentWeather", new MockWeatherService()) + .description("Get the weather in location") .inputType(MockWeatherService.Request.class) .build()) .defaultUser(u -> u.text("What's the weather like in San Francisco, Tokyo, and Paris? Return the temperature in Celsius.")) @@ -312,8 +312,8 @@ class BedrockConverseChatClientIT { Flux response = ChatClient.create(this.chatModel).prompt() .user("What's the weather like in San Francisco, Tokyo, and Paris? Return the temperature in Celsius.") .functions(FunctionCallback.builder() - .description("Get the weather in location") .function("getCurrentWeather", new MockWeatherService()) + .description("Get the weather in location") .inputType(MockWeatherService.Request.class) .build()) .stream() @@ -354,8 +354,8 @@ class BedrockConverseChatClientIT { Flux response = ChatClient.create(this.chatModel).prompt() .user("What's the weather like in Paris? Return the temperature in Celsius.") .functions(FunctionCallback.builder() - .description("Get the weather in location") .function("getCurrentWeather", new MockWeatherService()) + .description("Get the weather in location") .inputType(MockWeatherService.Request.class) .build()) .stream() diff --git a/models/spring-ai-bedrock-converse/src/test/java/org/springframework/ai/bedrock/converse/BedrockConverseUsageAggregationTests.java b/models/spring-ai-bedrock-converse/src/test/java/org/springframework/ai/bedrock/converse/BedrockConverseUsageAggregationTests.java index 3925b7f62..097982d3e 100644 --- a/models/spring-ai-bedrock-converse/src/test/java/org/springframework/ai/bedrock/converse/BedrockConverseUsageAggregationTests.java +++ b/models/spring-ai-bedrock-converse/src/test/java/org/springframework/ai/bedrock/converse/BedrockConverseUsageAggregationTests.java @@ -139,8 +139,8 @@ public class BedrockConverseUsageAggregationTests { .willReturn(converseResponseFinal); FunctionCallback functionCallback = FunctionCallback.builder() - .description("Gets the weather in location") .function("getCurrentWeather", (Request request) -> "15.0°C") + .description("Gets the weather in location") .inputType(Request.class) .build(); diff --git a/models/spring-ai-bedrock-converse/src/test/java/org/springframework/ai/bedrock/converse/BedrockProxyChatModelIT.java b/models/spring-ai-bedrock-converse/src/test/java/org/springframework/ai/bedrock/converse/BedrockProxyChatModelIT.java index a46c06d79..27bb6d936 100644 --- a/models/spring-ai-bedrock-converse/src/test/java/org/springframework/ai/bedrock/converse/BedrockProxyChatModelIT.java +++ b/models/spring-ai-bedrock-converse/src/test/java/org/springframework/ai/bedrock/converse/BedrockProxyChatModelIT.java @@ -255,9 +255,9 @@ class BedrockProxyChatModelIT { var promptOptions = FunctionCallingOptions.builder() .withFunctionCallbacks(List.of(FunctionCallback.builder() + .function("getCurrentWeather", new MockWeatherService()) .description( "Get the weather in location. Return temperature in 36°F or 36°C format. Use multi-turn if needed.") - .function("getCurrentWeather", new MockWeatherService()) .inputType(MockWeatherService.Request.class) .build())) .build(); @@ -283,9 +283,9 @@ class BedrockProxyChatModelIT { var promptOptions = FunctionCallingOptions.builder() .withModel("anthropic.claude-3-5-sonnet-20240620-v1:0") .withFunctionCallbacks(List.of(FunctionCallback.builder() + .function("getCurrentWeather", new MockWeatherService()) .description( "Get the weather in location. Return temperature in 36°F or 36°C format. Use multi-turn if needed.") - .function("getCurrentWeather", new MockWeatherService()) .inputType(MockWeatherService.Request.class) .build())) .build(); diff --git a/models/spring-ai-bedrock-converse/src/test/java/org/springframework/ai/bedrock/converse/experiments/BedrockConverseChatModelMain2.java b/models/spring-ai-bedrock-converse/src/test/java/org/springframework/ai/bedrock/converse/experiments/BedrockConverseChatModelMain2.java index 47e69dbd3..bcd6dfffa 100644 --- a/models/spring-ai-bedrock-converse/src/test/java/org/springframework/ai/bedrock/converse/experiments/BedrockConverseChatModelMain2.java +++ b/models/spring-ai-bedrock-converse/src/test/java/org/springframework/ai/bedrock/converse/experiments/BedrockConverseChatModelMain2.java @@ -53,8 +53,8 @@ public final class BedrockConverseChatModelMain2 { PortableFunctionCallingOptions.builder() .withModel(modelId) .withFunctionCallbacks(List.of(FunctionCallback.builder() - .description("Get the weather in location") .function("getCurrentWeather", new MockWeatherService()) + .description("Get the weather in location") .inputType(MockWeatherService.Request.class) .build())) .build()); diff --git a/models/spring-ai-bedrock-converse/src/test/java/org/springframework/ai/bedrock/converse/experiments/BedrockConverseChatModelMain3.java b/models/spring-ai-bedrock-converse/src/test/java/org/springframework/ai/bedrock/converse/experiments/BedrockConverseChatModelMain3.java index 078c3b2a6..00e9b61c2 100644 --- a/models/spring-ai-bedrock-converse/src/test/java/org/springframework/ai/bedrock/converse/experiments/BedrockConverseChatModelMain3.java +++ b/models/spring-ai-bedrock-converse/src/test/java/org/springframework/ai/bedrock/converse/experiments/BedrockConverseChatModelMain3.java @@ -51,8 +51,8 @@ public final class BedrockConverseChatModelMain3 { PortableFunctionCallingOptions.builder() .withModel(modelId) .withFunctionCallbacks(List.of(FunctionCallback.builder() - .description("Get the weather in location") .function("getCurrentWeather", new MockWeatherService()) + .description("Get the weather in location") .inputType(MockWeatherService.Request.class) .build())) .build()); diff --git a/models/spring-ai-minimax/src/test/java/org/springframework/ai/minimax/ChatCompletionRequestTests.java b/models/spring-ai-minimax/src/test/java/org/springframework/ai/minimax/ChatCompletionRequestTests.java index c89174872..07d460fd5 100644 --- a/models/spring-ai-minimax/src/test/java/org/springframework/ai/minimax/ChatCompletionRequestTests.java +++ b/models/spring-ai-minimax/src/test/java/org/springframework/ai/minimax/ChatCompletionRequestTests.java @@ -68,8 +68,8 @@ public class ChatCompletionRequestTests { MiniMaxChatOptions.builder() .withModel("PROMPT_MODEL") .withFunctionCallbacks(List.of(FunctionCallback.builder() - .description("Get the weather in location") .function(TOOL_FUNCTION_NAME, new MockWeatherService()) + .description("Get the weather in location") .inputType(MockWeatherService.Request.class) .build())) .build()), @@ -95,8 +95,8 @@ public class ChatCompletionRequestTests { MiniMaxChatOptions.builder() .withModel("DEFAULT_MODEL") .withFunctionCallbacks(List.of(FunctionCallback.builder() - .description("Get the weather in location") .function(TOOL_FUNCTION_NAME, new MockWeatherService()) + .description("Get the weather in location") .inputType(MockWeatherService.Request.class) .build())) .build()); @@ -127,8 +127,8 @@ public class ChatCompletionRequestTests { request = client.createRequest(new Prompt("Test message content", MiniMaxChatOptions.builder() .withFunctionCallbacks(List.of(FunctionCallback.builder() - .description("Overridden function description") .function(TOOL_FUNCTION_NAME, new MockWeatherService()) + .description("Overridden function description") .inputType(MockWeatherService.Request.class) .build())) .build()), diff --git a/models/spring-ai-mistral-ai/src/test/java/org/springframework/ai/mistralai/MistralAiChatClientIT.java b/models/spring-ai-mistral-ai/src/test/java/org/springframework/ai/mistralai/MistralAiChatClientIT.java index c32f555ca..45e0b3dce 100644 --- a/models/spring-ai-mistral-ai/src/test/java/org/springframework/ai/mistralai/MistralAiChatClientIT.java +++ b/models/spring-ai-mistral-ai/src/test/java/org/springframework/ai/mistralai/MistralAiChatClientIT.java @@ -226,10 +226,10 @@ class MistralAiChatClientIT { .options(MistralAiChatOptions.builder().withModel(MistralAiApi.ChatModel.SMALL).withToolChoice(ToolChoice.AUTO).build()) .user(u -> u.text("What's the weather like in San Francisco, Tokyo, and Paris? Use parallel function calling if required. Response should be in Celsius.")) .functions(FunctionCallback.builder() - .description("Get the weather in location") - .function("getCurrentWeather", new MockWeatherService()) - .inputType(MockWeatherService.Request.class) - .build()) + .function("getCurrentWeather", new MockWeatherService()) + .description("Get the weather in location") + .inputType(MockWeatherService.Request.class) + .build()) .call() .content(); // @formatter:on @@ -248,8 +248,8 @@ class MistralAiChatClientIT { String response = ChatClient.builder(this.chatModel) .defaultOptions(MistralAiChatOptions.builder().withModel(MistralAiApi.ChatModel.SMALL).build()) .defaultFunctions(FunctionCallback.builder() - .description("Get the weather in location") .function("getCurrentWeather", new MockWeatherService()) + .description("Get the weather in location") .inputType(MockWeatherService.Request.class) .build()) .defaultUser(u -> u.text("What's the weather like in San Francisco, Tokyo, and Paris? Use parallel function calling if required. Response should be in Celsius.")) @@ -272,8 +272,8 @@ class MistralAiChatClientIT { .options(MistralAiChatOptions.builder().withModel(MistralAiApi.ChatModel.SMALL).build()) .user("What's the weather like in San Francisco, Tokyo, and Paris? Use parallel function calling if required. Response should be in Celsius.") .functions(FunctionCallback.builder() - .description("Get the weather in location") .function("getCurrentWeather", new MockWeatherService()) + .description("Get the weather in location") .inputType(MockWeatherService.Request.class) .build()) .stream() diff --git a/models/spring-ai-mistral-ai/src/test/java/org/springframework/ai/mistralai/MistralAiChatModelIT.java b/models/spring-ai-mistral-ai/src/test/java/org/springframework/ai/mistralai/MistralAiChatModelIT.java index a87592e11..10bbea59f 100644 --- a/models/spring-ai-mistral-ai/src/test/java/org/springframework/ai/mistralai/MistralAiChatModelIT.java +++ b/models/spring-ai-mistral-ai/src/test/java/org/springframework/ai/mistralai/MistralAiChatModelIT.java @@ -194,8 +194,8 @@ class MistralAiChatModelIT { var promptOptions = MistralAiChatOptions.builder() .withModel(MistralAiApi.ChatModel.SMALL.getValue()) .withFunctionCallbacks(List.of(FunctionCallback.builder() - .description("Get the weather in location") .function("getCurrentWeather", new MockWeatherService()) + .description("Get the weather in location") .inputType(MockWeatherService.Request.class) .build())) .build(); @@ -217,8 +217,8 @@ class MistralAiChatModelIT { var promptOptions = MistralAiChatOptions.builder() .withModel(MistralAiApi.ChatModel.SMALL.getValue()) .withFunctionCallbacks(List.of(FunctionCallback.builder() - .description("Get the weather in location") .function("getCurrentWeather", new MockWeatherService()) + .description("Get the weather in location") .inputType(MockWeatherService.Request.class) .build())) .build(); diff --git a/models/spring-ai-moonshot/src/test/java/org/springframework/ai/moonshot/chat/MoonshotChatModelFunctionCallingIT.java b/models/spring-ai-moonshot/src/test/java/org/springframework/ai/moonshot/chat/MoonshotChatModelFunctionCallingIT.java index a1964e87b..400420671 100644 --- a/models/spring-ai-moonshot/src/test/java/org/springframework/ai/moonshot/chat/MoonshotChatModelFunctionCallingIT.java +++ b/models/spring-ai-moonshot/src/test/java/org/springframework/ai/moonshot/chat/MoonshotChatModelFunctionCallingIT.java @@ -64,8 +64,8 @@ class MoonshotChatModelFunctionCallingIT { var promptOptions = MoonshotChatOptions.builder() .withModel(MoonshotApi.ChatModel.MOONSHOT_V1_8K.getValue()) .withFunctionCallbacks(List.of(FunctionCallback.builder() - .description("Get the weather in location") .function("getCurrentWeather", new MockWeatherService()) + .description("Get the weather in location") .inputType(MockWeatherService.Request.class) .build())) .build(); @@ -87,8 +87,8 @@ class MoonshotChatModelFunctionCallingIT { var promptOptions = MoonshotChatOptions.builder() .withFunctionCallbacks(List.of(FunctionCallback.builder() - .description("Get the weather in location") .function("getCurrentWeather", new MockWeatherService()) + .description("Get the weather in location") .build())) .build(); diff --git a/models/spring-ai-ollama/src/test/java/org/springframework/ai/ollama/OllamaChatModelFunctionCallingIT.java b/models/spring-ai-ollama/src/test/java/org/springframework/ai/ollama/OllamaChatModelFunctionCallingIT.java index 3bad44cb1..5b819c647 100644 --- a/models/spring-ai-ollama/src/test/java/org/springframework/ai/ollama/OllamaChatModelFunctionCallingIT.java +++ b/models/spring-ai-ollama/src/test/java/org/springframework/ai/ollama/OllamaChatModelFunctionCallingIT.java @@ -64,9 +64,9 @@ class OllamaChatModelFunctionCallingIT extends BaseOllamaIT { var promptOptions = OllamaOptions.builder() .withModel(MODEL) .withFunctionCallbacks(List.of(FunctionCallback.builder() + .function("getCurrentWeather", new MockWeatherService()) .description( "Find the weather conditions, forecasts, and temperatures for a location, like a city or state.") - .function("getCurrentWeather", new MockWeatherService()) .inputType(MockWeatherService.Request.class) .build())) .build(); @@ -89,9 +89,9 @@ class OllamaChatModelFunctionCallingIT extends BaseOllamaIT { var promptOptions = OllamaOptions.builder() .withModel(MODEL) .withFunctionCallbacks(List.of(FunctionCallback.builder() + .function("getCurrentWeather", new MockWeatherService()) .description( "Find the weather conditions, forecasts, and temperatures for a location, like a city or state.") - .function("getCurrentWeather", new MockWeatherService()) .inputType(MockWeatherService.Request.class) .build())) .build(); diff --git a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/ChatCompletionRequestTests.java b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/ChatCompletionRequestTests.java index 2956d4fb8..45538fc48 100644 --- a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/ChatCompletionRequestTests.java +++ b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/ChatCompletionRequestTests.java @@ -68,8 +68,8 @@ public class ChatCompletionRequestTests { OpenAiChatOptions.builder() .withModel("PROMPT_MODEL") .withFunctionCallbacks(List.of(FunctionCallback.builder() - .description("Get the weather in location") .function(TOOL_FUNCTION_NAME, new MockWeatherService()) + .description("Get the weather in location") .inputType(MockWeatherService.Request.class) .build())) .build()), @@ -95,8 +95,8 @@ public class ChatCompletionRequestTests { OpenAiChatOptions.builder() .withModel("DEFAULT_MODEL") .withFunctionCallbacks(List.of(FunctionCallback.builder() - .description("Get the weather in location") .function(TOOL_FUNCTION_NAME, new MockWeatherService()) + .description("Get the weather in location") .inputType(MockWeatherService.Request.class) .build())) .build()); @@ -127,8 +127,8 @@ public class ChatCompletionRequestTests { request = client.createRequest(new Prompt("Test message content", OpenAiChatOptions.builder() .withFunctionCallbacks(List.of(FunctionCallback.builder() - .description("Overridden function description") .function(TOOL_FUNCTION_NAME, new MockWeatherService()) + .description("Overridden function description") .inputType(MockWeatherService.Request.class) .build())) .build()), diff --git a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/OpenAiChatModelFunctionCallingIT.java b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/OpenAiChatModelFunctionCallingIT.java index 97d98b22b..7d6f09d9f 100644 --- a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/OpenAiChatModelFunctionCallingIT.java +++ b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/OpenAiChatModelFunctionCallingIT.java @@ -85,8 +85,8 @@ class OpenAiChatModelFunctionCallingIT { functionCallTest(OpenAiChatOptions.builder() .withModel(OpenAiApi.ChatModel.GPT_4_O.getValue()) .withFunctionCallbacks(List.of(FunctionCallback.builder() - .description("Get the weather in location") .function("getCurrentWeather", new MockWeatherService()) + .description("Get the weather in location") .inputType(MockWeatherService.Request.class) .build())) .build()); @@ -121,8 +121,8 @@ class OpenAiChatModelFunctionCallingIT { functionCallTest(OpenAiChatOptions.builder() .withModel(OpenAiApi.ChatModel.GPT_4_O.getValue()) .withFunctionCallbacks(List.of(FunctionCallback.builder() - .description("Get the weather in location") .function("getCurrentWeather", biFunction) + .description("Get the weather in location") .inputType(MockWeatherService.Request.class) .build())) .withToolContext(Map.of("sessionId", "123")) @@ -147,8 +147,8 @@ class OpenAiChatModelFunctionCallingIT { streamFunctionCallTest(OpenAiChatOptions.builder() .withFunctionCallbacks(List.of((FunctionCallback.builder() - .description("Get the weather in location") .function("getCurrentWeather", new MockWeatherService()) + .description("Get the weather in location") .inputType(MockWeatherService.Request.class) // .responseConverter(response -> "" + response.temp() + response.unit()) .build()))) @@ -183,8 +183,8 @@ class OpenAiChatModelFunctionCallingIT { OpenAiChatOptions promptOptions = OpenAiChatOptions.builder() .withFunctionCallbacks(List.of((FunctionCallback.builder() - .description("Get the weather in location") .function("getCurrentWeather", biFunction) + .description("Get the weather in location") .inputType(MockWeatherService.Request.class) .build()))) .withToolContext(Map.of("sessionId", "123")) diff --git a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/OpenAiChatModelIT.java b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/OpenAiChatModelIT.java index 6b1a4a595..07f54e67f 100644 --- a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/OpenAiChatModelIT.java +++ b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/OpenAiChatModelIT.java @@ -330,8 +330,8 @@ public class OpenAiChatModelIT extends AbstractIT { var promptOptions = OpenAiChatOptions.builder() .withModel(OpenAiApi.ChatModel.GPT_4_O.getValue()) .withFunctionCallbacks(List.of(FunctionCallback.builder() - .description("Get the weather in location") .function("getCurrentWeather", new MockWeatherService()) + .description("Get the weather in location") .inputType(MockWeatherService.Request.class) .build())) .build(); @@ -355,8 +355,8 @@ public class OpenAiChatModelIT extends AbstractIT { var promptOptions = OpenAiChatOptions.builder() // .withModel(OpenAiApi.ChatModel.GPT_4_TURBO_PREVIEW.getValue()) .withFunctionCallbacks(List.of(FunctionCallback.builder() - .description("Get the weather in location") .function("getCurrentWeather", new MockWeatherService()) + .description("Get the weather in location") .inputType(MockWeatherService.Request.class) .build())) .build(); diff --git a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/client/OpenAiChatClientIT.java b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/client/OpenAiChatClientIT.java index 7278bfe84..57b2ce7d4 100644 --- a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/client/OpenAiChatClientIT.java +++ b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/client/OpenAiChatClientIT.java @@ -246,8 +246,8 @@ class OpenAiChatClientIT extends AbstractIT { void functionCallTest() { FunctionCallback functionCallback = FunctionCallback.builder() - .description("Get the weather in location") .function("getCurrentWeather", new MockWeatherService()) + .description("Get the weather in location") .inputType(MockWeatherService.Request.class) .build(); @@ -270,8 +270,8 @@ class OpenAiChatClientIT extends AbstractIT { // @formatter:off String response = ChatClient.builder(this.chatModel) .defaultFunctions(FunctionCallback.builder() - .description("Get the weather in location") .function("getCurrentWeather", new MockWeatherService()) + .description("Get the weather in location") .inputType(MockWeatherService.Request.class) .build()) .defaultUser(u -> u.text("What's the weather like in San Francisco, Tokyo, and Paris?")) @@ -291,8 +291,8 @@ class OpenAiChatClientIT extends AbstractIT { Flux response = ChatClient.create(this.chatModel).prompt() .user("What's the weather like in San Francisco, Tokyo, and Paris?") .functions(FunctionCallback.builder() - .description("Get the weather in location") .function("getCurrentWeather", new MockWeatherService()) + .description("Get the weather in location") .inputType(MockWeatherService.Request.class) .build()) .stream() diff --git a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/client/OpenAiChatClientMethodInvokingFunctionCallbackIT.java b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/client/OpenAiChatClientMethodInvokingFunctionCallbackIT.java index 6606b50bf..5d5e69188 100644 --- a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/client/OpenAiChatClientMethodInvokingFunctionCallbackIT.java +++ b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/client/OpenAiChatClientMethodInvokingFunctionCallbackIT.java @@ -61,8 +61,8 @@ class OpenAiChatClientMethodInvokingFunctionCallbackIT { String response = ChatClient.create(this.chatModel).prompt() .user("What's the weather like in San Francisco, Tokyo, and Paris? Use Celsius.") .functions(FunctionCallback.builder() - .description("Get the weather in location") .method("getWeatherStatic", String.class, Unit.class) + .description("Get the weather in location") .targetClass(TestFunctionClass.class) .build()) .call() @@ -83,8 +83,8 @@ class OpenAiChatClientMethodInvokingFunctionCallbackIT { String response = ChatClient.create(this.chatModel).prompt() .user("Turn light on in the living room.") .functions(FunctionCallback.builder() - .description("Can turn lights on or off by room name") .method("turnLight", String.class, boolean.class) + .description("Can turn lights on or off by room name") .targetObject(targetObject) .build()) .call() @@ -106,8 +106,8 @@ class OpenAiChatClientMethodInvokingFunctionCallbackIT { String response = ChatClient.create(this.chatModel).prompt() .user("What's the weather like in San Francisco, Tokyo, and Paris? Use Celsius.") .functions(FunctionCallback.builder() - .description("Get the weather in location") .method("getWeatherNonStatic", String.class, Unit.class) + .description("Get the weather in location") .targetObject(targetObject) .build()) .call() @@ -128,8 +128,8 @@ class OpenAiChatClientMethodInvokingFunctionCallbackIT { String response = ChatClient.create(this.chatModel).prompt() .user("What's the weather like in San Francisco, Tokyo, and Paris? Use Celsius.") .functions(FunctionCallback.builder() - .description("Get the weather in location") .method("getWeatherWithContext", String.class, Unit.class, ToolContext.class) + .description("Get the weather in location") .targetObject(targetObject) .build()) .toolContext(Map.of("tool", "value")) @@ -152,8 +152,8 @@ class OpenAiChatClientMethodInvokingFunctionCallbackIT { assertThatThrownBy(() -> ChatClient.create(this.chatModel).prompt() .user("What's the weather like in San Francisco, Tokyo, and Paris? Use Celsius.") .functions(FunctionCallback.builder() - .description("Get the weather in location") .method("getWeatherNonStatic", String.class, Unit.class) + .description("Get the weather in location") .targetObject(targetObject) .build()) .toolContext(Map.of("tool", "value")) @@ -173,8 +173,8 @@ class OpenAiChatClientMethodInvokingFunctionCallbackIT { String response = ChatClient.create(this.chatModel).prompt() .user("Turn light on in the living room.") .functions(FunctionCallback.builder() - .description("Can turn lights on in the Living Room") .method("turnLivingRoomLightOn") + .description("Can turn lights on in the Living Room") .targetObject(targetObject) .build()) .call() diff --git a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/client/OpenAiChatClientMultipleFunctionCallsIT.java b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/client/OpenAiChatClientMultipleFunctionCallsIT.java index 9ffa7777d..11a7699b7 100644 --- a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/client/OpenAiChatClientMultipleFunctionCallsIT.java +++ b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/client/OpenAiChatClientMultipleFunctionCallsIT.java @@ -85,8 +85,8 @@ class OpenAiChatClientMultipleFunctionCallsIT extends AbstractIT { response = chatClientBuilder.build().prompt() .user(u -> u.text("What's the weather like in San Francisco, Tokyo, and Paris?")) .functions(FunctionCallback.builder() - .description("Get the weather in location") .function("getCurrentWeather", new MockWeatherService()) + .description("Get the weather in location") .inputType(MockWeatherService.Request.class) .build()) .call() @@ -116,8 +116,8 @@ class OpenAiChatClientMultipleFunctionCallsIT extends AbstractIT { // @formatter:off String response = ChatClient.builder(this.chatModel) .defaultFunctions(FunctionCallback.builder() - .description("Get the weather in location") .function("getCurrentWeather", new MockWeatherService()) + .description("Get the weather in location") .inputType(MockWeatherService.Request.class) .build()) .defaultUser(u -> u.text("What's the weather like in San Francisco, Tokyo, and Paris?")) @@ -159,8 +159,8 @@ class OpenAiChatClientMultipleFunctionCallsIT extends AbstractIT { // @formatter:off String response = ChatClient.builder(this.chatModel) .defaultFunctions(FunctionCallback.builder() - .description("Get the weather in location") .function("getCurrentWeather", biFunction) + .description("Get the weather in location") .inputType(MockWeatherService.Request.class) .build()) .defaultUser(u -> u.text("What's the weather like in San Francisco, Tokyo, and Paris?")) @@ -203,8 +203,8 @@ class OpenAiChatClientMultipleFunctionCallsIT extends AbstractIT { // @formatter:off String response = ChatClient.builder(this.chatModel) .defaultFunctions(FunctionCallback.builder() - .description("Get the weather in location") .function("getCurrentWeather", biFunction) + .description("Get the weather in location") .inputType(MockWeatherService.Request.class) .build()) .defaultUser(u -> u.text("What's the weather like in San Francisco, Tokyo, and Paris?")) @@ -226,8 +226,8 @@ class OpenAiChatClientMultipleFunctionCallsIT extends AbstractIT { Flux response = ChatClient.create(this.chatModel).prompt() .user("What's the weather like in San Francisco, Tokyo, and Paris?") .functions(FunctionCallback.builder() - .description("Get the weather in location") .function("getCurrentWeather", new MockWeatherService()) + .description("Get the weather in location") .inputType(MockWeatherService.Request.class) .build()) .stream() diff --git a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/proxy/GroqWithOpenAiChatModelIT.java b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/proxy/GroqWithOpenAiChatModelIT.java index c3ee4f550..856aa847b 100644 --- a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/proxy/GroqWithOpenAiChatModelIT.java +++ b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/proxy/GroqWithOpenAiChatModelIT.java @@ -250,8 +250,8 @@ class GroqWithOpenAiChatModelIT { var promptOptions = OpenAiChatOptions.builder() .withFunctionCallbacks(List.of(FunctionCallback.builder() - .description("Get the weather in location") .function("getCurrentWeather", new MockWeatherService()) + .description("Get the weather in location") .inputType(MockWeatherService.Request.class) .build())) .build(); @@ -273,8 +273,8 @@ class GroqWithOpenAiChatModelIT { var promptOptions = OpenAiChatOptions.builder() .withFunctionCallbacks(List.of(FunctionCallback.builder() - .description("Get the weather in location") .function("getCurrentWeather", new MockWeatherService()) + .description("Get the weather in location") .inputType(MockWeatherService.Request.class) .build())) .build(); diff --git a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/proxy/MistralWithOpenAiChatModelIT.java b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/proxy/MistralWithOpenAiChatModelIT.java index 0c0ee7ad6..75fb99d6e 100644 --- a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/proxy/MistralWithOpenAiChatModelIT.java +++ b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/proxy/MistralWithOpenAiChatModelIT.java @@ -252,8 +252,8 @@ class MistralWithOpenAiChatModelIT { var promptOptions = OpenAiChatOptions.builder() .withModel(modelName) .withFunctionCallbacks(List.of(FunctionCallback.builder() - .description("Get the weather in location") .function("getCurrentWeather", new MockWeatherService()) + .description("Get the weather in location") .inputType(MockWeatherService.Request.class) .build())) .build(); @@ -277,8 +277,8 @@ class MistralWithOpenAiChatModelIT { var promptOptions = OpenAiChatOptions.builder() .withModel(modelName) .withFunctionCallbacks(List.of(FunctionCallback.builder() - .description("Get the weather in location") .function("getCurrentWeather", new MockWeatherService()) + .description("Get the weather in location") .inputType(MockWeatherService.Request.class) .build())) .build(); diff --git a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/proxy/NvidiaWithOpenAiChatModelIT.java b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/proxy/NvidiaWithOpenAiChatModelIT.java index 010896df4..a8e8d7b41 100644 --- a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/proxy/NvidiaWithOpenAiChatModelIT.java +++ b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/proxy/NvidiaWithOpenAiChatModelIT.java @@ -247,8 +247,8 @@ class NvidiaWithOpenAiChatModelIT { var promptOptions = OpenAiChatOptions.builder() .withFunctionCallbacks(List.of(FunctionCallback.builder() - .description("Get the weather in location") .function("getCurrentWeather", new MockWeatherService()) + .description("Get the weather in location") .inputType(MockWeatherService.Request.class) .build())) .build(); @@ -270,8 +270,8 @@ class NvidiaWithOpenAiChatModelIT { var promptOptions = OpenAiChatOptions.builder() .withFunctionCallbacks(List.of(FunctionCallback.builder() - .description("Get the weather in location") .function("getCurrentWeather", new MockWeatherService()) + .description("Get the weather in location") .inputType(MockWeatherService.Request.class) .build())) .build(); diff --git a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/proxy/OllamaWithOpenAiChatModelIT.java b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/proxy/OllamaWithOpenAiChatModelIT.java index e766074f6..779837554 100644 --- a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/proxy/OllamaWithOpenAiChatModelIT.java +++ b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/proxy/OllamaWithOpenAiChatModelIT.java @@ -269,8 +269,8 @@ class OllamaWithOpenAiChatModelIT { var promptOptions = OpenAiChatOptions.builder() .withFunctionCallbacks(List.of(FunctionCallback.builder() - .description("Get the weather in location") .function("getCurrentWeather", new MockWeatherService()) + .description("Get the weather in location") .inputType(MockWeatherService.Request.class) .build())) .build(); @@ -293,8 +293,8 @@ class OllamaWithOpenAiChatModelIT { var promptOptions = OpenAiChatOptions.builder() .withFunctionCallbacks(List.of(FunctionCallback.builder() - .description("Get the weather in location") .function("getCurrentWeather", new MockWeatherService()) + .description("Get the weather in location") .build())) .build(); diff --git a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/proxy/PerplexityWithOpenAiChatModelIT.java b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/proxy/PerplexityWithOpenAiChatModelIT.java index 2ef0135a3..be8f088ac 100644 --- a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/proxy/PerplexityWithOpenAiChatModelIT.java +++ b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/proxy/PerplexityWithOpenAiChatModelIT.java @@ -257,8 +257,8 @@ class PerplexityWithOpenAiChatModelIT { var promptOptions = OpenAiChatOptions.builder() .withFunctionCallbacks(List.of(FunctionCallback.builder() - .description("Get the weather in location") .function("getCurrentWeather", new MockWeatherService()) + .description("Get the weather in location") .inputType(MockWeatherService.Request.class) .build())) .build(); @@ -279,8 +279,8 @@ class PerplexityWithOpenAiChatModelIT { var promptOptions = OpenAiChatOptions.builder() .withFunctionCallbacks(List.of(FunctionCallback.builder() - .description("Get the weather in location") .function("getCurrentWeather", new MockWeatherService()) + .description("Get the weather in location") .inputType(MockWeatherService.Request.class) .build())) .build(); diff --git a/models/spring-ai-vertex-ai-gemini/src/test/java/org/springframework/ai/vertexai/gemini/CreateGeminiRequestTests.java b/models/spring-ai-vertex-ai-gemini/src/test/java/org/springframework/ai/vertexai/gemini/CreateGeminiRequestTests.java index eb821caa3..faf3bb79d 100644 --- a/models/spring-ai-vertex-ai-gemini/src/test/java/org/springframework/ai/vertexai/gemini/CreateGeminiRequestTests.java +++ b/models/spring-ai-vertex-ai-gemini/src/test/java/org/springframework/ai/vertexai/gemini/CreateGeminiRequestTests.java @@ -118,8 +118,8 @@ public class CreateGeminiRequestTests { VertexAiGeminiChatOptions.builder() .withModel("PROMPT_MODEL") .withFunctionCallbacks(List.of(FunctionCallback.builder() - .description("Get the weather in location") .function(TOOL_FUNCTION_NAME, new MockWeatherService()) + .description("Get the weather in location") .inputType(MockWeatherService.Request.class) .build())) .build()), @@ -146,8 +146,8 @@ public class CreateGeminiRequestTests { VertexAiGeminiChatOptions.builder() .withModel("DEFAULT_MODEL") .withFunctionCallbacks(List.of(FunctionCallback.builder() - .description("Get the weather in location") .function(TOOL_FUNCTION_NAME, new MockWeatherService()) + .description("Get the weather in location") .inputType(MockWeatherService.Request.class) .build())) .build()); @@ -179,8 +179,8 @@ public class CreateGeminiRequestTests { request = client.createGeminiRequest(new Prompt("Test message content", VertexAiGeminiChatOptions.builder() .withFunctionCallbacks(List.of(FunctionCallback.builder() - .description("Overridden function description") .function(TOOL_FUNCTION_NAME, new MockWeatherService()) + .description("Overridden function description") .inputType(MockWeatherService.Request.class) .build())) .build()), diff --git a/models/spring-ai-vertex-ai-gemini/src/test/java/org/springframework/ai/vertexai/gemini/function/VertexAiGeminiChatModelFunctionCallingIT.java b/models/spring-ai-vertex-ai-gemini/src/test/java/org/springframework/ai/vertexai/gemini/function/VertexAiGeminiChatModelFunctionCallingIT.java index 638e7e7d8..a9eea815d 100644 --- a/models/spring-ai-vertex-ai-gemini/src/test/java/org/springframework/ai/vertexai/gemini/function/VertexAiGeminiChatModelFunctionCallingIT.java +++ b/models/spring-ai-vertex-ai-gemini/src/test/java/org/springframework/ai/vertexai/gemini/function/VertexAiGeminiChatModelFunctionCallingIT.java @@ -84,9 +84,9 @@ public class VertexAiGeminiChatModelFunctionCallingIT { var promptOptions = VertexAiGeminiChatOptions.builder() .withFunctionCallbacks(List.of(FunctionCallback.builder() + .function("get_current_weather", new MockWeatherService()) .description("Get the current weather in a given location") .inputTypeSchema(openApiSchema) - .function("get_current_weather", new MockWeatherService()) .inputType(MockWeatherService.Request.class) .build())) .build(); @@ -109,16 +109,16 @@ public class VertexAiGeminiChatModelFunctionCallingIT { .withModel(VertexAiGeminiChatModel.ChatModel.GEMINI_1_5_FLASH) .withFunctionCallbacks(List.of( FunctionCallback.builder() + .function("get_current_weather", new MockWeatherService()) .schemaType(SchemaType.OPEN_API_SCHEMA) .description("Get the current weather in a given location.") - .function("get_current_weather", new MockWeatherService()) .inputType(MockWeatherService.Request.class) .build(), FunctionCallback.builder() + .function("get_payment_status", new PaymentStatus()) .schemaType(SchemaType.OPEN_API_SCHEMA) .description( "Retrieves the payment status for transaction. For example what is the payment status for transaction 700?") - .function("get_payment_status", new PaymentStatus()) .inputType(PaymentInfoRequest.class) .build())) .build(); @@ -150,16 +150,16 @@ public class VertexAiGeminiChatModelFunctionCallingIT { .withModel(VertexAiGeminiChatModel.ChatModel.GEMINI_1_5_FLASH) .withFunctionCallbacks(List.of( FunctionCallback.builder() + .function("get_current_weather", new MockWeatherService()) .schemaType(SchemaType.OPEN_API_SCHEMA) .description("Get the current weather in a given location.") - .function("get_current_weather", new MockWeatherService()) .inputType(MockWeatherService.Request.class) .build(), FunctionCallback.builder() + .function("get_payment_status", new PaymentStatus()) .schemaType(SchemaType.OPEN_API_SCHEMA) .description( "Retrieves the payment status for transaction. For example what is the payment status for transaction 700?") - .function("get_payment_status", new PaymentStatus()) .inputType(PaymentInfoRequest.class) .build())) .build(); @@ -190,9 +190,9 @@ public class VertexAiGeminiChatModelFunctionCallingIT { var promptOptions = VertexAiGeminiChatOptions.builder() .withModel(VertexAiGeminiChatModel.ChatModel.GEMINI_1_5_FLASH) .withFunctionCallbacks(List.of(FunctionCallback.builder() + .function("getCurrentWeather", new MockWeatherService()) .schemaType(SchemaType.OPEN_API_SCHEMA) .description("Get the current weather in a given location") - .function("getCurrentWeather", new MockWeatherService()) .inputType(MockWeatherService.Request.class) .build())) .build(); diff --git a/models/spring-ai-zhipuai/src/test/java/org/springframework/ai/zhipuai/ChatCompletionRequestTests.java b/models/spring-ai-zhipuai/src/test/java/org/springframework/ai/zhipuai/ChatCompletionRequestTests.java index c8a21d860..56b090edc 100644 --- a/models/spring-ai-zhipuai/src/test/java/org/springframework/ai/zhipuai/ChatCompletionRequestTests.java +++ b/models/spring-ai-zhipuai/src/test/java/org/springframework/ai/zhipuai/ChatCompletionRequestTests.java @@ -68,8 +68,8 @@ public class ChatCompletionRequestTests { ZhiPuAiChatOptions.builder() .withModel("PROMPT_MODEL") .withFunctionCallbacks(List.of(FunctionCallback.builder() - .description("Get the weather in location") .function(TOOL_FUNCTION_NAME, new MockWeatherService()) + .description("Get the weather in location") .inputType(MockWeatherService.Request.class) .build())) .build()), @@ -95,8 +95,8 @@ public class ChatCompletionRequestTests { ZhiPuAiChatOptions.builder() .withModel("DEFAULT_MODEL") .withFunctionCallbacks(List.of(FunctionCallback.builder() - .description("Get the weather in location") .function(TOOL_FUNCTION_NAME, new MockWeatherService()) + .description("Get the weather in location") .inputType(MockWeatherService.Request.class) .build())) .build()); @@ -127,8 +127,8 @@ public class ChatCompletionRequestTests { request = client.createRequest(new Prompt("Test message content", ZhiPuAiChatOptions.builder() .withFunctionCallbacks(List.of(FunctionCallback.builder() - .description("Overridden function description") .function(TOOL_FUNCTION_NAME, new MockWeatherService()) + .description("Overridden function description") .inputType(MockWeatherService.Request.class) .build())) .build()), diff --git a/models/spring-ai-zhipuai/src/test/java/org/springframework/ai/zhipuai/chat/ZhiPuAiChatModelIT.java b/models/spring-ai-zhipuai/src/test/java/org/springframework/ai/zhipuai/chat/ZhiPuAiChatModelIT.java index 17e273a1e..b5f035e31 100644 --- a/models/spring-ai-zhipuai/src/test/java/org/springframework/ai/zhipuai/chat/ZhiPuAiChatModelIT.java +++ b/models/spring-ai-zhipuai/src/test/java/org/springframework/ai/zhipuai/chat/ZhiPuAiChatModelIT.java @@ -231,8 +231,8 @@ class ZhiPuAiChatModelIT { var promptOptions = ZhiPuAiChatOptions.builder() .withModel(ZhiPuAiApi.ChatModel.GLM_4.getValue()) .withFunctionCallbacks(List.of(FunctionCallback.builder() - .description("Get the weather in location") .function("getCurrentWeather", new MockWeatherService()) + .description("Get the weather in location") .inputType(MockWeatherService.Request.class) .build())) .build(); @@ -257,8 +257,8 @@ class ZhiPuAiChatModelIT { var promptOptions = ZhiPuAiChatOptions.builder() .withModel(ZhiPuAiApi.ChatModel.GLM_4.getValue()) .withFunctionCallbacks(List.of(FunctionCallback.builder() - .description("Get the weather in location") .function("getCurrentWeather", new MockWeatherService()) + .description("Get the weather in location") .inputType(MockWeatherService.Request.class) .build())) .build(); diff --git a/spring-ai-core/src/main/java/org/springframework/ai/chat/client/DefaultChatClient.java b/spring-ai-core/src/main/java/org/springframework/ai/chat/client/DefaultChatClient.java index 4ab2f37d3..10b7d07c8 100644 --- a/spring-ai-core/src/main/java/org/springframework/ai/chat/client/DefaultChatClient.java +++ b/spring-ai-core/src/main/java/org/springframework/ai/chat/client/DefaultChatClient.java @@ -878,9 +878,9 @@ public class DefaultChatClient implements ChatClient { Assert.notNull(function, "function cannot be null"); var fcw = FunctionCallback.builder() + .function(name, function) .description(description) .responseConverter(Object::toString) - .function(name, function) .inputType(inputType) .build(); this.functionCallbacks.add(fcw); diff --git a/spring-ai-core/src/main/java/org/springframework/ai/model/function/DefaultCommonCallbackInvokingSpec.java b/spring-ai-core/src/main/java/org/springframework/ai/model/function/DefaultCommonCallbackInvokingSpec.java new file mode 100644 index 000000000..697d85675 --- /dev/null +++ b/spring-ai-core/src/main/java/org/springframework/ai/model/function/DefaultCommonCallbackInvokingSpec.java @@ -0,0 +1,115 @@ +package org.springframework.ai.model.function; + +import java.util.function.Function; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.databind.json.JsonMapper; + +import org.springframework.ai.model.function.FunctionCallback.CommonCallbackInvokingSpec; +import org.springframework.ai.model.function.FunctionCallback.SchemaType; +import org.springframework.ai.util.JacksonUtils; +import org.springframework.util.Assert; + +public class DefaultCommonCallbackInvokingSpec> + implements CommonCallbackInvokingSpec { + + /** + * The description of the function callback. Used to hint the LLM model about the + * tool's purpose and when to use it. + */ + protected String description; + + /** + * The schema type to use for the input type schema generation. The default is JSON + * Schema. Note: Vertex AI requires the input type schema to be in Open API schema + */ + protected SchemaType schemaType = SchemaType.JSON_SCHEMA; + + /** + * The function to convert the response object to a string. The default is to convert + * the response to a JSON string. + */ + protected Function responseConverter = response -> (response instanceof String) ? "" + response + : this.toJsonString(response); + + /** + * (Optional) Instead of generating the input type schema from the input type or + * method argument types, you can provide the schema directly. This will override the + * generated schema. + */ + protected String inputTypeSchema; + + protected ObjectMapper objectMapper = JsonMapper.builder() + .addModules(JacksonUtils.instantiateAvailableModules()) + .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES) + .disable(SerializationFeature.FAIL_ON_EMPTY_BEANS) + .build(); + + private String toJsonString(Object object) { + try { + return this.objectMapper.writeValueAsString(object); + } + catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + } + + @Override + public B description(String description) { + Assert.hasText(description, "Description must not be empty"); + this.description = description; + return (B) this; + } + + @Override + public B schemaType(SchemaType schemaType) { + Assert.notNull(schemaType, "SchemaType must not be null"); + this.schemaType = schemaType; + return (B) this; + } + + @Override + public B responseConverter(Function responseConverter) { + Assert.notNull(responseConverter, "ResponseConverter must not be null"); + this.responseConverter = responseConverter; + return (B) this; + } + + @Override + public B inputTypeSchema(String inputTypeSchema) { + Assert.hasText(inputTypeSchema, "InputTypeSchema must not be empty"); + this.inputTypeSchema = inputTypeSchema; + return (B) this; + } + + @Override + public B objectMapper(ObjectMapper objectMapper) { + Assert.notNull(objectMapper, "ObjectMapper must not be null"); + this.objectMapper = objectMapper; + return (B) this; + } + + public String getDescription() { + return this.description; + } + + public SchemaType getSchemaType() { + return this.schemaType; + } + + public Function getResponseConverter() { + return this.responseConverter; + } + + public String getInputTypeSchema() { + return this.inputTypeSchema; + } + + public ObjectMapper getObjectMapper() { + return this.objectMapper; + } + +} \ No newline at end of file diff --git a/spring-ai-core/src/main/java/org/springframework/ai/model/function/DefaultFunctionCallbackBuilder.java b/spring-ai-core/src/main/java/org/springframework/ai/model/function/DefaultFunctionCallbackBuilder.java index 1399758da..fe66bacd0 100644 --- a/spring-ai-core/src/main/java/org/springframework/ai/model/function/DefaultFunctionCallbackBuilder.java +++ b/spring-ai-core/src/main/java/org/springframework/ai/model/function/DefaultFunctionCallbackBuilder.java @@ -23,21 +23,14 @@ import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Supplier; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.DeserializationFeature; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.SerializationFeature; -import com.fasterxml.jackson.databind.json.JsonMapper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.ai.chat.model.ToolContext; import org.springframework.ai.model.ModelOptionsUtils; -import org.springframework.ai.model.function.FunctionCallback.Builder; import org.springframework.ai.model.function.FunctionCallback.FunctionInvokingSpec; import org.springframework.ai.model.function.FunctionCallback.MethodInvokingSpec; import org.springframework.ai.model.function.FunctionCallback.SchemaType; -import org.springframework.ai.util.JacksonUtils; import org.springframework.ai.util.ParsingUtils; import org.springframework.core.ParameterizedTypeReference; import org.springframework.util.Assert; @@ -54,82 +47,6 @@ public class DefaultFunctionCallbackBuilder implements FunctionCallback.Builder private final static Logger logger = LoggerFactory.getLogger(DefaultFunctionCallbackBuilder.class); - /** - * The description of the function callback. Used to hint the LLM model about the - * tool's purpose and when to use it. - */ - private String description; - - /** - * The schema type to use for the input type schema generation. The default is JSON - * Schema. Note: Vertex AI requires the input type schema to be in Open API schema - */ - private SchemaType schemaType = SchemaType.JSON_SCHEMA; - - /** - * The function to convert the response object to a string. The default is to convert - * the response to a JSON string. - */ - private Function responseConverter = response -> (response instanceof String) ? "" + response - : this.toJsonString(response); - - /** - * (Optional) Instead of generating the input type schema from the input type or - * method argument types, you can provide the schema directly. This will override the - * generated schema. - */ - private String inputTypeSchema; - - private ObjectMapper objectMapper = JsonMapper.builder() - .addModules(JacksonUtils.instantiateAvailableModules()) - .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES) - .disable(SerializationFeature.FAIL_ON_EMPTY_BEANS) - .build(); - - private String toJsonString(Object object) { - try { - return this.objectMapper.writeValueAsString(object); - } - catch (JsonProcessingException e) { - throw new RuntimeException(e); - } - } - - @Override - public Builder description(String description) { - Assert.hasText(description, "Description must not be empty"); - this.description = description; - return this; - } - - @Override - public Builder schemaType(SchemaType schemaType) { - Assert.notNull(schemaType, "SchemaType must not be null"); - this.schemaType = schemaType; - return this; - } - - @Override - public Builder responseConverter(Function responseConverter) { - Assert.notNull(responseConverter, "ResponseConverter must not be null"); - this.responseConverter = responseConverter; - return this; - } - - @Override - public Builder inputTypeSchema(String inputTypeSchema) { - Assert.hasText(inputTypeSchema, "InputTypeSchema must not be empty"); - this.inputTypeSchema = inputTypeSchema; - return this; - } - - @Override - public Builder objectMapper(ObjectMapper objectMapper) { - Assert.notNull(objectMapper, "ObjectMapper must not be null"); - this.objectMapper = objectMapper; - return this; - } - @Override public FunctionInvokingSpec function(String name, Function function) { return new DefaultFunctionInvokingSpec<>(name, function); @@ -170,7 +87,8 @@ public class DefaultFunctionCallbackBuilder implements FunctionCallback.Builder return generatedDescription; } - final class DefaultFunctionInvokingSpec implements FunctionInvokingSpec { + final class DefaultFunctionInvokingSpec extends DefaultCommonCallbackInvokingSpec> + implements FunctionInvokingSpec { private final String name; @@ -213,33 +131,35 @@ public class DefaultFunctionCallbackBuilder implements FunctionCallback.Builder @Override public FunctionCallback build() { - Assert.notNull(objectMapper, "ObjectMapper must not be null"); + Assert.notNull(this.getObjectMapper(), "ObjectMapper must not be null"); Assert.hasText(this.name, "Name must not be empty"); - Assert.notNull(responseConverter, "ResponseConverter must not be null"); + Assert.notNull(this.getResponseConverter(), "ResponseConverter must not be null"); Assert.notNull(this.inputType, "InputType must not be null"); - if (inputTypeSchema == null) { + if (this.getInputTypeSchema() == null) { boolean upperCaseTypeValues = schemaType == SchemaType.OPEN_API_SCHEMA; - inputTypeSchema = ModelOptionsUtils.getJsonSchema(this.inputType, upperCaseTypeValues); + this.inputTypeSchema = ModelOptionsUtils.getJsonSchema(this.inputType, upperCaseTypeValues); } BiFunction finalBiFunction = (this.biFunction != null) ? this.biFunction : (request, context) -> this.function.apply(request); - return new FunctionInvokingFunctionCallback(this.name, this.getDescription(), inputTypeSchema, - this.inputType, (Function) responseConverter, objectMapper, finalBiFunction); + return new FunctionInvokingFunctionCallback(this.name, this.getDescriptionExt(), this.getInputTypeSchema(), + this.inputType, (Function) this.getResponseConverter(), this.getObjectMapper(), + finalBiFunction); } - private String getDescription() { - if (StringUtils.hasText(description)) { - return description; + private String getDescriptionExt() { + if (StringUtils.hasText(this.getDescription())) { + return this.getDescription(); } return generateDescription(this.name); } } - final class DefaultMethodInvokingSpec implements FunctionCallback.MethodInvokingSpec { + final class DefaultMethodInvokingSpec extends DefaultCommonCallbackInvokingSpec + implements FunctionCallback.MethodInvokingSpec { private String name; @@ -285,13 +205,13 @@ public class DefaultFunctionCallbackBuilder implements FunctionCallback.Builder var method = ReflectionUtils.findMethod(this.targetClass, this.methodName, this.argumentTypes); Assert.notNull(method, "Method: '" + this.methodName + "' with arguments:" + Arrays.toString(this.argumentTypes) + " not found!"); - return new MethodInvokingFunctionCallback(this.targetObject, method, this.getDescription(), objectMapper, - this.name, responseConverter); + return new MethodInvokingFunctionCallback(this.targetObject, method, this.getDescriptionExt(), + this.getObjectMapper(), this.name, this.getResponseConverter()); } - private String getDescription() { - if (StringUtils.hasText(description)) { - return description; + private String getDescriptionExt() { + if (StringUtils.hasText(this.getDescription())) { + return this.getDescription(); } return generateDescription(StringUtils.hasText(this.name) ? this.name : this.methodName); diff --git a/spring-ai-core/src/main/java/org/springframework/ai/model/function/DefaultFunctionCallbackResolver.java b/spring-ai-core/src/main/java/org/springframework/ai/model/function/DefaultFunctionCallbackResolver.java index 6f3af304c..9e9e0fd1a 100644 --- a/spring-ai-core/src/main/java/org/springframework/ai/model/function/DefaultFunctionCallbackResolver.java +++ b/spring-ai-core/src/main/java/org/springframework/ai/model/function/DefaultFunctionCallbackResolver.java @@ -117,25 +117,25 @@ public class DefaultFunctionCallbackResolver implements ApplicationContextAware, if (KotlinDetector.isKotlinPresent()) { if (KotlinDelegate.isKotlinFunction(functionType.toClass())) { return FunctionCallback.builder() + .function(beanName, KotlinDelegate.wrapKotlinFunction(bean)) .schemaType(this.schemaType) .description(functionDescription) - .function(beanName, KotlinDelegate.wrapKotlinFunction(bean)) .inputType(ParameterizedTypeReference.forType(functionInputType.getType())) .build(); } if (KotlinDelegate.isKotlinBiFunction(functionType.toClass())) { return FunctionCallback.builder() + .function(beanName, KotlinDelegate.wrapKotlinBiFunction(bean)) .description(functionDescription) .schemaType(this.schemaType) - .function(beanName, KotlinDelegate.wrapKotlinBiFunction(bean)) .inputType(ParameterizedTypeReference.forType(functionInputType.getType())) .build(); } if (KotlinDelegate.isKotlinSupplier(functionType.toClass())) { return FunctionCallback.builder() + .function(beanName, KotlinDelegate.wrapKotlinSupplier(bean)) .description(functionDescription) .schemaType(this.schemaType) - .function(beanName, KotlinDelegate.wrapKotlinSupplier(bean)) .inputType(ParameterizedTypeReference.forType(functionInputType.getType())) .build(); } @@ -143,33 +143,33 @@ public class DefaultFunctionCallbackResolver implements ApplicationContextAware, if (bean instanceof Function function) { return FunctionCallback.builder() + .function(beanName, function) .schemaType(this.schemaType) .description(functionDescription) - .function(beanName, function) .inputType(ParameterizedTypeReference.forType(functionInputType.getType())) .build(); } if (bean instanceof BiFunction) { return FunctionCallback.builder() + .function(beanName, (BiFunction) bean) .description(functionDescription) .schemaType(this.schemaType) - .function(beanName, (BiFunction) bean) .inputType(ParameterizedTypeReference.forType(functionInputType.getType())) .build(); } if (bean instanceof Supplier supplier) { return FunctionCallback.builder() + .function(beanName, supplier) .description(functionDescription) .schemaType(this.schemaType) - .function(beanName, supplier) .inputType(ParameterizedTypeReference.forType(functionInputType.getType())) .build(); } if (bean instanceof Consumer consumer) { return FunctionCallback.builder() + .function(beanName, consumer) .description(functionDescription) .schemaType(this.schemaType) - .function(beanName, consumer) .inputType(ParameterizedTypeReference.forType(functionInputType.getType())) .build(); } diff --git a/spring-ai-core/src/main/java/org/springframework/ai/model/function/FunctionCallback.java b/spring-ai-core/src/main/java/org/springframework/ai/model/function/FunctionCallback.java index 8a30ad37d..06ac88fda 100644 --- a/spring-ai-core/src/main/java/org/springframework/ai/model/function/FunctionCallback.java +++ b/spring-ai-core/src/main/java/org/springframework/ai/model/function/FunctionCallback.java @@ -118,37 +118,6 @@ public interface FunctionCallback { */ interface Builder { - /** - * Function description. This description is used by the model do decide if the - * function should be called or not. - */ - Builder description(String description); - - /** - * Specifies what {@link SchemaType} is used by the AI model to validate the - * function input arguments. Most models use JSON Schema, except Vertex AI that - * uses OpenAPI types. - */ - Builder schemaType(SchemaType schemaType); - - /** - * Function response converter. The default implementation converts the output - * into String before sending it to the Model. Provide a custom function - * responseConverter implementation to override this. - */ - Builder responseConverter(Function responseConverter); - - /** - * You can provide the Input Type Schema directly. In this case it won't be - * generated from the inputType. - */ - Builder inputTypeSchema(String inputTypeSchema); - - /** - * Custom object mapper for JSON operations. - */ - Builder objectMapper(ObjectMapper objectMapper); - /** * Builds a {@link Function} invoking {@link FunctionCallback} instance. */ @@ -176,13 +145,48 @@ public interface FunctionCallback { } + interface CommonCallbackInvokingSpec> { + + /** + * Function description. This description is used by the model to decide if the + * function should be called or not. + */ + B description(String description); + + /** + * Specifies what {@link SchemaType} is used by the AI model to validate the + * function input arguments. Most models use JSON Schema, except Vertex AI that + * uses OpenAPI types. + */ + B schemaType(SchemaType schemaType); + + /** + * Function response converter. The default implementation converts the output + * into String before sending it to the Model. Provide a custom function + * responseConverter implementation to override this. + */ + B responseConverter(Function responseConverter); + + /** + * You can provide the Input Type Schema directly. In this case it won't be + * generated from the inputType. + */ + B inputTypeSchema(String inputTypeSchema); + + /** + * Custom object mapper for JSON operations. + */ + B objectMapper(ObjectMapper objectMapper); + + } + /** * {@link Function} invoking builder interface. * * @param Function input type. * @param Function output type. */ - interface FunctionInvokingSpec { + interface FunctionInvokingSpec extends CommonCallbackInvokingSpec> { /** * Function input type. The input type is used to validate the function input @@ -207,7 +211,7 @@ public interface FunctionCallback { /** * Method invoking builder interface. */ - interface MethodInvokingSpec { + interface MethodInvokingSpec extends CommonCallbackInvokingSpec { /** * Optional function name. If not provided the method name is used as the diff --git a/spring-ai-core/src/test/java/org/springframework/ai/chat/ChatBuilderTests.java b/spring-ai-core/src/test/java/org/springframework/ai/chat/ChatBuilderTests.java index 4789c5537..de1a69af6 100644 --- a/spring-ai-core/src/test/java/org/springframework/ai/chat/ChatBuilderTests.java +++ b/spring-ai-core/src/test/java/org/springframework/ai/chat/ChatBuilderTests.java @@ -80,8 +80,8 @@ public class ChatBuilderTests { String func = "func"; FunctionCallback cb = FunctionCallback.builder() - .description("cb") .function("cb", i -> i) + .description("cb") .inputType(Integer.class) .build(); diff --git a/spring-ai-core/src/test/java/org/springframework/ai/chat/client/ChatClientTest.java b/spring-ai-core/src/test/java/org/springframework/ai/chat/client/ChatClientTest.java index 2fa31cacf..b596efd3b 100644 --- a/spring-ai-core/src/test/java/org/springframework/ai/chat/client/ChatClientTest.java +++ b/spring-ai-core/src/test/java/org/springframework/ai/chat/client/ChatClientTest.java @@ -219,8 +219,8 @@ public class ChatClientTest { .param("param2", "value2")) .defaultFunctions("fun1", "fun2") .defaultFunctions(FunctionCallback.builder() - .description("fun3description") .function("fun3", mockFunction) + .description("fun3description") .inputType(String.class) .build()) .defaultUser(u -> u.text("Default user text {uparam1}, {uparam2}") @@ -350,8 +350,8 @@ public class ChatClientTest { .param("param2", "value2")) .defaultFunctions("fun1", "fun2") .defaultFunctions(FunctionCallback.builder() - .description("fun3description") .function("fun3", mockFunction) + .description("fun3description") .inputType(String.class) .build()) .defaultUser(u -> u.text("Default user text {uparam1}, {uparam2}") diff --git a/spring-ai-core/src/test/java/org/springframework/ai/chat/client/DefaultChatClientTests.java b/spring-ai-core/src/test/java/org/springframework/ai/chat/client/DefaultChatClientTests.java index 58f49a115..7cc9ff91b 100644 --- a/spring-ai-core/src/test/java/org/springframework/ai/chat/client/DefaultChatClientTests.java +++ b/spring-ai-core/src/test/java/org/springframework/ai/chat/client/DefaultChatClientTests.java @@ -1357,8 +1357,8 @@ class DefaultChatClientTests { ChatClient chatClient = new DefaultChatClientBuilder(mock(ChatModel.class)).build(); ChatClient.ChatClientRequestSpec spec = chatClient.prompt(); assertThatThrownBy(() -> spec.functions(FunctionCallback.builder() - .description("description") .function(null, input -> "hello") + .description("description") .inputType(String.class) .build())).isInstanceOf(IllegalArgumentException.class).hasMessage("Name must not be empty"); } @@ -1368,8 +1368,8 @@ class DefaultChatClientTests { ChatClient chatClient = new DefaultChatClientBuilder(mock(ChatModel.class)).build(); ChatClient.ChatClientRequestSpec spec = chatClient.prompt(); assertThatThrownBy(() -> spec.functions(FunctionCallback.builder() - .description("description") .function("", input -> "hello") + .description("description") .inputType(String.class) .build())).isInstanceOf(IllegalArgumentException.class).hasMessage("Name must not be empty"); } @@ -1379,8 +1379,8 @@ class DefaultChatClientTests { ChatClient chatClient = new DefaultChatClientBuilder(mock(ChatModel.class)).build(); ChatClient.ChatClientRequestSpec spec = chatClient.prompt(); assertThatThrownBy(() -> spec.functions(FunctionCallback.builder() + .function("name", input -> "hello") .description(null) - .function("", input -> "hello") .inputType(String.class) .build())).isInstanceOf(IllegalArgumentException.class).hasMessage("Description must not be empty"); } diff --git a/spring-ai-core/src/test/java/org/springframework/ai/model/function/DefaultFunctionCallbackBuilderTests.java b/spring-ai-core/src/test/java/org/springframework/ai/model/function/DefaultFunctionCallbackBuilderTests.java index e5774a86d..e7de18767 100644 --- a/spring-ai-core/src/test/java/org/springframework/ai/model/function/DefaultFunctionCallbackBuilderTests.java +++ b/spring-ai-core/src/test/java/org/springframework/ai/model/function/DefaultFunctionCallbackBuilderTests.java @@ -35,51 +35,54 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy; */ class DefaultFunctionCallbackBuilderTests { - // Common - + // Function @Test - void whenDescriptionIsNullThenThrow() { - assertThatThrownBy(() -> FunctionCallback.builder().description(null)) + void whenFunctionDescriptionIsNullThenThrow() { + assertThatThrownBy( + () -> FunctionCallback.builder().function("functionName", input -> "output").description(null)) .isInstanceOf(IllegalArgumentException.class) .hasMessage("Description must not be empty"); } @Test - void whenDescriptionIsEmptyThenThrow() { - assertThatThrownBy(() -> FunctionCallback.builder().description("")) + void whenFunctionDescriptionIsEmptyThenThrow() { + assertThatThrownBy(() -> FunctionCallback.builder().function("functionName", input -> "output").description("")) .isInstanceOf(IllegalArgumentException.class) .hasMessage("Description must not be empty"); } @Test - void whenInputTypeSchemaIsNullThenThrow() { - assertThatThrownBy(() -> FunctionCallback.builder().inputTypeSchema(null)) + void whenFunctionInputTypeSchemaIsNullThenThrow() { + assertThatThrownBy( + () -> FunctionCallback.builder().function("functionName", input -> "output").inputTypeSchema(null)) .isInstanceOf(IllegalArgumentException.class) .hasMessage("InputTypeSchema must not be empty"); } @Test - void whenInputTypeSchemaIsEmptyThenThrow() { - assertThatThrownBy(() -> FunctionCallback.builder().inputTypeSchema("")) + void whenFunctionInputTypeSchemaIsEmptyThenThrow() { + assertThatThrownBy( + () -> FunctionCallback.builder().function("functionName", input -> "output").inputTypeSchema("")) .isInstanceOf(IllegalArgumentException.class) .hasMessage("InputTypeSchema must not be empty"); } @Test - void whenSchemaTypeIsNullThenThrow() { - assertThatThrownBy(() -> FunctionCallback.builder().schemaType(null)) + void whenFunctionSchemaTypeIsNullThenThrow() { + assertThatThrownBy( + () -> FunctionCallback.builder().function("functionName", input -> "output").schemaType(null)) .isInstanceOf(IllegalArgumentException.class) .hasMessage("SchemaType must not be null"); } @Test - void whenResponseConverterIsNullThenThrow() { - assertThatThrownBy(() -> FunctionCallback.builder().responseConverter(null)) + void whenFunctionResponseConverterIsNullThenThrow() { + assertThatThrownBy( + () -> FunctionCallback.builder().function("functionName", input -> "output").responseConverter(null)) .isInstanceOf(IllegalArgumentException.class) .hasMessage("ResponseConverter must not be null"); } - // Function @Test void whenFunctionNameIsNullThenThrow2() { assertThatThrownBy(() -> FunctionCallback.builder().function(null, (Function) null)) @@ -111,8 +114,8 @@ class DefaultFunctionCallbackBuilderTests { @Test void whenFunctionWithInputTypeThenReturn() { FunctionCallback functionCallback = FunctionCallback.builder() - .description("description") .function("functionName", input -> "output") + .description("description") .inputType(String.class) .build(); assertThat(functionCallback).isNotNull(); @@ -179,6 +182,49 @@ class DefaultFunctionCallbackBuilderTests { } // Method + + @Test + void whenMethodDescriptionIsNullThenThrow() { + assertThatThrownBy(() -> FunctionCallback.builder().method("methodName").description(null)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Description must not be empty"); + } + + @Test + void whenMethodDescriptionIsEmptyThenThrow() { + assertThatThrownBy(() -> FunctionCallback.builder().method("methodName").description("")) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Description must not be empty"); + } + + @Test + void whenMethodInputTypeSchemaIsNullThenThrow() { + assertThatThrownBy(() -> FunctionCallback.builder().method("methodName").inputTypeSchema(null)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("InputTypeSchema must not be empty"); + } + + @Test + void whenMethodInputTypeSchemaIsEmptyThenThrow() { + assertThatThrownBy(() -> FunctionCallback.builder().method("methodName").inputTypeSchema("")) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("InputTypeSchema must not be empty"); + } + + @Test + void whenMethodSchemaTypeIsNullThenThrow() { + assertThatThrownBy(() -> FunctionCallback.builder().method("methodName").schemaType(null)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("SchemaType must not be null"); + } + + @Test + void whenMethodResponseConverterIsNullThenThrow() { + assertThatThrownBy(() -> FunctionCallback.builder().method("methodName").responseConverter(null)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("ResponseConverter must not be null"); + } + @Test void whenMethodNameIsNullThenThrow() { assertThatThrownBy(() -> FunctionCallback.builder().method(null)).isInstanceOf(IllegalArgumentException.class) diff --git a/spring-ai-core/src/test/java/org/springframework/ai/model/function/MethodInvokingFunctionCallbackTests.java b/spring-ai-core/src/test/java/org/springframework/ai/model/function/MethodInvokingFunctionCallbackTests.java index badc6b9b1..ec3e0c300 100644 --- a/spring-ai-core/src/test/java/org/springframework/ai/model/function/MethodInvokingFunctionCallbackTests.java +++ b/spring-ai-core/src/test/java/org/springframework/ai/model/function/MethodInvokingFunctionCallbackTests.java @@ -56,9 +56,9 @@ public class MethodInvokingFunctionCallbackTests { public void staticMethod() throws NoSuchMethodException, SecurityException { var functionCallback = FunctionCallback.builder() + .method("myStaticMethod", String.class, Unit.class, int.class, MyRecord.class, List.class) .description("weather at location") .objectMapper(new ObjectMapper()) - .method("myStaticMethod", String.class, Unit.class, int.class, MyRecord.class, List.class) .targetClass(TestClassWithFunctionMethods.class) .build(); @@ -80,8 +80,8 @@ public class MethodInvokingFunctionCallbackTests { var object = new TestClassWithFunctionMethods(); var functionCallback = FunctionCallback.builder() - .description("weather at location") .method("myNonStaticMethod", String.class, Unit.class, int.class, MyRecord.class, List.class) + .description("weather at location") .targetObject(object) .build(); @@ -101,9 +101,9 @@ public class MethodInvokingFunctionCallbackTests { public void noArgsNoReturnMethod() throws NoSuchMethodException, SecurityException { var functionCallback = FunctionCallback.builder() + .method("argumentLessReturnVoid") .description("weather at location") .objectMapper(new ObjectMapper()) - .method("argumentLessReturnVoid") .targetClass(TestClassWithFunctionMethods.class) .build(); diff --git a/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/chat/bedrock-converse.adoc b/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/chat/bedrock-converse.adoc index 2c63aa9b2..121c1acba 100644 --- a/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/chat/bedrock-converse.adoc +++ b/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/chat/bedrock-converse.adoc @@ -109,8 +109,8 @@ var options = FunctionCallingOptions.builder() .withTemperature(0.6) .withMaxTokens(300) .withFunctionCallbacks(List.of(FunctionCallback.builder() - .description("Get the weather in location. Return temperature in 36°F or 36°C format. Use multi-turn if needed.") .function("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())) .build(); diff --git a/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/chat/functions/anthropic-chat-functions.adoc b/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/chat/functions/anthropic-chat-functions.adoc index adfae3a9e..34241def1 100644 --- a/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/chat/functions/anthropic-chat-functions.adoc +++ b/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/chat/functions/anthropic-chat-functions.adoc @@ -176,8 +176,8 @@ UserMessage userMessage = new UserMessage("What's the weather like in Paris?"); var promptOptions = AnthropicChatOptions.builder() .withFunctionCallbacks(List.of(FunctionCallback.builder() - .description("Get the weather in location") // (2) function description .function("CurrentWeather", new MockWeatherService()) // (1) function name and instance + .description("Get the weather in location") // (2) function description .inputType(MockWeatherService.Request.class) // (3) function signature .build())) // function code .build(); diff --git a/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/chat/functions/azure-open-ai-chat-functions.adoc b/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/chat/functions/azure-open-ai-chat-functions.adoc index 9116fbc90..cc2deaa6f 100644 --- a/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/chat/functions/azure-open-ai-chat-functions.adoc +++ b/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/chat/functions/azure-open-ai-chat-functions.adoc @@ -124,8 +124,8 @@ static class Config { public FunctionCallback weatherFunctionInfo() { return FunctionCallback.builder() - .description("Get the current weather in a given location") // (2) function description .function("CurrentWeather", new MockWeatherService()) // (1) function name + .description("Get the current weather in a given location") // (2) function description .inputType(MockWeatherService.Request.class) // (3) function input type .build(); } @@ -181,8 +181,8 @@ UserMessage userMessage = new UserMessage("What's the weather like in San Franci var promptOptions = AzureOpenAiChatOptions.builder() .withFunctionCallbacks(List.of(FunctionCallback.builder() - .description("Get the current weather in a given location") // (2) function description .function("CurrentWeather", new MockWeatherService()) // (1) function name and instance + .description("Get the current weather in a given location") // (2) function description .inputType(MockWeatherService.Request.class) // (3) function input type .build())) .build(); diff --git a/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/chat/functions/minimax-chat-functions.adoc b/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/chat/functions/minimax-chat-functions.adoc index 1f528f5ff..e7e25fcf9 100644 --- a/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/chat/functions/minimax-chat-functions.adoc +++ b/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/chat/functions/minimax-chat-functions.adoc @@ -128,8 +128,8 @@ static class Config { public FunctionCallback weatherFunctionInfo() { return FunctionCallback.builder() - .description("Get the weather in location") // (2) function description .function("CurrentWeather", new MockWeatherService()) // (1) function name and instance + .description("Get the weather in location") // (2) function description .inputType(MockWeatherService.Request.class) // (3) function signature .build(); } @@ -186,8 +186,8 @@ UserMessage userMessage = new UserMessage("What's the weather like in San Franci var promptOptions = MiniMaxChatOptions.builder() .withFunctionCallbacks(List.of(FunctionCallback.builder() - .description("Get the weather in location") // (2) function description .function("CurrentWeather", new MockWeatherService()) // (1) function name and instance + .description("Get the weather in location") // (2) function description .inputType(MockWeatherService.Request.class) // (3) function signature .build())) // function code .build(); diff --git a/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/chat/functions/mistralai-chat-functions.adoc b/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/chat/functions/mistralai-chat-functions.adoc index 8b1b1ffbb..90084e9c8 100644 --- a/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/chat/functions/mistralai-chat-functions.adoc +++ b/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/chat/functions/mistralai-chat-functions.adoc @@ -126,8 +126,8 @@ static class Config { public FunctionCallback weatherFunctionInfo() { return FunctionCallback.builder() - .description("Get the weather in location") // (2) function description .function("CurrentWeather", new MockWeatherService()) // (1) function name and instance + .description("Get the weather in location") // (2) function description .inputType(MockWeatherService.Request.class) // (3) function signature .build(); } @@ -174,8 +174,8 @@ UserMessage userMessage = new UserMessage("What's the weather like in Paris?"); var promptOptions = MistralAiChatOptions.builder() .withFunctionCallbacks(List.of(FunctionCallback.builder() - .description("Get the weather in location") // (2) function description .function("CurrentWeather", new MockWeatherService()) // (1) function name and instance + .description("Get the weather in location") // (2) function description .inputType(MockWeatherService.Request.class) // (3) function signature .build())) // function code .build(); diff --git a/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/chat/functions/moonshot-chat-functions.adoc b/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/chat/functions/moonshot-chat-functions.adoc index f67cff371..c5575f500 100644 --- a/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/chat/functions/moonshot-chat-functions.adoc +++ b/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/chat/functions/moonshot-chat-functions.adoc @@ -128,8 +128,8 @@ static class Config { public FunctionCallback weatherFunctionInfo() { return FunctionCallback.builder() - .description("Get the weather in location") // (2) function description .function("CurrentWeather", new MockWeatherService()) // (1) function name and instance + .description("Get the weather in location") // (2) function description .inputType(MockWeatherService.Request.class) // (3) function signature .build(); } @@ -186,8 +186,8 @@ UserMessage userMessage = new UserMessage("What's the weather like in San Franci var promptOptions = MoonshotChatOptions.builder() .withFunctionCallbacks(List.of(FunctionCallback.builder() - .description("Get the weather in location") // (2) function description .function("CurrentWeather", new MockWeatherService()) // (1) function name + .description("Get the weather in location") // (2) function description .inputType(MockWeatherService.Request.class) // (3) function signature .build())) // function code .build(); diff --git a/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/chat/functions/ollama-chat-functions.adoc b/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/chat/functions/ollama-chat-functions.adoc index f2e0aad9c..a10b21bdf 100644 --- a/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/chat/functions/ollama-chat-functions.adoc +++ b/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/chat/functions/ollama-chat-functions.adoc @@ -130,8 +130,8 @@ static class Config { public FunctionCallback weatherFunctionInfo() { return FunctionCallback.builder() - .description("Get the weather in location") // (2) function description .function("CurrentWeather", new MockWeatherService()) // (1) function name + .description("Get the weather in location") // (2) function description .inputType(MockWeatherService.Request.class) // (3) function signature .build(); } @@ -187,8 +187,8 @@ UserMessage userMessage = new UserMessage("What's the weather like in San Franci var promptOptions = OllamaOptions.builder() .withFunctionCallbacks(List.of(FunctionCallback.builder() - .description("Get the weather in location") // (2) function description .function("CurrentWeather", new MockWeatherService()) // (1) function name and instance + .description("Get the weather in location") // (2) function description .inputType(MockWeatherService.Request.class) // (3) function signature .build())) // function code .build(); diff --git a/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/chat/functions/openai-chat-functions.adoc b/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/chat/functions/openai-chat-functions.adoc index ed520bb71..5adf6a416 100644 --- a/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/chat/functions/openai-chat-functions.adoc +++ b/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/chat/functions/openai-chat-functions.adoc @@ -123,8 +123,8 @@ static class Config { public FunctionCallback weatherFunctionInfo() { return FunctionCallback.builder() - .description("Get the weather in location") // (2) function description .function("CurrentWeather", new MockWeatherService()) // (1) function name and instance + .description("Get the weather in location") // (2) function description .inputType(MockWeatherService.Request.class) // (3) function input type .build(); } @@ -180,8 +180,8 @@ UserMessage userMessage = new UserMessage("What's the weather like in San Franci var promptOptions = OpenAiChatOptions.builder() .withFunctionCallbacks(List.of(FunctionCallback.builder() - .description("Get the weather in location") // (2) function description .function("CurrentWeather", new MockWeatherService()) // (1) function name and instance + .description("Get the weather in location") // (2) function description .inputType(MockWeatherService.Request.class) // (3) function input type .build())) // function code .build(); diff --git a/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/chat/functions/vertexai-gemini-chat-functions.adoc b/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/chat/functions/vertexai-gemini-chat-functions.adoc index 6259cd2f7..de5d31de3 100644 --- a/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/chat/functions/vertexai-gemini-chat-functions.adoc +++ b/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/chat/functions/vertexai-gemini-chat-functions.adoc @@ -130,9 +130,9 @@ static class Config { public FunctionCallback weatherFunctionInfo() { return FunctionCallback.builder() + .function("CurrentWeather", new MockWeatherService()) // (1) function name and instance .description("Get the current weather in a given location") // (2) function description .schemaType(SchemaType.OPEN_API_SCHEMA) // (3) schema type. Compulsory for Gemini function calling. - .function("CurrentWeather", new MockWeatherService()) // (1) function name and instance .inputType(MockWeatherService.Request.class) // (4) input type .build(); } @@ -189,9 +189,9 @@ UserMessage userMessage = new UserMessage("What's the weather like in San Franci var promptOptions = VertexAiGeminiChatOptions.builder() .withFunctionCallbacks(List.of(FunctionCallback.builder() + .function("CurrentWeather", new MockWeatherService()) .schemaType(SchemaType.OPEN_API_SCHEMA) // IMPORTANT!! .description("Get the weather in location") - .function("CurrentWeather", new MockWeatherService()) .inputType(MockWeatherService.Request.class) .build())) .build(); diff --git a/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/chat/functions/zhipuai-chat-functions.adoc b/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/chat/functions/zhipuai-chat-functions.adoc index aa0d6b24e..3de67f38b 100644 --- a/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/chat/functions/zhipuai-chat-functions.adoc +++ b/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/chat/functions/zhipuai-chat-functions.adoc @@ -128,8 +128,8 @@ static class Config { public FunctionCallback weatherFunctionInfo() { return FunctionCallback.builder() - .description("Get the weather in location") // (2) function description .function("CurrentWeather", new MockWeatherService()) // (1) function name and instance + .description("Get the weather in location") // (2) function description .inputType(MockWeatherService.Request.class) // (3) function signature .build(); } @@ -186,8 +186,8 @@ UserMessage userMessage = new UserMessage("What's the weather like in San Franci var promptOptions = ZhiPuAiChatOptions.builder() .withFunctionCallbacks(List.of(FunctionCallback.builder() - .description("Get the weather in location") // (2) function description .function("CurrentWeather", new MockWeatherService()) // (1) function name and instance + .description("Get the weather in location") // (2) function description .inputType(MockWeatherService.Request.class) // (3) function signature .build())) // function code .build(); diff --git a/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/function-callback.adoc b/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/function-callback.adoc index 4b88740a4..cbef5d944 100644 --- a/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/function-callback.adoc +++ b/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/function-callback.adoc @@ -34,8 +34,8 @@ NOTE: You can use lambda expressions or method references to define the function [source,java] ---- FunctionCallback callback = FunctionCallback.builder() - .description("Process a new order") .function("processOrder", (Order order) -> processOrderLogic(order)) + .description("Process a new order") .inputType(Order.class) .build(); ---- @@ -47,9 +47,9 @@ Using Function with input type and additional xref:api/functions.adoc#Tool-C [source,java] ---- FunctionCallback callback = FunctionCallback.builder() - .description("Process a new order with context") .function("processOrder", (Order order, ToolContext context) -> processOrderWithContext(order, context)) + .description("Process a new order with context") .inputType(Order.class) .build(); ---- @@ -61,8 +61,8 @@ Use `java.util.Supplier` or `java.util.function.Function` to define [source,java] ---- FunctionCallback.builder() - .description("Turns light on in the living room") .function("turnsLight", () -> state.put("Light", "ON")) + .description("Turns light on in the living room") .inputType(Void.class) .build(); ---- @@ -76,10 +76,10 @@ Use `java.util.Consumer` or `java.util.function.Function` to define record LightInfo(String roomName, boolean isOn) {} FunctionCallback.builder() - .description("Turns light on/off in a selected room") .function("turnsLight", (LightInfo lightInfo) -> { logger.info("Turning light to [" + lightInfo.isOn + "] in " + lightInfo.roomName()); }) + .description("Turns light on/off in a selected room") .inputType(LightInfo.class) .build(); ---- @@ -97,11 +97,11 @@ record TrainSearchSchedule(String from, String to, String date) {} record TrainSearchScheduleResponse(String from, String to, String date, String trainNumber) {} FunctionCallback.builder() - .description("Schedule a train reservation") .function("trainSchedule", (TrainSearchRequest request) -> { logger.info("Schedule: " + request.data().from() + " to " + request.data().to()); return new TrainSearchScheduleResponse(request.data().from(), request. data().to(), "", "123"); }) + .description("Schedule a train reservation") .inputType(new ParameterizedTypeReference>() {}) .build(); ---- @@ -132,8 +132,8 @@ public class WeatherService { } FunctionCallback callback = FunctionCallback.builder() - .description("Get weather information for a city") .method("getWeather", String.class, TemperatureUnit.class) + .description("Get weather information for a city") .targetClass(WeatherService.class) .build(); ---- @@ -156,8 +156,8 @@ DeviceController controller = new DeviceController(); String response = ChatClient.create(chatModel).prompt() .user("Turn on the living room lights") .functions(FunctionCallback.builder() - .description("Control device state") .method("setDeviceState", String.class,boolean.class,ToolContext.class) + .description("Control device state") .targetObject(controller) .build()) .toolContext(Map.of("location", "home")) diff --git a/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/functions.adoc b/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/functions.adoc index 60ed9c569..6e9b6cad7 100644 --- a/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/functions.adoc +++ b/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/functions.adoc @@ -206,8 +206,8 @@ static class Config { public FunctionCallback weatherFunctionInfo() { return FunctionCallback.builder() - .description("Get the weather in location") // (2) function description .function("CurrentWeather", new MockWeatherService()) // (1) function name and instance + .description("Get the weather in location") // (2) function description .inputType(MockWeatherService.Request.class) // (3) input type to build the JSON schema .build(); } @@ -226,8 +226,8 @@ class Config { fun weatherFunctionInfo(): FunctionCallback { return FunctionCallback.builder() - .description("Get the weather in location") // (2) function description .function("CurrentWeather", MockWeatherService()) // (1) function name and instance + .description("Get the weather in location") // (2) function description // (3) Required due to Kotlin SAM conversion being an opaque lambda .inputType() .build(); @@ -287,8 +287,8 @@ ChatClient chatClient = ... ChatResponse response = this.chatClient.prompt("What's the weather like in San Francisco, Tokyo, and Paris?") .functions(FunctionCallback.builder() - .description("Get the weather in location") // (2) function description .function("currentWeather", (Request request) -> new Response(30.0, Unit.C)) // (1) function name and instance + .description("Get the weather in location") // (2) function description .inputType(MockWeatherService.Request.class) // (3) input type to build the JSON schema .build()) .call() @@ -320,8 +320,8 @@ You need the `FunctionCallback.Builder` to create `MethodInvokingFunctionCallbac ---- // Create using builder pattern FunctionCallback methodInvokingCallback = FunctionCallback.builder() - .description("Function calling description") // Hints the AI to know when to call this method .method("MethodName", Class...argumentTypes) // The method to invoke and its argument types + .description("Function calling description") // Hints the AI to know when to call this method .targetObject(targetObject) // Required instance methods for static methods use targetClass .build(); ---- @@ -342,8 +342,8 @@ public class WeatherService { // Usage FunctionCallback callback = FunctionCallback.builder() - .description("Get weather information for a city") .method("getWeather", String.class, TemperatureUnit.class) + .description("Get weather information for a city") .targetClass(WeatherService.class) .build(); ---- @@ -364,8 +364,8 @@ DeviceController controller = new DeviceController(); String response = ChatClient.create(chatModel).prompt() .user("Turn on the living room lights") .functions(FunctionCallback.builder() - .description("Control device state") .method("setDeviceState", String.class,boolean.class,ToolContext.class) + .description("Control device state") .targetObject(controller) .build()) .toolContext(Map.of("location", "home")) @@ -423,8 +423,8 @@ BiFunction ChatResponse response = chatClient.prompt("What's the weather like in San Francisco, Tokyo, and Paris?") .functions(FunctionCallback.builder() - .description("Get the weather in location") .function("getCurrentWeather", this.weatherFunction) + .description("Get the weather in location") .inputType(MockWeatherService.Request.class) .build()) .toolContext(Map.of("sessionId", "1234", "userId", "5678")) @@ -453,8 +453,8 @@ DeviceController controller = new DeviceController(); String response = ChatClient.create(chatModel).prompt() .user("Turn on the living room lights") .functions(FunctionCallback.builder() - .description("Control device state") .method("setDeviceState", String.class,boolean.class,ToolContext.class) + .description("Control device state") .targetObject(controller) .build()) .toolContext(Map.of("location", "home")) diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/anthropic/tool/FunctionCallWithPromptFunctionIT.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/anthropic/tool/FunctionCallWithPromptFunctionIT.java index aa2d11101..f856113e6 100644 --- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/anthropic/tool/FunctionCallWithPromptFunctionIT.java +++ b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/anthropic/tool/FunctionCallWithPromptFunctionIT.java @@ -59,8 +59,8 @@ public class FunctionCallWithPromptFunctionIT { var promptOptions = AnthropicChatOptions.builder() .withFunctionCallbacks(List.of(FunctionCallback.builder() - .description("Get the weather in location. Return temperature in 36°F or 36°C format.") .function("CurrentWeatherService", new MockWeatherService()) + .description("Get the weather in location. Return temperature in 36°F or 36°C format.") .inputType(MockWeatherService.Request.class) .build())) .build(); diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/azure/tool/FunctionCallWithFunctionWrapperIT.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/azure/tool/FunctionCallWithFunctionWrapperIT.java index 721359ef2..f1400ab22 100644 --- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/azure/tool/FunctionCallWithFunctionWrapperIT.java +++ b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/azure/tool/FunctionCallWithFunctionWrapperIT.java @@ -80,8 +80,8 @@ public class FunctionCallWithFunctionWrapperIT { public FunctionCallback weatherFunctionInfo() { return FunctionCallback.builder() - .description("Get the current weather in a given location") .function("WeatherInfo", new MockWeatherService()) + .description("Get the current weather in a given location") .inputType(MockWeatherService.Request.class) .build(); } diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/azure/tool/FunctionCallWithPromptFunctionIT.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/azure/tool/FunctionCallWithPromptFunctionIT.java index 908fbc59e..c682aaa8e 100644 --- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/azure/tool/FunctionCallWithPromptFunctionIT.java +++ b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/azure/tool/FunctionCallWithPromptFunctionIT.java @@ -62,8 +62,8 @@ public class FunctionCallWithPromptFunctionIT { var promptOptions = AzureOpenAiChatOptions.builder() .withFunctionCallbacks(List.of(FunctionCallback.builder() - .description("Get the weather in location") .function("CurrentWeatherService", new MockWeatherService()) + .description("Get the weather in location") .inputType(MockWeatherService.Request.class) .build())) .build(); diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/bedrock/converse/tool/FunctionCallWithPromptFunctionIT.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/bedrock/converse/tool/FunctionCallWithPromptFunctionIT.java index d5bc70453..aa05b8dcc 100644 --- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/bedrock/converse/tool/FunctionCallWithPromptFunctionIT.java +++ b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/bedrock/converse/tool/FunctionCallWithPromptFunctionIT.java @@ -58,8 +58,8 @@ public class FunctionCallWithPromptFunctionIT { var promptOptions = FunctionCallingOptions.builder() .withFunctionCallbacks(List.of(FunctionCallback.builder() - .description("Get the weather in location. Return temperature in 36°F or 36°C format.") .function("CurrentWeatherService", new MockWeatherService()) + .description("Get the weather in location. Return temperature in 36°F or 36°C format.") .inputType(MockWeatherService.Request.class) .build())) .build(); diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/minimax/FunctionCallbackInPromptIT.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/minimax/FunctionCallbackInPromptIT.java index e1ca67dba..ad91d5094 100644 --- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/minimax/FunctionCallbackInPromptIT.java +++ b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/minimax/FunctionCallbackInPromptIT.java @@ -64,8 +64,8 @@ public class FunctionCallbackInPromptIT { var promptOptions = MiniMaxChatOptions.builder() .withFunctionCallbacks(List.of(FunctionCallback.builder() - .description("Get the weather in location") .function("CurrentWeatherService", new MockWeatherService()) + .description("Get the weather in location") .inputType(MockWeatherService.Request.class) .build())) .build(); @@ -90,8 +90,8 @@ public class FunctionCallbackInPromptIT { var promptOptions = MiniMaxChatOptions.builder() .withFunctionCallbacks(List.of(FunctionCallback.builder() - .description("Get the weather in location") .function("CurrentWeatherService", new MockWeatherService()) + .description("Get the weather in location") .inputType(MockWeatherService.Request.class) .build())) .build(); diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/minimax/MiniMaxFunctionCallbackIT.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/minimax/MiniMaxFunctionCallbackIT.java index d2fb03a69..3966cbf2c 100644 --- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/minimax/MiniMaxFunctionCallbackIT.java +++ b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/minimax/MiniMaxFunctionCallbackIT.java @@ -111,8 +111,8 @@ public class MiniMaxFunctionCallbackIT { public FunctionCallback weatherFunctionInfo() { return FunctionCallback.builder() - .description("Get the weather in location") .function("WeatherInfo", new MockWeatherService()) + .description("Get the weather in location") .inputType(MockWeatherService.Request.class) .build(); } diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/mistralai/tool/PaymentStatusPromptIT.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/mistralai/tool/PaymentStatusPromptIT.java index 8c001dc7e..b9d199e55 100644 --- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/mistralai/tool/PaymentStatusPromptIT.java +++ b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/mistralai/tool/PaymentStatusPromptIT.java @@ -65,9 +65,9 @@ public class PaymentStatusPromptIT { var promptOptions = MistralAiChatOptions.builder() .withFunctionCallbacks(List.of(FunctionCallback.builder() - .description("Get payment status of a transaction") .function("retrievePaymentStatus", (Transaction transaction) -> new Status(DATA.get(transaction).status())) + .description("Get payment status of a transaction") .inputType(Transaction.class) .build())) .build(); diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/mistralai/tool/WeatherServicePromptIT.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/mistralai/tool/WeatherServicePromptIT.java index 394a5f155..48edeb116 100644 --- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/mistralai/tool/WeatherServicePromptIT.java +++ b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/mistralai/tool/WeatherServicePromptIT.java @@ -74,8 +74,8 @@ public class WeatherServicePromptIT { var promptOptions = MistralAiChatOptions.builder() .withToolChoice(ToolChoice.AUTO) .withFunctionCallbacks(List.of(FunctionCallback.builder() - .description("Get the current weather in requested location") .function("CurrentWeatherService", new MyWeatherService()) + .description("Get the current weather in requested location") .inputType(MyWeatherService.Request.class) .build())) .build(); @@ -100,8 +100,8 @@ public class WeatherServicePromptIT { PortableFunctionCallingOptions functionOptions = FunctionCallingOptions.builder() .withFunctionCallbacks(List.of(FunctionCallback.builder() - .description("Get the current weather in requested location") .function("CurrentWeatherService", new MyWeatherService()) + .description("Get the current weather in requested location") .inputType(MyWeatherService.Request.class) .build())) diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/moonshot/tool/FunctionCallbackInPromptIT.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/moonshot/tool/FunctionCallbackInPromptIT.java index 3b95bc222..550939b63 100644 --- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/moonshot/tool/FunctionCallbackInPromptIT.java +++ b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/moonshot/tool/FunctionCallbackInPromptIT.java @@ -65,8 +65,8 @@ public class FunctionCallbackInPromptIT { var promptOptions = MoonshotChatOptions.builder() .withFunctionCallbacks(List.of(FunctionCallback.builder() - .description("Get the weather in location") .function("CurrentWeatherService", new MockWeatherService()) + .description("Get the weather in location") .inputType(MockWeatherService.Request.class) .build())) .build(); @@ -91,8 +91,8 @@ public class FunctionCallbackInPromptIT { var promptOptions = MoonshotChatOptions.builder() .withFunctionCallbacks(List.of(FunctionCallback.builder() - .description("Get the weather in location") .function("CurrentWeatherService", new MockWeatherService()) + .description("Get the weather in location") .inputType(MockWeatherService.Request.class) .build())) .build(); diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/moonshot/tool/MoonshotFunctionCallbackIT.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/moonshot/tool/MoonshotFunctionCallbackIT.java index c5141515e..f68bed8b0 100644 --- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/moonshot/tool/MoonshotFunctionCallbackIT.java +++ b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/moonshot/tool/MoonshotFunctionCallbackIT.java @@ -114,8 +114,8 @@ public class MoonshotFunctionCallbackIT { public FunctionCallback weatherFunctionInfo() { return FunctionCallback.builder() - .description("Get the weather in location") .function("WeatherInfo", new MockWeatherService()) + .description("Get the weather in location") .inputType(MockWeatherService.Request.class) .build(); } diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/ollama/tool/FunctionCallbackInPromptIT.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/ollama/tool/FunctionCallbackInPromptIT.java index 66d9ef09d..df37a4edb 100644 --- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/ollama/tool/FunctionCallbackInPromptIT.java +++ b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/ollama/tool/FunctionCallbackInPromptIT.java @@ -72,9 +72,9 @@ public class FunctionCallbackInPromptIT extends BaseOllamaIT { var promptOptions = OllamaOptions.builder() .withFunctionCallbacks(List.of(FunctionCallback.builder() + .function("CurrentWeatherService", new MockWeatherService()) .description( "Find the weather conditions, forecasts, and temperatures for a location, like a city or state.") - .function("CurrentWeatherService", new MockWeatherService()) .inputType(MockWeatherService.Request.class) .build())) .build(); @@ -99,9 +99,9 @@ public class FunctionCallbackInPromptIT extends BaseOllamaIT { var promptOptions = OllamaOptions.builder() .withFunctionCallbacks(List.of(FunctionCallback.builder() + .function("CurrentWeatherService", new MockWeatherService()) .description( "Find the weather conditions, forecasts, and temperatures for a location, like a city or state.") - .function("CurrentWeatherService", new MockWeatherService()) .inputType(MockWeatherService.Request.class) .build())) .build(); diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/ollama/tool/OllamaFunctionCallbackIT.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/ollama/tool/OllamaFunctionCallbackIT.java index d8c4c0ae6..1ee3667db 100644 --- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/ollama/tool/OllamaFunctionCallbackIT.java +++ b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/ollama/tool/OllamaFunctionCallbackIT.java @@ -140,9 +140,9 @@ public class OllamaFunctionCallbackIT extends BaseOllamaIT { public FunctionCallback weatherFunctionInfo() { return FunctionCallback.builder() + .function("WeatherInfo", new MockWeatherService()) .description( "Find the weather conditions, forecasts, and temperatures for a location, like a city or state.") - .function("WeatherInfo", new MockWeatherService()) .inputType(MockWeatherService.Request.class) .build(); } diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/openai/tool/FunctionCallbackInPrompt2IT.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/openai/tool/FunctionCallbackInPrompt2IT.java index 1ae2a5d84..aa0018ee8 100644 --- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/openai/tool/FunctionCallbackInPrompt2IT.java +++ b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/openai/tool/FunctionCallbackInPrompt2IT.java @@ -61,8 +61,8 @@ public class FunctionCallbackInPrompt2IT { String content = ChatClient.builder(chatModel).build().prompt() .user("What's the weather like in San Francisco, Tokyo, and Paris?") .functions(FunctionCallback.builder() - .description("Get the weather in location") .function("CurrentWeatherService", new MockWeatherService()) + .description("Get the weather in location") .inputType(MockWeatherService.Request.class) .build()) .call().content(); @@ -89,11 +89,11 @@ public class FunctionCallbackInPrompt2IT { String content = ChatClient.builder(chatModel).build().prompt() .user("Turn the light on in the kitchen and in the living room!") .functions(FunctionCallback.builder() - .description("Turn light on or off in a room") .function("turnLight", (LightInfo lightInfo) -> { logger.info("Turning light to [" + lightInfo.isOn + "] in " + lightInfo.roomName()); state.put(lightInfo.roomName(), lightInfo.isOn()); }) + .description("Turn light on or off in a room") .inputType(LightInfo.class) .build()) .call().content(); @@ -115,8 +115,8 @@ public class FunctionCallbackInPrompt2IT { String content = ChatClient.builder(chatModel).build().prompt() .user("What's the weather like in Amsterdam?") .functions(FunctionCallback.builder() - .description("Get the weather in location") .function("CurrentWeatherService", input -> "18 degrees Celsius") + .description("Get the weather in location") .inputType(MockWeatherService.Request.class) .build()) .call().content(); diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/openai/tool/FunctionCallbackInPromptIT.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/openai/tool/FunctionCallbackInPromptIT.java index fc4d4a447..9526c00f9 100644 --- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/openai/tool/FunctionCallbackInPromptIT.java +++ b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/openai/tool/FunctionCallbackInPromptIT.java @@ -63,8 +63,8 @@ public class FunctionCallbackInPromptIT { var promptOptions = OpenAiChatOptions.builder() .withFunctionCallbacks(List.of(FunctionCallback.builder() - .description("Get the weather in location") .function("CurrentWeatherService", new MockWeatherService()) + .description("Get the weather in location") .inputType(MockWeatherService.Request.class) .build())) .build(); @@ -92,8 +92,8 @@ public class FunctionCallbackInPromptIT { var promptOptions = OpenAiChatOptions.builder() .withFunctionCallbacks(List.of(FunctionCallback.builder() - .description("Get the weather in location") .function("CurrentWeatherService", new MockWeatherService()) + .description("Get the weather in location") .inputType(MockWeatherService.Request.class) .build())) .build(); diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/openai/tool/OpenAiFunctionCallback2IT.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/openai/tool/OpenAiFunctionCallback2IT.java index c471b154b..3499278e9 100644 --- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/openai/tool/OpenAiFunctionCallback2IT.java +++ b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/openai/tool/OpenAiFunctionCallback2IT.java @@ -96,8 +96,8 @@ public class OpenAiFunctionCallback2IT { public FunctionCallback weatherFunctionInfo() { return FunctionCallback.builder() - .description("Get the weather in location") .function("WeatherInfo", new MockWeatherService()) + .description("Get the weather in location") .inputType(MockWeatherService.Request.class) .build(); } diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/openai/tool/OpenAiFunctionCallbackIT.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/openai/tool/OpenAiFunctionCallbackIT.java index 468f8ff5e..1337eadb1 100644 --- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/openai/tool/OpenAiFunctionCallbackIT.java +++ b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/openai/tool/OpenAiFunctionCallbackIT.java @@ -107,8 +107,8 @@ public class OpenAiFunctionCallbackIT { public FunctionCallback weatherFunctionInfo() { return FunctionCallback.builder() - .description("Get the weather in location") .function("WeatherInfo", new MockWeatherService()) + .description("Get the weather in location") .inputType(MockWeatherService.Request.class) .build(); } diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/vertexai/gemini/tool/FunctionCallWithFunctionWrapperIT.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/vertexai/gemini/tool/FunctionCallWithFunctionWrapperIT.java index f617efdcb..79272be73 100644 --- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/vertexai/gemini/tool/FunctionCallWithFunctionWrapperIT.java +++ b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/vertexai/gemini/tool/FunctionCallWithFunctionWrapperIT.java @@ -80,9 +80,9 @@ public class FunctionCallWithFunctionWrapperIT { public FunctionCallback weatherFunctionInfo() { return FunctionCallback.builder() + .function("WeatherInfo", new MockWeatherService()) .description("Get the current weather in a given location") .schemaType(SchemaType.OPEN_API_SCHEMA) - .function("WeatherInfo", new MockWeatherService()) .inputType(MockWeatherService.Request.class) .build(); } diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/vertexai/gemini/tool/FunctionCallWithPromptFunctionIT.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/vertexai/gemini/tool/FunctionCallWithPromptFunctionIT.java index 3de7c0faf..d051490ed 100644 --- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/vertexai/gemini/tool/FunctionCallWithPromptFunctionIT.java +++ b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/vertexai/gemini/tool/FunctionCallWithPromptFunctionIT.java @@ -69,9 +69,9 @@ public class FunctionCallWithPromptFunctionIT { var promptOptions = VertexAiGeminiChatOptions.builder() .withFunctionCallbacks(List.of(FunctionCallback.builder() + .function("CurrentWeatherService", new MockWeatherService()) .schemaType(SchemaType.OPEN_API_SCHEMA) // IMPORTANT!! .description("Get the weather in location") - .function("CurrentWeatherService", new MockWeatherService()) .inputType(MockWeatherService.Request.class) .build())) .build(); diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/zhipuai/tool/FunctionCallbackInPromptIT.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/zhipuai/tool/FunctionCallbackInPromptIT.java index 7dbf49051..f3d39a0a7 100644 --- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/zhipuai/tool/FunctionCallbackInPromptIT.java +++ b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/zhipuai/tool/FunctionCallbackInPromptIT.java @@ -65,8 +65,8 @@ public class FunctionCallbackInPromptIT { var promptOptions = ZhiPuAiChatOptions.builder() .withFunctionCallbacks(List.of(FunctionCallback.builder() - .description("Get the weather in location") .function("CurrentWeatherService", new MockWeatherService()) + .description("Get the weather in location") .inputType(MockWeatherService.Request.class) // .responseConverter(response -> "" + response.temp() + // response.unit()) @@ -93,8 +93,8 @@ public class FunctionCallbackInPromptIT { var promptOptions = ZhiPuAiChatOptions.builder() .withFunctionCallbacks(List.of(FunctionCallback.builder() - .description("Get the weather in location") .function("CurrentWeatherService", new MockWeatherService()) + .description("Get the weather in location") .inputType(MockWeatherService.Request.class) .build())) .build(); diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/zhipuai/tool/ZhipuAiFunctionCallbackIT.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/zhipuai/tool/ZhipuAiFunctionCallbackIT.java index 73f9ea121..d22c745ea 100644 --- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/zhipuai/tool/ZhipuAiFunctionCallbackIT.java +++ b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/zhipuai/tool/ZhipuAiFunctionCallbackIT.java @@ -112,8 +112,8 @@ public class ZhipuAiFunctionCallbackIT { public FunctionCallback weatherFunctionInfo() { return FunctionCallback.builder() - .description("Get the weather in location") .function("WeatherInfo", new MockWeatherService()) + .description("Get the weather in location") .inputType(MockWeatherService.Request.class) // .responseConverter(response -> "" + response.temp() + response.unit()) .build(); diff --git a/spring-ai-spring-boot-autoconfigure/src/test/kotlin/org/springframework/ai/autoconfigure/ollama/tool/FunctionCallbackKotlinIT.kt b/spring-ai-spring-boot-autoconfigure/src/test/kotlin/org/springframework/ai/autoconfigure/ollama/tool/FunctionCallbackKotlinIT.kt index 0b3399027..c5d7d300a 100644 --- a/spring-ai-spring-boot-autoconfigure/src/test/kotlin/org/springframework/ai/autoconfigure/ollama/tool/FunctionCallbackKotlinIT.kt +++ b/spring-ai-spring-boot-autoconfigure/src/test/kotlin/org/springframework/ai/autoconfigure/ollama/tool/FunctionCallbackKotlinIT.kt @@ -106,10 +106,10 @@ class FunctionCallbackKotlinIT : BaseOllamaIT() { @Bean open fun weatherFunctionInfo(): FunctionCallback { return FunctionCallback.builder() + .function("WeatherInfo", MockKotlinWeatherService()) .description( "Find the weather conditions, forecasts, and temperatures for a location, like a city or state." ) - .function("WeatherInfo", MockKotlinWeatherService()) .inputType(KotlinRequest::class.java) .build() }