This is an automated email from the ASF dual-hosted git repository.
zbendhiba pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/main by this push:
new e9e869f9c25 CAMEL-21719: Neo4j Embedding Data transfomer for RAG
results (#17072)
e9e869f9c25 is described below
commit e9e869f9c2587677b976e2fe2eff96f7cd48718b
Author: Zineb BENDHIBA <[email protected]>
AuthorDate: Wed Feb 5 20:09:12 2025 +0100
CAMEL-21719: Neo4j Embedding Data transfomer for RAG results (#17072)
---
.../apache/camel/catalog/transformers.properties | 1 +
.../camel/catalog/transformers/neo4j-rag.json | 14 ++++++
...angChain4jEmbeddingsComponentNeo4jTargetIT.java | 29 +++++++++++-
.../org/apache/camel/transformer.properties | 2 +-
.../org/apache/camel/transformer/neo4j-rag | 2 +
.../org/apache/camel/transformer/neo4j-rag.json | 14 ++++++
.../camel-neo4j/src/main/docs/neo4j-component.adoc | 49 +++++++++++++++-----
...ataTypeTransformer.java => Neo4jEmbedding.java} | 41 +++++++++++------
.../camel/component/neo4j/Neo4jProducer.java | 52 ++++++++++++++++------
.../Neo4jEmbeddingDataTypeTransformer.java | 17 ++++++-
...Neo4jReverseEmbeddingsDataTypeTransformer.java} | 27 ++++++++---
.../neo4j/it/Neo4jVectorEmbeddingsIT.java | 48 ++++++++++++++++----
12 files changed, 238 insertions(+), 58 deletions(-)
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/transformers.properties
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/transformers.properties
index 4a3560e1fe5..b1ffb14a926 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/transformers.properties
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/transformers.properties
@@ -29,6 +29,7 @@ google-storage-application-cloudevents
http-application-cloudevents
milvus-embeddings
neo4j-embeddings
+neo4j-rag
pinecone-embeddings
protobuf-binary
protobuf-x-java-object
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/transformers/neo4j-rag.json
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/transformers/neo4j-rag.json
new file mode 100644
index 00000000000..27e84a5f080
--- /dev/null
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/transformers/neo4j-rag.json
@@ -0,0 +1,14 @@
+{
+ "transformer": {
+ "kind": "transformer",
+ "name": "neo4j:rag",
+ "title": "Neo4j (Rag)",
+ "description": "Prepares the similarity search LangChain4j embeddings to
become a List of String for LangChain4j RAG",
+ "deprecated": false,
+ "javaType":
"org.apache.camel.component.neo4j.transformer.Neo4jReverseEmbeddingsDataTypeTransformer",
+ "groupId": "org.apache.camel",
+ "artifactId": "camel-neo4j",
+ "version": "4.10.0-SNAPSHOT"
+ }
+}
+
diff --git
a/components/camel-ai/camel-langchain4j-embeddings/src/test/java/org/apache/camel/component/langchain4j/embeddings/LangChain4jEmbeddingsComponentNeo4jTargetIT.java
b/components/camel-ai/camel-langchain4j-embeddings/src/test/java/org/apache/camel/component/langchain4j/embeddings/LangChain4jEmbeddingsComponentNeo4jTargetIT.java
index 46fd8a65aa0..b6d29f463d5 100644
---
a/components/camel-ai/camel-langchain4j-embeddings/src/test/java/org/apache/camel/component/langchain4j/embeddings/LangChain4jEmbeddingsComponentNeo4jTargetIT.java
+++
b/components/camel-ai/camel-langchain4j-embeddings/src/test/java/org/apache/camel/component/langchain4j/embeddings/LangChain4jEmbeddingsComponentNeo4jTargetIT.java
@@ -16,6 +16,7 @@
*/
package org.apache.camel.component.langchain4j.embeddings;
+import java.util.Collection;
import java.util.List;
import java.util.Map;
@@ -28,6 +29,7 @@ import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.component.neo4j.Neo4Operation;
import org.apache.camel.component.neo4j.Neo4jComponent;
import org.apache.camel.component.neo4j.Neo4jConstants;
+import org.apache.camel.spi.DataType;
import org.apache.camel.test.infra.neo4j.services.Neo4jService;
import org.apache.camel.test.infra.neo4j.services.Neo4jServiceFactory;
import org.apache.camel.test.junit5.CamelTestSupport;
@@ -47,7 +49,7 @@ import static org.junit.Assert.assertTrue;
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class LangChain4jEmbeddingsComponentNeo4jTargetIT extends
CamelTestSupport {
- public static final String NEO4J_URI = "neo4j:neo4j";
+ public static final String NEO4J_URI =
"neo4j:neo4j?vectorIndexName=myIndex&label=Test";
@RegisterExtension
static Neo4jService NEO4J = Neo4jServiceFactory.createSingletonService();
@@ -138,6 +140,20 @@ public class LangChain4jEmbeddingsComponentNeo4jTargetIT
extends CamelTestSuppor
}
+ @Test
+ @Order(3)
+ public void rag_similarity_search() {
+ Exchange result = fluentTemplate.to("direct:search")
+ .withBody("hi")
+ .request(Exchange.class);
+
+ assertThat(result).isNotNull();
+ assertThat(result.getException()).isNull();
+
+
assertThat(result.getIn().getBody()).isInstanceOfSatisfying(Collection.class, c
-> assertThat(c).hasSize(1));
+ assertTrue(result.getIn().getBody(List.class).contains("hi"));
+ }
+
@Override
protected RoutesBuilder createRouteBuilder() {
return new RouteBuilder() {
@@ -149,6 +165,17 @@ public class LangChain4jEmbeddingsComponentNeo4jTargetIT
extends CamelTestSuppor
.setHeader(Neo4jConstants.Headers.LABEL).constant("Test")
.transform(new
org.apache.camel.spi.DataType("neo4j:embeddings"))
.to(NEO4J_URI);
+
+ from("direct:search")
+ .to("langchain4j-embeddings:test")
+ // transform prompt into embeddings for search
+ .transform(
+ new DataType("neo4j:embeddings"))
+ .setHeader(Neo4jConstants.Headers.OPERATION,
constant(Neo4Operation.VECTOR_SIMILARITY_SEARCH))
+ .to(NEO4J_URI)
+ // decode retrieved embeddings for RAG
+ .transform(
+ new DataType("neo4j:rag"));
}
};
}
diff --git
a/components/camel-ai/camel-neo4j/src/generated/resources/META-INF/services/org/apache/camel/transformer.properties
b/components/camel-ai/camel-neo4j/src/generated/resources/META-INF/services/org/apache/camel/transformer.properties
index 90fcac63c4f..10c4e1d06a4 100644
---
a/components/camel-ai/camel-neo4j/src/generated/resources/META-INF/services/org/apache/camel/transformer.properties
+++
b/components/camel-ai/camel-neo4j/src/generated/resources/META-INF/services/org/apache/camel/transformer.properties
@@ -1,5 +1,5 @@
# Generated by camel build tools - do NOT edit this file!
-transformers=neo4j:embeddings
+transformers=neo4j:embeddings neo4j:rag
groupId=org.apache.camel
artifactId=camel-neo4j
version=4.10.0-SNAPSHOT
diff --git
a/components/camel-ai/camel-neo4j/src/generated/resources/META-INF/services/org/apache/camel/transformer/neo4j-rag
b/components/camel-ai/camel-neo4j/src/generated/resources/META-INF/services/org/apache/camel/transformer/neo4j-rag
new file mode 100644
index 00000000000..179c57958a6
--- /dev/null
+++
b/components/camel-ai/camel-neo4j/src/generated/resources/META-INF/services/org/apache/camel/transformer/neo4j-rag
@@ -0,0 +1,2 @@
+# Generated by camel build tools - do NOT edit this file!
+class=org.apache.camel.component.neo4j.transformer.Neo4jReverseEmbeddingsDataTypeTransformer
diff --git
a/components/camel-ai/camel-neo4j/src/generated/resources/META-INF/services/org/apache/camel/transformer/neo4j-rag.json
b/components/camel-ai/camel-neo4j/src/generated/resources/META-INF/services/org/apache/camel/transformer/neo4j-rag.json
new file mode 100644
index 00000000000..27e84a5f080
--- /dev/null
+++
b/components/camel-ai/camel-neo4j/src/generated/resources/META-INF/services/org/apache/camel/transformer/neo4j-rag.json
@@ -0,0 +1,14 @@
+{
+ "transformer": {
+ "kind": "transformer",
+ "name": "neo4j:rag",
+ "title": "Neo4j (Rag)",
+ "description": "Prepares the similarity search LangChain4j embeddings to
become a List of String for LangChain4j RAG",
+ "deprecated": false,
+ "javaType":
"org.apache.camel.component.neo4j.transformer.Neo4jReverseEmbeddingsDataTypeTransformer",
+ "groupId": "org.apache.camel",
+ "artifactId": "camel-neo4j",
+ "version": "4.10.0-SNAPSHOT"
+ }
+}
+
diff --git a/components/camel-ai/camel-neo4j/src/main/docs/neo4j-component.adoc
b/components/camel-ai/camel-neo4j/src/main/docs/neo4j-component.adoc
index 5fc4b0a8960..ea552b2faa2 100644
--- a/components/camel-ai/camel-neo4j/src/main/docs/neo4j-component.adoc
+++ b/components/camel-ai/camel-neo4j/src/main/docs/neo4j-component.adoc
@@ -223,16 +223,20 @@ The URI endpoint should contain also specify the vector
index name.
=== Create a vector
To create a vector in a database named `test`, use the operation
`CREATE_VECTOR`.
The URI endpoint should also specify the label, the alias and the vector index
name.
+Put the vector array in the `CamelLangChain4jEmbeddingsVector` header, and the
corresponding text in the body.
+The `id` can be generated by Camel Neo4j.
+
+Camel Neo4j will create the node and store the vector as an `embedding`
property, the text as `text` property and the `id`as `id` property.
-Camel Neo4j will create the node and store the vector as an `embedding`
property.
.Example:
[source,java]
----
- Exchange result =
fluentTemplate.to("neo4j:test?vectorIndexName=movieIdx&label=Movie&alias=m")
- .withHeader(Neo4j.Headers.OPERATION,
Neo4Operation.CREATE_VECTOR)
- .withHeader(Neo4j.Headers.VECTOR_ID, testData.getId())
- .withBody(List.of(0.8f, 0.6f))
+ Exchange result =
fluentTemplate.to("neo4j:test?vectorIndexName=myIndex&label=Test&alias=t")
+ .withHeader(Neo4jConstants.Headers.OPERATION,
Neo4Operation.CREATE_VECTOR)
+ .withHeader(Neo4jConstants.Headers.VECTOR_ID, "1")
+ .withHeader("CamelLangChain4jEmbeddingsVector", new float[] {
10.8f, 10.6f })
+ .withBody("Hello World!")
.request(Exchange.class);
----
@@ -244,9 +248,8 @@ The URI endpoint should also specify the label, the alias
and the vector index n
.Example:
[source,java]
----
- Exchange result =
fluentTemplate.to("neo4j:test?vectorIndexName=movieIdx&label=Movie&alias=m")
- .withHeader(Neo4j.Headers.OPERATION,
Neo4Operation.CREATE_VECTOR)
- .withHeader(Neo4j.Headers.VECTOR_ID, testData.getId())
+ Exchange result =
fluentTemplate.to("neo4j:test?vectorIndexName=myIndex&label=Test&alias=t")
+ .withHeader(Neo4jConstants.Headers.OPERATION,
Neo4Operation.VECTOR_SIMILARITY_SEARCH)
.withBody(List.of(0.75f, 0.65f))
.request(Exchange.class);
----
@@ -254,7 +257,7 @@ The URI endpoint should also specify the label, the alias
and the vector index n
== Generate Embeddings with Langchain4j-embeddings
You can generate embeddings with an Embedding Models using the camel
Lancghain4j Embeddings components. Camel Neo4j introduces a DataType
`neo4j:embeddings` that automates the transformations of the Lancghain4j
embeddings to Neo4j vectors.
-.Example of a camel Route that create embeddings with Camel Langchain4j
Embeddings
+.Example of a camel Route that create embeddings with Camel Langchain4j
Embeddings, and ingest them into Neo4j database.
[source,java]
----
from("direct:in")
@@ -262,5 +265,31 @@ You can generate embeddings with an Embedding Models using
the camel Lancghain4j
.setHeader(Neo4j.Headers.OPERATION).constant(Neo4Operation.CREATE_VECTOR)
.setHeader(Neo4j.Headers.LABEL).constant("Test")
.transform(new DataType("neo4j:embeddings"))
- .to("neo4j:test");
+ .to("neo4j:neo4j?vectorIndexName=myIndex&label=Test");
----
+
+== Similarity Search for LangChain4j RAG
+You can enhance the Camel LangChain4j chat RAG experience by integrating Neo4j
similarity search with Camel Neo4j DataTypes.
+
+To achieve this, use the `neo4j:embeddings` DataType to generate embeddings
from the prompt. These embeddings will then be utilized for the similarity
search operation.
+
+Next, use the `neo4j:rag` DataType to convert the retrieved embeddings into a
List<String> for RAG. This list can be directly used with the
`LangChain4jRagAggregatorStrategy` from the LangChain4j chat component.
+
+NOTE: The retrieved embeddings must be ingested in Neo4j as LangChain4j
embeddings.
+
+.Example of a camel Route that performs a similarity search in the Vector
index, using a string and returning a list of strings
+[source,java]
+----
+ from("direct:search")
+ .to("langchain4j-embeddings:test")
+ // transform prompt into embeddings for search
+ .transform(
+ new DataType("neo4j:embeddings"))
+ .setHeader(Neo4jConstants.Headers.OPERATION,
constant(Neo4Operation.VECTOR_SIMILARITY_SEARCH))
+ .to("neo4j:neo4j?vectorIndexName=myIndex&label=Test")
+ // decode retrieved embeddings for RAG
+ .transform(
+ new DataType("neo4j:rag"));
+----
+
+
diff --git
a/components/camel-ai/camel-neo4j/src/main/java/org/apache/camel/component/neo4j/transformer/Neo4jEmbeddingDataTypeTransformer.java
b/components/camel-ai/camel-neo4j/src/main/java/org/apache/camel/component/neo4j/Neo4jEmbedding.java
similarity index 50%
copy from
components/camel-ai/camel-neo4j/src/main/java/org/apache/camel/component/neo4j/transformer/Neo4jEmbeddingDataTypeTransformer.java
copy to
components/camel-ai/camel-neo4j/src/main/java/org/apache/camel/component/neo4j/Neo4jEmbedding.java
index 78544256206..02d06718ecc 100644
---
a/components/camel-ai/camel-neo4j/src/main/java/org/apache/camel/component/neo4j/transformer/Neo4jEmbeddingDataTypeTransformer.java
+++
b/components/camel-ai/camel-neo4j/src/main/java/org/apache/camel/component/neo4j/Neo4jEmbedding.java
@@ -14,21 +14,34 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.camel.component.neo4j.transformer;
+package org.apache.camel.component.neo4j;
-import dev.langchain4j.data.embedding.Embedding;
-import org.apache.camel.Message;
-import org.apache.camel.ai.CamelLangchain4jAttributes;
-import org.apache.camel.spi.DataType;
-import org.apache.camel.spi.DataTypeTransformer;
-import org.apache.camel.spi.Transformer;
+/**
+ * Class that represents the embedding to persist when using LangChain4j - The
names of the properties correspond to the
+ * ones in LangChain4j project for compatibility.
+ */
+public class Neo4jEmbedding {
+ private String id;
+
+ private String text;
+
+ private float[] vectors;
+
+ public Neo4jEmbedding(String id, String text, float[] vectors) {
+ this.id = id;
+ this.text = text;
+ this.vectors = vectors;
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public String getText() {
+ return text;
+ }
-@DataTypeTransformer(name = "neo4j:embeddings",
- description = "Prepares the message to become an object
writable by Neo4j component")
-public class Neo4jEmbeddingDataTypeTransformer extends Transformer {
- @Override
- public void transform(Message message, DataType fromType, DataType toType)
{
- Embedding embedding =
message.getHeader(CamelLangchain4jAttributes.CAMEL_LANGCHAIN4J_EMBEDDING_VECTOR,
Embedding.class);
- message.setBody(embedding.vector());
+ public float[] getVectors() {
+ return vectors;
}
}
diff --git
a/components/camel-ai/camel-neo4j/src/main/java/org/apache/camel/component/neo4j/Neo4jProducer.java
b/components/camel-ai/camel-neo4j/src/main/java/org/apache/camel/component/neo4j/Neo4jProducer.java
index 6dfa6bc8838..c1ac6cb0a87 100644
---
a/components/camel-ai/camel-neo4j/src/main/java/org/apache/camel/component/neo4j/Neo4jProducer.java
+++
b/components/camel-ai/camel-neo4j/src/main/java/org/apache/camel/component/neo4j/Neo4jProducer.java
@@ -25,6 +25,7 @@ import org.apache.camel.Exchange;
import org.apache.camel.InvalidPayloadException;
import org.apache.camel.Message;
import org.apache.camel.NoSuchHeaderException;
+import org.apache.camel.ai.CamelLangchain4jAttributes;
import org.apache.camel.support.DefaultProducer;
import org.apache.camel.util.ObjectHelper;
import org.neo4j.driver.Driver;
@@ -242,37 +243,60 @@ public class Neo4jProducer extends DefaultProducer {
private void createVector(Exchange exchange) {
final String alias
- = getEndpoint().getConfiguration().getAlias() != null ?
getEndpoint().getConfiguration().getAlias() : "x";
+ = getEndpoint().getConfiguration().getAlias() != null ?
getEndpoint().getConfiguration().getAlias() : "e";
- final String label =
exchange.getMessage().getHeader(Neo4jConstants.Headers.LABEL,
- () -> getEndpoint().getConfiguration().getLabel(),
String.class);
- ObjectHelper.notNull(label, "label");
-
- final String id
- =
exchange.getMessage().getHeader(Neo4jConstants.Headers.VECTOR_ID, () ->
UUID.randomUUID(), String.class);
+ final String label
+ = getEndpoint().getConfiguration().getLabel() != null
+ ? getEndpoint().getConfiguration().getLabel() :
"Embedding";
- final float[] body = exchange.getMessage().getBody(float[].class);
+ String id;
+ String text;
+ float[] vectors;
final String databaseName = getEndpoint().getName();
+ Object body = exchange.getMessage().getBody();
+
+ if (body instanceof Neo4jEmbedding) {
+ id = ((Neo4jEmbedding) body).getId();
+ text = ((Neo4jEmbedding) body).getText();
+ vectors = ((Neo4jEmbedding) body).getVectors();
+ } else {
+ id =
exchange.getMessage().getHeader(Neo4jConstants.Headers.VECTOR_ID, () ->
UUID.randomUUID(), String.class);
+ vectors =
exchange.getMessage().getHeader(CamelLangchain4jAttributes.CAMEL_LANGCHAIN4J_EMBEDDING_VECTOR,
+ float[].class);
+ text = exchange.getMessage().getBody(String.class);
+ }
+
+ ObjectHelper.notNull(text, "text");
+ ObjectHelper.notNull(vectors, "vectors");
+
String query = String.format("""
- MERGE (%s:%s {id: $id})
+ MERGE (%s:%s {id: $id, text: $text})
WITH %s
CALL db.create.setNodeVectorProperty(%s, 'embedding',
$embedding);
""", alias, label, alias, alias);
Map<String, Object> params = Map.of(
- "embedding", Values.value(body),
- "id", id);
+ "embedding", Values.value(vectors),
+ "id", id,
+ "text", text);
executeWriteQuery(exchange, query, params, databaseName,
Neo4Operation.CREATE_VECTOR);
}
- public void similaritySearch(Exchange exchange) {
+ public void similaritySearch(Exchange exchange) throws
InvalidPayloadException {
final String vectorIndexName =
getEndpoint().getConfiguration().getVectorIndexName();
ObjectHelper.notNull(vectorIndexName, "vectorIndexName");
- final float[] body = exchange.getMessage().getBody(float[].class);
+ float[] vectors;
+
+ Object body = exchange.getMessage().getMandatoryBody();
+ if (body instanceof Neo4jEmbedding) {
+ vectors = ((Neo4jEmbedding) body).getVectors();
+ } else {
+ vectors = exchange.getMessage().getBody(float[].class);
+ }
final double minScore = getEndpoint().getConfiguration().getMinScore();
@@ -288,7 +312,7 @@ public class Neo4jProducer extends DefaultProducer {
""";
Map<String, Object> params = Map.of("indexName", vectorIndexName,
- "embeddingValue", body,
+ "embeddingValue", vectors,
"minScore", minScore,
"maxResults", maxResults);
diff --git
a/components/camel-ai/camel-neo4j/src/main/java/org/apache/camel/component/neo4j/transformer/Neo4jEmbeddingDataTypeTransformer.java
b/components/camel-ai/camel-neo4j/src/main/java/org/apache/camel/component/neo4j/transformer/Neo4jEmbeddingDataTypeTransformer.java
index 78544256206..2908a8ab057 100644
---
a/components/camel-ai/camel-neo4j/src/main/java/org/apache/camel/component/neo4j/transformer/Neo4jEmbeddingDataTypeTransformer.java
+++
b/components/camel-ai/camel-neo4j/src/main/java/org/apache/camel/component/neo4j/transformer/Neo4jEmbeddingDataTypeTransformer.java
@@ -16,9 +16,14 @@
*/
package org.apache.camel.component.neo4j.transformer;
+import java.util.UUID;
+
import dev.langchain4j.data.embedding.Embedding;
+import dev.langchain4j.data.segment.TextSegment;
import org.apache.camel.Message;
import org.apache.camel.ai.CamelLangchain4jAttributes;
+import org.apache.camel.component.neo4j.Neo4jConstants;
+import org.apache.camel.component.neo4j.Neo4jEmbedding;
import org.apache.camel.spi.DataType;
import org.apache.camel.spi.DataTypeTransformer;
import org.apache.camel.spi.Transformer;
@@ -28,7 +33,15 @@ import org.apache.camel.spi.Transformer;
public class Neo4jEmbeddingDataTypeTransformer extends Transformer {
@Override
public void transform(Message message, DataType fromType, DataType toType)
{
- Embedding embedding =
message.getHeader(CamelLangchain4jAttributes.CAMEL_LANGCHAIN4J_EMBEDDING_VECTOR,
Embedding.class);
- message.setBody(embedding.vector());
+ final Embedding embedding
+ =
message.getHeader(CamelLangchain4jAttributes.CAMEL_LANGCHAIN4J_EMBEDDING_VECTOR,
Embedding.class);
+
+ final TextSegment text = message.getBody(TextSegment.class);
+
+ final String id = message.getHeader(Neo4jConstants.Headers.VECTOR_ID,
() -> UUID.randomUUID(), String.class);
+
+ Neo4jEmbedding neo4jEmbedding = new Neo4jEmbedding(id, text.text(),
embedding.vector());
+
+ message.setBody(neo4jEmbedding);
}
}
diff --git
a/components/camel-ai/camel-neo4j/src/main/java/org/apache/camel/component/neo4j/transformer/Neo4jEmbeddingDataTypeTransformer.java
b/components/camel-ai/camel-neo4j/src/main/java/org/apache/camel/component/neo4j/transformer/Neo4jReverseEmbeddingsDataTypeTransformer.java
similarity index 59%
copy from
components/camel-ai/camel-neo4j/src/main/java/org/apache/camel/component/neo4j/transformer/Neo4jEmbeddingDataTypeTransformer.java
copy to
components/camel-ai/camel-neo4j/src/main/java/org/apache/camel/component/neo4j/transformer/Neo4jReverseEmbeddingsDataTypeTransformer.java
index 78544256206..cc711176585 100644
---
a/components/camel-ai/camel-neo4j/src/main/java/org/apache/camel/component/neo4j/transformer/Neo4jEmbeddingDataTypeTransformer.java
+++
b/components/camel-ai/camel-neo4j/src/main/java/org/apache/camel/component/neo4j/transformer/Neo4jReverseEmbeddingsDataTypeTransformer.java
@@ -14,21 +14,34 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
package org.apache.camel.component.neo4j.transformer;
-import dev.langchain4j.data.embedding.Embedding;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
import org.apache.camel.Message;
-import org.apache.camel.ai.CamelLangchain4jAttributes;
import org.apache.camel.spi.DataType;
import org.apache.camel.spi.DataTypeTransformer;
import org.apache.camel.spi.Transformer;
-@DataTypeTransformer(name = "neo4j:embeddings",
- description = "Prepares the message to become an object
writable by Neo4j component")
-public class Neo4jEmbeddingDataTypeTransformer extends Transformer {
+/**
+ * Maps a List of retrieved LangChain4j Embeddings with similarity search to a
List of String for LangChain4j RAG
+ **/
+@DataTypeTransformer(name = "neo4j:rag",
+ description = "Prepares the similarity search LangChain4j
embeddings to become a List of String for LangChain4j RAG")
+public class Neo4jReverseEmbeddingsDataTypeTransformer extends Transformer {
+
@Override
public void transform(Message message, DataType fromType, DataType toType)
{
- Embedding embedding =
message.getHeader(CamelLangchain4jAttributes.CAMEL_LANGCHAIN4J_EMBEDDING_VECTOR,
Embedding.class);
- message.setBody(embedding.vector());
+ final List<Map<String, Object>> embeddings =
message.getBody(List.class);
+
+ List<String> result = embeddings.stream()
+ .map(embedding -> (String) embedding.getOrDefault("text", ""))
+ .collect(Collectors.toList());
+
+ message.setBody(result);
+
}
}
diff --git
a/components/camel-ai/camel-neo4j/src/test/java/org/apache/camel/component/neo4j/it/Neo4jVectorEmbeddingsIT.java
b/components/camel-ai/camel-neo4j/src/test/java/org/apache/camel/component/neo4j/it/Neo4jVectorEmbeddingsIT.java
index b2e04c3a2c9..ab5fce9bd7a 100644
---
a/components/camel-ai/camel-neo4j/src/test/java/org/apache/camel/component/neo4j/it/Neo4jVectorEmbeddingsIT.java
+++
b/components/camel-ai/camel-neo4j/src/test/java/org/apache/camel/component/neo4j/it/Neo4jVectorEmbeddingsIT.java
@@ -20,8 +20,10 @@ import java.util.List;
import org.apache.camel.Exchange;
import org.apache.camel.Message;
+import org.apache.camel.ai.CamelLangchain4jAttributes;
import org.apache.camel.component.neo4j.Neo4Operation;
import org.apache.camel.component.neo4j.Neo4jConstants;
+import org.apache.camel.component.neo4j.Neo4jEmbedding;
import org.apache.camel.component.neo4j.Neo4jTestSupport;
import org.junit.jupiter.api.MethodOrderer;
import org.junit.jupiter.api.Order;
@@ -62,7 +64,8 @@ public class Neo4jVectorEmbeddingsIT extends Neo4jTestSupport
{
Exchange result =
fluentTemplate.to("neo4j:neo4j?vectorIndexName=movieIdx&label=Movie&alias=m")
.withHeader(Neo4jConstants.Headers.OPERATION,
Neo4Operation.CREATE_VECTOR)
.withHeader(Neo4jConstants.Headers.VECTOR_ID, testData.getId())
- .withBody(testData.getVectors())
+
.withHeader(CamelLangchain4jAttributes.CAMEL_LANGCHAIN4J_EMBEDDING_VECTOR,
testData.getVectors())
+ .withBody(testData.getText())
.request(Exchange.class);
assertNotNull(result);
@@ -77,8 +80,29 @@ public class Neo4jVectorEmbeddingsIT extends
Neo4jTestSupport {
assertEquals("A node creation is expected ", 1,
in.getHeader(Neo4jConstants.Headers.QUERY_RESULT_NODES_CREATED));
}
- @Test
@Order(2)
+ @Test
+ void addGeneratedNeo4jEmbedding() {
+ Neo4jEmbedding neo4jEmbedding = new Neo4jEmbedding("15", "Hello
World", new float[] { 10.8f, 10.6f });
+ Exchange result =
fluentTemplate.to("neo4j:neo4j?vectorIndexName=movieIdx&label=Movie&alias=m")
+ .withHeader(Neo4jConstants.Headers.OPERATION,
Neo4Operation.CREATE_VECTOR)
+ .withBody(neo4jEmbedding)
+ .request(Exchange.class);
+
+ assertNotNull(result);
+
+ Message in = result.getMessage();
+ assertNotNull(in);
+
+ assertEquals(Neo4Operation.CREATE_VECTOR,
in.getHeader(Neo4jConstants.Headers.OPERATION));
+ assertTrue("The executed request should contain the procedure of
setting vector embedding",
+ in.getHeader(Neo4jConstants.Headers.QUERY_RESULT, String.class)
+ .contains("CALL db.create.setNodeVectorProperty"));
+ assertEquals("A node creation is expected ", 1,
in.getHeader(Neo4jConstants.Headers.QUERY_RESULT_NODES_CREATED));
+ }
+
+ @Test
+ @Order(3)
public void similaritySeach() {
Exchange result =
fluentTemplate.to("neo4j:neo4j?vectorIndexName=movieIdx&label=Movie&alias=m")
.withHeader(Neo4jConstants.Headers.OPERATION,
Neo4Operation.VECTOR_SIMILARITY_SEARCH)
@@ -98,7 +122,7 @@ public class Neo4jVectorEmbeddingsIT extends
Neo4jTestSupport {
}
@Test
- @Order(3)
+ @Order(4)
void dropVectorIndex() {
Exchange result =
fluentTemplate.to("neo4j:neo4j?vectorIndexName=movieIdx")
.withHeader(Neo4jConstants.Headers.OPERATION,
Neo4Operation.DROP_VECTOR_INDEX)
@@ -116,17 +140,19 @@ public class Neo4jVectorEmbeddingsIT extends
Neo4jTestSupport {
// Enum to provide test data
public enum TestData {
- VECTOR_1(9, List.of(0.8f, 0.6f)),
- VECTOR_2(10, List.of(0.1f, 0.9f)),
- VECTOR_3(11, List.of(0.7f, 0.7f)),
- VECTOR_4(12, List.of(-0.3f, -0.9f)),
- VECTOR_5(13, List.of(1.2f, 0.8f));
+ VECTOR_1(9, "VECTOR_1", List.of(0.8f, 0.6f)),
+ VECTOR_2(10, "VECTOR_2", List.of(0.1f, 0.9f)),
+ VECTOR_3(11, "VECTOR_3", List.of(0.7f, 0.7f)),
+ VECTOR_4(12, "VECTOR_4", List.of(-0.3f, -0.9f)),
+ VECTOR_5(13, "VECTOR_5", List.of(1.2f, 0.8f));
private final int id;
+ private final String text;
private final List<Float> vectors;
- TestData(int id, List<Float> vectors) {
+ TestData(int id, String text, List<Float> vectors) {
this.id = id;
+ this.text = text;
this.vectors = vectors;
}
@@ -137,5 +163,9 @@ public class Neo4jVectorEmbeddingsIT extends
Neo4jTestSupport {
public List<Float> getVectors() {
return vectors;
}
+
+ public String getText() {
+ return text;
+ }
}
}