Skip to content

Commit

Permalink
Merge pull request #83 from Encamina/@apicazo/add-named-memory-providers
Browse files Browse the repository at this point in the history
Add KeyedNamed services
  • Loading branch information
Hiunkeru authored Mar 7, 2024
2 parents 1acead4 + 8c154ac commit 67660f9
Show file tree
Hide file tree
Showing 4 changed files with 127 additions and 2 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ Previous classification is not required if changes are simple or all belong to t
- Changes in the prompt of `QuestionAnsweringPlugin` to enhance language detection in the response.
- Slight improvement in the `DeleteAsync` method in `CosmosRepository`.
- Some boy scouting and typo fixes in comments.
- Added new extension method to add Qdrant and Azure Search AI as Keyed Memory Store.

## [8.1.4]

Expand Down
2 changes: 1 addition & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@

<PropertyGroup>
<VersionPrefix>8.1.5</VersionPrefix>
<VersionSuffix>preview-03</VersionSuffix>
<VersionSuffix>preview-04</VersionSuffix>
</PropertyGroup>

<!--
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,4 +106,71 @@ public static IServiceCollection AddSemanticTextMemory(this IServiceCollection s
.Build();
});
}

/// <summary>
/// Adds Azure AI Search as a named vector database for a memory store (<see cref="IMemoryStore"/>) to the <see cref="IServiceCollection"/> as a singleton service.
/// </summary>
/// <remarks>
/// This extension methods requires a <see cref="AzureAISearchOptions"/> to be already configured.
/// </remarks>
/// <param name="services"> The <see cref="IServiceCollection"/> to add services to.</param>
/// <param name="memoryProviderName">The name or key for the memory provider.</param>
/// <returns>The <see cref="IServiceCollection"/> so that additional calls can be chained.</returns>
public static IServiceCollection AddAzureAISearchNamedMemoryStore(this IServiceCollection services, string memoryProviderName)
{
return services.AddKeyedSingleton<IMemoryStore>(memoryProviderName, (serviceProvider, _) =>
{
var optionsMonitor = serviceProvider.GetRequiredService<IOptionsMonitor<AzureAISearchOptions>>();

static AzureAISearchMemoryStore Builder(AzureAISearchOptions options)
{
return new AzureAISearchMemoryStore(options.Endpoint.AbsoluteUri, options.Key);
}

var debouncedBuilder = Debouncer.Debounce<AzureAISearchOptions>(options => Builder(options), 300);

var memory = Builder(optionsMonitor.CurrentValue);

optionsMonitor.OnChange(debouncedBuilder);

return memory;
});
}

/// <summary>
/// Adds Qdrant vector database as a named memory store (<see cref="IMemoryStore"/>) to the <see cref="IServiceCollection"/> as a singleton service.
/// </summary>
/// <remarks>
/// This extension methods requires a <see cref="QdrantOptions"/> to be already configured.
/// </remarks>
/// <param name="services"> The <see cref="IServiceCollection"/> to add services to.</param>
/// <param name="memoryProviderName">The name or key for the memory provider.</param>
/// <returns>The <see cref="IServiceCollection"/> so that additional calls can be chained.</returns>
public static IServiceCollection AddQdrantNamedMemoryStore(this IServiceCollection services, string memoryProviderName)
{
return services.AddKeyedSingleton<IMemoryStore>(memoryProviderName, (serviceProvider, _) =>
{
var optionsMonitor = serviceProvider.GetRequiredService<IOptionsMonitor<QdrantOptions>>();

var httpClient = new HttpClient(new HttpClientHandler()
{
CheckCertificateRevocationList = true,
}, disposeHandler: false);

static QdrantMemoryStore Builder(IServiceProvider serviceProvider, HttpClient httpClient, QdrantOptions options)
{
httpClient.ConfigureHttpClientForQdrant(options);

return new QdrantMemoryStore(httpClient, options.VectorSize, loggerFactory: serviceProvider.GetService<ILoggerFactory>());
}

var debouncedBuilder = Debouncer.Debounce<QdrantOptions>(options => Builder(serviceProvider, httpClient, options), 300);

var memory = Builder(serviceProvider, httpClient, optionsMonitor.CurrentValue);

optionsMonitor.OnChange(debouncedBuilder);

return memory;
});
}
}
59 changes: 58 additions & 1 deletion src/Encamina.Enmarcha.SemanticKernel.Connectors.Memory/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ First, [install NuGet](http://docs.nuget.org/docs/start-here/installing-nuget).

## How to use

First, you need to add the [QdrantOptions](../Encamina.Enmarcha.Data.Qdrant.Abstractions/QdrantOptions.cs) to your project configuration. You can achieve this by using any [configuration provider](https://learn.microsoft.com/en-us/dotnet/core/extensions/configuration). The followng code is an example of how the settings would appear using the `appsettings.json` file:
First, if you want use Qdrant memory provider you need to add the [QdrantOptions](../Encamina.Enmarcha.Data.Qdrant.Abstractions/QdrantOptions.cs) to your project configuration. You can achieve this by using any [configuration provider](https://learn.microsoft.com/en-us/dotnet/core/extensions/configuration). The followng code is an example of how the settings would appear using the `appsettings.json` file:

```json
// ...
Expand All @@ -33,6 +33,21 @@ First, you need to add the [QdrantOptions](../Encamina.Enmarcha.Data.Qdrant.Abst
// ...
```

If you want to use Azure AI Search as a memory provider, you need to add the [AzureSearchOptions](../Encamina.Enmarcha.Data.AzureAISearch.Abstractions/AzureAISearchOptions.cs) to your project configuration. You can achieve this by using any [configuration provider](https://learn.microsoft.com/en-us/dotnet/core/extensions/configuration). The following code is an example of how the settings would appear using the `appsettings.json` file:

```json
// ...
"AzureSearchOptions": {
"Endpoint": "https://sample-searchendpoint/", // Endpoint
"Key": "xxxxxxxxxx" // API Key used by Azure Search AI as a form of client authentication.
},
// ...
```


```json


Next, in `Program.cs` or a similar entry point file in your project, add the following code:

```csharp
Expand All @@ -53,8 +68,23 @@ builder.Services.AddOptions<QdrantOptions>().Bind(builder.Configuration.GetSecti

// Adds Qdrant as IMemoryStore
services.AddQdrantMemoryStore();

// Or adds Azure AI Search as IMemoryStore
services.AddAzureAISearchMemoryStore();

```

If you need add both memory providers, you can use the following code:

```csharp


services.AddAzureAISearchNamedMemoryStore("AzureAISearchMemoryStore"); //AzureAISearchMemoryStore is the name of the memory store that you can use in the future to inject the IMemoryStore
services.AddQdrantNamedMemoryStore("QdrantMemoryStore"); //QdrantMemoryStore is the name of the memory store that you can use in the future to inject the IMemoryStore
```


In the previous code, it can be observed that in the first part is necessary to add certain [Qdrant configurations](../Encamina.Enmarcha.Data.Qdrant.Abstractions/QdrantOptions.cs) that are available in the [Encamina.Enmarcha.Data.Qdrant.Abstractions](../Encamina.Enmarcha.Data.Qdrant.Abstractions/README.md) nuget package. The last line of code corresponds to an extension method that will add the specified implementation of the [IMemoryStore](https://github.com/microsoft/semantic-kernel/blob/76db027273371ea81e6db66afcb1d888cc53b459/dotnet/src/SemanticKernel.Abstractions/Memory/IMemoryStore.cs#L13) interface as a singleton. With this, you have Qdrant configured as the storage to save and retrieve embeddings.

After the initial configuration, we typically configure Semantic Kernel as `Scoped`.
Expand Down Expand Up @@ -114,4 +144,31 @@ public class MyClass
.ToListAsync(); // ToListAsync method is provided by System.Linq.Async nuget https://www.nuget.org/packages/System.Linq.Async
}
}
```


If use NamedKeys, you can use the following code:

```csharp

internal class YourClassWithAzureSearch
{
private readonly Kernel kernel;

public YourClass([FromKeyedServices("Your Provider Name , fe: 'AzureAISearchMemoryStore'")]Kernel kernel)
{
this.kernel = kernel;
}
}

internal class YourClassWithQdrant
{
private readonly Kernel kernel;

public YourClass([FromKeyedServices("Your Provider Name , fe: 'QdrantMemoryStore'")]Kernel kernel)
{
this.kernel = kernel;
}
}

```

0 comments on commit 67660f9

Please sign in to comment.