Browse Source

feat: Add UseTextMessageStore Impl

pull/1421/head
colin 3 weeks ago
parent
commit
6d51f28f30
  1. 5
      aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN.Abp.AIManagement.Domain.Shared.csproj
  2. 2
      aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Localization/Resources/en.json
  3. 2
      aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Localization/Resources/zh-Hans.json
  4. 5
      aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Messages/UserMessageRecordConsts.cs
  5. 5
      aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Messages/UserTextMessageRecordConsts.cs
  6. 1
      aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN.Abp.AIManagement.Domain.csproj
  7. 15
      aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/AbpAIManagementDomainMappers.cs
  8. 14
      aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Messages/IUserTextMessageRecordRepository.cs
  9. 69
      aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Messages/UserMessageRecord.cs
  10. 111
      aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Messages/UserMessageStore.cs
  11. 27
      aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Messages/UserTextMessageRecord.cs
  12. 22
      aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Settings/AIManagementSettingDefinitionProvider.cs
  13. 10
      aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Settings/AIManagementSettingNames.cs
  14. 6
      aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/AIManagementDbContext.cs
  15. 16
      aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/AIManagementDbContextModelBuilderExtensions.cs
  16. 5
      aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/AbpAIManagementEntityFrameworkCoreModule.cs
  17. 31
      aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/EfCoreUserTextMessageRecordRepository.cs
  18. 6
      aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/IAIManagementDbContext.cs

5
aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN.Abp.AIManagement.Domain.Shared.csproj

@ -14,6 +14,11 @@
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<None Remove="LINGYUN\Abp\AIManagement\Localization\Resources\*.json" />
<EmbeddedResource Include="LINGYUN\Abp\AIManagement\Localization\Resources\*.json" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Volo.Abp.Ddd.Domain.Shared" />
</ItemGroup>

2
aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Localization/Resources/en.json

@ -1,5 +1,7 @@
{
"culture": "en",
"texts": {
"DisplayName:MaxLatestHistoryMessagesToKeep": "Carry the recent conversation records",
"Description:MaxLatestHistoryMessagesToKeep": "When a user initiates a conversation with a large model, the upper limit of the user's recent conversation history that is carried along."
}
}

2
aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Localization/Resources/zh-Hans.json

@ -1,5 +1,7 @@
{
"culture": "zh-Hans",
"texts": {
"DisplayName:MaxLatestHistoryMessagesToKeep": "携带最近对话记录",
"Description:MaxLatestHistoryMessagesToKeep": "用户发起与大模型对话时,携带用户最近对话记录的上限."
}
}

5
aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Messages/UserMessageRecordConsts.cs

@ -0,0 +1,5 @@
namespace LINGYUN.Abp.AIManagement.Messages;
public static class UserMessageRecordConsts
{
public static int MaxConversationIdLength { get; set; } = 64;
}

5
aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Messages/UserTextMessageRecordConsts.cs

@ -0,0 +1,5 @@
namespace LINGYUN.Abp.AIManagement.Messages;
public static class UserTextMessageRecordConsts
{
public static int MaxContentLength { get; set; } = 1024;
}

1
aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN.Abp.AIManagement.Domain.csproj

@ -18,6 +18,7 @@
<PackageReference Include="Volo.Abp.Caching" />
<PackageReference Include="Volo.Abp.Ddd.Domain" />
<PackageReference Include="Volo.Abp.Mapperly" />
<PackageReference Include="Volo.Abp.Settings" />
<PackageReference Include="Polly" />
</ItemGroup>

15
aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/AbpAIManagementDomainMappers.cs

@ -0,0 +1,15 @@
using LINGYUN.Abp.AI.Models;
using LINGYUN.Abp.AIManagement.Messages;
using Riok.Mapperly.Abstractions;
using Volo.Abp.Mapperly;
using Volo.Abp.ObjectExtending;
namespace LINGYUN.Abp.AIManagement;
[Mapper(RequiredMappingStrategy = RequiredMappingStrategy.Target)]
[MapExtraProperties(DefinitionChecks = MappingPropertyDefinitionChecks.None)]
public partial class UserTextMessageRecordToUserTextMessageMapper : MapperBase<UserTextMessageRecord, UserTextMessage>
{
public override partial UserTextMessage Map(UserTextMessageRecord source);
public override partial void Map(UserTextMessageRecord source, UserTextMessage destination);
}

14
aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Messages/IUserTextMessageRecordRepository.cs

@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Volo.Abp.Domain.Repositories;
namespace LINGYUN.Abp.AIManagement.Messages;
public interface IUserTextMessageRecordRepository : IBasicRepository<UserTextMessageRecord, Guid>
{
Task<IEnumerable<UserTextMessageRecord>> GetHistoryMessagesAsync(
string conversationId,
int maxResultCount = 0,
CancellationToken cancellationToken = default);
}

69
aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Messages/UserMessageRecord.cs

@ -0,0 +1,69 @@
using LINGYUN.Abp.AIManagement.Workspaces;
using System;
using Volo.Abp;
using Volo.Abp.Data;
using Volo.Abp.Domain.Entities.Auditing;
using Volo.Abp.MultiTenancy;
namespace LINGYUN.Abp.AIManagement.Messages;
public abstract class UserMessageRecord : AuditedAggregateRoot<Guid>, IMultiTenant
{
public Guid? TenantId { get; private set; }
public string Workspace { get; private set; }
public string? ConversationId { get; private set; }
public string? ReplyMessage { get; private set; }
protected UserMessageRecord()
{
ExtraProperties = new ExtraPropertyDictionary();
this.SetDefaultsForExtraProperties();
}
public UserMessageRecord(
Guid id,
string workspace,
Guid? tenantId = null)
: base(id)
{
TenantId = tenantId;
Workspace = workspace;
}
public virtual UserMessageRecord WithConversationId(string conversationId)
{
ConversationId = Check.NotNullOrWhiteSpace(conversationId, nameof(conversationId), UserMessageRecordConsts.MaxConversationIdLength);
return this;
}
public virtual UserMessageRecord WithReply(string replyMessage)
{
ReplyMessage = replyMessage;
return this;
}
public void Patch(UserMessageRecord otherMessage)
{
if (ConversationId != otherMessage.ConversationId)
{
ConversationId = otherMessage.ConversationId;
}
if (ReplyMessage != otherMessage.ReplyMessage)
{
ReplyMessage = otherMessage.ReplyMessage;
}
if (!this.HasSameExtraProperties(otherMessage))
{
ExtraProperties.Clear();
foreach (var property in otherMessage.ExtraProperties)
{
ExtraProperties.Add(property.Key, property.Value);
}
}
}
}

111
aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Messages/UserMessageStore.cs

@ -0,0 +1,111 @@
using LINGYUN.Abp.AI.Messages;
using LINGYUN.Abp.AI.Models;
using LINGYUN.Abp.AIManagement.Settings;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Guids;
using Volo.Abp.MultiTenancy;
using Volo.Abp.ObjectMapping;
using Volo.Abp.Settings;
namespace LINGYUN.Abp.AIManagement.Messages;
[Dependency(ReplaceServices = true)]
public class UserMessageStore : IUserMessageStore, ITransientDependency
{
private readonly ICurrentTenant _currentTenant;
private readonly IGuidGenerator _guidGenerator;
private readonly ISettingProvider _settingProvider;
private readonly IObjectMapper<AbpAIManagementDomainModule> _objectMapper;
private readonly IUserTextMessageRecordRepository _messageRecordRepository;
public UserMessageStore(
ICurrentTenant currentTenant,
IGuidGenerator guidGenerator,
ISettingProvider settingProvider,
IObjectMapper<AbpAIManagementDomainModule> objectMapper,
IUserTextMessageRecordRepository messageRecordRepository)
{
_currentTenant = currentTenant;
_guidGenerator = guidGenerator;
_settingProvider = settingProvider;
_objectMapper = objectMapper;
_messageRecordRepository = messageRecordRepository;
}
public async virtual Task<IEnumerable<UserMessage>> GetHistoryMessagesAsync(string conversationId)
{
var maxLatestHistoryMessagesToKeep = await _settingProvider.GetAsync(AIManagementSettingNames.UserMessage.MaxLatestHistoryMessagesToKeep, 0);
if (maxLatestHistoryMessagesToKeep < 1)
{
return Array.Empty<UserMessage>();
}
var userTextMessages = await _messageRecordRepository.GetHistoryMessagesAsync(conversationId, maxLatestHistoryMessagesToKeep);
return _objectMapper.Map<IEnumerable<UserTextMessageRecord>, IEnumerable<UserTextMessage>>(userTextMessages);
}
public async virtual Task<string> SaveMessageAsync(UserMessage message)
{
var messageId = message.Id;
if (messageId.IsNullOrWhiteSpace())
{
messageId = _guidGenerator.Create().ToString();
message.WithMessageId(messageId);
}
await StoreMessageAsync(Guid.Parse(messageId), message);
return messageId;
}
protected async virtual Task StoreMessageAsync(Guid messageId, UserMessage message)
{
switch (message)
{
case UserTextMessage textMessage:
await StoreUserTextMessageAsync(messageId, textMessage);
break;
}
}
protected async virtual Task StoreUserTextMessageAsync(Guid messageId, UserTextMessage textMessage)
{
var textMessageRecord = await _messageRecordRepository.FindAsync(messageId);
if (textMessageRecord == null)
{
textMessageRecord = new UserTextMessageRecord(
messageId,
textMessage.Workspace,
textMessage.Content,
_currentTenant.Id);
UpdateUserMessageRecord(textMessageRecord, textMessage);
await _messageRecordRepository.InsertAsync(textMessageRecord);
}
else
{
textMessageRecord.WithContent(textMessage.Content);
UpdateUserMessageRecord(textMessageRecord, textMessage);
await _messageRecordRepository.UpdateAsync(textMessageRecord);
}
}
private static void UpdateUserMessageRecord(UserMessageRecord messageRecord, UserMessage message)
{
if (!message.ConversationId.IsNullOrWhiteSpace())
{
messageRecord.WithConversationId(message.ConversationId);
}
if (!message.ReplyMessage.IsNullOrWhiteSpace())
{
messageRecord.WithConversationId(message.ReplyMessage);
}
}
}

27
aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Messages/UserTextMessageRecord.cs

@ -0,0 +1,27 @@
using System;
using Volo.Abp;
namespace LINGYUN.Abp.AIManagement.Messages;
public class UserTextMessageRecord : UserMessageRecord
{
public string Content { get; private set; }
public UserTextMessageRecord()
{
}
public UserTextMessageRecord(
Guid id,
string workspace,
string content,
Guid? tenantId = null) : base(id, workspace, tenantId)
{
WithContent(content);
}
public virtual UserTextMessageRecord WithContent(string content)
{
Content = Check.NotNullOrWhiteSpace(content, nameof(content), UserTextMessageRecordConsts.MaxContentLength);
return this;
}
}

22
aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Settings/AIManagementSettingDefinitionProvider.cs

@ -0,0 +1,22 @@
using LINGYUN.Abp.AIManagement.Localization;
using Volo.Abp.Localization;
using Volo.Abp.Settings;
namespace LINGYUN.Abp.AIManagement.Settings;
public class AIManagementSettingDefinitionProvider : SettingDefinitionProvider
{
public override void Define(ISettingDefinitionContext context)
{
context.Add(
new SettingDefinition(
AIManagementSettingNames.UserMessage.MaxLatestHistoryMessagesToKeep,
defaultValue: "5",
displayName: L("DisplayName:MaxLatestHistoryMessagesToKeep"),
description: L("Description:MaxLatestHistoryMessagesToKeep")));
}
private static ILocalizableString L(string name)
{
return LocalizableString.Create<AIManagementResource>(name);
}
}

10
aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Settings/AIManagementSettingNames.cs

@ -0,0 +1,10 @@
namespace LINGYUN.Abp.AIManagement.Settings;
public static class AIManagementSettingNames
{
public const string Prefix = "Abp.AIManagement";
public static class UserMessage
{
public const string MaxLatestHistoryMessagesToKeep = Prefix + ".MaxLatestHistoryMessagesToKeep";
}
}

6
aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/AIManagementDbContext.cs

@ -1,4 +1,5 @@
using LINGYUN.Abp.AIManagement.Workspaces;
using LINGYUN.Abp.AIManagement.Messages;
using LINGYUN.Abp.AIManagement.Workspaces;
using Microsoft.EntityFrameworkCore;
using Volo.Abp.Data;
using Volo.Abp.EntityFrameworkCore;
@ -8,7 +9,8 @@ namespace LINGYUN.Abp.AIManagement.EntityFrameworkCore;
[ConnectionStringName(AbpAIManagementDbProperties.ConnectionStringName)]
public class AIManagementDbContext : AbpDbContext<AIManagementDbContext>, IAIManagementDbContext
{
public DbSet<WorkspaceDefinitionRecord> Workspaces { get; set; }
public DbSet<WorkspaceDefinitionRecord> WorkspaceDefinitions { get; set; }
public DbSet<UserTextMessageRecord> UserTextMessageRecords { get; set; }
public AIManagementDbContext(
DbContextOptions<AIManagementDbContext> options) : base(options)
{

16
aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/AIManagementDbContextModelBuilderExtensions.cs

@ -1,4 +1,5 @@
using JetBrains.Annotations;
using LINGYUN.Abp.AIManagement.Messages;
using LINGYUN.Abp.AIManagement.Workspaces;
using Microsoft.EntityFrameworkCore;
using Volo.Abp;
@ -12,6 +13,21 @@ public static class AIManagementDbContextModelBuilderExtensions
{
Check.NotNull(builder, nameof(builder));
builder.Entity<UserTextMessageRecord>(b =>
{
b.ToTable(AbpAIManagementDbProperties.DbTablePrefix + "UserTextMessages", AbpAIManagementDbProperties.DbSchema);
b.ConfigureByConvention();
b.Property(x => x.Workspace).HasMaxLength(WorkspaceDefinitionRecordConsts.MaxNameLength).IsRequired();
b.Property(x => x.ConversationId).HasMaxLength(UserMessageRecordConsts.MaxConversationIdLength);
b.Property(x => x.Content).HasMaxLength(UserTextMessageRecordConsts.MaxContentLength).IsRequired();
b.HasIndex(x => new { x.TenantId, x.ConversationId });
b.ApplyObjectExtensionMappings();
});
if (builder.IsHostDatabase())
{
builder.Entity<WorkspaceDefinitionRecord>(b =>

5
aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/AbpAIManagementEntityFrameworkCoreModule.cs

@ -1,4 +1,5 @@
using LINGYUN.Abp.AIManagement.Workspaces;
using LINGYUN.Abp.AIManagement.Messages;
using LINGYUN.Abp.AIManagement.Workspaces;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.EntityFrameworkCore;
using Volo.Abp.Modularity;
@ -16,6 +17,8 @@ public class AbpAIManagementEntityFrameworkCoreModule : AbpModule
{
options.AddDefaultRepositories<IAIManagementDbContext>();
options.AddRepository<UserTextMessageRecord, EfCoreUserTextMessageRecordRepository>();
options.AddRepository<WorkspaceDefinitionRecord, EfCoreWorkspaceDefinitionRecordRepository>();
});
}

31
aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/EfCoreUserTextMessageRecordRepository.cs

@ -0,0 +1,31 @@
using LINGYUN.Abp.AIManagement.Messages;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Volo.Abp.Domain.Repositories.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore;
namespace LINGYUN.Abp.AIManagement.EntityFrameworkCore;
public class EfCoreUserTextMessageRecordRepository : EfCoreRepository<IAIManagementDbContext, UserTextMessageRecord, Guid>, IUserTextMessageRecordRepository
{
public EfCoreUserTextMessageRecordRepository(
IDbContextProvider<IAIManagementDbContext> dbContextProvider)
: base(dbContextProvider)
{
}
public async virtual Task<IEnumerable<UserTextMessageRecord>> GetHistoryMessagesAsync(
string conversationId,
int maxResultCount = 0,
CancellationToken cancellationToken = default)
{
return await (await GetQueryableAsync())
.Where(x => x.ConversationId == conversationId)
.OrderByDescending(x => x.CreationTime)
.Take(maxResultCount)
.ToListAsync(GetCancellationToken(cancellationToken));
}
}

6
aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/IAIManagementDbContext.cs

@ -1,4 +1,5 @@
using LINGYUN.Abp.AIManagement.Workspaces;
using LINGYUN.Abp.AIManagement.Messages;
using LINGYUN.Abp.AIManagement.Workspaces;
using Microsoft.EntityFrameworkCore;
using Volo.Abp.Data;
using Volo.Abp.EntityFrameworkCore;
@ -8,5 +9,6 @@ namespace LINGYUN.Abp.AIManagement.EntityFrameworkCore;
[ConnectionStringName(AbpAIManagementDbProperties.ConnectionStringName)]
public interface IAIManagementDbContext : IEfCoreDbContext
{
DbSet<WorkspaceDefinitionRecord> Workspaces { get; }
DbSet<WorkspaceDefinitionRecord> WorkspaceDefinitions { get; }
DbSet<UserTextMessageRecord> UserTextMessageRecords { get; }
}

Loading…
Cancel
Save