BotSharp/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/ChatHubConversationHook.cs

227 lines
8.4 KiB
C#
Raw Normal View History

2025-05-07 21:34:39 +00:00
using BotSharp.Abstraction.Conversations.Dtos;
2025-07-28 22:41:28 +00:00
using BotSharp.Abstraction.Conversations.Enums;
2025-07-15 21:52:13 +00:00
using BotSharp.Abstraction.Routing.Enums;
2025-08-04 19:21:11 +00:00
using BotSharp.Abstraction.Routing.Models;
2024-11-04 22:44:41 +00:00
using BotSharp.Abstraction.SideCar;
2025-05-07 21:34:39 +00:00
using BotSharp.Abstraction.Users.Dtos;
2023-11-22 16:27:55 +00:00
using Microsoft.AspNetCore.SignalR;
using System;
2025-07-28 22:41:28 +00:00
using System.Runtime.CompilerServices;
2023-11-22 16:27:55 +00:00
namespace BotSharp.Plugin.ChatHub.Hooks;
public class ChatHubConversationHook : ConversationHookBase
{
private readonly IServiceProvider _services;
private readonly IHubContext<SignalRHub> _chatHub;
2025-02-27 22:19:43 +00:00
private readonly ILogger<ChatHubConversationHook> _logger;
2023-11-28 03:19:07 +00:00
private readonly IUserIdentity _user;
2024-07-23 22:44:39 +00:00
private readonly BotSharpOptions _options;
2025-02-13 20:52:46 +00:00
private readonly ChatHubSettings _settings;
2024-07-23 22:44:39 +00:00
public ChatHubConversationHook(
IServiceProvider services,
2023-11-28 03:19:07 +00:00
IHubContext<SignalRHub> chatHub,
2025-02-27 22:19:43 +00:00
ILogger<ChatHubConversationHook> logger,
2024-03-18 19:36:41 +00:00
BotSharpOptions options,
2025-02-13 20:52:46 +00:00
ChatHubSettings settings,
2023-11-28 03:19:07 +00:00
IUserIdentity user)
2023-11-22 16:27:55 +00:00
{
_services = services;
_chatHub = chatHub;
2025-02-27 22:19:43 +00:00
_logger = logger;
2023-11-28 03:19:07 +00:00
_user = user;
2024-03-18 19:36:41 +00:00
_options = options;
2025-02-13 20:52:46 +00:00
_settings = settings;
2024-12-12 01:16:59 +00:00
Priority = -1; // Make sure this hook is the top one.
2023-11-22 16:27:55 +00:00
}
public override async Task OnConversationInitialized(Conversation conversation)
{
2024-10-29 19:59:46 +00:00
if (!AllowSendingMessage()) return;
2023-11-22 16:27:55 +00:00
var userService = _services.GetRequiredService<IUserService>();
2025-05-07 21:34:39 +00:00
var conv = ConversationDto.FromSession(conversation);
2023-11-22 16:27:55 +00:00
var user = await userService.GetUser(conv.User.Id);
2025-05-07 21:34:39 +00:00
conv.User = UserDto.FromUser(user);
2023-11-22 16:27:55 +00:00
2025-07-28 22:41:28 +00:00
await SendEvent(ChatEvent.OnConversationInitFromClient, conv.Id, conv);
2023-11-22 16:27:55 +00:00
await base.OnConversationInitialized(conversation);
}
public override async Task OnMessageReceived(RoleDialogModel message)
{
2024-10-29 19:59:46 +00:00
if (!AllowSendingMessage()) return;
2023-11-22 16:27:55 +00:00
var conv = _services.GetRequiredService<IConversationService>();
var userService = _services.GetRequiredService<IUserService>();
var sender = await userService.GetMyProfile();
// Update console conversation UI for CSR
2025-05-07 21:34:39 +00:00
var model = new ChatResponseDto()
2023-11-22 16:27:55 +00:00
{
ConversationId = conv.ConversationId,
MessageId = message.MessageId,
2024-10-10 17:55:44 +00:00
Payload = message.Payload,
2024-04-23 21:27:09 +00:00
Text = !string.IsNullOrEmpty(message.SecondaryContent) ? message.SecondaryContent : message.Content,
2025-05-07 21:34:39 +00:00
Sender = UserDto.FromUser(sender)
2024-07-23 22:44:39 +00:00
};
2025-07-28 22:41:28 +00:00
await SendEvent(ChatEvent.OnMessageReceivedFromClient, conv.ConversationId, model);
2023-11-22 16:27:55 +00:00
2024-02-16 21:58:39 +00:00
// Send typing-on to client
2024-07-23 22:44:39 +00:00
var action = new ConversationSenderActionModel
2024-02-16 21:58:39 +00:00
{
ConversationId = conv.ConversationId,
SenderAction = SenderActionEnum.TypingOn
2024-07-23 22:44:39 +00:00
};
2025-07-28 22:41:28 +00:00
await SendEvent(ChatEvent.OnSenderActionGenerated, conv.ConversationId, action);
2023-11-22 16:27:55 +00:00
await base.OnMessageReceived(message);
}
2025-08-04 19:21:11 +00:00
public override async Task OnFunctionExecuting(RoleDialogModel message, InvokeFunctionOptions? options = null)
2024-05-01 15:45:12 +00:00
{
2025-08-04 19:21:11 +00:00
await base.OnFunctionExecuting(message, options);
2024-05-01 15:45:12 +00:00
}
2024-03-18 19:36:41 +00:00
public override async Task OnPostbackMessageReceived(RoleDialogModel message, PostbackMessageModel replyMsg)
{
await this.OnMessageReceived(message);
}
2023-11-22 16:27:55 +00:00
public override async Task OnResponseGenerated(RoleDialogModel message)
{
if (!AllowSendingMessage()) return;
2024-10-29 19:59:46 +00:00
2023-11-22 16:27:55 +00:00
var conv = _services.GetRequiredService<IConversationService>();
2025-03-14 22:04:54 +00:00
var state = _services.GetRequiredService<IConversationStateService>();
2025-09-06 17:16:32 +00:00
var sender = new UserDto
{
FirstName = "AI",
LastName = "Assistant",
Role = AgentRole.Assistant
};
2025-07-28 22:41:28 +00:00
var data = new ChatResponseDto()
2023-11-22 16:27:55 +00:00
{
ConversationId = conv.ConversationId,
MessageId = message.MessageId,
2025-09-08 19:29:08 +00:00
MessageLabel = message.MessageLabel,
2024-04-23 21:27:09 +00:00
Text = !string.IsNullOrEmpty(message.SecondaryContent) ? message.SecondaryContent : message.Content,
2024-02-28 16:16:46 +00:00
Function = message.FunctionName,
2024-04-23 21:27:09 +00:00
RichContent = message.SecondaryRichContent ?? message.RichContent,
2024-02-12 21:49:18 +00:00
Data = message.Data,
2025-03-14 22:04:54 +00:00
States = state.GetStates(),
IsStreaming = message.IsStreaming,
2025-09-06 17:16:32 +00:00
Sender = sender
2024-07-23 22:44:39 +00:00
};
2024-02-15 20:43:36 +00:00
// Send type-off to client
2025-08-01 19:41:17 +00:00
var action = new ConversationSenderActionModel
{
2025-08-01 19:41:17 +00:00
ConversationId = conv.ConversationId,
SenderAction = SenderActionEnum.TypingOff
};
await SendEvent(ChatEvent.OnSenderActionGenerated, conv.ConversationId, action);
2025-07-28 22:41:28 +00:00
await SendEvent(ChatEvent.OnMessageReceivedFromAssistant, conv.ConversationId, data);
2025-09-05 06:49:14 +00:00
var wrapper = message.AdditionalMessageWrapper;
if (wrapper?.SendingInterval > 0 && wrapper?.Messages?.Count > 0)
{
action.SenderAction = SenderActionEnum.TypingOn;
await SendEvent(ChatEvent.OnSenderActionGenerated, conv.ConversationId, action);
2025-09-05 06:49:14 +00:00
foreach (var item in wrapper.Messages)
{
2025-09-06 17:16:32 +00:00
if (!string.IsNullOrWhiteSpace(item.Indication))
{
data = new ChatResponseDto
{
ConversationId = conv.ConversationId,
MessageId = item.MessageId,
2025-09-08 19:29:08 +00:00
MessageLabel = item.MessageLabel,
2025-09-06 17:16:32 +00:00
Indication = item.Indication,
Sender = sender
};
await SendEvent(ChatEvent.OnIndicationReceived, conv.ConversationId, data);
}
2025-09-05 06:49:14 +00:00
await Task.Delay(wrapper.SendingInterval);
data = new ChatResponseDto
{
ConversationId = conv.ConversationId,
MessageId = item.MessageId,
2025-09-09 06:58:46 +00:00
MessageLabel = item.MessageLabel,
Text = !string.IsNullOrEmpty(item.SecondaryContent) ? item.SecondaryContent : item.Content,
Function = item.FunctionName,
RichContent = item.SecondaryRichContent ?? item.RichContent,
Data = item.Data,
States = state.GetStates(),
IsAppend = true,
2025-09-06 17:16:32 +00:00
Sender = sender
};
await SendEvent(ChatEvent.OnMessageReceivedFromAssistant, conv.ConversationId, data);
}
action.SenderAction = SenderActionEnum.TypingOff;
await SendEvent(ChatEvent.OnSenderActionGenerated, conv.ConversationId, action);
}
2024-02-16 21:58:39 +00:00
await base.OnResponseGenerated(message);
2024-01-26 21:47:06 +00:00
}
2024-03-11 20:32:13 +00:00
2024-10-07 21:35:39 +00:00
public override async Task OnNotificationGenerated(RoleDialogModel message)
{
var conv = _services.GetRequiredService<IConversationService>();
2025-07-28 22:41:28 +00:00
var data = new ChatResponseDto()
2024-10-07 21:35:39 +00:00
{
ConversationId = conv.ConversationId,
MessageId = message.MessageId,
Text = !string.IsNullOrEmpty(message.SecondaryContent) ? message.SecondaryContent : message.Content,
Function = message.FunctionName,
RichContent = message.SecondaryRichContent ?? message.RichContent,
Data = message.Data,
2025-05-07 21:34:39 +00:00
Sender = new()
2024-10-07 21:35:39 +00:00
{
FirstName = "AI",
LastName = "Assistant",
Role = AgentRole.Assistant
}
2025-07-28 22:41:28 +00:00
};
2024-10-07 21:35:39 +00:00
2025-07-28 22:41:28 +00:00
await SendEvent(ChatEvent.OnNotificationGenerated, conv.ConversationId, data);
2024-10-07 21:35:39 +00:00
await base.OnNotificationGenerated(message);
}
2024-03-11 20:32:13 +00:00
public override async Task OnMessageDeleted(string conversationId, string messageId)
{
2025-05-07 21:34:39 +00:00
var model = new ChatResponseDto
2024-03-11 20:32:13 +00:00
{
ConversationId = conversationId,
MessageId = messageId
2024-07-23 22:44:39 +00:00
};
2025-02-13 20:52:46 +00:00
2025-07-28 22:41:28 +00:00
await SendEvent(ChatEvent.OnMessageDeleted, conversationId, model);
2024-03-11 20:32:13 +00:00
await base.OnMessageDeleted(conversationId, messageId);
}
2024-07-23 22:44:39 +00:00
#region Private methods
2024-10-29 19:59:46 +00:00
private bool AllowSendingMessage()
{
2024-11-04 22:51:02 +00:00
var sidecar = _services.GetService<IConversationSideCar>();
2025-07-09 16:14:47 +00:00
return sidecar == null || !sidecar.IsEnabled;
2024-10-29 19:59:46 +00:00
}
2025-07-28 22:41:28 +00:00
private async Task SendEvent<T>(string @event, string conversationId, T data, [CallerMemberName] string callerName = "")
2025-02-13 20:52:46 +00:00
{
2025-07-28 22:41:28 +00:00
var user = _services.GetRequiredService<IUserIdentity>();
2025-08-06 16:27:26 +00:00
var json = JsonSerializer.Serialize(data, _options.JsonSerializerOptions);
await EventEmitter.SendChatEvent(_services, _logger, @event, conversationId, user?.Id, json, nameof(ChatHubConversationHook), callerName);
2025-02-13 20:52:46 +00:00
}
2024-07-23 22:44:39 +00:00
#endregion
2023-11-22 16:27:55 +00:00
}