From 570cd9a7cc577ba946aeb9768a201690f1147d84 Mon Sep 17 00:00:00 2001 From: Mark Pollack Date: Thu, 8 May 2025 02:39:54 -0400 Subject: [PATCH] refactor: remove inter package dependency cycles in spring-ai-model Remove circular dependencies: Move utility classes from various packages to dedicated support packages: - Move ToolCallbacks from ai.tool to ai.support - Move UsageUtils to UsageCalculator in ai.support - Move tool.util to tool.support - Create new ToolDefinitions utility class Support packages for the classes in question is more idomatic in spring than util packages. Signed-off-by: Mark Pollack --- .../tool/OllamaFunctionToolBeanIT.java | 2 +- .../ToolCallingAutoConfigurationTests.java | 4 +- .../ai/mcp/AsyncMcpToolCallback.java | 3 +- .../ai/mcp/AsyncMcpToolCallbackProvider.java | 2 +- .../ai/mcp/SyncMcpToolCallback.java | 3 +- .../ai/mcp/SyncMcpToolCallbackProvider.java | 3 +- .../ai/mcp/ToolUtilsTests.java | 5 +- .../ai/anthropic/AnthropicChatModel.java | 7 ++- ...lientMethodInvokingFunctionCallbackIT.java | 16 +++--- .../ai/azure/openai/AzureOpenAiChatModel.java | 9 +-- .../ai/deepseek/DeepSeekChatModel.java | 6 +- .../ai/mistralai/MistralAiChatModel.java | 6 +- .../MistralAiChatCompletionRequestTest.java | 3 +- .../ai/mistralai/MistralAiChatModelIT.java | 2 +- .../ai/ollama/OllamaChatModelIT.java | 2 +- .../ai/ollama/OllamaChatRequestTests.java | 3 +- .../ai/openai/OpenAiChatModel.java | 9 +-- .../ai/openai/ChatCompletionRequestTests.java | 3 +- .../ai/openai/chat/OpenAiChatModelIT.java | 2 +- ...lientMethodInvokingFunctionCallbackIT.java | 14 ++--- .../gemini/VertexAiGeminiChatModel.java | 6 +- .../schema/VertexToolCallingManager.java | 3 +- ...texAiGeminiPaymentTransactionMethodIT.java | 2 +- .../ai/chat/client/DefaultChatClient.java | 9 +-- .../client/DefaultChatClientUtilsTests.java | 5 +- ...tChatClientObservationConventionTests.java | 3 +- .../tests/tool/MethodToolCallbackTests.java | 2 +- .../tests/tool/ToolCallingManagerTests.java | 2 +- .../ai/model/tool/ToolCallingChatOptions.java | 2 +- .../ai/{tool => support}/ToolCallbacks.java | 3 +- .../UsageCalculator.java} | 8 ++- .../definition/DefaultToolDefinition.java | 6 +- .../ai/tool/definition/ToolDefinition.java | 31 ---------- .../tool/function/FunctionToolCallback.java | 5 +- .../ai/tool/metadata/ToolMetadata.java | 2 +- .../method/MethodToolCallbackProvider.java | 7 +-- .../SpringBeanToolCallbackResolver.java | 2 +- .../ai/tool/support/ToolDefinitions.java | 57 +++++++++++++++++++ .../ai/tool/{util => support}/ToolUtils.java | 2 +- .../tool/{util => support}/package-info.java | 2 +- .../tool/DefaultToolCallingManagerTests.java | 7 ++- .../tool/ToolCallingChatOptionsTests.java | 3 +- 42 files changed, 156 insertions(+), 117 deletions(-) rename spring-ai-model/src/main/java/org/springframework/ai/{tool => support}/ToolCallbacks.java (92%) rename spring-ai-model/src/main/java/org/springframework/ai/{chat/metadata/UsageUtils.java => support/UsageCalculator.java} (93%) create mode 100644 spring-ai-model/src/main/java/org/springframework/ai/tool/support/ToolDefinitions.java rename spring-ai-model/src/main/java/org/springframework/ai/tool/{util => support}/ToolUtils.java (98%) rename spring-ai-model/src/main/java/org/springframework/ai/tool/{util => support}/package-info.java (94%) diff --git a/auto-configurations/models/spring-ai-autoconfigure-model-ollama/src/test/java/org/springframework/ai/model/ollama/autoconfigure/tool/OllamaFunctionToolBeanIT.java b/auto-configurations/models/spring-ai-autoconfigure-model-ollama/src/test/java/org/springframework/ai/model/ollama/autoconfigure/tool/OllamaFunctionToolBeanIT.java index a565870131b..837129a4868 100644 --- a/auto-configurations/models/spring-ai-autoconfigure-model-ollama/src/test/java/org/springframework/ai/model/ollama/autoconfigure/tool/OllamaFunctionToolBeanIT.java +++ b/auto-configurations/models/spring-ai-autoconfigure-model-ollama/src/test/java/org/springframework/ai/model/ollama/autoconfigure/tool/OllamaFunctionToolBeanIT.java @@ -36,7 +36,7 @@ import org.springframework.ai.model.tool.ToolCallingChatOptions; import org.springframework.ai.ollama.OllamaChatModel; import org.springframework.ai.ollama.api.OllamaOptions; -import org.springframework.ai.tool.ToolCallbacks; +import org.springframework.ai.support.ToolCallbacks; import org.springframework.ai.tool.annotation.Tool; import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.test.context.runner.ApplicationContextRunner; 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 497c3e78709..8df23b89ac7 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 @@ -26,7 +26,6 @@ import org.springframework.ai.tool.ToolCallback; import org.springframework.ai.tool.ToolCallbackProvider; import org.springframework.ai.tool.annotation.Tool; -import org.springframework.ai.tool.definition.ToolDefinition; import org.springframework.ai.tool.execution.DefaultToolExecutionExceptionProcessor; import org.springframework.ai.tool.execution.ToolExecutionExceptionProcessor; import org.springframework.ai.tool.function.FunctionToolCallback; @@ -34,6 +33,7 @@ import org.springframework.ai.tool.method.MethodToolCallbackProvider; import org.springframework.ai.tool.resolution.DelegatingToolCallbackResolver; import org.springframework.ai.tool.resolution.ToolCallbackResolver; +import org.springframework.ai.tool.support.ToolDefinitions; import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.test.context.runner.ApplicationContextRunner; import org.springframework.context.annotation.Bean; @@ -175,7 +175,7 @@ public ToolCallbackProvider blabla() { public ToolCallback toolCallbacks6() { var toolMethod = ReflectionUtils.findMethod(WeatherService.class, "getAlert", String.class); return MethodToolCallback.builder() - .toolDefinition(ToolDefinition.builder(toolMethod).build()) + .toolDefinition(ToolDefinitions.builder(toolMethod).build()) .toolMethod(toolMethod) .toolObject(new WeatherService()) .build(); diff --git a/mcp/common/src/main/java/org/springframework/ai/mcp/AsyncMcpToolCallback.java b/mcp/common/src/main/java/org/springframework/ai/mcp/AsyncMcpToolCallback.java index 812f82cba25..d54e1a8b12a 100644 --- a/mcp/common/src/main/java/org/springframework/ai/mcp/AsyncMcpToolCallback.java +++ b/mcp/common/src/main/java/org/springframework/ai/mcp/AsyncMcpToolCallback.java @@ -25,6 +25,7 @@ import org.springframework.ai.chat.model.ToolContext; import org.springframework.ai.model.ModelOptionsUtils; import org.springframework.ai.tool.ToolCallback; +import org.springframework.ai.tool.definition.DefaultToolDefinition; import org.springframework.ai.tool.definition.ToolDefinition; /** @@ -84,7 +85,7 @@ public AsyncMcpToolCallback(McpAsyncClient mcpClient, Tool tool) { */ @Override public ToolDefinition getToolDefinition() { - return ToolDefinition.builder() + return DefaultToolDefinition.builder() .name(McpToolUtils.prefixedToolName(this.asyncMcpClient.getClientInfo().name(), this.tool.name())) .description(this.tool.description()) .inputSchema(ModelOptionsUtils.toJsonString(this.tool.inputSchema())) diff --git a/mcp/common/src/main/java/org/springframework/ai/mcp/AsyncMcpToolCallbackProvider.java b/mcp/common/src/main/java/org/springframework/ai/mcp/AsyncMcpToolCallbackProvider.java index 1b6a191156c..3525b9593e3 100644 --- a/mcp/common/src/main/java/org/springframework/ai/mcp/AsyncMcpToolCallbackProvider.java +++ b/mcp/common/src/main/java/org/springframework/ai/mcp/AsyncMcpToolCallbackProvider.java @@ -27,7 +27,7 @@ import org.springframework.ai.tool.ToolCallback; import org.springframework.ai.tool.ToolCallbackProvider; -import org.springframework.ai.tool.util.ToolUtils; +import org.springframework.ai.tool.support.ToolUtils; import org.springframework.util.CollectionUtils; /** diff --git a/mcp/common/src/main/java/org/springframework/ai/mcp/SyncMcpToolCallback.java b/mcp/common/src/main/java/org/springframework/ai/mcp/SyncMcpToolCallback.java index aa51e1f3681..441b3d4d508 100644 --- a/mcp/common/src/main/java/org/springframework/ai/mcp/SyncMcpToolCallback.java +++ b/mcp/common/src/main/java/org/springframework/ai/mcp/SyncMcpToolCallback.java @@ -26,6 +26,7 @@ import org.springframework.ai.chat.model.ToolContext; import org.springframework.ai.model.ModelOptionsUtils; import org.springframework.ai.tool.ToolCallback; +import org.springframework.ai.tool.definition.DefaultToolDefinition; import org.springframework.ai.tool.definition.ToolDefinition; /** @@ -88,7 +89,7 @@ public SyncMcpToolCallback(McpSyncClient mcpClient, Tool tool) { */ @Override public ToolDefinition getToolDefinition() { - return ToolDefinition.builder() + return DefaultToolDefinition.builder() .name(McpToolUtils.prefixedToolName(this.mcpClient.getClientInfo().name(), this.tool.name())) .description(this.tool.description()) .inputSchema(ModelOptionsUtils.toJsonString(this.tool.inputSchema())) diff --git a/mcp/common/src/main/java/org/springframework/ai/mcp/SyncMcpToolCallbackProvider.java b/mcp/common/src/main/java/org/springframework/ai/mcp/SyncMcpToolCallbackProvider.java index bdcf595ff3e..7d0aa4276a1 100644 --- a/mcp/common/src/main/java/org/springframework/ai/mcp/SyncMcpToolCallbackProvider.java +++ b/mcp/common/src/main/java/org/springframework/ai/mcp/SyncMcpToolCallbackProvider.java @@ -16,7 +16,6 @@ package org.springframework.ai.mcp; -import java.util.ArrayList; import java.util.List; import java.util.function.BiPredicate; @@ -25,7 +24,7 @@ import org.springframework.ai.tool.ToolCallback; import org.springframework.ai.tool.ToolCallbackProvider; -import org.springframework.ai.tool.util.ToolUtils; +import org.springframework.ai.tool.support.ToolUtils; import org.springframework.util.Assert; import org.springframework.util.CollectionUtils; diff --git a/mcp/common/src/test/java/org/springframework/ai/mcp/ToolUtilsTests.java b/mcp/common/src/test/java/org/springframework/ai/mcp/ToolUtilsTests.java index bc0d8f89c41..2bcbe305c5d 100644 --- a/mcp/common/src/test/java/org/springframework/ai/mcp/ToolUtilsTests.java +++ b/mcp/common/src/test/java/org/springframework/ai/mcp/ToolUtilsTests.java @@ -35,6 +35,7 @@ import reactor.test.StepVerifier; import org.springframework.ai.tool.ToolCallback; +import org.springframework.ai.tool.definition.DefaultToolDefinition; import org.springframework.ai.tool.definition.ToolDefinition; import static org.assertj.core.api.Assertions.assertThat; @@ -193,7 +194,7 @@ void toAsyncToolSpecificationShouldConvertMultipleCallbacks() { private ToolCallback createMockToolCallback(String name, String result) { ToolCallback callback = mock(ToolCallback.class); - ToolDefinition definition = ToolDefinition.builder() + ToolDefinition definition = DefaultToolDefinition.builder() .name(name) .description("Test tool") .inputSchema("{}") @@ -205,7 +206,7 @@ private ToolCallback createMockToolCallback(String name, String result) { private ToolCallback createMockToolCallback(String name, RuntimeException error) { ToolCallback callback = mock(ToolCallback.class); - ToolDefinition definition = ToolDefinition.builder() + ToolDefinition definition = DefaultToolDefinition.builder() .name(name) .description("Test tool") .inputSchema("{}") diff --git a/models/spring-ai-anthropic/src/main/java/org/springframework/ai/anthropic/AnthropicChatModel.java b/models/spring-ai-anthropic/src/main/java/org/springframework/ai/anthropic/AnthropicChatModel.java index 568a922fe8a..4c5d74a3239 100644 --- a/models/spring-ai-anthropic/src/main/java/org/springframework/ai/anthropic/AnthropicChatModel.java +++ b/models/spring-ai-anthropic/src/main/java/org/springframework/ai/anthropic/AnthropicChatModel.java @@ -51,7 +51,7 @@ import org.springframework.ai.chat.metadata.DefaultUsage; import org.springframework.ai.chat.metadata.EmptyUsage; import org.springframework.ai.chat.metadata.Usage; -import org.springframework.ai.chat.metadata.UsageUtils; +import org.springframework.ai.support.UsageCalculator; import org.springframework.ai.chat.model.ChatModel; import org.springframework.ai.chat.model.ChatResponse; import org.springframework.ai.chat.model.Generation; @@ -194,7 +194,8 @@ public ChatResponse internalCall(Prompt prompt, ChatResponse previousChatRespons Usage currentChatResponseUsage = usage != null ? this.getDefaultUsage(completionResponse.usage()) : new EmptyUsage(); - Usage accumulatedUsage = UsageUtils.getCumulativeUsage(currentChatResponseUsage, previousChatResponse); + Usage accumulatedUsage = UsageCalculator.getCumulativeUsage(currentChatResponseUsage, + previousChatResponse); ChatResponse chatResponse = toChatResponse(completionEntity.getBody(), accumulatedUsage); observationContext.setResponse(chatResponse); @@ -256,7 +257,7 @@ public Flux internalStream(Prompt prompt, ChatResponse previousCha Flux chatResponseFlux = response.switchMap(chatCompletionResponse -> { AnthropicApi.Usage usage = chatCompletionResponse.usage(); Usage currentChatResponseUsage = usage != null ? this.getDefaultUsage(chatCompletionResponse.usage()) : new EmptyUsage(); - Usage accumulatedUsage = UsageUtils.getCumulativeUsage(currentChatResponseUsage, previousChatResponse); + Usage accumulatedUsage = UsageCalculator.getCumulativeUsage(currentChatResponseUsage, previousChatResponse); ChatResponse chatResponse = toChatResponse(chatCompletionResponse, accumulatedUsage); if (this.toolExecutionEligibilityPredicate.isToolExecutionRequired(prompt.getOptions(), chatResponse) && chatResponse.hasFinishReasons(Set.of("tool_use"))) { diff --git a/models/spring-ai-anthropic/src/test/java/org/springframework/ai/anthropic/client/AnthropicChatClientMethodInvokingFunctionCallbackIT.java b/models/spring-ai-anthropic/src/test/java/org/springframework/ai/anthropic/client/AnthropicChatClientMethodInvokingFunctionCallbackIT.java index cb3f4fdb6bc..09f10e889a9 100644 --- a/models/spring-ai-anthropic/src/test/java/org/springframework/ai/anthropic/client/AnthropicChatClientMethodInvokingFunctionCallbackIT.java +++ b/models/spring-ai-anthropic/src/test/java/org/springframework/ai/anthropic/client/AnthropicChatClientMethodInvokingFunctionCallbackIT.java @@ -32,8 +32,8 @@ import org.springframework.ai.chat.model.ChatModel; import org.springframework.ai.chat.model.ToolContext; import org.springframework.ai.tool.annotation.Tool; -import org.springframework.ai.tool.definition.ToolDefinition; import org.springframework.ai.tool.method.MethodToolCallback; +import org.springframework.ai.tool.support.ToolDefinitions; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.ActiveProfiles; @@ -68,7 +68,7 @@ void methodGetWeatherGeneratedDescription() { String response = ChatClient.create(this.chatModel).prompt() .user("What's the weather like in San Francisco, Tokyo, and Paris? Use Celsius.") .toolCallbacks(MethodToolCallback.builder() - .toolDefinition(ToolDefinition.builder(toolMethod).build()) + .toolDefinition(ToolDefinitions.builder(toolMethod).build()) .toolMethod(toolMethod) .build()) .call() @@ -90,7 +90,7 @@ void methodGetWeatherStatic() { String response = ChatClient.create(this.chatModel).prompt() .user("What's the weather like in San Francisco, Tokyo, and Paris? Use Celsius.") .toolCallbacks(MethodToolCallback.builder() - .toolDefinition(ToolDefinition.builder(toolMethod) + .toolDefinition(ToolDefinitions.builder(toolMethod) .description("Get the weather in location") .build()) .toolMethod(toolMethod) @@ -117,7 +117,7 @@ void methodTurnLightNoResponse() { String response = ChatClient.create(this.chatModel).prompt() .user("Turn light on in the living room.") .toolCallbacks(MethodToolCallback.builder() - .toolDefinition(ToolDefinition.builder(turnLightMethod) + .toolDefinition(ToolDefinitions.builder(turnLightMethod) .description("Turn light on in the living room.") .build()) .toolMethod(turnLightMethod) @@ -145,7 +145,7 @@ void methodGetWeatherNonStatic() { String response = ChatClient.create(this.chatModel).prompt() .user("What's the weather like in San Francisco, Tokyo, and Paris? Use Celsius.") .toolCallbacks(MethodToolCallback.builder() - .toolDefinition(ToolDefinition.builder(toolMethod) + .toolDefinition(ToolDefinitions.builder(toolMethod) .description("Get the weather in location") .build()) .toolMethod(toolMethod) @@ -172,7 +172,7 @@ void methodGetWeatherToolContext() { String response = ChatClient.create(this.chatModel).prompt() .user("What's the weather like in San Francisco, Tokyo, and Paris? Use Celsius.") .toolCallbacks(MethodToolCallback.builder() - .toolDefinition(ToolDefinition.builder(toolMethod) + .toolDefinition(ToolDefinitions.builder(toolMethod) .description("Get the weather in location") .build()) .toolMethod(toolMethod) @@ -203,7 +203,7 @@ void methodGetWeatherWithContextMethodButMissingContext() { assertThatThrownBy(() -> ChatClient.create(this.chatModel).prompt() .user("What's the weather like in San Francisco, Tokyo, and Paris? Use Celsius.") .toolCallbacks(MethodToolCallback.builder() - .toolDefinition(ToolDefinition.builder(toolMethod) + .toolDefinition(ToolDefinitions.builder(toolMethod) .description("Get the weather in location") .build()) .toolMethod(toolMethod) @@ -229,7 +229,7 @@ void methodNoParameters() { .user("Turn light on in the living room.") .toolCallbacks(MethodToolCallback.builder() .toolMethod(toolMethod) - .toolDefinition(ToolDefinition.builder(toolMethod) + .toolDefinition(ToolDefinitions.builder(toolMethod) .description("Can turn lights on in the Living Room") .build()) .toolObject(targetObject) diff --git a/models/spring-ai-azure-openai/src/main/java/org/springframework/ai/azure/openai/AzureOpenAiChatModel.java b/models/spring-ai-azure-openai/src/main/java/org/springframework/ai/azure/openai/AzureOpenAiChatModel.java index a94115a9b99..514091ee6f9 100644 --- a/models/spring-ai-azure-openai/src/main/java/org/springframework/ai/azure/openai/AzureOpenAiChatModel.java +++ b/models/spring-ai-azure-openai/src/main/java/org/springframework/ai/azure/openai/AzureOpenAiChatModel.java @@ -77,7 +77,7 @@ import org.springframework.ai.chat.metadata.PromptMetadata; import org.springframework.ai.chat.metadata.PromptMetadata.PromptFilterMetadata; import org.springframework.ai.chat.metadata.Usage; -import org.springframework.ai.chat.metadata.UsageUtils; +import org.springframework.ai.support.UsageCalculator; import org.springframework.ai.chat.model.ChatModel; import org.springframework.ai.chat.model.ChatResponse; import org.springframework.ai.chat.model.Generation; @@ -357,7 +357,8 @@ public Flux internalStream(Prompt prompt, ChatResponse previousCha // Accumulate the usage from the previous chat response CompletionsUsage usage = chatCompletion.getUsage(); Usage currentChatResponseUsage = usage != null ? getDefaultUsage(usage) : new EmptyUsage(); - Usage accumulatedUsage = UsageUtils.getCumulativeUsage(currentChatResponseUsage, previousChatResponse); + Usage accumulatedUsage = UsageCalculator.getCumulativeUsage(currentChatResponseUsage, + previousChatResponse); return toChatResponse(chatCompletion, accumulatedUsage); }).buffer(2, 1).map(bufferList -> { ChatResponse chatResponse1 = bufferList.get(0); @@ -365,7 +366,7 @@ public Flux internalStream(Prompt prompt, ChatResponse previousCha if (bufferList.size() == 2) { ChatResponse chatResponse2 = bufferList.get(1); if (chatResponse2 != null && chatResponse2.getMetadata() != null - && !UsageUtils.isEmpty(chatResponse2.getMetadata().getUsage())) { + && !UsageCalculator.isEmpty(chatResponse2.getMetadata().getUsage())) { return toChatResponse(chatResponse1, chatResponse2.getMetadata().getUsage()); } } @@ -462,7 +463,7 @@ private ChatResponse toChatResponse(ChatCompletions chatCompletions, ChatRespons if (chatCompletions.getUsage() != null) { currentUsage = getDefaultUsage(chatCompletions.getUsage()); } - Usage cumulativeUsage = UsageUtils.getCumulativeUsage(currentUsage, previousChatResponse); + Usage cumulativeUsage = UsageCalculator.getCumulativeUsage(currentUsage, previousChatResponse); return new ChatResponse(generations, from(chatCompletions, promptFilterMetadata, cumulativeUsage)); } diff --git a/models/spring-ai-deepseek/src/main/java/org/springframework/ai/deepseek/DeepSeekChatModel.java b/models/spring-ai-deepseek/src/main/java/org/springframework/ai/deepseek/DeepSeekChatModel.java index 0d893bc2bcf..ffce033a421 100644 --- a/models/spring-ai-deepseek/src/main/java/org/springframework/ai/deepseek/DeepSeekChatModel.java +++ b/models/spring-ai-deepseek/src/main/java/org/springframework/ai/deepseek/DeepSeekChatModel.java @@ -43,6 +43,7 @@ import org.springframework.ai.model.ModelOptionsUtils; import org.springframework.ai.model.tool.*; import org.springframework.ai.retry.RetryUtils; +import org.springframework.ai.support.UsageCalculator; import org.springframework.ai.tool.definition.ToolDefinition; import org.springframework.http.ResponseEntity; import org.springframework.retry.support.RetryTemplate; @@ -179,7 +180,8 @@ public ChatResponse internalCall(Prompt prompt, ChatResponse previousChatRespons // Current usage DeepSeekApi.Usage usage = completionEntity.getBody().usage(); Usage currentChatResponseUsage = usage != null ? getDefaultUsage(usage) : new EmptyUsage(); - Usage accumulatedUsage = UsageUtils.getCumulativeUsage(currentChatResponseUsage, previousChatResponse); + Usage accumulatedUsage = UsageCalculator.getCumulativeUsage(currentChatResponseUsage, + previousChatResponse); ChatResponse chatResponse = new ChatResponse(generations, from(completionEntity.getBody(), accumulatedUsage)); @@ -256,7 +258,7 @@ public Flux internalStream(Prompt prompt, ChatResponse previousCha }).toList(); DeepSeekApi.Usage usage = chatCompletion2.usage(); Usage currentUsage = (usage != null) ? getDefaultUsage(usage) : new EmptyUsage(); - Usage cumulativeUsage = UsageUtils.getCumulativeUsage(currentUsage, previousChatResponse); + Usage cumulativeUsage = UsageCalculator.getCumulativeUsage(currentUsage, previousChatResponse); return new ChatResponse(generations, from(chatCompletion2, cumulativeUsage)); } diff --git a/models/spring-ai-mistral-ai/src/main/java/org/springframework/ai/mistralai/MistralAiChatModel.java b/models/spring-ai-mistral-ai/src/main/java/org/springframework/ai/mistralai/MistralAiChatModel.java index 738e2640dcf..eee07597c7f 100644 --- a/models/spring-ai-mistral-ai/src/main/java/org/springframework/ai/mistralai/MistralAiChatModel.java +++ b/models/spring-ai-mistral-ai/src/main/java/org/springframework/ai/mistralai/MistralAiChatModel.java @@ -39,7 +39,7 @@ import org.springframework.ai.chat.metadata.ChatResponseMetadata; import org.springframework.ai.chat.metadata.DefaultUsage; import org.springframework.ai.chat.metadata.Usage; -import org.springframework.ai.chat.metadata.UsageUtils; +import org.springframework.ai.support.UsageCalculator; import org.springframework.ai.chat.model.ChatModel; import org.springframework.ai.chat.model.ChatResponse; import org.springframework.ai.chat.model.Generation; @@ -216,7 +216,7 @@ public ChatResponse internalCall(Prompt prompt, ChatResponse previousChatRespons }).toList(); DefaultUsage usage = getDefaultUsage(completionEntity.getBody().usage()); - Usage cumulativeUsage = UsageUtils.getCumulativeUsage(usage, previousChatResponse); + Usage cumulativeUsage = UsageCalculator.getCumulativeUsage(usage, previousChatResponse); ChatResponse chatResponse = new ChatResponse(generations, from(completionEntity.getBody(), cumulativeUsage)); @@ -298,7 +298,7 @@ public Flux internalStream(Prompt prompt, ChatResponse previousCha if (chatCompletion2.usage() != null) { DefaultUsage usage = getDefaultUsage(chatCompletion2.usage()); - Usage cumulativeUsage = UsageUtils.getCumulativeUsage(usage, previousChatResponse); + Usage cumulativeUsage = UsageCalculator.getCumulativeUsage(usage, previousChatResponse); return new ChatResponse(generations, from(chatCompletion2, cumulativeUsage)); } else { diff --git a/models/spring-ai-mistral-ai/src/test/java/org/springframework/ai/mistralai/MistralAiChatCompletionRequestTest.java b/models/spring-ai-mistral-ai/src/test/java/org/springframework/ai/mistralai/MistralAiChatCompletionRequestTest.java index d937609e76c..e6bf2490cc0 100644 --- a/models/spring-ai-mistral-ai/src/test/java/org/springframework/ai/mistralai/MistralAiChatCompletionRequestTest.java +++ b/models/spring-ai-mistral-ai/src/test/java/org/springframework/ai/mistralai/MistralAiChatCompletionRequestTest.java @@ -25,6 +25,7 @@ import org.springframework.ai.mistralai.api.MistralAiApi; import org.springframework.ai.model.tool.ToolCallingChatOptions; import org.springframework.ai.tool.ToolCallback; +import org.springframework.ai.tool.definition.DefaultToolDefinition; import org.springframework.ai.tool.definition.ToolDefinition; import org.springframework.boot.test.context.SpringBootTest; @@ -106,7 +107,7 @@ static class TestToolCallback implements ToolCallback { private final ToolDefinition toolDefinition; TestToolCallback(String name) { - this.toolDefinition = ToolDefinition.builder().name(name).inputSchema("{}").build(); + this.toolDefinition = DefaultToolDefinition.builder().name(name).inputSchema("{}").build(); } @Override diff --git a/models/spring-ai-mistral-ai/src/test/java/org/springframework/ai/mistralai/MistralAiChatModelIT.java b/models/spring-ai-mistral-ai/src/test/java/org/springframework/ai/mistralai/MistralAiChatModelIT.java index 06569657389..95386aed92d 100644 --- a/models/spring-ai-mistral-ai/src/test/java/org/springframework/ai/mistralai/MistralAiChatModelIT.java +++ b/models/spring-ai-mistral-ai/src/test/java/org/springframework/ai/mistralai/MistralAiChatModelIT.java @@ -56,7 +56,7 @@ import org.springframework.ai.model.tool.ToolCallingChatOptions; import org.springframework.ai.model.tool.ToolCallingManager; import org.springframework.ai.model.tool.ToolExecutionResult; -import org.springframework.ai.tool.ToolCallbacks; +import org.springframework.ai.support.ToolCallbacks; import org.springframework.ai.tool.annotation.Tool; import org.springframework.ai.tool.function.FunctionToolCallback; import org.springframework.beans.factory.annotation.Autowired; diff --git a/models/spring-ai-ollama/src/test/java/org/springframework/ai/ollama/OllamaChatModelIT.java b/models/spring-ai-ollama/src/test/java/org/springframework/ai/ollama/OllamaChatModelIT.java index 09be0f726a7..ed460135f87 100644 --- a/models/spring-ai-ollama/src/test/java/org/springframework/ai/ollama/OllamaChatModelIT.java +++ b/models/spring-ai-ollama/src/test/java/org/springframework/ai/ollama/OllamaChatModelIT.java @@ -52,7 +52,7 @@ import org.springframework.ai.ollama.management.ModelManagementOptions; import org.springframework.ai.ollama.management.OllamaModelManager; import org.springframework.ai.ollama.management.PullModelStrategy; -import org.springframework.ai.tool.ToolCallbacks; +import org.springframework.ai.support.ToolCallbacks; import org.springframework.ai.tool.annotation.Tool; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.SpringBootConfiguration; diff --git a/models/spring-ai-ollama/src/test/java/org/springframework/ai/ollama/OllamaChatRequestTests.java b/models/spring-ai-ollama/src/test/java/org/springframework/ai/ollama/OllamaChatRequestTests.java index 423e3d3ed65..59baa37bec2 100644 --- a/models/spring-ai-ollama/src/test/java/org/springframework/ai/ollama/OllamaChatRequestTests.java +++ b/models/spring-ai-ollama/src/test/java/org/springframework/ai/ollama/OllamaChatRequestTests.java @@ -26,6 +26,7 @@ import org.springframework.ai.ollama.api.OllamaApi; import org.springframework.ai.ollama.api.OllamaOptions; import org.springframework.ai.tool.ToolCallback; +import org.springframework.ai.tool.definition.DefaultToolDefinition; import org.springframework.ai.tool.definition.ToolDefinition; import static org.assertj.core.api.Assertions.assertThat; @@ -167,7 +168,7 @@ static class TestToolCallback implements ToolCallback { private final ToolDefinition toolDefinition; TestToolCallback(String name) { - this.toolDefinition = ToolDefinition.builder().name(name).inputSchema("{}").build(); + this.toolDefinition = DefaultToolDefinition.builder().name(name).inputSchema("{}").build(); } @Override diff --git a/models/spring-ai-openai/src/main/java/org/springframework/ai/openai/OpenAiChatModel.java b/models/spring-ai-openai/src/main/java/org/springframework/ai/openai/OpenAiChatModel.java index 01af8d3b7fd..01f619b0a8f 100644 --- a/models/spring-ai-openai/src/main/java/org/springframework/ai/openai/OpenAiChatModel.java +++ b/models/spring-ai-openai/src/main/java/org/springframework/ai/openai/OpenAiChatModel.java @@ -43,7 +43,7 @@ import org.springframework.ai.chat.metadata.EmptyUsage; import org.springframework.ai.chat.metadata.RateLimit; import org.springframework.ai.chat.metadata.Usage; -import org.springframework.ai.chat.metadata.UsageUtils; +import org.springframework.ai.support.UsageCalculator; import org.springframework.ai.chat.model.ChatModel; import org.springframework.ai.chat.model.ChatResponse; import org.springframework.ai.chat.model.Generation; @@ -227,7 +227,8 @@ public ChatResponse internalCall(Prompt prompt, ChatResponse previousChatRespons // Current usage OpenAiApi.Usage usage = chatCompletion.usage(); Usage currentChatResponseUsage = usage != null ? getDefaultUsage(usage) : new EmptyUsage(); - Usage accumulatedUsage = UsageUtils.getCumulativeUsage(currentChatResponseUsage, previousChatResponse); + Usage accumulatedUsage = UsageCalculator.getCumulativeUsage(currentChatResponseUsage, + previousChatResponse); ChatResponse chatResponse = new ChatResponse(generations, from(chatCompletion, rateLimit, accumulatedUsage)); @@ -321,7 +322,7 @@ public Flux internalStream(Prompt prompt, ChatResponse previousCha // @formatter:on OpenAiApi.Usage usage = chatCompletion2.usage(); Usage currentChatResponseUsage = usage != null ? getDefaultUsage(usage) : new EmptyUsage(); - Usage accumulatedUsage = UsageUtils.getCumulativeUsage(currentChatResponseUsage, + Usage accumulatedUsage = UsageCalculator.getCumulativeUsage(currentChatResponseUsage, previousChatResponse); return new ChatResponse(generations, from(chatCompletion2, null, accumulatedUsage)); } @@ -345,7 +346,7 @@ public Flux internalStream(Prompt prompt, ChatResponse previousCha // This is the usage from the final Chat response for a // given Chat request. Usage usage = secondResponse.getMetadata().getUsage(); - if (!UsageUtils.isEmpty(usage)) { + if (!UsageCalculator.isEmpty(usage)) { // Store the usage from the final response to the // penultimate response for accumulation. return new ChatResponse(firstResponse.getResults(), diff --git a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/ChatCompletionRequestTests.java b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/ChatCompletionRequestTests.java index 1de3a339f4a..3d7623c96f4 100644 --- a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/ChatCompletionRequestTests.java +++ b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/ChatCompletionRequestTests.java @@ -27,6 +27,7 @@ import org.springframework.ai.openai.api.OpenAiApi; import org.springframework.ai.openai.api.tool.MockWeatherService; import org.springframework.ai.tool.ToolCallback; +import org.springframework.ai.tool.definition.DefaultToolDefinition; import org.springframework.ai.tool.definition.ToolDefinition; import org.springframework.ai.tool.function.FunctionToolCallback; @@ -166,7 +167,7 @@ static class TestToolCallback implements ToolCallback { private final ToolDefinition toolDefinition; TestToolCallback(String name) { - this.toolDefinition = ToolDefinition.builder().name(name).inputSchema("{}").build(); + this.toolDefinition = DefaultToolDefinition.builder().name(name).inputSchema("{}").build(); } @Override diff --git a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/OpenAiChatModelIT.java b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/OpenAiChatModelIT.java index 4c6fe05e87f..310f2202d86 100644 --- a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/OpenAiChatModelIT.java +++ b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/OpenAiChatModelIT.java @@ -69,7 +69,7 @@ import org.springframework.ai.openai.api.OpenAiApi.ChatCompletionRequest.AudioParameters.Voice; import org.springframework.ai.openai.api.tool.MockWeatherService; import org.springframework.ai.openai.testutils.AbstractIT; -import org.springframework.ai.tool.ToolCallbacks; +import org.springframework.ai.support.ToolCallbacks; import org.springframework.ai.tool.annotation.Tool; import org.springframework.ai.tool.function.FunctionToolCallback; import org.springframework.beans.factory.annotation.Value; diff --git a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/client/OpenAiChatClientMethodInvokingFunctionCallbackIT.java b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/client/OpenAiChatClientMethodInvokingFunctionCallbackIT.java index 5c5eec2f79f..2a766a6b055 100644 --- a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/client/OpenAiChatClientMethodInvokingFunctionCallbackIT.java +++ b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/client/OpenAiChatClientMethodInvokingFunctionCallbackIT.java @@ -29,8 +29,8 @@ import org.springframework.ai.chat.model.ChatModel; import org.springframework.ai.chat.model.ToolContext; import org.springframework.ai.openai.OpenAiTestConfiguration; -import org.springframework.ai.tool.definition.ToolDefinition; import org.springframework.ai.tool.method.MethodToolCallback; +import org.springframework.ai.tool.support.ToolDefinitions; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.ActiveProfiles; @@ -67,7 +67,7 @@ void methodGetWeatherStatic() { String response = ChatClient.create(this.chatModel).prompt() .user("What's the weather like in San Francisco, Tokyo, and Paris? Use Celsius.") .toolCallbacks(MethodToolCallback.builder() - .toolDefinition(ToolDefinition.builder(toolMethod) + .toolDefinition(ToolDefinitions.builder(toolMethod) .description("Get the weather in location") .build()) .toolMethod(toolMethod) @@ -92,7 +92,7 @@ void methodTurnLightNoResponse() { String response = ChatClient.create(this.chatModel).prompt() .user("Turn light on in the living room.") .toolCallbacks(MethodToolCallback.builder() - .toolDefinition(ToolDefinition.builder(toolMethod) + .toolDefinition(ToolDefinitions.builder(toolMethod) .description("Can turn lights on or off by room name") .build()) .toolMethod(toolMethod) @@ -120,7 +120,7 @@ void methodGetWeatherNonStatic() { String response = ChatClient.create(this.chatModel).prompt() .user("What's the weather like in San Francisco, Tokyo, and Paris? Use Celsius.") .toolCallbacks(MethodToolCallback.builder() - .toolDefinition(ToolDefinition.builder(toolMethod) + .toolDefinition(ToolDefinitions.builder(toolMethod) .description("Get the weather in location") .build()) .toolMethod(toolMethod) @@ -147,7 +147,7 @@ void methodGetWeatherToolContext() { String response = ChatClient.create(this.chatModel).prompt() .user("What's the weather like in San Francisco, Tokyo, and Paris? Use Celsius.") .toolCallbacks(MethodToolCallback.builder() - .toolDefinition(ToolDefinition.builder(toolMethod) + .toolDefinition(ToolDefinitions.builder(toolMethod) .description("Get the weather in location") .build()) .toolMethod(toolMethod) @@ -176,7 +176,7 @@ void methodGetWeatherToolContextButMissingContextArgument() { assertThatThrownBy(() -> ChatClient.create(this.chatModel).prompt() .user("What's the weather like in San Francisco, Tokyo, and Paris? Use Celsius.") .toolCallbacks(MethodToolCallback.builder() - .toolDefinition(ToolDefinition.builder(toolMethod) + .toolDefinition(ToolDefinitions.builder(toolMethod) .description("Get the weather in location") .build()) .toolMethod(toolMethod) @@ -200,7 +200,7 @@ void methodNoParameters() { String response = ChatClient.create(this.chatModel).prompt() .user("Turn light on in the living room.") .toolCallbacks(MethodToolCallback.builder() - .toolDefinition(ToolDefinition.builder(toolMethod) + .toolDefinition(ToolDefinitions.builder(toolMethod) .description("Can turn lights on in the Living Room") .build()) .toolMethod(toolMethod) diff --git a/models/spring-ai-vertex-ai-gemini/src/main/java/org/springframework/ai/vertexai/gemini/VertexAiGeminiChatModel.java b/models/spring-ai-vertex-ai-gemini/src/main/java/org/springframework/ai/vertexai/gemini/VertexAiGeminiChatModel.java index eaac6e586be..2c958b83e1d 100644 --- a/models/spring-ai-vertex-ai-gemini/src/main/java/org/springframework/ai/vertexai/gemini/VertexAiGeminiChatModel.java +++ b/models/spring-ai-vertex-ai-gemini/src/main/java/org/springframework/ai/vertexai/gemini/VertexAiGeminiChatModel.java @@ -63,7 +63,7 @@ import org.springframework.ai.chat.metadata.DefaultUsage; import org.springframework.ai.chat.metadata.EmptyUsage; import org.springframework.ai.chat.metadata.Usage; -import org.springframework.ai.chat.metadata.UsageUtils; +import org.springframework.ai.support.UsageCalculator; import org.springframework.ai.chat.model.ChatModel; import org.springframework.ai.chat.model.ChatResponse; import org.springframework.ai.chat.model.Generation; @@ -415,7 +415,7 @@ private ChatResponse internalCall(Prompt prompt, ChatResponse previousChatRespon Usage currentUsage = (usage != null) ? new DefaultUsage(usage.getPromptTokenCount(), usage.getCandidatesTokenCount()) : new EmptyUsage(); - Usage cumulativeUsage = UsageUtils.getCumulativeUsage(currentUsage, previousChatResponse); + Usage cumulativeUsage = UsageCalculator.getCumulativeUsage(currentUsage, previousChatResponse); ChatResponse chatResponse = new ChatResponse(generations, toChatResponseMetadata(cumulativeUsage)); observationContext.setResponse(chatResponse); @@ -528,7 +528,7 @@ public Flux internalStream(Prompt prompt, ChatResponse previousCha GenerateContentResponse.UsageMetadata usage = response.getUsageMetadata(); Usage currentUsage = (usage != null) ? getDefaultUsage(usage) : new EmptyUsage(); - Usage cumulativeUsage = UsageUtils.getCumulativeUsage(currentUsage, previousChatResponse); + Usage cumulativeUsage = UsageCalculator.getCumulativeUsage(currentUsage, previousChatResponse); ChatResponse chatResponse = new ChatResponse(generations, toChatResponseMetadata(cumulativeUsage)); return Flux.just(chatResponse); }); diff --git a/models/spring-ai-vertex-ai-gemini/src/main/java/org/springframework/ai/vertexai/gemini/schema/VertexToolCallingManager.java b/models/spring-ai-vertex-ai-gemini/src/main/java/org/springframework/ai/vertexai/gemini/schema/VertexToolCallingManager.java index 3261bdb13c0..bd8924dd8c2 100644 --- a/models/spring-ai-vertex-ai-gemini/src/main/java/org/springframework/ai/vertexai/gemini/schema/VertexToolCallingManager.java +++ b/models/spring-ai-vertex-ai-gemini/src/main/java/org/springframework/ai/vertexai/gemini/schema/VertexToolCallingManager.java @@ -25,6 +25,7 @@ import org.springframework.ai.model.tool.ToolCallingChatOptions; import org.springframework.ai.model.tool.ToolCallingManager; import org.springframework.ai.model.tool.ToolExecutionResult; +import org.springframework.ai.tool.definition.DefaultToolDefinition; import org.springframework.ai.tool.definition.ToolDefinition; import org.springframework.ai.util.json.schema.JsonSchemaGenerator; import org.springframework.util.Assert; @@ -75,7 +76,7 @@ public List resolveToolDefinitions(ToolCallingChatOptions chatOp ObjectNode openApiSchema = JsonSchemaConverter.convertToOpenApiSchema(jsonSchema); JsonSchemaGenerator.convertTypeValuesToUpperCase(openApiSchema); - return ToolDefinition.builder() + return DefaultToolDefinition.builder() .name(td.name()) .description(td.description()) .inputSchema(openApiSchema.toPrettyString()) 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 75296acf525..128800abf39 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 @@ -36,7 +36,7 @@ import org.springframework.ai.model.tool.ToolCallingManager; import org.springframework.ai.tool.ToolCallback; import org.springframework.ai.tool.ToolCallbackProvider; -import org.springframework.ai.tool.ToolCallbacks; +import org.springframework.ai.support.ToolCallbacks; import org.springframework.ai.tool.annotation.Tool; import org.springframework.ai.tool.execution.DefaultToolExecutionExceptionProcessor; import org.springframework.ai.tool.resolution.DelegatingToolCallbackResolver; diff --git a/spring-ai-client-chat/src/main/java/org/springframework/ai/chat/client/DefaultChatClient.java b/spring-ai-client-chat/src/main/java/org/springframework/ai/chat/client/DefaultChatClient.java index a83898c5779..82327f43981 100644 --- a/spring-ai-client-chat/src/main/java/org/springframework/ai/chat/client/DefaultChatClient.java +++ b/spring-ai-client-chat/src/main/java/org/springframework/ai/chat/client/DefaultChatClient.java @@ -23,11 +23,9 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; -import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Optional; -import java.util.concurrent.ConcurrentHashMap; import java.util.function.Consumer; import io.micrometer.observation.Observation; @@ -46,28 +44,23 @@ import org.springframework.ai.chat.client.observation.DefaultChatClientObservationConvention; import org.springframework.ai.chat.messages.AbstractMessage; import org.springframework.ai.chat.messages.Message; -import org.springframework.ai.chat.messages.SystemMessage; -import org.springframework.ai.chat.messages.UserMessage; import org.springframework.ai.chat.model.ChatModel; 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.chat.prompt.Prompt; -import org.springframework.ai.chat.prompt.PromptTemplate; import org.springframework.ai.content.Media; import org.springframework.ai.converter.BeanOutputConverter; import org.springframework.ai.converter.StructuredOutputConverter; -import org.springframework.ai.model.tool.ToolCallingChatOptions; import org.springframework.ai.template.TemplateRenderer; import org.springframework.ai.template.st.StTemplateRenderer; import org.springframework.ai.tool.ToolCallback; import org.springframework.ai.tool.ToolCallbackProvider; -import org.springframework.ai.tool.ToolCallbacks; +import org.springframework.ai.support.ToolCallbacks; import org.springframework.core.ParameterizedTypeReference; import org.springframework.core.io.Resource; import org.springframework.lang.Nullable; import org.springframework.util.Assert; -import org.springframework.util.CollectionUtils; import org.springframework.util.MimeType; import org.springframework.util.StringUtils; diff --git a/spring-ai-client-chat/src/test/java/org/springframework/ai/chat/client/DefaultChatClientUtilsTests.java b/spring-ai-client-chat/src/test/java/org/springframework/ai/chat/client/DefaultChatClientUtilsTests.java index 3f0ed5148e5..a14f5d3fdce 100644 --- a/spring-ai-client-chat/src/test/java/org/springframework/ai/chat/client/DefaultChatClientUtilsTests.java +++ b/spring-ai-client-chat/src/test/java/org/springframework/ai/chat/client/DefaultChatClientUtilsTests.java @@ -26,6 +26,7 @@ import org.springframework.ai.template.TemplateRenderer; import org.springframework.ai.template.st.StTemplateRenderer; import org.springframework.ai.tool.ToolCallback; +import org.springframework.ai.tool.definition.DefaultToolDefinition; import org.springframework.ai.tool.definition.ToolDefinition; import org.springframework.ai.tool.metadata.ToolMetadata; @@ -419,12 +420,12 @@ static class TestToolCallback implements ToolCallback { private final ToolMetadata toolMetadata; TestToolCallback(String name) { - this.toolDefinition = ToolDefinition.builder().name(name).inputSchema("{}").build(); + this.toolDefinition = DefaultToolDefinition.builder().name(name).inputSchema("{}").build(); this.toolMetadata = ToolMetadata.builder().build(); } TestToolCallback(String name, boolean returnDirect) { - this.toolDefinition = ToolDefinition.builder().name(name).inputSchema("{}").build(); + this.toolDefinition = DefaultToolDefinition.builder().name(name).inputSchema("{}").build(); this.toolMetadata = ToolMetadata.builder().returnDirect(returnDirect).build(); } diff --git a/spring-ai-client-chat/src/test/java/org/springframework/ai/chat/client/observation/DefaultChatClientObservationConventionTests.java b/spring-ai-client-chat/src/test/java/org/springframework/ai/chat/client/observation/DefaultChatClientObservationConventionTests.java index 313ec8295a2..4333c0883c7 100644 --- a/spring-ai-client-chat/src/test/java/org/springframework/ai/chat/client/observation/DefaultChatClientObservationConventionTests.java +++ b/spring-ai-client-chat/src/test/java/org/springframework/ai/chat/client/observation/DefaultChatClientObservationConventionTests.java @@ -39,6 +39,7 @@ import org.springframework.ai.observation.conventions.AiProvider; import org.springframework.ai.observation.conventions.SpringAiKind; import org.springframework.ai.tool.ToolCallback; +import org.springframework.ai.tool.definition.DefaultToolDefinition; import org.springframework.ai.tool.definition.ToolDefinition; import static org.assertj.core.api.Assertions.assertThat; @@ -86,7 +87,7 @@ static ToolCallback dummyFunction(String name) { @Override public ToolDefinition getToolDefinition() { - return ToolDefinition.builder().name(name).inputSchema("{}").build(); + return DefaultToolDefinition.builder().name(name).inputSchema("{}").build(); } @Override diff --git a/spring-ai-integration-tests/src/test/java/org/springframework/ai/integration/tests/tool/MethodToolCallbackTests.java b/spring-ai-integration-tests/src/test/java/org/springframework/ai/integration/tests/tool/MethodToolCallbackTests.java index 7b01f13f44f..cf2ab69df85 100644 --- a/spring-ai-integration-tests/src/test/java/org/springframework/ai/integration/tests/tool/MethodToolCallbackTests.java +++ b/spring-ai-integration-tests/src/test/java/org/springframework/ai/integration/tests/tool/MethodToolCallbackTests.java @@ -29,7 +29,7 @@ import org.springframework.ai.integration.tests.tool.domain.Book; import org.springframework.ai.integration.tests.tool.domain.BookService; import org.springframework.ai.openai.OpenAiChatModel; -import org.springframework.ai.tool.ToolCallbacks; +import org.springframework.ai.support.ToolCallbacks; import org.springframework.ai.tool.annotation.Tool; import org.springframework.ai.tool.method.MethodToolCallback; import org.springframework.beans.factory.annotation.Autowired; diff --git a/spring-ai-integration-tests/src/test/java/org/springframework/ai/integration/tests/tool/ToolCallingManagerTests.java b/spring-ai-integration-tests/src/test/java/org/springframework/ai/integration/tests/tool/ToolCallingManagerTests.java index ba63fbbb93c..89621cc8cb1 100644 --- a/spring-ai-integration-tests/src/test/java/org/springframework/ai/integration/tests/tool/ToolCallingManagerTests.java +++ b/spring-ai-integration-tests/src/test/java/org/springframework/ai/integration/tests/tool/ToolCallingManagerTests.java @@ -37,7 +37,7 @@ import org.springframework.ai.model.tool.ToolCallingManager; import org.springframework.ai.model.tool.ToolExecutionResult; import org.springframework.ai.openai.OpenAiChatModel; -import org.springframework.ai.tool.ToolCallbacks; +import org.springframework.ai.support.ToolCallbacks; import org.springframework.ai.tool.annotation.Tool; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; diff --git a/spring-ai-model/src/main/java/org/springframework/ai/model/tool/ToolCallingChatOptions.java b/spring-ai-model/src/main/java/org/springframework/ai/model/tool/ToolCallingChatOptions.java index cba1fd37472..f06e71aa869 100644 --- a/spring-ai-model/src/main/java/org/springframework/ai/model/tool/ToolCallingChatOptions.java +++ b/spring-ai-model/src/main/java/org/springframework/ai/model/tool/ToolCallingChatOptions.java @@ -26,7 +26,7 @@ import org.springframework.ai.chat.model.ChatModel; import org.springframework.ai.chat.prompt.ChatOptions; import org.springframework.ai.tool.ToolCallback; -import org.springframework.ai.tool.util.ToolUtils; +import org.springframework.ai.tool.support.ToolUtils; import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.CollectionUtils; diff --git a/spring-ai-model/src/main/java/org/springframework/ai/tool/ToolCallbacks.java b/spring-ai-model/src/main/java/org/springframework/ai/support/ToolCallbacks.java similarity index 92% rename from spring-ai-model/src/main/java/org/springframework/ai/tool/ToolCallbacks.java rename to spring-ai-model/src/main/java/org/springframework/ai/support/ToolCallbacks.java index 10d69dd7530..105c9d75b00 100644 --- a/spring-ai-model/src/main/java/org/springframework/ai/tool/ToolCallbacks.java +++ b/spring-ai-model/src/main/java/org/springframework/ai/support/ToolCallbacks.java @@ -14,8 +14,9 @@ * limitations under the License. */ -package org.springframework.ai.tool; +package org.springframework.ai.support; +import org.springframework.ai.tool.ToolCallback; import org.springframework.ai.tool.method.MethodToolCallbackProvider; /** diff --git a/spring-ai-model/src/main/java/org/springframework/ai/chat/metadata/UsageUtils.java b/spring-ai-model/src/main/java/org/springframework/ai/support/UsageCalculator.java similarity index 93% rename from spring-ai-model/src/main/java/org/springframework/ai/chat/metadata/UsageUtils.java rename to spring-ai-model/src/main/java/org/springframework/ai/support/UsageCalculator.java index c4239a97003..5914fe1d101 100644 --- a/spring-ai-model/src/main/java/org/springframework/ai/chat/metadata/UsageUtils.java +++ b/spring-ai-model/src/main/java/org/springframework/ai/support/UsageCalculator.java @@ -14,8 +14,10 @@ * limitations under the License. */ -package org.springframework.ai.chat.metadata; +package org.springframework.ai.support; +import org.springframework.ai.chat.metadata.DefaultUsage; +import org.springframework.ai.chat.metadata.Usage; import org.springframework.ai.chat.model.ChatResponse; /** @@ -23,9 +25,9 @@ * * @author Ilayaperumal Gopinathan */ -public final class UsageUtils { +public final class UsageCalculator { - private UsageUtils() { + private UsageCalculator() { throw new UnsupportedOperationException("This is a utility class and cannot be instantiated"); } diff --git a/spring-ai-model/src/main/java/org/springframework/ai/tool/definition/DefaultToolDefinition.java b/spring-ai-model/src/main/java/org/springframework/ai/tool/definition/DefaultToolDefinition.java index 69632a45e0f..b38c6708d17 100644 --- a/spring-ai-model/src/main/java/org/springframework/ai/tool/definition/DefaultToolDefinition.java +++ b/spring-ai-model/src/main/java/org/springframework/ai/tool/definition/DefaultToolDefinition.java @@ -16,7 +16,8 @@ package org.springframework.ai.tool.definition; -import org.springframework.ai.tool.util.ToolUtils; +import org.springframework.ai.tool.support.ToolUtils; +import org.springframework.ai.util.ParsingUtils; import org.springframework.util.Assert; import org.springframework.util.StringUtils; @@ -66,7 +67,8 @@ public Builder inputSchema(String inputSchema) { public ToolDefinition build() { if (!StringUtils.hasText(this.description)) { - this.description = ToolUtils.getToolDescriptionFromName(this.name); + Assert.hasText(this.name, "toolName cannot be null or empty"); + this.description = ParsingUtils.reConcatenateCamelCase(this.name, " "); } return new DefaultToolDefinition(this.name, this.description, this.inputSchema); } diff --git a/spring-ai-model/src/main/java/org/springframework/ai/tool/definition/ToolDefinition.java b/spring-ai-model/src/main/java/org/springframework/ai/tool/definition/ToolDefinition.java index 10fef32f0aa..089c7b30538 100644 --- a/spring-ai-model/src/main/java/org/springframework/ai/tool/definition/ToolDefinition.java +++ b/spring-ai-model/src/main/java/org/springframework/ai/tool/definition/ToolDefinition.java @@ -16,12 +16,6 @@ package org.springframework.ai.tool.definition; -import java.lang.reflect.Method; - -import org.springframework.ai.tool.util.ToolUtils; -import org.springframework.ai.util.json.schema.JsonSchemaGenerator; -import org.springframework.util.Assert; - /** * Definition used by the AI model to determine when and how to call the tool. * @@ -45,29 +39,4 @@ public interface ToolDefinition { */ String inputSchema(); - /** - * Create a default {@link ToolDefinition} builder. - */ - static DefaultToolDefinition.Builder builder() { - return DefaultToolDefinition.builder(); - } - - /** - * Create a default {@link ToolDefinition} builder from a {@link Method}. - */ - static DefaultToolDefinition.Builder builder(Method method) { - Assert.notNull(method, "method cannot be null"); - return DefaultToolDefinition.builder() - .name(ToolUtils.getToolName(method)) - .description(ToolUtils.getToolDescription(method)) - .inputSchema(JsonSchemaGenerator.generateForMethodInput(method)); - } - - /** - * Create a default {@link ToolDefinition} instance from a {@link Method}. - */ - static ToolDefinition from(Method method) { - return ToolDefinition.builder(method).build(); - } - } diff --git a/spring-ai-model/src/main/java/org/springframework/ai/tool/function/FunctionToolCallback.java b/spring-ai-model/src/main/java/org/springframework/ai/tool/function/FunctionToolCallback.java index 98a5d4731eb..091f9e2b8e4 100644 --- a/spring-ai-model/src/main/java/org/springframework/ai/tool/function/FunctionToolCallback.java +++ b/spring-ai-model/src/main/java/org/springframework/ai/tool/function/FunctionToolCallback.java @@ -27,11 +27,12 @@ import org.springframework.ai.chat.model.ToolContext; import org.springframework.ai.tool.ToolCallback; +import org.springframework.ai.tool.definition.DefaultToolDefinition; import org.springframework.ai.tool.definition.ToolDefinition; import org.springframework.ai.tool.execution.DefaultToolCallResultConverter; import org.springframework.ai.tool.execution.ToolCallResultConverter; import org.springframework.ai.tool.metadata.ToolMetadata; -import org.springframework.ai.tool.util.ToolUtils; +import org.springframework.ai.tool.support.ToolUtils; import org.springframework.ai.util.json.JsonParser; import org.springframework.ai.util.json.schema.JsonSchemaGenerator; import org.springframework.core.ParameterizedTypeReference; @@ -203,7 +204,7 @@ public Builder toolCallResultConverter(ToolCallResultConverter toolCallRes public FunctionToolCallback build() { Assert.notNull(this.inputType, "inputType cannot be null"); - var toolDefinition = ToolDefinition.builder() + var toolDefinition = DefaultToolDefinition.builder() .name(this.name) .description(StringUtils.hasText(this.description) ? this.description : ToolUtils.getToolDescriptionFromName(this.name)) diff --git a/spring-ai-model/src/main/java/org/springframework/ai/tool/metadata/ToolMetadata.java b/spring-ai-model/src/main/java/org/springframework/ai/tool/metadata/ToolMetadata.java index 81963f8b13b..c620282f497 100644 --- a/spring-ai-model/src/main/java/org/springframework/ai/tool/metadata/ToolMetadata.java +++ b/spring-ai-model/src/main/java/org/springframework/ai/tool/metadata/ToolMetadata.java @@ -18,7 +18,7 @@ import java.lang.reflect.Method; -import org.springframework.ai.tool.util.ToolUtils; +import org.springframework.ai.tool.support.ToolUtils; import org.springframework.util.Assert; /** diff --git a/spring-ai-model/src/main/java/org/springframework/ai/tool/method/MethodToolCallbackProvider.java b/spring-ai-model/src/main/java/org/springframework/ai/tool/method/MethodToolCallbackProvider.java index bc43383ef19..666aa6f97f3 100644 --- a/spring-ai-model/src/main/java/org/springframework/ai/tool/method/MethodToolCallbackProvider.java +++ b/spring-ai-model/src/main/java/org/springframework/ai/tool/method/MethodToolCallbackProvider.java @@ -19,7 +19,6 @@ import java.lang.reflect.Method; import java.util.Arrays; import java.util.List; -import java.util.Optional; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Supplier; @@ -32,9 +31,9 @@ import org.springframework.ai.tool.ToolCallback; import org.springframework.ai.tool.ToolCallbackProvider; import org.springframework.ai.tool.annotation.Tool; -import org.springframework.ai.tool.definition.ToolDefinition; import org.springframework.ai.tool.metadata.ToolMetadata; -import org.springframework.ai.tool.util.ToolUtils; +import org.springframework.ai.tool.support.ToolDefinitions; +import org.springframework.ai.tool.support.ToolUtils; import org.springframework.aop.support.AopUtils; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; @@ -88,7 +87,7 @@ public ToolCallback[] getToolCallbacks() { .filter(toolMethod -> toolMethod.isAnnotationPresent(Tool.class)) .filter(toolMethod -> !isFunctionalType(toolMethod)) .map(toolMethod -> MethodToolCallback.builder() - .toolDefinition(ToolDefinition.from(toolMethod)) + .toolDefinition(ToolDefinitions.from(toolMethod)) .toolMetadata(ToolMetadata.from(toolMethod)) .toolMethod(toolMethod) .toolObject(toolObject) diff --git a/spring-ai-model/src/main/java/org/springframework/ai/tool/resolution/SpringBeanToolCallbackResolver.java b/spring-ai-model/src/main/java/org/springframework/ai/tool/resolution/SpringBeanToolCallbackResolver.java index 7f9bf83e960..19beeebf5dc 100644 --- a/spring-ai-model/src/main/java/org/springframework/ai/tool/resolution/SpringBeanToolCallbackResolver.java +++ b/spring-ai-model/src/main/java/org/springframework/ai/tool/resolution/SpringBeanToolCallbackResolver.java @@ -33,7 +33,7 @@ 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.util.ToolUtils; +import org.springframework.ai.tool.support.ToolUtils; import org.springframework.ai.util.json.schema.JsonSchemaGenerator; import org.springframework.ai.util.json.schema.SchemaType; import org.springframework.context.ApplicationContext; diff --git a/spring-ai-model/src/main/java/org/springframework/ai/tool/support/ToolDefinitions.java b/spring-ai-model/src/main/java/org/springframework/ai/tool/support/ToolDefinitions.java new file mode 100644 index 00000000000..25819ea719d --- /dev/null +++ b/spring-ai-model/src/main/java/org/springframework/ai/tool/support/ToolDefinitions.java @@ -0,0 +1,57 @@ +/* + * 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.tool.support; + +import java.lang.reflect.Method; + +import org.springframework.ai.tool.definition.DefaultToolDefinition; +import org.springframework.ai.tool.definition.ToolDefinition; +import org.springframework.ai.util.json.schema.JsonSchemaGenerator; +import org.springframework.util.Assert; + +/** + * Utility class for creating {@link ToolDefinition} builders and instances from Java + * {@link Method} objects. + *

+ * This class provides static methods to facilitate the construction of + * {@link ToolDefinition} objects by extracting relevant metadata from Java reflection + * {@link Method} instances. + *

+ * + * @author Mark Pollack + * @since 1.0.0 + */ +public class ToolDefinitions { + + /** + * Create a default {@link ToolDefinition} builder from a {@link Method}. + */ + public static DefaultToolDefinition.Builder builder(Method method) { + Assert.notNull(method, "method cannot be null"); + return DefaultToolDefinition.builder() + .name(ToolUtils.getToolName(method)) + .description(ToolUtils.getToolDescription(method)) + .inputSchema(JsonSchemaGenerator.generateForMethodInput(method)); + } + + /** + * Create a default {@link ToolDefinition} instance from a {@link Method}. + */ + public static ToolDefinition from(Method method) { + return builder(method).build(); + } + +} diff --git a/spring-ai-model/src/main/java/org/springframework/ai/tool/util/ToolUtils.java b/spring-ai-model/src/main/java/org/springframework/ai/tool/support/ToolUtils.java similarity index 98% rename from spring-ai-model/src/main/java/org/springframework/ai/tool/util/ToolUtils.java rename to spring-ai-model/src/main/java/org/springframework/ai/tool/support/ToolUtils.java index 5d07d28311e..c49b9c6ff71 100644 --- a/spring-ai-model/src/main/java/org/springframework/ai/tool/util/ToolUtils.java +++ b/spring-ai-model/src/main/java/org/springframework/ai/tool/support/ToolUtils.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.ai.tool.util; +package org.springframework.ai.tool.support; import java.lang.reflect.Method; import java.util.Arrays; diff --git a/spring-ai-model/src/main/java/org/springframework/ai/tool/util/package-info.java b/spring-ai-model/src/main/java/org/springframework/ai/tool/support/package-info.java similarity index 94% rename from spring-ai-model/src/main/java/org/springframework/ai/tool/util/package-info.java rename to spring-ai-model/src/main/java/org/springframework/ai/tool/support/package-info.java index 6fb2e67ae1d..fb73a691013 100644 --- a/spring-ai-model/src/main/java/org/springframework/ai/tool/util/package-info.java +++ b/spring-ai-model/src/main/java/org/springframework/ai/tool/support/package-info.java @@ -16,7 +16,7 @@ @NonNullApi @NonNullFields -package org.springframework.ai.tool.util; +package org.springframework.ai.tool.support; import org.springframework.lang.NonNullApi; import org.springframework.lang.NonNullFields; diff --git a/spring-ai-model/src/test/java/org/springframework/ai/model/tool/DefaultToolCallingManagerTests.java b/spring-ai-model/src/test/java/org/springframework/ai/model/tool/DefaultToolCallingManagerTests.java index 760bc3bc88c..7dba4ad2518 100644 --- a/spring-ai-model/src/test/java/org/springframework/ai/model/tool/DefaultToolCallingManagerTests.java +++ b/spring-ai-model/src/test/java/org/springframework/ai/model/tool/DefaultToolCallingManagerTests.java @@ -29,6 +29,7 @@ import org.springframework.ai.chat.model.Generation; import org.springframework.ai.chat.prompt.Prompt; import org.springframework.ai.tool.ToolCallback; +import org.springframework.ai.tool.definition.DefaultToolDefinition; import org.springframework.ai.tool.definition.ToolDefinition; import org.springframework.ai.tool.execution.ToolExecutionException; import org.springframework.ai.tool.execution.ToolExecutionExceptionProcessor; @@ -323,12 +324,12 @@ static class TestToolCallback implements ToolCallback { private final ToolMetadata toolMetadata; TestToolCallback(String name) { - this.toolDefinition = ToolDefinition.builder().name(name).inputSchema("{}").build(); + this.toolDefinition = DefaultToolDefinition.builder().name(name).inputSchema("{}").build(); this.toolMetadata = ToolMetadata.builder().build(); } TestToolCallback(String name, boolean returnDirect) { - this.toolDefinition = ToolDefinition.builder().name(name).inputSchema("{}").build(); + this.toolDefinition = DefaultToolDefinition.builder().name(name).inputSchema("{}").build(); this.toolMetadata = ToolMetadata.builder().returnDirect(returnDirect).build(); } @@ -354,7 +355,7 @@ static class FailingToolCallback implements ToolCallback { private final ToolDefinition toolDefinition; FailingToolCallback(String name) { - this.toolDefinition = ToolDefinition.builder().name(name).inputSchema("{}").build(); + this.toolDefinition = DefaultToolDefinition.builder().name(name).inputSchema("{}").build(); } @Override 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 c81de4c6880..6d5d599dccd 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 @@ -23,6 +23,7 @@ import org.junit.jupiter.api.Test; import org.springframework.ai.tool.ToolCallback; +import org.springframework.ai.tool.definition.DefaultToolDefinition; import org.springframework.ai.tool.definition.ToolDefinition; import static org.assertj.core.api.Assertions.assertThat; @@ -180,7 +181,7 @@ static class TestToolCallback implements ToolCallback { private final ToolDefinition toolDefinition; TestToolCallback(String name) { - this.toolDefinition = ToolDefinition.builder().name(name).inputSchema("{}").build(); + this.toolDefinition = DefaultToolDefinition.builder().name(name).inputSchema("{}").build(); } @Override