Skip to content

Commit

Permalink
Merge pull request #106 from Encamina/@mramos/improve-chatHistoryProv…
Browse files Browse the repository at this point in the history
…ider-2

Add possibility of be able to manage the chat history with a value different from UserId
  • Loading branch information
MarioRamosEs authored Apr 15, 2024
2 parents c9a2098 + 433aff8 commit 3bfaf26
Show file tree
Hide file tree
Showing 6 changed files with 35 additions and 27 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,12 @@ Also, any bug fix must start with the prefix �Bug fix:� followed by the desc

Previous classification is not required if changes are simple or all belong to the same category.

## [8.1.6]

### Breaking Changes

- Renamed `UserId` to `IndexerId` in `ChatMessageHistoryRecord`. This change requires consumers to update their database to match the new property name.
- In case of using Cosmos DB, `IndexerId` should be the new partition key of the collection. You can learn how to change the partition key and do the data migration [here](https://learn.microsoft.com/en-us/azure/cosmos-db/nosql/change-partition-key).

## [8.1.5]

Expand Down
4 changes: 2 additions & 2 deletions Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@
</PropertyGroup>

<PropertyGroup>
<VersionPrefix>8.1.5</VersionPrefix>
<VersionSuffix></VersionSuffix>
<VersionPrefix>8.1.6</VersionPrefix>
<VersionSuffix>preview-01</VersionSuffix>
</PropertyGroup>

<!--
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,28 +10,28 @@ public interface IChatHistoryProvider
/// <summary>
/// Deletes all chat history messages for a specific user.
/// </summary>
/// <param name="userId">The unique identifier of the user that is owner of the chat.</param>
/// <param name="indexerId">The unique identifier of the chat history indexer.</param>
/// <param name="cancellationToken">A cancellation token that can be used to receive notice of cancellation.</param>
/// <returns>A <see cref="Task"/> that on completion indicates the asynchronous operation has executed.</returns>
Task DeleteChatMessagesHistoryAsync(string userId, CancellationToken cancellationToken);
Task DeleteChatMessagesHistoryAsync(string indexerId, CancellationToken cancellationToken);

/// <summary>
/// Loads chat history messages.
/// </summary>
/// <param name="chatHistory">The current chat history.</param>
/// <param name="userId">The unique identifier of the user that is owner of the chat.</param>
/// <param name="indexerId">The unique identifier of the chat history indexer.</param>
/// <param name="remainingTokens">The total remaining tokens available for loading messages from the chat history.</param>
/// <param name="cancellationToken">A cancellation token that can be used to receive notice of cancellation.</param>
/// <returns>A <see cref="Task"/> that on completion indicates the asynchronous operation has executed.</returns>
Task LoadChatMessagesHistoryAsync(ChatHistory chatHistory, string userId, int remainingTokens, CancellationToken cancellationToken);
Task LoadChatMessagesHistoryAsync(ChatHistory chatHistory, string indexerId, int remainingTokens, CancellationToken cancellationToken);

/// <summary>
/// Saves a chat message into the conversation history.
/// </summary>
/// <param name="userId">The user's unique identifier.</param>
/// <param name="indexerId">The unique identifier of the chat history indexer.</param>
/// <param name="roleName">The name of the role associated with the chat message. For example the `user`, the `assistant` or the `system`.</param>
/// <param name="message">The message.</param>
/// <param name="cancellationToken">A cancellation token that can be used to receive notice of cancellation.</param>
/// <returns>A <see cref="Task"/> that on completion indicates the asynchronous operation has executed.</returns>
Task SaveChatMessagesHistoryAsync(string userId, string roleName, string message, CancellationToken cancellationToken);
Task SaveChatMessagesHistoryAsync(string indexerId, string roleName, string message, CancellationToken cancellationToken);
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,16 +34,16 @@ public ChatHistoryProvider(Func<string, int> tokensLengthFunction, IAsyncReposit
}

/// <inheritdoc/>
public Task DeleteChatMessagesHistoryAsync(string userId, CancellationToken cancellationToken)
public Task DeleteChatMessagesHistoryAsync(string indexerId, CancellationToken cancellationToken)
{
return chatMessagesHistoryRepository.DeleteAsync(userId, cancellationToken);
return chatMessagesHistoryRepository.DeleteAsync(indexerId, cancellationToken);
}

/// <inheritdoc/>
/// <remarks>
/// The maximum number of messages to load is configured in <c>ChatHistoryProviderOptions.HistoryMaxMessages</c>.
/// </remarks>
public async Task LoadChatMessagesHistoryAsync(ChatHistory chatHistory, string userId, int remainingTokens, CancellationToken cancellationToken)
public async Task LoadChatMessagesHistoryAsync(ChatHistory chatHistory, string indexerId, int remainingTokens, CancellationToken cancellationToken)
{
if (options.HistoryMaxMessages <= 0 || remainingTokens <= 0)
{
Expand All @@ -52,7 +52,7 @@ public async Task LoadChatMessagesHistoryAsync(ChatHistory chatHistory, string u

// Obtain the chat history for the user, ordered by timestamps descending to get the most recent messages first, and then take 'N' messages.
// This means that the first elements in the list are the most recent or newer messages, and the last elements in the list are the oldest messages.
var result = (await chatMessagesHistoryRepository.GetAllAsync(chatMessages => chatMessages.Where(chatMessage => chatMessage.UserId == userId)
var result = (await chatMessagesHistoryRepository.GetAllAsync(chatMessages => chatMessages.Where(chatMessage => chatMessage.IndexerId == indexerId)
.OrderByDescending(chatMessage => chatMessage.TimestampUtc)
.Take(options.HistoryMaxMessages), cancellationToken)).ToList();

Expand Down Expand Up @@ -102,12 +102,12 @@ public async Task LoadChatMessagesHistoryAsync(ChatHistory chatHistory, string u
}

/// <inheritdoc/>
public async Task SaveChatMessagesHistoryAsync(string userId, string roleName, string message, CancellationToken cancellationToken)
public async Task SaveChatMessagesHistoryAsync(string indexerId, string roleName, string message, CancellationToken cancellationToken)
{
await chatMessagesHistoryRepository.AddAsync(new ChatMessageHistoryRecord()
{
Id = Guid.NewGuid().ToString(),
UserId = userId,
IndexerId = indexerId,
RoleName = roleName,
Message = message,
TimestampUtc = DateTime.UtcNow,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,14 @@ public class ChatMessageHistoryRecord : IIdentifiable<string>
public virtual string Id { get; init; }

/// <summary>
/// Gets the unique identifier of the user owner of the chat.
/// Gets the unique identifier of the chat history indexer.
/// </summary>
[JsonProperty(@"userId")]
[JsonPropertyName(@"userId")]
public virtual string UserId { get; init; }
/// <remarks>
/// Identifier that relates several messages. This could be, for example, a conversationId, a userId, or any other relevant identifier.
/// </remarks>
[JsonProperty(@"indexerId")]
[JsonPropertyName(@"indexerId")]
public virtual string IndexerId { get; init; }

/// <summary>
/// Gets the name of the role associated with the chat message.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ public ChatWithHistoryPlugin(Kernel kernel, string chatModelName, Func<string, i
/// </summary>
/// <param name="cancellationToken">A cancellation token that can be used to receive notice of cancellation.</param>
/// <param name="ask">What the user says or asks when chatting.</param>
/// <param name="userId">A unique identifier for the user when chatting.</param>
/// <param name="chatIndexerId">The unique identifier of the chat history indexer.</param>
/// <param name="userName">The name of the user.</param>
/// <param name="locale">The preferred language of the user while chatting.</param>
/// <returns>A string representing the response from the Artificial Intelligence.</returns>
Expand All @@ -64,7 +64,7 @@ public ChatWithHistoryPlugin(Kernel kernel, string chatModelName, Func<string, i
public virtual async Task<string> ChatAsync(
CancellationToken cancellationToken,
[Description(@"What the user says or asks when chatting")] string ask,
[Description(@"A unique identifier for the user when chatting")] string userId,
[Description(@"The unique identifier of the chat history indexer")] string chatIndexerId,
[Description(@"The name of the user")] string userName = null,
[Description(@"The preferred language of the user while chatting")] string locale = null)
{
Expand Down Expand Up @@ -93,34 +93,34 @@ public virtual async Task<string> ChatAsync(
return await GetErrorMessageAsync(chatHistory, locale, systemPromptTokens, askTokens, chatModelMaxTokens, cancellationToken);
}

await chatHistoryProvider.LoadChatMessagesHistoryAsync(chatHistory, userId, remainingTokens, cancellationToken);
await chatHistoryProvider.LoadChatMessagesHistoryAsync(chatHistory, chatIndexerId, remainingTokens, cancellationToken);

chatHistory.AddUserMessage(ask);

var chatMessage = await kernel.GetRequiredService<IChatCompletionService>().GetChatMessageContentAsync(chatHistory, options.ChatRequestSettings, kernel, cancellationToken);
var response = chatMessage.Content;

await chatHistoryProvider.SaveChatMessagesHistoryAsync(userId, AuthorRole.User.ToString(), ask, cancellationToken); // Save in chat history the user message (a.k.a. ask).
await chatHistoryProvider.SaveChatMessagesHistoryAsync(userId, AuthorRole.Assistant.ToString(), response, cancellationToken); // Save in chat history the assistant message (a.k.a. response).
await chatHistoryProvider.SaveChatMessagesHistoryAsync(chatIndexerId, AuthorRole.User.ToString(), ask, cancellationToken); // Save in chat history the user message (a.k.a. ask).
await chatHistoryProvider.SaveChatMessagesHistoryAsync(chatIndexerId, AuthorRole.Assistant.ToString(), response, cancellationToken); // Save in chat history the assistant message (a.k.a. response).

return response;
}

/// <summary>
/// Deletes the chat message history when a user asks to forget previous conversations or to start over again.
/// </summary>
/// <param name="userId">The unique identifier of the user owner of the chat history.</param>
/// <param name="chatIndexerId">The unique identifier of the chat history indexer.</param>
/// <param name="cancellationToken">A cancellation token that can be used to cancel the operation.</param>
/// <returns>A Task representing the asynchronous operation.</returns>
[KernelFunction]
[Description(@"Deletes the chat message history when a user asks to forget previous conversations or to start all over again.")]
public async Task DeleteChatMessagesHistoryAsync(
[Description(@"A unique identifier of the user")] string userId,
[Description(@"The unique identifier of the chat history indexer")] string chatIndexerId,
CancellationToken cancellationToken)
{
ArgumentException.ThrowIfNullOrWhiteSpace(userId);
ArgumentException.ThrowIfNullOrWhiteSpace(chatIndexerId);

await chatHistoryProvider.DeleteChatMessagesHistoryAsync(userId, cancellationToken);
await chatHistoryProvider.DeleteChatMessagesHistoryAsync(chatIndexerId, cancellationToken);
}

/// <summary>
Expand Down

0 comments on commit 3bfaf26

Please sign in to comment.