This is an automated email from the ASF dual-hosted git repository.

Croway 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 0757d655cf9e CAMEL-24534: camel-qdrant - honour metadata value types 
instead of casting them to String
0757d655cf9e is described below

commit 0757d655cf9e12213b87117bb9dea69b103315fb
Author: Andrea Cosentino <[email protected]>
AuthorDate: Fri Aug 28 06:53:48 2026 +0200

    CAMEL-24534: camel-qdrant - honour metadata value types instead of casting 
them to String
    
    QdrantEmbeddingsDataTypeTransformer mapped every document metadata entry 
into the
    Qdrant payload with ValueFactory.value((String) value). The unchecked cast 
throws
    ClassCastException as soon as a metadata value is not a String, which 
happens
    routinely because LangChain4j document splitters add numeric metadata such 
as the
    chunk index and page number - so a normal ingestion pipeline that stores 
metadata
    fails at runtime.
    
    Convert each value to the matching Qdrant Value type (boolean, integer, 
double,
    otherwise String), mirroring how the pgvector sibling handles 
Number/Boolean.
    
    Co-Authored-By: Claude Opus 4.8 <[email protected]>
    Signed-off-by: Andrea Cosentino <[email protected]>
---
 .../QdrantEmbeddingsDataTypeTransformer.java       | 24 +++++++-
 .../QdrantEmbeddingsDataTypeTransformerTest.java   | 66 ++++++++++++++++++++++
 2 files changed, 89 insertions(+), 1 deletion(-)

diff --git 
a/components/camel-ai/camel-qdrant/src/main/java/org/apache/camel/component/qdrant/transform/QdrantEmbeddingsDataTypeTransformer.java
 
b/components/camel-ai/camel-qdrant/src/main/java/org/apache/camel/component/qdrant/transform/QdrantEmbeddingsDataTypeTransformer.java
index 3046fe09238e..6207c3b43087 100644
--- 
a/components/camel-ai/camel-qdrant/src/main/java/org/apache/camel/component/qdrant/transform/QdrantEmbeddingsDataTypeTransformer.java
+++ 
b/components/camel-ai/camel-qdrant/src/main/java/org/apache/camel/component/qdrant/transform/QdrantEmbeddingsDataTypeTransformer.java
@@ -25,6 +25,7 @@ import io.qdrant.client.PointIdFactory;
 import io.qdrant.client.ValueFactory;
 import io.qdrant.client.VectorsFactory;
 import io.qdrant.client.grpc.Common;
+import io.qdrant.client.grpc.JsonWithInt.Value;
 import io.qdrant.client.grpc.Points;
 import org.apache.camel.Message;
 import org.apache.camel.ai.CamelLangchain4jAttributes;
@@ -55,10 +56,31 @@ public class QdrantEmbeddingsDataTypeTransformer extends 
Transformer {
             builder.putPayload("text_segment", 
ValueFactory.value(text.text()));
             Metadata metadata = text.metadata();
             metadata.toMap()
-                    .forEach((key, value) -> builder.putPayload(key, 
ValueFactory.value((String) value)));
+                    .forEach((key, value) -> builder.putPayload(key, 
toValue(value)));
 
         }
 
         message.setBody(builder.build());
     }
+
+    /**
+     * Converts a LangChain4j metadata value to a Qdrant payload value. 
Metadata is not always a String - document
+     * splitters routinely add numeric entries such as the chunk index or page 
number - so the value type must be
+     * honoured instead of being blindly cast to String.
+     */
+    private static Value toValue(Object value) {
+        if (value == null) {
+            return ValueFactory.nullValue();
+        }
+        if (value instanceof Boolean booleanValue) {
+            return ValueFactory.value(booleanValue);
+        }
+        if (value instanceof Integer || value instanceof Long) {
+            return ValueFactory.value(((Number) value).longValue());
+        }
+        if (value instanceof Number number) {
+            return ValueFactory.value(number.doubleValue());
+        }
+        return ValueFactory.value(String.valueOf(value));
+    }
 }
diff --git 
a/components/camel-ai/camel-qdrant/src/test/java/org/apache/camel/component/qdrant/transform/QdrantEmbeddingsDataTypeTransformerTest.java
 
b/components/camel-ai/camel-qdrant/src/test/java/org/apache/camel/component/qdrant/transform/QdrantEmbeddingsDataTypeTransformerTest.java
new file mode 100644
index 000000000000..f6b1f34da15b
--- /dev/null
+++ 
b/components/camel-ai/camel-qdrant/src/test/java/org/apache/camel/component/qdrant/transform/QdrantEmbeddingsDataTypeTransformerTest.java
@@ -0,0 +1,66 @@
+/*
+ * 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.component.qdrant.transform;
+
+import java.util.Map;
+
+import dev.langchain4j.data.document.Metadata;
+import dev.langchain4j.data.embedding.Embedding;
+import dev.langchain4j.data.segment.TextSegment;
+import io.qdrant.client.grpc.JsonWithInt.Value;
+import io.qdrant.client.grpc.Points;
+import org.apache.camel.Exchange;
+import org.apache.camel.ai.CamelLangchain4jAttributes;
+import org.apache.camel.impl.DefaultCamelContext;
+import org.apache.camel.spi.DataType;
+import org.apache.camel.support.DefaultExchange;
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+class QdrantEmbeddingsDataTypeTransformerTest {
+
+    @Test
+    void mapsMixedMetadataTypesToTypedPayload() throws Exception {
+        // A TextSegment carrying String and numeric metadata, as document 
splitters routinely produce
+        // (chunk index, page number, ...). The transformer must not blindly 
cast every value to String.
+        Metadata metadata = new Metadata()
+                .put("source", "doc.txt")
+                .put("index", 3)
+                .put("score", 0.75);
+        TextSegment segment = TextSegment.from("hello world", metadata);
+        Embedding embedding = new Embedding(new float[] { 0.1f, 0.2f, 0.3f });
+
+        try (DefaultCamelContext context = new DefaultCamelContext()) {
+            context.start();
+            Exchange exchange = new DefaultExchange(context);
+            
exchange.getMessage().setHeader(CamelLangchain4jAttributes.CAMEL_LANGCHAIN4J_EMBEDDING_VECTOR,
 embedding);
+            exchange.getMessage().setBody(segment);
+
+            new 
QdrantEmbeddingsDataTypeTransformer().transform(exchange.getMessage(), 
DataType.ANY, DataType.ANY);
+
+            Points.PointStruct point = 
exchange.getMessage().getBody(Points.PointStruct.class);
+            assertThat(point).isNotNull();
+
+            Map<String, Value> payload = point.getPayloadMap();
+            
assertThat(payload.get("text_segment").getStringValue()).isEqualTo("hello 
world");
+            
assertThat(payload.get("source").getStringValue()).isEqualTo("doc.txt");
+            assertThat(payload.get("index").getIntegerValue()).isEqualTo(3L);
+            assertThat(payload.get("score").getDoubleValue()).isEqualTo(0.75);
+        }
+    }
+}

Reply via email to