using AIStudio.Tools.Databases; using AIStudio.Tools.Databases.VectorStore; namespace AIStudio.Tests.Tools; /// /// Keeps the points of one data source in memory, vectors included, for tests which index documents. /// internal sealed class MemoryVectorStore() : VectorStoreClient("Test vectors in memory", string.Empty) { public Dictionary Points { get; } = new(StringComparer.Ordinal); /// /// The points each call of RetrieveVectorsAsync asked for, in the order of the calls. /// public List> RetrieveRequests { get; } = []; /// /// Called with the points of every InsertEmbedding before they are kept, to look at the other store at that moment. /// public Func, Task>? BeforeInsert { get; set; } public override Task EnsureVectorStoreExists(string storeName, string dataSourceName, int vectorSize, CancellationToken token) => Task.FromResult(new VectorStoreEnsureResult(this.Points.Count == 0)); public override Task VectorStoreExists(string storeName, CancellationToken token) => Task.FromResult(true); public override async Task InsertEmbedding(string storeName, IReadOnlyList points, CancellationToken token) { if (this.BeforeInsert is not null) await this.BeforeInsert(points); foreach (var point in points) this.Points[point.PointId] = point; } public override Task> RetrieveVectorsAsync(string storeName, IReadOnlyList pointIds, CancellationToken token) { this.RetrieveRequests.Add(pointIds.ToList()); return Task.FromResult>(pointIds .Where(this.Points.ContainsKey) .Select(pointId => new StoredVector(pointId, this.Points[pointId].Vector)) .ToList()); } public override Task DeleteEmbeddingByFile(string storeName, string filePath, string parentFileId, IReadOnlyCollection keepPointIds, CancellationToken token) { var deletedPointIds = this.Points.Values .Where(point => (point.FilePath == filePath || point.ParentFileId == parentFileId) && !keepPointIds.Contains(point.PointId)) .Select(point => point.PointId) .ToList(); foreach (var pointId in deletedPointIds) this.Points.Remove(pointId); return Task.CompletedTask; } public override Task DeleteVectorStore(string storeName, CancellationToken token) { this.Points.Clear(); return Task.CompletedTask; } public override Task OptimizeVectorStore(string storeName, CancellationToken token) => Task.CompletedTask; public override Task RelocateEmbeddingFileAsync(string storeName, string parentFileId, string absolutePath, string relativePath, string fingerprint, CancellationToken token) { foreach (var point in this.Points.Values.Where(point => point.ParentFileId == parentFileId).ToList()) this.Points[point.PointId] = point with { AbsolutePath = absolutePath, RelativePath = relativePath, Fingerprint = fingerprint }; return Task.CompletedTask; } public override Task> SearchEmbeddingAsync(string storeName, IReadOnlyList vector, int maxMatches, IReadOnlyList hiddenChunks, CancellationToken token) => throw new NotSupportedException(); public override Task> SearchEmbeddingAsync(string storeName, IReadOnlyList vector, int maxMatches, VectorSearchFilter filter, IReadOnlyList hiddenChunks, CancellationToken token) => throw new NotSupportedException(); public override Task PrepareBackupAsync(CancellationToken token) => throw new NotSupportedException(); public override Task CopyStoreForBackupAsync(string backupId, string storeName, CancellationToken token) => throw new NotSupportedException(); public override async IAsyncEnumerable<(string Label, string Value)> GetDisplayInfo() { await Task.CompletedTask; yield break; } public override void Dispose() { } }