Skip to content

Commit

Permalink
Weaviate connector (microsoft#1219)
Browse files Browse the repository at this point in the history
Adds a WeaviateMemoryStore, which allows for memories to be stored in Weaviate.
  • Loading branch information
codebrain authored Jun 8, 2023
1 parent 75d5004 commit 5742bb1
Show file tree
Hide file tree
Showing 36 changed files with 1,689 additions and 2 deletions.
2 changes: 1 addition & 1 deletion FEATURE_MATRIX.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@
| Azure Search || 🔄 || Azure Cognitive Search under development, currently in private preview |
| Qdrant |||| |
| Pinecone |||| |
| Weaviate | ||| Currently supported on Python 3.9-3.11, 3.8 coming soon |
| Weaviate | ||| Currently supported on Python 3.9-3.11, 3.8 coming soon |
| ChromaDb |||| |
| Milvus |||| Coming soon |
| Sqlite |||| Vector optimization requires [sqlite-vss](https://github.com/asg017/sqlite-vss) |
Expand Down
9 changes: 9 additions & 0 deletions dotnet/SK-dotnet.sln
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "InternalUtilities", "Intern
src\InternalUtilities\InternalUtilities.props = src\InternalUtilities\InternalUtilities.props
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Connectors.Memory.Weaviate", "src\Connectors\Connectors.Memory.Weaviate\Connectors.Memory.Weaviate.csproj", "{6AAB0620-33A1-4A98-A63B-6560B9BA47A4}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenApiSkillsExample", "..\samples\dotnet\openapi-skills\OpenApiSkillsExample.csproj", "{4D91A3E0-C404-495B-AD4A-411C4E83CF54}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Connectors.Memory.DuckDB", "src\Connectors\Connectors.Memory.DuckDB\Connectors.Memory.DuckDB.csproj", "{50FAE231-6F24-4779-9D02-12ABBC9A49E2}"
Expand Down Expand Up @@ -290,6 +292,12 @@ Global
{136823BE-8665-4D57-87E0-EF41535539E2}.Publish|Any CPU.Build.0 = Publish|Any CPU
{136823BE-8665-4D57-87E0-EF41535539E2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{136823BE-8665-4D57-87E0-EF41535539E2}.Release|Any CPU.Build.0 = Release|Any CPU
{6AAB0620-33A1-4A98-A63B-6560B9BA47A4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6AAB0620-33A1-4A98-A63B-6560B9BA47A4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6AAB0620-33A1-4A98-A63B-6560B9BA47A4}.Publish|Any CPU.ActiveCfg = Publish|Any CPU
{6AAB0620-33A1-4A98-A63B-6560B9BA47A4}.Publish|Any CPU.Build.0 = Publish|Any CPU
{6AAB0620-33A1-4A98-A63B-6560B9BA47A4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6AAB0620-33A1-4A98-A63B-6560B9BA47A4}.Release|Any CPU.Build.0 = Release|Any CPU
{4D91A3E0-C404-495B-AD4A-411C4E83CF54}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4D91A3E0-C404-495B-AD4A-411C4E83CF54}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4D91A3E0-C404-495B-AD4A-411C4E83CF54}.Publish|Any CPU.ActiveCfg = Release|Any CPU
Expand Down Expand Up @@ -343,6 +351,7 @@ Global
{E52F805C-794A-4CA9-B684-DFF358B18820} = {9ECD1AA0-75B3-4E25-B0B5-9F0945B64974}
{136823BE-8665-4D57-87E0-EF41535539E2} = {0247C2C9-86C3-45BA-8873-28B0948EDC0C}
{4D3DAE63-41C6-4E1C-A35A-E77BDFC40675} = {831DDCA2-7D2C-4C31-80DB-6BDB3E1F7AE0}
{6AAB0620-33A1-4A98-A63B-6560B9BA47A4} = {0247C2C9-86C3-45BA-8873-28B0948EDC0C}
{4D91A3E0-C404-495B-AD4A-411C4E83CF54} = {FA3720F1-C99A-49B2-9577-A940257098BF}
{50FAE231-6F24-4779-9D02-12ABBC9A49E2} = {0247C2C9-86C3-45BA-8873-28B0948EDC0C}
EndGlobalSection
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<!-- THIS PROPERTY GROUP MUST COME FIRST -->
<AssemblyName>Microsoft.SemanticKernel.Connectors.Memory.Weaviate</AssemblyName>
<RootNamespace>$(AssemblyName)</RootNamespace>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>

<!-- IMPORT NUGET PACKAGE SHARED PROPERTIES -->
<Import Project="$(RepoRoot)/dotnet/nuget/nuget-package.props" />

<PropertyGroup>
<!-- NuGet Package Settings -->
<Title>Semantic Kernel - Weaviate Connector</Title>
<Description>Weaviate connector for Semantic Kernel skills and semantic memory</Description>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="System.Text.Json" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\SemanticKernel\SemanticKernel.csproj" />
</ItemGroup>

<ItemGroup>
<Compile Include="$(RepoRoot)/dotnet/src/InternalUtilities/Linq/AsyncEnumerable.cs"/>
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// Copyright (c) Microsoft. All rights reserved.

// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

// This was copied from https://github.com/dotnet/runtime/blob/39b9607807f29e48cae4652cd74735182b31182e/src/libraries/System.Private.CoreLib/src/System/Diagnostics/CodeAnalysis/NullableAttributes.cs
// and updated to have the scope of the attributes be internal.

#pragma warning disable IDE0130 // Namespace does not match folder structure
// ReSharper disable once CheckNamespace
namespace System.Diagnostics.CodeAnalysis;
#pragma warning restore IDE0130

#if !NETCOREAPP

/// <summary>Specifies that an output will not be null even if the corresponding type allows it.</summary>
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, Inherited = false)]
internal sealed class NotNullAttribute : Attribute
{
}

#endif
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// Copyright (c) Microsoft. All rights reserved.

using System;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;

namespace Microsoft.SemanticKernel.Connectors.Memory.Weaviate.Diagnostics;

internal static class Verify
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static void NotNull([NotNull] object? obj, string message)
{
if (obj != null) { return; }

throw new ArgumentNullException(null, message);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static void NotNullOrEmpty([NotNull] string? str, string message)
{
NotNull(str, message);
if (!string.IsNullOrWhiteSpace(str)) { return; }

throw new ArgumentOutOfRangeException(message);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static void ArgNotNullOrEmpty([NotNull] string? str, string paramName, [CallerMemberName] string? caller = default)
{
NotNull(str, paramName);
if (!string.IsNullOrWhiteSpace(str)) { return; }

throw new ArgumentException(paramName, $"Parameter {paramName} cannot be empty." + (!string.IsNullOrEmpty(caller) ? $"({caller})" : string.Empty));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
// Copyright (c) Microsoft. All rights reserved.

using System;
using Microsoft.SemanticKernel.Diagnostics;

namespace Microsoft.SemanticKernel.Connectors.Memory.Weaviate.Diagnostics;

#pragma warning disable RCS1194 // Implement exception constructors

/// <summary>
/// Exception thrown for errors related to the Weaviate connector.
/// </summary>
public class WeaviateMemoryException : SKException
{
/// <summary>
/// Initializes a new instance of the <see cref="WeaviateMemoryException"/> class with a provided error code.
/// </summary>
/// <param name="errorCode">The error code.</param>
public WeaviateMemoryException(ErrorCodes errorCode)
: this(errorCode, message: null, innerException: null)
{
}

/// <summary>
/// Initializes a new instance of the <see cref="WeaviateMemoryException"/> class with a provided error code and message.
/// </summary>
/// <param name="errorCode">The error code.</param>
/// <param name="message">The exception message.</param>
public WeaviateMemoryException(ErrorCodes errorCode, string? message)
: this(errorCode, message, innerException: null)
{
}

/// <summary>
/// Initializes a new instance of the <see cref="WeaviateMemoryException"/> class with a provided error code and inner exception.
/// </summary>
/// <param name="errorCode">The error code.</param>
/// <param name="innerException">The exception that is the cause of the current exception.</param>
public WeaviateMemoryException(ErrorCodes errorCode, Exception? innerException)
: this(errorCode, message: null, innerException)
{
}

/// <summary>
/// Initializes a new instance of the <see cref="WeaviateMemoryException"/> class with a provided error code, message, and inner exception.
/// </summary>
/// <param name="errorCode">The error code.</param>
/// <param name="message">A string that describes the error.</param>
/// <param name="innerException">The exception that is the cause of the current exception.</param>
public WeaviateMemoryException(ErrorCodes errorCode, string? message, Exception? innerException)
: base(GetDefaultMessage(errorCode, message, innerException), innerException)
{
this.ErrorCode = errorCode;
}

/// <summary>
/// Gets the error code for this exception.
/// </summary>
public ErrorCodes ErrorCode { get; }

/// <summary>Translate the error code into a default message.</summary>
private static string GetDefaultMessage(ErrorCodes errorCode, string? message, Exception? innerException)
{
if (message is not null)
{
return message;
}

string description = errorCode switch
{
ErrorCodes.FailedToUpsertVectors => "Failed to upsert vectors",
ErrorCodes.FailedToGetVectorData => "Failed to get vector data",
ErrorCodes.FailedToRemoveVectorData => "Failed to remove vector data",
ErrorCodes.CollectionNameConflict => "Naming conflict for the collection name",
ErrorCodes.FailedToCreateCollection => "Failed to create the collection",
ErrorCodes.FailedToDeleteCollection => "Failed to delete the collection",
ErrorCodes.FailedToListCollections => "Failed to list collections",
ErrorCodes.FailedToGetClass => "Failed to get class",
_ => $"Unknown error ({errorCode:G})",
};

return innerException is not null ? $"{description}: {innerException.Message}" : description;
}

/// <summary>
/// Error codes for the Weaviate connector exceptions.
/// </summary>
public enum ErrorCodes
{
/// <summary>
/// Failed to upsert the vector.
/// </summary>
FailedToUpsertVectors,

/// <summary>
/// Failed to get vector data from Weaviate.
/// </summary>
FailedToGetVectorData,

/// <summary>
/// Failed to remove vector data from Weaviate.
/// </summary>
FailedToRemoveVectorData,

/// <summary>
/// Failed to create a collection.
/// </summary>
FailedToCreateCollection,

// ReSharper disable once CommentTypo
/// <summary>
/// Naming conflict for the collection name.
/// For example a collectionName of '__this_collection' and 'this_collection' are
/// both transformed to the class name of SKthiscollection - even though
/// semantic kernel would consider them as unique collection names.
/// </summary>
CollectionNameConflict,

/// <summary>
/// Failed to delete a collection.
/// </summary>
FailedToDeleteCollection,

/// <summary>
/// Failed to list collections.
/// </summary>
FailedToListCollections,

/// <summary>
/// Failed to get a Weaviate class.
/// </summary>
FailedToGetClass
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// Copyright (c) Microsoft. All rights reserved.

using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using Microsoft.SemanticKernel.Connectors.Memory.Weaviate.Model;
using Microsoft.SemanticKernel.Memory;

namespace Microsoft.SemanticKernel.Connectors.Memory.Weaviate.Http.ApiSchema;

internal sealed class BatchRequest
{
private readonly string _class;

private BatchRequest(string @class)
{
this._class = @class;
this.Objects = new();
}

// ReSharper disable once UnusedMember.Global
public string[] Fields { get; } = { "ALL" };

// ReSharper disable once MemberCanBePrivate.Global
// ReSharper disable once CollectionNeverQueried.Global
public List<WeaviateObject> Objects { get; set; }

public static BatchRequest Create(string @class)
{
return new(@class);
}

public void Add(MemoryRecord record)
{
record.Key = ToWeaviateFriendlyId(record.Metadata.Id);

WeaviateObject weaviateObject = new()
{
Class = this._class,
Id = record.Key,
Vector = record.Embedding.Vector.ToArray(),
Properties = new()
{
{ "sk_timestamp", record.Timestamp! },
{ "sk_id", record.Metadata.Id },
{ "sk_description", record.Metadata.Description },
{ "sk_text", record.Metadata.Text },
{ "sk_additional_metadata", record.Metadata.AdditionalMetadata }
}
};

this.Objects.Add(weaviateObject);
}

private static string ToWeaviateFriendlyId(string id)
{
return $"{id.Trim().Replace(' ', '-').Replace('/', '_').Replace('\\', '_').Replace('?', '_').Replace('#', '_')}";
}

public HttpRequestMessage Build()
{
return HttpRequest.CreatePostRequest(
"batch/objects",
this);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Copyright (c) Microsoft. All rights reserved.

using System;
using System.Text.Json.Serialization;
using Microsoft.SemanticKernel.Connectors.Memory.Weaviate.Http.JsonConverter;
using Microsoft.SemanticKernel.Connectors.Memory.Weaviate.Model;

namespace Microsoft.SemanticKernel.Connectors.Memory.Weaviate.Http.ApiSchema;

// ReSharper disable once ClassNeverInstantiated.Global
#pragma warning disable CA1812 // 'BatchResponse' is an internal class that is apparently never instantiated. If so, remove the code from the assembly. If this class is intended to contain only static members, make it 'static' (Module in Visual Basic).
internal sealed class BatchResponse : WeaviateObject
#pragma warning restore CA1812 // 'BatchResponse' is an internal class that is apparently never instantiated. If so, remove the code from the assembly. If this class is intended to contain only static members, make it 'static' (Module in Visual Basic).
{
public Deprecation[]? Deprecations { get; set; }
public ObjectResponseResult? Result { get; set; }

[JsonConverter(typeof(UnixSecondsDateTimeJsonConverter))]
[JsonPropertyName("creationTimeUnix")]
public DateTime? CreationTime { get; set; }

[JsonConverter(typeof(UnixSecondsDateTimeJsonConverter))]
[JsonPropertyName("lastUpdateTimeUnix")]
public DateTime? LastUpdateTime { get; set; }
}
Loading

0 comments on commit 5742bb1

Please sign in to comment.