Skip to content

Commit 0a4b974

Browse files
mp2163wilocu
authored andcommitted
Issue 3924
Signed-off-by: Mattia Pasetto <[email protected]>
1 parent c122fe1 commit 0a4b974

File tree

2 files changed

+113
-1
lines changed

2 files changed

+113
-1
lines changed

spring-ai-model/src/main/java/org/springframework/ai/tool/method/MethodToolCallback.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,15 @@ public String call(String toolInput, @Nullable ToolContext toolContext) {
104104

105105
Map<String, Object> toolArguments = extractToolArguments(toolInput);
106106

107-
Object[] methodArguments = buildMethodArguments(toolArguments, toolContext);
107+
Object[] methodArguments;
108+
try {
109+
methodArguments = buildMethodArguments(toolArguments, toolContext);
110+
}
111+
catch (IllegalArgumentException ex) {
112+
logger.debug("Failed to build method arguments for tool: {}. Error: {}", this.toolDefinition.name(),
113+
ex.getMessage());
114+
throw new ToolExecutionException(this.toolDefinition, ex);
115+
}
108116

109117
Object result = callMethod(methodArguments);
110118

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
2+
/*
3+
* Copyright 2023-2025 the original author or authors.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* https://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
package org.springframework.ai.tool.method;
19+
20+
import java.lang.reflect.Method;
21+
22+
import org.junit.jupiter.api.Test;
23+
24+
import org.springframework.ai.tool.annotation.Tool;
25+
import org.springframework.ai.tool.definition.DefaultToolDefinition;
26+
import org.springframework.ai.tool.definition.ToolDefinition;
27+
import org.springframework.ai.tool.execution.ToolExecutionException;
28+
29+
import static org.assertj.core.api.Assertions.assertThat;
30+
import static org.assertj.core.api.Assertions.assertThatThrownBy;
31+
32+
/**
33+
* Unit tests for {@link MethodToolCallback}.
34+
*
35+
* @author Thomas Vitale
36+
*/
37+
class MethodToolCallbackTests {
38+
39+
@Test
40+
void shouldThrowToolExecutionExceptionForInvalidEnumArgument() throws Exception {
41+
// Given
42+
TestToolClass testTool = new TestToolClass();
43+
Method method = TestToolClass.class.getDeclaredMethod("processOrder", OrderType.class);
44+
ToolDefinition toolDefinition = DefaultToolDefinition.builder()
45+
.name("processOrder")
46+
.description("Process an order")
47+
.inputSchema("{}")
48+
.build();
49+
50+
MethodToolCallback callback = new MethodToolCallback(toolDefinition, null, method, testTool, null);
51+
52+
// When/Then - Invalid enum value should throw ToolExecutionException
53+
assertThatThrownBy(() -> callback.call("{\"orderType\": \"INVALID_TYPE\"}"))
54+
.isInstanceOf(ToolExecutionException.class)
55+
.hasCauseInstanceOf(IllegalArgumentException.class)
56+
.hasMessageContaining("No enum constant");
57+
58+
// Verify the ToolExecutionException contains the correct tool definition
59+
try {
60+
callback.call("{\"orderType\": \"INVALID_TYPE\"}");
61+
}
62+
catch (ToolExecutionException ex) {
63+
assertThat(ex.getToolDefinition()).isEqualTo(toolDefinition);
64+
assertThat(ex.getCause()).isInstanceOf(IllegalArgumentException.class);
65+
}
66+
}
67+
68+
@Test
69+
void shouldSucceedWithValidEnumArgument() throws Exception {
70+
// Given
71+
TestToolClass testTool = new TestToolClass();
72+
Method method = TestToolClass.class.getDeclaredMethod("processOrder", OrderType.class);
73+
ToolDefinition toolDefinition = DefaultToolDefinition.builder()
74+
.name("processOrder")
75+
.description("Process an order")
76+
.inputSchema("{}")
77+
.build();
78+
79+
MethodToolCallback callback = new MethodToolCallback(toolDefinition, null, method, testTool, null);
80+
81+
// When
82+
String result = callback.call("{\"orderType\": \"ONE_DAY\"}");
83+
84+
// Then
85+
assertThat(result).isEqualTo("\"Processing ONE_DAY order\"");
86+
}
87+
88+
// Test classes
89+
static class TestToolClass {
90+
91+
@Tool(description = "Process an order with the specified delivery type")
92+
public String processOrder(OrderType orderType) {
93+
return "Processing " + orderType + " order";
94+
}
95+
96+
}
97+
98+
enum OrderType {
99+
100+
ONE_DAY, TWO_DAY, THREE_DAY
101+
102+
}
103+
104+
}

0 commit comments

Comments
 (0)