Skip to content

Commit 5f09c21

Browse files
committed
refactor: simplify method invocation builder API
Merges method() and argumentTypes() builder methods into a single method() call for better usability and compile-time safety. Updates all tests and documentation to use the new API.
1 parent 5c92f06 commit 5f09c21

File tree

6 files changed

+24
-46
lines changed

6 files changed

+24
-46
lines changed

models/spring-ai-anthropic/src/test/java/org/springframework/ai/anthropic/client/AnthropicChatClientMethodFunctionCallbackIT.java

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -59,9 +59,8 @@ void methodGetWeatherStatic() {
5959
.user("What's the weather like in San Francisco, Tokyo, and Paris? Use Celsius.")
6060
.functions(FunctionCallback.builder()
6161
.description("Get the weather in location")
62-
.method("getWeatherStatic")
62+
.method("getWeatherStatic", String.class, Unit.class)
6363
.targetClass(TestFunctionClass.class)
64-
.argumentTypes(String.class, Unit.class)
6564
.build())
6665
.call()
6766
.content();
@@ -82,9 +81,8 @@ void methodTurnLightNoResponse() {
8281
.user("Turn light on in the living room.")
8382
.functions(FunctionCallback.builder()
8483
.description("Turn light on in the living room.")
85-
.method("turnLight")
84+
.method("turnLight", String.class, boolean.class)
8685
.targetObject(targetObject)
87-
.argumentTypes(String.class, boolean.class)
8886
.build())
8987
.call()
9088
.content();
@@ -106,8 +104,7 @@ void methodGetWeatherNonStatic() {
106104
.user("What's the weather like in San Francisco, Tokyo, and Paris? Use Celsius.")
107105
.functions(FunctionCallback.builder()
108106
.description("Get the weather in location")
109-
.method("getWeatherNonStatic")
110-
.argumentTypes(String.class, Unit.class)
107+
.method("getWeatherNonStatic",String.class, Unit.class)
111108
.targetObject(targetObject)
112109
.build())
113110
.call()
@@ -129,8 +126,7 @@ void methodGetWeatherToolContext() {
129126
.user("What's the weather like in San Francisco, Tokyo, and Paris? Use Celsius.")
130127
.functions(FunctionCallback.builder()
131128
.description("Get the weather in location")
132-
.method("getWeatherWithContext")
133-
.argumentTypes(String.class, Unit.class, ToolContext.class)
129+
.method("getWeatherWithContext", String.class, Unit.class, ToolContext.class)
134130
.targetObject(targetObject)
135131
.build())
136132
.toolContext(Map.of("tool", "value"))
@@ -154,8 +150,7 @@ void methodGetWeatherToolContextButNonContextMethod() {
154150
.user("What's the weather like in San Francisco, Tokyo, and Paris? Use Celsius.")
155151
.functions(FunctionCallback.builder()
156152
.description("Get the weather in location")
157-
.method("getWeatherNonStatic")
158-
.argumentTypes(String.class, Unit.class)
153+
.method("getWeatherNonStatic", String.class, Unit.class)
159154
.targetObject(targetObject)
160155
.build())
161156
.toolContext(Map.of("tool", "value"))

models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/client/OpenAiChatClientMethodFunctionCallbackIT.java

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,7 @@ void methodGetWeatherStatic() {
6161
.user("What's the weather like in San Francisco, Tokyo, and Paris? Use Celsius.")
6262
.functions(FunctionCallback.builder()
6363
.description("Get the weather in location")
64-
.method("getWeatherStatic")
65-
.argumentTypes(String.class, Unit.class)
64+
.method("getWeatherStatic",String.class, Unit.class)
6665
.targetClass(TestFunctionClass.class)
6766
.build())
6867
.call()
@@ -84,8 +83,7 @@ void methodTurnLightNoResponse() {
8483
.user("Turn light on in the living room.")
8584
.functions(FunctionCallback.builder()
8685
.description("Can turn lights on or off by room name")
87-
.method("turnLight")
88-
.argumentTypes(String.class, boolean.class)
86+
.method("turnLight", String.class, boolean.class)
8987
.targetObject(targetObject)
9088
.build())
9189
.call()
@@ -108,8 +106,7 @@ void methodGetWeatherNonStatic() {
108106
.user("What's the weather like in San Francisco, Tokyo, and Paris? Use Celsius.")
109107
.functions(FunctionCallback.builder()
110108
.description("Get the weather in location")
111-
.method("getWeatherNonStatic")
112-
.argumentTypes(String.class, Unit.class)
109+
.method("getWeatherNonStatic",String.class, Unit.class)
113110
.targetObject(targetObject)
114111
.build())
115112
.call()
@@ -131,8 +128,7 @@ void methodGetWeatherToolContext() {
131128
.user("What's the weather like in San Francisco, Tokyo, and Paris? Use Celsius.")
132129
.functions(FunctionCallback.builder()
133130
.description("Get the weather in location")
134-
.method("getWeatherWithContext")
135-
.argumentTypes(String.class, Unit.class, ToolContext.class)
131+
.method("getWeatherWithContext", String.class, Unit.class, ToolContext.class)
136132
.targetObject(targetObject)
137133
.build())
138134
.toolContext(Map.of("tool", "value"))
@@ -156,8 +152,7 @@ void methodGetWeatherToolContextButNonContextMethod() {
156152
.user("What's the weather like in San Francisco, Tokyo, and Paris? Use Celsius.")
157153
.functions(FunctionCallback.builder()
158154
.description("Get the weather in location")
159-
.method("getWeatherNonStatic")
160-
.argumentTypes(String.class, Unit.class)
155+
.method("getWeatherNonStatic", String.class, Unit.class)
161156
.targetObject(targetObject)
162157
.build())
163158
.toolContext(Map.of("tool", "value"))

spring-ai-core/src/main/java/org/springframework/ai/model/function/DefaultFunctionCallbackBuilder.java

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -118,9 +118,8 @@ public <I, O> FunctionInvokerBuilder<I, O> function(BiFunction<I, ToolContext, O
118118
}
119119

120120
@Override
121-
public MethodInvokerBuilder method(String methodName) {
122-
Assert.hasText(methodName, "Method name must not be empty");
123-
return new MethodInvokerBuilderImpl(methodName);
121+
public MethodInvokerBuilder method(String methodName, Class<?>... argumentTypes) {
122+
return new MethodInvokerBuilderImpl(methodName, argumentTypes);
124123
}
125124

126125
public class FunctionInvokerBuilderImpl<I, O> implements FunctionInvokerBuilder<I, O> {
@@ -196,10 +195,13 @@ public class MethodInvokerBuilderImpl implements FunctionCallback.MethodInvokerB
196195

197196
private Object targetObject;
198197

199-
private Class<?>[] arguments = new Class[0];
198+
private final Class<?>[] argumentTypes;
200199

201-
private MethodInvokerBuilderImpl(String methodName) {
200+
private MethodInvokerBuilderImpl(String methodName, Class<?>... argumentTypes) {
201+
Assert.hasText(methodName, "Method name must not be null");
202+
Assert.notNull(argumentTypes, "Argument types must not be null");
202203
this.methodName = methodName;
204+
this.argumentTypes = argumentTypes;
203205
}
204206

205207
public MethodInvokerBuilder targetClass(Class<?> targetClass) {
@@ -216,18 +218,11 @@ public MethodInvokerBuilder targetObject(Object methodObject) {
216218
return this;
217219
}
218220

219-
@Override
220-
public MethodInvokerBuilder argumentTypes(Class<?>... arguments) {
221-
Assert.notNull(arguments, "Arguments must not be null");
222-
this.arguments = arguments;
223-
return this;
224-
}
225-
226221
@Override
227222
public FunctionCallback build() {
228223
Assert.isTrue(this.targetClass != null || this.targetObject != null,
229224
"Target class or object must not be null");
230-
var method = ReflectionUtils.findMethod(targetClass, methodName, arguments);
225+
var method = ReflectionUtils.findMethod(targetClass, methodName, argumentTypes);
231226
return new MethodFunctionCallback(this.targetObject, method, description, objectMapper);
232227
}
233228

spring-ai-core/src/main/java/org/springframework/ai/model/function/FunctionCallback.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ interface Builder {
132132

133133
<I, O> FunctionInvokerBuilder<I, O> function(BiFunction<I, ToolContext, O> biFunction);
134134

135-
MethodInvokerBuilder method(String methodName);
135+
MethodInvokerBuilder method(String methodName, Class<?>... argumentTypes);
136136

137137
}
138138

@@ -169,8 +169,6 @@ interface MethodInvokerBuilder {
169169

170170
MethodInvokerBuilder targetClass(Class<?> targetClass);
171171

172-
MethodInvokerBuilder argumentTypes(Class<?>... arguments);
173-
174172
/**
175173
* Builds the {@link FunctionCallback} instance.
176174
*/

spring-ai-core/src/test/java/org/springframework/ai/model/function/MethodFunctionCallbackTests.java

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -58,9 +58,8 @@ public void staticMethod() throws NoSuchMethodException, SecurityException {
5858
var functionCallback = FunctionCallback.builder()
5959
.description("weather at location")
6060
.objectMapper(new ObjectMapper())
61-
.method("myStaticMethod")
61+
.method("myStaticMethod", String.class, Unit.class, int.class, MyRecord.class, List.class)
6262
.targetClass(TestClassWithFunctionMethods.class)
63-
.argumentTypes(String.class, Unit.class, int.class, MyRecord.class, List.class)
6463
.build();
6564

6665
String response = functionCallback.call(this.value);
@@ -82,9 +81,8 @@ public void nonStaticMethod() throws NoSuchMethodException, SecurityException {
8281

8382
var functionCallback = FunctionCallback.builder()
8483
.description("weather at location")
85-
.method("myNonStaticMethod")
84+
.method("myNonStaticMethod", String.class, Unit.class, int.class, MyRecord.class, List.class)
8685
.targetObject(object)
87-
.argumentTypes(String.class, Unit.class, int.class, MyRecord.class, List.class)
8886
.build();
8987

9088
String response = functionCallback.call(this.value);

spring-ai-docs/src/main/antora/modules/ROOT/pages/api/functions.adoc

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -314,8 +314,7 @@ You need the `FunctionCallback.Builder` to create `MethodFunctionCallback` like
314314
FunctionCallback callback = FunctionCallback.builder()
315315
.description("Method description") // Required: Helps AI understand the function
316316
.objectMapper(objectMapper) // Optional: Custom ObjectMapper
317-
.method("MethodName") // Required: The method to invoke
318-
.argumentTypes(Class<?>...argumentTypes) // Required unless method has no arguments
317+
.method("MethodName", Class<?>...argumentTypes) // Required: The method to invoke and its argument types
319318
.targetObject(targetObject) // Required only for instance methods
320319
.build();
321320
----
@@ -337,9 +336,8 @@ public class WeatherService {
337336
// Usage
338337
FunctionCallback callback = FunctionCallback.builder()
339338
.description("Get weather information for a city")
340-
.method("getWeather")
339+
.method("getWeather", String.class, TemperatureUnit.class)
341340
.targetClass(WeatherService.class)
342-
.argumentTypes(String.class, TemperatureUnit.class)
343341
.build();
344342
----
345343
Instance Method with ToolContext::
@@ -360,8 +358,7 @@ String response = ChatClient.create(chatModel).prompt()
360358
.user("Turn on the living room lights")
361359
.functions(FunctionCallback.builder()
362360
.description("Control device state")
363-
.method("setDeviceState")
364-
.argumentTypes(String.class,boolean.class,ToolContext.class)
361+
.method("setDeviceState", String.class,boolean.class,ToolContext.class)
365362
.targetObject(controller)
366363
.build())
367364
.toolContext(Map.of("location", "home"))

0 commit comments

Comments
 (0)