From 3ab9926bfc08514ec57fef4acc1351391f37b447 Mon Sep 17 00:00:00 2001 From: colin Date: Fri, 16 Jan 2026 17:35:54 +0800 Subject: [PATCH] feat: Rename UserMessage to ChatMessage --- .../LINGYUN/Abp/AI/Agent/AgentService.cs | 82 +++++++++++-------- .../LINGYUN/Abp/AI/Agent/IAgentService.cs | 2 +- .../LINGYUN.Abp.AI.Core.csproj | 1 + .../LINGYUN/Abp/AI/Chats/IChatMessageStore.cs | 11 +++ .../Abp/AI/Chats/InMemoryChatMessageStore.cs | 52 ++++++++++++ .../Abp/AI/Messages/IUserMessageStore.cs | 11 --- .../AI/Messages/InMemoryUserMessageStore.cs | 46 ----------- .../LINGYUN/Abp/AI/Models/ChatMessage.cs | 53 ++++++++++++ .../LINGYUN/Abp/AI/Models/TextChatMessage.cs | 25 ++++++ .../LINGYUN/Abp/AI/Models/TokenUsageInfo.cs | 34 +++++++- .../LINGYUN/Abp/AI/Models/UserMessage.cs | 53 ------------ .../LINGYUN/Abp/AI/Models/UserTextMessage.cs | 20 ----- .../LINGYUN/Abp/AI/Tokens/ITokenUsageStore.cs | 3 +- .../Abp/AI/Tokens/InMemoryTokenUsageStore.cs | 31 ++----- .../Chats/ChatMessageRecordConsts.cs | 6 ++ .../Chats/TextChatMessageRecordConsts.cs | 5 ++ .../Messages/UserMessageRecordConsts.cs | 5 -- .../Messages/UserTextMessageRecordConsts.cs | 5 -- .../AbpAIManagementDomainMappers.cs | 6 +- .../AIManagement/Chats/ChatMessageRecord.cs | 57 +++++++++++++ .../ChatMessageStore.cs} | 41 +++++----- .../ITextChatMessageRecordRepository.cs} | 6 +- .../Chats/TextChatMessageRecord.cs | 31 +++++++ .../Messages/UserMessageRecord.cs | 69 ---------------- .../Messages/UserTextMessageRecord.cs | 27 ------ .../AIManagementSettingDefinitionProvider.cs | 2 +- .../Settings/AIManagementSettingNames.cs | 2 +- .../AIManagement/Tokens/TokenUsageRecord.cs} | 4 +- ...nagementDbContextModelBuilderExtensions.cs | 56 +++++++++---- ...bpAIManagementEntityFrameworkCoreModule.cs | 4 +- ... EfCoreTextChatMessageRecordRepository.cs} | 9 +- .../ChatRoleValueConverter.cs | 11 +++ 32 files changed, 423 insertions(+), 347 deletions(-) create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Chats/IChatMessageStore.cs create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Chats/InMemoryChatMessageStore.cs delete mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Messages/IUserMessageStore.cs delete mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Messages/InMemoryUserMessageStore.cs create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Models/ChatMessage.cs create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Models/TextChatMessage.cs delete mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Models/UserMessage.cs delete mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Models/UserTextMessage.cs create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Chats/ChatMessageRecordConsts.cs create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Chats/TextChatMessageRecordConsts.cs delete mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Messages/UserMessageRecordConsts.cs delete mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Messages/UserTextMessageRecordConsts.cs create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Chats/ChatMessageRecord.cs rename aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/{Messages/UserMessageStore.cs => Chats/ChatMessageStore.cs} (68%) rename aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/{Messages/IUserTextMessageRecordRepository.cs => Chats/ITextChatMessageRecordRepository.cs} (56%) create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Chats/TextChatMessageRecord.cs delete mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Messages/UserMessageRecord.cs delete mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Messages/UserTextMessageRecord.cs rename aspnet-core/modules/ai/{LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Models/ChatMessageInfo.cs => LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Tokens/TokenUsageRecord.cs} (50%) rename aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/{EfCoreUserTextMessageRecordRepository.cs => EfCoreTextChatMessageRecordRepository.cs} (70%) create mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/ValueConversions/ChatRoleValueConverter.cs diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/LINGYUN/Abp/AI/Agent/AgentService.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/LINGYUN/Abp/AI/Agent/AgentService.cs index 17ea6b7a9..3d4f11183 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/LINGYUN/Abp/AI/Agent/AgentService.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/LINGYUN/Abp/AI/Agent/AgentService.cs @@ -1,6 +1,7 @@ -using LINGYUN.Abp.AI.Messages; +using LINGYUN.Abp.AI.Chats; using LINGYUN.Abp.AI.Models; using LINGYUN.Abp.AI.Tokens; +using Microsoft.Agents.AI; using Microsoft.Extensions.AI; using System; using System.Collections.Generic; @@ -8,24 +9,29 @@ using System.Linq; using System.Text; using System.Threading.Tasks; using Volo.Abp.DependencyInjection; +using Volo.Abp.Timing; +using AIChatMessage = Microsoft.Extensions.AI.ChatMessage; namespace LINGYUN.Abp.AI.Agent; public class AgentService : IAgentService, IScopedDependency { + private readonly IClock _clock; private readonly IAgentFactory _agentFactory; private readonly ITokenUsageStore _tokenUsageStore; - private readonly IUserMessageStore _userMessageStore; + private readonly IChatMessageStore _chatMessageStore; public AgentService( + IClock clock, IAgentFactory agentFactory, ITokenUsageStore tokenUsageStore, - IUserMessageStore userMessageStore) + IChatMessageStore chatMessageStore) { + _clock = clock; _agentFactory = agentFactory; _tokenUsageStore = tokenUsageStore; - _userMessageStore = userMessageStore; + _chatMessageStore = chatMessageStore; } - public async virtual IAsyncEnumerable SendMessageAsync(UserMessage message) + public async virtual IAsyncEnumerable SendMessageAsync(Models.ChatMessage message) { var messages = await BuildChatMessages(message); @@ -33,62 +39,70 @@ public class AgentService : IAgentService, IScopedDependency var agentRunRes = agent.RunStreamingAsync(messages); + var tokenUsageInfo = new TokenUsageInfo(message.Workspace); var agentMessageBuilder = new StringBuilder(); - await foreach (var item in agentRunRes) + await foreach (var response in agentRunRes) { - agentMessageBuilder.Append(item); - yield return item.Text; - - await StoreTokenUsageInfo(message, item.RawRepresentation); + UpdateTokenUsageInfo(tokenUsageInfo, response); + agentMessageBuilder.Append(response.Text); + yield return response.Text; } - await StoreChatMessage(message, agentMessageBuilder.ToString()); + var messageId = await StoreChatMessage(message, agentMessageBuilder.ToString()); + + tokenUsageInfo.WithConversationId(message.ConversationId); + tokenUsageInfo.WithMessageId(messageId); + + Console.WriteLine(); + Console.WriteLine($"消耗Token: {tokenUsageInfo}"); + + await StoreTokenUsageInfo(tokenUsageInfo); } - protected virtual async Task> BuildChatMessages(UserMessage message) + protected virtual async Task> BuildChatMessages(Models.ChatMessage message) { - var messages = new List(); + var messages = new List(); if (!message.ConversationId.IsNullOrWhiteSpace()) { - var historyMessages = await _userMessageStore.GetHistoryMessagesAsync(message.ConversationId); + var historyMessages = await _chatMessageStore.GetHistoryMessagesAsync(message.ConversationId); foreach (var chatMessage in historyMessages) { - messages.Add(new ChatMessage(ChatRole.System, chatMessage.GetMessagePrompt())); + messages.Add(new AIChatMessage(ChatRole.System, chatMessage.GetMessagePrompt())); } } - messages.Add(new ChatMessage(ChatRole.User, message.GetMessagePrompt())); + messages.Add(new AIChatMessage(ChatRole.User, message.GetMessagePrompt())); return messages; } - protected async virtual Task StoreChatMessage(UserMessage message, string agentMessage) + protected async virtual Task StoreChatMessage(Models.ChatMessage message, string agentMessage) { - message.WithReply(agentMessage); + message.WithReply(agentMessage, _clock.Now); - await _userMessageStore.SaveMessageAsync(message); + return await _chatMessageStore.SaveMessageAsync(message); } - protected async virtual Task StoreTokenUsageInfo(UserMessage message, object? rawRepresentation) + protected async virtual Task StoreTokenUsageInfo(TokenUsageInfo tokenUsageInfo) { - if (rawRepresentation is ChatResponseUpdate update) + await _tokenUsageStore.SaveTokenUsageAsync(tokenUsageInfo); + } + + private static void UpdateTokenUsageInfo(TokenUsageInfo tokenUsageInfo, AgentRunResponseUpdate response) + { + if (response.RawRepresentation != null && + response.RawRepresentation is ChatResponseUpdate update) { - var tokenUsageInfos = update.Contents - .OfType() - .Where(usage => usage.Details != null) - .Select(usage => new TokenUsageInfo(message.Workspace) - { - TotalTokenCount = usage.Details.TotalTokenCount, - CachedInputTokenCount = usage.Details.CachedInputTokenCount, - InputTokenCount = usage.Details.InputTokenCount, - OutputTokenCount = usage.Details.OutputTokenCount, - ReasoningTokenCount = usage.Details.ReasoningTokenCount, - }); - - await _tokenUsageStore.SaveTokenUsagesAsync(tokenUsageInfos); + var usageContents = update.Contents.OfType(); + + tokenUsageInfo.InputTokenCount = usageContents.Max(x => x.Details.InputTokenCount); + tokenUsageInfo.OutputTokenCount = usageContents.Max(x => x.Details.OutputTokenCount); + tokenUsageInfo.TotalTokenCount = usageContents.Max(x => x.Details.TotalTokenCount); + tokenUsageInfo.ReasoningTokenCount = usageContents.Max(x => x.Details.ReasoningTokenCount); + tokenUsageInfo.CachedInputTokenCount = usageContents.Max(x => x.Details.CachedInputTokenCount); } } } diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/LINGYUN/Abp/AI/Agent/IAgentService.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/LINGYUN/Abp/AI/Agent/IAgentService.cs index 24ad9e19f..109f267d3 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/LINGYUN/Abp/AI/Agent/IAgentService.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/LINGYUN/Abp/AI/Agent/IAgentService.cs @@ -4,5 +4,5 @@ using System.Collections.Generic; namespace LINGYUN.Abp.AI.Agent; public interface IAgentService { - IAsyncEnumerable SendMessageAsync(UserMessage message); + IAsyncEnumerable SendMessageAsync(ChatMessage message); } diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN.Abp.AI.Core.csproj b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN.Abp.AI.Core.csproj index 57d089b1c..4dc562c9c 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN.Abp.AI.Core.csproj +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN.Abp.AI.Core.csproj @@ -22,6 +22,7 @@ + diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Chats/IChatMessageStore.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Chats/IChatMessageStore.cs new file mode 100644 index 000000000..023f79b20 --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Chats/IChatMessageStore.cs @@ -0,0 +1,11 @@ +using LINGYUN.Abp.AI.Models; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace LINGYUN.Abp.AI.Chats; +public interface IChatMessageStore +{ + Task SaveMessageAsync(ChatMessage message); + + Task> GetHistoryMessagesAsync(string conversationId); +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Chats/InMemoryChatMessageStore.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Chats/InMemoryChatMessageStore.cs new file mode 100644 index 000000000..293e57202 --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Chats/InMemoryChatMessageStore.cs @@ -0,0 +1,52 @@ +using LINGYUN.Abp.AI.Models; +using Microsoft.Extensions.DependencyInjection; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Volo.Abp.DependencyInjection; + +namespace LINGYUN.Abp.AI.Chats; + +[Dependency(ServiceLifetime.Singleton, TryRegister = true)] +public class InMemoryChatMessageStore : IChatMessageStore +{ + private static readonly ConcurrentDictionary> _userMessageCache = new ConcurrentDictionary>(); + + public Task> GetHistoryMessagesAsync(string conversationId) + { + var messages = new List(); + + foreach (var userMessages in _userMessageCache.Values) + { + messages.AddRange(userMessages.Where(x => x.ConversationId == conversationId)); + } + + return Task.FromResult>( + messages + .OrderByDescending(x => x.CreatedAt) + .Take(5) + .OrderBy(x => x.CreatedAt)); + } + + public Task SaveMessageAsync(ChatMessage message) + { + var messageId = message.Id; + if (messageId.IsNullOrWhiteSpace()) + { + messageId = Guid.NewGuid().ToString(); + message.WithMessageId(messageId); + } + if (_userMessageCache.ContainsKey(messageId)) + { + _userMessageCache[messageId].Add(message); + } + else + { + _userMessageCache[messageId] = new List() { message }; + } + + return Task.FromResult(messageId); + } +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Messages/IUserMessageStore.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Messages/IUserMessageStore.cs deleted file mode 100644 index b56406d5f..000000000 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Messages/IUserMessageStore.cs +++ /dev/null @@ -1,11 +0,0 @@ -using LINGYUN.Abp.AI.Models; -using System.Collections.Generic; -using System.Threading.Tasks; - -namespace LINGYUN.Abp.AI.Messages; -public interface IUserMessageStore -{ - Task SaveMessageAsync(UserMessage message); - - Task> GetHistoryMessagesAsync(string conversationId); -} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Messages/InMemoryUserMessageStore.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Messages/InMemoryUserMessageStore.cs deleted file mode 100644 index a12cf1de4..000000000 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Messages/InMemoryUserMessageStore.cs +++ /dev/null @@ -1,46 +0,0 @@ -using LINGYUN.Abp.AI.Models; -using Microsoft.Extensions.DependencyInjection; -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Volo.Abp.DependencyInjection; - -namespace LINGYUN.Abp.AI.Messages; - -[Dependency(ServiceLifetime.Singleton, TryRegister = true)] -public class InMemoryUserMessageStore : IUserMessageStore -{ - private static readonly ConcurrentDictionary> _userMessageCache = new ConcurrentDictionary>(); - - public Task> GetHistoryMessagesAsync(string conversationId) - { - if (_userMessageCache.TryGetValue(conversationId, out var messages)) - { - return Task.FromResult(messages.Take(5)); - } - - return Task.FromResult>(Array.Empty()); - } - - public Task SaveMessageAsync(UserMessage message) - { - var messageId = message.Id; - if (messageId.IsNullOrWhiteSpace()) - { - messageId = Guid.NewGuid().ToString(); - message.WithMessageId(messageId); - } - if (_userMessageCache.ContainsKey(messageId)) - { - _userMessageCache[messageId].Add(message); - } - else - { - _userMessageCache[messageId] = new List() { message }; - } - - return Task.FromResult(messageId); - } -} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Models/ChatMessage.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Models/ChatMessage.cs new file mode 100644 index 000000000..e173d053a --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Models/ChatMessage.cs @@ -0,0 +1,53 @@ +using Microsoft.Extensions.AI; +using System; + +namespace LINGYUN.Abp.AI.Models; +public abstract class ChatMessage +{ + public string Workspace { get; } + + public string? Id { get; private set; } + + public string? ConversationId { get; private set; } + + public string? ReplyMessage { get; private set; } + + public DateTime? ReplyAt { get; private set; } + + public ChatRole Role { get; private set; } + + public DateTime CreatedAt { get; private set; } + protected ChatMessage( + string workspace, + ChatRole? role = null, + DateTime? createdAt = null) + { + Workspace = workspace; + Role = role ?? ChatRole.User; + CreatedAt = createdAt ?? DateTime.Now; + } + + public virtual ChatMessage WithMessageId(string id) + { + Id = id; + return this; + } + + public virtual ChatMessage WithConversationId(string conversationId) + { + ConversationId = conversationId; + return this; + } + + public virtual ChatMessage WithReply(string replyMessage, DateTime replyAt) + { + ReplyMessage = replyMessage; + ReplyAt = replyAt; + return this; + } + + public virtual string GetMessagePrompt() + { + return string.Empty; + } +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Models/TextChatMessage.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Models/TextChatMessage.cs new file mode 100644 index 000000000..20ea67e1c --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Models/TextChatMessage.cs @@ -0,0 +1,25 @@ +using Microsoft.Extensions.AI; +using System; + +namespace LINGYUN.Abp.AI.Models; +public class TextChatMessage : ChatMessage +{ + /// + /// 消息内容 + /// + public string Content { get; } + public TextChatMessage( + string workspace, + string content, + ChatRole? role = null, + DateTime? createdAt = null) + : base(workspace, role, createdAt) + { + Content = content; + } + + public override string GetMessagePrompt() + { + return Content; + } +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Models/TokenUsageInfo.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Models/TokenUsageInfo.cs index 61c8e0457..08238d79b 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Models/TokenUsageInfo.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Models/TokenUsageInfo.cs @@ -1,7 +1,11 @@ -namespace LINGYUN.Abp.AI.Models; +using System.Text; + +namespace LINGYUN.Abp.AI.Models; public class TokenUsageInfo { public string Workspace { get; } + public string? MessageId { get; private set; } + public string? ConversationId { get; private set; } public long? InputTokenCount { get; set; } public long? OutputTokenCount { get; set; } public long? TotalTokenCount { get; set; } @@ -11,4 +15,32 @@ public class TokenUsageInfo { Workspace = workspace; } + public virtual TokenUsageInfo WithMessageId(string id) + { + MessageId = id; + return this; + } + + public virtual TokenUsageInfo WithConversationId(string? conversationId) + { + ConversationId = conversationId; + return this; + } + + public override string ToString() + { + var sb = new StringBuilder(); + + sb.AppendLine("---------------------- TokenUsage Begin ----------------------"); + sb.AppendLine($"====== Workspace - {Workspace}"); + sb.AppendLine($"====== MessageId - {MessageId}"); + sb.AppendLine($"====== ConversationId - {ConversationId}"); + sb.AppendLine($"====== InputTokenCount - {InputTokenCount}"); + sb.AppendLine($"====== OutputTokenCount - {OutputTokenCount}"); + sb.AppendLine($"====== TotalTokenCount - {TotalTokenCount}"); + sb.AppendLine($"====== ReasoningTokenCount - {ReasoningTokenCount}"); + sb.AppendLine("---------------------- TokenUsage End ----------------------"); + + return sb.ToString(); + } } diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Models/UserMessage.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Models/UserMessage.cs deleted file mode 100644 index 7681ae260..000000000 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Models/UserMessage.cs +++ /dev/null @@ -1,53 +0,0 @@ -namespace LINGYUN.Abp.AI.Models; -public abstract class UserMessage -{ - /// - /// 工作区 - /// - public string Workspace { get; } - /// - /// 消息Id - /// - /// - /// 在持久化设施处更新 - /// - public string? Id { get; private set; } - /// - /// 对话Id - /// - /// - /// 用于从客户端存储中持久化和检索聊天历史的唯一标识符,如果未指定则与AI对话时无上下文关联 - /// - public string? ConversationId { get; private set; } - /// - /// AI回复消息 - /// - public string ReplyMessage { get; private set; } - protected UserMessage(string workspace) - { - Workspace = workspace; - } - - public virtual UserMessage WithMessageId(string id) - { - Id = id; - return this; - } - - public virtual UserMessage WithConversationId(string conversationId) - { - ConversationId = conversationId; - return this; - } - - public virtual UserMessage WithReply(string replyMessage) - { - ReplyMessage = replyMessage; - return this; - } - - public virtual string GetMessagePrompt() - { - return string.Empty; - } -} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Models/UserTextMessage.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Models/UserTextMessage.cs deleted file mode 100644 index 55cb12ae6..000000000 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Models/UserTextMessage.cs +++ /dev/null @@ -1,20 +0,0 @@ -namespace LINGYUN.Abp.AI.Models; -public class UserTextMessage : UserMessage -{ - /// - /// 消息内容 - /// - public string Content { get; } - public UserTextMessage( - string workspace, - string content) - : base(workspace) - { - Content = content; - } - - public override string GetMessagePrompt() - { - return Content; - } -} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Tokens/ITokenUsageStore.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Tokens/ITokenUsageStore.cs index 32b4d0718..dd44f988a 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Tokens/ITokenUsageStore.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Tokens/ITokenUsageStore.cs @@ -1,9 +1,8 @@ using LINGYUN.Abp.AI.Models; -using System.Collections.Generic; using System.Threading.Tasks; namespace LINGYUN.Abp.AI.Tokens; public interface ITokenUsageStore { - Task SaveTokenUsagesAsync(IEnumerable usageInfos); + Task SaveTokenUsageAsync(TokenUsageInfo tokenUsageInfo); } diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Tokens/InMemoryTokenUsageStore.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Tokens/InMemoryTokenUsageStore.cs index 9c7862de8..9f492b8b2 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Tokens/InMemoryTokenUsageStore.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Tokens/InMemoryTokenUsageStore.cs @@ -2,7 +2,6 @@ using Microsoft.Extensions.DependencyInjection; using System.Collections.Concurrent; using System.Collections.Generic; -using System.Linq; using System.Threading.Tasks; using Volo.Abp.DependencyInjection; @@ -11,31 +10,19 @@ namespace LINGYUN.Abp.AI.Tokens; [Dependency(ServiceLifetime.Singleton, TryRegister = true)] public class InMemoryTokenUsageStore : ITokenUsageStore { - private static readonly ConcurrentDictionary _tokenUsageCache = new ConcurrentDictionary(); + private static readonly ConcurrentDictionary> _tokenUsageCache = new ConcurrentDictionary>(); - public Task SaveTokenUsagesAsync(IEnumerable usageInfos) + public Task SaveTokenUsageAsync(TokenUsageInfo tokenUsageInfo) { - foreach (var usageInfo in usageInfos.GroupBy(x => x.Workspace)) + if (_tokenUsageCache.TryGetValue(tokenUsageInfo.Workspace, out var tokenUsageInfos)) { - var tokenUsageInfo = new TokenUsageInfo(usageInfo.Key) - { - InputTokenCount = usageInfo.Sum(x => x.InputTokenCount), - TotalTokenCount = usageInfo.Sum(x => x.TotalTokenCount), - OutputTokenCount = usageInfo.Sum(x => x.OutputTokenCount), - ReasoningTokenCount = usageInfo.Sum(x => x.ReasoningTokenCount), - CachedInputTokenCount = usageInfo.Sum(x => x.CachedInputTokenCount), - }; - - if (!_tokenUsageCache.ContainsKey(usageInfo.Key)) - { - _tokenUsageCache.TryAdd(usageInfo.Key, tokenUsageInfo); - } - else - { - _tokenUsageCache[usageInfo.Key] = tokenUsageInfo; - } + tokenUsageInfos.Add(tokenUsageInfo); } - + else + { + _tokenUsageCache.TryAdd(tokenUsageInfo.Workspace, [tokenUsageInfo]); + } + return Task.CompletedTask; } } diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Chats/ChatMessageRecordConsts.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Chats/ChatMessageRecordConsts.cs new file mode 100644 index 000000000..3af24e28f --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Chats/ChatMessageRecordConsts.cs @@ -0,0 +1,6 @@ +namespace LINGYUN.Abp.AIManagement.Chats; +public static class ChatMessageRecordConsts +{ + public static int MaxConversationIdLength { get; set; } = 64; + public static int MaxChatRoleLength { get; set; } = 20; +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Chats/TextChatMessageRecordConsts.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Chats/TextChatMessageRecordConsts.cs new file mode 100644 index 000000000..a603c0e87 --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Chats/TextChatMessageRecordConsts.cs @@ -0,0 +1,5 @@ +namespace LINGYUN.Abp.AIManagement.Chats; +public static class TextChatMessageRecordConsts +{ + public static int MaxContentLength { get; set; } = 1024; +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Messages/UserMessageRecordConsts.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Messages/UserMessageRecordConsts.cs deleted file mode 100644 index e51d5365a..000000000 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Messages/UserMessageRecordConsts.cs +++ /dev/null @@ -1,5 +0,0 @@ -namespace LINGYUN.Abp.AIManagement.Messages; -public static class UserMessageRecordConsts -{ - public static int MaxConversationIdLength { get; set; } = 64; -} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Messages/UserTextMessageRecordConsts.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Messages/UserTextMessageRecordConsts.cs deleted file mode 100644 index 184b93ab0..000000000 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Messages/UserTextMessageRecordConsts.cs +++ /dev/null @@ -1,5 +0,0 @@ -namespace LINGYUN.Abp.AIManagement.Messages; -public static class UserTextMessageRecordConsts -{ - public static int MaxContentLength { get; set; } = 1024; -} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/AbpAIManagementDomainMappers.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/AbpAIManagementDomainMappers.cs index 1993a46f5..75b113067 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/AbpAIManagementDomainMappers.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/AbpAIManagementDomainMappers.cs @@ -8,8 +8,8 @@ namespace LINGYUN.Abp.AIManagement; [Mapper(RequiredMappingStrategy = RequiredMappingStrategy.Target)] [MapExtraProperties(DefinitionChecks = MappingPropertyDefinitionChecks.None)] -public partial class UserTextMessageRecordToUserTextMessageMapper : MapperBase +public partial class UserTextMessageRecordToUserTextMessageMapper : MapperBase { - public override partial UserTextMessage Map(UserTextMessageRecord source); - public override partial void Map(UserTextMessageRecord source, UserTextMessage destination); + public override partial TextChatMessage Map(UserTextMessageRecord source); + public override partial void Map(UserTextMessageRecord source, TextChatMessage destination); } diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Chats/ChatMessageRecord.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Chats/ChatMessageRecord.cs new file mode 100644 index 000000000..2b08d4c67 --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Chats/ChatMessageRecord.cs @@ -0,0 +1,57 @@ +using Microsoft.Extensions.AI; +using System; +using Volo.Abp; +using Volo.Abp.Data; +using Volo.Abp.Domain.Entities.Auditing; +using Volo.Abp.MultiTenancy; + +namespace LINGYUN.Abp.AIManagement.Chats; +public abstract class ChatMessageRecord : AuditedAggregateRoot, IMultiTenant +{ + public Guid? TenantId { get; private set; } + + public string Workspace { get; private set; } + + public ChatRole Role { get; private set; } + + public DateTime CreatedAt { get; private set; } + + public string? ConversationId { get; private set; } + + public string? ReplyMessage { get; private set; } + + public DateTime? ReplyAt { get; private set; } + + protected ChatMessageRecord() + { + ExtraProperties = new ExtraPropertyDictionary(); + this.SetDefaultsForExtraProperties(); + } + + public ChatMessageRecord( + Guid id, + string workspace, + ChatRole role, + DateTime createdAt, + Guid? tenantId = null) + : base(id) + { + Workspace = workspace; + Role = role; + CreatedAt = createdAt; + TenantId = tenantId; + } + + public virtual ChatMessageRecord SetConversationId(string conversationId) + { + ConversationId = Check.NotNullOrWhiteSpace(conversationId, nameof(conversationId), ChatMessageRecordConsts.MaxConversationIdLength); + return this; + } + + public virtual ChatMessageRecord SetReply(string replyMessage, DateTime? replyAt) + { + ReplyMessage = replyMessage; + ReplyAt = replyAt; + return this; + } +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Messages/UserMessageStore.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Chats/ChatMessageStore.cs similarity index 68% rename from aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Messages/UserMessageStore.cs rename to aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Chats/ChatMessageStore.cs index 04dd720a3..feb7b9f80 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Messages/UserMessageStore.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Chats/ChatMessageStore.cs @@ -1,4 +1,4 @@ -using LINGYUN.Abp.AI.Messages; +using LINGYUN.Abp.AI.Chats; using LINGYUN.Abp.AI.Models; using LINGYUN.Abp.AIManagement.Settings; using System; @@ -10,23 +10,23 @@ using Volo.Abp.MultiTenancy; using Volo.Abp.ObjectMapping; using Volo.Abp.Settings; -namespace LINGYUN.Abp.AIManagement.Messages; +namespace LINGYUN.Abp.AIManagement.Chats; [Dependency(ReplaceServices = true)] -public class UserMessageStore : IUserMessageStore, ITransientDependency +public class ChatMessageStore : IChatMessageStore, ITransientDependency { private readonly ICurrentTenant _currentTenant; private readonly IGuidGenerator _guidGenerator; private readonly ISettingProvider _settingProvider; private readonly IObjectMapper _objectMapper; - private readonly IUserTextMessageRecordRepository _messageRecordRepository; + private readonly ITextChatMessageRecordRepository _messageRecordRepository; - public UserMessageStore( + public ChatMessageStore( ICurrentTenant currentTenant, IGuidGenerator guidGenerator, ISettingProvider settingProvider, IObjectMapper objectMapper, - IUserTextMessageRecordRepository messageRecordRepository) + ITextChatMessageRecordRepository messageRecordRepository) { _currentTenant = currentTenant; _guidGenerator = guidGenerator; @@ -35,20 +35,21 @@ public class UserMessageStore : IUserMessageStore, ITransientDependency _messageRecordRepository = messageRecordRepository; } - public async virtual Task> GetHistoryMessagesAsync(string conversationId) + public async virtual Task> GetHistoryMessagesAsync(string conversationId) { - var maxLatestHistoryMessagesToKeep = await _settingProvider.GetAsync(AIManagementSettingNames.UserMessage.MaxLatestHistoryMessagesToKeep, 0); + var maxLatestHistoryMessagesToKeep = await _settingProvider.GetAsync( + AIManagementSettingNames.ChatMessage.MaxLatestHistoryMessagesToKeep, 0); if (maxLatestHistoryMessagesToKeep < 1) { - return Array.Empty(); + return Array.Empty(); } var userTextMessages = await _messageRecordRepository.GetHistoryMessagesAsync(conversationId, maxLatestHistoryMessagesToKeep); - return _objectMapper.Map, IEnumerable>(userTextMessages); + return _objectMapper.Map, IEnumerable>(userTextMessages); } - public async virtual Task SaveMessageAsync(UserMessage message) + public async virtual Task SaveMessageAsync(ChatMessage message) { var messageId = message.Id; if (messageId.IsNullOrWhiteSpace()) @@ -62,25 +63,27 @@ public class UserMessageStore : IUserMessageStore, ITransientDependency return messageId; } - protected async virtual Task StoreMessageAsync(Guid messageId, UserMessage message) + protected async virtual Task StoreMessageAsync(Guid messageId, ChatMessage message) { switch (message) { - case UserTextMessage textMessage: + case TextChatMessage textMessage: await StoreUserTextMessageAsync(messageId, textMessage); break; } } - protected async virtual Task StoreUserTextMessageAsync(Guid messageId, UserTextMessage textMessage) + protected async virtual Task StoreUserTextMessageAsync(Guid messageId, TextChatMessage textMessage) { var textMessageRecord = await _messageRecordRepository.FindAsync(messageId); if (textMessageRecord == null) { - textMessageRecord = new UserTextMessageRecord( + textMessageRecord = new TextChatMessageRecord( messageId, textMessage.Workspace, textMessage.Content, + textMessage.Role, + textMessage.CreatedAt, _currentTenant.Id); UpdateUserMessageRecord(textMessageRecord, textMessage); @@ -89,7 +92,7 @@ public class UserMessageStore : IUserMessageStore, ITransientDependency } else { - textMessageRecord.WithContent(textMessage.Content); + textMessageRecord.SetContent(textMessage.Content); UpdateUserMessageRecord(textMessageRecord, textMessage); @@ -97,15 +100,15 @@ public class UserMessageStore : IUserMessageStore, ITransientDependency } } - private static void UpdateUserMessageRecord(UserMessageRecord messageRecord, UserMessage message) + private static void UpdateUserMessageRecord(ChatMessageRecord messageRecord, ChatMessage message) { if (!message.ConversationId.IsNullOrWhiteSpace()) { - messageRecord.WithConversationId(message.ConversationId); + messageRecord.SetConversationId(message.ConversationId); } if (!message.ReplyMessage.IsNullOrWhiteSpace()) { - messageRecord.WithConversationId(message.ReplyMessage); + messageRecord.SetReply(message.ReplyMessage, message.ReplyAt); } } } diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Messages/IUserTextMessageRecordRepository.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Chats/ITextChatMessageRecordRepository.cs similarity index 56% rename from aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Messages/IUserTextMessageRecordRepository.cs rename to aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Chats/ITextChatMessageRecordRepository.cs index 6ecb5ab8a..ad641cb43 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Messages/IUserTextMessageRecordRepository.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Chats/ITextChatMessageRecordRepository.cs @@ -4,10 +4,10 @@ using System.Threading; using System.Threading.Tasks; using Volo.Abp.Domain.Repositories; -namespace LINGYUN.Abp.AIManagement.Messages; -public interface IUserTextMessageRecordRepository : IBasicRepository +namespace LINGYUN.Abp.AIManagement.Chats; +public interface ITextChatMessageRecordRepository : IBasicRepository { - Task> GetHistoryMessagesAsync( + Task> GetHistoryMessagesAsync( string conversationId, int maxResultCount = 0, CancellationToken cancellationToken = default); diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Chats/TextChatMessageRecord.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Chats/TextChatMessageRecord.cs new file mode 100644 index 000000000..16efa2653 --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Chats/TextChatMessageRecord.cs @@ -0,0 +1,31 @@ +using Microsoft.Extensions.AI; +using System; +using Volo.Abp; + +namespace LINGYUN.Abp.AIManagement.Chats; +public class TextChatMessageRecord : ChatMessageRecord +{ + public string Content { get; private set; } + + public TextChatMessageRecord() + { + } + + public TextChatMessageRecord( + Guid id, + string workspace, + string content, + ChatRole role, + DateTime createdAt, + Guid? tenantId = null) + : base(id, workspace, role, createdAt, tenantId) + { + SetContent(content); + } + + public virtual TextChatMessageRecord SetContent(string content) + { + Content = Check.NotNullOrWhiteSpace(content, nameof(content), TextChatMessageRecordConsts.MaxContentLength); + return this; + } +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Messages/UserMessageRecord.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Messages/UserMessageRecord.cs deleted file mode 100644 index 559a4f189..000000000 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Messages/UserMessageRecord.cs +++ /dev/null @@ -1,69 +0,0 @@ -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, 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); - } - } - } -} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Messages/UserTextMessageRecord.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Messages/UserTextMessageRecord.cs deleted file mode 100644 index 874790b9b..000000000 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Messages/UserTextMessageRecord.cs +++ /dev/null @@ -1,27 +0,0 @@ -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; - } -} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Settings/AIManagementSettingDefinitionProvider.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Settings/AIManagementSettingDefinitionProvider.cs index c862256fc..e36886420 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Settings/AIManagementSettingDefinitionProvider.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Settings/AIManagementSettingDefinitionProvider.cs @@ -9,7 +9,7 @@ public class AIManagementSettingDefinitionProvider : SettingDefinitionProvider { context.Add( new SettingDefinition( - AIManagementSettingNames.UserMessage.MaxLatestHistoryMessagesToKeep, + AIManagementSettingNames.ChatMessage.MaxLatestHistoryMessagesToKeep, defaultValue: "5", displayName: L("DisplayName:MaxLatestHistoryMessagesToKeep"), description: L("Description:MaxLatestHistoryMessagesToKeep"))); diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Settings/AIManagementSettingNames.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Settings/AIManagementSettingNames.cs index 3cdb8355d..5e87515f6 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Settings/AIManagementSettingNames.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Settings/AIManagementSettingNames.cs @@ -3,7 +3,7 @@ public static class AIManagementSettingNames { public const string Prefix = "Abp.AIManagement"; - public static class UserMessage + public static class ChatMessage { public const string MaxLatestHistoryMessagesToKeep = Prefix + ".MaxLatestHistoryMessagesToKeep"; } diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Models/ChatMessageInfo.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Tokens/TokenUsageRecord.cs similarity index 50% rename from aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Models/ChatMessageInfo.cs rename to aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Tokens/TokenUsageRecord.cs index 279c9ba9b..fedf9a194 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Models/ChatMessageInfo.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Tokens/TokenUsageRecord.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.Text; -namespace LINGYUN.Abp.AI.Models; -internal class ChatMessageInfo +namespace LINGYUN.Abp.AIManagement.Tokens; +public class TokenUsageRecord { } diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/AIManagementDbContextModelBuilderExtensions.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/AIManagementDbContextModelBuilderExtensions.cs index 181054e54..90a2d3fc9 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/AIManagementDbContextModelBuilderExtensions.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/AIManagementDbContextModelBuilderExtensions.cs @@ -1,5 +1,6 @@ using JetBrains.Annotations; -using LINGYUN.Abp.AIManagement.Messages; +using LINGYUN.Abp.AIManagement.Chats; +using LINGYUN.Abp.AIManagement.EntityFrameworkCore.ValueConversions; using LINGYUN.Abp.AIManagement.Workspaces; using Microsoft.EntityFrameworkCore; using Volo.Abp; @@ -13,15 +14,24 @@ public static class AIManagementDbContextModelBuilderExtensions { Check.NotNull(builder, nameof(builder)); - builder.Entity(b => + builder.Entity(b => { - b.ToTable(AbpAIManagementDbProperties.DbTablePrefix + "UserTextMessages", AbpAIManagementDbProperties.DbSchema); + b.ToTable(AbpAIManagementDbProperties.DbTablePrefix + "TextChatMessages", 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.Property(x => x.Workspace) + .HasMaxLength(WorkspaceDefinitionRecordConsts.MaxNameLength) + .IsRequired(); + b.Property(x => x.Role) + .HasMaxLength(ChatMessageRecordConsts.MaxChatRoleLength) + .HasConversion(new ChatRoleValueConverter()) + .IsRequired(); + b.Property(x => x.ConversationId) + .HasMaxLength(ChatMessageRecordConsts.MaxConversationIdLength); + b.Property(x => x.Content) + .HasMaxLength(TextChatMessageRecordConsts.MaxContentLength) + .IsRequired(); b.HasIndex(x => new { x.TenantId, x.ConversationId }); @@ -36,16 +46,30 @@ public static class AIManagementDbContextModelBuilderExtensions b.ConfigureByConvention(); - b.Property(x => x.Name).HasMaxLength(WorkspaceDefinitionRecordConsts.MaxNameLength).IsRequired(); - b.Property(x => x.Provider).HasMaxLength(WorkspaceDefinitionRecordConsts.MaxProviderLength).IsRequired(); - b.Property(x => x.ModelName).HasMaxLength(WorkspaceDefinitionRecordConsts.MaxModelNameLength).IsRequired(); - b.Property(x => x.DisplayName).HasMaxLength(WorkspaceDefinitionRecordConsts.MaxDisplayNameLength).IsRequired(); - b.Property(x => x.Description).HasMaxLength(WorkspaceDefinitionRecordConsts.MaxDescriptionLength); - b.Property(x => x.ApiKey).HasMaxLength(WorkspaceDefinitionRecordConsts.MaxApiKeyLength); - b.Property(x => x.ApiBaseUrl).HasMaxLength(WorkspaceDefinitionRecordConsts.MaxApiKeyLength); - b.Property(x => x.SystemPrompt).HasMaxLength(WorkspaceDefinitionRecordConsts.MaxSystemPromptLength); - b.Property(x => x.Instructions).HasMaxLength(WorkspaceDefinitionRecordConsts.MaxInstructionsLength); - b.Property(x => x.StateCheckers).HasMaxLength(WorkspaceDefinitionRecordConsts.MaxStateCheckersLength); + b.Property(x => x.Name) + .HasMaxLength(WorkspaceDefinitionRecordConsts.MaxNameLength) + .IsRequired(); + b.Property(x => x.Provider) + .HasMaxLength(WorkspaceDefinitionRecordConsts.MaxProviderLength) + .IsRequired(); + b.Property(x => x.ModelName) + .HasMaxLength(WorkspaceDefinitionRecordConsts.MaxModelNameLength) + .IsRequired(); + b.Property(x => x.DisplayName) + .HasMaxLength(WorkspaceDefinitionRecordConsts.MaxDisplayNameLength) + .IsRequired(); + b.Property(x => x.Description) + .HasMaxLength(WorkspaceDefinitionRecordConsts.MaxDescriptionLength); + b.Property(x => x.ApiKey) + .HasMaxLength(WorkspaceDefinitionRecordConsts.MaxApiKeyLength); + b.Property(x => x.ApiBaseUrl) + .HasMaxLength(WorkspaceDefinitionRecordConsts.MaxApiKeyLength); + b.Property(x => x.SystemPrompt) + .HasMaxLength(WorkspaceDefinitionRecordConsts.MaxSystemPromptLength); + b.Property(x => x.Instructions) + .HasMaxLength(WorkspaceDefinitionRecordConsts.MaxInstructionsLength); + b.Property(x => x.StateCheckers) + .HasMaxLength(WorkspaceDefinitionRecordConsts.MaxStateCheckersLength); b.HasIndex(x => new { x.Name }).IsUnique(); diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/AbpAIManagementEntityFrameworkCoreModule.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/AbpAIManagementEntityFrameworkCoreModule.cs index 0371fb3fd..f372b441f 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/AbpAIManagementEntityFrameworkCoreModule.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/AbpAIManagementEntityFrameworkCoreModule.cs @@ -1,4 +1,4 @@ -using LINGYUN.Abp.AIManagement.Messages; +using LINGYUN.Abp.AIManagement.Chats; using LINGYUN.Abp.AIManagement.Workspaces; using Microsoft.Extensions.DependencyInjection; using Volo.Abp.EntityFrameworkCore; @@ -17,7 +17,7 @@ public class AbpAIManagementEntityFrameworkCoreModule : AbpModule { options.AddDefaultRepositories(); - options.AddRepository(); + options.AddRepository(); options.AddRepository(); }); diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/EfCoreUserTextMessageRecordRepository.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/EfCoreTextChatMessageRecordRepository.cs similarity index 70% rename from aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/EfCoreUserTextMessageRecordRepository.cs rename to aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/EfCoreTextChatMessageRecordRepository.cs index 6d1ddc943..3377e6304 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/EfCoreUserTextMessageRecordRepository.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/EfCoreTextChatMessageRecordRepository.cs @@ -1,4 +1,4 @@ -using LINGYUN.Abp.AIManagement.Messages; +using LINGYUN.Abp.AIManagement.Chats; using Microsoft.EntityFrameworkCore; using System; using System.Collections.Generic; @@ -9,15 +9,15 @@ using Volo.Abp.Domain.Repositories.EntityFrameworkCore; using Volo.Abp.EntityFrameworkCore; namespace LINGYUN.Abp.AIManagement.EntityFrameworkCore; -public class EfCoreUserTextMessageRecordRepository : EfCoreRepository, IUserTextMessageRecordRepository +public class EfCoreTextChatMessageRecordRepository : EfCoreRepository, ITextChatMessageRecordRepository { - public EfCoreUserTextMessageRecordRepository( + public EfCoreTextChatMessageRecordRepository( IDbContextProvider dbContextProvider) : base(dbContextProvider) { } - public async virtual Task> GetHistoryMessagesAsync( + public async virtual Task> GetHistoryMessagesAsync( string conversationId, int maxResultCount = 0, CancellationToken cancellationToken = default) @@ -26,6 +26,7 @@ public class EfCoreUserTextMessageRecordRepository : EfCoreRepository x.ConversationId == conversationId) .OrderByDescending(x => x.CreationTime) .Take(maxResultCount) + .OrderBy(x => x.CreationTime) .ToListAsync(GetCancellationToken(cancellationToken)); } } diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/ValueConversions/ChatRoleValueConverter.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/ValueConversions/ChatRoleValueConverter.cs new file mode 100644 index 000000000..c4a1faf2c --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/ValueConversions/ChatRoleValueConverter.cs @@ -0,0 +1,11 @@ +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Microsoft.Extensions.AI; + +namespace LINGYUN.Abp.AIManagement.EntityFrameworkCore.ValueConversions; +public class ChatRoleValueConverter(ConverterMappingHints? mappingHints = null) : ValueConverter( + value => value.Value, + value => new ChatRole(value), + mappingHints + ) +{ +}