Skip to content

Commit 82d98b6

Browse files
committed
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 <[email protected]>
1 parent 3dfea48 commit 82d98b6

File tree

4 files changed

+15
-19
lines changed

4 files changed

+15
-19
lines changed

Diff for: models/spring-ai-anthropic/src/test/java/org/springframework/ai/anthropic/client/AnthropicChatClientMethodInvokingFunctionCallbackIT.java

+3-4
Original file line numberDiff line numberDiff line change
@@ -192,13 +192,13 @@ void methodGetWeatherToolContext() {
192192
}
193193

194194
@Test
195-
void methodGetWeatherToolContextButNonContextMethod() {
195+
void methodGetWeatherWithContextMethodButMissingContext() {
196196

197197
TestFunctionClass targetObject = new TestFunctionClass();
198198

199199
// @formatter:off
200200
var toolMethod = ReflectionUtils.findMethod(
201-
TestFunctionClass.class, "getWeatherNonStatic", String.class, Unit.class);
201+
TestFunctionClass.class, "getWeatherWithContext", String.class, Unit.class, ToolContext.class);
202202

203203
assertThatThrownBy(() -> ChatClient.create(this.chatModel).prompt()
204204
.user("What's the weather like in San Francisco, Tokyo, and Paris? Use Celsius.")
@@ -209,11 +209,10 @@ void methodGetWeatherToolContextButNonContextMethod() {
209209
.toolMethod(toolMethod)
210210
.toolObject(targetObject)
211211
.build())
212-
.toolContext(Map.of("tool", "value"))
213212
.call()
214213
.content())
215214
.isInstanceOf(IllegalArgumentException.class)
216-
.hasMessage("ToolContext is not supported by the method as an argument");
215+
.hasMessage("ToolContext is required by the method as an argument");
217216
// @formatter:on
218217
}
219218

Diff for: models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/client/OpenAiChatClientMethodInvokingFunctionCallbackIT.java

+5-6
Original file line numberDiff line numberDiff line change
@@ -165,12 +165,12 @@ void methodGetWeatherToolContext() {
165165
}
166166

167167
@Test
168-
void methodGetWeatherToolContextButNonContextMethod() {
168+
void methodGetWeatherToolContextButMissingContextArgument() {
169169

170170
TestFunctionClass targetObject = new TestFunctionClass();
171171

172-
var toolMethod = ReflectionUtils.findMethod(TestFunctionClass.class, "getWeatherNonStatic", String.class,
173-
Unit.class);
172+
var toolMethod = ReflectionUtils.findMethod(TestFunctionClass.class, "getWeatherWithContext", String.class,
173+
Unit.class, ToolContext.class);
174174

175175
// @formatter:off
176176
assertThatThrownBy(() -> ChatClient.create(this.chatModel).prompt()
@@ -181,12 +181,11 @@ void methodGetWeatherToolContextButNonContextMethod() {
181181
.build())
182182
.toolMethod(toolMethod)
183183
.toolObject(targetObject)
184-
.build())
185-
.toolContext(Map.of("tool", "value"))
184+
.build())
186185
.call()
187186
.content())
188187
.isInstanceOf(IllegalArgumentException.class)
189-
.hasMessage("ToolContext is not supported by the method as an argument");
188+
.hasMessage("ToolContext is required by the method as an argument");
190189
// @formatter:on
191190
}
192191

Diff for: spring-ai-core/src/main/java/org/springframework/ai/tool/method/MethodToolCallback.java

+3-3
Original file line numberDiff line numberDiff line change
@@ -115,11 +115,11 @@ public String call(String toolInput, @Nullable ToolContext toolContext) {
115115
}
116116

117117
private void validateToolContextSupport(@Nullable ToolContext toolContext) {
118-
var isToolContextRequired = toolContext != null && !CollectionUtils.isEmpty(toolContext.getContext());
118+
var isNonEmptyToolContextProvided = toolContext != null && !CollectionUtils.isEmpty(toolContext.getContext());
119119
var isToolContextAcceptedByMethod = Stream.of(toolMethod.getParameterTypes())
120120
.anyMatch(type -> ClassUtils.isAssignable(type, ToolContext.class));
121-
if (isToolContextRequired && !isToolContextAcceptedByMethod) {
122-
throw new IllegalArgumentException("ToolContext is not supported by the method as an argument");
121+
if (isToolContextAcceptedByMethod && !isNonEmptyToolContextProvided) {
122+
throw new IllegalArgumentException("ToolContext is required by the method as an argument");
123123
}
124124
}
125125

Diff for: spring-ai-core/src/test/java/org/springframework/ai/tool/method/MethodToolCallbackTests.java

+4-6
Original file line numberDiff line numberDiff line change
@@ -69,23 +69,21 @@ void shouldHandleToolContextWhenSupported() {
6969
}
7070

7171
@Test
72-
void shouldThrowExceptionWhenToolContextNotSupported() {
73-
Method toolMethod = getMethod("publicMethod", PublicTools.class);
72+
void shouldThrowExceptionWhenToolContextArgumentIsMissing() {
73+
Method toolMethod = getMethod("methodWithToolContext", ToolContextTools.class);
7474
MethodToolCallback callback = MethodToolCallback.builder()
7575
.toolDefinition(ToolDefinition.from(toolMethod))
7676
.toolMetadata(ToolMetadata.from(toolMethod))
7777
.toolMethod(toolMethod)
7878
.toolObject(new PublicTools())
7979
.build();
8080

81-
ToolContext toolContext = new ToolContext(Map.of("key", "value"));
82-
8381
assertThatThrownBy(() -> callback.call("""
8482
{
8583
"input": "test"
8684
}
87-
""", toolContext)).isInstanceOf(IllegalArgumentException.class)
88-
.hasMessageContaining("ToolContext is not supported");
85+
""")).isInstanceOf(IllegalArgumentException.class)
86+
.hasMessageContaining("ToolContext is required by the method as an argument");
8987
}
9088

9189
@Test

0 commit comments

Comments
 (0)