diff --git a/auto-configurations/models/spring-ai-autoconfigure-model-bedrock-ai/src/main/java/org/springframework/ai/model/bedrock/converse/autoconfigure/BedrockConverseProxyChatProperties.java b/auto-configurations/models/spring-ai-autoconfigure-model-bedrock-ai/src/main/java/org/springframework/ai/model/bedrock/converse/autoconfigure/BedrockConverseProxyChatProperties.java index 3afa4a2500a..acf9fdd9a00 100644 --- a/auto-configurations/models/spring-ai-autoconfigure-model-bedrock-ai/src/main/java/org/springframework/ai/model/bedrock/converse/autoconfigure/BedrockConverseProxyChatProperties.java +++ b/auto-configurations/models/spring-ai-autoconfigure-model-bedrock-ai/src/main/java/org/springframework/ai/model/bedrock/converse/autoconfigure/BedrockConverseProxyChatProperties.java @@ -44,7 +44,7 @@ public ToolCallingChatOptions getOptions() { } public void setOptions(ToolCallingChatOptions options) { - Assert.notNull(options, "FunctionCallingOptions must not be null"); + Assert.notNull(options, "ToolCallingChatOptions must not be null"); this.options = options; } diff --git a/auto-configurations/models/spring-ai-autoconfigure-model-minimax/src/test/java/org/springframework/ai/model/minimax/autoconfigure/FunctionCallbackInPromptIT.java b/auto-configurations/models/spring-ai-autoconfigure-model-minimax/src/test/java/org/springframework/ai/model/minimax/autoconfigure/FunctionCallbackInPromptIT.java index b694a13d294..174bfc1a7af 100644 --- a/auto-configurations/models/spring-ai-autoconfigure-model-minimax/src/test/java/org/springframework/ai/model/minimax/autoconfigure/FunctionCallbackInPromptIT.java +++ b/auto-configurations/models/spring-ai-autoconfigure-model-minimax/src/test/java/org/springframework/ai/model/minimax/autoconfigure/FunctionCallbackInPromptIT.java @@ -32,7 +32,6 @@ import org.springframework.ai.chat.prompt.Prompt; import org.springframework.ai.minimax.MiniMaxChatModel; import org.springframework.ai.minimax.MiniMaxChatOptions; -import org.springframework.ai.model.function.FunctionCallback; import org.springframework.ai.retry.autoconfigure.SpringAiRetryAutoConfiguration; import org.springframework.ai.tool.function.FunctionToolCallback; import org.springframework.boot.autoconfigure.AutoConfigurations; diff --git a/auto-configurations/models/spring-ai-autoconfigure-model-minimax/src/test/java/org/springframework/ai/model/minimax/autoconfigure/FunctionCallbackWithPlainFunctionBeanIT.java b/auto-configurations/models/spring-ai-autoconfigure-model-minimax/src/test/java/org/springframework/ai/model/minimax/autoconfigure/FunctionCallbackWithPlainFunctionBeanIT.java index 54e137274e8..a566b84c046 100644 --- a/auto-configurations/models/spring-ai-autoconfigure-model-minimax/src/test/java/org/springframework/ai/model/minimax/autoconfigure/FunctionCallbackWithPlainFunctionBeanIT.java +++ b/auto-configurations/models/spring-ai-autoconfigure-model-minimax/src/test/java/org/springframework/ai/model/minimax/autoconfigure/FunctionCallbackWithPlainFunctionBeanIT.java @@ -33,7 +33,6 @@ import org.springframework.ai.chat.prompt.Prompt; import org.springframework.ai.minimax.MiniMaxChatModel; import org.springframework.ai.minimax.MiniMaxChatOptions; -import org.springframework.ai.model.function.FunctionCallingOptions; import org.springframework.ai.model.tool.ToolCallingChatOptions; import org.springframework.ai.retry.autoconfigure.SpringAiRetryAutoConfiguration; import org.springframework.boot.autoconfigure.AutoConfigurations; diff --git a/auto-configurations/models/spring-ai-autoconfigure-model-minimax/src/test/java/org/springframework/ai/model/minimax/autoconfigure/MiniMaxFunctionCallbackIT.java b/auto-configurations/models/spring-ai-autoconfigure-model-minimax/src/test/java/org/springframework/ai/model/minimax/autoconfigure/MiniMaxFunctionCallbackIT.java index 44df67c2097..8e41a0641dd 100644 --- a/auto-configurations/models/spring-ai-autoconfigure-model-minimax/src/test/java/org/springframework/ai/model/minimax/autoconfigure/MiniMaxFunctionCallbackIT.java +++ b/auto-configurations/models/spring-ai-autoconfigure-model-minimax/src/test/java/org/springframework/ai/model/minimax/autoconfigure/MiniMaxFunctionCallbackIT.java @@ -32,7 +32,6 @@ import org.springframework.ai.chat.prompt.Prompt; import org.springframework.ai.minimax.MiniMaxChatModel; import org.springframework.ai.minimax.MiniMaxChatOptions; -import org.springframework.ai.model.function.FunctionCallback; import org.springframework.ai.model.tool.autoconfigure.ToolCallingAutoConfiguration; import org.springframework.ai.retry.autoconfigure.SpringAiRetryAutoConfiguration; import org.springframework.ai.tool.function.FunctionToolCallback; diff --git a/auto-configurations/models/spring-ai-autoconfigure-model-ollama/src/test/kotlin/org/springframework/ai/model/ollama/autoconfigure/tool/FunctionCallbackContextKotlinIT.kt b/auto-configurations/models/spring-ai-autoconfigure-model-ollama/src/test/kotlin/org/springframework/ai/model/ollama/autoconfigure/tool/FunctionCallbackContextKotlinIT.kt index 63ad113bcc8..a9495617f17 100644 --- a/auto-configurations/models/spring-ai-autoconfigure-model-ollama/src/test/kotlin/org/springframework/ai/model/ollama/autoconfigure/tool/FunctionCallbackContextKotlinIT.kt +++ b/auto-configurations/models/spring-ai-autoconfigure-model-ollama/src/test/kotlin/org/springframework/ai/model/ollama/autoconfigure/tool/FunctionCallbackContextKotlinIT.kt @@ -59,7 +59,7 @@ class FunctionCallbackResolverKotlinIT : BaseOllamaIT() { .withUserConfiguration(Config::class.java) @Test - fun functionCallTest() { + fun toolCallTest() { this.contextRunner.run {context -> val chatModel = context.getBean(OllamaChatModel::class.java) diff --git a/auto-configurations/models/spring-ai-autoconfigure-model-ollama/src/test/kotlin/org/springframework/ai/model/ollama/autoconfigure/tool/FunctionCallbackKotlinIT.kt b/auto-configurations/models/spring-ai-autoconfigure-model-ollama/src/test/kotlin/org/springframework/ai/model/ollama/autoconfigure/tool/ToolCallbackKotlinIT.kt similarity index 95% rename from auto-configurations/models/spring-ai-autoconfigure-model-ollama/src/test/kotlin/org/springframework/ai/model/ollama/autoconfigure/tool/FunctionCallbackKotlinIT.kt rename to auto-configurations/models/spring-ai-autoconfigure-model-ollama/src/test/kotlin/org/springframework/ai/model/ollama/autoconfigure/tool/ToolCallbackKotlinIT.kt index 6976c18ab74..000d7eecb98 100644 --- a/auto-configurations/models/spring-ai-autoconfigure-model-ollama/src/test/kotlin/org/springframework/ai/model/ollama/autoconfigure/tool/FunctionCallbackKotlinIT.kt +++ b/auto-configurations/models/spring-ai-autoconfigure-model-ollama/src/test/kotlin/org/springframework/ai/model/ollama/autoconfigure/tool/ToolCallbackKotlinIT.kt @@ -33,7 +33,7 @@ import org.springframework.context.annotation.Configuration import org.springframework.context.annotation.Description -class FunctionCallbackKotlinIT : BaseOllamaIT() { +class ToolCallbackKotlinIT : BaseOllamaIT() { companion object { @@ -46,7 +46,7 @@ class FunctionCallbackKotlinIT : BaseOllamaIT() { } } - private val logger = LoggerFactory.getLogger(FunctionCallbackKotlinIT::class.java) + private val logger = LoggerFactory.getLogger(ToolCallbackKotlinIT::class.java) private val contextRunner = ApplicationContextRunner() .withPropertyValues( @@ -59,7 +59,7 @@ class FunctionCallbackKotlinIT : BaseOllamaIT() { .withUserConfiguration(Config::class.java) @Test - fun functionCallTest() { + fun toolCallTest() { this.contextRunner.run {context -> val chatModel = context.getBean(OllamaChatModel::class.java) diff --git a/auto-configurations/models/spring-ai-autoconfigure-model-vertex-ai/src/main/java/org/springframework/ai/model/vertexai/autoconfigure/gemini/VertexAiGeminiChatAutoConfiguration.java b/auto-configurations/models/spring-ai-autoconfigure-model-vertex-ai/src/main/java/org/springframework/ai/model/vertexai/autoconfigure/gemini/VertexAiGeminiChatAutoConfiguration.java index 09f91c6a947..2a61904ea9e 100644 --- a/auto-configurations/models/spring-ai-autoconfigure-model-vertex-ai/src/main/java/org/springframework/ai/model/vertexai/autoconfigure/gemini/VertexAiGeminiChatAutoConfiguration.java +++ b/auto-configurations/models/spring-ai-autoconfigure-model-vertex-ai/src/main/java/org/springframework/ai/model/vertexai/autoconfigure/gemini/VertexAiGeminiChatAutoConfiguration.java @@ -113,16 +113,4 @@ public VertexAiGeminiChatModel vertexAiGeminiChat(VertexAI vertexAi, VertexAiGem return chatModel; } - /** - * Because of the OPEN_API_SCHEMA type, the FunctionCallbackResolver instance must - * different from the other JSON schema types. - */ - // private FunctionCallbackResolver springAiFunctionManager(ApplicationContext - // context) { - // DefaultFunctionCallbackResolver manager = new DefaultFunctionCallbackResolver(); - // manager.setSchemaType(SchemaType.OPEN_API_SCHEMA); - // manager.setApplicationContext(context); - // return manager; - // } - } diff --git a/auto-configurations/models/spring-ai-autoconfigure-model-vertex-ai/src/test/java/org/springframework/ai/model/vertexai/autoconfigure/gemini/tool/FunctionCallWithFunctionBeanIT.java b/auto-configurations/models/spring-ai-autoconfigure-model-vertex-ai/src/test/java/org/springframework/ai/model/vertexai/autoconfigure/gemini/tool/FunctionCallWithFunctionBeanIT.java index 89c827ee571..5a0bf553b84 100644 --- a/auto-configurations/models/spring-ai-autoconfigure-model-vertex-ai/src/test/java/org/springframework/ai/model/vertexai/autoconfigure/gemini/tool/FunctionCallWithFunctionBeanIT.java +++ b/auto-configurations/models/spring-ai-autoconfigure-model-vertex-ai/src/test/java/org/springframework/ai/model/vertexai/autoconfigure/gemini/tool/FunctionCallWithFunctionBeanIT.java @@ -27,7 +27,7 @@ import org.springframework.ai.chat.messages.UserMessage; import org.springframework.ai.chat.model.ChatResponse; import org.springframework.ai.chat.prompt.Prompt; -import org.springframework.ai.model.function.FunctionCallingOptions; +import org.springframework.ai.model.tool.ToolCallingChatOptions; import org.springframework.ai.model.vertexai.autoconfigure.gemini.VertexAiGeminiChatAutoConfiguration; import org.springframework.ai.vertexai.gemini.VertexAiGeminiChatModel; import org.springframework.ai.vertexai.gemini.VertexAiGeminiChatOptions; @@ -109,14 +109,14 @@ void functionCallWithPortableFunctionCallingOptions() { """); ChatResponse response = chatModel.call(new Prompt(List.of(userMessage), - FunctionCallingOptions.builder().function("weatherFunction").build())); + ToolCallingChatOptions.builder().toolNames("weatherFunction").build())); logger.info("Response: {}", response); assertThat(response.getResult().getOutput().getText()).contains("30", "10", "15"); response = chatModel.call(new Prompt(List.of(userMessage), - VertexAiGeminiChatOptions.builder().function("weatherFunction3").build())); + VertexAiGeminiChatOptions.builder().toolName("weatherFunction3").build())); logger.info("Response: {}", response); diff --git a/auto-configurations/models/spring-ai-autoconfigure-model-zhipuai/src/test/java/org/springframework/ai/model/zhipuai/autoconfigure/tool/FunctionCallbackInPromptIT.java b/auto-configurations/models/spring-ai-autoconfigure-model-zhipuai/src/test/java/org/springframework/ai/model/zhipuai/autoconfigure/tool/FunctionCallbackInPromptIT.java index 00367ebefd8..0cf2847ec3a 100644 --- a/auto-configurations/models/spring-ai-autoconfigure-model-zhipuai/src/test/java/org/springframework/ai/model/zhipuai/autoconfigure/tool/FunctionCallbackInPromptIT.java +++ b/auto-configurations/models/spring-ai-autoconfigure-model-zhipuai/src/test/java/org/springframework/ai/model/zhipuai/autoconfigure/tool/FunctionCallbackInPromptIT.java @@ -30,7 +30,6 @@ import org.springframework.ai.chat.model.ChatResponse; import org.springframework.ai.chat.model.Generation; import org.springframework.ai.chat.prompt.Prompt; -import org.springframework.ai.model.function.FunctionCallback; import org.springframework.ai.model.zhipuai.autoconfigure.ZhiPuAiChatAutoConfiguration; import org.springframework.ai.retry.autoconfigure.SpringAiRetryAutoConfiguration; import org.springframework.ai.tool.function.FunctionToolCallback; diff --git a/auto-configurations/models/spring-ai-autoconfigure-model-zhipuai/src/test/java/org/springframework/ai/model/zhipuai/autoconfigure/tool/FunctionCallbackWithPlainFunctionBeanIT.java b/auto-configurations/models/spring-ai-autoconfigure-model-zhipuai/src/test/java/org/springframework/ai/model/zhipuai/autoconfigure/tool/FunctionCallbackWithPlainFunctionBeanIT.java index 03ff513be90..addd2d0961a 100644 --- a/auto-configurations/models/spring-ai-autoconfigure-model-zhipuai/src/test/java/org/springframework/ai/model/zhipuai/autoconfigure/tool/FunctionCallbackWithPlainFunctionBeanIT.java +++ b/auto-configurations/models/spring-ai-autoconfigure-model-zhipuai/src/test/java/org/springframework/ai/model/zhipuai/autoconfigure/tool/FunctionCallbackWithPlainFunctionBeanIT.java @@ -31,7 +31,6 @@ import org.springframework.ai.chat.model.ChatResponse; import org.springframework.ai.chat.model.Generation; import org.springframework.ai.chat.prompt.Prompt; -import org.springframework.ai.model.function.FunctionCallingOptions; import org.springframework.ai.model.tool.ToolCallingChatOptions; import org.springframework.ai.model.zhipuai.autoconfigure.ZhiPuAiChatAutoConfiguration; import org.springframework.ai.retry.autoconfigure.SpringAiRetryAutoConfiguration; diff --git a/auto-configurations/models/spring-ai-autoconfigure-model-zhipuai/src/test/java/org/springframework/ai/model/zhipuai/autoconfigure/tool/ZhipuAiFunctionCallbackIT.java b/auto-configurations/models/spring-ai-autoconfigure-model-zhipuai/src/test/java/org/springframework/ai/model/zhipuai/autoconfigure/tool/ZhipuAiFunctionCallbackIT.java index b9a4176ea58..13088a263c1 100644 --- a/auto-configurations/models/spring-ai-autoconfigure-model-zhipuai/src/test/java/org/springframework/ai/model/zhipuai/autoconfigure/tool/ZhipuAiFunctionCallbackIT.java +++ b/auto-configurations/models/spring-ai-autoconfigure-model-zhipuai/src/test/java/org/springframework/ai/model/zhipuai/autoconfigure/tool/ZhipuAiFunctionCallbackIT.java @@ -30,7 +30,6 @@ import org.springframework.ai.chat.model.ChatResponse; import org.springframework.ai.chat.model.Generation; import org.springframework.ai.chat.prompt.Prompt; -import org.springframework.ai.model.function.FunctionCallback; import org.springframework.ai.model.zhipuai.autoconfigure.ZhiPuAiChatAutoConfiguration; import org.springframework.ai.retry.autoconfigure.SpringAiRetryAutoConfiguration; import org.springframework.ai.tool.ToolCallback; diff --git a/auto-configurations/models/tool/spring-ai-autoconfigure-model-tool/src/test/java/org/springframework/ai/model/tool/autoconfigure/ToolCallingAutoConfigurationTests.java b/auto-configurations/models/tool/spring-ai-autoconfigure-model-tool/src/test/java/org/springframework/ai/model/tool/autoconfigure/ToolCallingAutoConfigurationTests.java index be73e1d2b2a..497c3e78709 100644 --- a/auto-configurations/models/tool/spring-ai-autoconfigure-model-tool/src/test/java/org/springframework/ai/model/tool/autoconfigure/ToolCallingAutoConfigurationTests.java +++ b/auto-configurations/models/tool/spring-ai-autoconfigure-model-tool/src/test/java/org/springframework/ai/model/tool/autoconfigure/ToolCallingAutoConfigurationTests.java @@ -75,24 +75,26 @@ void resolveMultipleFunctionAndToolCallbacks() { assertThat(toolCallbackResolver).isInstanceOf(DelegatingToolCallbackResolver.class); assertThat(toolCallbackResolver.resolve("getForecast")).isNotNull(); - assertThat(toolCallbackResolver.resolve("getForecast").getName()).isEqualTo("getForecast"); + assertThat(toolCallbackResolver.resolve("getForecast").getToolDefinition().name()) + .isEqualTo("getForecast"); assertThat(toolCallbackResolver.resolve("getAlert")).isNotNull(); - assertThat(toolCallbackResolver.resolve("getAlert").getName()).isEqualTo("getAlert"); + assertThat(toolCallbackResolver.resolve("getAlert").getToolDefinition().name()).isEqualTo("getAlert"); assertThat(toolCallbackResolver.resolve("weatherFunction1")).isNotNull(); - assertThat(toolCallbackResolver.resolve("weatherFunction1").getName()).isEqualTo("weatherFunction1"); + assertThat(toolCallbackResolver.resolve("weatherFunction1").getToolDefinition().name()) + .isEqualTo("weatherFunction1"); assertThat(toolCallbackResolver.resolve("getCurrentWeather3")).isNotNull(); - assertThat(toolCallbackResolver.resolve("getCurrentWeather3").getName()) + assertThat(toolCallbackResolver.resolve("getCurrentWeather3").getToolDefinition().name()) .isEqualTo("getCurrentWeather3"); assertThat(toolCallbackResolver.resolve("getCurrentWeather4")).isNotNull(); - assertThat(toolCallbackResolver.resolve("getCurrentWeather4").getName()) + assertThat(toolCallbackResolver.resolve("getCurrentWeather4").getToolDefinition().name()) .isEqualTo("getCurrentWeather4"); assertThat(toolCallbackResolver.resolve("getCurrentWeather5")).isNotNull(); - assertThat(toolCallbackResolver.resolve("getCurrentWeather5").getName()) + assertThat(toolCallbackResolver.resolve("getCurrentWeather5").getToolDefinition().name()) .isEqualTo("getCurrentWeather5"); }); } diff --git a/models/spring-ai-vertex-ai-gemini/src/test/java/org/springframework/ai/vertexai/gemini/tool/VertexAiGeminiPaymentTransactionMethodIT.java b/models/spring-ai-vertex-ai-gemini/src/test/java/org/springframework/ai/vertexai/gemini/tool/VertexAiGeminiPaymentTransactionMethodIT.java index 2cb65c80f0f..cc12dfe8b94 100644 --- a/models/spring-ai-vertex-ai-gemini/src/test/java/org/springframework/ai/vertexai/gemini/tool/VertexAiGeminiPaymentTransactionMethodIT.java +++ b/models/spring-ai-vertex-ai-gemini/src/test/java/org/springframework/ai/vertexai/gemini/tool/VertexAiGeminiPaymentTransactionMethodIT.java @@ -223,10 +223,10 @@ ToolCallingManager toolCallingManager(GenericApplicationContext applicationConte List tcps, List toolCallbacks, ObjectProvider observationRegistry) { - List allFunctionCallbacks = new ArrayList(toolCallbacks); - tcps.stream().map(pr -> List.of(pr.getToolCallbacks())).forEach(allFunctionCallbacks::addAll); + List allToolCallbacks = new ArrayList(toolCallbacks); + tcps.stream().map(pr -> List.of(pr.getToolCallbacks())).forEach(allToolCallbacks::addAll); - var staticToolCallbackResolver = new StaticToolCallbackResolver(allFunctionCallbacks); + var staticToolCallbackResolver = new StaticToolCallbackResolver(allToolCallbacks); var springBeanToolCallbackResolver = SpringBeanToolCallbackResolver.builder() .applicationContext(applicationContext) 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 da1f9e4ee4d..a175a3058fa 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 @@ -20,9 +20,7 @@ import org.junit.jupiter.api.Test; -import org.springframework.ai.chat.prompt.ChatOptions; import org.springframework.ai.chat.prompt.Prompt; -import org.springframework.ai.model.function.FunctionCallback; import org.springframework.ai.tool.function.FunctionToolCallback; import org.springframework.ai.zhipuai.api.MockWeatherService; import org.springframework.ai.zhipuai.api.ZhiPuAiApi; 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 4baed31e5dd..34dc56a3af4 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 @@ -48,7 +48,6 @@ import org.springframework.ai.converter.BeanOutputConverter; import org.springframework.ai.converter.ListOutputConverter; import org.springframework.ai.converter.MapOutputConverter; -import org.springframework.ai.model.function.FunctionCallback; import org.springframework.ai.tool.function.FunctionToolCallback; import org.springframework.ai.zhipuai.ZhiPuAiChatOptions; import org.springframework.ai.zhipuai.ZhiPuAiTestConfiguration; diff --git a/spring-ai-client-chat/src/test/java/org/springframework/ai/chat/client/ChatClientTest.java b/spring-ai-client-chat/src/test/java/org/springframework/ai/chat/client/ChatClientTest.java index 352a7bdece3..a572fc20c18 100644 --- a/spring-ai-client-chat/src/test/java/org/springframework/ai/chat/client/ChatClientTest.java +++ b/spring-ai-client-chat/src/test/java/org/springframework/ai/chat/client/ChatClientTest.java @@ -248,7 +248,7 @@ void mutateDefaults() { var fco = (ToolCallingChatOptions) prompt.getOptions(); assertThat(fco.getToolNames()).containsExactlyInAnyOrder("fun1", "fun2"); - assertThat(fco.getToolCallbacks().iterator().next().getName()).isEqualTo("fun3"); + assertThat(fco.getToolCallbacks().iterator().next().getToolDefinition().name()).isEqualTo("fun3"); // Streaming content = join(chatClient.prompt().stream().content()); @@ -270,7 +270,7 @@ void mutateDefaults() { fco = (ToolCallingChatOptions) prompt.getOptions(); assertThat(fco.getToolNames()).containsExactlyInAnyOrder("fun1", "fun2"); - assertThat(fco.getToolCallbacks().iterator().next().getName()).isEqualTo("fun3"); + assertThat(fco.getToolCallbacks().iterator().next().getToolDefinition().name()).isEqualTo("fun3"); // mutate builder // @formatter:off @@ -300,7 +300,7 @@ void mutateDefaults() { fco = (ToolCallingChatOptions) prompt.getOptions(); assertThat(fco.getToolNames()).containsExactlyInAnyOrder("fun1", "fun2", "fun4"); - assertThat(fco.getToolCallbacks().iterator().next().getName()).isEqualTo("fun3"); + assertThat(fco.getToolCallbacks().iterator().next().getToolDefinition().name()).isEqualTo("fun3"); // Streaming content = join(chatClient.prompt().stream().content()); @@ -322,7 +322,7 @@ void mutateDefaults() { fco = (ToolCallingChatOptions) prompt.getOptions(); assertThat(fco.getToolNames()).containsExactlyInAnyOrder("fun1", "fun2", "fun4"); - assertThat(fco.getToolCallbacks().iterator().next().getName()).isEqualTo("fun3"); + assertThat(fco.getToolCallbacks().iterator().next().getToolDefinition().name()).isEqualTo("fun3"); } @@ -385,7 +385,7 @@ void mutatePrompt() { var tco = (ToolCallingChatOptions) prompt.getOptions(); assertThat(tco.getToolNames()).containsExactlyInAnyOrder("fun1", "fun2", "fun5"); - assertThat(tco.getToolCallbacks().iterator().next().getName()).isEqualTo("fun3"); + assertThat(tco.getToolCallbacks().iterator().next().getToolDefinition().name()).isEqualTo("fun3"); // Streaming // @formatter:off @@ -416,7 +416,7 @@ void mutatePrompt() { var tcoptions = (ToolCallingChatOptions) prompt.getOptions(); assertThat(tcoptions.getToolNames()).containsExactlyInAnyOrder("fun1", "fun2", "fun5"); - assertThat(tcoptions.getToolCallbacks().iterator().next().getName()).isEqualTo("fun3"); + assertThat(tcoptions.getToolCallbacks().iterator().next().getToolDefinition().name()).isEqualTo("fun3"); } @Test diff --git a/spring-ai-client-chat/src/test/java/org/springframework/ai/chat/client/DefaultChatClientTests.java b/spring-ai-client-chat/src/test/java/org/springframework/ai/chat/client/DefaultChatClientTests.java index 3a8d269fd77..530d73f35bf 100644 --- a/spring-ai-client-chat/src/test/java/org/springframework/ai/chat/client/DefaultChatClientTests.java +++ b/spring-ai-client-chat/src/test/java/org/springframework/ai/chat/client/DefaultChatClientTests.java @@ -1490,8 +1490,6 @@ void whenToolCallbacksThenReturn() { assertThat(defaultSpec.getToolCallbacks()).contains(toolCallback); } - // FunctionCallback.builder().description("description").function(null,input->"hello").inputType(String.class).build() - @Test void whenFunctionNameIsNullThenThrow() { ChatClient chatClient = new DefaultChatClientBuilder(mock(ChatModel.class)).build(); @@ -1543,7 +1541,8 @@ void whenFunctionThenReturn() { .description("description") .build()); DefaultChatClient.DefaultChatClientRequestSpec defaultSpec = (DefaultChatClient.DefaultChatClientRequestSpec) spec; - assertThat(defaultSpec.getToolCallbacks()).anyMatch(callback -> callback.getName().equals("name")); + assertThat(defaultSpec.getToolCallbacks()) + .anyMatch(callback -> callback.getToolDefinition().name().equals("name")); } @Test @@ -1555,7 +1554,8 @@ void whenFunctionAndInputTypeThenReturn() { .description("description") .build()); DefaultChatClient.DefaultChatClientRequestSpec defaultSpec = (DefaultChatClient.DefaultChatClientRequestSpec) spec; - assertThat(defaultSpec.getToolCallbacks()).anyMatch(callback -> callback.getName().equals("name")); + assertThat(defaultSpec.getToolCallbacks()) + .anyMatch(callback -> callback.getToolDefinition().name().equals("name")); } @Test @@ -1609,7 +1609,8 @@ void whenBiFunctionThenReturn() { .inputType(String.class) .build()); DefaultChatClient.DefaultChatClientRequestSpec defaultSpec = (DefaultChatClient.DefaultChatClientRequestSpec) spec; - assertThat(defaultSpec.getToolCallbacks()).anyMatch(callback -> callback.getName().equals("name")); + assertThat(defaultSpec.getToolCallbacks()) + .anyMatch(callback -> callback.getToolDefinition().name().equals("name")); } @Test @@ -1631,7 +1632,7 @@ void whenFunctionBeanNamesThenReturn() { } @Test - void whenFunctionCallbacksElementIsNullThenThrow() { + void whenFunctionToolCallbacksElementIsNullThenThrow() { ChatClient chatClient = new DefaultChatClientBuilder(mock(ChatModel.class)).build(); ChatClient.ChatClientRequestSpec spec = chatClient.prompt(); assertThatThrownBy(() -> spec.tools(mock(FunctionToolCallback.class), null)) @@ -1640,7 +1641,7 @@ void whenFunctionCallbacksElementIsNullThenThrow() { } @Test - void whenFunctionCallbacksThenReturn() { + void whenFunctionToolCallbacksThenReturn() { ChatClient chatClient = new DefaultChatClientBuilder(mock(ChatModel.class)).build(); ChatClient.ChatClientRequestSpec spec = chatClient.prompt(); FunctionToolCallback functionToolCallback = mock(FunctionToolCallback.class); diff --git a/spring-ai-client-chat/src/test/java/org/springframework/ai/chat/client/advisor/api/AdvisedRequestTests.java b/spring-ai-client-chat/src/test/java/org/springframework/ai/chat/client/advisor/api/AdvisedRequestTests.java index 0cb3fc93ff7..f63cf6bcd71 100644 --- a/spring-ai-client-chat/src/test/java/org/springframework/ai/chat/client/advisor/api/AdvisedRequestTests.java +++ b/spring-ai-client-chat/src/test/java/org/springframework/ai/chat/client/advisor/api/AdvisedRequestTests.java @@ -93,7 +93,7 @@ void whenFunctionNamesIsNullThenThrows() { } @Test - void whenFunctionCallbacksIsNullThenThrows() { + void whenToolCallbacksIsNullThenThrows() { assertThatThrownBy(() -> new AdvisedRequest(mock(ChatModel.class), "user", null, null, List.of(), List.of(), null, List.of(), Map.of(), Map.of(), List.of(), Map.of(), Map.of(), Map.of())) .isInstanceOf(IllegalArgumentException.class) diff --git a/spring-ai-docs/src/main/antora/modules/ROOT/nav.adoc b/spring-ai-docs/src/main/antora/modules/ROOT/nav.adoc index 34d211039b1..f5f636a64fa 100644 --- a/spring-ai-docs/src/main/antora/modules/ROOT/nav.adoc +++ b/spring-ai-docs/src/main/antora/modules/ROOT/nav.adoc @@ -33,7 +33,6 @@ **** xref:api/chat/functions/openai-chat-functions.adoc[OpenAI Function Calling (Deprecated)] *** xref:api/chat/qianfan-chat.adoc[QianFan] *** xref:api/chat/zhipuai-chat.adoc[ZhiPu AI] -// **** xref:api/chat/functions/zhipuai-chat-functions.adoc[Function Calling] *** xref:api/chat/watsonx-ai-chat.adoc[watsonx.AI] ** xref:api/embeddings.adoc[Embedding Models] *** xref:api/bedrock.adoc[Amazon Bedrock] @@ -107,7 +106,6 @@ ** xref:api/prompt.adoc[] ** xref:api/chat/prompt-engineering-patterns.adoc[] * xref:api/testing.adoc[AI Model Evaluation] -* xref:api/functions.adoc[Function Calling (Deprecated)] * Service Connections 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 ef2410283e3..8fdeecedcd3 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 @@ -20,7 +20,7 @@ Your function can in turn invoke other 3rd party services to provide the results Spring AI makes this as easy as defining a `@Bean` definition that returns a `java.util.Function` and supplying the bean name as an option when invoking the `ChatModel`. Under the hood, Spring wraps your POJO (the function) with the appropriate adapter code that enables interaction with the AI Model, saving you from writing tedious boilerplate code. -The basis of the underlying infrastructure is the link:https://github.com/spring-projects/spring-ai/blob/main/spring-ai-model/src/main/java/org/springframework/ai/model/function/FunctionCallback.java[FunctionCallback.java] interface and the companion Builder utility class to simplify the implementation and registration of Java callback functions. +The basis of the underlying infrastructure is the link:https://github.com/spring-projects/spring-ai/blob/main/spring-ai-model/src/main/java/org/springframework/ai/tool/ToolCallback.java[ToolCallback.java] interface and the companion Builder utility class to simplify the implementation and registration of Java callback functions. == How it works @@ -72,7 +72,7 @@ We start with describing the most POJO friendly options. In this approach you define `@Beans` in your application context as you would any other Spring managed object. -Internally, Spring AI `ChatModel` will create an instance of a `FunctionCallback` that adds the logic for it being invoked via the AI model. +Internally, Spring AI `ChatModel` will create an instance of a `ToolCallback` that adds the logic for it being invoked via the AI model. The name of the `@Bean` is passed as a `ChatOption`. @@ -117,9 +117,9 @@ It is a best practice to annotate the request object with information such that The link:https://github.com/spring-projects/spring-ai/blob/main/auto-configurations/models/spring-ai-autoconfigure-model-anthropic/src/test/java/org/springframework/ai/model/anthropic/autoconfigure/tool/FunctionCallWithFunctionBeanIT.java[FunctionCallWithFunctionBeanIT.java] demonstrates this approach. -==== FunctionCallback +==== ToolCallback -Another way to register a function is to create a `FunctionCallback` instance like this: +Another way to register a function is to create a `ToolCallback` instance like this: [source,java] ---- 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 4f2e985e9be..9f99e2f8b17 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 @@ -19,7 +19,7 @@ Your function can in turn invoke other 3rd party services to provide the results Spring AI makes this as easy as defining a `@Bean` definition that returns a `java.util.Function` and supplying the bean name as an option when invoking the `ChatModel`. Under the hood, Spring wraps your POJO (the function) with the appropriate adapter code that enables interaction with the AI Model, saving you from writing tedious boilerplate code. -The basis of the underlying infrastructure is the link:https://github.com/spring-projects/spring-ai/blob/main/spring-ai-model/src/main/java/org/springframework/ai/model/function/FunctionCallback.java[FunctionCallback.java] interface and the companion Builder utility class to simplify the implementation and registration of Java callback functions. +The basis of the underlying infrastructure is the link:https://github.com/spring-projects/spring-ai/blob/main/spring-ai-model/src/main/java/org/springframework/ai/tool/ToolCallback.java[ToolCallback.java] interface and the companion Builder utility class to simplify the implementation and registration of Java callback functions. == How it works @@ -70,7 +70,7 @@ We start with describing the most POJO friendly options. In this approach you define `@Beans` in your application context as you would any other Spring managed object. -Internally, Spring AI `ChatModel` will create an instance of a `FunctionCallback` instance that adds the logic for it being invoked via the AI model. +Internally, Spring AI `ChatModel` will create an instance of a `ToolCallback` instance that adds the logic for it being invoked via the AI model. The name of the `@Bean` is passed as a `ChatOption`. @@ -113,9 +113,9 @@ It is a best practice to annotate the request object with information such that The link:https://github.com/spring-projects/spring-ai/blob/main/auto-configurations/models/spring-ai-autoconfigure-model-anthropic/src/test/java/org/springframework/ai/model/azure/openai/autoconfigure/tool/FunctionCallWithFunctionBeanIT.java[FunctionCallWithFunctionBeanIT.java] demonstrates this approach. -==== FunctionCallback Wrapper +==== ToolCallback Wrapper -Another way to register a function is to create a `FunctionCallback` instance like this: +Another way to register a function is to create a `ToolCallback` instance like this: [source,java] ---- 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 cbb074f95e3..39020c09536 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 @@ -14,7 +14,7 @@ As a developer, you need to implement a function that takes the function call ar Spring AI makes this as easy as defining a `@Bean` definition that returns a `java.util.Function` and supplying the bean name as an option when invoking the `ChatModel`. Under the hood, Spring wraps your POJO (the function) with the appropriate adapter code that enables interaction with the AI Model, saving you from writing tedious boilerplate code. -The basis of the underlying infrastructure is the link:https://github.com/spring-projects/spring-ai/blob/main/spring-ai-model/src/main/java/org/springframework/ai/model/function/FunctionCallback.java[FunctionCallback.java] interface and the companion Builder utility class to simplify the implementation and registration of Java callback functions. +The basis of the underlying infrastructure is the link:https://github.com/spring-projects/spring-ai/blob/main/spring-ai-model/src/main/java/org/springframework/ai/tool/ToolCallback.java[ToolCallback.java] interface and the companion Builder utility class to simplify the implementation and registration of Java callback functions. // Additionally, the Auto-Configuration provides a way to auto-register any Function beans definition as function calling candidates in the `ChatModel`. @@ -71,7 +71,7 @@ We start with describing the most POJO friendly options. In this approach you define `@Beans` in your application context as you would any other Spring managed object. -Internally, Spring AI `ChatModel` will create an instance of a `FunctionCallback` instance that adds the logic for it being invoked via the AI model. +Internally, Spring AI `ChatModel` will create an instance of a `ToolCallback` instance that adds the logic for it being invoked via the AI model. The name of the `@Bean` is passed as a `ChatOption`. @@ -115,9 +115,9 @@ It is a best practice to annotate the request object with information such that The link:https://github.com/spring-projects/spring-ai/blob/main/auto-configurations/models/spring-ai-autoconfigure-model-minimax/src/test/java/org/springframework/ai/model/minimax/autoconfigure/FunctionCallbackWithPlainFunctionBeanIT.java[FunctionCallbackWithPlainFunctionBeanIT.java] demonstrates this approach. -==== FunctionCallback Wrapper +==== ToolCallback Wrapper -Another way register a function is to create `FunctionCallback` instance like this: +Another way register a function is to create `ToolCallback` instance like this: [source,java] ---- @@ -125,9 +125,9 @@ Another way register a function is to create `FunctionCallback` instance like th static class Config { @Bean - public FunctionCallback weatherFunctionInfo() { + public FunctionToolCallback weatherFunctionInfo() { - return FunctionCallback.builder() + return FunctionToolCallback.builder() .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 @@ -142,7 +142,7 @@ It also provides a description (2) and the function signature (3) to let the mod NOTE: By default, the response converter does a JSON serialization of the Response object. -NOTE: The `FunctionCallback` internally resolves the function call signature based on the `MockWeatherService.Request` class. +NOTE: The `FunctionToolCallback` internally resolves the function call signature based on the `MockWeatherService.Request` class. === Specifying functions in Chat Options @@ -185,8 +185,7 @@ MiniMaxChatModel chatModel = ... UserMessage userMessage = new UserMessage("What's the weather like in San Francisco, Tokyo, and Paris?"); var promptOptions = MiniMaxChatOptions.builder() - .functionCallbacks(List.of(FunctionCallback.builder() - .function("CurrentWeather", new MockWeatherService()) // (1) function name and instance + .toolCallbacks(List.of(FunctionToolCallback.builder("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 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 f8d1356f579..d51566f5192 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 @@ -18,7 +18,7 @@ Your function can in turn invoke other 3rd party services to provide the results Spring AI makes this as easy as defining a `@Bean` definition that returns a `java.util.Function` and supplying the bean name as an option when invoking the `ChatModel`. Under the hood, Spring wraps your POJO (the function) with the appropriate adapter code that enables interaction with the AI Model, saving you from writing tedious boilerplate code. -The basis of the underlying infrastructure is the link:https://github.com/spring-projects/spring-ai/blob/main/spring-ai-model/src/main/java/org/springframework/ai/model/function/FunctionCallback.java[FunctionCallback.java] interface and the companion Builder utility class to simplify the implementation and registration of Java callback functions. +The basis of the underlying infrastructure is the link:https://github.com/spring-projects/spring-ai/blob/main/spring-ai-model/src/main/java/org/springframework/ai/tool/ToolCallback.java[ToolCallback.java] interface and the companion Builder utility class to simplify the implementation and registration of Java callback functions. == How it works @@ -70,7 +70,7 @@ We start by describing the most POJO-friendly options. In this approach, you define a `@Bean` in your application context as you would any other Spring managed object. -Internally, Spring AI `ChatModel` will create an instance of a `FunctionCallback` that adds the logic for it being invoked via the AI model. +Internally, Spring AI `ChatModel` will create an instance of a `ToolCallback` that adds the logic for it being invoked via the AI model. The name of the `@Bean` is passed as a `ChatOption`. [source,java] @@ -115,7 +115,7 @@ The link:https://github.com/spring-projects/spring-ai/blob/main/auto-configurati TIP: The Mistral AI link:https://github.com/spring-projects/spring-ai/blob/main/auto-configurations/models/spring-ai-autoconfigure-model-mistral-ai/src/test/java/org/springframework/ai/model/mistralai/autoconfigure/tool/PaymentStatusBeanOpenAiIT.java[PaymentStatusBeanOpenAiIT] implements the same function using the OpenAI API. Mistral AI is almost identical to OpenAI in this regard. -==== FunctionCallback Wrapper +==== ToolCallback Wrapper Another way to register a function is to create a `FunctionToolCallback` like this: @@ -141,7 +141,7 @@ It also provides a description (2) and the function signature (3) to let the mod NOTE: By default, the response converter performs a JSON serialization of the Response object. -NOTE: The `FunctionCallback` internally resolves the function call signature based on the `MockWeatherService.Request` class. +NOTE: The `FunctionToolCallback` internally resolves the function call signature based on the `MockWeatherService.Request` class. === Specifying functions in Chat Options 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 131cadf5e49..8f11cf7a1fb 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 @@ -23,7 +23,7 @@ Your function can in turn invoke other 3rd party services to provide the results Spring AI makes this as easy as defining a `@Bean` definition that returns a `java.util.Function` and supplying the bean name as an option when invoking the `ChatModel`. Under the hood, Spring wraps your POJO (the function) with the appropriate adapter code that enables interaction with the AI Model, saving you from writing tedious boilerplate code. -The basis of the underlying infrastructure is the link:https://github.com/spring-projects/spring-ai/blob/main/spring-ai-model/src/main/java/org/springframework/ai/model/function/FunctionCallback.java[FunctionCallback.java] interface and the companion Builder utility class to simplify the implementation and registration of Java callback functions. +The basis of the underlying infrastructure is the link:https://github.com/spring-projects/spring-ai/blob/main/spring-ai-model/src/main/java/org/springframework/ai/tool/ToolCallback.java[ToolCallback.java] interface and the companion Builder utility class to simplify the implementation and registration of Java callback functions. == How it works @@ -78,7 +78,7 @@ We start by describing the most POJO-friendly options. In this approach, you define a `@Bean` in your application context as you would any other Spring managed object. -Internally, Spring AI `ChatModel` will create an instance of a `FunctionCallback` that adds the logic for it being invoked via the AI model. +Internally, Spring AI `ChatModel` will create an instance of a `ToolCallback` that adds the logic for it being invoked via the AI model. The name of the `@Bean` is passed as a `ChatOption`. [source,java] @@ -117,9 +117,9 @@ public record Request(String location, Unit unit) {} It is a best practice to annotate the request object with information such that the generated JSON schema of that function is as descriptive as possible to help the AI model pick the correct function to invoke. -==== FunctionCallback +==== ToolCallback -Another way to register a function is to create a `FunctionCallback` like this: +Another way to register a function is to create a `ToolCallback` like this: [source,java] ---- 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 efc8f256bea..7beee879308 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 @@ -16,7 +16,7 @@ As a developer, you need to implement a function that takes the function call ar Spring AI makes this as easy as defining a `@Bean` definition that returns a `java.util.Function` and supplying the bean name as an option when invoking the `ChatModel`. Under the hood, Spring wraps your POJO (the function) with the appropriate adapter code that enables interaction with the AI Model, saving you from writing tedious boilerplate code. -The basis of the underlying infrastructure is the link:https://github.com/spring-projects/spring-ai/blob/main/spring-ai-model/src/main/java/org/springframework/ai/model/function/FunctionCallback.java[FunctionCallback.java] interface and the companion Builder utility class to simplify the implementation and registration of Java callback functions. +The basis of the underlying infrastructure is the link:https://github.com/spring-projects/spring-ai/blob/main/spring-ai-model/src/main/java/org/springframework/ai/tool/ToolCallback.java[ToolCallback.java] interface and the companion Builder utility class to simplify the implementation and registration of Java callback functions. // Additionally, the Auto-Configuration provides a way to auto-register any Function beans definition as function calling candidates in the `ChatModel`. @@ -71,7 +71,7 @@ We start by describing the most POJO-friendly options. In this approach, you define a `@Bean` in your application context as you would any other Spring managed object. -Internally, Spring AI `ChatModel` will create an instance of a `FunctionCallback` that adds the logic for it being invoked via the AI model. +Internally, Spring AI `ChatModel` will create an instance of a `ToolCallback` that adds the logic for it being invoked via the AI model. The name of the `@Bean` is passed as a `ChatOption`. [source,java] @@ -112,9 +112,9 @@ It is a best practice to annotate the request object with information such that The link:https://github.com/spring-projects/spring-ai/blob/main/auto-configurations/models/spring-ai-autoconfigure-model-openai/src/test/java/org/springframework/ai/model/openai/autoconfigure/tool/FunctionCallbackWithPlainFunctionBeanIT.java[FunctionCallbackWithPlainFunctionBeanIT.java] demonstrates this approach. -==== FunctionCallback Wrapper +==== FunctionToolCallback Wrapper -Another way to register a function is to create a `FunctionCallback` like this: +Another way to register a function is to create a `toolCallback` like this: [source,java] ---- 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 d3ba29c3009..61dec956903 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 @@ -14,7 +14,7 @@ As a developer, you need to implement a function that takes the function call ar Spring AI makes this as easy as defining a `@Bean` definition that returns a `java.util.Function` and supplying the bean name as an option when invoking the `ChatModel`. Under the hood, Spring wraps your POJO (the function) with the appropriate adapter code that enables interaction with the AI Model, saving you from writing tedious boilerplate code. -The basis of the underlying infrastructure is the link:https://github.com/spring-projects/spring-ai/blob/main/spring-ai-model/src/main/java/org/springframework/ai/model/function/FunctionCallback.java[FunctionCallback.java] interface and the companion Builder utility class to simplify the implementation and registration of Java callback functions. +The basis of the underlying infrastructure is the link:https://github.com/spring-projects/spring-ai/blob/main/spring-ai-model/src/main/java/org/springframework/ai/tool/ToolCallback.java[ToolCallback.java] interface and the companion Builder utility class to simplify the implementation and registration of Java callback functions. // Additionally, the Auto-Configuration provides a way to auto-register any Function beans definition as function calling candidates in the `ChatModel`. @@ -71,7 +71,7 @@ We start with describing the most POJO friendly options. In this approach you define `@Beans` in your application context as you would any other Spring managed object. -Internally, Spring AI `ChatModel` will create an instance of a `FunctionCallback` instance that adds the logic for it being invoked via the AI model. +Internally, Spring AI `ChatModel` will create an instance of a `ToolCallback` instance that adds the logic for it being invoked via the AI model. The name of the `@Bean` is passed as a `ChatOption`. @@ -115,9 +115,9 @@ It is a best practice to annotate the request object with information such that The link:https://github.com/spring-projects/spring-ai/blob/main/auto-configurations/models/spring-ai-autoconfigure-model-zhipuai/src/test/java/org/springframework/ai/model/zhipuai/autoconfigure/tool/FunctionCallbackWithPlainFunctionBeanIT.java[FunctionCallbackWithPlainFunctionBeanIT.java] demonstrates this approach. -==== FunctionCallback Wrapper +==== FunctionToolCallback Wrapper -Another way register a function is to create `FunctionCallback` instance like this: +Another way register a function is to create `ToolCallback` instance like this: [source,java] ---- @@ -125,10 +125,9 @@ Another way register a function is to create `FunctionCallback` instance like th static class Config { @Bean - public FunctionCallback weatherFunctionInfo() { + public FunctionToolCallback weatherFunctionInfo() { - return FunctionCallback.builder() - .function("CurrentWeather", new MockWeatherService()) // (1) function name and instance + return FunctionToolCallback.builder("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(); @@ -142,7 +141,7 @@ It also provides a description (2) and the input type (3) used to generate the J NOTE: By default, the response converter does a JSON serialization of the Response object. -NOTE: The `FunctionCallback` internally resolves the function call signature based on the `MockWeatherService.Request` class. +NOTE: The `FunctionToolCallback` internally resolves the function call signature based on the `MockWeatherService.Request` class. === Specifying functions in Chat Options @@ -185,8 +184,7 @@ ZhiPuAiChatModel chatModel = ... UserMessage userMessage = new UserMessage("What's the weather like in San Francisco, Tokyo, and Paris?"); var promptOptions = ZhiPuAiChatOptions.builder() - .functionCallbacks(List.of(FunctionCallback.builder() - .function("CurrentWeather", new MockWeatherService()) // (1) function name and instance + .toolCallbacks(List.of(FunctionToolCallback.builder("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 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 deleted file mode 100644 index c89144fd378..00000000000 --- a/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/function-callback.adoc +++ /dev/null @@ -1,258 +0,0 @@ -= FunctionCallback - -WARNING: This page describes the previous version of the Function Calling API, which has been deprecated and marked for remove in the next release. The current version is available at xref:api/tools.adoc[Tool Calling]. See the xref:api/tools-migration.adoc[Migration Guide] for more information. - -The `FunctionCallback` interface in Spring AI provides a standardized way to implement Large Language Model (LLM) function calling capabilities. It allows developers to register custom functions that can be called by AI models when specific conditions or intents are detected in the prompts. - -The `FunctionCallback` interface defines several key methods: - -* `getName()`: Returns the unique function name within the AI model context -* `getDescription()`: Provides a description that helps the model decide when to invoke the function -* `getInputTypeSchema()`: Defines the JSON schema for the function's input parameters -* `call(String functionArguments)`: Handles the actual function execution -* `call(String functionArguments, ToolContext toolContext)`: Extended version that supports additional xref:api/functions.adoc#Tool-Context[tool context] - -== Builder Pattern - -Spring AI provides a fluent builder API for creating `FunctionCallback` implementations. -This is particularly useful for defining function callbacks that you can register, pragmatically, on the fly, with your `ChatClient` or `ChatModel` model calls. - -Use the `FunctionCallback.builder()` method to create a new builder instance and chain the configuration methods to set the function name, description, input type, and other properties. -The `FunctionCallback.Builder` is a hierarchical with the following structure: - -- FunctionCallback.Builder - The root builder interface used for configuring the xref:_common_configurations[shared properties]. -- FunctionInvokingSpec - The xref:Function-Invoking[function invoking] builder interface. -- MethodInvokingSpec - The xref:Method-Invoking[method invoking] builder interface. - -[[Function-Invoking]] -== Function-Invoking Approach - -Converts any `java.util.function.Function`, `BiFunction`, `Supplier` or `Consumer` into a `FunctionCallback` that can be called by the AI model. - -NOTE: You can use lambda expressions or method references to define the function logic but you must provide the input type of the function using the `inputType(TYPE)`. - -=== Function - -[source,java] ----- -FunctionCallback callback = FunctionCallback.builder() - .function("processOrder", (Order order) -> processOrderLogic(order)) - .description("Process a new order") - .inputType(Order.class) - .build(); ----- - -=== BiFunction - -Using Function with input type and additional xref:api/functions.adoc#Tool-Context[ToolContext] parameter: - -[source,java] ----- -FunctionCallback callback = FunctionCallback.builder() - .function("processOrder", (Order order, ToolContext context) -> - processOrderWithContext(order, context)) - .description("Process a new order with context") - .inputType(Order.class) - .build(); ----- - -=== Supplier - -Use `java.util.Supplier` or `java.util.function.Function` to define functions that don't take any input: - -[source,java] ----- -FunctionCallback.builder() - .function("turnsLight", () -> state.put("Light", "ON")) - .description("Turns light on in the living room") - .inputType(Void.class) - .build(); ----- - -=== Consumer - -Use `java.util.Consumer` or `java.util.function.Function` to define functions that don't produce output: - -[source,java] ----- -record LightInfo(String roomName, boolean isOn) {} - -FunctionCallback.builder() - .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(); ----- - -=== Generics Input Type - -Use the `ParameterizedTypeReference` to define functions with generic input types: - -[source,java] ----- -record TrainSearchRequest(T data) {} - -record TrainSearchSchedule(String from, String to, String date) {} - -record TrainSearchScheduleResponse(String from, String to, String date, String trainNumber) {} - -FunctionCallback.builder() - .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(); ----- - -[[Method-Invoking]] -== Method Invoking Approach - -Enables method invocation through reflection while automatically handling JSON schema generation and parameter conversion. It’s particularly useful for integrating Java methods as callable functions within AI model interactions. - -The method invoking implements the `FunctionCallback` interface and provides: - -- Automatic JSON schema generation for method parameters -- Support for both static and instance methods -- Any number of parameters (including none) and return values (including void) -- Any parameter/return types (primitives, objects, collections) -- Special handling for xref:api/functions.adoc#Tool-Context[ToolContext] parameters - -=== Static Method Invocation - -You can refer to a static method in a class by providing the method name, parameter types, and the target class. - -[source,java] ----- -public class WeatherService { - public static String getWeather(String city, TemperatureUnit unit) { - return "Temperature in " + city + ": 20" + unit; - } -} - -FunctionCallback callback = FunctionCallback.builder() - .method("getWeather", String.class, TemperatureUnit.class) - .description("Get weather information for a city") - .targetClass(WeatherService.class) - .build(); ----- - -=== Object instance Method Invocation - -You can refer to an instance method in a class by providing the method name, parameter types, and the target object instance. - -[source,java] ----- -public class DeviceController { - public void setDeviceState(String deviceId, boolean state, ToolContext context) { - Map contextData = context.getContext(); - // Implementation using context data - } -} - -DeviceController controller = new DeviceController(); - -String response = ChatClient.create(chatModel).prompt() - .user("Turn on the living room lights") - .functions(FunctionCallback.builder() - .method("setDeviceState", String.class,boolean.class,ToolContext.class) - .description("Control device state") - .targetObject(controller) - .build()) - .toolContext(Map.of("location", "home")) - .call() - .content(); ----- - -TIP: Optionally, using the `.name()`, you can set a custom function name different from the method name. - -== Common Configurations - -There are several common configurations that you can use to customize the function callbacks. - -=== Schema Type - -The framework supports different schema types for generating the schemas for the input parameters: - -* JSON Schema (default) -* OpenAPI Schema (for Vertex AI compatibility) - -[source,java] ----- -FunctionCallback.builder() - .schemaType(SchemaType.OPEN_API_SCHEMA) - // ... other configuration - .build(); ----- - -=== Custom Response Handling - -You can provide a custom response converter to format the function response before sending it back to the AI model. -Most AI Models expect an text response, so it is your responsibility to convert the function response to a text format. -By default, the response is converted to a String. - -TIP: Many models cope well with JSON responses, so you can return a JSON string. - -[source,java] ----- -FunctionCallback.builder() - .responseConverter(response -> - customResponseFormatter.format(response)) - // ... other configuration - .build(); ----- - -=== Custom Object Mapping - -Spring AI uses ObjectMapper for JSON serialization and deserialization. -You can provide a custom ObjectMapper to handle custom object mapping: - -[source,java] ----- -FunctionCallback.builder() - .objectMapper(customObjectMapper) - // ... other configuration - .build(); ----- - -== Best Practices - -=== Descriptive Names and Descriptions - -* Provide unique function names -* Write comprehensive descriptions to help the model understand when to invoke the function - -=== Input Type & Schema - -* For the function invoking approach, define input types explicitly and use `ParameterizedTypeReference` for generic types. -* Consider using custom schema when auto-generated ones don't meet requirements. - -=== Error Handling - -* Implement proper error handling in function implementations and return the error message in the response -* You can use the ToolContext to provide additional error context when needed - -=== Tool Context Usage - -* Use xref:api/functions.adoc#Tool-Context[ToolContext] when additional state or context is required that is provided from the User and not part of the function input generated by the AI model. -* Use `BiFunction` to access the ToolContext in the function invocation approach and add `ToolContext` parameter in the method invoking approach. - -=== Notes on Schema Generation - -* The framework automatically generates JSON schemas from Java types -* For function invoking, the schema is generated based on the input type for the function that needs to be set using `inputType(TYPE)`. Use `ParameterizedTypeReference` for generic types. -* Generated schemas respect Jackson annotations on model classes -* You can bypass the automatic generation by providing custom schemas using `inputTypeSchema()` - -=== Common Pitfalls to Avoid - -==== Lack of Description -* Always provide explicit descriptions instead of relying on auto-generated ones -* Clear descriptions improve model's function selection accuracy - -==== Schema Mismatches -* Ensure input types match the Function's input parameter types. -* Use `ParameterizedTypeReference` for generic types. 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 deleted file mode 100644 index 5de11ccda3b..00000000000 --- a/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/functions.adoc +++ /dev/null @@ -1,470 +0,0 @@ -[[Function]] -= Function Calling API - -WARNING: This page describes the previous version of the Function Calling API, which has been deprecated and marked for remove in the next release. The current version is available at xref:api/tools.adoc[Tool Calling]. See the xref:api/tools-migration.adoc[Migration Guide] for more information. - -The integration of function support in AI models, permits the model to request the execution of client-side functions, thereby accessing necessary information or performing tasks dynamically as required. - -Spring AI currently supports tool/function calling for the following AI Models: - -* Anthropic Claude: Refer to the xref:api/chat/functions/anthropic-chat-functions.adoc[Anthropic Claude tool/function calling]. -* Azure OpenAI: Refer to the xref:api/chat/functions/azure-open-ai-chat-functions.adoc[Azure OpenAI tool/function calling]. -* Amazon Bedrock Converse: Refer to the xref:api/chat/bedrock-converse.adoc#_tool_calling[Amazon Bedrock Converse tool/function calling]. -* Google VertexAI Gemini: Refer to the xref:api/chat/vertexai-gemini-chat.adoc#_tool_calling[Vertex AI Gemini tool/function calling]. -* Groq: Refer to the xref:api/chat/groq-chat.adoc#_function_calling[Groq tool/function calling]. -* Mistral AI: Refer to the xref:api/chat/functions/mistralai-chat-functions.adoc[Mistral AI tool/function calling]. -// * MiniMax : Refer to the xref:api/chat/functions/minimax-chat-functions.adoc[MiniMax function invocation docs]. -* Ollama: Refer to the xref:api/chat/functions/ollama-chat-functions.adoc[Ollama tool/function calling] -* OpenAI: Refer to the xref:api/chat/functions/openai-chat-functions.adoc[OpenAI tool/function calling]. -// * ZhiPu AI : Refer to the xref:api/chat/functions/zhipuai-chat-functions.adoc[ZhiPu AI function invocation docs]. - -image::function-calling-basic-flow.jpg[Function calling, width=700, align="center"] - -You can register custom Java functions with the `ChatClient` and have the AI model intelligently choose to output a JSON object containing arguments to call one or many of the registered functions. -This allows you to connect the LLM capabilities with external tools and APIs. -The AI models are trained to detect when a function should be called and to respond with JSON that adheres to the function signature. - -The API does not call the function directly; instead, the model generates JSON that you can use to call the function in your code and return the result back to the model to complete the conversation. - -Spring AI provides flexible and user-friendly ways to register and call custom functions. -In general, the custom functions need to provide a function `name`, `description`, and the function call `signature` (as JSON schema) to let the model know what arguments the function expects. The `description` helps the model to understand when to call the function. - -As a developer, you need to implement a function that takes the function call arguments sent from the AI model, and responds with the result back to the model. Your function can in turn invoke other 3rd party services to provide the results. - -Spring AI makes this as easy as defining a `@Bean` definition that returns a `java.util.Function` and supplying the bean name as an option when invoking the `ChatClient` or registering the function dynamically in your prompt request. - -Under the hood, Spring wraps your POJO (the function) with the appropriate adapter code that enables interaction with the AI Model, saving you from writing tedious boilerplate code. -The basis of the underlying infrastructure is the link:https://github.com/spring-projects/spring-ai/blob/main/spring-ai-core/src/main/java/org/springframework/ai/tool/function/FunctionToolCallback.java[FunctionToolCallback.java] interface and the companion Builder utility class to simplify the implementation and registration of Java callback functions. - -== How it works - -Suppose we want the AI model to respond with information that it does not have, for example, the current temperature at a given location. - -We can provide the AI model with metadata about our own functions that it can use to retrieve that information as it processes your prompt. - -For example, if during the processing of a prompt, the AI Model determines that it needs additional information about the temperature in a given location, it will start a server-side generated request/response interaction. -Instead of returning the final response message, the AI Model returns at special Tool Call request, providing the function name and arguments (as JSON). -It is the responsibility of the client to process this message and execute the named function and return the response -as Tool Response message back to the AI Model. - -Spring AI greatly simplifies the code you need to write to support function invocation. -It brokers the function invocation conversation for you. -You can simply provide your function definition as a `@Bean` and then provide the bean name of the function in your prompt options or pass the function directly as a parameter in your prompt request options. - -You can also reference multiple function bean names in your prompt. - -=== Example Use Case - -Lets define a simple use case that we can use as an example to explain how function invocation works. -Let's create a chatbot that answer questions by calling our own function. -To support the response of the chatbot, we will register our own function that takes a location and returns the current weather in that location. - -When the model needs to answer a question such as `"What’s the weather like in Boston?"` the AI model will invoke the client providing the location value as an argument to be passed to the function. This RPC-like data is passed as JSON. - -Our function calls some SaaS-based weather service API and returns the weather response back to the model to complete the conversation. In this example, we will use a simple implementation named `MockWeatherService` that hard-codes the temperature for various locations. - -The following `MockWeatherService` class represents the weather service API: - --- -[tabs] -====== -Java:: -+ -[source,java] ----- -public class MockWeatherService implements Function { - - public enum Unit { C, F } - public record Request(String location, Unit unit) {} - public record Response(double temp, Unit unit) {} - - public Response apply(Request request) { - return new Response(30.0, Unit.C); - } -} ----- -Kotlin:: -+ -[source,kotlin] ----- -class MockWeatherService : Function1 { - override fun invoke(request: Request) = Response(30.0, Unit.C) -} - -enum class Unit { C, F } -data class Request(val location: String, val unit: Unit) {} -data class Response(val temp: Double, val unit: Unit) {} ----- -====== --- - -== Server-Side Registration - -=== Functions as Beans - -Spring AI provides multiple ways to register custom functions as beans in the Spring context. - -We start by describing the most POJO-friendly options. - -==== Plain Functions - -In this approach, you define a `@Bean` in your application context as you would any other Spring managed object. - -Internally, Spring AI `ChatModel` will create an instance of a `FunctionToolCallback` that adds the logic for it being invoked via the AI model. -The name of the `@Bean` is used function name. - --- -[tabs] -====== -Java:: -+ -[source,java] ----- -@Configuration -static class Config { - - @Bean - @Description("Get the weather in location") // function description - public Function currentWeather() { - return new MockWeatherService(); - } - -} ----- -Kotlin:: -+ -[source,kotlin] ----- -@Configuration -class Config { - - @Bean - @Description("Get the weather in location") // function description - fun currentWeather(): (Request) -> Response = MockWeatherService() - -} ----- -====== --- - -The `@Description` annotation is optional and provides a function description that helps the model understand when to call the function. It is an important property to set to help the AI model determine what client side function to invoke. - -Another option for providing the description of the function is to use the `@JsonClassDescription` annotation on the `MockWeatherService.Request`: - --- -[tabs] -====== -Java:: -+ -[source,java] ----- -@Configuration -static class Config { - - @Bean - public Function currentWeather() { // bean name as function name - return new MockWeatherService(); - } -} - -@JsonClassDescription("Get the weather in location") // function description -public record Request(String location, Unit unit) {} ----- -Kotlin:: -+ -[source,kotlin] ----- -@Configuration -class Config { - - @Bean - fun currentWeather(): (Request) -> Response { // bean name as function name - return MockWeatherService() - } -} - -@JsonClassDescription("Get the weather in location") // function description -data class Request(val location: String, val unit: Unit) ----- -====== --- - -It is a best practice to annotate the request object with information such that the generated JSON schema of that function is as descriptive as possible to help the AI model pick the correct function to invoke. - -==== FunctionToolCallback - -Another way to register a function is to create a `FunctionToolCallback` like this: - --- -[tabs] -====== -Java:: -+ -[source,java] ----- -@Configuration -static class Config { - - @Bean - public FunctionToolCallback weatherFunctionInfo() { - - return FunctionToolCallback.builder("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(); - } -} ----- -Kotlin:: -+ -[source,kotlin] ----- -import org.springframework.ai.model.function.withInputType - -@Configuration -class Config { - - @Bean - fun weatherFunctionInfo(): FunctionToolCallback { - - return FunctionToolCallback.builder("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(); - } -} - ----- -====== --- - -It wraps the 3rd party `MockWeatherService` function and registers it as a `CurrentWeather` function with the `ChatClient`. -It also provides a description (2) and an optional response converter to convert the response into a text as expected by the model. - -NOTE: By default, the response converter performs a JSON serialization of the Response object. - -NOTE: The `FunctionToolCallback.Builder` internally resolves the function call signature based on the `MockWeatherService.Request` class. - -=== Enable functions by bean name - -To let the model know and call your `CurrentWeather` function you need to enable it in your prompt requests: - -[source,java] ----- -ChatClient chatClient = ... - -ChatResponse response = this.chatClient.prompt("What's the weather like in San Francisco, Tokyo, and Paris?") - .tools("CurrentWeather") // Enable the function - .call(). - chatResponse(); - -logger.info("Response: {}", response); ----- - -The above user question will trigger 3 calls to the `CurrentWeather` function (one for each city) and the final response will be something like this: - ----- -Here is the current weather for the requested cities: -- San Francisco, CA: 30.0°C -- Tokyo, Japan: 10.0°C -- Paris, France: 15.0°C ----- - -The link:https://github.com/spring-projects/spring-ai/blob/main/auto-configurations/models/spring-ai-autoconfigure-model-openai/src/test/java/org/springframework/ai/model/openai/autoconfigure/tool/FunctionCallbackWithPlainFunctionBeanIT.java[FunctionCallbackWithPlainFunctionBeanIT.java] test demo this approach. - -== Client-Side Registration - -In addition to the auto-configuration, you can register callback functions, dynamically. -You can use either the function invoking or method invoking approaches to register functions with your `ChatClient` or `ChatModel` requests. - -The client-side registration enables you to register functions by default. - -=== Function Invoking - -[source,java] ----- -ChatClient chatClient = ... - -ChatResponse response = this.chatClient.prompt("What's the weather like in San Francisco, Tokyo, and Paris?") - .tools(FunctionToolCallback.builder("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() - .chatResponse(); ----- - -NOTE: The on the fly functions are enabled by default for the duration of this request. - -This approach allows to choose dynamically different functions to be called based on the user input. - -The https://github.com/spring-projects/spring-ai/blob/main/auto-configurations/models/spring-ai-autoconfigure-model-openai/src/test/java/org/springframework/ai/model/openai/autoconfigure/tool/FunctionCallbackInPromptIT.java[FunctionCallbackInPromptIT.java] integration test provides a complete example of how to register a function with the `ChatClient` and use it in a prompt request. - -=== Method Invoking - -The `MethodInvokingFunctionCallback` enables method invocation through reflection while automatically handling JSON schema generation and parameter conversion. -It's particularly useful for integrating Java methods as callable functions within AI model interactions. - -The `MethodInvokingFunctionCallback` implements the `FunctionCallback` interface and provides: - -- Automatic JSON schema generation for method parameters -- Support for both static and instance methods -- Any number of parameters (including none) and return values (including void) -- Any parameter/return types (primitives, objects, collections) -- Special handling for `ToolContext` parameters - -You need the `MethodToolCallback.Builder` to create `MethodInvokingFunctionCallback` like this: - -[source,java] ----- -// Create using builder pattern -FunctionCallback methodInvokingCallback = FunctionCallback.builder() - .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(); ----- - -Here are a few usage examples: - -[tabs] -====== -Static Method Invocation:: -+ -[source,java] ----- -public class WeatherService { - public static String getWeather(String city, TemperatureUnit unit) { - return "Temperature in " + city + ": 20" + unit; - } -} - -// Usage -var toolMethod = ReflectionUtils.findMethod(WeatherService.class, "getWeather", String.class, TemperatureUnit.class); -MethodToolCallback callback = MethodToolCallback.builder() - .toolDefinition(ToolDefinition.builder(toolMethod) - .description("Get the weather in location") - .build()) - .toolMethod(toolMethod) - .toolObject(targetObject) - .build(); ----- -Instance Method with ToolContext:: -+ -[source,java] ----- -public class DeviceController { - public void setDeviceState(String deviceId, boolean state, ToolContext context) { - Map contextData = context.getContext(); - // Implementation using context data - } -} - -// Usage -DeviceController controller = new DeviceController(); -var toolMethod = ReflectionUtils.findMethod( - DeviceController.class, "setDeviceState", String.class, Boolean.class, ToolContext.class); -String response = ChatClient.create(chatModel).prompt() - .user("Turn on the living room lights") - .tools(MethodToolCallback.builder() - .toolDefinition(ToolDefinition.builder(toolMethod) - .description("Control device state") - .build()) - .toolMethod(toolMethod) - .toolObject(controller) - .build()) - .toolContext(Map.of("location", "home")) - .call() - .content(); ----- -====== - -The https://github.com/spring-projects/spring-ai/blob/main/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/client/OpenAiChatClientMethodInvokingFunctionCallbackIT.java[OpenAiChatClientMethodInvokingFunctionCallbackIT] -integration test provides additional examples of how to use the FunctionCallback.Builder to create method invocation FunctionCallbacks. - -[[Tool-Context]] -== Tool Context - -Spring AI now supports passing additional contextual information to function callbacks through a tool context. -This feature allows you to provide extra, user provided, data that can be used within the function execution along with the function arguments passed by the AI model. - -image::function-calling-tool-context.jpg[Function calling with Tool Context, width=700, align="center"] - -The https://github.com/spring-projects/spring-ai/blob/main/spring-ai-model/src/main/java/org/springframework/ai/chat/model/ToolContext.java[ToolContext] class provides a way to pass additional context information. - -=== Using Tool Context - -In case of function-invoking, the context information that is passed in as the second argument of a `java.util.BiFunction`. - -For method-invoking, the context information is passed as a method argument of type `ToolContext`. - -==== Function Invoking - -You can set the tool context when building your chat options and use a BiFunction for your callback: - -[source,java] ----- -BiFunction weatherFunction = - (request, toolContext) -> { - String sessionId = (String) toolContext.getContext().get("sessionId"); - String userId = (String) toolContext.getContext().get("userId"); - - // Use sessionId and userId in your function logic - double temperature = 0; - if (request.location().contains("Paris")) { - temperature = 15; - } - else if (request.location().contains("Tokyo")) { - temperature = 10; - } - else if (request.location().contains("San Francisco")) { - temperature = 30; - } - - return new MockWeatherService.Response(temperature, 15, 20, 2, 53, 45, MockWeatherService.Unit.C); - }; - - -ChatResponse response = chatClient.prompt("What's the weather like in San Francisco, Tokyo, and Paris?") - .tools(FunctionToolCallback.builder("getCurrentWeather", this.weatherFunction) - .description("Get the weather in location") - .inputType(MockWeatherService.Request.class) - .build()) - .toolContext(Map.of("sessionId", "1234", "userId", "5678")) - .call() - .chatResponse(); ----- - -In this example, the `weatherFunction` is defined as a BiFunction that takes both the request and the tool context as parameters. This allows you to access the context directly within the function logic. - -This approach allows you to pass session-specific or user-specific information to your functions, enabling more contextual and personalized responses. - -==== Method Invoking - -[source,java] ----- -public class DeviceController { - public void setDeviceState(String deviceId, boolean state, ToolContext context) { - Map contextData = context.getContext(); - // Implementation using context data - } -} - -// Usage -DeviceController controller = new DeviceController(); -var toolMethod = ReflectionUtils.findMethod( - DeviceController.class, "setDeviceState", String.class, Boolean.class, ToolContext.class); -String response = ChatClient.create(chatModel).prompt() - .user("Turn on the living room lights") - .tools(MethodToolCallback.builder() - .toolDefinition(ToolDefinition.builder(toolMethod) - .description("Control device state") - .build()) - .toolMethod(toolMethod) - .toolObject(controller) - .build()) - .toolContext(Map.of("location", "home")) - .call() - .content(); ----- diff --git a/spring-ai-model/src/main/java/org/springframework/ai/aot/SpringAiCoreRuntimeHints.java b/spring-ai-model/src/main/java/org/springframework/ai/aot/SpringAiCoreRuntimeHints.java index fa28973e502..92702410ee7 100644 --- a/spring-ai-model/src/main/java/org/springframework/ai/aot/SpringAiCoreRuntimeHints.java +++ b/spring-ai-model/src/main/java/org/springframework/ai/aot/SpringAiCoreRuntimeHints.java @@ -16,7 +16,6 @@ package org.springframework.ai.aot; -import java.lang.reflect.Method; import java.util.Set; import org.springframework.ai.chat.messages.AbstractMessage; @@ -26,15 +25,11 @@ import org.springframework.ai.chat.messages.SystemMessage; import org.springframework.ai.chat.messages.ToolResponseMessage; import org.springframework.ai.chat.messages.UserMessage; -import org.springframework.ai.model.function.DefaultFunctionCallbackResolver; -import org.springframework.ai.model.function.FunctionCallback; -import org.springframework.aot.hint.ExecutableMode; import org.springframework.aot.hint.RuntimeHints; import org.springframework.aot.hint.RuntimeHintsRegistrar; import org.springframework.core.io.ClassPathResource; import org.springframework.lang.NonNull; import org.springframework.lang.Nullable; -import org.springframework.util.ReflectionUtils; public class SpringAiCoreRuntimeHints implements RuntimeHintsRegistrar { @@ -42,19 +37,11 @@ public class SpringAiCoreRuntimeHints implements RuntimeHintsRegistrar { public void registerHints(@NonNull RuntimeHints hints, @Nullable ClassLoader classLoader) { var chatTypes = Set.of(AbstractMessage.class, AssistantMessage.class, ToolResponseMessage.class, Message.class, - MessageType.class, UserMessage.class, SystemMessage.class, DefaultFunctionCallbackResolver.class, - FunctionCallback.class); + MessageType.class, UserMessage.class, SystemMessage.class); for (var c : chatTypes) { hints.reflection().registerType(c); } - Method getDescription = ReflectionUtils.findMethod(FunctionCallback.class, "getDescription"); - hints.reflection().registerMethod(getDescription, ExecutableMode.INVOKE); - Method getInputTypeSchema = ReflectionUtils.findMethod(FunctionCallback.class, "getInputTypeSchema"); - hints.reflection().registerMethod(getInputTypeSchema, ExecutableMode.INVOKE); - Method getName = ReflectionUtils.findMethod(FunctionCallback.class, "getName"); - hints.reflection().registerMethod(getName, ExecutableMode.INVOKE); - for (var r : Set.of("embedding/embedding-model-dimensions.properties")) { hints.resources().registerResource(new ClassPathResource(r)); } diff --git a/spring-ai-model/src/main/java/org/springframework/ai/chat/model/AbstractToolCallSupport.java b/spring-ai-model/src/main/java/org/springframework/ai/chat/model/AbstractToolCallSupport.java deleted file mode 100644 index 090ea4a28ac..00000000000 --- a/spring-ai-model/src/main/java/org/springframework/ai/chat/model/AbstractToolCallSupport.java +++ /dev/null @@ -1,297 +0,0 @@ -/* - * Copyright 2023-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.ai.chat.model; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; - -import org.springframework.ai.chat.messages.AssistantMessage; -import org.springframework.ai.chat.messages.Message; -import org.springframework.ai.chat.messages.ToolResponseMessage; -import org.springframework.ai.chat.prompt.Prompt; -import org.springframework.ai.model.function.FunctionCallback; -import org.springframework.ai.model.function.FunctionCallbackResolver; -import org.springframework.ai.model.function.FunctionCallingOptions; -import org.springframework.ai.model.tool.ToolCallingManager; -import org.springframework.util.Assert; -import org.springframework.util.CollectionUtils; - -/** - * Abstract base class for tool call support. Provides functionality for handling function - * callbacks and executing functions. - * - * @author Christian Tzolov - * @author Grogdunn - * @author Thomas Vitale - * @author Jihoon Kim - * @since 1.0.0 - * @deprecated Use {@link ToolCallingManager} instead. - */ -@Deprecated -public abstract class AbstractToolCallSupport { - - protected static final boolean IS_RUNTIME_CALL = true; - - /** - * The function callback register is used to resolve the function callbacks by name. - */ - protected final Map functionCallbackRegister = new ConcurrentHashMap<>(); - - /** - * The function callback resolver is used to resolve the function callbacks by name - * from the Spring context. It is optional and usually used with Spring - * auto-configuration. - */ - protected final FunctionCallbackResolver functionCallbackResolver; - - @Deprecated - protected AbstractToolCallSupport(FunctionCallbackResolver functionCallbackResolver) { - this(functionCallbackResolver, FunctionCallingOptions.builder().build(), List.of()); - } - - @Deprecated - protected AbstractToolCallSupport(FunctionCallbackResolver functionCallbackResolver, - FunctionCallingOptions functionCallingOptions, List toolFunctionCallbacks) { - - this.functionCallbackResolver = functionCallbackResolver; - - List defaultFunctionCallbacks = merge(functionCallingOptions, toolFunctionCallbacks); - - if (!CollectionUtils.isEmpty(defaultFunctionCallbacks)) { - this.functionCallbackRegister.putAll(defaultFunctionCallbacks.stream() - .collect(ConcurrentHashMap::new, (m, v) -> m.put(v.getName(), v), ConcurrentHashMap::putAll)); - } - } - - private static List merge(FunctionCallingOptions functionOptions, - List toolFunctionCallbacks) { - List toolFunctionCallbacksCopy = new ArrayList<>(); - if (!CollectionUtils.isEmpty(toolFunctionCallbacks)) { - toolFunctionCallbacksCopy.addAll(toolFunctionCallbacks); - } - - if (!CollectionUtils.isEmpty(functionOptions.getFunctionCallbacks())) { - toolFunctionCallbacksCopy.addAll(functionOptions.getFunctionCallbacks()); - // Make sure that that function callbacks are registered directly to the - // functionCallbackRegister and not passed in the default options. - functionOptions.setFunctionCallbacks(List.of()); - } - return toolFunctionCallbacksCopy; - } - - @Deprecated - public Map getFunctionCallbackRegister() { - return this.functionCallbackRegister; - } - - /** - * Handle the runtime function callback configurations. Register the function - * callbacks - * @param runtimeFunctionOptions FunctionCallingOptions to handle. - * @return Set of function names to call. - */ - @Deprecated - protected Set runtimeFunctionCallbackConfigurations(FunctionCallingOptions runtimeFunctionOptions) { - - Set enabledFunctionsToCall = new HashSet<>(); - - if (runtimeFunctionOptions != null) { - // Add the explicitly enabled functions. - if (!CollectionUtils.isEmpty(runtimeFunctionOptions.getFunctions())) { - enabledFunctionsToCall.addAll(runtimeFunctionOptions.getFunctions()); - } - - // Add the function callbacks to the register and automatically enable them. - if (!CollectionUtils.isEmpty(runtimeFunctionOptions.getFunctionCallbacks())) { - runtimeFunctionOptions.getFunctionCallbacks().stream().forEach(functionCallback -> { - - // Register the tool callback. - this.functionCallbackRegister.put(functionCallback.getName(), functionCallback); - - // Automatically enable the function, usually from prompt callback. - enabledFunctionsToCall.add(functionCallback.getName()); - }); - } - } - - return enabledFunctionsToCall; - } - - @Deprecated - protected List handleToolCalls(Prompt prompt, ChatResponse response) { - Optional toolCallGeneration = response.getResults() - .stream() - .filter(g -> !CollectionUtils.isEmpty(g.getOutput().getToolCalls())) - .findFirst(); - if (toolCallGeneration.isEmpty()) { - throw new IllegalStateException("No tool call generation found in the response!"); - } - AssistantMessage assistantMessage = toolCallGeneration.get().getOutput(); - - Map toolContextMap = Map.of(); - if (prompt.getOptions() instanceof FunctionCallingOptions functionCallOptions - && !CollectionUtils.isEmpty(functionCallOptions.getToolContext())) { - - toolContextMap = new HashMap<>(functionCallOptions.getToolContext()); - - List toolCallHistory = new ArrayList<>(prompt.copy().getInstructions()); - toolCallHistory.add(new AssistantMessage(assistantMessage.getText(), assistantMessage.getMetadata(), - assistantMessage.getToolCalls())); - - toolContextMap.put(ToolContext.TOOL_CALL_HISTORY, toolCallHistory); - } - - ToolResponseMessage toolMessageResponse = this.executeFunctions(assistantMessage, - new ToolContext(toolContextMap)); - - List toolConversationHistory = this.buildToolCallConversation(prompt.getInstructions(), - assistantMessage, toolMessageResponse); - - return toolConversationHistory; - } - - @Deprecated - protected List buildToolCallConversation(List previousMessages, AssistantMessage assistantMessage, - ToolResponseMessage toolResponseMessage) { - List messages = new ArrayList<>(previousMessages); - messages.add(assistantMessage); - messages.add(toolResponseMessage); - return messages; - } - - /** - * Resolve the function callbacks by name. Retrieve them from the registry or try to - * resolve them from the Application Context. - * @param functionNames Name of function callbacks to retrieve. - * @return list of resolved FunctionCallbacks. - */ - @Deprecated - protected List resolveFunctionCallbacks(Set functionNames) { - - List retrievedFunctionCallbacks = new ArrayList<>(); - - for (String functionName : functionNames) { - if (!this.functionCallbackRegister.containsKey(functionName)) { - - if (this.functionCallbackResolver != null) { - FunctionCallback functionCallback = this.functionCallbackResolver.resolve(functionName); - if (functionCallback != null) { - this.functionCallbackRegister.put(functionName, functionCallback); - } - else { - throw new IllegalStateException( - "No function callback [" + functionName + "] found in tht FunctionCallbackRegister"); - } - } - else { - throw new IllegalStateException("No function callback found for name: " + functionName); - } - } - FunctionCallback functionCallback = this.functionCallbackRegister.get(functionName); - - retrievedFunctionCallbacks.add(functionCallback); - } - - return retrievedFunctionCallbacks; - } - - @Deprecated - protected ToolResponseMessage executeFunctions(AssistantMessage assistantMessage, ToolContext toolContext) { - - List toolResponses = new ArrayList<>(); - - for (AssistantMessage.ToolCall toolCall : assistantMessage.getToolCalls()) { - - var functionName = toolCall.name(); - String functionArguments = toolCall.arguments(); - - if (!this.functionCallbackRegister.containsKey(functionName)) { - throw new IllegalStateException("No function callback found for function name: " + functionName); - } - - String functionResponse = this.functionCallbackRegister.get(functionName) - .call(functionArguments, toolContext); - - toolResponses.add(new ToolResponseMessage.ToolResponse(toolCall.id(), functionName, functionResponse)); - } - - return new ToolResponseMessage(toolResponses, Map.of()); - } - - @Deprecated - protected boolean isToolCall(ChatResponse chatResponse, Set toolCallFinishReasons) { - Assert.isTrue(!CollectionUtils.isEmpty(toolCallFinishReasons), "Tool call finish reasons cannot be empty!"); - - if (chatResponse == null) { - return false; - } - - var generations = chatResponse.getResults(); - if (CollectionUtils.isEmpty(generations)) { - return false; - } - - return generations.stream().anyMatch(g -> isToolCall(g, toolCallFinishReasons)); - } - - /** - * Check if the generation is a tool call. The tool call finish reasons are used to - * determine if the generation is a tool call. - * @param generation the generation to check. - * @param toolCallFinishReasons the tool call finish reasons to check. - * @return true if the generation is a tool call, false otherwise. - */ - @Deprecated - protected boolean isToolCall(Generation generation, Set toolCallFinishReasons) { - var finishReason = (generation.getMetadata().getFinishReason() != null) - ? generation.getMetadata().getFinishReason() : ""; - return generation.getOutput().hasToolCalls() && toolCallFinishReasons.stream() - .map(s -> s.toLowerCase()) - .toList() - .contains(finishReason.toLowerCase()); - } - - /** - * Check if the proxyToolCalls is enabled for the given prompt or the default tool - * call options. The prompt options take precedence over the default options. When the - * proxyToolCalls is enabled the ChatModel implementation will not handle the function - * calling internally. The tool call and tool response messages are exposed outside - * the ChatModel implementation. - * @param prompt the prompt to check. - * @param defaultOptions the default tool call options to check. - * @return true if the proxyToolCalls is enabled, false otherwise. - */ - @Deprecated - protected boolean isProxyToolCalls(Prompt prompt, FunctionCallingOptions defaultOptions) { - if (prompt.getOptions() instanceof FunctionCallingOptions functionCallOptions - && functionCallOptions.getProxyToolCalls() != null) { - return functionCallOptions.getProxyToolCalls(); - } - else if (defaultOptions.getProxyToolCalls() != null) { - return defaultOptions.getProxyToolCalls(); - } - - return false; - } - -} diff --git a/spring-ai-model/src/main/java/org/springframework/ai/chat/model/ToolContext.java b/spring-ai-model/src/main/java/org/springframework/ai/chat/model/ToolContext.java index b9eb024ec6c..a359decc29f 100644 --- a/spring-ai-model/src/main/java/org/springframework/ai/chat/model/ToolContext.java +++ b/spring-ai-model/src/main/java/org/springframework/ai/chat/model/ToolContext.java @@ -33,7 +33,7 @@ * *

* The context is typically populated from the {@code toolContext} field of - * {@code FunctionCallingOptions} and is used in the function execution process. + * {@code ToolCallingChatOptions} and is used in the function execution process. *

* *

diff --git a/spring-ai-model/src/main/java/org/springframework/ai/model/function/AbstractFunctionCallback.java b/spring-ai-model/src/main/java/org/springframework/ai/model/function/AbstractFunctionCallback.java deleted file mode 100644 index 7ae2a17ad49..00000000000 --- a/spring-ai-model/src/main/java/org/springframework/ai/model/function/AbstractFunctionCallback.java +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Copyright 2023-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.ai.model.function; - -import java.lang.reflect.Type; -import java.util.Objects; -import java.util.function.BiFunction; -import java.util.function.Function; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; - -import org.springframework.ai.chat.model.ToolContext; -import org.springframework.ai.tool.function.FunctionToolCallback; -import org.springframework.util.Assert; - -/** - * Abstract implementation of the {@link FunctionCallback} for interacting with the - * Model's function calling protocol and a {@link Function} wrapping the interaction with - * the 3rd party service/function. - * - * Implement the {@code O apply(I request) } method to implement the interaction with the - * 3rd party service/function. - * - * The {@link #responseConverter} function is responsible to convert the 3rd party - * function's output type into a string expected by the LLM model. - * - * @param the 3rd party service input type. - * @param the 3rd party service output type. - * @author Christian Tzolov - * @deprecated in favor of {@link FunctionToolCallback}. - */ -@Deprecated -abstract class AbstractFunctionCallback implements BiFunction, FunctionCallback { - - private final String name; - - private final String description; - - private final Type inputType; - - private final String inputTypeSchema; - - private final ObjectMapper objectMapper; - - private final Function responseConverter; - - /** - * Constructs a new {@link AbstractFunctionCallback} with the given name, description, - * input type and default object mapper. - * @param name Function name. Should be unique within the ChatModel's function - * registry. - * @param description Function description. Used as a "system prompt" by the model to - * decide if the function should be called. - * @param inputTypeSchema Used to compute, the argument's Schema (such as JSON Schema - * or OpenAPI Schema)required by the Model's function calling protocol. - * @param inputType Used to compute, the argument's JSON schema required by the - * Model's function calling protocol. - * @param responseConverter Used to convert the function's output type to a string. - * @param objectMapper Used to convert the function's input and output types to and - * from JSON. - */ - protected AbstractFunctionCallback(String name, String description, String inputTypeSchema, Type inputType, - Function responseConverter, ObjectMapper objectMapper) { - Assert.notNull(name, "Name must not be null"); - Assert.notNull(description, "Description must not be null"); - Assert.notNull(inputType, "InputType must not be null"); - Assert.notNull(inputTypeSchema, "InputTypeSchema must not be null"); - Assert.notNull(responseConverter, "ResponseConverter must not be null"); - Assert.notNull(objectMapper, "ObjectMapper must not be null"); - this.name = name; - this.description = description; - this.inputType = inputType; - this.inputTypeSchema = inputTypeSchema; - this.responseConverter = responseConverter; - this.objectMapper = objectMapper; - } - - @Override - public String getName() { - return this.name; - } - - @Override - public String getDescription() { - return this.description; - } - - @Override - public String getInputTypeSchema() { - return this.inputTypeSchema; - } - - @Override - public String call(String functionInput, ToolContext toolContext) { - I request = fromJson(functionInput, this.inputType); - O response = this.apply(request, toolContext); - return this.responseConverter.apply(response); - } - - @Override - public String call(String functionArguments) { - // Convert the tool calls JSON arguments into a Java function request object. - I request = fromJson(functionArguments, this.inputType); - // extend conversation with function response. - return this.andThen(this.responseConverter).apply(request, null); - } - - private T fromJson(String json, Type targetType) { - try { - return this.objectMapper.readValue(json, this.objectMapper.constructType(targetType)); - } - catch (JsonProcessingException e) { - throw new RuntimeException(e); - } - } - - @Override - public int hashCode() { - return Objects.hash(this.name, this.description, this.inputType); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null || getClass() != obj.getClass()) { - return false; - } - - AbstractFunctionCallback other = (AbstractFunctionCallback) obj; - - return Objects.equals(this.name, other.name) && Objects.equals(this.description, other.description) - && Objects.equals(this.inputType, other.inputType); - - } - -} diff --git a/spring-ai-model/src/main/java/org/springframework/ai/model/function/DefaultCommonCallbackInvokingSpec.java b/spring-ai-model/src/main/java/org/springframework/ai/model/function/DefaultCommonCallbackInvokingSpec.java deleted file mode 100644 index e84014c23b7..00000000000 --- a/spring-ai-model/src/main/java/org/springframework/ai/model/function/DefaultCommonCallbackInvokingSpec.java +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Copyright 2024-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package 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.tool.function.FunctionToolCallback; -import org.springframework.ai.tool.method.MethodToolCallback; -import org.springframework.ai.util.JacksonUtils; -import org.springframework.util.Assert; - -/** - * @deprecated Use specific builder for the type of tool you need, e.g. - * {@link FunctionToolCallback.Builder} and {@link MethodToolCallback.Builder}. - */ -@Deprecated -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; - } - -} diff --git a/spring-ai-model/src/main/java/org/springframework/ai/model/function/DefaultFunctionCallbackBuilder.java b/spring-ai-model/src/main/java/org/springframework/ai/model/function/DefaultFunctionCallbackBuilder.java deleted file mode 100644 index b0d53b204a8..00000000000 --- a/spring-ai-model/src/main/java/org/springframework/ai/model/function/DefaultFunctionCallbackBuilder.java +++ /dev/null @@ -1,227 +0,0 @@ -/* - * Copyright 2023-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.ai.model.function; - -import java.lang.reflect.Type; -import java.util.Arrays; -import java.util.function.BiFunction; -import java.util.function.Consumer; -import java.util.function.Function; -import java.util.function.Supplier; - -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.FunctionInvokingSpec; -import org.springframework.ai.model.function.FunctionCallback.MethodInvokingSpec; -import org.springframework.ai.model.function.FunctionCallback.SchemaType; -import org.springframework.ai.tool.function.FunctionToolCallback; -import org.springframework.ai.tool.method.MethodToolCallback; -import org.springframework.ai.util.ParsingUtils; -import org.springframework.core.ParameterizedTypeReference; -import org.springframework.util.Assert; -import org.springframework.util.ReflectionUtils; -import org.springframework.util.StringUtils; - -/** - * Default implementation of the {@link FunctionCallback.Builder}. - * - * @author Christian Tzolov - * @since 1.0.0 - * @deprecated Use specific builder for the type of tool you need, e.g. - * {@link FunctionToolCallback.Builder} and {@link MethodToolCallback.Builder}. - */ -@Deprecated -public class DefaultFunctionCallbackBuilder implements FunctionCallback.Builder { - - private static final Logger logger = LoggerFactory.getLogger(DefaultFunctionCallbackBuilder.class); - - @Override - public FunctionInvokingSpec function(String name, Function function) { - return new DefaultFunctionInvokingSpec<>(name, function); - } - - @Override - public FunctionInvokingSpec function(String name, BiFunction biFunction) { - return new DefaultFunctionInvokingSpec<>(name, biFunction); - } - - @Override - public FunctionInvokingSpec function(String name, Supplier supplier) { - Function function = input -> supplier.get(); - return new DefaultFunctionInvokingSpec<>(name, function).inputType(Void.class); - } - - public FunctionInvokingSpec function(String name, Consumer consumer) { - Function function = (I input) -> { - consumer.accept(input); - return null; - }; - return new DefaultFunctionInvokingSpec<>(name, function); - } - - @Override - public MethodInvokingSpec method(String methodName, Class... argumentTypes) { - return new DefaultMethodInvokingSpec(methodName, argumentTypes); - } - - private String generateDescription(String fromName) { - - String generatedDescription = ParsingUtils.reConcatenateCamelCase(fromName, " "); - - logger.info("Description is not set! A best effort attempt to generate a description:'{}' from the:'{}'", - generatedDescription, fromName); - logger.info("It is recommended to set the Description explicitly! Use the 'description()' method!"); - - return generatedDescription; - } - - final class DefaultFunctionInvokingSpec extends DefaultCommonCallbackInvokingSpec> - implements FunctionInvokingSpec { - - private final String name; - - private Type inputType; - - private final BiFunction biFunction; - - private final Function function; - - private DefaultFunctionInvokingSpec(String name, BiFunction biFunction) { - Assert.hasText(name, "Name must not be empty"); - Assert.notNull(biFunction, "BiFunction must not be null"); - this.name = name; - this.biFunction = biFunction; - this.function = null; - } - - private DefaultFunctionInvokingSpec(String name, Function function) { - Assert.hasText(name, "Name must not be empty"); - Assert.notNull(function, "Function must not be null"); - this.name = name; - this.biFunction = null; - this.function = function; - } - - @Override - public FunctionInvokingSpec inputType(Class inputType) { - Assert.notNull(inputType, "InputType must not be null"); - this.inputType = inputType; - return this; - } - - @Override - public FunctionInvokingSpec inputType(ParameterizedTypeReference inputType) { - Assert.notNull(inputType, "InputType must not be null"); - this.inputType = inputType.getType(); - return this; - } - - @Override - public FunctionCallback build() { - - Assert.notNull(this.getObjectMapper(), "ObjectMapper must not be null"); - Assert.hasText(this.name, "Name must not be empty"); - Assert.notNull(this.getResponseConverter(), "ResponseConverter must not be null"); - Assert.notNull(this.inputType, "InputType must not be null"); - - if (this.getInputTypeSchema() == null) { - boolean upperCaseTypeValues = this.schemaType == SchemaType.OPEN_API_SCHEMA; - 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.getDescriptionExt(), - this.getInputTypeSchema(), this.inputType, (Function) this.getResponseConverter(), - this.getObjectMapper(), finalBiFunction); - } - - private String getDescriptionExt() { - if (StringUtils.hasText(this.getDescription())) { - return this.getDescription(); - } - return generateDescription(this.name); - } - - } - - final class DefaultMethodInvokingSpec extends DefaultCommonCallbackInvokingSpec - implements MethodInvokingSpec { - - private String name; - - private final String methodName; - - private Class targetClass; - - private Object targetObject; - - private final Class[] argumentTypes; - - private DefaultMethodInvokingSpec(String methodName, Class... argumentTypes) { - Assert.hasText(methodName, "Method name must not be null"); - Assert.notNull(argumentTypes, "Argument types must not be null"); - this.methodName = methodName; - this.argumentTypes = argumentTypes; - } - - public MethodInvokingSpec name(String name) { - Assert.hasText(name, "Name must not be empty"); - this.name = name; - return this; - } - - public MethodInvokingSpec targetClass(Class targetClass) { - Assert.notNull(targetClass, "Target class must not be null"); - this.targetClass = targetClass; - return this; - } - - @Override - public MethodInvokingSpec targetObject(Object methodObject) { - Assert.notNull(methodObject, "Method object must not be null"); - this.targetObject = methodObject; - this.targetClass = methodObject.getClass(); - return this; - } - - @Override - public FunctionCallback build() { - Assert.isTrue(this.targetClass != null || this.targetObject != null, - "Target class or object must not be null"); - 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.getDescriptionExt(), - this.getObjectMapper(), this.name, this.getResponseConverter()); - } - - 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-model/src/main/java/org/springframework/ai/model/function/DefaultFunctionCallbackResolver.java b/spring-ai-model/src/main/java/org/springframework/ai/model/function/DefaultFunctionCallbackResolver.java deleted file mode 100644 index 6278a450be3..00000000000 --- a/spring-ai-model/src/main/java/org/springframework/ai/model/function/DefaultFunctionCallbackResolver.java +++ /dev/null @@ -1,215 +0,0 @@ -/* - * Copyright 2023-2024 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.ai.model.function; - -import java.util.function.BiFunction; -import java.util.function.Consumer; -import java.util.function.Function; -import java.util.function.Supplier; - -import com.fasterxml.jackson.annotation.JsonClassDescription; -import kotlin.jvm.functions.Function0; -import kotlin.jvm.functions.Function1; -import kotlin.jvm.functions.Function2; - -import org.springframework.ai.chat.model.ToolContext; -import org.springframework.ai.model.function.FunctionCallback.SchemaType; -import org.springframework.ai.tool.resolution.SpringBeanToolCallbackResolver; -import org.springframework.ai.tool.resolution.TypeResolverHelper; -import org.springframework.beans.BeansException; -import org.springframework.context.ApplicationContext; -import org.springframework.context.ApplicationContextAware; -import org.springframework.context.annotation.Description; -import org.springframework.context.support.GenericApplicationContext; -import org.springframework.core.KotlinDetector; -import org.springframework.core.ParameterizedTypeReference; -import org.springframework.core.ResolvableType; -import org.springframework.lang.NonNull; -import org.springframework.util.StringUtils; - -/** - * A Spring {@link ApplicationContextAware} implementation that provides a way to retrieve - * a {@link Function} from the Spring context and wrap it into a {@link FunctionCallback}. - *

- * The name of the function is determined by the bean name. - *

- * The description of the function is determined by the following rules: - *

    - *
  • Provided as a default description
  • - *
  • Provided as a {@code @Description} annotation on the bean
  • - *
  • Provided as a {@code @JsonClassDescription} annotation on the input class
  • - *
- * - * @author Christian Tzolov - * @author Christopher Smith - * @author Sebastien Deleuze - * @deprecated Use {@link SpringBeanToolCallbackResolver} instead. - */ -@Deprecated -public class DefaultFunctionCallbackResolver implements ApplicationContextAware, FunctionCallbackResolver { - - private GenericApplicationContext applicationContext; - - private SchemaType schemaType = SchemaType.JSON_SCHEMA; - - public void setSchemaType(SchemaType schemaType) { - this.schemaType = schemaType; - } - - @Override - public void setApplicationContext(@NonNull ApplicationContext applicationContext) throws BeansException { - this.applicationContext = (GenericApplicationContext) applicationContext; - } - - @Override - public FunctionCallback resolve(@NonNull String beanName) { - ResolvableType functionType = TypeResolverHelper.resolveBeanType(this.applicationContext, beanName); - ResolvableType functionInputType = (ResolvableType.forType(Supplier.class).isAssignableFrom(functionType)) - ? ResolvableType.forType(Void.class) : TypeResolverHelper.getFunctionArgumentType(functionType, 0); - - String functionDescription = resolveFunctionDescription(beanName, functionInputType.toClass()); - Object bean = this.applicationContext.getBean(beanName); - - return buildFunctionCallback(beanName, functionType, functionInputType, functionDescription, bean); - } - - private String resolveFunctionDescription(String beanName, Class functionInputClass) { - String functionDescription = ""; - - if (!StringUtils.hasText(functionDescription)) { - Description descriptionAnnotation = this.applicationContext.findAnnotationOnBean(beanName, - Description.class); - if (descriptionAnnotation != null) { - functionDescription = descriptionAnnotation.value(); - } - - if (!StringUtils.hasText(functionDescription)) { - JsonClassDescription jsonClassDescriptionAnnotation = functionInputClass - .getAnnotation(JsonClassDescription.class); - if (jsonClassDescriptionAnnotation != null) { - functionDescription = jsonClassDescriptionAnnotation.value(); - } - } - - if (!StringUtils.hasText(functionDescription)) { - throw new IllegalStateException("Could not determine function description. " - + "Please provide a description either as a default parameter, via @Description annotation on the bean " - + "or @JsonClassDescription annotation on the input class."); - } - } - - return functionDescription; - } - - private FunctionCallback buildFunctionCallback(String beanName, ResolvableType functionType, - ResolvableType functionInputType, String functionDescription, Object bean) { - - if (KotlinDetector.isKotlinPresent()) { - if (KotlinDelegate.isKotlinFunction(functionType.toClass())) { - return FunctionCallback.builder() - .function(beanName, KotlinDelegate.wrapKotlinFunction(bean)) - .schemaType(this.schemaType) - .description(functionDescription) - .inputType(ParameterizedTypeReference.forType(functionInputType.getType())) - .build(); - } - if (KotlinDelegate.isKotlinBiFunction(functionType.toClass())) { - return FunctionCallback.builder() - .function(beanName, KotlinDelegate.wrapKotlinBiFunction(bean)) - .description(functionDescription) - .schemaType(this.schemaType) - .inputType(ParameterizedTypeReference.forType(functionInputType.getType())) - .build(); - } - if (KotlinDelegate.isKotlinSupplier(functionType.toClass())) { - return FunctionCallback.builder() - .function(beanName, KotlinDelegate.wrapKotlinSupplier(bean)) - .description(functionDescription) - .schemaType(this.schemaType) - .inputType(ParameterizedTypeReference.forType(functionInputType.getType())) - .build(); - } - } - - if (bean instanceof Function function) { - return FunctionCallback.builder() - .function(beanName, function) - .schemaType(this.schemaType) - .description(functionDescription) - .inputType(ParameterizedTypeReference.forType(functionInputType.getType())) - .build(); - } - if (bean instanceof BiFunction) { - return FunctionCallback.builder() - .function(beanName, (BiFunction) bean) - .description(functionDescription) - .schemaType(this.schemaType) - .inputType(ParameterizedTypeReference.forType(functionInputType.getType())) - .build(); - } - if (bean instanceof Supplier supplier) { - return FunctionCallback.builder() - .function(beanName, supplier) - .description(functionDescription) - .schemaType(this.schemaType) - .inputType(ParameterizedTypeReference.forType(functionInputType.getType())) - .build(); - } - if (bean instanceof Consumer consumer) { - return FunctionCallback.builder() - .function(beanName, consumer) - .description(functionDescription) - .schemaType(this.schemaType) - .inputType(ParameterizedTypeReference.forType(functionInputType.getType())) - .build(); - } - - throw new IllegalStateException("Unsupported function type"); - } - - private static final class KotlinDelegate { - - public static boolean isKotlinSupplier(Class clazz) { - return Function0.class.isAssignableFrom(clazz); - } - - @SuppressWarnings("unchecked") - public static Supplier wrapKotlinSupplier(Object function) { - return () -> ((Function0) function).invoke(); - } - - public static boolean isKotlinFunction(Class clazz) { - return Function1.class.isAssignableFrom(clazz); - } - - @SuppressWarnings("unchecked") - public static Function wrapKotlinFunction(Object function) { - return t -> ((Function1) function).invoke(t); - } - - public static boolean isKotlinBiFunction(Class clazz) { - return Function2.class.isAssignableFrom(clazz); - } - - @SuppressWarnings("unchecked") - public static BiFunction wrapKotlinBiFunction(Object function) { - return (t, u) -> ((Function2) function).invoke(t, u); - } - - } - -} diff --git a/spring-ai-model/src/main/java/org/springframework/ai/model/function/DefaultFunctionCallingOptions.java b/spring-ai-model/src/main/java/org/springframework/ai/model/function/DefaultFunctionCallingOptions.java deleted file mode 100644 index 5d580e54d7b..00000000000 --- a/spring-ai-model/src/main/java/org/springframework/ai/model/function/DefaultFunctionCallingOptions.java +++ /dev/null @@ -1,254 +0,0 @@ -/* - * Copyright 2024-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.ai.model.function; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import org.springframework.ai.chat.prompt.ChatOptions; -import org.springframework.ai.model.tool.DefaultToolCallingChatOptions; -import org.springframework.util.Assert; -import org.springframework.util.CollectionUtils; -import org.springframework.util.StringUtils; - -/** - * Default implementation of {@link FunctionCallingOptions}. - * - * @author Christian Tzolov - * @author Thomas Vitale - * @author Ilayaperumal Gopinathan - * @deprecated in favor of {@link DefaultToolCallingChatOptions}. - */ -@Deprecated -public class DefaultFunctionCallingOptions implements FunctionCallingOptions { - - private List functionCallbacks = new ArrayList<>(); - - private Set functions = new HashSet<>(); - - private Boolean proxyToolCalls = false; - - private Map context = new HashMap<>(); - - private String model; - - private Double frequencyPenalty; - - private Integer maxTokens; - - private Double presencePenalty; - - private List stopSequences; - - private Double temperature; - - private Integer topK; - - private Double topP; - - @Override - public List getFunctionCallbacks() { - return Collections.unmodifiableList(this.functionCallbacks); - } - - public void setFunctionCallbacks(List functionCallbacks) { - Assert.notNull(functionCallbacks, "FunctionCallbacks must not be null"); - this.functionCallbacks = new ArrayList<>(functionCallbacks); - } - - @Override - public Set getFunctions() { - return Collections.unmodifiableSet(this.functions); - } - - public void setFunctions(Set functions) { - Assert.notNull(functions, "Functions must not be null"); - this.functions = new HashSet<>(functions); - } - - @Override - public Boolean getProxyToolCalls() { - return this.proxyToolCalls; - } - - public void setProxyToolCalls(Boolean proxyToolCalls) { - this.proxyToolCalls = proxyToolCalls; - } - - public Map getToolContext() { - return Collections.unmodifiableMap(this.context); - } - - public void setToolContext(Map context) { - Assert.notNull(context, "Context must not be null"); - this.context = new HashMap<>(context); - } - - @Override - public String getModel() { - return this.model; - } - - public void setModel(String model) { - this.model = model; - } - - @Override - public Double getFrequencyPenalty() { - return this.frequencyPenalty; - } - - public void setFrequencyPenalty(Double frequencyPenalty) { - this.frequencyPenalty = frequencyPenalty; - } - - @Override - public Integer getMaxTokens() { - return this.maxTokens; - } - - public void setMaxTokens(Integer maxTokens) { - this.maxTokens = maxTokens; - } - - @Override - public Double getPresencePenalty() { - return this.presencePenalty; - } - - public void setPresencePenalty(Double presencePenalty) { - this.presencePenalty = presencePenalty; - } - - @Override - public List getStopSequences() { - return this.stopSequences != null ? Collections.unmodifiableList(this.stopSequences) : null; - } - - public void setStopSequences(List stopSequences) { - this.stopSequences = stopSequences; - } - - @Override - public Double getTemperature() { - return this.temperature; - } - - public void setTemperature(Double temperature) { - this.temperature = temperature; - } - - @Override - public Integer getTopK() { - return this.topK; - } - - public void setTopK(Integer topK) { - this.topK = topK; - } - - @Override - public Double getTopP() { - return this.topP; - } - - public void setTopP(Double topP) { - this.topP = topP; - } - - @Override - @SuppressWarnings("unchecked") - public T copy() { - DefaultFunctionCallingOptions copy = new DefaultFunctionCallingOptions(); - copy.setModel(this.getModel()); - copy.setFrequencyPenalty(this.getFrequencyPenalty()); - copy.setMaxTokens(this.getMaxTokens()); - copy.setPresencePenalty(this.getPresencePenalty()); - copy.setStopSequences(this.getStopSequences() != null ? new ArrayList<>(this.getStopSequences()) : null); - copy.setTemperature(this.getTemperature()); - copy.setTopK(this.getTopK()); - copy.setTopP(this.getTopP()); - copy.setFunctions(new HashSet<>(this.functions)); - copy.setFunctionCallbacks(new ArrayList<>(this.functionCallbacks)); - copy.setProxyToolCalls(this.proxyToolCalls); - copy.setToolContext(new HashMap<>(this.getToolContext())); - return (T) copy; - } - - public FunctionCallingOptions merge(ChatOptions options) { - Builder builder = FunctionCallingOptions.builder(); - builder.model(StringUtils.hasText(options.getModel()) ? options.getModel() : this.getModel()) - .frequencyPenalty( - options.getFrequencyPenalty() != null ? options.getFrequencyPenalty() : this.getFrequencyPenalty()) - .maxTokens(options.getMaxTokens() != null ? options.getMaxTokens() : this.getMaxTokens()) - .presencePenalty( - options.getPresencePenalty() != null ? options.getPresencePenalty() : this.getPresencePenalty()) - .stopSequences(options.getStopSequences() != null ? options.getStopSequences() : this.getStopSequences()) - .temperature(options.getTemperature() != null ? options.getTemperature() : this.getTemperature()) - .topK(options.getTopK() != null ? options.getTopK() : this.getTopK()) - .topP(options.getTopP() != null ? options.getTopP() : this.getTopP()); - - // Try to get function-specific properties if options is a FunctionCallingOptions - if (options instanceof FunctionCallingOptions functionOptions) { - builder.proxyToolCalls(functionOptions.getProxyToolCalls() != null ? functionOptions.getProxyToolCalls() - : this.proxyToolCalls); - - Set functions = new HashSet<>(); - if (!CollectionUtils.isEmpty(this.functions)) { - functions.addAll(this.functions); - } - if (!CollectionUtils.isEmpty(functionOptions.getFunctions())) { - functions.addAll(functionOptions.getFunctions()); - } - builder.functions(functions); - - List functionCallbacks = new ArrayList<>(); - if (!CollectionUtils.isEmpty(this.functionCallbacks)) { - functionCallbacks.addAll(this.functionCallbacks); - } - if (!CollectionUtils.isEmpty(functionOptions.getFunctionCallbacks())) { - functionCallbacks.addAll(functionOptions.getFunctionCallbacks()); - } - builder.functionCallbacks(functionCallbacks); - - Map context = new HashMap<>(); - if (!CollectionUtils.isEmpty(this.context)) { - context.putAll(this.context); - } - if (!CollectionUtils.isEmpty(functionOptions.getToolContext())) { - context.putAll(functionOptions.getToolContext()); - } - builder.toolContext(context); - } - else { - // If not a FunctionCallingOptions, preserve current function-specific - // properties - builder.proxyToolCalls(this.proxyToolCalls); - builder.functions(new HashSet<>(this.functions)); - builder.functionCallbacks(new ArrayList<>(this.functionCallbacks)); - builder.toolContext(new HashMap<>(this.context)); - } - - return builder.build(); - } - -} diff --git a/spring-ai-model/src/main/java/org/springframework/ai/model/function/DefaultFunctionCallingOptionsBuilder.java b/spring-ai-model/src/main/java/org/springframework/ai/model/function/DefaultFunctionCallingOptionsBuilder.java deleted file mode 100644 index 85b58101703..00000000000 --- a/spring-ai-model/src/main/java/org/springframework/ai/model/function/DefaultFunctionCallingOptionsBuilder.java +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Copyright 2024-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.ai.model.function; - -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import org.springframework.ai.model.tool.DefaultToolCallingChatOptions; -import org.springframework.util.Assert; - -/** - * Default implementation of {@link FunctionCallingOptions.Builder}. - * - * @author Christian Tzolov - * @author Thomas Vitale - * @author Ilayaperumal Gopinathan - * @deprecated in favor of {@link DefaultToolCallingChatOptions.Builder}. - */ -@Deprecated -public class DefaultFunctionCallingOptionsBuilder implements FunctionCallingOptions.Builder { - - private final DefaultFunctionCallingOptions options; - - public DefaultFunctionCallingOptionsBuilder() { - this.options = new DefaultFunctionCallingOptions(); - } - - // Function calling specific methods - @Override - public FunctionCallingOptions.Builder functionCallbacks(List functionCallbacks) { - this.options.setFunctionCallbacks(functionCallbacks); - return this; - } - - @Override - public FunctionCallingOptions.Builder functionCallbacks(FunctionCallback... functionCallbacks) { - Assert.notNull(functionCallbacks, "FunctionCallbacks must not be null"); - this.options.setFunctionCallbacks(List.of(functionCallbacks)); - return this; - } - - @Override - public FunctionCallingOptions.Builder functions(Set functions) { - this.options.setFunctions(functions); - return this; - } - - @Override - public FunctionCallingOptions.Builder function(String function) { - Assert.notNull(function, "Function must not be null"); - var set = new HashSet<>(this.options.getFunctions()); - set.add(function); - this.options.setFunctions(set); - return this; - } - - @Override - public FunctionCallingOptions.Builder proxyToolCalls(Boolean proxyToolCalls) { - this.options.setProxyToolCalls(proxyToolCalls); - return this; - } - - @Override - public FunctionCallingOptions.Builder toolContext(Map context) { - Assert.notNull(context, "Tool context must not be null"); - Map newContext = new HashMap<>(this.options.getToolContext()); - newContext.putAll(context); - this.options.setToolContext(newContext); - return this; - } - - @Override - public FunctionCallingOptions.Builder toolContext(String key, Object value) { - Assert.notNull(key, "Key must not be null"); - Assert.notNull(value, "Value must not be null"); - Map newContext = new HashMap<>(this.options.getToolContext()); - newContext.put(key, value); - this.options.setToolContext(newContext); - return this; - } - - // ChatOptions.Builder methods with covariant return type - @Override - public FunctionCallingOptions.Builder model(String model) { - this.options.setModel(model); - return this; - } - - @Override - public FunctionCallingOptions.Builder frequencyPenalty(Double frequencyPenalty) { - this.options.setFrequencyPenalty(frequencyPenalty); - return this; - } - - @Override - public FunctionCallingOptions.Builder maxTokens(Integer maxTokens) { - this.options.setMaxTokens(maxTokens); - return this; - } - - @Override - public FunctionCallingOptions.Builder presencePenalty(Double presencePenalty) { - this.options.setPresencePenalty(presencePenalty); - return this; - } - - @Override - public FunctionCallingOptions.Builder stopSequences(List stop) { - this.options.setStopSequences(stop); - return this; - } - - @Override - public FunctionCallingOptions.Builder temperature(Double temperature) { - this.options.setTemperature(temperature); - return this; - } - - @Override - public FunctionCallingOptions.Builder topK(Integer topK) { - this.options.setTopK(topK); - return this; - } - - @Override - public FunctionCallingOptions.Builder topP(Double topP) { - this.options.setTopP(topP); - return this; - } - - @Override - public FunctionCallingOptions build() { - return this.options.copy(); - } - -} diff --git a/spring-ai-model/src/main/java/org/springframework/ai/model/function/FunctionCallback.java b/spring-ai-model/src/main/java/org/springframework/ai/model/function/FunctionCallback.java deleted file mode 100644 index 0553c0d7f29..00000000000 --- a/spring-ai-model/src/main/java/org/springframework/ai/model/function/FunctionCallback.java +++ /dev/null @@ -1,252 +0,0 @@ -/* - * Copyright 2023-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.ai.model.function; - -import java.util.function.BiFunction; -import java.util.function.Consumer; -import java.util.function.Function; -import java.util.function.Supplier; - -import com.fasterxml.jackson.databind.ObjectMapper; - -import org.springframework.ai.chat.model.ToolContext; -import org.springframework.ai.tool.ToolCallback; -import org.springframework.ai.tool.function.FunctionToolCallback; -import org.springframework.ai.tool.method.MethodToolCallback; -import org.springframework.core.ParameterizedTypeReference; - -/** - * Represents a model function call handler. Implementations are registered with the - * Models and called on prompts that trigger the function call. - * - * @author Christian Tzolov - * @deprecated in favor of {@link ToolCallback}. - */ -@Deprecated -public interface FunctionCallback { - - /** - * @return Returns the Function name. Unique within the model. - */ - String getName(); - - /** - * @return Returns the function description. This description is used by the model do - * decide if the function should be called or not. - */ - String getDescription(); - - /** - * @return Returns the JSON schema of the function input type. - */ - String getInputTypeSchema(); - - /** - * Called when a model detects and triggers a function call. The model is responsible - * to pass the function arguments in the pre-configured JSON schema format. - * @param functionInput JSON string with the function arguments to be passed to the - * function. The arguments are defined as JSON schema usually registered with the - * model. - * @return String containing the function call response. - */ - String call(String functionInput); - - /** - * Called when a model detects and triggers a function call. The model is responsible - * to pass the function arguments in the pre-configured JSON schema format. - * Additionally, the model can pass a context map to the function if available. The - * context is used to pass additional user provided state in addition to the arguments - * provided by the AI model. - * @param functionInput JSON string with the function arguments to be passed to the - * function. The arguments are defined as JSON schema usually registered with the - * model. Arguments are provided by the AI model. - * @param tooContext Map with the function context. The context is used to pass - * additional user provided state in addition to the arguments provided by the AI - * model. - * @return String containing the function call response. - */ - default String call(String functionInput, ToolContext toolContext) { - if (toolContext != null && !toolContext.getContext().isEmpty()) { - throw new UnsupportedOperationException("Function context is not supported!"); - } - return call(functionInput); - } - - /** - * Creates a new {@link Builder} instance used to build a default - * {@link FunctionCallback} instance. - * @return Returns a new {@link Builder} instance. - */ - static Builder builder() { - return new DefaultFunctionCallbackBuilder(); - } - - /** - * Describes the type of the schema used to describe the input parameters of the - * function. - */ - enum SchemaType { - - /** - * JSON schema - */ - JSON_SCHEMA, - /** - * Open API schema - */ - OPEN_API_SCHEMA - - } - - /** - * Builder for creating a {@link FunctionCallback} instance. This is a hierarchical - * builder with the following structure: - *
    - *
  • {@link Builder} - The root builder interface. - *
  • {@link FunctionInvokingSpec} - The function invoking builder interface. - *
  • {@link MethodInvokingSpec} - The method invoking builder interface. - *
- * - * @deprecated Use specific builder for the type of tool you need, e.g. - * {@link FunctionToolCallback.Builder} and {@link MethodToolCallback.Builder}. - */ - @Deprecated - interface Builder { - - /** - * Builds a {@link Function} invoking {@link FunctionCallback} instance. - */ - FunctionInvokingSpec function(String name, Function function); - - /** - * Builds a {@link BiFunction} invoking {@link FunctionCallback} instance. - */ - FunctionInvokingSpec function(String name, BiFunction biFunction); - - /** - * Builds a {@link Supplier} invoking {@link FunctionCallback} instance. - */ - FunctionInvokingSpec function(String name, Supplier supplier); - - /** - * Builds a {@link Consumer} invoking {@link FunctionCallback} instance. - */ - FunctionInvokingSpec function(String name, Consumer consumer); - - /** - * Builds a Method invoking {@link FunctionCallback} instance. - */ - MethodInvokingSpec method(String methodName, Class... argumentTypes); - - } - - 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 extends CommonCallbackInvokingSpec> { - - /** - * Function input type. The input type is used to validate the function input - * arguments. - * @see #inputType(ParameterizedTypeReference) - */ - FunctionInvokingSpec inputType(Class inputType); - - /** - * Function input type retaining generic types. The input type is used to validate - * the function input arguments. - */ - FunctionInvokingSpec inputType(ParameterizedTypeReference inputType); - - /** - * Builds the {@link FunctionCallback} instance. - */ - FunctionCallback build(); - - } - - /** - * Method invoking builder interface. - */ - interface MethodInvokingSpec extends CommonCallbackInvokingSpec { - - /** - * Optional function name. If not provided the method name is used as the - * function. - * @param name Function name. Unique within the model. - */ - MethodInvokingSpec name(String name); - - /** - * For non-static objects the target object is used to invoke the method. - * @param methodObject target object where the method is defined. - */ - MethodInvokingSpec targetObject(Object methodObject); - - /** - * Target class where the method is defined. Used for static methods. For - * non-static methods the target object is used. - * @param targetClass method target class. - */ - MethodInvokingSpec targetClass(Class targetClass); - - /** - * Builds the {@link FunctionCallback} instance. - */ - FunctionCallback build(); - - } - -} diff --git a/spring-ai-model/src/main/java/org/springframework/ai/model/function/FunctionCallbackResolver.java b/spring-ai-model/src/main/java/org/springframework/ai/model/function/FunctionCallbackResolver.java deleted file mode 100644 index 6da86bd6e01..00000000000 --- a/spring-ai-model/src/main/java/org/springframework/ai/model/function/FunctionCallbackResolver.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2024-2024 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.ai.model.function; - -import org.springframework.ai.tool.resolution.ToolCallbackResolver; -import org.springframework.lang.NonNull; - -/** - * Strategy interface for resolving {@link FunctionCallback} instances. - * - * @author Christian Tzolov - * @since 1.0.0 - * @deprecated Use {@link ToolCallbackResolver} instead. - */ -@Deprecated -public interface FunctionCallbackResolver { - - /** - * Resolve the {@link FunctionCallback} instance by its name. - * @param name the name of the function to resolve - * @return the {@link FunctionCallback} instance - */ - FunctionCallback resolve(@NonNull String name); - -} diff --git a/spring-ai-model/src/main/java/org/springframework/ai/model/function/FunctionCallingOptions.java b/spring-ai-model/src/main/java/org/springframework/ai/model/function/FunctionCallingOptions.java deleted file mode 100644 index e45e8db5f92..00000000000 --- a/spring-ai-model/src/main/java/org/springframework/ai/model/function/FunctionCallingOptions.java +++ /dev/null @@ -1,180 +0,0 @@ -/* - * Copyright 2023-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.ai.model.function; - -import java.util.List; -import java.util.Map; -import java.util.Set; - -import org.springframework.ai.chat.prompt.ChatOptions; -import org.springframework.ai.model.tool.ToolCallingChatOptions; - -/** - * FunctionCallingOptions is a set of options that can be used to configure the function - * calling behavior of the ChatModel. - * - * @author Christian Tzolov - * @author Ilayaperumal Gopinathan - * @deprecated in favor of {@link ToolCallingChatOptions}. - */ -@Deprecated -public interface FunctionCallingOptions extends ChatOptions { - - /** - * @return Returns {@link DefaultFunctionCallingOptionsBuilder} to create a new - * instance of {@link FunctionCallingOptions}. - */ - static Builder builder() { - return new DefaultFunctionCallingOptionsBuilder(); - } - - /** - * Function Callbacks to be registered with the ChatModel. For Prompt Options the - * toolCallbacks are automatically enabled for the duration of the prompt execution. - * For Default Options the FunctionCallbacks are registered but disabled by default. - * You have to use "functions" property to list the function names from the ChatModel - * registry to be used in the chat completion requests. - * @return Return the Function Callbacks to be registered with the ChatModel. - */ - List getFunctionCallbacks(); - - /** - * Set the Function Callbacks to be registered with the ChatModel. - * @param functionCallbacks the Function Callbacks to be registered with the - * ChatModel. - */ - void setFunctionCallbacks(List functionCallbacks); - - /** - * @return {@link Set} of function names from the ChatModel registry to be used in the - * next chat completion requests. - */ - Set getFunctions(); - - /** - * Set the list of function names from the ChatModel registry to be used in the next - * chat completion requests. - * @param functions the list of function names from the ChatModel registry to be used - * in the next chat completion requests. - */ - void setFunctions(Set functions); - - default Boolean getProxyToolCalls() { - return false; - } - - default void setProxyToolCalls(Boolean proxyToolCalls) { - if (proxyToolCalls != null) { - throw new UnsupportedOperationException("Setting Proxy Tool Calls are not supported!"); - } - } - - Map getToolContext(); - - void setToolContext(Map tooContext); - - /** - * Builder for creating {@link FunctionCallingOptions} instance. - */ - interface Builder extends ChatOptions.Builder { - - /** - * The list of Function Callbacks to be registered with the Chat model. - * @param functionCallbacks the list of Function Callbacks. - * @return the FunctionCallOptions Builder. - */ - Builder functionCallbacks(List functionCallbacks); - - /** - * The Function Callbacks to be registered with the Chat model. - * @param functionCallbacks the function callbacks. - * @return the FunctionCallOptions Builder. - */ - Builder functionCallbacks(FunctionCallback... functionCallbacks); - - /** - * {@link Set} of function names to be registered with the Chat model. - * @param functions the {@link Set} of function names - * @return the FunctionCallOptions Builder. - */ - Builder functions(Set functions); - - /** - * The function name to be registered with the chat model. - * @param function the name of the function. - * @return the FunctionCallOptions Builder. - */ - Builder function(String function); - - /** - * Boolean flag to indicate if the proxy ToolCalls is enabled. - * @param proxyToolCalls boolean value to enable proxy ToolCalls. - * @return the FunctionCallOptions Builder. - */ - Builder proxyToolCalls(Boolean proxyToolCalls); - - /** - * Add a {@link Map} of context values into tool context. - * @param context the map representing the tool context. - * @return the FunctionCallOptions Builder. - */ - Builder toolContext(Map context); - - /** - * Add a specific key/value pair to the tool context. - * @param key the key to use. - * @param value the corresponding value. - * @return the FunctionCallOptions Builder. - */ - Builder toolContext(String key, Object value); - - /** - * Builds the {@link FunctionCallingOptions}. - * @return the FunctionCalling options. - */ - @Override - FunctionCallingOptions build(); - - // Override all ChatOptions.Builder methods to return - // FunctionCallingOptions.Builder - @Override - Builder model(String model); - - @Override - Builder frequencyPenalty(Double frequencyPenalty); - - @Override - Builder maxTokens(Integer maxTokens); - - @Override - Builder presencePenalty(Double presencePenalty); - - @Override - Builder stopSequences(List stopSequences); - - @Override - Builder temperature(Double temperature); - - @Override - Builder topK(Integer topK); - - @Override - Builder topP(Double topP); - - } - -} diff --git a/spring-ai-model/src/main/java/org/springframework/ai/model/function/FunctionInvokingFunctionCallback.java b/spring-ai-model/src/main/java/org/springframework/ai/model/function/FunctionInvokingFunctionCallback.java deleted file mode 100644 index 0fed96ab0ce..00000000000 --- a/spring-ai-model/src/main/java/org/springframework/ai/model/function/FunctionInvokingFunctionCallback.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2023-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.ai.model.function; - -import java.lang.reflect.Type; -import java.util.function.BiFunction; -import java.util.function.Function; - -import com.fasterxml.jackson.databind.ObjectMapper; - -import org.springframework.ai.chat.model.ToolContext; -import org.springframework.ai.tool.function.FunctionToolCallback; -import org.springframework.util.Assert; - -/** - * Note that the underlying function is responsible for converting the output into format - * that can be consumed by the Model. The default implementation converts the output into - * String before sending it to the Model. Provide a custom function responseConverter - * implementation to override this. - * - * @param the input type - * @param the output type - * @author Christian Tzolov - * @deprecated in favor of {@link FunctionToolCallback}. - */ -@Deprecated -public final class FunctionInvokingFunctionCallback extends AbstractFunctionCallback { - - private final BiFunction biFunction; - - FunctionInvokingFunctionCallback(String name, String description, String inputTypeSchema, Type inputType, - Function responseConverter, ObjectMapper objectMapper, BiFunction function) { - super(name, description, inputTypeSchema, inputType, responseConverter, objectMapper); - Assert.notNull(function, "Function must not be null"); - this.biFunction = function; - } - - @Override - public O apply(I input, ToolContext context) { - return this.biFunction.apply(input, context); - } - -} diff --git a/spring-ai-model/src/main/java/org/springframework/ai/model/function/MethodInvokingFunctionCallback.java b/spring-ai-model/src/main/java/org/springframework/ai/model/function/MethodInvokingFunctionCallback.java deleted file mode 100644 index e3a724b1548..00000000000 --- a/spring-ai-model/src/main/java/org/springframework/ai/model/function/MethodInvokingFunctionCallback.java +++ /dev/null @@ -1,277 +0,0 @@ -/* - * Copyright 2023-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.ai.model.function; - -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; -import java.util.List; -import java.util.Map; -import java.util.function.Function; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.node.ObjectNode; -import com.fasterxml.jackson.module.jsonSchema.JsonSchema; -import com.fasterxml.jackson.module.jsonSchema.JsonSchemaGenerator; -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.tool.method.MethodToolCallback; -import org.springframework.util.Assert; -import org.springframework.util.ClassUtils; -import org.springframework.util.CollectionUtils; -import org.springframework.util.ReflectionUtils; - -/** - * A {@link FunctionCallback} that invokes methods on objects via reflection, supporting: - *
    - *
  • Static and non-static methods
  • - *
  • Any number of parameters (including none)
  • - *
  • Any parameter/return types (primitives, objects, collections)
  • - *
  • Special handling for {@link ToolContext} parameters
  • - *
- * Automatically infers the input parameters JSON schema from method's argument types. - * - * @author Christian Tzolov - * @since 1.0.0 - * @deprecated in favor of {@link MethodToolCallback}. - */ -@Deprecated -public class MethodInvokingFunctionCallback implements FunctionCallback { - - private static final Logger logger = LoggerFactory.getLogger(MethodInvokingFunctionCallback.class); - - /** - * Object instance that contains the method to be invoked. If the method is static - * this object can be null. - */ - private final Object functionObject; - - /** - * The method to be invoked. - */ - private final Method method; - - /** - * Description to help the LLM model to understand worth the method does and when to - * use it. - */ - private final String description; - - /** - * Internal ObjectMapper used to serialize/deserialize the method input and output. - */ - private final ObjectMapper mapper; - - /** - * The JSON schema generated from the method input parameters. - */ - private final String inputSchema; - - /** - * Flag indicating if the method accepts a {@link ToolContext} as input parameter. - */ - private boolean isToolContextMethod = false; - - /** - * Optional function name. If not provided the method name is used as the function. - */ - private final String name; - - /** - * - */ - private final Function responseConverter; - - MethodInvokingFunctionCallback(Object functionObject, Method method, String description, ObjectMapper mapper, - String name, Function responseConverter) { - - Assert.notNull(method, "Method must not be null"); - Assert.notNull(mapper, "ObjectMapper must not be null"); - Assert.hasText(description, "Description must not be empty"); - Assert.notNull(responseConverter, "Response converter must not be null"); - - this.method = method; - this.description = description; - this.mapper = mapper; - this.functionObject = functionObject; - this.name = name; - this.responseConverter = responseConverter; - - Assert.isTrue(this.functionObject != null || Modifier.isStatic(this.method.getModifiers()), - "Function object must be provided for non-static methods!"); - - // Generate the JSON schema from the method input parameters - Map> methodParameters = Stream.of(method.getParameters()) - .collect(Collectors.toMap(param -> param.getName(), param -> param.getType())); - - this.inputSchema = this.generateJsonSchema(methodParameters); - - logger.debug("Generated JSON Schema: {}", this.inputSchema); - } - - @Override - public String getName() { - return org.springframework.util.StringUtils.hasText(this.name) ? this.name : this.method.getName(); - } - - @Override - public String getDescription() { - return this.description; - } - - @Override - public String getInputTypeSchema() { - return this.inputSchema; - } - - @Override - public String call(String functionInput) { - return this.call(functionInput, null); - } - - @Override - public String call(String functionInput, ToolContext toolContext) { - - try { - - // If the toolContext is not empty but the method does not accept ToolContext - // as - // input parameter then throw an exception. - if (toolContext != null && !CollectionUtils.isEmpty(toolContext.getContext()) - && !this.isToolContextMethod) { - throw new IllegalArgumentException("Configured method does not accept ToolContext as input parameter!"); - } - - @SuppressWarnings("unchecked") - Map map = this.mapper.readValue(functionInput, Map.class); - - // ReflectionUtils.findMethod - Object[] methodArgs = Stream.of(this.method.getParameters()).map(parameter -> { - Class type = parameter.getType(); - if (ClassUtils.isAssignable(type, ToolContext.class)) { - return toolContext; - } - Object rawValue = map.get(parameter.getName()); - return this.toJavaType(rawValue, type); - }).toArray(); - - Object response = ReflectionUtils.invokeMethod(this.method, this.functionObject, methodArgs); - - var returnType = this.method.getReturnType(); - if (returnType == Void.TYPE) { - return "Done"; - } - else if (returnType == Class.class || returnType.isRecord() || returnType == List.class - || returnType == Map.class) { - return ModelOptionsUtils.toJsonString(response); - } - - return this.responseConverter.apply(response); - } - catch (Exception e) { - ReflectionUtils.handleReflectionException(e); - return null; - } - } - - /** - * Generates a JSON schema from the given named classes. - * @param namedClasses The named classes to generate the schema from. - * @return The generated JSON schema. - */ - protected String generateJsonSchema(Map> namedClasses) { - try { - JsonSchemaGenerator schemaGen = new JsonSchemaGenerator(this.mapper); - - ObjectNode rootNode = this.mapper.createObjectNode(); - rootNode.put("$schema", "https://json-schema.org/draft/2020-12/schema"); - rootNode.put("type", "object"); - ObjectNode propertiesNode = rootNode.putObject("properties"); - - for (Map.Entry> entry : namedClasses.entrySet()) { - String className = entry.getKey(); - Class clazz = entry.getValue(); - - if (ClassUtils.isAssignable(clazz, ToolContext.class)) { - // Skip the ToolContext class from the schema generation. - this.isToolContextMethod = true; - continue; - } - - JsonSchema schema = schemaGen.generateSchema(clazz); - JsonNode schemaNode = this.mapper.valueToTree(schema); - propertiesNode.set(className, schemaNode); - } - - return this.mapper.writerWithDefaultPrettyPrinter().writeValueAsString(rootNode); - } - catch (Exception e) { - throw new RuntimeException(e); - } - } - - /** - * Converts the given value to the specified Java type. - * @param value The value to convert. - * @param javaType The Java type to convert to. - * @return Returns the converted value. - */ - protected Object toJavaType(Object value, Class javaType) { - - if (value == null) { - return null; - } - - javaType = ClassUtils.resolvePrimitiveIfNecessary(javaType); - - if (javaType == String.class) { - return value.toString(); - } - else if (javaType == Integer.class) { - return Integer.parseInt(value.toString()); - } - else if (javaType == Long.class) { - return Long.parseLong(value.toString()); - } - else if (javaType == Double.class) { - return Double.parseDouble(value.toString()); - } - else if (javaType == Float.class) { - return Float.parseFloat(value.toString()); - } - else if (javaType == Boolean.class) { - return Boolean.parseBoolean(value.toString()); - } - else if (javaType.isEnum()) { - return Enum.valueOf((Class) javaType, value.toString()); - } - - try { - String json = this.mapper.writeValueAsString(value); - return this.mapper.readValue(json, javaType); - } - catch (Exception e) { - throw new RuntimeException(e); - } - } - -} diff --git a/spring-ai-model/src/main/java/org/springframework/ai/model/tool/DefaultToolCallingManager.java b/spring-ai-model/src/main/java/org/springframework/ai/model/tool/DefaultToolCallingManager.java index a3afa93e517..e96b5952817 100644 --- a/spring-ai-model/src/main/java/org/springframework/ai/model/tool/DefaultToolCallingManager.java +++ b/spring-ai-model/src/main/java/org/springframework/ai/model/tool/DefaultToolCallingManager.java @@ -33,7 +33,6 @@ import org.springframework.ai.chat.model.Generation; import org.springframework.ai.chat.model.ToolContext; import org.springframework.ai.chat.prompt.Prompt; -import org.springframework.ai.model.function.FunctionCallback; import org.springframework.ai.tool.ToolCallback; import org.springframework.ai.tool.definition.ToolDefinition; import org.springframework.ai.tool.execution.DefaultToolExecutionExceptionProcessor; @@ -93,7 +92,9 @@ public List resolveToolDefinitions(ToolCallingChatOptions chatOp // Skip the tool if it is already present in the request toolCallbacks. // That might happen if a tool is defined in the options // both as a ToolCallback and as a tool name. - if (chatOptions.getToolCallbacks().stream().anyMatch(tool -> tool.getName().equals(toolName))) { + if (chatOptions.getToolCallbacks() + .stream() + .anyMatch(tool -> tool.getToolDefinition().name().equals(toolName))) { continue; } ToolCallback toolCallback = this.toolCallbackResolver.resolve(toolName); @@ -163,9 +164,7 @@ private static List buildConversationHistoryBeforeToolExecution(Prompt } /** - * Execute the tool call and return the response message. To ensure backward - * compatibility, both {@link ToolCallback} and {@link FunctionCallback} are - * supported. + * Execute the tool call and return the response message. */ private InternalToolExecutionResult executeToolCall(Prompt prompt, AssistantMessage assistantMessage, ToolContext toolContext) { @@ -186,7 +185,7 @@ private InternalToolExecutionResult executeToolCall(Prompt prompt, AssistantMess String toolInputArguments = toolCall.arguments(); ToolCallback toolCallback = toolCallbacks.stream() - .filter(tool -> toolName.equals(tool.getName())) + .filter(tool -> toolName.equals(tool.getToolDefinition().name())) .findFirst() .orElseGet(() -> this.toolCallbackResolver.resolve(toolName)); diff --git a/spring-ai-model/src/main/java/org/springframework/ai/model/tool/ToolExecutionEligibilityChecker.java b/spring-ai-model/src/main/java/org/springframework/ai/model/tool/ToolExecutionEligibilityChecker.java index 031522883e8..6ba92766929 100644 --- a/spring-ai-model/src/main/java/org/springframework/ai/model/tool/ToolExecutionEligibilityChecker.java +++ b/spring-ai-model/src/main/java/org/springframework/ai/model/tool/ToolExecutionEligibilityChecker.java @@ -20,7 +20,6 @@ import org.springframework.ai.chat.model.ChatResponse; import org.springframework.ai.chat.prompt.ChatOptions; -import org.springframework.ai.model.function.FunctionCallingOptions; import org.springframework.util.Assert; /** @@ -69,10 +68,6 @@ default boolean isInternalToolExecutionEnabled(ChatOptions chatOptions) { internalToolExecutionEnabled = Boolean.TRUE .equals(toolCallingChatOptions.getInternalToolExecutionEnabled()); } - else if (chatOptions instanceof FunctionCallingOptions functionCallingOptions - && functionCallingOptions.getProxyToolCalls() != null) { - internalToolExecutionEnabled = Boolean.TRUE.equals(!functionCallingOptions.getProxyToolCalls()); - } else { internalToolExecutionEnabled = true; } diff --git a/spring-ai-model/src/main/java/org/springframework/ai/tool/StaticToolCallbackProvider.java b/spring-ai-model/src/main/java/org/springframework/ai/tool/StaticToolCallbackProvider.java index e4aa55fcbcd..dafb5f62ac1 100644 --- a/spring-ai-model/src/main/java/org/springframework/ai/tool/StaticToolCallbackProvider.java +++ b/spring-ai-model/src/main/java/org/springframework/ai/tool/StaticToolCallbackProvider.java @@ -31,14 +31,14 @@ * *

* Example usage:

{@code
- * FunctionCallback callback1 = new MyFunctionCallback();
- * FunctionCallback callback2 = new AnotherFunctionCallback();
+ * ToolCallback callback1 = new MyFunctionCallback();
+ * ToolCallback callback2 = new AnotherFunctionCallback();
  *
  * // Create provider with varargs constructor
  * ToolCallbackProvider provider1 = new StaticToolCallbackProvider(callback1, callback2);
  *
  * // Or create provider with List constructor
- * List callbacks = Arrays.asList(callback1, callback2);
+ * List callbacks = Arrays.asList(callback1, callback2);
  * ToolCallbackProvider provider2 = new StaticToolCallbackProvider(callbacks);
  * }
* diff --git a/spring-ai-model/src/main/java/org/springframework/ai/tool/ToolCallback.java b/spring-ai-model/src/main/java/org/springframework/ai/tool/ToolCallback.java index 587a51c9c5d..19bc0a56b71 100644 --- a/spring-ai-model/src/main/java/org/springframework/ai/tool/ToolCallback.java +++ b/spring-ai-model/src/main/java/org/springframework/ai/tool/ToolCallback.java @@ -17,7 +17,6 @@ package org.springframework.ai.tool; import org.springframework.ai.chat.model.ToolContext; -import org.springframework.ai.model.function.FunctionCallback; import org.springframework.ai.tool.definition.ToolDefinition; import org.springframework.ai.tool.metadata.ToolMetadata; import org.springframework.lang.Nullable; @@ -28,7 +27,7 @@ * @author Thomas Vitale * @since 1.0.0 */ -public interface ToolCallback extends FunctionCallback { +public interface ToolCallback { /** * Definition used by the AI model to determine when and how to call the tool. @@ -59,22 +58,4 @@ default String call(String toolInput, @Nullable ToolContext tooContext) { return call(toolInput); } - @Override - @Deprecated // Call getToolDefinition().name() instead - default String getName() { - return getToolDefinition().name(); - } - - @Override - @Deprecated // Call getToolDefinition().description() instead - default String getDescription() { - return getToolDefinition().description(); - } - - @Override - @Deprecated // Call getToolDefinition().inputTypeSchema() instead - default String getInputTypeSchema() { - return getToolDefinition().inputSchema(); - } - } diff --git a/spring-ai-model/src/main/java/org/springframework/ai/tool/method/MethodToolCallback.java b/spring-ai-model/src/main/java/org/springframework/ai/tool/method/MethodToolCallback.java index ffc2d0e7542..1d49a456039 100644 --- a/spring-ai-model/src/main/java/org/springframework/ai/tool/method/MethodToolCallback.java +++ b/spring-ai-model/src/main/java/org/springframework/ai/tool/method/MethodToolCallback.java @@ -129,7 +129,7 @@ private Map extractToolArguments(String toolInput) { }); } - // Based on the implementation in MethodInvokingFunctionCallback. + // Based on the implementation in MethodToolCallback. private Object[] buildMethodArguments(Map toolInputArguments, @Nullable ToolContext toolContext) { return Stream.of(this.toolMethod.getParameters()).map(parameter -> { if (parameter.getType().isAssignableFrom(ToolContext.class)) { diff --git a/spring-ai-model/src/main/java/org/springframework/ai/tool/resolution/ToolCallbackResolver.java b/spring-ai-model/src/main/java/org/springframework/ai/tool/resolution/ToolCallbackResolver.java index 259be087309..8efa01e9ccd 100644 --- a/spring-ai-model/src/main/java/org/springframework/ai/tool/resolution/ToolCallbackResolver.java +++ b/spring-ai-model/src/main/java/org/springframework/ai/tool/resolution/ToolCallbackResolver.java @@ -16,7 +16,6 @@ package org.springframework.ai.tool.resolution; -import org.springframework.ai.model.function.FunctionCallback; import org.springframework.ai.tool.ToolCallback; import org.springframework.lang.Nullable; @@ -29,7 +28,7 @@ public interface ToolCallbackResolver { /** - * Resolve the {@link FunctionCallback} for the given tool name. + * Resolve the {@link ToolCallback} for the given tool name. */ @Nullable ToolCallback resolve(String toolName); diff --git a/spring-ai-model/src/main/java/org/springframework/ai/util/json/JsonParser.java b/spring-ai-model/src/main/java/org/springframework/ai/util/json/JsonParser.java index 88452dbb0e9..9361f8705c1 100644 --- a/spring-ai-model/src/main/java/org/springframework/ai/util/json/JsonParser.java +++ b/spring-ai-model/src/main/java/org/springframework/ai/util/json/JsonParser.java @@ -112,7 +112,7 @@ public static String toJson(@Nullable Object object) { /** * Convert a Java Object to a typed Object. Based on the implementation in - * MethodInvokingFunctionCallback. + * MethodToolCallback. */ @SuppressWarnings({ "rawtypes", "unchecked" }) public static Object toTypedObject(Object value, Class type) { diff --git a/spring-ai-model/src/main/kotlin/org/springframework/ai/model/function/FunctionCallbackExtensions.kt b/spring-ai-model/src/main/kotlin/org/springframework/ai/model/function/FunctionCallbackExtensions.kt deleted file mode 100644 index 3201ea9a3da..00000000000 --- a/spring-ai-model/src/main/kotlin/org/springframework/ai/model/function/FunctionCallbackExtensions.kt +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright 2023-2024 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.ai.model.function - -import org.springframework.core.ParameterizedTypeReference - -/** - * Extension for [FunctionCallback.FunctionInvokerBuilder.inputType] providing a `inputType()` - * variant. - * - * @author Sebastien Deleuze - */ -inline fun FunctionCallback.FunctionInvokingSpec.inputType(): FunctionCallback.FunctionInvokingSpec = - inputType(I::class.java) - diff --git a/spring-ai-model/src/test/java/org/springframework/ai/aot/SpringAiCoreRuntimeHintsTest.java b/spring-ai-model/src/test/java/org/springframework/ai/aot/SpringAiCoreRuntimeHintsTest.java index b379c9bb159..c39629d209c 100644 --- a/spring-ai-model/src/test/java/org/springframework/ai/aot/SpringAiCoreRuntimeHintsTest.java +++ b/spring-ai-model/src/test/java/org/springframework/ai/aot/SpringAiCoreRuntimeHintsTest.java @@ -18,11 +18,9 @@ import org.junit.jupiter.api.Test; -import org.springframework.ai.model.function.FunctionCallback; import org.springframework.aot.hint.RuntimeHints; import static org.assertj.core.api.Assertions.assertThat; -import static org.springframework.aot.hint.predicate.RuntimeHintsPredicates.reflection; import static org.springframework.aot.hint.predicate.RuntimeHintsPredicates.resource; class SpringAiCoreRuntimeHintsTest { @@ -33,10 +31,6 @@ void core() { var springAiCore = new SpringAiCoreRuntimeHints(); springAiCore.registerHints(runtimeHints, null); assertThat(runtimeHints).matches(resource().forResource("embedding/embedding-model-dimensions.properties")); - - assertThat(runtimeHints).matches(reflection().onMethod(FunctionCallback.class, "getDescription")); - assertThat(runtimeHints).matches(reflection().onMethod(FunctionCallback.class, "getInputTypeSchema")); - assertThat(runtimeHints).matches(reflection().onMethod(FunctionCallback.class, "getName")); } } diff --git a/spring-ai-model/src/test/java/org/springframework/ai/model/tool/DefaultToolExecutionEligibilityPredicateTests.java b/spring-ai-model/src/test/java/org/springframework/ai/model/tool/DefaultToolExecutionEligibilityPredicateTests.java index 37ca8b7168a..8b92a3fad79 100644 --- a/spring-ai-model/src/test/java/org/springframework/ai/model/tool/DefaultToolExecutionEligibilityPredicateTests.java +++ b/spring-ai-model/src/test/java/org/springframework/ai/model/tool/DefaultToolExecutionEligibilityPredicateTests.java @@ -25,7 +25,6 @@ import org.springframework.ai.chat.model.ChatResponse; import org.springframework.ai.chat.model.Generation; import org.springframework.ai.chat.prompt.ChatOptions; -import org.springframework.ai.model.function.FunctionCallingOptions; import static org.assertj.core.api.Assertions.assertThat; @@ -96,26 +95,9 @@ void whenToolExecutionDisabledAndNoToolCalls() { assertThat(result).isFalse(); } - @Test - void whenFunctionCallingOptionsAndToolExecutionEnabled() { - // Create a FunctionCallingOptions with proxy tool calls disabled (which means - // internal tool execution is enabled) - FunctionCallingOptions options = FunctionCallingOptions.builder().proxyToolCalls(false).build(); - - // Create a ChatResponse with tool calls - AssistantMessage.ToolCall toolCall = new AssistantMessage.ToolCall("id1", "function", "testTool", "{}"); - AssistantMessage assistantMessage = new AssistantMessage("test", Map.of(), List.of(toolCall)); - ChatResponse chatResponse = new ChatResponse(List.of(new Generation(assistantMessage))); - - // Test the predicate - boolean result = this.predicate.test(options, chatResponse); - assertThat(result).isTrue(); - } - @Test void whenRegularChatOptionsAndHasToolCalls() { - // Create regular ChatOptions (not ToolCallingChatOptions or - // FunctionCallingOptions) + // Create regular ChatOptions (not ToolCallingChatOptions) ChatOptions options = ChatOptions.builder().build(); // Create a ChatResponse with tool calls diff --git a/spring-ai-model/src/test/java/org/springframework/ai/model/tool/ToolCallingChatOptionsTests.java b/spring-ai-model/src/test/java/org/springframework/ai/model/tool/ToolCallingChatOptionsTests.java index 62645ba84ae..c81de4c6880 100644 --- a/spring-ai-model/src/test/java/org/springframework/ai/model/tool/ToolCallingChatOptionsTests.java +++ b/spring-ai-model/src/test/java/org/springframework/ai/model/tool/ToolCallingChatOptionsTests.java @@ -22,7 +22,6 @@ import org.junit.jupiter.api.Test; -import org.springframework.ai.model.function.FunctionCallingOptions; import org.springframework.ai.tool.ToolCallback; import org.springframework.ai.tool.definition.ToolDefinition; @@ -56,13 +55,6 @@ void whenToolCallingChatOptionsAndExecutionEnabledDefault() { assertThat(ToolCallingChatOptions.isInternalToolExecutionEnabled(options)).isTrue(); } - @Test - void whenFunctionCallingOptionsAndExecutionEnabledTrue() { - FunctionCallingOptions options = FunctionCallingOptions.builder().build(); - options.setProxyToolCalls(false); - assertThat(ToolCallingChatOptions.isInternalToolExecutionEnabled(options)).isTrue(); - } - @Test void whenMergeRuntimeAndDefaultToolNames() { Set runtimeToolNames = Set.of("toolA"); @@ -102,7 +94,7 @@ void whenMergeRuntimeAndDefaultToolCallbacks() { List mergedToolCallbacks = ToolCallingChatOptions.mergeToolCallbacks(runtimeToolCallbacks, defaultToolCallbacks); assertThat(mergedToolCallbacks).hasSize(1); - assertThat(mergedToolCallbacks.get(0).getName()).isEqualTo("toolA"); + assertThat(mergedToolCallbacks.get(0).getToolDefinition().name()).isEqualTo("toolA"); } @Test @@ -112,7 +104,7 @@ void whenMergeRuntimeAndEmptyDefaultToolCallbacks() { List mergedToolCallbacks = ToolCallingChatOptions.mergeToolCallbacks(runtimeToolCallbacks, defaultToolCallbacks); assertThat(mergedToolCallbacks).hasSize(1); - assertThat(mergedToolCallbacks.get(0).getName()).isEqualTo("toolA"); + assertThat(mergedToolCallbacks.get(0).getToolDefinition().name()).isEqualTo("toolA"); } @Test @@ -122,7 +114,7 @@ void whenMergeEmptyRuntimeAndDefaultToolCallbacks() { List mergedToolCallbacks = ToolCallingChatOptions.mergeToolCallbacks(runtimeToolCallbacks, defaultToolCallbacks); assertThat(mergedToolCallbacks).hasSize(1); - assertThat(mergedToolCallbacks.get(0).getName()).isEqualTo("toolB"); + assertThat(mergedToolCallbacks.get(0).getToolDefinition().name()).isEqualTo("toolB"); } @Test diff --git a/spring-ai-model/src/test/kotlin/org/springframework/ai/tool/resolution/FunctionCallbackExtensionsTests.kt b/spring-ai-model/src/test/kotlin/org/springframework/ai/tool/resolution/FunctionCallbackExtensionsTests.kt deleted file mode 100644 index b32e0a4db25..00000000000 --- a/spring-ai-model/src/test/kotlin/org/springframework/ai/tool/resolution/FunctionCallbackExtensionsTests.kt +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2023-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.ai.tool.resolution - -import io.mockk.every -import io.mockk.mockk -import io.mockk.verify -import org.junit.jupiter.api.Test -import org.springframework.ai.model.function.FunctionCallback -import org.springframework.ai.model.function.inputType - -class FunctionCallbackExtensionsTests { - - private val spec = mockk>() - - @Test - fun inputType() { - every { spec.inputType(any>()) } returns spec - spec.inputType() - verify { spec.inputType(WeatherRequest::class.java) } - } -} diff --git a/src/checkstyle/checkstyle-suppressions.xml b/src/checkstyle/checkstyle-suppressions.xml index 6d1f023c2cb..8569e8d4bda 100644 --- a/src/checkstyle/checkstyle-suppressions.xml +++ b/src/checkstyle/checkstyle-suppressions.xml @@ -31,8 +31,6 @@ - -