atiaomar1978-hub commented on code in PR #25273:
URL: https://github.com/apache/camel/pull/25273#discussion_r3741027830


##########
components/camel-ai/camel-langchain4j-embeddingstore/src/main/java/org/apache/camel/component/langchain4j/embeddingstore/LangChain4jEmbeddingStoreProducer.java:
##########
@@ -109,63 +110,145 @@ public void process(Exchange exchange) throws Exception {
     }
 
     /**
-     * Adds an embedding to the store with optional text segment.
+     * Adds embeddings to the store with optional text segments and 
caller-supplied IDs.
      *
      * <p>
-     * Expects the following headers:
+     * Supports both single and batch operations:
+     * </p>
+     *
+     * <p>
+     * <b>Single operation</b> - when the {@code 
CamelLangChain4jEmbeddingsEmbedding} header contains a single
+     * {@link Embedding}:
      * </p>
      * <ul>
-     * <li>{@code CamelLangchain4jEmbeddingEmbedding} - The embedding vector 
(required)</li>
-     * <li>{@code CamelLangchain4jEmbeddingTextSegment} - Associated text 
segment (optional)</li>
+     * <li>With caller-supplied ID header ({@code 
CamelLangchain4jEmbeddingStoreEmbeddingId}): calls
+     * {@code add(id, embedding)}</li>
+     * <li>With text segment header: calls {@code add(embedding, 
textSegment)}</li>
+     * <li>Without text segment: calls {@code add(embedding)}</li>
      * </ul>
      *
      * <p>
-     * Returns the generated embedding ID in the message body.
+     * <b>Batch operation</b> - when the {@code 
CamelLangChain4jEmbeddingsEmbeddings} header contains a
+     * {@code List<Embedding>}:
      * </p>
+     * <ul>
+     * <li>With IDs header ({@code 
CamelLangchain4jEmbeddingStoreEmbeddingIds}) and text segments body: calls
+     * {@code addAll(ids, embeddings, textSegments)}</li>
+     * <li>With text segments body: calls {@code addAll(embeddings, 
textSegments)}</li>
+     * <li>Without text segments: calls {@code addAll(embeddings)}</li>
+     * </ul>
      *
      * @param  exchange  the Camel exchange containing the embedding data
      * @throws Exception if the add operation fails
      */
+    @SuppressWarnings("unchecked")
     private void add(Exchange exchange) throws Exception {
         final Message in = exchange.getMessage();
+        EmbeddingStore<TextSegment> store = 
getEndpoint().getConfiguration().getEmbeddingStore();
+
+        // Check for batch embeddings header first
+        List<Embedding> embeddings = 
in.getHeader(LangChain4jEmbeddingsHeaders.EMBEDDINGS, List.class);
+        if (embeddings != null) {
+            addBatch(in, store, embeddings);
+            return;
+        }
 
+        // Single embedding path
         if (in.getHeader(LangChain4jEmbeddingsHeaders.EMBEDDING) == null) {
             throw new NoSuchHeaderException(
                     "The embedding is a required header for ADD operations", 
exchange,
                     LangChain4jEmbeddingsHeaders.EMBEDDING);
         }
 
         Embedding embedding = 
in.getHeader(LangChain4jEmbeddingsHeaders.EMBEDDING, Embedding.class);
+
+        // Check for caller-supplied ID
+        String callerId = 
in.getHeader(LangChain4jEmbeddingStoreHeaders.EMBEDDING_ID, String.class);
         String id;
 
-        if (in.getHeader(LangChain4jEmbeddingsHeaders.TEXT_SEGMENT) != null) {
+        if (callerId != null) {
+            store.add(callerId, embedding);
+            id = callerId;
+        } else if (in.getHeader(LangChain4jEmbeddingsHeaders.TEXT_SEGMENT) != 
null) {
             TextSegment text = 
in.getHeader(LangChain4jEmbeddingsHeaders.TEXT_SEGMENT, TextSegment.class);
-            id = 
getEndpoint().getConfiguration().getEmbeddingStore().add(embedding, text);
+            id = store.add(embedding, text);
         } else {
-            id = 
getEndpoint().getConfiguration().getEmbeddingStore().add(embedding);
+            id = store.add(embedding);
         }
 
-        Message out = exchange.getMessage();
-        out.setBody(id);
+        in.setBody(id);
+    }
+
+    @SuppressWarnings("unchecked")
+    private void addBatch(Message in, EmbeddingStore<TextSegment> store, 
List<Embedding> embeddings) {
+        List<String> callerIds = 
in.getHeader(LangChain4jEmbeddingStoreHeaders.EMBEDDING_IDS, List.class);
+        Object body = in.getBody();
+        List<TextSegment> textSegments = null;
+
+        if (body instanceof List && !((List<?>) body).isEmpty() && ((List<?>) 
body).get(0) instanceof TextSegment) {
+            textSegments = (List<TextSegment>) body;
+        }
+
+        List<String> ids;
+        if (callerIds != null && textSegments != null) {
+            store.addAll(callerIds, embeddings, textSegments);
+            ids = callerIds;
+        } else if (textSegments != null) {
+            ids = store.addAll(embeddings, textSegments);
+        } else {
+            ids = store.addAll(embeddings);
+        }
+
+        in.setBody(ids);
     }
 
     /**
-     * Removes an embedding from the store by its ID.
+     * Removes embeddings from the store. Supports multiple removal strategies:
      *
-     * <p>
-     * Expects the embedding ID as the message body (String).
-     * </p>
+     * <ul>
+     * <li><b>By filter</b>: when the {@code 
CamelLangchain4jEmbeddingStoreFilter} header is set, removes all embeddings
+     * matching the filter via {@code removeAll(Filter)}</li>
+     * <li><b>By ID list</b>: when the body is a {@code Collection<String>}, 
removes all specified embeddings via
+     * {@code removeAll(Collection)}</li>
+     * <li><b>By single ID</b>: when the body is a single {@code String}, 
removes that embedding via
+     * {@code remove(id)}</li>
+     * <li><b>Clear all</b>: when the body is null/empty and no filter is set, 
removes all embeddings from the store via

Review Comment:
   **Bugbot:** Confirmed fixed in latest commits — `remove()` now throws 
`IllegalArgumentException` when body is null/empty and no filter is set 
(`removeWithNoBodyOrFilterThrows` test). No longer calls `removeAll()` on the 
whole store.
   
   _AI-generated Bugbot reply on behalf of atiaomar1978-hub._



##########
components/camel-ai/camel-langchain4j-embeddingstore/src/main/java/org/apache/camel/component/langchain4j/embeddingstore/LangChain4jEmbeddingStoreProducer.java:
##########
@@ -109,63 +110,145 @@ public void process(Exchange exchange) throws Exception {
     }
 
     /**
-     * Adds an embedding to the store with optional text segment.
+     * Adds embeddings to the store with optional text segments and 
caller-supplied IDs.
      *
      * <p>
-     * Expects the following headers:
+     * Supports both single and batch operations:
+     * </p>
+     *
+     * <p>
+     * <b>Single operation</b> - when the {@code 
CamelLangChain4jEmbeddingsEmbedding} header contains a single
+     * {@link Embedding}:
      * </p>
      * <ul>
-     * <li>{@code CamelLangchain4jEmbeddingEmbedding} - The embedding vector 
(required)</li>
-     * <li>{@code CamelLangchain4jEmbeddingTextSegment} - Associated text 
segment (optional)</li>
+     * <li>With caller-supplied ID header ({@code 
CamelLangchain4jEmbeddingStoreEmbeddingId}): calls
+     * {@code add(id, embedding)}</li>
+     * <li>With text segment header: calls {@code add(embedding, 
textSegment)}</li>
+     * <li>Without text segment: calls {@code add(embedding)}</li>
      * </ul>
      *
      * <p>
-     * Returns the generated embedding ID in the message body.
+     * <b>Batch operation</b> - when the {@code 
CamelLangChain4jEmbeddingsEmbeddings} header contains a
+     * {@code List<Embedding>}:
      * </p>
+     * <ul>
+     * <li>With IDs header ({@code 
CamelLangchain4jEmbeddingStoreEmbeddingIds}) and text segments body: calls
+     * {@code addAll(ids, embeddings, textSegments)}</li>
+     * <li>With text segments body: calls {@code addAll(embeddings, 
textSegments)}</li>
+     * <li>Without text segments: calls {@code addAll(embeddings)}</li>
+     * </ul>
      *
      * @param  exchange  the Camel exchange containing the embedding data
      * @throws Exception if the add operation fails
      */
+    @SuppressWarnings("unchecked")
     private void add(Exchange exchange) throws Exception {
         final Message in = exchange.getMessage();
+        EmbeddingStore<TextSegment> store = 
getEndpoint().getConfiguration().getEmbeddingStore();
+
+        // Check for batch embeddings header first
+        List<Embedding> embeddings = 
in.getHeader(LangChain4jEmbeddingsHeaders.EMBEDDINGS, List.class);
+        if (embeddings != null) {
+            addBatch(in, store, embeddings);
+            return;
+        }
 
+        // Single embedding path
         if (in.getHeader(LangChain4jEmbeddingsHeaders.EMBEDDING) == null) {
             throw new NoSuchHeaderException(
                     "The embedding is a required header for ADD operations", 
exchange,
                     LangChain4jEmbeddingsHeaders.EMBEDDING);
         }
 
         Embedding embedding = 
in.getHeader(LangChain4jEmbeddingsHeaders.EMBEDDING, Embedding.class);
+
+        // Check for caller-supplied ID
+        String callerId = 
in.getHeader(LangChain4jEmbeddingStoreHeaders.EMBEDDING_ID, String.class);
         String id;
 
-        if (in.getHeader(LangChain4jEmbeddingsHeaders.TEXT_SEGMENT) != null) {
+        if (callerId != null) {
+            store.add(callerId, embedding);
+            id = callerId;
+        } else if (in.getHeader(LangChain4jEmbeddingsHeaders.TEXT_SEGMENT) != 
null) {
             TextSegment text = 
in.getHeader(LangChain4jEmbeddingsHeaders.TEXT_SEGMENT, TextSegment.class);

Review Comment:
   **Bugbot:** Confirmed fixed — when `EMBEDDING_IDS` is set without text 
segments, code loops `store.add(id, embedding)` per pair 
(`addBatchWithCallerIdsNoTextSegments` test). Caller IDs are preserved.
   
   _AI-generated Bugbot reply on behalf of atiaomar1978-hub._



##########
components/camel-ai/camel-langchain4j-embeddings/src/main/java/org/apache/camel/component/langchain4j/embeddings/LangChain4jEmbeddingsProducer.java:
##########
@@ -55,4 +68,30 @@ public void process(Exchange exchange) throws Exception {
         message.setHeader(LangChain4jEmbeddingsHeaders.TEXT_SEGMENT, in);
         message.setHeader(LangChain4jEmbeddingsHeaders.EMBEDDING, 
result.content());
     }
+
+    private void processBatch(Exchange exchange, EmbeddingModel model, Message 
message, List<Object> bodyList)
+            throws Exception {
+        // Convert each element to TextSegment using the type converter

Review Comment:
   **Grok:** Confirmed fixed — uses `import java.util.ArrayList` and `new 
ArrayList<>(bodyList.size())`.
   
   _AI-generated Grok reply on behalf of atiaomar1978-hub._



##########
components/camel-ai/camel-langchain4j-embeddings/src/main/java/org/apache/camel/component/langchain4j/embeddings/LangChain4jEmbeddingsHeaders.java:
##########
@@ -35,6 +35,10 @@ public class LangChain4jEmbeddingsHeaders {
     @Metadata(description = "Embedding representation of a text", javaType = 
"dev.langchain4j.data.embedding.Embedding")
     public static final String EMBEDDING = 
CamelLangchain4jAttributes.CAMEL_LANGCHAIN4J_EMBEDDING;
 
+    @Metadata(description = "List of embeddings from a batch embedAll 
operation",

Review Comment:
   **Grok:** Confirmed fixed — `CAMEL_LANGCHAIN4J_EMBEDDINGS` added to 
`CamelLangchain4jAttributes` with `@since 4.22`; 
`LangChain4jEmbeddingsHeaders.EMBEDDINGS` references it.
   
   _AI-generated Grok reply on behalf of atiaomar1978-hub._



##########
components/camel-ai/camel-langchain4j-embeddingstore/src/main/java/org/apache/camel/component/langchain4j/embeddingstore/LangChain4jEmbeddingStoreProducer.java:
##########
@@ -109,63 +110,150 @@ public void process(Exchange exchange) throws Exception {
     }
 
     /**
-     * Adds an embedding to the store with optional text segment.
+     * Adds embeddings to the store with optional text segments and 
caller-supplied IDs.
      *
      * <p>
-     * Expects the following headers:
+     * Supports both single and batch operations:
+     * </p>
+     *
+     * <p>
+     * <b>Single operation</b> - when the {@code 
CamelLangChain4jEmbeddingsEmbedding} header contains a single
+     * {@link Embedding}:
      * </p>
      * <ul>
-     * <li>{@code CamelLangchain4jEmbeddingEmbedding} - The embedding vector 
(required)</li>
-     * <li>{@code CamelLangchain4jEmbeddingTextSegment} - Associated text 
segment (optional)</li>
+     * <li>With caller-supplied ID header ({@code 
CamelLangchain4jEmbeddingStoreEmbeddingId}): calls
+     * {@code add(id, embedding)}</li>
+     * <li>With text segment header: calls {@code add(embedding, 
textSegment)}</li>
+     * <li>Without text segment: calls {@code add(embedding)}</li>
      * </ul>
      *
      * <p>
-     * Returns the generated embedding ID in the message body.
+     * <b>Batch operation</b> - when the {@code 
CamelLangChain4jEmbeddingsEmbeddings} header contains a
+     * {@code List<Embedding>}:
      * </p>
+     * <ul>
+     * <li>With IDs header ({@code 
CamelLangchain4jEmbeddingStoreEmbeddingIds}) and text segments body: calls
+     * {@code addAll(ids, embeddings, textSegments)}</li>
+     * <li>With text segments body: calls {@code addAll(embeddings, 
textSegments)}</li>
+     * <li>Without text segments: calls {@code addAll(embeddings)}</li>
+     * </ul>
      *
      * @param  exchange  the Camel exchange containing the embedding data
      * @throws Exception if the add operation fails
      */
+    @SuppressWarnings("unchecked")
     private void add(Exchange exchange) throws Exception {
         final Message in = exchange.getMessage();
+        EmbeddingStore<TextSegment> store = 
getEndpoint().getConfiguration().getEmbeddingStore();
+
+        // Check for batch embeddings header first
+        List<Embedding> embeddings = 
in.getHeader(LangChain4jEmbeddingsHeaders.EMBEDDINGS, List.class);
+        if (embeddings != null) {
+            addBatch(in, store, embeddings);
+            return;
+        }
 
+        // Single embedding path
         if (in.getHeader(LangChain4jEmbeddingsHeaders.EMBEDDING) == null) {
             throw new NoSuchHeaderException(
                     "The embedding is a required header for ADD operations", 
exchange,
                     LangChain4jEmbeddingsHeaders.EMBEDDING);
         }
 
         Embedding embedding = 
in.getHeader(LangChain4jEmbeddingsHeaders.EMBEDDING, Embedding.class);
+
+        // Check for caller-supplied ID
+        String callerId = 
in.getHeader(LangChain4jEmbeddingStoreHeaders.EMBEDDING_ID, String.class);
         String id;
 
-        if (in.getHeader(LangChain4jEmbeddingsHeaders.TEXT_SEGMENT) != null) {
+        if (callerId != null) {
+            store.add(callerId, embedding);
+            id = callerId;
+        } else if (in.getHeader(LangChain4jEmbeddingsHeaders.TEXT_SEGMENT) != 
null) {
             TextSegment text = 
in.getHeader(LangChain4jEmbeddingsHeaders.TEXT_SEGMENT, TextSegment.class);
-            id = 
getEndpoint().getConfiguration().getEmbeddingStore().add(embedding, text);
+            id = store.add(embedding, text);
         } else {
-            id = 
getEndpoint().getConfiguration().getEmbeddingStore().add(embedding);
+            id = store.add(embedding);
         }
 
-        Message out = exchange.getMessage();
-        out.setBody(id);
+        in.setBody(id);
+    }
+
+    @SuppressWarnings("unchecked")
+    private void addBatch(Message in, EmbeddingStore<TextSegment> store, 
List<Embedding> embeddings) {
+        List<String> callerIds = 
in.getHeader(LangChain4jEmbeddingStoreHeaders.EMBEDDING_IDS, List.class);
+        Object body = in.getBody();
+        List<TextSegment> textSegments = null;
+
+        if (body instanceof List && !((List<?>) body).isEmpty() && ((List<?>) 
body).get(0) instanceof TextSegment) {
+            textSegments = (List<TextSegment>) body;
+        }
+
+        List<String> ids;
+        if (callerIds != null && textSegments != null) {
+            store.addAll(callerIds, embeddings, textSegments);
+            ids = callerIds;
+        } else if (callerIds != null) {
+            // No addAll(ids, embeddings) overload in langchain4j, so loop 
with add(id, embedding)
+            for (int i = 0; i < embeddings.size(); i++) {

Review Comment:
   **Bugbot:** Still open in current HEAD — no validation that 
`callerIds.size() == embeddings.size()` (or textSegments) before `addAll(...)` 
/ loop. Mismatch can throw `IndexOutOfBoundsException` or store partial data. 
Recommend explicit `IllegalArgumentException` + test.
   
   _AI-generated Bugbot reply on behalf of atiaomar1978-hub._



##########
components/camel-ai/camel-langchain4j-embeddings/src/main/java/org/apache/camel/component/langchain4j/embeddings/LangChain4jEmbeddingsProducer.java:
##########
@@ -55,4 +69,30 @@ public void process(Exchange exchange) throws Exception {
         message.setHeader(LangChain4jEmbeddingsHeaders.TEXT_SEGMENT, in);
         message.setHeader(LangChain4jEmbeddingsHeaders.EMBEDDING, 
result.content());
     }
+
+    private void processBatch(Exchange exchange, EmbeddingModel model, Message 
message, List<Object> bodyList)
+            throws Exception {
+        // Convert each element to TextSegment using the type converter
+        List<TextSegment> segments = new ArrayList<>(bodyList.size());
+        for (Object item : bodyList) {
+            TextSegment segment = 
exchange.getContext().getTypeConverter().mandatoryConvertTo(TextSegment.class, 
item);
+            segments.add(segment);
+        }
+
+        final Response<List<Embedding>> result = model.embedAll(segments);
+
+        if (result.finishReason() != null) {
+            message.setHeader(LangChain4jEmbeddingsHeaders.FINISH_REASON, 
result.finishReason());
+        }
+
+        if (result.tokenUsage() != null) {
+            message.setHeader(LangChain4jEmbeddingsHeaders.INPUT_TOKEN_COUNT, 
result.tokenUsage().inputTokenCount());
+            message.setHeader(LangChain4jEmbeddingsHeaders.OUTPUT_TOKEN_COUNT, 
result.tokenUsage().outputTokenCount());
+            message.setHeader(LangChain4jEmbeddingsHeaders.TOTAL_TOKEN_COUNT, 
result.tokenUsage().totalTokenCount());
+        }
+
+        List<Embedding> embeddings = result.content();
+        message.setHeader(LangChain4jEmbeddingsHeaders.EMBEDDINGS, embeddings);
+        message.setBody(embeddings);

Review Comment:
   **Grok:** Still open — batch `processBatch` sets body to `List<Embedding>` 
and does not propagate source `TextSegment`s (no `TEXT_SEGMENT` header list). 
Docs show embed→store pipeline but route must manually keep segments in body 
for store ADD. Consider documenting explicitly or setting a `List<TextSegment>` 
header.
   
   _AI-generated Grok reply on behalf of atiaomar1978-hub._



##########
components/camel-ai/camel-langchain4j-embeddingstore/src/main/java/org/apache/camel/component/langchain4j/embeddingstore/LangChain4jEmbeddingStoreProducer.java:
##########
@@ -109,63 +110,150 @@ public void process(Exchange exchange) throws Exception {
     }
 
     /**
-     * Adds an embedding to the store with optional text segment.
+     * Adds embeddings to the store with optional text segments and 
caller-supplied IDs.
      *
      * <p>
-     * Expects the following headers:
+     * Supports both single and batch operations:
+     * </p>
+     *
+     * <p>
+     * <b>Single operation</b> - when the {@code 
CamelLangChain4jEmbeddingsEmbedding} header contains a single
+     * {@link Embedding}:
      * </p>
      * <ul>
-     * <li>{@code CamelLangchain4jEmbeddingEmbedding} - The embedding vector 
(required)</li>
-     * <li>{@code CamelLangchain4jEmbeddingTextSegment} - Associated text 
segment (optional)</li>
+     * <li>With caller-supplied ID header ({@code 
CamelLangchain4jEmbeddingStoreEmbeddingId}): calls
+     * {@code add(id, embedding)}</li>
+     * <li>With text segment header: calls {@code add(embedding, 
textSegment)}</li>
+     * <li>Without text segment: calls {@code add(embedding)}</li>
      * </ul>
      *
      * <p>
-     * Returns the generated embedding ID in the message body.
+     * <b>Batch operation</b> - when the {@code 
CamelLangChain4jEmbeddingsEmbeddings} header contains a
+     * {@code List<Embedding>}:
      * </p>
+     * <ul>
+     * <li>With IDs header ({@code 
CamelLangchain4jEmbeddingStoreEmbeddingIds}) and text segments body: calls
+     * {@code addAll(ids, embeddings, textSegments)}</li>
+     * <li>With text segments body: calls {@code addAll(embeddings, 
textSegments)}</li>
+     * <li>Without text segments: calls {@code addAll(embeddings)}</li>
+     * </ul>
      *
      * @param  exchange  the Camel exchange containing the embedding data
      * @throws Exception if the add operation fails
      */
+    @SuppressWarnings("unchecked")
     private void add(Exchange exchange) throws Exception {
         final Message in = exchange.getMessage();
+        EmbeddingStore<TextSegment> store = 
getEndpoint().getConfiguration().getEmbeddingStore();
+
+        // Check for batch embeddings header first
+        List<Embedding> embeddings = 
in.getHeader(LangChain4jEmbeddingsHeaders.EMBEDDINGS, List.class);
+        if (embeddings != null) {
+            addBatch(in, store, embeddings);
+            return;
+        }
 
+        // Single embedding path
         if (in.getHeader(LangChain4jEmbeddingsHeaders.EMBEDDING) == null) {
             throw new NoSuchHeaderException(
                     "The embedding is a required header for ADD operations", 
exchange,
                     LangChain4jEmbeddingsHeaders.EMBEDDING);
         }
 
         Embedding embedding = 
in.getHeader(LangChain4jEmbeddingsHeaders.EMBEDDING, Embedding.class);
+
+        // Check for caller-supplied ID
+        String callerId = 
in.getHeader(LangChain4jEmbeddingStoreHeaders.EMBEDDING_ID, String.class);
         String id;
 
-        if (in.getHeader(LangChain4jEmbeddingsHeaders.TEXT_SEGMENT) != null) {
+        if (callerId != null) {
+            store.add(callerId, embedding);
+            id = callerId;
+        } else if (in.getHeader(LangChain4jEmbeddingsHeaders.TEXT_SEGMENT) != 
null) {
             TextSegment text = 
in.getHeader(LangChain4jEmbeddingsHeaders.TEXT_SEGMENT, TextSegment.class);
-            id = 
getEndpoint().getConfiguration().getEmbeddingStore().add(embedding, text);
+            id = store.add(embedding, text);
         } else {
-            id = 
getEndpoint().getConfiguration().getEmbeddingStore().add(embedding);
+            id = store.add(embedding);
         }
 
-        Message out = exchange.getMessage();
-        out.setBody(id);
+        in.setBody(id);
+    }
+
+    @SuppressWarnings("unchecked")
+    private void addBatch(Message in, EmbeddingStore<TextSegment> store, 
List<Embedding> embeddings) {
+        List<String> callerIds = 
in.getHeader(LangChain4jEmbeddingStoreHeaders.EMBEDDING_IDS, List.class);
+        Object body = in.getBody();
+        List<TextSegment> textSegments = null;
+
+        if (body instanceof List && !((List<?>) body).isEmpty() && ((List<?>) 
body).get(0) instanceof TextSegment) {
+            textSegments = (List<TextSegment>) body;
+        }
+
+        List<String> ids;
+        if (callerIds != null && textSegments != null) {
+            store.addAll(callerIds, embeddings, textSegments);
+            ids = callerIds;
+        } else if (callerIds != null) {
+            // No addAll(ids, embeddings) overload in langchain4j, so loop 
with add(id, embedding)
+            for (int i = 0; i < embeddings.size(); i++) {
+                store.add(callerIds.get(i), embeddings.get(i));
+            }
+            ids = callerIds;
+        } else if (textSegments != null) {
+            ids = store.addAll(embeddings, textSegments);
+        } else {
+            ids = store.addAll(embeddings);
+        }
+
+        in.setBody(ids);
     }
 
     /**
-     * Removes an embedding from the store by its ID.
+     * Removes embeddings from the store. Supports multiple removal strategies:
      *
-     * <p>
-     * Expects the embedding ID as the message body (String).
-     * </p>
+     * <ul>
+     * <li><b>By filter</b>: when the {@code 
CamelLangchain4jEmbeddingStoreFilter} header is set, removes all embeddings
+     * matching the filter via {@code removeAll(Filter)}</li>
+     * <li><b>By ID list</b>: when the body is a {@code Collection<String>}, 
removes all specified embeddings via
+     * {@code removeAll(Collection)}</li>
+     * <li><b>By single ID</b>: when the body is a single {@code String}, 
removes that embedding via
+     * {@code remove(id)}</li>
+     * </ul>
      *
-     * @param  exchange  the Camel exchange containing the embedding ID to 
remove
+     * @param  exchange  the Camel exchange containing removal parameters
      * @throws Exception if the remove operation fails
      */
+    @SuppressWarnings("unchecked")
     private void remove(Exchange exchange) throws Exception {
         final Message in = exchange.getMessage();
-        String id = in.getBody(String.class);
+        EmbeddingStore<TextSegment> store = 
getEndpoint().getConfiguration().getEmbeddingStore();
 
-        getEndpoint().getConfiguration().getEmbeddingStore().remove(id);
+        // Check for filter-based removal first
+        Filter filter = in.getHeader(LangChain4jEmbeddingStoreHeaders.FILTER, 
Filter.class);
+        if (filter != null) {
+            store.removeAll(filter);
+            return;
+        }
 
-        Message out = exchange.getMessage();
+        Object body = in.getBody();
+
+        // Batch removal by collection of IDs
+        if (body instanceof Collection) {
+            Collection<String> ids = (Collection<String>) body;
+            store.removeAll(ids);
+            return;
+        }
+
+        // Single ID removal
+        String id = in.getBody(String.class);
+        if (id != null && !id.isEmpty()) {
+            store.remove(id);
+            return;
+        }
+
+        throw new IllegalArgumentException(

Review Comment:
   **Bugbot:** Still open — no entry in `camel-4x-upgrade-guide-4_22.adoc` for 
REMOVE null-body behavior change (previously langchain4j `remove(null)` threw; 
now Camel throws `IllegalArgumentException` with clearer message). Required per 
project rules for behavior changes.
   
   _AI-generated Bugbot reply on behalf of atiaomar1978-hub._



##########
components/camel-ai/camel-langchain4j-embeddingstore/src/main/java/org/apache/camel/component/langchain4j/embeddingstore/LangChain4jEmbeddingStoreProducer.java:
##########
@@ -109,63 +110,150 @@ public void process(Exchange exchange) throws Exception {
     }
 
     /**
-     * Adds an embedding to the store with optional text segment.
+     * Adds embeddings to the store with optional text segments and 
caller-supplied IDs.
      *
      * <p>
-     * Expects the following headers:
+     * Supports both single and batch operations:
+     * </p>
+     *
+     * <p>
+     * <b>Single operation</b> - when the {@code 
CamelLangChain4jEmbeddingsEmbedding} header contains a single
+     * {@link Embedding}:
      * </p>
      * <ul>
-     * <li>{@code CamelLangchain4jEmbeddingEmbedding} - The embedding vector 
(required)</li>
-     * <li>{@code CamelLangchain4jEmbeddingTextSegment} - Associated text 
segment (optional)</li>
+     * <li>With caller-supplied ID header ({@code 
CamelLangchain4jEmbeddingStoreEmbeddingId}): calls
+     * {@code add(id, embedding)}</li>
+     * <li>With text segment header: calls {@code add(embedding, 
textSegment)}</li>
+     * <li>Without text segment: calls {@code add(embedding)}</li>
      * </ul>
      *
      * <p>
-     * Returns the generated embedding ID in the message body.
+     * <b>Batch operation</b> - when the {@code 
CamelLangChain4jEmbeddingsEmbeddings} header contains a
+     * {@code List<Embedding>}:
      * </p>
+     * <ul>
+     * <li>With IDs header ({@code 
CamelLangchain4jEmbeddingStoreEmbeddingIds}) and text segments body: calls
+     * {@code addAll(ids, embeddings, textSegments)}</li>
+     * <li>With text segments body: calls {@code addAll(embeddings, 
textSegments)}</li>
+     * <li>Without text segments: calls {@code addAll(embeddings)}</li>
+     * </ul>
      *
      * @param  exchange  the Camel exchange containing the embedding data
      * @throws Exception if the add operation fails
      */
+    @SuppressWarnings("unchecked")
     private void add(Exchange exchange) throws Exception {
         final Message in = exchange.getMessage();
+        EmbeddingStore<TextSegment> store = 
getEndpoint().getConfiguration().getEmbeddingStore();
+
+        // Check for batch embeddings header first
+        List<Embedding> embeddings = 
in.getHeader(LangChain4jEmbeddingsHeaders.EMBEDDINGS, List.class);
+        if (embeddings != null) {
+            addBatch(in, store, embeddings);
+            return;
+        }
 
+        // Single embedding path
         if (in.getHeader(LangChain4jEmbeddingsHeaders.EMBEDDING) == null) {
             throw new NoSuchHeaderException(
                     "The embedding is a required header for ADD operations", 
exchange,
                     LangChain4jEmbeddingsHeaders.EMBEDDING);
         }
 
         Embedding embedding = 
in.getHeader(LangChain4jEmbeddingsHeaders.EMBEDDING, Embedding.class);
+
+        // Check for caller-supplied ID
+        String callerId = 
in.getHeader(LangChain4jEmbeddingStoreHeaders.EMBEDDING_ID, String.class);
         String id;
 
-        if (in.getHeader(LangChain4jEmbeddingsHeaders.TEXT_SEGMENT) != null) {
+        if (callerId != null) {
+            store.add(callerId, embedding);

Review Comment:
   **Grok:** Acknowledged design trade-off — `EMBEDDING_ID` branch runs before 
`TEXT_SEGMENT`, so metadata text is dropped on single ADD. Document in 
component adoc or reject when both headers are set.
   
   _AI-generated Grok reply on behalf of atiaomar1978-hub._



##########
components/camel-ai/camel-langchain4j-embeddings/src/main/java/org/apache/camel/component/langchain4j/embeddings/LangChain4jEmbeddingsProducer.java:
##########
@@ -35,11 +38,22 @@ public LangChain4jEmbeddingsEndpoint getEndpoint() {
     }
 
     @Override
+    @SuppressWarnings("unchecked")
     public void process(Exchange exchange) throws Exception {
-        final TextSegment in = 
exchange.getMessage().getMandatoryBody(TextSegment.class);
         final EmbeddingModel model = 
getEndpoint().getConfiguration().getEmbeddingModel();
-        final Response<Embedding> result = model.embed(in);
         final Message message = exchange.getMessage();
+        Object body = message.getBody();
+
+        if (body instanceof List) {

Review Comment:
   **Bugbot:** Batch mode triggers on any `List` body — including empty lists 
or lists whose elements are not strings/segments (e.g. accidental 
`List<Embedding>`). Consider checking element type or requiring 
`List<String>`/`List<TextSegment>` explicitly with a clear error.
   
   _AI-generated Bugbot inline comment on behalf of atiaomar1978-hub._



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to