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.git
The following commit(s) were added to refs/heads/main by this push:
new 1c0cd75af8 Fixes #8865. Add langchain4j-embeddingstore-ql4j
integration test module
1c0cd75af8 is described below
commit 1c0cd75af84b022f5ef875e66a7fccf82e5f61a6
Author: Jomin Mathew <[email protected]>
AuthorDate: Mon Sep 7 07:06:41 2026 +0100
Fixes #8865. Add langchain4j-embeddingstore-ql4j integration test module
Add integration-tests/langchain4j-embeddingstore-ql4j/ module to verify that
camel-quarkus-langchain4j-embeddingstore works correctly when Quarkus
LangChain4j is on the classpath.
Uses quarkus-langchain4j-ollama with a QL4J-managed EmbeddingModel (mocked
via WireMock), Qdrant via testcontainers for the embedding store, and tests
Camel-QL4J interop for ADD/SEARCH/REMOVE operations plus ContentRetriever
cross-compatibility. Registered in test-categories.yaml group-07.
Co-authored-by: jomin mathew <>
---
.../langchain4j-embeddingstore-ql4j/pom.xml | 177 +++++++++++++++++++++
.../it/Langchain4jEmbeddingstoreQl4jProducers.java | 67 ++++++++
.../it/Langchain4jEmbeddingstoreQl4jResource.java | 131 +++++++++++++++
.../it/Langchain4jEmbeddingstoreQl4jRoutes.java | 31 ++++
.../src/main/resources/application.properties | 23 +++
.../it/Langchain4jQdrantEmbeddingstoreQl4jIT.java | 23 +++
.../Langchain4jQdrantEmbeddingstoreQl4jTest.java | 120 ++++++++++++++
.../ql4j/it/OllamaWireMockTestResource.java | 49 ++++++
.../embeddingstore/ql4j/it/QdrantTestResource.java | 105 ++++++++++++
.../mappings/embeddings/api_embed-camel-store.json | 22 +++
.../mappings/embeddings/api_embed-ql4j-ingest.json | 22 +++
integration-tests/pom.xml | 1 +
tooling/scripts/test-categories.yaml | 1 +
13 files changed, 772 insertions(+)
diff --git a/integration-tests/langchain4j-embeddingstore-ql4j/pom.xml
b/integration-tests/langchain4j-embeddingstore-ql4j/pom.xml
new file mode 100644
index 0000000000..c5bd0a6876
--- /dev/null
+++ b/integration-tests/langchain4j-embeddingstore-ql4j/pom.xml
@@ -0,0 +1,177 @@
+<?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.40.0-SNAPSHOT</version>
+ <relativePath>../../poms/build-parent-it/pom.xml</relativePath>
+ </parent>
+
+
<artifactId>camel-quarkus-integration-test-langchain4j-embeddingstore-ql4j</artifactId>
+ <name>Camel Quarkus :: Integration Tests :: LangChain4j Embedding Store
QL4J</name>
+ <description>Integration tests for Camel Quarkus LangChain4j Embedding
Store extension with Quarkus LangChain4j on the classpath</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-embeddingstore</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>io.quarkiverse.langchain4j</groupId>
+ <artifactId>quarkus-langchain4j-ollama</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>io.quarkiverse.langchain4j</groupId>
+ <artifactId>quarkus-langchain4j-qdrant</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>io.quarkus</groupId>
+ <artifactId>quarkus-rest</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>io.quarkus</groupId>
+ <artifactId>quarkus-rest-jackson</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.testcontainers</groupId>
+ <artifactId>testcontainers-qdrant</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.awaitility</groupId>
+ <artifactId>awaitility</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-embeddingstore-deployment</artifactId>
+ <version>${project.version}</version>
+ <type>pom</type>
+ <scope>test</scope>
+ <exclusions>
+ <exclusion>
+ <groupId>*</groupId>
+ <artifactId>*</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ </dependencies>
+ </profile>
+ <profile>
+ <id>skip-testcontainers-tests</id>
+ <activation>
+ <property>
+ <name>skip-testcontainers-tests</name>
+ </property>
+ </activation>
+ <properties>
+ <skipTests>true</skipTests>
+ </properties>
+ </profile>
+ </profiles>
+
+</project>
diff --git
a/integration-tests/langchain4j-embeddingstore-ql4j/src/main/java/org/apache/camel/quarkus/component/langchain4j/embeddingstore/ql4j/it/Langchain4jEmbeddingstoreQl4jProducers.java
b/integration-tests/langchain4j-embeddingstore-ql4j/src/main/java/org/apache/camel/quarkus/component/langchain4j/embeddingstore/ql4j/it/Langchain4jEmbeddingstoreQl4jProducers.java
new file mode 100644
index 0000000000..04a78de3ad
--- /dev/null
+++
b/integration-tests/langchain4j-embeddingstore-ql4j/src/main/java/org/apache/camel/quarkus/component/langchain4j/embeddingstore/ql4j/it/Langchain4jEmbeddingstoreQl4jProducers.java
@@ -0,0 +1,67 @@
+/*
+ * 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.embeddingstore.ql4j.it;
+
+import dev.langchain4j.data.segment.TextSegment;
+import dev.langchain4j.model.embedding.EmbeddingModel;
+import dev.langchain4j.rag.content.retriever.ContentRetriever;
+import dev.langchain4j.rag.content.retriever.EmbeddingStoreContentRetriever;
+import dev.langchain4j.store.embedding.EmbeddingStore;
+import jakarta.enterprise.context.ApplicationScoped;
+import jakarta.enterprise.inject.Produces;
+import jakarta.inject.Inject;
+import jakarta.inject.Singleton;
+import org.apache.camel.CamelContext;
+import
org.apache.camel.component.langchain4j.embeddingstore.EmbeddingStoreFactory;
+
+@ApplicationScoped
+public class Langchain4jEmbeddingstoreQl4jProducers {
+ @Inject
+ EmbeddingStore<TextSegment> embeddingStore;
+
+ @Produces
+ @Singleton
+ EmbeddingStoreFactory embeddingStoreFactory() {
+ return new EmbeddingStoreFactory() {
+ CamelContext camelContext;
+
+ @Override
+ public EmbeddingStore<TextSegment> createEmbeddingStore() {
+ return embeddingStore;
+ }
+
+ @Override
+ public void setCamelContext(CamelContext camelContext) {
+ this.camelContext = camelContext;
+ }
+
+ @Override
+ public CamelContext getCamelContext() {
+ return this.camelContext;
+ }
+ };
+ }
+
+ @Produces
+ @Singleton
+ ContentRetriever contentRetriever(EmbeddingModel embeddingModel) {
+ return EmbeddingStoreContentRetriever.builder()
+ .embeddingStore(embeddingStore)
+ .embeddingModel(embeddingModel)
+ .build();
+ }
+}
diff --git
a/integration-tests/langchain4j-embeddingstore-ql4j/src/main/java/org/apache/camel/quarkus/component/langchain4j/embeddingstore/ql4j/it/Langchain4jEmbeddingstoreQl4jResource.java
b/integration-tests/langchain4j-embeddingstore-ql4j/src/main/java/org/apache/camel/quarkus/component/langchain4j/embeddingstore/ql4j/it/Langchain4jEmbeddingstoreQl4jResource.java
new file mode 100644
index 0000000000..b14e04459d
--- /dev/null
+++
b/integration-tests/langchain4j-embeddingstore-ql4j/src/main/java/org/apache/camel/quarkus/component/langchain4j/embeddingstore/ql4j/it/Langchain4jEmbeddingstoreQl4jResource.java
@@ -0,0 +1,131 @@
+/*
+ * 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.embeddingstore.ql4j.it;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import dev.langchain4j.data.segment.TextSegment;
+import dev.langchain4j.model.embedding.EmbeddingModel;
+import dev.langchain4j.rag.content.Content;
+import dev.langchain4j.rag.content.retriever.ContentRetriever;
+import dev.langchain4j.rag.query.Query;
+import dev.langchain4j.store.embedding.EmbeddingMatch;
+import jakarta.enterprise.context.ApplicationScoped;
+import jakarta.inject.Inject;
+import jakarta.ws.rs.Consumes;
+import jakarta.ws.rs.DELETE;
+import jakarta.ws.rs.POST;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.PathParam;
+import jakarta.ws.rs.Produces;
+import jakarta.ws.rs.QueryParam;
+import jakarta.ws.rs.core.MediaType;
+import org.apache.camel.FluentProducerTemplate;
+import
org.apache.camel.component.langchain4j.embeddingstore.EmbeddingStoreFactory;
+import
org.apache.camel.component.langchain4j.embeddingstore.LangChain4jEmbeddingStoreAction;
+import
org.apache.camel.component.langchain4j.embeddingstore.LangChain4jEmbeddingStoreHeaders;
+
+@Path("/langchain4j-embeddingstore")
+@ApplicationScoped
+public class Langchain4jEmbeddingstoreQl4jResource {
+ @Inject
+ FluentProducerTemplate fluentProducerTemplate;
+
+ @Inject
+ ContentRetriever contentRetriever;
+
+ @Inject
+ EmbeddingModel embeddingModel;
+
+ @Inject
+ EmbeddingStoreFactory embeddingStoreFactory;
+
+ @Path("/{embeddingStoreType}/add")
+ @POST
+ @Consumes(MediaType.TEXT_PLAIN)
+ @Produces(MediaType.TEXT_PLAIN)
+ public String embeddingStoreAdd(
+ @PathParam("embeddingStoreType") String embeddingStoreType,
+ String content) {
+
+ return fluentProducerTemplate.to("direct:start")
+ .withHeader("embeddingStoreType", embeddingStoreType)
+ .withHeader(LangChain4jEmbeddingStoreHeaders.ACTION,
LangChain4jEmbeddingStoreAction.ADD)
+ .withBody(content)
+ .request(String.class);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Path("/{embeddingStoreType}/search")
+ @POST
+ @Consumes(MediaType.TEXT_PLAIN)
+ @Produces(MediaType.APPLICATION_JSON)
+ public List<Map<String, String>> embeddingStoreSearch(
+ @PathParam("embeddingStoreType") String embeddingStoreType,
+ String content) {
+
+ List<EmbeddingMatch<TextSegment>> matches =
fluentProducerTemplate.to("direct:start")
+ .withHeader("embeddingStoreType", embeddingStoreType)
+ .withHeader(LangChain4jEmbeddingStoreHeaders.ACTION,
LangChain4jEmbeddingStoreAction.SEARCH)
+ .withBody(content)
+ .request(List.class);
+
+ List<Map<String, String>> results = new ArrayList<>();
+ matches.forEach(match -> {
+ Map<String, String> embeddingInfo = new HashMap<>();
+ embeddingInfo.put("embeddingId", match.embeddingId());
+ embeddingInfo.put("text", match.embedded().text());
+ results.add(embeddingInfo);
+ });
+
+ return results;
+ }
+
+ @Path("/{embeddingStoreType}/remove")
+ @DELETE
+ public void embeddingStoreDelete(
+ @PathParam("embeddingStoreType") String embeddingStoreType,
+ @QueryParam("embeddingId") String embeddingId) {
+ fluentProducerTemplate.to("direct:start")
+ .withHeader("embeddingStoreType", embeddingStoreType)
+ .withHeader(LangChain4jEmbeddingStoreHeaders.ACTION,
LangChain4jEmbeddingStoreAction.REMOVE)
+ .withBody(embeddingId)
+ .request();
+ }
+
+ @Path("/qdrant/retrieve")
+ @POST
+ @Consumes(MediaType.TEXT_PLAIN)
+ @Produces(MediaType.APPLICATION_JSON)
+ public List<String> ql4jRetrieve(String query) {
+ List<Content> results = contentRetriever.retrieve(Query.from(query));
+ return results.stream().map(c -> c.textSegment().text()).toList();
+ }
+
+ @Path("/qdrant/ingest")
+ @POST
+ @Consumes(MediaType.TEXT_PLAIN)
+ @Produces(MediaType.TEXT_PLAIN)
+ public String ql4jIngest(String text) throws Exception {
+ TextSegment segment = TextSegment.from(text);
+ return embeddingStoreFactory.createEmbeddingStore()
+ .add(embeddingModel.embed(segment).content(), segment);
+ }
+}
diff --git
a/integration-tests/langchain4j-embeddingstore-ql4j/src/main/java/org/apache/camel/quarkus/component/langchain4j/embeddingstore/ql4j/it/Langchain4jEmbeddingstoreQl4jRoutes.java
b/integration-tests/langchain4j-embeddingstore-ql4j/src/main/java/org/apache/camel/quarkus/component/langchain4j/embeddingstore/ql4j/it/Langchain4jEmbeddingstoreQl4jRoutes.java
new file mode 100644
index 0000000000..d635c15fe6
--- /dev/null
+++
b/integration-tests/langchain4j-embeddingstore-ql4j/src/main/java/org/apache/camel/quarkus/component/langchain4j/embeddingstore/ql4j/it/Langchain4jEmbeddingstoreQl4jRoutes.java
@@ -0,0 +1,31 @@
+/*
+ * 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.embeddingstore.ql4j.it;
+
+import org.apache.camel.builder.RouteBuilder;
+
+public class Langchain4jEmbeddingstoreQl4jRoutes extends RouteBuilder {
+ @Override
+ public void configure() throws Exception {
+ from("direct:start")
+
.choice().when().simple("${header.CamelLangchain4jEmbeddingStoreAction} !=
'REMOVE'")
+ .toD("langchain4j-embeddings:${header.embeddingStoreType}")
+ .endChoice()
+ .end()
+
.toD("langchain4j-embeddingstore:${header.embeddingStoreType}");
+ }
+}
diff --git
a/integration-tests/langchain4j-embeddingstore-ql4j/src/main/resources/application.properties
b/integration-tests/langchain4j-embeddingstore-ql4j/src/main/resources/application.properties
new file mode 100644
index 0000000000..a23f7fddc8
--- /dev/null
+++
b/integration-tests/langchain4j-embeddingstore-ql4j/src/main/resources/application.properties
@@ -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.
+## ---------------------------------------------------------------------------
+
+quarkus.devservices.enabled=false
+quarkus.langchain4j.devservices.enabled=false
+quarkus.langchain4j.ollama.base-url=http://localhost:11434
+quarkus.langchain4j.ollama.embedding-model.model-id=nomic-embed-text
+quarkus.langchain4j.embedding-model.provider=ollama
+quarkus.langchain4j.qdrant.collection-name=camel-quarkus-ql4j-collection
diff --git
a/integration-tests/langchain4j-embeddingstore-ql4j/src/test/java/org/apache/camel/quarkus/component/langchain4j/embeddingstore/ql4j/it/Langchain4jQdrantEmbeddingstoreQl4jIT.java
b/integration-tests/langchain4j-embeddingstore-ql4j/src/test/java/org/apache/camel/quarkus/component/langchain4j/embeddingstore/ql4j/it/Langchain4jQdrantEmbeddingstoreQl4jIT.java
new file mode 100644
index 0000000000..068f893cb5
--- /dev/null
+++
b/integration-tests/langchain4j-embeddingstore-ql4j/src/test/java/org/apache/camel/quarkus/component/langchain4j/embeddingstore/ql4j/it/Langchain4jQdrantEmbeddingstoreQl4jIT.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.embeddingstore.ql4j.it;
+
+import io.quarkus.test.junit.QuarkusIntegrationTest;
+
+@QuarkusIntegrationTest
+class Langchain4jQdrantEmbeddingstoreQl4jIT extends
Langchain4jQdrantEmbeddingstoreQl4jTest {
+}
diff --git
a/integration-tests/langchain4j-embeddingstore-ql4j/src/test/java/org/apache/camel/quarkus/component/langchain4j/embeddingstore/ql4j/it/Langchain4jQdrantEmbeddingstoreQl4jTest.java
b/integration-tests/langchain4j-embeddingstore-ql4j/src/test/java/org/apache/camel/quarkus/component/langchain4j/embeddingstore/ql4j/it/Langchain4jQdrantEmbeddingstoreQl4jTest.java
new file mode 100644
index 0000000000..a7609ad8e6
--- /dev/null
+++
b/integration-tests/langchain4j-embeddingstore-ql4j/src/test/java/org/apache/camel/quarkus/component/langchain4j/embeddingstore/ql4j/it/Langchain4jQdrantEmbeddingstoreQl4jTest.java
@@ -0,0 +1,120 @@
+/*
+ * 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.embeddingstore.ql4j.it;
+
+import java.util.UUID;
+import java.util.concurrent.TimeUnit;
+
+import io.quarkus.test.common.QuarkusTestResource;
+import io.quarkus.test.junit.QuarkusTest;
+import io.restassured.RestAssured;
+import org.awaitility.Awaitility;
+import org.junit.jupiter.api.Test;
+
+import static org.hamcrest.Matchers.greaterThanOrEqualTo;
+import static org.hamcrest.Matchers.hasItem;
+import static org.hamcrest.Matchers.is;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+@QuarkusTestResource(OllamaWireMockTestResource.class)
+@QuarkusTestResource(QdrantTestResource.class)
+@QuarkusTest
+class Langchain4jQdrantEmbeddingstoreQl4jTest {
+ static final String CAMEL_MESSAGE = "Hello Camel Quarkus LangChain4j
Embedding Store";
+ static final String QL4J_MESSAGE = "Hello from Quarkus LangChain4j direct
ingest";
+
+ @Test
+ void qdrantEmbeddingstoreQl4jInterop() {
+ // 1. Camel ADD
+ String id = RestAssured.given()
+ .body(CAMEL_MESSAGE)
+ .post("/langchain4j-embeddingstore/qdrant/add")
+ .then()
+ .statusCode(200)
+ .extract()
+ .body()
+ .asString();
+ assertNotNull(UUID.fromString(id));
+
+ // 2. Camel SEARCH
+ RestAssured.given()
+ .body(CAMEL_MESSAGE)
+ .post("/langchain4j-embeddingstore/qdrant/search")
+ .then()
+ .statusCode(200)
+ .body(
+ "size()", is(1),
+ "[0].embeddingId", is(id),
+ "[0].text", is(CAMEL_MESSAGE));
+
+ // 3. QL4J retrieves Camel-written data
+ RestAssured.given()
+ .body(CAMEL_MESSAGE)
+ .post("/langchain4j-embeddingstore/qdrant/retrieve")
+ .then()
+ .statusCode(200)
+ .body(
+ "size()", greaterThanOrEqualTo(1),
+ "$", hasItem(CAMEL_MESSAGE));
+
+ // 4. QL4J ingests directly (bypassing Camel)
+ String ql4jId = RestAssured.given()
+ .body(QL4J_MESSAGE)
+ .post("/langchain4j-embeddingstore/qdrant/ingest")
+ .then()
+ .statusCode(200)
+ .extract()
+ .body()
+ .asString();
+ assertNotNull(UUID.fromString(ql4jId));
+
+ // 5. Camel SEARCH finds QL4J-written data
+ Awaitility.await().pollInterval(250, TimeUnit.MILLISECONDS).atMost(10,
TimeUnit.SECONDS).untilAsserted(() -> {
+ RestAssured.given()
+ .body(QL4J_MESSAGE)
+ .post("/langchain4j-embeddingstore/qdrant/search")
+ .then()
+ .statusCode(200)
+ .body(
+ "size()", greaterThanOrEqualTo(1),
+ "[0].text", is(QL4J_MESSAGE));
+ });
+
+ // 6. Camel REMOVE both embeddings with confirmation
+ RestAssured.given()
+ .queryParam("embeddingId", id)
+ .delete("/langchain4j-embeddingstore/qdrant/remove")
+ .then()
+ .statusCode(204);
+
+ RestAssured.given()
+ .queryParam("embeddingId", ql4jId)
+ .delete("/langchain4j-embeddingstore/qdrant/remove")
+ .then()
+ .statusCode(204);
+
+ Awaitility.await().pollInterval(250, TimeUnit.MILLISECONDS).atMost(10,
TimeUnit.SECONDS).untilAsserted(() -> {
+ RestAssured.given()
+ .body(CAMEL_MESSAGE)
+ .post("/langchain4j-embeddingstore/qdrant/search")
+ .then()
+ .statusCode(200)
+ .body(
+ "size()", is(0));
+ });
+ }
+}
diff --git
a/integration-tests/langchain4j-embeddingstore-ql4j/src/test/java/org/apache/camel/quarkus/component/langchain4j/embeddingstore/ql4j/it/OllamaWireMockTestResource.java
b/integration-tests/langchain4j-embeddingstore-ql4j/src/test/java/org/apache/camel/quarkus/component/langchain4j/embeddingstore/ql4j/it/OllamaWireMockTestResource.java
new file mode 100644
index 0000000000..65758d7186
--- /dev/null
+++
b/integration-tests/langchain4j-embeddingstore-ql4j/src/test/java/org/apache/camel/quarkus/component/langchain4j/embeddingstore/ql4j/it/OllamaWireMockTestResource.java
@@ -0,0 +1,49 @@
+/*
+ * 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.embeddingstore.ql4j.it;
+
+import java.util.Map;
+
+import
org.apache.camel.quarkus.test.wiremock.WireMockTestResourceLifecycleManager;
+
+public class OllamaWireMockTestResource extends
WireMockTestResourceLifecycleManager {
+ private static final String OLLAMA_ENV_URL = "LANGCHAIN4J_OLLAMA_BASE_URL";
+
+ @Override
+ public Map<String, String> start() {
+ Map<String, String> properties = super.start();
+ String wiremockUrl = properties.get("wiremock.url");
+ String url = wiremockUrl != null ? wiremockUrl :
getRecordTargetBaseUrl();
+ properties.put("quarkus.langchain4j.ollama.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 "embeddings";
+ }
+}
diff --git
a/integration-tests/langchain4j-embeddingstore-ql4j/src/test/java/org/apache/camel/quarkus/component/langchain4j/embeddingstore/ql4j/it/QdrantTestResource.java
b/integration-tests/langchain4j-embeddingstore-ql4j/src/test/java/org/apache/camel/quarkus/component/langchain4j/embeddingstore/ql4j/it/QdrantTestResource.java
new file mode 100644
index 0000000000..fd21a4f68e
--- /dev/null
+++
b/integration-tests/langchain4j-embeddingstore-ql4j/src/test/java/org/apache/camel/quarkus/component/langchain4j/embeddingstore/ql4j/it/QdrantTestResource.java
@@ -0,0 +1,105 @@
+/*
+ * 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.embeddingstore.ql4j.it;
+
+import java.io.IOException;
+import java.net.URI;
+import java.net.http.HttpClient;
+import java.net.http.HttpRequest;
+import java.net.http.HttpResponse;
+import java.util.HashMap;
+import java.util.Map;
+
+import io.quarkus.test.common.QuarkusTestResourceLifecycleManager;
+import org.eclipse.microprofile.config.ConfigProvider;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testcontainers.containers.GenericContainer;
+import org.testcontainers.containers.output.Slf4jLogConsumer;
+import org.testcontainers.qdrant.QdrantContainer;
+import org.testcontainers.utility.DockerImageName;
+
+public class QdrantTestResource implements QuarkusTestResourceLifecycleManager
{
+ private static final Logger LOG =
LoggerFactory.getLogger(QdrantTestResource.class);
+ private static final String QDRANT_IMAGE =
ConfigProvider.getConfig().getValue("qdrant.container.image", String.class);
+ private static final int QDRANT_HTTP_PORT = 6333;
+ private static final String COLLECTION_NAME =
"camel-quarkus-ql4j-collection";
+
+ private GenericContainer<?> qdrantContainer;
+
+ @Override
+ public Map<String, String> start() {
+ Map<String, String> properties = new HashMap<>();
+
+ DockerImageName qdrantImageName =
DockerImageName.parse(QDRANT_IMAGE).asCompatibleSubstituteFor("qdrant/qdrant");
+ qdrantContainer = new QdrantContainer(qdrantImageName)
+ .withLogConsumer(new Slf4jLogConsumer(LOG));
+ qdrantContainer.start();
+
+ String host = qdrantContainer.getHost();
+ Integer httpPort = qdrantContainer.getMappedPort(QDRANT_HTTP_PORT);
+
+ createCollection(host, httpPort);
+
+ properties.put("quarkus.qdrant.host", host);
+ properties.put("quarkus.qdrant.port", httpPort.toString());
+
+ LOG.info("Properties: {}", properties);
+
+ return properties;
+ }
+
+ private void createCollection(String host, int port) {
+ try {
+ HttpClient httpClient = HttpClient.newHttpClient();
+ String url = String.format("http://%s:%d/collections/%s", host,
port, COLLECTION_NAME);
+ // Vector size must match the embedding dimension in the WireMock
stubs under mappings/embeddings/
+ String body = """
+ {
+ "vectors": {
+ "size": 5,
+ "distance": "Cosine"
+ }
+ }
+ """;
+ HttpRequest request = HttpRequest.newBuilder()
+ .uri(URI.create(url))
+ .PUT(HttpRequest.BodyPublishers.ofString(body))
+ .header("Content-Type", "application/json")
+ .build();
+ HttpResponse<String> response = httpClient.send(request,
HttpResponse.BodyHandlers.ofString());
+ if (response.statusCode() != 200) {
+ throw new RuntimeException("Failed to create Qdrant
collection: " + response.body());
+ }
+ LOG.info("Created Qdrant collection '{}': {}", COLLECTION_NAME,
response.body());
+ } catch (IOException | InterruptedException e) {
+ throw new RuntimeException("Failed to create Qdrant collection",
e);
+ }
+ }
+
+ @Override
+ public void stop() {
+ try {
+ if (qdrantContainer != null) {
+ qdrantContainer.stop();
+ }
+ } catch (Exception ex) {
+ LOG.error("An issue occurred while stopping Qdrant container", ex);
+ }
+ }
+
+}
diff --git
a/integration-tests/langchain4j-embeddingstore-ql4j/src/test/resources/mappings/embeddings/api_embed-camel-store.json
b/integration-tests/langchain4j-embeddingstore-ql4j/src/test/resources/mappings/embeddings/api_embed-camel-store.json
new file mode 100644
index 0000000000..0a39aebc01
--- /dev/null
+++
b/integration-tests/langchain4j-embeddingstore-ql4j/src/test/resources/mappings/embeddings/api_embed-camel-store.json
@@ -0,0 +1,22 @@
+{
+ "id" : "b1c2d3e4-f5a6-7890-abcd-ef1234567801",
+ "name" : "api_embed_camel_store",
+ "request" : {
+ "url" : "/api/embed",
+ "method" : "POST",
+ "bodyPatterns" : [ {
+ "equalToJson" : "{\"model\":\"nomic-embed-text\",\"input\":\"Hello Camel
Quarkus LangChain4j Embedding Store\"}",
+ "ignoreArrayOrder" : true,
+ "ignoreExtraElements" : true
+ } ]
+ },
+ "response" : {
+ "status" : 200,
+ "body" :
"{\"model\":\"nomic-embed-text\",\"embeddings\":[[0.8,0.1,0.3,0.5,0.2]]}",
+ "headers" : {
+ "Content-Type" : "application/json; charset=utf-8"
+ }
+ },
+ "uuid" : "b1c2d3e4-f5a6-7890-abcd-ef1234567801",
+ "persistent" : true
+}
diff --git
a/integration-tests/langchain4j-embeddingstore-ql4j/src/test/resources/mappings/embeddings/api_embed-ql4j-ingest.json
b/integration-tests/langchain4j-embeddingstore-ql4j/src/test/resources/mappings/embeddings/api_embed-ql4j-ingest.json
new file mode 100644
index 0000000000..6a1834712f
--- /dev/null
+++
b/integration-tests/langchain4j-embeddingstore-ql4j/src/test/resources/mappings/embeddings/api_embed-ql4j-ingest.json
@@ -0,0 +1,22 @@
+{
+ "id" : "b1c2d3e4-f5a6-7890-abcd-ef1234567802",
+ "name" : "api_embed_ql4j_ingest",
+ "request" : {
+ "url" : "/api/embed",
+ "method" : "POST",
+ "bodyPatterns" : [ {
+ "equalToJson" : "{\"model\":\"nomic-embed-text\",\"input\":\"Hello from
Quarkus LangChain4j direct ingest\"}",
+ "ignoreArrayOrder" : true,
+ "ignoreExtraElements" : true
+ } ]
+ },
+ "response" : {
+ "status" : 200,
+ "body" :
"{\"model\":\"nomic-embed-text\",\"embeddings\":[[0.2,0.7,0.4,0.1,0.9]]}",
+ "headers" : {
+ "Content-Type" : "application/json; charset=utf-8"
+ }
+ },
+ "uuid" : "b1c2d3e4-f5a6-7890-abcd-ef1234567802",
+ "persistent" : true
+}
diff --git a/integration-tests/pom.xml b/integration-tests/pom.xml
index 901ecc7b62..d20a4931cf 100644
--- a/integration-tests/pom.xml
+++ b/integration-tests/pom.xml
@@ -167,6 +167,7 @@
<module>langchain4j-embeddings</module>
<module>langchain4j-embeddings-ql4j</module>
<module>langchain4j-embeddingstore</module>
+ <module>langchain4j-embeddingstore-ql4j</module>
<module>langchain4j-ingest</module>
<module>langchain4j-rag-bridge-ql4j</module>
<module>langchain4j-tokenizer</module>
diff --git a/tooling/scripts/test-categories.yaml
b/tooling/scripts/test-categories.yaml
index 8da660ecd9..1a342ec5e3 100644
--- a/tooling/scripts/test-categories.yaml
+++ b/tooling/scripts/test-categories.yaml
@@ -262,6 +262,7 @@ group-13:
- jdbc-grouped
- kubernetes
- langchain4j-embeddingstore
+ - langchain4j-embeddingstore-ql4j
- milvus
- salesforce
- sap-netweaver