Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions integration-tests/langchain4j-chat-ql4j/README.adoc
Original file line number Diff line number Diff line change
@@ -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`
149 changes: 149 additions & 0 deletions integration-tests/langchain4j-chat-ql4j/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--

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.

-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.apache.camel.quarkus</groupId>
<artifactId>camel-quarkus-build-parent-it</artifactId>
<version>3.39.0-SNAPSHOT</version>
<relativePath>../../poms/build-parent-it/pom.xml</relativePath>
</parent>

<artifactId>camel-quarkus-integration-test-langchain4j-chat-ql4j</artifactId>
<name>Camel Quarkus :: Integration Tests :: LangChain4j Chat QL4J</name>
<description>Integration tests for Camel Quarkus LangChain4j Chat extension with Quarkus LangChain4j provided chat models</description>

<dependencyManagement>
<dependencies>
<dependency>
<groupId>io.quarkiverse.langchain4j</groupId>
<artifactId>quarkus-langchain4j-bom</artifactId>
<version>${quarkiverse-langchain4j.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

<dependencies>
<dependency>
<groupId>org.apache.camel.quarkus</groupId>
<artifactId>camel-quarkus-direct</artifactId>
</dependency>
<dependency>
<groupId>org.apache.camel.quarkus</groupId>
<artifactId>camel-quarkus-langchain4j-chat</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-rest</artifactId>
</dependency>

<!-- Quarkus-langchain4j dependencies-->
<dependency>
<groupId>io.quarkiverse.langchain4j</groupId>
<artifactId>quarkus-langchain4j-ollama</artifactId>
</dependency>

<!-- test dependencies -->
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>rest-assured</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.camel.quarkus</groupId>
<artifactId>camel-quarkus-integration-wiremock-support</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<profiles>
<profile>
<id>native</id>
<activation>
<property>
<name>native</name>
</property>
</activation>
<properties>
<quarkus.native.enabled>true</quarkus.native.enabled>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>integration-test</goal>
<goal>verify</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
<profile>
<id>virtualDependencies</id>
<activation>
<property>
<name>!noVirtualDependencies</name>
</property>
</activation>
<dependencies>
<!-- The following dependencies guarantee that this module is built after them. You can update them by running `mvn process-resources -Pformat -N` from the source tree root directory -->
<dependency>
<groupId>org.apache.camel.quarkus</groupId>
<artifactId>camel-quarkus-direct-deployment</artifactId>
<version>${project.version}</version>
<type>pom</type>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>*</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.camel.quarkus</groupId>
<artifactId>camel-quarkus-langchain4j-chat-deployment</artifactId>
<version>${project.version}</version>
<type>pom</type>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>*</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
</profile>
</profiles>
</project>
Original file line number Diff line number Diff line change
@@ -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;
}
}
Original file line number Diff line number Diff line change
@@ -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<String, Object> 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<ChatMessage> 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);
}
}
Original file line number Diff line number Diff line change
@@ -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");
}
}
Original file line number Diff line number Diff line change
@@ -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
Loading
Loading