-
-
Notifications
You must be signed in to change notification settings - Fork 92
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Co-authored-by: Evgenii Khoroshev <[email protected]>
- Loading branch information
1 parent
f30ef1d
commit e14cdb0
Showing
11 changed files
with
236 additions
and
37 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
81 changes: 81 additions & 0 deletions
81
src/libs/Databases/LangChain.Databases.Redis/RedisChatMessageHistory.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
using System.Text.Json; | ||
using LangChain.Memory; | ||
using LangChain.Providers; | ||
using StackExchange.Redis; | ||
|
||
namespace LangChain.Databases; | ||
|
||
/// <summary> | ||
/// Chat message history stored in a Redis database. | ||
/// </summary> | ||
public class RedisChatMessageHistory : BaseChatMessageHistory | ||
{ | ||
private readonly string _sessionId; | ||
private readonly string _keyPrefix; | ||
private readonly TimeSpan? _ttl; | ||
private readonly Lazy<ConnectionMultiplexer> _multiplexer; | ||
|
||
/// <inheritdoc /> | ||
public RedisChatMessageHistory( | ||
string sessionId, | ||
string connectionString, | ||
string keyPrefix = "message_store:", | ||
TimeSpan? ttl = null) | ||
{ | ||
_sessionId = sessionId; | ||
_keyPrefix = keyPrefix; | ||
_ttl = ttl; | ||
|
||
_multiplexer = new Lazy<ConnectionMultiplexer>( | ||
() => | ||
{ | ||
var multiplexer = ConnectionMultiplexer.Connect(connectionString); | ||
|
||
return multiplexer; | ||
}, | ||
LazyThreadSafetyMode.ExecutionAndPublication); | ||
} | ||
|
||
/// <summary> | ||
/// Construct the record key to use | ||
/// </summary> | ||
private string Key => _keyPrefix + _sessionId; | ||
|
||
/// <summary> | ||
/// Retrieve the messages from Redis | ||
/// TODO: use async methods | ||
/// </summary> | ||
public override IReadOnlyList<Message> Messages | ||
{ | ||
get | ||
{ | ||
var database = _multiplexer.Value.GetDatabase(); | ||
var values = database.ListRange(Key, start: 0, stop: -1); | ||
var messages = values.Select(v => JsonSerializer.Deserialize<Message>(v.ToString())).Reverse(); | ||
|
||
return messages.ToList(); | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Append the message to the record in Redis | ||
/// </summary> | ||
public override async Task AddMessage(Message message) | ||
{ | ||
var database = _multiplexer.Value.GetDatabase(); | ||
await database.ListLeftPushAsync(Key, JsonSerializer.Serialize(message)).ConfigureAwait(false); | ||
if (_ttl.HasValue) | ||
{ | ||
await database.KeyExpireAsync(Key, _ttl).ConfigureAwait(false); | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Clear session memory from Redis | ||
/// </summary> | ||
public override async Task Clear() | ||
{ | ||
var database = _multiplexer.Value.GetDatabase(); | ||
await database.KeyDeleteAsync(Key).ConfigureAwait(false); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
11 changes: 11 additions & 0 deletions
11
...gChain.Databases.Redis.IntegrationTests/LangChain.Databases.Redis.IntegrationTests.csproj
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
|
||
<PropertyGroup> | ||
<TargetFramework>net8.0</TargetFramework> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<ProjectReference Include="..\..\libs\Databases\LangChain.Databases.Redis\LangChain.Databases.Redis.csproj" /> | ||
</ItemGroup> | ||
|
||
</Project> |
88 changes: 88 additions & 0 deletions
88
src/tests/LangChain.Databases.Redis.IntegrationTests/RedisChatMessageHistoryTests.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
using LangChain.Providers; | ||
|
||
namespace LangChain.Databases.Redis.IntegrationTests; | ||
|
||
/// <summary> | ||
/// In order to run tests please run redis locally, e.g. with docker | ||
/// docker run -p 6379:6379 redis | ||
/// </summary> | ||
[TestFixture] | ||
[Explicit] | ||
public class RedisChatMessageHistoryTests | ||
{ | ||
private readonly string _connectionString = "127.0.0.1:6379"; | ||
|
||
[Test] | ||
public void GetMessages_EmptyHistory_Ok() | ||
{ | ||
var sessionId = "GetMessages_EmptyHistory_Ok"; | ||
var history = new RedisChatMessageHistory( | ||
sessionId, | ||
_connectionString, | ||
ttl: TimeSpan.FromSeconds(30)); | ||
|
||
var existing = history.Messages; | ||
|
||
existing.Should().BeEmpty(); | ||
} | ||
|
||
[Test] | ||
public async Task AddMessage_Ok() | ||
{ | ||
var sessionId = "RedisChatMessageHistoryTests_AddMessage_Ok"; | ||
var history = new RedisChatMessageHistory( | ||
sessionId, | ||
_connectionString, | ||
ttl: TimeSpan.FromSeconds(30)); | ||
|
||
var humanMessage = Message.Human("Hi, AI"); | ||
await history.AddMessage(humanMessage); | ||
var aiMessage = Message.Ai("Hi, human"); | ||
await history.AddMessage(aiMessage); | ||
|
||
var actual = history.Messages; | ||
|
||
actual.Should().HaveCount(2); | ||
|
||
actual[0].Role.Should().Be(humanMessage.Role); | ||
actual[0].Content.Should().BeEquivalentTo(humanMessage.Content); | ||
|
||
actual[1].Role.Should().Be(aiMessage.Role); | ||
actual[1].Content.Should().BeEquivalentTo(aiMessage.Content); | ||
} | ||
|
||
[Test] | ||
public async Task Ttl_Ok() | ||
{ | ||
var sessionId = "Ttl_Ok"; | ||
var history = new RedisChatMessageHistory( | ||
sessionId, | ||
_connectionString, | ||
ttl: TimeSpan.FromSeconds(2)); | ||
|
||
var humanMessage = Message.Human("Hi, AI"); | ||
await history.AddMessage(humanMessage); | ||
|
||
await Task.Delay(2_500); | ||
|
||
var existing = history.Messages; | ||
|
||
existing.Should().BeEmpty(); | ||
} | ||
|
||
[Test] | ||
public async Task Clear_Ok() | ||
{ | ||
var sessionId = "Ttl_Ok"; | ||
var history = new RedisChatMessageHistory( | ||
sessionId, | ||
_connectionString, | ||
ttl: TimeSpan.FromSeconds(30)); | ||
|
||
await history.Clear(); | ||
|
||
var existing = history.Messages; | ||
|
||
existing.Should().BeEmpty(); | ||
} | ||
} |