GH-3540: Allow user-provided embeddings in VectorStore - #3541
aniketg-21 wants to merge 3 commits into
Conversation
Signed-off-by: Aniket Gupta <70100745+aniketg-21@users.noreply.github.com>
|
had a brief look, thanks for the PR. design wise
should instead be a pair object, so that there is no ambiguity of which document has which embedding vs. two separate args |
Signed-off-by: Aniket Gupta <70100745+aniketg-21@users.noreply.github.com>
|
@markpollack thanks for your suggestion. @sobychacko its ready to be reviewed. |
|
@aniketg-21 Apologies for not getting back to this PR sooner — it wasn't in the roadmap for previous releases, but we're finally at a point where we're pulling the general themes you describe into a feature item for 2.1.x. Thanks for putting this together, and for iterating on Mark's feedback — really appreciate the effort here. The underlying need (bringing your own precomputed embeddings) is something we definitely want, and I've actually got a version of this in progress on our end as part of a broader set of vector store additions — so I'd like to bring this in through that. The tradeoff I'm weighing is backward compatibility: changing the Your direction carries straight over, though: the Since it's already in progress on our side, please hold off on any further changes here — I don't want you spending cycles on work that'd end up superseded. I'll keep this open and link back once it's up so the lineage is clear and you get credit. Thanks again — the proposal and discussion here genuinely helped shape it. 🙏 |
|
Thanks for the update and detailed context! The additive I'll hold off on further changes here as requested. |
Fixes spring-projects#6989 Bring-your-own-vector write path: an EmbeddedDocument record pairing a Document with a caller-supplied vector, and a default VectorStore.upsert that throws so stores opt in. Wired into AbstractObservationVectorStore (UPSERT observation + doUpsert), with replace-by-id implementations for pgvector, Redis, Elasticsearch, and Qdrant. Adds a shared AbstractVectorStoreUpsertTests TCK, the DocumentMetadata.CONTENT_REF key for non-text content, and reference docs. See also spring-projects#3540 and spring-projects#3541 Signed-off-by: Soby Chacko <soby.chacko@broadcom.com>
Fixes spring-projects#6989 User supplied embeddings write path: an EmbeddedDocument record pairing a Document with a caller-supplied vector, and a default VectorStore.upsert that throws so stores opt in. Wired into AbstractObservationVectorStore (UPSERT observation + doUpsert), with replace-by-id implementations for pgvector, Redis, Elasticsearch, and Qdrant. Adds a shared AbstractVectorStoreUpsertTests TCK, the DocumentMetadata.CONTENT_REF key for non-text content, and reference docs. See also spring-projects#3540 and spring-projects#3541 Signed-off-by: Soby Chacko <soby.chacko@broadcom.com>
|
As discussed above we are closing this PR. The new PR based on the new design is here: #7002 Thanks @aniketg-21 for the initial inspiration for these changes. |
Fixes spring-projects#6989 User supplied embeddings write path: an EmbeddedDocument record pairing a Document with a caller-supplied vector, and a default VectorStore.upsert that throws so stores opt in. Wired into AbstractObservationVectorStore (UPSERT observation + doUpsert), with replace-by-id implementations for pgvector, Redis, Elasticsearch, and Qdrant. Adds a shared AbstractVectorStoreUpsertTests TCK, the DocumentMetadata.CONTENT_REF key for non-text content, and reference docs. See also spring-projects#3540 and spring-projects#3541 Signed-off-by: Soby Chacko <soby.chacko@broadcom.com>
Every store computes embeddings inside add(), so there was no way to write vectors you already have, whether they came from a batch API, another team's pipeline, or a multimodal model over an image. upsert takes a document and its vector together and stores the vector as given. Writing the same id again replaces the row, so a job that uses stable ids can be re-run without creating duplicates. Stores opt in one at a time: the default implementation throws, and add() is untouched. - EmbeddedDocument pairs a Document with a float[], rejecting empty and non-finite vectors - pgvector, Redis, Elasticsearch and Qdrant implement it, each checking the whole batch against its index width before writing - Media documents are rejected, as add() rejects them, because every store needs text on the row; empty text is still allowed - upsert appears in metrics as its own operation - A shared test suite runs the same checks against all four stores, on Testcontainers and without an API key - DocumentMetadata.CONTENT_REF holds a pointer to content kept outside the store, for a row whose vector came from an image or a file too large to inline. Nothing in the framework reads it - Redis returns only the metadata fields declared when the store is built, and now warns when a document carries others Fixes spring-projects#6989 See also spring-projects#3540 and spring-projects#3541 Signed-off-by: Soby Chacko <soby.chacko@broadcom.com>
🚀 Motivation
Current VectorStore implementations (e.g., ChromaVectorStore) automatically generate embeddings from Document.content using the configured EmbeddingModel. However, more advanced use cases require greater flexibility:
🔧 Enhancement
This PR introduces support for user-supplied embeddings, allowing applications to decouple document storage from semantic representation. This separation enables more accurate and intentional embedding strategies for search and retrieval.
✅ Backward Compatibility
Fully maintained. The default pipeline continues to auto-generate embeddings when none are explicitly provided.
🧪 Testing & Limitations
This feature has been tested with SimpleVectorStore. Other implementations (e.g., Pinecone, Milvus) were not tested due to environment constraints and may require further validation or adapter adjustments.
✅ Issue Closed
Closes #3540