From 5491b960d172e3d5e84ba17102998de9b60e8123 Mon Sep 17 00:00:00 2001 From: Christian Tzolov Date: Wed, 7 May 2025 10:51:56 +0200 Subject: [PATCH] fix(tool): handle non-existing tool callbacks gracefully - Add error handling to SpringBeanToolCallbackResolver to return null instead of throwing exceptions when resolving non-existing tools. This make is consitent with other tool resolver implementations and centralize the error handling in the tool calling manager. - Add test to verify that the ToolCallbackResolver correctly returns null for non-existing tools. Related to #2667 Signed-off-by: Christian Tzolov --- .../ToolCallingAutoConfigurationTests.java | 12 ++++++++++ .../SpringBeanToolCallbackResolver.java | 22 ++++++++++++------- 2 files changed, 26 insertions(+), 8 deletions(-) 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..9bbcf9f3f62 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 @@ -99,6 +99,18 @@ void resolveMultipleFunctionAndToolCallbacks() { }); } + @Test + void resolveMissingToolCallbacks() { + new ApplicationContextRunner().withConfiguration(AutoConfigurations.of(ToolCallingAutoConfiguration.class)) + .withUserConfiguration(Config.class) + .run(context -> { + var toolCallbackResolver = context.getBean(ToolCallbackResolver.class); + assertThat(toolCallbackResolver).isInstanceOf(DelegatingToolCallbackResolver.class); + + assertThat(toolCallbackResolver.resolve("NonExisting")).isNull(); + }); + } + static class WeatherService { @Tool(description = "Get the weather in location. Return temperature in 36°F or 36°C format.") 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..945300a60a3 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 @@ -88,18 +88,24 @@ public ToolCallback resolve(String toolName) { return resolvedToolCallback; } - ResolvableType toolType = TypeResolverHelper.resolveBeanType(this.applicationContext, toolName); - ResolvableType toolInputType = (ResolvableType.forType(Supplier.class).isAssignableFrom(toolType)) - ? ResolvableType.forType(Void.class) : TypeResolverHelper.getFunctionArgumentType(toolType, 0); + try { + ResolvableType toolType = TypeResolverHelper.resolveBeanType(this.applicationContext, toolName); + ResolvableType toolInputType = (ResolvableType.forType(Supplier.class).isAssignableFrom(toolType)) + ? ResolvableType.forType(Void.class) : TypeResolverHelper.getFunctionArgumentType(toolType, 0); - String toolDescription = resolveToolDescription(toolName, toolInputType.toClass()); - Object bean = this.applicationContext.getBean(toolName); + String toolDescription = resolveToolDescription(toolName, toolInputType.toClass()); + Object bean = this.applicationContext.getBean(toolName); - resolvedToolCallback = buildToolCallback(toolName, toolType, toolInputType, toolDescription, bean); + resolvedToolCallback = buildToolCallback(toolName, toolType, toolInputType, toolDescription, bean); - toolCallbacksCache.put(toolName, resolvedToolCallback); + toolCallbacksCache.put(toolName, resolvedToolCallback); - return resolvedToolCallback; + return resolvedToolCallback; + } + catch (Exception e) { + logger.debug("ToolCallback resolution failed from Spring application context", e); + return null; + } } public SchemaType getSchemaType() {