From c4f838ad3942e7160c247a1782f1657cf4573450 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matheus=20Andr=C3=A9?= Date: Tue, 4 Aug 2026 18:45:51 -0300 Subject: [PATCH] feat: integration tests for LangChain4j Chat QL4J --- .../langchain4j-chat-ql4j/README.adoc | 30 ++++ .../langchain4j-chat-ql4j/pom.xml | 149 ++++++++++++++++++ .../chat/ql4j/it/ChatModelProducers.java | 42 +++++ .../ql4j/it/LangChain4jChatQl4jResource.java | 88 +++++++++++ .../ql4j/it/LangChain4jChatQl4jRoutes.java | 40 +++++ .../src/main/resources/application.properties | 29 ++++ .../chat/ql4j/it/LangChain4jChatQl4jIT.java | 23 +++ .../chat/ql4j/it/LangChain4jChatQl4jTest.java | 76 +++++++++ .../chat/ql4j/it/OllamaTestResource.java | 50 ++++++ ...-1f0d3c2a-8b4e-4a6f-9c1d-2e5b7a9d0c11.json | 22 +++ ...-2a1b4c5d-6e7f-4890-a1b2-c3d4e5f60712.json | 22 +++ ...-3b2c5d6e-7f80-4901-b2c3-d4e5f6071823.json | 22 +++ ...-4c3d6e7f-8091-4a12-83d4-e5f607182934.json | 22 +++ integration-tests/pom.xml | 1 + tooling/scripts/test-categories.yaml | 1 + 15 files changed, 617 insertions(+) create mode 100644 integration-tests/langchain4j-chat-ql4j/README.adoc create mode 100644 integration-tests/langchain4j-chat-ql4j/pom.xml create mode 100644 integration-tests/langchain4j-chat-ql4j/src/main/java/org/apache/camel/quarkus/component/langchain4j/chat/ql4j/it/ChatModelProducers.java create mode 100644 integration-tests/langchain4j-chat-ql4j/src/main/java/org/apache/camel/quarkus/component/langchain4j/chat/ql4j/it/LangChain4jChatQl4jResource.java create mode 100644 integration-tests/langchain4j-chat-ql4j/src/main/java/org/apache/camel/quarkus/component/langchain4j/chat/ql4j/it/LangChain4jChatQl4jRoutes.java create mode 100644 integration-tests/langchain4j-chat-ql4j/src/main/resources/application.properties create mode 100644 integration-tests/langchain4j-chat-ql4j/src/test/java/org/apache/camel/quarkus/component/langchain4j/chat/ql4j/it/LangChain4jChatQl4jIT.java create mode 100644 integration-tests/langchain4j-chat-ql4j/src/test/java/org/apache/camel/quarkus/component/langchain4j/chat/ql4j/it/LangChain4jChatQl4jTest.java create mode 100644 integration-tests/langchain4j-chat-ql4j/src/test/java/org/apache/camel/quarkus/component/langchain4j/chat/ql4j/it/OllamaTestResource.java create mode 100644 integration-tests/langchain4j-chat-ql4j/src/test/resources/mappings/chat-ql4j/api_chat-1f0d3c2a-8b4e-4a6f-9c1d-2e5b7a9d0c11.json create mode 100644 integration-tests/langchain4j-chat-ql4j/src/test/resources/mappings/chat-ql4j/api_chat-2a1b4c5d-6e7f-4890-a1b2-c3d4e5f60712.json create mode 100644 integration-tests/langchain4j-chat-ql4j/src/test/resources/mappings/chat-ql4j/api_chat-3b2c5d6e-7f80-4901-b2c3-d4e5f6071823.json create mode 100644 integration-tests/langchain4j-chat-ql4j/src/test/resources/mappings/chat-ql4j/api_chat-4c3d6e7f-8091-4a12-83d4-e5f607182934.json diff --git a/integration-tests/langchain4j-chat-ql4j/README.adoc b/integration-tests/langchain4j-chat-ql4j/README.adoc new file mode 100644 index 00000000000..3812faa890b --- /dev/null +++ b/integration-tests/langchain4j-chat-ql4j/README.adoc @@ -0,0 +1,30 @@ +== Camel Quarkus LangChain4j Chat with Quarkus LangChain4j Integration Tests + +Tests for `camel-quarkus-langchain4j-chat` where the `ChatModel` instances are created and configured by Quarkus LangChain4j (`quarkus-langchain4j-ollama`), +instead of being built manually. + +The following scenarios are covered: + +* Simple chat message using the default Quarkus LangChain4j provided `ChatModel` (autowired by the Camel `langchain4j-chat` component) +* Prompt template chat with the default Quarkus LangChain4j provided `ChatModel` +* Multi-turn conversation with the default Quarkus LangChain4j provided `ChatModel` +* Multiple Quarkus LangChain4j models configured (`@ModelName("custom")` vs default) where the WireMock stubs verify each Camel route resolved the expected model + +By default, the integration tests use WireMock to stub the Ollama API interactions. + +To run the integration tests against a real Ollama instance, you need the `orca-mini` and `granite4:3b` models downloaded. + +When Ollama is running, set the following environment variables: + +[source,shell] +---- +export LANGCHAIN4J_OLLAMA_BASE_URL=your-ollama-api-url +---- + +If the WireMock stub recordings need updating, then remove the existing files from `src/test/resources/mappings` and run tests with either: + +System property `-Dwiremock.record=true` + +Or + +Set environment variable `WIREMOCK_RECORD=true` diff --git a/integration-tests/langchain4j-chat-ql4j/pom.xml b/integration-tests/langchain4j-chat-ql4j/pom.xml new file mode 100644 index 00000000000..a77975e11ef --- /dev/null +++ b/integration-tests/langchain4j-chat-ql4j/pom.xml @@ -0,0 +1,149 @@ + + + + 4.0.0 + + org.apache.camel.quarkus + camel-quarkus-build-parent-it + 3.39.0-SNAPSHOT + ../../poms/build-parent-it/pom.xml + + + camel-quarkus-integration-test-langchain4j-chat-ql4j + Camel Quarkus :: Integration Tests :: LangChain4j Chat QL4J + Integration tests for Camel Quarkus LangChain4j Chat extension with Quarkus LangChain4j provided chat models + + + + + io.quarkiverse.langchain4j + quarkus-langchain4j-bom + ${quarkiverse-langchain4j.version} + pom + import + + + + + + + org.apache.camel.quarkus + camel-quarkus-direct + + + org.apache.camel.quarkus + camel-quarkus-langchain4j-chat + + + io.quarkus + quarkus-rest + + + + + io.quarkiverse.langchain4j + quarkus-langchain4j-ollama + + + + + io.quarkus + quarkus-junit + test + + + io.rest-assured + rest-assured + test + + + org.apache.camel.quarkus + camel-quarkus-integration-wiremock-support + test + + + + + + native + + + native + + + + true + + + + + org.apache.maven.plugins + maven-failsafe-plugin + + + + integration-test + verify + + + + + + + + + virtualDependencies + + + !noVirtualDependencies + + + + + + org.apache.camel.quarkus + camel-quarkus-direct-deployment + ${project.version} + pom + test + + + * + * + + + + + org.apache.camel.quarkus + camel-quarkus-langchain4j-chat-deployment + ${project.version} + pom + test + + + * + * + + + + + + + diff --git a/integration-tests/langchain4j-chat-ql4j/src/main/java/org/apache/camel/quarkus/component/langchain4j/chat/ql4j/it/ChatModelProducers.java b/integration-tests/langchain4j-chat-ql4j/src/main/java/org/apache/camel/quarkus/component/langchain4j/chat/ql4j/it/ChatModelProducers.java new file mode 100644 index 00000000000..68f063a738f --- /dev/null +++ b/integration-tests/langchain4j-chat-ql4j/src/main/java/org/apache/camel/quarkus/component/langchain4j/chat/ql4j/it/ChatModelProducers.java @@ -0,0 +1,42 @@ +/* + * 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.apache.camel.quarkus.component.langchain4j.chat.ql4j.it; + +import dev.langchain4j.model.chat.ChatModel; +import io.quarkiverse.langchain4j.ModelName; +import io.smallrye.common.annotation.Identifier; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.inject.Produces; +import jakarta.inject.Inject; + +@ApplicationScoped +public class ChatModelProducers { + // Quarkus LangChain4j only creates model beans for models that have injection points. + // This injection point forces the creation of the default ChatModel bean so that the + // Camel langchain4j-chat component can autowire it from the registry + @Inject + ChatModel defaultChatModel; + + // Quarkus LangChain4j named model beans only have the @ModelName qualifier, which the Camel + // registry cannot look up by name. Re-expose the named model under an @Identifier so that + // routes can reference it via chatModel=#customChatModel + @Produces + @Identifier("customChatModel") + ChatModel customChatModel(@ModelName("custom") ChatModel chatModel) { + return chatModel; + } +} diff --git a/integration-tests/langchain4j-chat-ql4j/src/main/java/org/apache/camel/quarkus/component/langchain4j/chat/ql4j/it/LangChain4jChatQl4jResource.java b/integration-tests/langchain4j-chat-ql4j/src/main/java/org/apache/camel/quarkus/component/langchain4j/chat/ql4j/it/LangChain4jChatQl4jResource.java new file mode 100644 index 00000000000..339c28824ac --- /dev/null +++ b/integration-tests/langchain4j-chat-ql4j/src/main/java/org/apache/camel/quarkus/component/langchain4j/chat/ql4j/it/LangChain4jChatQl4jResource.java @@ -0,0 +1,88 @@ +/* + * 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.apache.camel.quarkus.component.langchain4j.chat.ql4j.it; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import dev.langchain4j.data.message.AiMessage; +import dev.langchain4j.data.message.ChatMessage; +import dev.langchain4j.data.message.SystemMessage; +import dev.langchain4j.data.message.UserMessage; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; +import org.apache.camel.ProducerTemplate; +import org.apache.camel.component.langchain4j.chat.LangChain4jChatHeaders; + +@Path("/langchain4j-chat-ql4j") +@ApplicationScoped +public class LangChain4jChatQl4jResource { + @Inject + ProducerTemplate producerTemplate; + + @Path("/simple-message") + @POST + @Produces(MediaType.TEXT_PLAIN) + public String defaultModelSimpleMessage(String message) { + return producerTemplate.requestBody("direct:defaultModelSimpleMessage", message, String.class); + } + + @Path("/prompt-message") + @GET + @Produces(MediaType.TEXT_PLAIN) + public String defaultModelPromptMessage() { + var promptTemplate = "Create a recipe for a {{dishType}} with the following ingredients: {{ingredients}}"; + + Map variables = new HashMap<>(); + variables.put("dishType", "oven dish"); + variables.put("ingredients", "potato, tomato, feta, olive oil"); + + return producerTemplate.requestBodyAndHeader("direct:defaultModelPromptMessage", variables, + LangChain4jChatHeaders.PROMPT_TEMPLATE, promptTemplate, String.class); + } + + @Path("/multi-turn-conversation") + @GET + @Produces(MediaType.TEXT_PLAIN) + public String defaultModelMultiTurnConversation() { + List messages = new ArrayList<>(); + messages.add(new SystemMessage("You are asked to provide recommendations for a restaurant based on user reviews.")); + messages.add(new UserMessage("Hello, my name is Karen.")); + messages.add(new AiMessage("Hello Karen, how can I help you?")); + messages.add(new UserMessage("I'd like you to recommend a restaurant for me.")); + messages.add(new AiMessage("Sure, what type of cuisine are you interested in?")); + messages.add(new UserMessage("I'd like Moroccan food.")); + messages.add(new AiMessage("Sure, do you have a preference for the location?")); + messages.add(new UserMessage("Paris, Rue Montorgueil.")); + + return producerTemplate.requestBody("direct:defaultModelMultipleMessages", messages, String.class); + } + + @Path("/named-model-simple-message") + @POST + @Produces(MediaType.TEXT_PLAIN) + public String namedModelSimpleMessage(String message) { + return producerTemplate.requestBody("direct:namedModelSimpleMessage", message, String.class); + } +} diff --git a/integration-tests/langchain4j-chat-ql4j/src/main/java/org/apache/camel/quarkus/component/langchain4j/chat/ql4j/it/LangChain4jChatQl4jRoutes.java b/integration-tests/langchain4j-chat-ql4j/src/main/java/org/apache/camel/quarkus/component/langchain4j/chat/ql4j/it/LangChain4jChatQl4jRoutes.java new file mode 100644 index 00000000000..4aa3342b287 --- /dev/null +++ b/integration-tests/langchain4j-chat-ql4j/src/main/java/org/apache/camel/quarkus/component/langchain4j/chat/ql4j/it/LangChain4jChatQl4jRoutes.java @@ -0,0 +1,40 @@ +/* + * 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.apache.camel.quarkus.component.langchain4j.chat.ql4j.it; + +import jakarta.enterprise.context.ApplicationScoped; +import org.apache.camel.builder.RouteBuilder; + +@ApplicationScoped +public class LangChain4jChatQl4jRoutes extends RouteBuilder { + @Override + public void configure() { + // The default Quarkus LangChain4j ChatModel bean is autowired from the registry + from("direct:defaultModelSimpleMessage") + .to("langchain4j-chat:defaultModelSimple?chatOperation=CHAT_SINGLE_MESSAGE"); + + from("direct:defaultModelPromptMessage") + .to("langchain4j-chat:defaultModelPrompt?chatOperation=CHAT_SINGLE_MESSAGE_WITH_PROMPT"); + + from("direct:defaultModelMultipleMessages") + .to("langchain4j-chat:defaultModelMultiple?chatOperation=CHAT_MULTIPLE_MESSAGES"); + + // The Quarkus LangChain4j @ModelName("custom") ChatModel bean re-exposed as customChatModel + from("direct:namedModelSimpleMessage") + .to("langchain4j-chat:namedModelSimple?chatOperation=CHAT_SINGLE_MESSAGE&chatModel=#customChatModel"); + } +} diff --git a/integration-tests/langchain4j-chat-ql4j/src/main/resources/application.properties b/integration-tests/langchain4j-chat-ql4j/src/main/resources/application.properties new file mode 100644 index 00000000000..124cf3a177b --- /dev/null +++ b/integration-tests/langchain4j-chat-ql4j/src/main/resources/application.properties @@ -0,0 +1,29 @@ +## --------------------------------------------------------------------------- +## 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. +## --------------------------------------------------------------------------- +quarkus.http.test-timeout=120S + +quarkus.devservices.enabled=false +quarkus.langchain4j.devservices.preload=false + +# Default chat model +quarkus.langchain4j.ollama.chat-model.model-id=orca-mini +quarkus.langchain4j.ollama.chat-model.temperature=0.3 + +# Named chat model resolvable via @ModelName("custom") +quarkus.langchain4j.custom.chat-model.provider=ollama +quarkus.langchain4j.ollama.custom.chat-model.model-id=granite4:3b +quarkus.langchain4j.ollama.custom.chat-model.temperature=0.3 diff --git a/integration-tests/langchain4j-chat-ql4j/src/test/java/org/apache/camel/quarkus/component/langchain4j/chat/ql4j/it/LangChain4jChatQl4jIT.java b/integration-tests/langchain4j-chat-ql4j/src/test/java/org/apache/camel/quarkus/component/langchain4j/chat/ql4j/it/LangChain4jChatQl4jIT.java new file mode 100644 index 00000000000..2ddfbb2c1d7 --- /dev/null +++ b/integration-tests/langchain4j-chat-ql4j/src/test/java/org/apache/camel/quarkus/component/langchain4j/chat/ql4j/it/LangChain4jChatQl4jIT.java @@ -0,0 +1,23 @@ +/* + * 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.apache.camel.quarkus.component.langchain4j.chat.ql4j.it; + +import io.quarkus.test.junit.QuarkusIntegrationTest; + +@QuarkusIntegrationTest +class LangChain4jChatQl4jIT extends LangChain4jChatQl4jTest { +} diff --git a/integration-tests/langchain4j-chat-ql4j/src/test/java/org/apache/camel/quarkus/component/langchain4j/chat/ql4j/it/LangChain4jChatQl4jTest.java b/integration-tests/langchain4j-chat-ql4j/src/test/java/org/apache/camel/quarkus/component/langchain4j/chat/ql4j/it/LangChain4jChatQl4jTest.java new file mode 100644 index 00000000000..ff02834ad10 --- /dev/null +++ b/integration-tests/langchain4j-chat-ql4j/src/test/java/org/apache/camel/quarkus/component/langchain4j/chat/ql4j/it/LangChain4jChatQl4jTest.java @@ -0,0 +1,76 @@ +/* + * 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.apache.camel.quarkus.component.langchain4j.chat.ql4j.it; + +import io.quarkus.test.common.QuarkusTestResource; +import io.quarkus.test.junit.QuarkusTest; +import io.restassured.RestAssured; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.Matchers.allOf; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.containsStringIgnoringCase; + +@QuarkusTest +@QuarkusTestResource(OllamaTestResource.class) +class LangChain4jChatQl4jTest { + + @Test + void simpleMessageUsingQuarkusLangChain4jDefaultModel() { + RestAssured.given() + .body("Hello my name is Darth Vader!") + .post("/langchain4j-chat-ql4j/simple-message") + .then() + .statusCode(200) + .body(containsString("Hello")); + } + + @Test + void promptMessageUsingQuarkusLangChain4jDefaultModel() { + RestAssured.get("/langchain4j-chat-ql4j/prompt-message") + .then() + .statusCode(200) + .body(allOf( + containsString("potato"), + containsString("tomato"), + containsString("feta"), + containsString("olive oil"))); + } + + @Test + void multiTurnConversationUsingQuarkusLangChain4jDefaultModel() { + RestAssured.get("/langchain4j-chat-ql4j/multi-turn-conversation") + .then() + .statusCode(200) + .body(containsStringIgnoringCase("Moroccan")); + } + + /** + * The WireMock stub for this scenario only matches requests targeting the model configured for the + * Quarkus LangChain4j named model 'custom'. Thus the test can only pass if Camel resolved the correct + * ChatModel from the multiple configured model beans. + */ + @Test + void simpleMessageUsingQuarkusLangChain4jNamedModel() { + RestAssured.given() + .body("Which large language model are you?") + .post("/langchain4j-chat-ql4j/named-model-simple-message") + .then() + .statusCode(200) + .body(containsStringIgnoringCase("Granite")); + } +} diff --git a/integration-tests/langchain4j-chat-ql4j/src/test/java/org/apache/camel/quarkus/component/langchain4j/chat/ql4j/it/OllamaTestResource.java b/integration-tests/langchain4j-chat-ql4j/src/test/java/org/apache/camel/quarkus/component/langchain4j/chat/ql4j/it/OllamaTestResource.java new file mode 100644 index 00000000000..7d4c2c94234 --- /dev/null +++ b/integration-tests/langchain4j-chat-ql4j/src/test/java/org/apache/camel/quarkus/component/langchain4j/chat/ql4j/it/OllamaTestResource.java @@ -0,0 +1,50 @@ +/* + * 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.apache.camel.quarkus.component.langchain4j.chat.ql4j.it; + +import java.util.Map; + +import org.apache.camel.quarkus.test.wiremock.WireMockTestResourceLifecycleManager; + +public class OllamaTestResource extends WireMockTestResourceLifecycleManager { + private static final String OLLAMA_ENV_URL = "LANGCHAIN4J_OLLAMA_BASE_URL"; + + @Override + public Map start() { + Map properties = super.start(); + String wiremockUrl = properties.get("wiremock.url"); + String url = wiremockUrl != null ? wiremockUrl : getRecordTargetBaseUrl(); + properties.put("quarkus.langchain4j.ollama.base-url", url); + properties.put("quarkus.langchain4j.ollama.custom.base-url", url); + return properties; + } + + @Override + protected String getRecordTargetBaseUrl() { + return System.getenv(OLLAMA_ENV_URL); + } + + @Override + protected boolean isMockingEnabled() { + return !envVarsPresent(OLLAMA_ENV_URL); + } + + @Override + protected String getStubsSubdirectory() { + return "chat-ql4j"; + } +} diff --git a/integration-tests/langchain4j-chat-ql4j/src/test/resources/mappings/chat-ql4j/api_chat-1f0d3c2a-8b4e-4a6f-9c1d-2e5b7a9d0c11.json b/integration-tests/langchain4j-chat-ql4j/src/test/resources/mappings/chat-ql4j/api_chat-1f0d3c2a-8b4e-4a6f-9c1d-2e5b7a9d0c11.json new file mode 100644 index 00000000000..7c654ec8db9 --- /dev/null +++ b/integration-tests/langchain4j-chat-ql4j/src/test/resources/mappings/chat-ql4j/api_chat-1f0d3c2a-8b4e-4a6f-9c1d-2e5b7a9d0c11.json @@ -0,0 +1,22 @@ +{ + "id" : "1f0d3c2a-8b4e-4a6f-9c1d-2e5b7a9d0c11", + "name" : "api_chat", + "request" : { + "url" : "/api/chat", + "method" : "POST", + "bodyPatterns" : [ { + "equalToJson" : "{\n \"model\" : \"orca-mini\",\n \"messages\" : [ {\n \"role\" : \"user\",\n \"content\" : \"Hello my name is Darth Vader!\"\n } ]\n}", + "ignoreArrayOrder" : true, + "ignoreExtraElements" : true + } ] + }, + "response" : { + "status" : 200, + "body" : "{\"model\":\"orca-mini\",\"created_at\":\"2026-08-04T12:23:40.200733369Z\",\"message\":{\"role\":\"assistant\",\"content\":\"Hello Darth Vader! I am your loyal AI assistant. Is there anything I can assist you with today?\"},\"done_reason\":\"stop\",\"done\":true,\"total_duration\":974483342,\"load_duration\":5203531,\"prompt_eval_count\":49,\"prompt_eval_duration\":48645877,\"eval_count\":24,\"eval_duration\":919787081}", + "headers" : { + "Content-Type" : "application/json; charset=utf-8" + } + }, + "uuid" : "1f0d3c2a-8b4e-4a6f-9c1d-2e5b7a9d0c11", + "persistent" : true +} diff --git a/integration-tests/langchain4j-chat-ql4j/src/test/resources/mappings/chat-ql4j/api_chat-2a1b4c5d-6e7f-4890-a1b2-c3d4e5f60712.json b/integration-tests/langchain4j-chat-ql4j/src/test/resources/mappings/chat-ql4j/api_chat-2a1b4c5d-6e7f-4890-a1b2-c3d4e5f60712.json new file mode 100644 index 00000000000..dded7956d4f --- /dev/null +++ b/integration-tests/langchain4j-chat-ql4j/src/test/resources/mappings/chat-ql4j/api_chat-2a1b4c5d-6e7f-4890-a1b2-c3d4e5f60712.json @@ -0,0 +1,22 @@ +{ + "id" : "2a1b4c5d-6e7f-4890-a1b2-c3d4e5f60712", + "name" : "api_chat", + "request" : { + "url" : "/api/chat", + "method" : "POST", + "bodyPatterns" : [ { + "equalToJson" : "{\n \"model\" : \"orca-mini\",\n \"messages\" : [ {\n \"role\" : \"user\",\n \"content\" : \"Create a recipe for a oven dish with the following ingredients: potato, tomato, feta, olive oil\"\n } ]\n}", + "ignoreArrayOrder" : true, + "ignoreExtraElements" : true + } ] + }, + "response" : { + "status" : 200, + "body" : "{\"model\":\"orca-mini\",\"created_at\":\"2026-08-04T12:23:52.914650649Z\",\"message\":{\"role\":\"assistant\",\"content\":\"Sure! Here's a simple recipe for an oven dish:\\n\\nIngredients:\\n- 1 medium-sized potato, peeled and chopped\\n- 1 medium-sized tomato, sliced\\n- 100g feta cheese, crumbled\\n- 2 tablespoons olive oil\\n\\nInstructions: Toss the potato and tomato with the olive oil, top with the feta and bake at 200C for 35 minutes.\"},\"done_reason\":\"stop\",\"done\":true,\"total_duration\":1373187042,\"load_duration\":5337644,\"prompt_eval_count\":58,\"prompt_eval_duration\":50645877,\"eval_count\":92,\"eval_duration\":1219787081}", + "headers" : { + "Content-Type" : "application/json; charset=utf-8" + } + }, + "uuid" : "2a1b4c5d-6e7f-4890-a1b2-c3d4e5f60712", + "persistent" : true +} diff --git a/integration-tests/langchain4j-chat-ql4j/src/test/resources/mappings/chat-ql4j/api_chat-3b2c5d6e-7f80-4901-b2c3-d4e5f6071823.json b/integration-tests/langchain4j-chat-ql4j/src/test/resources/mappings/chat-ql4j/api_chat-3b2c5d6e-7f80-4901-b2c3-d4e5f6071823.json new file mode 100644 index 00000000000..51a57560697 --- /dev/null +++ b/integration-tests/langchain4j-chat-ql4j/src/test/resources/mappings/chat-ql4j/api_chat-3b2c5d6e-7f80-4901-b2c3-d4e5f6071823.json @@ -0,0 +1,22 @@ +{ + "id" : "3b2c5d6e-7f80-4901-b2c3-d4e5f6071823", + "name" : "api_chat", + "request" : { + "url" : "/api/chat", + "method" : "POST", + "bodyPatterns" : [ { + "equalToJson" : "{\n \"model\" : \"orca-mini\",\n \"messages\" : [ {\n \"role\" : \"system\",\n \"content\" : \"You are asked to provide recommendations for a restaurant based on user reviews.\"\n }, {\n \"role\" : \"user\",\n \"content\" : \"Hello, my name is Karen.\"\n }, {\n \"role\" : \"assistant\",\n \"content\" : \"Hello Karen, how can I help you?\"\n }, {\n \"role\" : \"user\",\n \"content\" : \"I'd like you to recommend a restaurant for me.\"\n }, {\n \"role\" : \"assistant\",\n \"content\" : \"Sure, what type of cuisine are you interested in?\"\n }, {\n \"role\" : \"user\",\n \"content\" : \"I'd like Moroccan food.\"\n }, {\n \"role\" : \"assistant\",\n \"content\" : \"Sure, do you have a preference for the location?\"\n }, {\n \"role\" : \"user\",\n \"content\" : \"Paris, Rue Montorgueil.\"\n } ]\n}", + "ignoreArrayOrder" : true, + "ignoreExtraElements" : true + } ] + }, + "response" : { + "status" : 200, + "body" : "{\"model\":\"orca-mini\",\"created_at\":\"2026-08-04T12:23:57.641289281Z\",\"message\":{\"role\":\"assistant\",\"content\":\"Based on user reviews, I would recommend Le Tajine d'Or, a Moroccan restaurant located on Rue Montorgueil in Paris. It has an average rating of 4.5 stars and its couscous and lamb tajine receive consistently positive feedback.\"},\"done_reason\":\"stop\",\"done\":true,\"total_duration\":1074483342,\"load_duration\":5203531,\"prompt_eval_count\":112,\"prompt_eval_duration\":68645877,\"eval_count\":57,\"eval_duration\":989787081}", + "headers" : { + "Content-Type" : "application/json; charset=utf-8" + } + }, + "uuid" : "3b2c5d6e-7f80-4901-b2c3-d4e5f6071823", + "persistent" : true +} diff --git a/integration-tests/langchain4j-chat-ql4j/src/test/resources/mappings/chat-ql4j/api_chat-4c3d6e7f-8091-4a12-83d4-e5f607182934.json b/integration-tests/langchain4j-chat-ql4j/src/test/resources/mappings/chat-ql4j/api_chat-4c3d6e7f-8091-4a12-83d4-e5f607182934.json new file mode 100644 index 00000000000..2d80aa253be --- /dev/null +++ b/integration-tests/langchain4j-chat-ql4j/src/test/resources/mappings/chat-ql4j/api_chat-4c3d6e7f-8091-4a12-83d4-e5f607182934.json @@ -0,0 +1,22 @@ +{ + "id" : "4c3d6e7f-8091-4a12-83d4-e5f607182934", + "name" : "api_chat", + "request" : { + "url" : "/api/chat", + "method" : "POST", + "bodyPatterns" : [ { + "equalToJson" : "{\n \"model\" : \"granite4:3b\",\n \"messages\" : [ {\n \"role\" : \"user\",\n \"content\" : \"Which large language model are you?\"\n } ]\n}", + "ignoreArrayOrder" : true, + "ignoreExtraElements" : true + } ] + }, + "response" : { + "status" : 200, + "body" : "{\"model\":\"granite4:3b\",\"created_at\":\"2026-08-04T12:24:05.123456789Z\",\"message\":{\"role\":\"assistant\",\"content\":\"I am Granite, a large language model developed by IBM.\"},\"done_reason\":\"stop\",\"done\":true,\"total_duration\":874483342,\"load_duration\":5203531,\"prompt_eval_count\":32,\"prompt_eval_duration\":38645877,\"eval_count\":15,\"eval_duration\":819787081}", + "headers" : { + "Content-Type" : "application/json; charset=utf-8" + } + }, + "uuid" : "4c3d6e7f-8091-4a12-83d4-e5f607182934", + "persistent" : true +} diff --git a/integration-tests/pom.xml b/integration-tests/pom.xml index b29781f585d..61fafdae052 100644 --- a/integration-tests/pom.xml +++ b/integration-tests/pom.xml @@ -162,6 +162,7 @@ langchain4j-agent-bean-binding-ql4j langchain4j-agent-ql4j langchain4j-chat + langchain4j-chat-ql4j langchain4j-embeddings langchain4j-embeddingstore langchain4j-tokenizer diff --git a/tooling/scripts/test-categories.yaml b/tooling/scripts/test-categories.yaml index 6eecf4224ad..8dfd9d333c2 100644 --- a/tooling/scripts/test-categories.yaml +++ b/tooling/scripts/test-categories.yaml @@ -160,6 +160,7 @@ group-08: - joor - jq - langchain4j-chat + - langchain4j-chat-ql4j - main-unknown-args-ignore - master-file - pdf