GH-1826 Fix EmbeddingModel's usage on Document#embedding

- Since the Document object's reference to the `embedding` is deprecated and will be removed, the VectorStore implementations require a way to store the embedding of the corresponding Document objects

     - One way to fix this is, to have the EmbeddingModel#embed to return the embeddings in the same order as that of the Documents passed to it.

       - Since both the Document and embedding collections use the List object, their iteration operation will make sure to keep them in line with the same order.

       - A fix is required to preserve the order when batching strategy is applied.
	  - Updated the Javadoc for BatchingStrategy
          - Fixed the Document List order in TokenCountBatchingStrategy

    - Refactored the vector store implementations to update this change

Resolves #GH-1826
This commit is contained in:
Ilayaperumal Gopinathan
2024-11-25 22:21:11 +00:00
committed by Christian Tzolov
parent 6cfe5e79e8
commit ebd29e0959
24 changed files with 118 additions and 94 deletions

View File

@@ -31,7 +31,9 @@ public interface BatchingStrategy {
/**
* {@link EmbeddingModel} implementations can call this method to optimize embedding
* tokens. The incoming collection of {@link Document}s are split into su-batches.
* tokens. The incoming collection of {@link Document}s are split into sub-batches. It
* is important to preserve the order of the list of {@link Document}s when batching
* as they are mapped to their corresponding embeddings by their order.
* @param documents to batch
* @return a list of sub-batches that contain {@link Document}s.
*/

View File

@@ -78,25 +78,23 @@ public interface EmbeddingModel extends Model<EmbeddingRequest, EmbeddingRespons
* @param options {@link EmbeddingOptions}.
* @param batchingStrategy {@link BatchingStrategy}.
* @return a list of float[] that represents the vectors for the incoming
* {@link Document}s.
* {@link Document}s. The returned list is expected to be in the same order of the
* {@link Document} list.
*/
default List<float[]> embed(List<Document> documents, EmbeddingOptions options, BatchingStrategy batchingStrategy) {
Assert.notNull(documents, "Documents must not be null");
List<float[]> embeddings = new ArrayList<>();
List<float[]> embeddings = new ArrayList<>(documents.size());
List<List<Document>> batch = batchingStrategy.batch(documents);
for (List<Document> subBatch : batch) {
List<String> texts = subBatch.stream().map(Document::getContent).toList();
EmbeddingRequest request = new EmbeddingRequest(texts, options);
EmbeddingResponse response = this.call(request);
for (int i = 0; i < subBatch.size(); i++) {
Document document = subBatch.get(i);
float[] output = response.getResults().get(i).getOutput();
embeddings.add(output);
document.setEmbedding(output);
embeddings.add(response.getResults().get(i).getOutput());
}
}
Assert.isTrue(embeddings.size() == documents.size(),
"Embeddings must have the same number as that of the documents");
return embeddings;
}

View File

@@ -17,7 +17,7 @@
package org.springframework.ai.embedding;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@@ -139,7 +139,9 @@ public class TokenCountBatchingStrategy implements BatchingStrategy {
List<List<Document>> batches = new ArrayList<>();
int currentSize = 0;
List<Document> currentBatch = new ArrayList<>();
Map<Document, Integer> documentTokens = new HashMap<>();
// Make sure the documentTokens' entry order is preserved by making it a
// LinkedHashMap.
Map<Document, Integer> documentTokens = new LinkedHashMap<>();
for (Document document : documents) {
int tokenCount = this.tokenCountEstimator