diff --git a/langchain4j-core/src/main/java/dev/langchain4j/agent/tool/ToolExecutionRequestUtil.java b/langchain4j-core/src/main/java/dev/langchain4j/agent/tool/ToolExecutionRequestUtil.java index 374969ca260..c6bcaf58922 100644 --- a/langchain4j-core/src/main/java/dev/langchain4j/agent/tool/ToolExecutionRequestUtil.java +++ b/langchain4j-core/src/main/java/dev/langchain4j/agent/tool/ToolExecutionRequestUtil.java @@ -11,6 +11,9 @@ * Utility class for {@link ToolExecutionRequest}. */ public class ToolExecutionRequestUtil { + + private static final Pattern TRAILING_COMMA_PATTERN = Pattern.compile(",(\\s*[}\\]])"); + private ToolExecutionRequestUtil() {} /** @@ -52,12 +55,11 @@ public static Map argumentsAsMap(String arguments) { * @param json the JSON string * @return the corrected JSON string */ - private static String removeTrailingComma(String json) { + static String removeTrailingComma(String json) { if (json == null || json.isEmpty()) { return json; } - Pattern pattern = Pattern.compile(",(\\s*[}\\]])"); - Matcher matcher = pattern.matcher(json); + Matcher matcher = TRAILING_COMMA_PATTERN.matcher(json); return matcher.replaceAll("$1"); } } diff --git a/langchain4j-core/src/test/java/dev/langchain4j/agent/tool/ToolExecutionRequestUtilTest.java b/langchain4j-core/src/test/java/dev/langchain4j/agent/tool/ToolExecutionRequestUtilTest.java index 45221d329f2..3a6452d6221 100644 --- a/langchain4j-core/src/test/java/dev/langchain4j/agent/tool/ToolExecutionRequestUtilTest.java +++ b/langchain4j-core/src/test/java/dev/langchain4j/agent/tool/ToolExecutionRequestUtilTest.java @@ -1,12 +1,18 @@ package dev.langchain4j.agent.tool; -import static org.junit.jupiter.api.Assertions.assertTrue; - import com.google.gson.internal.LinkedTreeMap; -import java.util.ArrayList; -import java.util.List; import org.assertj.core.api.WithAssertions; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; class ToolExecutionRequestUtilTest implements WithAssertions { @Test @@ -69,4 +75,79 @@ public void test_argument_comma_array() { assertThat(((LinkedTreeMap) key).get("qux")).isEqualTo(12.0); }); } + + @ParameterizedTest + @MethodSource + void should_remove_trailing_comma(String inputJson, String expectedOutputJson) { + String outputJson = ToolExecutionRequestUtil.removeTrailingComma(inputJson); + assertEquals(expectedOutputJson, outputJson); + } + + private static Stream should_remove_trailing_comma() { + return Stream.of( + Arguments.of( + "{\"name\":\"John\",\"age\":30}", + "{\"name\":\"John\",\"age\":30}" + ), + Arguments.of( + "{\"name\":\"John\",\"age\":30,}", + "{\"name\":\"John\",\"age\":30}" + ), + Arguments.of( + "[\"apple\",\"banana\"]", + "[\"apple\",\"banana\"]" + ), + Arguments.of( + "[\"apple\",\"banana\",]", + "[\"apple\",\"banana\"]" + ), + Arguments.of( + "{\"person\":{\"name\":\"John\",\"age\":30},\"city\":\"New York\"}", + "{\"person\":{\"name\":\"John\",\"age\":30},\"city\":\"New York\"}" + ), + Arguments.of( + "{\"person\":{\"name\":\"John\",\"age\":30,},\"city\":\"New York\",}", + "{\"person\":{\"name\":\"John\",\"age\":30},\"city\":\"New York\"}" + ), + Arguments.of( + "[{\"name\":\"John\",\"hobbies\":[\"reading\",\"swimming\"]}]", + "[{\"name\":\"John\",\"hobbies\":[\"reading\",\"swimming\"]}]" + ), + Arguments.of( + "[{\"name\":\"John\",\"hobbies\":[\"reading\",\"swimming\",],},]", + "[{\"name\":\"John\",\"hobbies\":[\"reading\",\"swimming\"]}]" + ), + Arguments.of( + "{,}", + "{}" + ), + Arguments.of( + "[,]", + "[]" + ), + Arguments.of( + "{\"name\":\"John\"}", + "{\"name\":\"John\"}" + ), Arguments.of( + "{\"name\":\"John\",}", + "{\"name\":\"John\"}" + ), + Arguments.of( + "{\"a\":[{\"b\":{\"c\":[],},\"d\":{},},],\"e\":\"value\",}", + "{\"a\":[{\"b\":{\"c\":[]},\"d\":{}}],\"e\":\"value\"}" + ), + Arguments.of( + "{\"a\":\"value1\", /*comment*/ \"b\":\"value2\",}", + "{\"a\":\"value1\", /*comment*/ \"b\":\"value2\"}" + ), + Arguments.of( + "{ \"name\" : \"John\" , \"age\" : 30 , }", + "{ \"name\" : \"John\" , \"age\" : 30 }" + ), + Arguments.of( + "{\n \"name\": \"John\",\n \"age\": 30,\n}", + "{\n \"name\": \"John\",\n \"age\": 30\n}" + ) + ); + } } \ No newline at end of file