Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
<PackageVersion Include="OllamaSharp" Version="5.1.14" />
<PackageVersion Include="PdfPig" Version="0.1.10" />
<PackageVersion Include="Polly.Core" Version="8.5.2" />
<PackageVersion Include="Qdrant.Client" Version="1.14.0" />
<PackageVersion Include="RabbitMQ.Client" Version="7.1.2" />
<PackageVersion Include="ReadLine" Version="2.0.1" />
<PackageVersion Include="Swashbuckle.AspNetCore" Version="8.1.1" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
},
"Services": {
"Qdrant": {
"Endpoint": "http://127.0.0.1:6333",
"Endpoint": "http://127.0.0.1:6334",
"APIKey": ""
},
"OpenAI": {
Expand Down
49 changes: 49 additions & 0 deletions extensions/Qdrant/Qdrant/Internals/QdrantFilter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// Copyright (c) Microsoft. All rights reserved.

using System.Collections.Generic;
using System.Linq;
using Qdrant.Client.Grpc;

namespace Microsoft.KernelMemory.MemoryDb.Qdrant.Client;

internal static class QdrantFilter
{
public static Filter? BuildFilter(IEnumerable<IEnumerable<string>?>? tagGroups)
{
if (tagGroups == null)
{
return null;
}

var list = tagGroups.ToList();
var filter = new Filter();

if (list.Count < 2)
{
var tags = list.FirstOrDefault();
if (tags == null)
{
return null;
}

filter.Must.AddRange(tags.Where(t => !string.IsNullOrEmpty(t)).Select(t => Conditions.MatchText("tags", t)));
return filter;
}

var orFilter = new Filter();
foreach (var tags in list)
{
if (tags == null)
{
continue;
}

var andFilter = new Filter();
andFilter.Must.AddRange(tags.Where(t => !string.IsNullOrEmpty(t)).Select(t => Conditions.MatchText("tags", t)));
orFilter.Should.Add(Conditions.Filter(andFilter));
}

filter.Must.Add(Conditions.Filter(orFilter));
return filter;
}
}
81 changes: 81 additions & 0 deletions extensions/Qdrant/Qdrant/Internals/QdrantPointStruct.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
// Copyright (c) Microsoft. All rights reserved.

using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using Google.Protobuf;
using Microsoft.KernelMemory.MemoryStorage;
using Qdrant.Client.Grpc;

namespace Microsoft.KernelMemory.MemoryDb.Qdrant.Client;

internal static class QdrantPointStruct
{
private const string Id = "id";
private const string Tags = "tags";
private const string Payload = "payload";

public static PointStruct FromMemoryRecord(MemoryRecord record)
{
return new PointStruct
{
Vectors = new Vectors { Vector = new Vector { Data = { record.Vector.Data.ToArray() } } },
Payload =
{
[Id] = record.Id,
[Tags] = record.Tags.Pairs.Select(tag => $"{tag.Key}{Constants.ReservedEqualsChar}{tag.Value}").ToArray(),
[Payload] = Value.Parser.ParseJson(JsonSerializer.Serialize(record.Payload, QdrantConfig.JSONOptions)),
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can implement an IDictionary<string, object> -> Struct method over this serialize/deserialize (to later serialize again) approach.

}
};
}

public static MemoryRecord ToMemoryRecord(ScoredPoint scoredPoint, bool withEmbedding = true)
{
MemoryRecord result = new()
{
Id = scoredPoint.Id.Uuid,
Payload = scoredPoint.Payload.TryGetValue(Payload, out var payload)
? JsonSerializer.Deserialize<Dictionary<string, object>>(JsonFormatter.Default.Format(payload.StructValue), QdrantConfig.JSONOptions) ?? []
: []
};

if (withEmbedding)
{
result.Vector = new Embedding(scoredPoint.Vectors.Vector.Data.ToArray());
}

foreach (string[] keyValue in scoredPoint.Payload[Tags].ListValue.Values.Select(tag => tag.StringValue.Split(Constants.ReservedEqualsChar, 2)))
{
string key = keyValue[0];
string? value = keyValue.Length == 1 ? null : keyValue[1];
result.Tags.Add(key, value);
}

return result;
}

public static MemoryRecord ToMemoryRecord(RetrievedPoint retrievedPoint, bool withEmbedding = true)
{
MemoryRecord result = new()
{
Id = retrievedPoint.Id.Uuid,
Payload = retrievedPoint.Payload.TryGetValue(Payload, out var payload)
? JsonSerializer.Deserialize<Dictionary<string, object>>(JsonFormatter.Default.Format(payload.StructValue), QdrantConfig.JSONOptions) ?? []
: []
};

if (withEmbedding)
{
result.Vector = new Embedding(retrievedPoint.Vectors.Vector.Data.ToArray());
}

foreach (string[] keyValue in retrievedPoint.Payload[Tags].ListValue.Values.Select(tag => tag.StringValue.Split(Constants.ReservedEqualsChar, 2)))
{
string key = keyValue[0];
string? value = keyValue.Length == 1 ? null : keyValue[1];
result.Tags.Add(key, value);
}

return result;
}
}
1 change: 1 addition & 0 deletions extensions/Qdrant/Qdrant/Qdrant.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

<ItemGroup>
<PackageReference Include="System.Linq.Async" />
<PackageReference Include="Qdrant.Client"/>
</ItemGroup>

<ItemGroup>
Expand Down
Loading