This is an automated email from the ASF dual-hosted git repository. jamesnetherton pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/camel-quarkus-examples.git
commit 7250eb026ec7732d1ebb5fc94d7397f1df252b94 Author: James Netherton <[email protected]> AuthorDate: Mon Sep 28 08:25:13 2026 +0100 Reinstate Quarkus LangChain4j in data-extract example --- .github/dependabot.yml | 1 - data-extract-langchain4j/README.adoc | 4 +- data-extract-langchain4j/pom.xml | 17 +++--- .../extraction/CustomPojoExtractionService.java | 11 +++- .../java/org/acme/extraction/CustomPojoStore.java | 2 +- .../java/org/acme/extraction/DataExtractAgent.java | 65 ---------------------- .../extraction/DataExtractAgentConfiguration.java | 58 ------------------- .../src/main/java/org/acme/extraction/Routes.java | 13 +++-- .../src/main/resources/application.properties | 16 +++++- .../org/acme/extraction/OllamaTestResource.java | 4 +- ...chat-1185138d-1018-48a9-9212-5227e0eebf90.json} | 10 ++-- ...chat-5cf07a94-1704-41fd-aaed-c3279242686a.json} | 10 ++-- ...chat-f33209a6-d198-4e89-983c-9916b795ca98.json} | 10 ++-- 13 files changed, 57 insertions(+), 164 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 2106e5b8..eb47bee0 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -44,7 +44,6 @@ updates: allow: # Quarkiverse extensions - dependency-name: "io.quarkiverse.*:*" - - dependency-name: "dev.langchain4j:*" - dependency-name: "org.zeroturnaround:zt-exec" # Test dependencies - dependency-name: "org.assertj:assertj-core" diff --git a/data-extract-langchain4j/README.adoc b/data-extract-langchain4j/README.adoc index 6f3cafe9..01febca4 100644 --- a/data-extract-langchain4j/README.adoc +++ b/data-extract-langchain4j/README.adoc @@ -113,9 +113,7 @@ Notice how all of this is computed simultaneously during a single LLM inference. At the end, the application should have extracted 3 POJOs. For each of them, it could be interesting to compare the unstructured input text and the corresponding structured POJO. -Details of the LangChain4j `AiService` setup can be found in class `CustomPojoExtractionService`. - -Details of the custom data extract `camel-langchain4j-agent` AI agent implementation can be found in classes `DataExtractAgent` and `DataExtractAgentConfiguration`. +More details can be found in the `src/main/java/org/acme/extraction/CustomPojoExtractionService.java` class, a https://docs.quarkiverse.io/quarkus-langchain4j/dev/index.html[Quarkus LangChain4j] AI service that is invoked from the Camel route with the `bean` EIP. ==== Native mode diff --git a/data-extract-langchain4j/pom.xml b/data-extract-langchain4j/pom.xml index 087d222c..519bdb2e 100644 --- a/data-extract-langchain4j/pom.xml +++ b/data-extract-langchain4j/pom.xml @@ -52,7 +52,8 @@ <maven-resources-plugin.version>3.3.1</maven-resources-plugin.version> <maven-surefire-plugin.version>3.5.6</maven-surefire-plugin.version> - <langchain4j.version>1.19.3</langchain4j.version> + <!-- the Quarkus LangChain4j release Camel Quarkus is built against --> + <quarkiverse-langchain4j.version>1.13.3</quarkiverse-langchain4j.version> <wiremock.version>3.13.2</wiremock.version> </properties> @@ -60,9 +61,9 @@ <dependencies> <!-- Import BOM --> <dependency> - <groupId>dev.langchain4j</groupId> - <artifactId>langchain4j-bom</artifactId> - <version>${langchain4j.version}</version> + <groupId>io.quarkiverse.langchain4j</groupId> + <artifactId>quarkus-langchain4j-bom</artifactId> + <version>${quarkiverse-langchain4j.version}</version> <type>pom</type> <scope>import</scope> </dependency> @@ -100,17 +101,13 @@ <groupId>org.apache.camel.quarkus</groupId> <artifactId>camel-quarkus-platform-http</artifactId> </dependency> - <dependency> - <groupId>org.apache.camel.quarkus</groupId> - <artifactId>camel-quarkus-langchain4j-agent</artifactId> - </dependency> <dependency> <groupId>org.apache.camel.quarkus</groupId> <artifactId>camel-quarkus-bean</artifactId> </dependency> <dependency> - <groupId>dev.langchain4j</groupId> - <artifactId>langchain4j-ollama</artifactId> + <groupId>io.quarkiverse.langchain4j</groupId> + <artifactId>quarkus-langchain4j-ollama</artifactId> </dependency> <!-- Test --> diff --git a/data-extract-langchain4j/src/main/java/org/acme/extraction/CustomPojoExtractionService.java b/data-extract-langchain4j/src/main/java/org/acme/extraction/CustomPojoExtractionService.java index 521e2e5a..0c1f6381 100644 --- a/data-extract-langchain4j/src/main/java/org/acme/extraction/CustomPojoExtractionService.java +++ b/data-extract-langchain4j/src/main/java/org/acme/extraction/CustomPojoExtractionService.java @@ -22,9 +22,17 @@ import java.util.Locale; import com.fasterxml.jackson.annotation.JsonProperty; import dev.langchain4j.service.UserMessage; import dev.langchain4j.service.V; +import io.quarkiverse.langchain4j.RegisterAiService; import io.quarkus.runtime.annotations.RegisterForReflection; +import jakarta.enterprise.context.ApplicationScoped; +import org.apache.camel.Handler; -@RegisterForReflection +/** + * The AI service is implemented by Quarkus LangChain4j and backed by the Ollama chat model configured in + * application.properties. No chat memory is used, so that each extraction is independent of the previous ones. + */ +@RegisterAiService(chatMemoryProviderSupplier = RegisterAiService.NoChatMemoryProviderSupplier.class) +@ApplicationScoped public interface CustomPojoExtractionService { @RegisterForReflection(registerFullHierarchy = true) @@ -66,5 +74,6 @@ public interface CustomPojoExtractionService { * The text parameter of this method is automatically injected as {{text}} in the CUSTOM_POJO_EXTRACT_PROMPT. */ @UserMessage(CUSTOM_POJO_EXTRACT_PROMPT) + @Handler CustomPojo extractFromText(@V("text") String text); } diff --git a/data-extract-langchain4j/src/main/java/org/acme/extraction/CustomPojoStore.java b/data-extract-langchain4j/src/main/java/org/acme/extraction/CustomPojoStore.java index 4c827ef5..32ca2821 100644 --- a/data-extract-langchain4j/src/main/java/org/acme/extraction/CustomPojoStore.java +++ b/data-extract-langchain4j/src/main/java/org/acme/extraction/CustomPojoStore.java @@ -30,13 +30,13 @@ public class CustomPojoStore { private static final Logger LOG = Logger.getLogger(CustomPojoStore.class); private List<CustomPojo> pojos = new CopyOnWriteArrayList<>(); + @Handler CustomPojo addPojo(CustomPojo pojo) { LOG.info("An extracted POJO has been added to the store: " + pojo); pojos.add(pojo); return pojo; } - @Handler Map<String, List<CustomPojo>> listPojos() { return Map.of("pojos", List.copyOf(pojos)); } diff --git a/data-extract-langchain4j/src/main/java/org/acme/extraction/DataExtractAgent.java b/data-extract-langchain4j/src/main/java/org/acme/extraction/DataExtractAgent.java deleted file mode 100644 index 652276e0..00000000 --- a/data-extract-langchain4j/src/main/java/org/acme/extraction/DataExtractAgent.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.acme.extraction; - -import dev.langchain4j.service.AiServices; -import dev.langchain4j.service.Result; -import dev.langchain4j.service.tool.ToolProvider; -import org.acme.extraction.CustomPojoExtractionService.CustomPojo; -import org.apache.camel.component.langchain4j.agent.api.Agent; -import org.apache.camel.component.langchain4j.agent.api.AgentConfiguration; -import org.apache.camel.component.langchain4j.agent.api.AiAgentBody; - -/** - * Custom agent to extract data from text. - */ -public class DataExtractAgent implements Agent { - private final AgentConfiguration configuration; - private final CustomPojoStore pojoStore; - - public DataExtractAgent( - AgentConfiguration configuration, - CustomPojoStore pojoStore) { - this.configuration = configuration; - this.pojoStore = pojoStore; - } - - /** - * Returns a string representation of the extracted {@link CustomPojo}. - */ - @Override - public Result<String> chat(AiAgentBody<?> aiAgentBody, ToolProvider toolProvider) { - CustomPojo response = createService(toolProvider).extractFromText(aiAgentBody.getUserMessage()); - - // Store extracted CustomPojoExtractionService.CustomPojos objects into the CustomPojoStore for later inspection - pojoStore.addPojo(response); - - // Return a string representation of the result POJO - return Result.<String> builder().content(response.toString()).build(); - } - - CustomPojoExtractionService createService(ToolProvider toolProvider) { - var builder = AiServices.builder(CustomPojoExtractionService.class) - .chatModel(configuration.getChatModel()); - - if (toolProvider != null) { - builder.toolProvider(toolProvider); - } - - return builder.build(); - } -} diff --git a/data-extract-langchain4j/src/main/java/org/acme/extraction/DataExtractAgentConfiguration.java b/data-extract-langchain4j/src/main/java/org/acme/extraction/DataExtractAgentConfiguration.java deleted file mode 100644 index 3d63ca27..00000000 --- a/data-extract-langchain4j/src/main/java/org/acme/extraction/DataExtractAgentConfiguration.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.acme.extraction; - -import java.time.Duration; - -import dev.langchain4j.model.chat.Capability; -import dev.langchain4j.model.ollama.OllamaChatModel; -import io.smallrye.common.annotation.Identifier; -import jakarta.enterprise.context.ApplicationScoped; -import jakarta.inject.Singleton; -import org.apache.camel.component.langchain4j.agent.api.Agent; -import org.apache.camel.component.langchain4j.agent.api.AgentConfiguration; -import org.eclipse.microprofile.config.inject.ConfigProperty; - -@ApplicationScoped -public class DataExtractAgentConfiguration { - public static final String AGENT_ID = "data-extract-agent"; - - @ConfigProperty(name = "langchain4j.ollama.base-url") - String baseUrl; - - @ConfigProperty(name = "langchain4j.ollama.chat-model.model-id") - String chatModelId; - - @Singleton - AgentConfiguration agentConfiguration() { - return new AgentConfiguration().withChatModel(OllamaChatModel.builder() - .baseUrl(baseUrl) - .topK(1) - .topP(0.1) - .modelName(chatModelId) - .supportedCapabilities(Capability.RESPONSE_FORMAT_JSON_SCHEMA) - .temperature(0.0) - .timeout(Duration.ofMinutes(3)) - .build()); - } - - @Singleton - @Identifier(AGENT_ID) - Agent agent(AgentConfiguration configuration, CustomPojoStore pojoStore) { - return new DataExtractAgent(configuration, pojoStore); - } -} diff --git a/data-extract-langchain4j/src/main/java/org/acme/extraction/Routes.java b/data-extract-langchain4j/src/main/java/org/acme/extraction/Routes.java index dd55cdde..f8761150 100644 --- a/data-extract-langchain4j/src/main/java/org/acme/extraction/Routes.java +++ b/data-extract-langchain4j/src/main/java/org/acme/extraction/Routes.java @@ -21,11 +21,12 @@ import jakarta.inject.Inject; import org.apache.camel.builder.RouteBuilder; import org.apache.camel.model.dataformat.JsonLibrary; -import static org.acme.extraction.DataExtractAgentConfiguration.AGENT_ID; - @ApplicationScoped public class Routes extends RouteBuilder { + @Inject + CustomPojoExtractionService customPojoExtractionService; + @Inject CustomPojoStore customPojoStore; @@ -36,12 +37,14 @@ public class Routes extends RouteBuilder { .log("A document has been received by the camel-quarkus-file extension: ${body}") // Get the content to fulfill the CustomPojoExtractService text argument .setBody().jsonpath("$.content") - // Initiate a conversation with the LLM - .toF("langchain4j-agent:%s", AGENT_ID); + // The CustomPojoExtractionService transforms the conversation transcript into a CustomPojoExtractionService.CustomPojo + .bean(customPojoExtractionService) + // Store extracted CustomPojoExtractionService.CustomPojos objects into the CustomPojoStore for later inspection + .bean(customPojoStore); // This route make it possible to inspect the extracted POJOs, mainly used for demo and test from("platform-http:/custom-pojo-store?produces=application/json") - .bean(customPojoStore) + .bean(customPojoStore, "listPojos") .marshal().json(JsonLibrary.Jackson); } } diff --git a/data-extract-langchain4j/src/main/resources/application.properties b/data-extract-langchain4j/src/main/resources/application.properties index a673874b..a25ca99c 100644 --- a/data-extract-langchain4j/src/main/resources/application.properties +++ b/data-extract-langchain4j/src/main/resources/application.properties @@ -17,7 +17,17 @@ quarkus.banner.enabled = false quarkus.default-locale=en_US -# Adjust as per your where your Ollama instance is running -langchain4j.ollama.base-url=http://localhost:11434 +# Configure Quarkus LangChain4j that handles interactions with the Large Language Model +# Adjust as per where your Ollama instance is running +quarkus.langchain4j.ollama.base-url=http://localhost:11434 +quarkus.langchain4j.ollama.timeout=3m # The chat model to use -langchain4j.ollama.chat-model.model-id=granite4.1:3b +quarkus.langchain4j.ollama.chat-model.model-id=granite4.1:3b +quarkus.langchain4j.ollama.chat-model.temperature=0 +quarkus.langchain4j.ollama.chat-model.top-k=1 +quarkus.langchain4j.ollama.chat-model.top-p=0.1 +# Ollama is run by you rather than by Dev Services +quarkus.langchain4j.ollama.devservices.enabled=false +# Uncomment lines below to log Ollama client requests and responses +#quarkus.langchain4j.ollama.log-requests=true +#quarkus.langchain4j.ollama.log-responses=true diff --git a/data-extract-langchain4j/src/test/java/org/acme/extraction/OllamaTestResource.java b/data-extract-langchain4j/src/test/java/org/acme/extraction/OllamaTestResource.java index 8b9f809b..e46edd06 100644 --- a/data-extract-langchain4j/src/test/java/org/acme/extraction/OllamaTestResource.java +++ b/data-extract-langchain4j/src/test/java/org/acme/extraction/OllamaTestResource.java @@ -88,7 +88,7 @@ public class OllamaTestResource implements QuarkusTestResourceLifecycleManager { .withLogConsumer(new Slf4jLogConsumer(LOG).withPrefix("basicAuthContainer")); ollamaContainer.start(); - String ollamaModelId = getConfig().getValue("langchain4j.ollama.chat-model.model-id", String.class); + String ollamaModelId = getConfig().getValue("quarkus.langchain4j.ollama.chat-model.model-id", String.class); ExecResult result = ollamaContainer.execInContainer("ollama", "pull", ollamaModelId); long pullBegin = currentTimeMillis(); @@ -111,7 +111,7 @@ public class OllamaTestResource implements QuarkusTestResourceLifecycleManager { } } - return Map.of("langchain4j.ollama.base-url", baseUrl); + return Map.of("quarkus.langchain4j.ollama.base-url", baseUrl); } catch (Exception ex) { throw new RuntimeException("An issue occurred while starting ollama container", ex); } diff --git a/data-extract-langchain4j/src/test/resources/mappings/api_chat-862490ff-9aa9-45d3-b4dc-1dc489ac6dda.json b/data-extract-langchain4j/src/test/resources/mappings/api_chat-1185138d-1018-48a9-9212-5227e0eebf90.json similarity index 79% rename from data-extract-langchain4j/src/test/resources/mappings/api_chat-862490ff-9aa9-45d3-b4dc-1dc489ac6dda.json rename to data-extract-langchain4j/src/test/resources/mappings/api_chat-1185138d-1018-48a9-9212-5227e0eebf90.json index 5fb0dfb3..f8ac98ef 100644 --- a/data-extract-langchain4j/src/test/resources/mappings/api_chat-862490ff-9aa9-45d3-b4dc-1dc489ac6dda.json +++ b/data-extract-langchain4j/src/test/resources/mappings/api_chat-1185138d-1018-48a9-9212-5227e0eebf90.json @@ -1,5 +1,5 @@ { - "id" : "862490ff-9aa9-45d3-b4dc-1dc489ac6dda", + "id" : "1185138d-1018-48a9-9212-5227e0eebf90", "name" : "api_chat", "request" : { "url" : "/api/chat", @@ -12,13 +12,13 @@ }, "response" : { "status" : 200, - "body" : "{\"model\":\"granite4.1:3b\",\"created_at\":\"2026-05-27T17:05:54.559691677Z\",\"message\":{\"role\":\"assistant\",\"content\":\"{\\n \\\"customerSatisfied\\\": false,\\n \\\"customerName\\\": \\\"John Doe\\\",\\n \\\"customerBirthday\\\": {\\\"year\\\": 2001, \\\"month\\\": 11, \\\"day\\\": 1},\\n \\\"summary\\\": \\\"Customer John Doe is upset because the insurance only reimbursed half of his expenses due to an accident. He claims he was never notified about the autom [...] + "body" : "{\"model\":\"granite4.1:3b\",\"created_at\":\"2026-09-25T13:38:21.297963885Z\",\"message\":{\"role\":\"assistant\",\"content\":\"{\\n \\\"customerSatisfied\\\": false,\\n \\\"customerName\\\": \\\"John Doe\\\",\\n \\\"customerBirthday\\\": {\\\"year\\\": 2001, \\\"month\\\": 11, \\\"day\\\": 1},\\n \\\"summary\\\": \\\"Customer John Doe is dissatisfied with the insurance company because they were reimbursed only half of their expenses due to an accident. The customer cl [...] "headers" : { - "Date" : "Wed, 27 May 2026 17:05:54 GMT", + "Date" : "Fri, 25 Sep 2026 13:38:21 GMT", "Content-Type" : "application/json; charset=utf-8" } }, - "uuid" : "862490ff-9aa9-45d3-b4dc-1dc489ac6dda", + "uuid" : "1185138d-1018-48a9-9212-5227e0eebf90", "persistent" : true, - "insertionIndex" : 5 + "insertionIndex" : 2 } \ No newline at end of file diff --git a/data-extract-langchain4j/src/test/resources/mappings/api_chat-bc242ba3-eb22-4c60-9bab-22fabf382956.json b/data-extract-langchain4j/src/test/resources/mappings/api_chat-5cf07a94-1704-41fd-aaed-c3279242686a.json similarity index 75% rename from data-extract-langchain4j/src/test/resources/mappings/api_chat-bc242ba3-eb22-4c60-9bab-22fabf382956.json rename to data-extract-langchain4j/src/test/resources/mappings/api_chat-5cf07a94-1704-41fd-aaed-c3279242686a.json index 0eb23c92..f5c633d1 100644 --- a/data-extract-langchain4j/src/test/resources/mappings/api_chat-bc242ba3-eb22-4c60-9bab-22fabf382956.json +++ b/data-extract-langchain4j/src/test/resources/mappings/api_chat-5cf07a94-1704-41fd-aaed-c3279242686a.json @@ -1,5 +1,5 @@ { - "id" : "bc242ba3-eb22-4c60-9bab-22fabf382956", + "id" : "5cf07a94-1704-41fd-aaed-c3279242686a", "name" : "api_chat", "request" : { "url" : "/api/chat", @@ -12,13 +12,13 @@ }, "response" : { "status" : 200, - "body" : "{\"model\":\"granite4.1:3b\",\"created_at\":\"2026-05-27T17:05:40.026864606Z\",\"message\":{\"role\":\"assistant\",\"content\":\"{\\n \\\"customerSatisfied\\\": true,\\n \\\"customerName\\\": \\\"Sarah London\\\",\\n \\\"customerBirthday\\\": {\\n \\\"year\\\": 1986,\\n \\\"month\\\": 7,\\n \\\"day\\\": 10\\n },\\n \\\"summary\\\": \\\"The customer, Sarah London, called to declare an accident on her main vehicle. The operator confirmed that all expenses related [...] + "body" : "{\"model\":\"granite4.1:3b\",\"created_at\":\"2026-09-25T13:38:05.835599711Z\",\"message\":{\"role\":\"assistant\",\"content\":\"{\\n \\\"customerSatisfied\\\": true,\\n \\\"customerName\\\": \\\"Sarah London\\\",\\n \\\"customerBirthday\\\": {\\n \\\"year\\\": 1986,\\n \\\"month\\\": 7,\\n \\\"day\\\": 10\\n },\\n \\\"summary\\\": \\\"The customer, Sarah London, is calling to declare an accident on her main vehicle. She inquires about the possibility of reimb [...] "headers" : { - "Date" : "Wed, 27 May 2026 17:05:40 GMT", + "Date" : "Fri, 25 Sep 2026 13:38:05 GMT", "Content-Type" : "application/json; charset=utf-8" } }, - "uuid" : "bc242ba3-eb22-4c60-9bab-22fabf382956", + "uuid" : "5cf07a94-1704-41fd-aaed-c3279242686a", "persistent" : true, - "insertionIndex" : 6 + "insertionIndex" : 3 } \ No newline at end of file diff --git a/data-extract-langchain4j/src/test/resources/mappings/api_chat-d0e262d3-6c01-4f8e-a595-617dae7f3687.json b/data-extract-langchain4j/src/test/resources/mappings/api_chat-f33209a6-d198-4e89-983c-9916b795ca98.json similarity index 80% rename from data-extract-langchain4j/src/test/resources/mappings/api_chat-d0e262d3-6c01-4f8e-a595-617dae7f3687.json rename to data-extract-langchain4j/src/test/resources/mappings/api_chat-f33209a6-d198-4e89-983c-9916b795ca98.json index 57825da3..207894a4 100644 --- a/data-extract-langchain4j/src/test/resources/mappings/api_chat-d0e262d3-6c01-4f8e-a595-617dae7f3687.json +++ b/data-extract-langchain4j/src/test/resources/mappings/api_chat-f33209a6-d198-4e89-983c-9916b795ca98.json @@ -1,5 +1,5 @@ { - "id" : "d0e262d3-6c01-4f8e-a595-617dae7f3687", + "id" : "f33209a6-d198-4e89-983c-9916b795ca98", "name" : "api_chat", "request" : { "url" : "/api/chat", @@ -12,13 +12,13 @@ }, "response" : { "status" : 200, - "body" : "{\"model\":\"granite4.1:3b\",\"created_at\":\"2026-05-27T17:06:10.934259518Z\",\"message\":{\"role\":\"assistant\",\"content\":\"{\\n \\\"customerSatisfied\\\": true,\\n \\\"customerName\\\": \\\"Kate Boss\\\",\\n \\\"customerBirthday\\\": {\\n \\\"year\\\": 1999,\\n \\\"month\\\": 8,\\n \\\"day\\\": 13\\n },\\n \\\"summary\\\": \\\"The customer, Kate Boss, contacted the operator at a police station after an accident and needed proof of insurance for documenta [...] + "body" : "{\"model\":\"granite4.1:3b\",\"created_at\":\"2026-09-25T13:38:38.112715116Z\",\"message\":{\"role\":\"assistant\",\"content\":\"{\\n \\\"customerSatisfied\\\": true,\\n \\\"customerName\\\": \\\"Kate Boss\\\",\\n \\\"customerBirthday\\\": {\\n \\\"year\\\": 1999,\\n \\\"month\\\": 8,\\n \\\"day\\\": 13\\n },\\n \\\"summary\\\": \\\"The customer, Kate Boss, contacted the operator at a police station after an accident and needed proof of insurance for the polic [...] "headers" : { - "Date" : "Wed, 27 May 2026 17:06:10 GMT", + "Date" : "Fri, 25 Sep 2026 13:38:38 GMT", "Content-Type" : "application/json; charset=utf-8" } }, - "uuid" : "d0e262d3-6c01-4f8e-a595-617dae7f3687", + "uuid" : "f33209a6-d198-4e89-983c-9916b795ca98", "persistent" : true, - "insertionIndex" : 4 + "insertionIndex" : 1 } \ No newline at end of file
