Files
AI-Studio/app/Tests/Tools/Databases/IndexStoreChunkLocationTests.cs
T

82 lines
4.7 KiB
C#

using AIStudio.Tools.Databases;
using AIStudio.Tools.Databases.IndexStore;
using AIStudio.Tools.Services.Indexing;
using Microsoft.EntityFrameworkCore;
namespace AIStudio.Tests.Tools.Databases;
/// <summary>
/// Checks that the index keeps where each chunk is: its sheet and rows, its lines, and the attachment of a mail it is from.
/// </summary>
/// <remarks>
/// The vector store keeps only the pages of a chunk, so the index store is the one place these are
/// kept, and every passage the user is shown takes them from there.
/// </remarks>
[TestFixture]
public sealed class IndexStoreChunkLocationTests
{
private const string DATA_SOURCE_ID = "6f1d6a4e-6a5e-4c62-9a4f-0f2d2c8b7a11";
private const string PATH = "/tmp/test-data/budget.xlsx";
private static readonly CancellationToken TOKEN = CancellationToken.None;
[Test]
public async Task AChunkKeepsWhereItIs()
{
await using var store = await TemporaryIndexStore.CreateAsync();
await store.Client.UpsertDataSourceAsync(DATA_SOURCE_ID, "LOCAL_DIRECTORY", "b0a4c4d2-1f3e-4f0a-8c9d-5a6b7c8d9e01", "signature", string.Empty, 3, TOKEN);
var parentFileId = IndexedDocumentIds.CreateParentId(DATA_SOURCE_ID, PATH);
var now = DateTimeOffset.UtcNow;
await store.Client.UpsertFileAsync(DATA_SOURCE_ID, new EmbeddingStateFile(parentFileId, PATH, "budget.xlsx", "budget.xlsx", "xlsx", "fingerprint", 64, now, now, now, 3), TOKEN);
await store.Client.UpsertChunksAsync(DATA_SOURCE_ID,
[
new EmbeddingStateChunk(IndexedDocumentIds.CreateChunkId(DATA_SOURCE_ID, "fingerprint", 0), parentFileId, null, null, 0, "Rent and food.", now, SheetName: "Budget", StartRow: 3, EndRow: 7),
new EmbeddingStateChunk(IndexedDocumentIds.CreateChunkId(DATA_SOURCE_ID, "fingerprint", 1), parentFileId, 2, 3, 1, "Costs fell.", now, StartLine: 12, EndLine: 40, AttachmentPosition: 1),
new EmbeddingStateChunk(IndexedDocumentIds.CreateChunkId(DATA_SOURCE_ID, "fingerprint", 2), parentFileId, null, null, 2, "Says nothing about where it is.", now),
], TOKEN);
List<(string? SheetName, int? StartRow, int? EndRow, int? StartLine, int? EndLine, int? AttachmentPosition)> stored;
await using (var context = store.CreateContext())
stored = (await context.EmbeddingChunks.OrderBy(chunk => chunk.ChunkIndex).ToListAsync(TOKEN))
.Select(chunk => (chunk.SheetName, chunk.StartRow, chunk.EndRow, chunk.StartLine, chunk.EndLine, chunk.AttachmentPosition))
.ToList();
Assert.That(stored, Is.EqualTo(new (string?, int?, int?, int?, int?, int?)[]
{
("Budget", 3, 7, null, null, null),
(null, null, null, 12, 40, 1),
(null, null, null, null, null, null),
}), "A chunk without a location keeps none, which reads as unknown.");
}
/// <remarks>
/// The keyword search reads the location along with the chunk. The vector store keeps only the
/// pages, so its hits are completed by their chunk ids.
/// </remarks>
[Test]
public async Task BothSearchesLearnWhereAChunkIs()
{
await using var store = await TemporaryIndexStore.CreateAsync();
await store.Client.UpsertDataSourceAsync(DATA_SOURCE_ID, "LOCAL_DIRECTORY", "b0a4c4d2-1f3e-4f0a-8c9d-5a6b7c8d9e01", "signature", string.Empty, 3, TOKEN);
var parentFileId = IndexedDocumentIds.CreateParentId(DATA_SOURCE_ID, PATH);
var chunkId = IndexedDocumentIds.CreateChunkId(DATA_SOURCE_ID, "fingerprint", 0);
var now = DateTimeOffset.UtcNow;
await store.Client.UpsertFileAsync(DATA_SOURCE_ID, new EmbeddingStateFile(parentFileId, PATH, "budget.xlsx", "budget.xlsx", "xlsx", "fingerprint", 64, now, now, now, 1), TOKEN);
await store.Client.UpsertChunksAsync(DATA_SOURCE_ID, [new EmbeddingStateChunk(chunkId, parentFileId, null, null, 0, "The rent rose.", now, SheetName: "Budget", StartRow: 3, EndRow: 7)], TOKEN);
var found = (await store.Client.SearchChunksAsync(DATA_SOURCE_ID, "rent", 10, TOKEN)).Single();
var locations = await store.Client.GetChunkLocationsAsync(DATA_SOURCE_ID, [chunkId, Guid.NewGuid().ToString()], TOKEN);
var ofAnotherDataSource = await store.Client.GetChunkLocationsAsync("0e2f4c6a-8b1d-4e3f-a5c7-9d1b3f5e7a90", [chunkId], TOKEN);
var expected = new ChunkLocation(SheetName: "Budget", StartRow: 3, EndRow: 7);
Assert.Multiple(() =>
{
Assert.That(found.Location, Is.EqualTo(expected));
Assert.That(locations, Is.EquivalentTo(new Dictionary<string, ChunkLocation> { [chunkId] = expected }), "A chunk the index does not know is left out.");
Assert.That(ofAnotherDataSource, Is.Empty, "A chunk is looked up within its own data source only.");
});
}
}