Skip to content

Commit a409489

Browse files
committed
Support no-args tools
1 parent 0715ab6 commit a409489

File tree

4 files changed

+30
-31
lines changed

4 files changed

+30
-31
lines changed

core/deployment/src/main/java/io/quarkiverse/langchain4j/deployment/ToolProcessor.java

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -155,13 +155,6 @@ public void handleTools(CombinedIndexBuildItem indexBuildItem,
155155
validationErrorFound = true;
156156
continue;
157157
}
158-
if (toolMethod.parametersCount() == 0) {
159-
validation.produce(
160-
new ValidationPhaseBuildItem.ValidationErrorBuildItem(new IllegalStateException(
161-
"Tool method '" + toolMethod.name() + "' on class '" + className
162-
+ "' must have at least one parameter")));
163-
validationErrorFound = true;
164-
}
165158
discoveredTools.put(toolMethod.name(), toolMethod.declaringClass());
166159

167160
if (Modifier.isPrivate(toolMethod.flags())) {
@@ -276,16 +269,19 @@ private static String generateInvoker(MethodInfo methodInfo, ClassOutput classOu
276269
MethodCreator invokeMc = classCreator.getMethodCreator(
277270
MethodDescriptor.ofMethod(implClassName, "invoke", Object.class, Object.class, Object[].class));
278271

279-
ResultHandle[] targetMethodHandles = null;
280-
if (methodInfo.parametersCount() != 0) {
272+
ResultHandle result;
273+
if (methodInfo.parametersCount() > 0) {
281274
List<ResultHandle> argumentHandles = new ArrayList<>(methodInfo.parametersCount());
282275
for (int i = 0; i < methodInfo.parametersCount(); i++) {
283276
argumentHandles.add(invokeMc.readArrayValue(invokeMc.getMethodParam(1), i));
284277
}
285-
targetMethodHandles = argumentHandles.toArray(new ResultHandle[0]);
278+
ResultHandle[] targetMethodHandles = argumentHandles.toArray(new ResultHandle[0]);
279+
result = invokeMc.invokeVirtualMethod(MethodDescriptor.of(methodInfo), invokeMc.getMethodParam(0),
280+
targetMethodHandles);
281+
} else {
282+
result = invokeMc.invokeVirtualMethod(MethodDescriptor.of(methodInfo), invokeMc.getMethodParam(0));
286283
}
287-
ResultHandle result = invokeMc.invokeVirtualMethod(MethodDescriptor.of(methodInfo), invokeMc.getMethodParam(0),
288-
targetMethodHandles);
284+
289285
boolean toolReturnsVoid = methodInfo.returnType().kind() == Type.Kind.VOID;
290286
if (toolReturnsVoid) {
291287
invokeMc.returnValue(invokeMc.load("Success"));

core/deployment/src/test/java/io/quarkiverse/langchain4j/test/ToolExecutorTest.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import org.jboss.shrinkwrap.api.ShrinkWrap;
1212
import org.jboss.shrinkwrap.api.spec.JavaArchive;
1313
import org.junit.jupiter.api.Disabled;
14+
import org.junit.jupiter.api.Test;
1415
import org.junit.jupiter.api.extension.RegisterExtension;
1516
import org.junit.jupiter.params.ParameterizedTest;
1617
import org.junit.jupiter.params.provider.ValueSource;
@@ -73,6 +74,11 @@ byte bytes(byte arg0, Byte arg1) {
7374
BigInteger bigIntegers(BigInteger arg0, BigInteger arg1) {
7475
return arg0.add(arg1);
7576
}
77+
78+
@Tool
79+
int noArgs() {
80+
return 1;
81+
}
7682
}
7783

7884
@ParameterizedTest
@@ -233,6 +239,18 @@ void should_execute_tool_with_parameters_of_type_BigInteger(String arguments) {
233239
executeAndAssert(arguments, "bigIntegers", "4");
234240
}
235241

242+
@Test
243+
void should_execute_tool_with_no_args() {
244+
ToolExecutionRequest request = ToolExecutionRequest.builder()
245+
.build();
246+
247+
ToolExecutor toolExecutor = getToolExecutor("noArgs");
248+
249+
String result = toolExecutor.execute(request, null);
250+
251+
assertThat(result).isEqualTo("1");
252+
}
253+
236254
private void executeAndAssert(String arguments, String methodName,
237255
String expectedResult) {
238256
ToolExecutionRequest request = ToolExecutionRequest.builder()

core/runtime/src/main/java/io/quarkiverse/langchain4j/runtime/tool/QuarkusToolExecutor.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import java.lang.reflect.InvocationTargetException;
44
import java.util.Arrays;
5+
import java.util.Collections;
56
import java.util.Map;
67
import java.util.function.BiFunction;
78

@@ -111,6 +112,9 @@ private Object[] prepareArguments(ToolExecutionRequest toolExecutionRequest,
111112
}
112113

113114
private Map<String, Object> convertJsonToArguments(String argumentsJsonStr) throws JsonProcessingException {
115+
if (argumentsJsonStr == null || argumentsJsonStr.isEmpty()) {
116+
return Collections.emptyMap();
117+
}
114118
Mappable mappable = QuarkusJsonCodecFactory.ObjectMapperHolder.MAPPER.readValue(argumentsJsonStr, loadMapperClass());
115119
return mappable.obtainFieldValuesMap();
116120
}

openai/openai-vanilla/deployment/src/test/java/org/acme/examples/aiservices/ToolValidationTest.java

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -39,25 +39,6 @@ void test() {
3939
}
4040
}
4141

42-
@Nested
43-
@DisplayName("No parameter detection")
44-
class NoParameterTools {
45-
@RegisterExtension
46-
static final QuarkusUnitTest unitTest = new QuarkusUnitTest()
47-
.setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class).addClasses(MyInvalidTool.class))
48-
.assertException(t -> {
49-
assertThat(t)
50-
.isInstanceOf(DeploymentException.class)
51-
.hasCauseInstanceOf(IllegalStateException.class)
52-
.hasMessageContaining("Tool method 'myTool' on class");
53-
});
54-
55-
@Test
56-
void test() {
57-
fail("Should not be called");
58-
}
59-
}
60-
6142
@Nested
6243
@DisplayName("Abstract tool detection")
6344
class AbstractTools {

0 commit comments

Comments
 (0)