ChatSessionManager 1.0.4
See the version list below for details.
dotnet add package ChatSessionManager --version 1.0.4
NuGet\Install-Package ChatSessionManager -Version 1.0.4
<PackageReference Include="ChatSessionManager" Version="1.0.4" />
paket add ChatSessionManager --version 1.0.4
#r "nuget: ChatSessionManager, 1.0.4"
// Install ChatSessionManager as a Cake Addin #addin nuget:?package=ChatSessionManager&version=1.0.4 // Install ChatSessionManager as a Cake Tool #tool nuget:?package=ChatSessionManager&version=1.0.4
ChatSessionManager
Overview
ChatHistoryManager is a robust and scalable solution designed to store and query chat history data for AI applications. This repository contains the core components necessary to seamlessly integrate chat session storage into your AI-driven applications.
Features
- Efficient Data Storage: Store chat sessions in a structured format.
- Easy Retrieval: Query and retrieve chat sessions quickly.
- Scalability: Designed to handle large volumes of chat data.
- Extensible: Easily integrate with various AI frameworks and tools.
- Secure: Ensure data integrity and security.
Installation
Clone the repository:
git clone https://github.com/muatassim/ChatSessionManager.git
Navigate to the project directory:
cd ChatSessionManager
Install dependencies:
dotnet restore
Add to Dependency Injection:
//For Adding AzureAISearch services.AddAzureAISearchChatHistory(context.Configuration);
Chat History Data Service
The IChatHistoryDataService
interface provides various methods for managing and querying chat documents in a data source. This guide explains how to use each method with examples.
Configuration
Add the following settings to your appsettings.json
file:
{
"ChatSessionManagerOptions": {
"AzureAiSearch": {
"ServiceName": "--Azure Ai Service Name--",
"ApiKey": "--API KEY--",
"SemanticSearchConfigName": "my-semantic-config",
"VectorSearchHNSWConfig": "my-hnsw-vector-config",
"VectorSearchProfile": "my-vector-profile",
"ModelDimension": "1536",
"IndexName": "INDEX NAME"
}
}
}
Usage
To integrate the AzureAISearchChatHistoryDataService
implementation of the IChatHistoryDataService
interface, follow these steps:
Semantic Kernel Chat Example
This section shows how to use the Semantic Kernel chat with history. The example demonstrates how to ask a series of questions where the context from previous answers is important.
[TestMethod]
[DataRow("I'm planning a trip to Paris. Can you tell me the best time of year to visit and some must-see attractions?",
"Given that I'm interested in art and history, what are some lesser-known museums in Paris that I should visit?")]
[DataRow("I want to adopt a healthier diet. Can you suggest some nutritious foods to incorporate into my meals?",
"Based on the foods you suggested, can you give me a simple recipe for a balanced meal?")]
[DataRow("I'm looking for a good mystery novel to read. Can you suggest one?", "Sounds interesting. What can you tell me about the main character in 'The Girl with the Dragon Tattoo'?")]
[DataRow("I want to start a workout routine to build muscle. Any tips on what exercises I should do?", "Can you suggest a weekly workout plan that includes those exercises?")]
public async Task ChatWithHistoryExampleAsync_Test(string question, string followUpQuestion)
{
IOptions<AzureOpenAIOptions> options = AppHost.GetServiceProvider().GetRequiredService<IOptions<AzureOpenAIOptions>>();
Assert.IsNotNull(options);
//var openAI = new OpenAI(options.Value.Key, options.Value.Endpoint);
AzureOpenAIOptions azureOpenAIOptions = options.Value as AzureOpenAIOptions;
IChatHistoryDataService chatHistoryDataService = AppHost.GetServiceProvider().GetKeyedService<IChatHistoryDataService>(nameof(AzureAISearchChatHistoryDataService));
Assert.IsNotNull(chatHistoryDataService);
Kernel kernel = AppHost.GetServiceProvider().GetService<Kernel>();
Assert.IsNotNull(kernel);
IChatCompletionService chatCompletionService = kernel.GetRequiredService<IChatCompletionService>();
Assert.IsNotNull(chatCompletionService);
ITextEmbeddingGenerationService textEmbeddingGenerationService = kernel.GetRequiredService<ITextEmbeddingGenerationService>();
Assert.IsNotNull(textEmbeddingGenerationService);
//Get Question 1 Vector
ReadOnlyMemory<float> questionEmbedding = await textEmbeddingGenerationService.GenerateEmbeddingAsync(question);
Assert.IsNotNull(questionEmbedding);
await AskQuestion(question, chatHistoryDataService, chatCompletionService,questionEmbedding);
//Second Question Vector
ReadOnlyMemory<float> followUpQuestionEmbedding = await textEmbeddingGenerationService.GenerateEmbeddingAsync(followUpQuestion);
Assert.IsNotNull(followUpQuestionEmbedding);
//Start next Question
await AskQuestion(followUpQuestion, chatHistoryDataService, chatCompletionService, followUpQuestionEmbedding);
//Get History Records
//Send the Question Again
}
private static async Task AskQuestion(string question,
IChatHistoryDataService chatHistoryDataService,
IChatCompletionService chatCompletionService,
ReadOnlyMemory<float> questionEmbedding )
{
///Question 1 Record
ChatHistory chatHistory = [];
chatHistory.AddSystemMessage("You are an AI assistant who answers the users questions in a thoughtfull manner and are precise with your answer.");
//Add history and usermessage
var historyContext = await chatHistoryDataService.GetChatHistoryContextAsync(question,questionEmbedding,2,userId,0.5);
if (historyContext != null)
{
chatHistory.AddMessage(AuthorRole.Assistant, historyContext.ToString());
}
chatHistory.AddUserMessage(question);
ChatMessageContent messageContent = await chatCompletionService.GetChatMessageContentAsync(question);
//Save Question 1 and Response
// await SaveChat(question, chatHistoryDataService, questionEmbedding, chatHistory, messageContent);
}
private static async Task SaveChat(string question, IChatHistoryDataService chatHistoryDataService, ReadOnlyMemory<float> questionEmbedding, ChatHistory chatHistory, ChatMessageContent messageContent)
{
ChatDocument chatDocument = new()
{
Id = Guid.NewGuid().ToString(),
UserId = userId,
Content = messageContent.Content,
IpAddress = "127.0.0.1",
SessionId = sessionId,
Timestamp = DateTime.UtcNow,
QuestionVector = questionEmbedding,
Question = question,
Role = AuthorRole.User.Label
};
chatHistory.Add(messageContent);
//Save the conversation to the UserStore
(List<LogMessage> messages, bool success) response = await chatHistoryDataService.AddDocumentAsync(chatDocument);
Assert.IsNotNull(response);
}
Interface Methods
AddDocumentAsync
Adds a document to the data source.
Method Signature:
Task<(List<LogMessage> messages, bool success)> AddDocumentAsync(ChatDocument chatDocument);
Example:
var chatDocument = new ChatDocument { UserId = "user123", Content = "Hello, world!" };
var (messages, success) = await chatHistoryDataService.AddDocumentAsync(chatDocument);
if (success) Console.WriteLine("Document added successfully");
CreateDataSourceIfNotExistAsync
Creates the DataSource
Method Signature
Task<(List<LogMessage> messages, bool success)> CreateDataSourceIfNotExistAsync();
Example:
var (messages, success) = await chatHistoryDataService.CreateDataSourceIfNotExistAsync();
if (success) Console.WriteLine("Data source created successfully");
DeleteIfDataSourceExistsAsync
Delete the DataSource if it exists
Method Signature
Task<(List<LogMessage> messages, bool success)> DeleteIfDataSourceExistsAsync();
Example:
var (messages, success) = await chatHistoryDataService.DeleteIfDataSourceExistsAsync();
if (success) Console.WriteLine("Data source deleted successfully");
DataSourceExistsAsync
Check if Data Source Exists
Method Signature
Task<bool> DataSourceExistsAsync();
Example:
var exists = await chatHistoryDataService.DataSourceExistsAsync();
Console.WriteLine($"Data source exists: {exists}");
FindAsync
Find Records by predicate
Method Signature
Task<ChatDocument> FindAsync(Expression<Func<ChatDocument, bool>> predicate);
Example:
var chatDocument = await chatHistoryDataService.FindAsync(doc => doc.UserId == "user123");
Console.WriteLine($"Found document: {chatDocument?.Content}");
Expression<Func<ChatDocument, bool>> expr = x => x.Question == question;
var document = await chatHistoryDataService.FindAsync(expr);
Console.WriteLine($"Found document: {document?.Content}");
FindAllAsync
Find all related records based on predicate
Method signature
Task<List<ChatDocument>> FindAllAsync(Expression<Func<ChatDocument, bool>> predicate);
Example:
var chatDocuments = await chatHistoryDataService.FindAllAsync(doc => doc.UserId == "user123");
foreach (var doc in chatDocuments)
{
Console.WriteLine($"Found document: {doc.Content}");
}
GetChatHistoryContextAsync
Get Chat History Context which can be added to IChatCompletion
Method Signature
Task<HistoryContext> GetChatHistoryContextAsync(Expression<Func<ChatDocument, bool>> predicate);
Example:
var historyContext = await chatHistoryDataService.GetChatHistoryContextAsync(doc => doc.UserId == "user123");
Console.WriteLine($"History context: {historyContext}");
Task<HistoryContext> GetChatHistoryContextAsync(string query, ReadOnlyMemory<float>? queryEmbeddings, int size, string userId, double rerankerScoreThreshold);
var query = "What is the capital of France?";
var queryEmbeddings = new ReadOnlyMemory<float>(/* Embedding values */);
int size = 10;
string userId = "user123";
double rerankerScoreThreshold = 3.5;
var historyContext = await chatHistoryDataService.GetChatHistoryContextAsync(query, queryEmbeddings, size, userId, rerankerScoreThreshold);
Console.WriteLine($"History context: {historyContext}");
//Add to ChatCompletion
IChatCompletionService chatCompletionService = kernel.GetRequiredService<IChatCompletionService>();
if (historyContext != null)
{
chatHistory.AddMessage(AuthorRole.Assistant, historyContext.ToString());
}
GetDocumentsByQueryAsync
Get documents by Query for the user
Method Signature
Task<List<ChatDocument>> GetDocumentsByQueryAsync(string query, ReadOnlyMemory<float>? queryEmbeddings, int size, string userId, double rerankerScoreThreshold = 3.5);
Example:
var query = "What is the capital of France?";
var queryEmbeddings = new ReadOnlyMemory<float>(/* Embedding values */);
int size = 10;
string userId = "user123";
double rerankerScoreThreshold = 3.5;
var chatDocuments = await chatHistoryDataService.GetDocumentsByQueryAsync(query, queryEmbeddings, size, userId, rerankerScoreThreshold);
foreach (var doc in chatDocuments)
{
Console.WriteLine($"Question: {doc.Question}, Answer: {doc.Content}");
}
GetDocumentsByUserIdAsync
Get documents by UserId
Method Signature
Task<List<ChatDocument>> GetDocumentsByUserIdAsync(string userId);
Example:
var chatDocuments = await chatHistoryDataService.GetDocumentsByUserIdAsync("user123");
foreach (var doc in chatDocuments)
{
Console.WriteLine($"Question: {doc.Question}, Answer: {doc.Content}");
}
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. |
-
net8.0
- Azure.Search.Documents (>= 11.6.0)
- Humanizer (>= 2.14.1)
- Microsoft.Extensions.Configuration (>= 8.0.0)
- Microsoft.Extensions.Configuration.Json (>= 8.0.1)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 8.0.2)
- Microsoft.Extensions.Logging (>= 8.0.1)
- Microsoft.Extensions.Logging.Console (>= 8.0.1)
- Microsoft.Extensions.Logging.Debug (>= 8.0.1)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
Updated nuget packages