diff --git a/core/runtime/src/main/java/io/quarkiverse/langchain4j/QuarkusJsonCodecFactory.java b/core/runtime/src/main/java/io/quarkiverse/langchain4j/QuarkusJsonCodecFactory.java index 263f237d4..2051251cb 100644 --- a/core/runtime/src/main/java/io/quarkiverse/langchain4j/QuarkusJsonCodecFactory.java +++ b/core/runtime/src/main/java/io/quarkiverse/langchain4j/QuarkusJsonCodecFactory.java @@ -31,6 +31,9 @@ public Json.JsonCodec create() { private static class Codec implements Json.JsonCodec { + private static final String JSON_START_MARKER = "```json\n"; + private static final String JSON_END_MARKER = "\n```"; + @Override public String toJson(Object o) { try { @@ -43,7 +46,8 @@ public String toJson(Object o) { @Override public T fromJson(String json, Class type) { try { - return ObjectMapperHolder.MAPPER.readValue(json, type); + String sanitizedJson = sanitize(json, type); + return ObjectMapperHolder.MAPPER.readValue(sanitizedJson, type); } catch (JsonProcessingException e) { if ((e instanceof JsonParseException) && (type.isEnum())) { // this is the case where LangChain4j simply passes the string value of the enum to Json.fromJson() @@ -55,6 +59,16 @@ public T fromJson(String json, Class type) { } } + private String sanitize(String original, Class type) { + if (String.class.equals(type)) { + return original; + } + if (original.startsWith(JSON_START_MARKER) && original.endsWith(JSON_END_MARKER)) { + return original.substring(JSON_START_MARKER.length(), original.length() - JSON_END_MARKER.length()); + } + return original; + } + @Override public InputStream toInputStream(Object o, Class type) throws IOException { return new ByteArrayInputStream(ObjectMapperHolder.WRITER.writeValueAsBytes(o)); diff --git a/model-providers/openai/openai-vanilla/deployment/src/test/java/org/acme/examples/aiservices/AiServicesTest.java b/model-providers/openai/openai-vanilla/deployment/src/test/java/org/acme/examples/aiservices/AiServicesTest.java index 614753458..844f90fe2 100644 --- a/model-providers/openai/openai-vanilla/deployment/src/test/java/org/acme/examples/aiservices/AiServicesTest.java +++ b/model-providers/openai/openai-vanilla/deployment/src/test/java/org/acme/examples/aiservices/AiServicesTest.java @@ -295,7 +295,7 @@ interface Chef { void test_create_recipe_from_list_of_ingredients() throws IOException { setChatCompletionMessageContent( // this is supposed to be a string inside a json string hence all the escaping... - "{\\n\\\"title\\\": \\\"Greek Salad\\\",\\n\\\"description\\\": \\\"A refreshing and tangy salad with Mediterranean flavors.\\\",\\n\\\"steps\\\": [\\n\\\"Chop, dice, and slice.\\\",\\n\\\"Mix veggies with feta.\\\",\\n\\\"Drizzle with olive oil.\\\",\\n\\\"Toss gently, then serve.\\\"\\n],\\n\\\"preparationTimeMinutes\\\": 15\\n}"); + "```json\\n{\\n\\\"title\\\": \\\"Greek Salad\\\",\\n\\\"description\\\": \\\"A refreshing and tangy salad with Mediterranean flavors.\\\",\\n\\\"steps\\\": [\\n\\\"Chop, dice, and slice.\\\",\\n\\\"Mix veggies with feta.\\\",\\n\\\"Drizzle with olive oil.\\\",\\n\\\"Toss gently, then serve.\\\"\\n],\\n\\\"preparationTimeMinutes\\\": 15\\n}\\n```"); Chef chef = AiServices.create(Chef.class, createChatModel()); Recipe result = chef.createRecipeFrom("cucumber", "tomato", "feta", "onion", "olives");