=== Named embedding stores from Quarkus LangChain4j

When the https://docs.quarkiverse.io/quarkus-langchain4j/dev/index.html[Quarkus LangChain4j] extensions are present, any `EmbeddingStore` CDI bean qualified with `@EmbeddingStoreName` is automatically bound into the Camel registry under its qualifier value.
This covers both stores declared through Quarkus LangChain4j named configuration (for example `quarkus.langchain4j.pgvector.products.\*`) and stores produced by your own `@Produces` methods.

Such a store can be referenced from a route by name, with no manual registry binding:

[source,java]
----
from("direct:ingest-products")
    .to("langchain4j-embeddingstore:products?embeddingStore=#products&embeddingModel=#embeddingModel");
----

A store only referenced from routes does not need to be injected anywhere in Java code; it is retained and instantiated lazily on first use. If the Camel registry already resolves a different `EmbeddingStore` under the same name (for example a `@Named` bean), that existing bean keeps winning lookups and a warning is logged at startup.

=== Retrieval augmentors for `@RegisterAiService`

With Quarkus LangChain4j present, a `RetrievalAugmentor` is produced automatically from the `@Default` `EmbeddingStore` and `EmbeddingModel` beans, so an `@RegisterAiService` interface answers from the same store a Camel route ingests into. Nothing is produced when the application declares its own `RetrievalAugmentor`.

Augmentors can also be declared explicitly, one per store:

[source,properties]
----
quarkus.camel.langchain4j.rag.augmentors.products.embedding-store-name=products
quarkus.camel.langchain4j.rag.augmentors.products.default=true
quarkus.camel.langchain4j.rag.augmentors.support.embedding-store-name=support-docs
----

Each entry produces a `@Named` `RetrievalAugmentor`. With more than one configured, exactly one must be marked `default=true`: that one serves the unqualified lookup `@RegisterAiService` performs, while the others remain selectable by name. Configuring several without marking one fails the build, because an ambiguous unqualified lookup disables RAG for every AI service without reporting anything. A single entry needs no marking.

Selecting one of the others means naming it through a supplier, since `@RegisterAiService` either takes the unqualified augmentor or a `Supplier` class:

[source,java]
----
public class SupportAugmentorSupplier implements Supplier<RetrievalAugmentor> {
    @Inject
    @Named("support")
    RetrievalAugmentor augmentor;

    @Override
    public RetrievalAugmentor get() {
        return augmentor;
    }
}

@RegisterAiService(retrievalAugmentor = SupportAugmentorSupplier.class)
public interface SupportAssistant {
    String chat(String question);
}
----

=== Filtering what retrieval may see

Metadata stored next to a segment — a tenant key, a classification — isolates nothing unless retrieval filters on it. A single `RagRetrievalFilterSupplier` bean, if present, is consulted on every retrieval performed by a produced augmentor:

[source,java]
----
@ApplicationScoped
public class TenantFilterSupplier implements RagRetrievalFilterSupplier {
    @Inject
    CurrentTenant currentTenant; // request scoped

    @Override
    public Filter filter(Query query, String augmentorName, String embeddingStoreName) {
        if (!"products".equals(embeddingStoreName)) {
            return null; // only this store carries tenant metadata
        }
        String tenant = currentTenant.name();
        if (tenant == null) {
            // fail closed: returning null here would serve every tenant's documents
            throw new IllegalStateException("No tenant in scope");
        }
        return metadataKey("tenant").isEqualTo(tenant);
    }
}
----

The one bean serves every produced augmentor, so it is told which augmentor retrieves and which store is being searched; both are `null` for the auto-produced default augmentor, which is backed by the `@Default` beans. Filtering on a metadata key a store does not carry matches nothing, so return `null` for the stores an implementation does not know about.

Returning `null` means unfiltered retrieval, as it does in LangChain4j itself, so an implementation that cannot determine the caller must throw instead. The tenant may equally come from `query.metadata().chatMemoryId()`, which is the only source that still works when retrieval does not run on the caller's thread. At most one implementation may exist, and it must not be `@Dependent`; both are build failures.

Two limits are worth knowing before treating this as an access control:

* The filter reaches only the augmentors this extension produces. Easy RAG, or an application-provided `RetrievalAugmentor`, replaces them, and the filter is then never consulted — the build logs a warning when it detects that combination.
* `EmbeddingStore` implementations are not required to support filtering. One that ignores the filter returns every match, so verify against the store actually in use.
