fix: reverse ToolContext validation logic in MethodToolCallback

Changes the validation logic in MethodToolCallback to check if a ToolContext is required by the method but not provided,
rather than checking if a ToolContext is provided but not supported by the method.
This ensures methods that expect a ToolContext parameter receive one.

Updates tests cases to reflect the new validation logic

Resolves #2337

Signed-off-by: Christian Tzolov <christian.tzolov@broadcom.com>
This commit is contained in:
Christian Tzolov
2025-02-27 11:01:55 +01:00
parent 3dfea48249
commit 82d98b6b48
4 changed files with 15 additions and 19 deletions

View File

@@ -192,13 +192,13 @@ class AnthropicChatClientMethodInvokingFunctionCallbackIT {
}
@Test
void methodGetWeatherToolContextButNonContextMethod() {
void methodGetWeatherWithContextMethodButMissingContext() {
TestFunctionClass targetObject = new TestFunctionClass();
// @formatter:off
var toolMethod = ReflectionUtils.findMethod(
TestFunctionClass.class, "getWeatherNonStatic", String.class, Unit.class);
TestFunctionClass.class, "getWeatherWithContext", String.class, Unit.class, ToolContext.class);
assertThatThrownBy(() -> ChatClient.create(this.chatModel).prompt()
.user("What's the weather like in San Francisco, Tokyo, and Paris? Use Celsius.")
@@ -209,11 +209,10 @@ class AnthropicChatClientMethodInvokingFunctionCallbackIT {
.toolMethod(toolMethod)
.toolObject(targetObject)
.build())
.toolContext(Map.of("tool", "value"))
.call()
.content())
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("ToolContext is not supported by the method as an argument");
.hasMessage("ToolContext is required by the method as an argument");
// @formatter:on
}

View File

@@ -165,12 +165,12 @@ class OpenAiChatClientMethodInvokingFunctionCallbackIT {
}
@Test
void methodGetWeatherToolContextButNonContextMethod() {
void methodGetWeatherToolContextButMissingContextArgument() {
TestFunctionClass targetObject = new TestFunctionClass();
var toolMethod = ReflectionUtils.findMethod(TestFunctionClass.class, "getWeatherNonStatic", String.class,
Unit.class);
var toolMethod = ReflectionUtils.findMethod(TestFunctionClass.class, "getWeatherWithContext", String.class,
Unit.class, ToolContext.class);
// @formatter:off
assertThatThrownBy(() -> ChatClient.create(this.chatModel).prompt()
@@ -181,12 +181,11 @@ class OpenAiChatClientMethodInvokingFunctionCallbackIT {
.build())
.toolMethod(toolMethod)
.toolObject(targetObject)
.build())
.toolContext(Map.of("tool", "value"))
.build())
.call()
.content())
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("ToolContext is not supported by the method as an argument");
.hasMessage("ToolContext is required by the method as an argument");
// @formatter:on
}

View File

@@ -115,11 +115,11 @@ public class MethodToolCallback implements ToolCallback {
}
private void validateToolContextSupport(@Nullable ToolContext toolContext) {
var isToolContextRequired = toolContext != null && !CollectionUtils.isEmpty(toolContext.getContext());
var isNonEmptyToolContextProvided = toolContext != null && !CollectionUtils.isEmpty(toolContext.getContext());
var isToolContextAcceptedByMethod = Stream.of(toolMethod.getParameterTypes())
.anyMatch(type -> ClassUtils.isAssignable(type, ToolContext.class));
if (isToolContextRequired && !isToolContextAcceptedByMethod) {
throw new IllegalArgumentException("ToolContext is not supported by the method as an argument");
if (isToolContextAcceptedByMethod && !isNonEmptyToolContextProvided) {
throw new IllegalArgumentException("ToolContext is required by the method as an argument");
}
}

View File

@@ -69,8 +69,8 @@ class MethodToolCallbackTests {
}
@Test
void shouldThrowExceptionWhenToolContextNotSupported() {
Method toolMethod = getMethod("publicMethod", PublicTools.class);
void shouldThrowExceptionWhenToolContextArgumentIsMissing() {
Method toolMethod = getMethod("methodWithToolContext", ToolContextTools.class);
MethodToolCallback callback = MethodToolCallback.builder()
.toolDefinition(ToolDefinition.from(toolMethod))
.toolMetadata(ToolMetadata.from(toolMethod))
@@ -78,14 +78,12 @@ class MethodToolCallbackTests {
.toolObject(new PublicTools())
.build();
ToolContext toolContext = new ToolContext(Map.of("key", "value"));
assertThatThrownBy(() -> callback.call("""
{
"input": "test"
}
""", toolContext)).isInstanceOf(IllegalArgumentException.class)
.hasMessageContaining("ToolContext is not supported");
""")).isInstanceOf(IllegalArgumentException.class)
.hasMessageContaining("ToolContext is required by the method as an argument");
}
@Test