# feat(core): forward document metadata to embedding implementations to enable multimodal providers

**URL:** <https://forum.langchain.com/t/feat-core-forward-document-metadata-to-embedding-implementations-to-enable-multimodal-providers/3924>\
**Category:** LangChain\
**Tags:** python-help\
**Created:** [June 15, 2026, 1:26pm UTC](https://forum.langchain.com/t/feat-core-forward-document-metadata-to-embedding-implementations-to-enable-multimodal-providers/3924 "2026-06-15T13:26:28Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![VedantSonani](https://yyz1.discourse-cdn.com/flex007/user_avatar/forum.langchain.com/vedantsonani/32/3139_2.png) [@VedantSonani](https://forum.langchain.com/u/VedantSonani)\
**Post date:** [June 15, 2026, 1:26pm UTC](https://forum.langchain.com/t/feat-core-forward-document-metadata-to-embedding-implementations-to-enable-multimodal-providers/3924/1 "2026-06-15T13:26:29Z")

</div>

## Background

Several embedding providers now support multimodal inputs — text, images, audio, and video — in the same embedding space:

- **Gemini Embedding 2** (`gemini-embedding-2-preview`) — text, images, video, audio, PDFs
- **Voyage AI** (`voyage-multimodal-3`) — text and images
- **Cohere** (`embed-v4.0`) — text and images

These models are production-ready. The current LangChain architecture makes them impossible to use correctly without workarounds.

## Current Limitation

`VectorStore.add_documents()` strips documents down to strings before calling `embed_documents()`:

```python
def add_documents(self, documents: list[Document], **kwargs):
    texts = [doc.page_content for doc in documents]
    metadatas = [doc.metadata for doc in documents]
    return self.add_texts(texts, metadatas, **kwargs)

```

`add_texts()` then calls the embedding implementation with only the text:

```python
def add_texts(self, texts, metadatas=None, **kwargs):
    embeddings = self.embedding.embed_documents(texts) # metadata discarded
    ...

```

As a result, embedding implementations have no access to the original `Document` or its metadata.

## Current Workaround

To work around this, multimodal content must be serialized directly into `page_content`:

```python
Document(
    page_content=" ___IMAGE___ |A cat sitting on a sofa|gs://bucket/cat.png"
)

```

A custom embedding implementation must then parse this string to reconstruct the multimodal input — standard embedding implementations have no awareness of this format.

This works but has real costs: `page_content` becomes a transport format rather than a readable text representation. This breaks BM25 retrieval, re-ranking pipelines, and any UI that displays `page_content` directly to users.

## Proposed Change

Forward `metadatas` from `VectorStore` into `embed_documents()` as an optional keyword argument.

### 1. Embedding interface — additive, optional parameter

```python
class Embeddings(ABC):

    @abstractmethod
    def embed_documents(
        self,
        texts: List[str],
        metadatas: Optional[List[dict]] = None, # new, optional
    ) -> List[List[float]]:
        ...

    @abstractmethod
    def embed_query(
        self,
        text: str,
        metadata: Optional[dict] = None, # new, optional
    ) -> List[float]:
        ...

```

### 2. VectorStore — stop discarding metadata before embedding

```python
def add_texts(self, texts, metadatas=None, **kwargs):
    embeddings = self.embedding.embed_documents(texts, metadatas=metadatas)
    ...

```

`metadatas` is already computed in `add_documents()` — it just isn’t forwarded today.

### 3. Multimodal provider — opt in by using the metadata

```python
class GoogleGenerativeAIEmbeddings(Embeddings):

    def embed_documents(
        self,
        texts: List[str],
        metadatas: Optional[List[dict]] = None,
    ) -> List[List[float]]:
        if metadatas is None:
            return self._embed_texts(texts)

        requests = []
        for text, meta in zip(texts, metadatas or [{}] * len(texts)):
            modality = meta.get("modality", "text")
            if modality == "image":
                requests.append({
                    "text": text,
                    "image_uri": meta["image_uri"],
                })
            else:
                requests.append({"text": text})
        return self._embed_multimodal(requests)

```

With this in place, documents can be represented cleanly:

```python
Document(
    page_content="A cat sitting on a sofa",
    metadata={
        "image_uri": "gs://bucket/cat.png",
        "modality": "image",
        "source": "product_catalog.pdf",
        "page": 3,
    }
)

```

## Backward Compatibility

All existing `Embeddings` subclasses are unaffected. Because `metadatas` is an optional keyword argument with a default of `None`, any implementation that does not override the signature continues to work without modification. Implementations that do opt in must handle `metadatas=None` gracefully to remain safe when `embed_documents()` is called directly outside a `VectorStore` context.

| Scenario | Behavior |
| --- | --- |
| Existing embedding implementation (unchanged) | Ignores `metadatas` kwarg — no regression |
| Multimodal provider (e.g. Gemini, Voyage, Cohere) | Reads `metadatas` to construct multimodal requests |
| `embed_documents(texts)` called directly | `metadatas` defaults to `None` — implementations handle gracefully |
| VectorStore using `add_texts()` without metadata | Passes `metadatas=None` — safe fallback to text-only path |

## Scope

This proposal touches two things:

1. The `Embeddings` base class in `langchain-core` — add optional `metadatas` kwarg to `embed_documents` and `embed_query`
2. VectorStore implementations — forward `metadatas` into `embed_documents` instead of discarding it

No new abstractions, no new base classes, no migration required for existing integrations.

## Test Cases

- Calling `embed_documents(texts)` without `metadatas` on any existing  
implementation must return the same result as before — no regression
- Calling `embed_documents(texts, metadatas=None)` explicitly must behave  
identically to the above
- A multimodal implementation receiving `metadatas` with `"modality": "image"`  
must produce embeddings in the same vector space as text embeddings from  
the same model
- A multimodal implementation receiving `metadatas=None` must fall back to  
the text-only path without raising an error
- `embed_query(text, metadata=None)` with no metadata must return the same  
result as the current `embed_query(text)`

---

<div class="post-metadata">

**Author:** ![niilooy](https://yyz1.discourse-cdn.com/flex007/user_avatar/forum.langchain.com/niilooy/32/2886_2.png) [@niilooy](https://forum.langchain.com/u/niilooy)\
**Post date:** [June 23, 2026, 5:21am UTC](https://forum.langchain.com/t/feat-core-forward-document-metadata-to-embedding-implementations-to-enable-multimodal-providers/3924/2 "2026-06-23T05:21:01Z")

</div>

Hello @VedantSonani welcome to our community! Please feel free to create an issue [here](https://github.com/langchain-ai/langchain/issues)

---

<div class="post-metadata">

**Author:** ![VedantSonani](https://yyz1.discourse-cdn.com/flex007/user_avatar/forum.langchain.com/vedantsonani/32/3139_2.png) [@VedantSonani](https://forum.langchain.com/u/VedantSonani)\
**Post date:** [June 23, 2026, 5:59am UTC](https://forum.langchain.com/t/feat-core-forward-document-metadata-to-embedding-implementations-to-enable-multimodal-providers/3924/3 "2026-06-23T05:59:53Z")

</div>

Thanks! I have already opened an issue for this here: [#38186](https://github.com/langchain-ai/langchain/issues/38186)  
I’ve also been investigating a prototype and posted some implementation details there.

---

<div class="post-metadata">

**Author:** ![VedantSonani](https://yyz1.discourse-cdn.com/flex007/user_avatar/forum.langchain.com/vedantsonani/32/3139_2.png) [@VedantSonani](https://forum.langchain.com/u/VedantSonani)\
**Post date:** [October 10, 2026, 9:24am UTC](https://forum.langchain.com/t/feat-core-forward-document-metadata-to-embedding-implementations-to-enable-multimodal-providers/3924/4 "2026-10-10T09:24:02Z")

</div>

Hey @mdrxy , Following up on this, is this feature proposal something we could pick up or discuss for implementation in `langchain-core`?
