send by group

This commit is contained in:
Jicheng Lu 2025-02-13 14:52:46 -06:00
parent 521ce62c41
commit 5c6e499051
11 changed files with 242 additions and 41 deletions

View file

@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>$(TargetFramework)</TargetFramework>

View file

@ -1,5 +1,4 @@
using BotSharp.Abstraction.Loggers;
using BotSharp.Abstraction.Routing;
using BotSharp.Abstraction.Interpreters.Settings;
using BotSharp.Core.Crontab.Abstraction;
using BotSharp.Plugin.ChatHub.Hooks;
using Microsoft.Extensions.Configuration;
@ -18,6 +17,10 @@ public class ChatHubPlugin : IBotSharpPlugin
public void RegisterDI(IServiceCollection services, IConfiguration config)
{
var settings = new ChatHubSettings();
config.Bind("ChatHub", settings);
services.AddSingleton(x => settings);
// Register hooks
services.AddScoped<IConversationHook, ChatHubConversationHook>();
services.AddScoped<IConversationHook, StreamingLogHook>();

View file

@ -0,0 +1,7 @@
namespace BotSharp.Plugin.ChatHub.Enums;
public static class EventDispatchType
{
public const string Group = "group";
public const string User = "user";
}

View file

@ -9,8 +9,9 @@ public class ChatHubConversationHook : ConversationHookBase
private readonly IHubContext<SignalRHub> _chatHub;
private readonly IUserIdentity _user;
private readonly BotSharpOptions _options;
private readonly ChatHubSettings _settings;
#region Event
#region Events
private const string INIT_CLIENT_CONVERSATION = "OnConversationInitFromClient";
private const string RECEIVE_CLIENT_MESSAGE = "OnMessageReceivedFromClient";
private const string RECEIVE_ASSISTANT_MESSAGE = "OnMessageReceivedFromAssistant";
@ -23,12 +24,14 @@ public class ChatHubConversationHook : ConversationHookBase
IServiceProvider services,
IHubContext<SignalRHub> chatHub,
BotSharpOptions options,
ChatHubSettings settings,
IUserIdentity user)
{
_services = services;
_chatHub = chatHub;
_user = user;
_options = options;
_settings = settings;
Priority = -1; // Make sure this hook is the top one.
}
@ -42,7 +45,8 @@ public class ChatHubConversationHook : ConversationHookBase
var user = await userService.GetUser(conv.User.Id);
conv.User = UserViewModel.FromUser(user);
await InitClientConversation(conv);
//await InitClientConversation(conv);
await InitClientConversation(conv.Id, conv);
await base.OnConversationInitialized(conversation);
}
@ -63,7 +67,8 @@ public class ChatHubConversationHook : ConversationHookBase
Text = !string.IsNullOrEmpty(message.SecondaryContent) ? message.SecondaryContent : message.Content,
Sender = UserViewModel.FromUser(sender)
};
await ReceiveClientMessage(model);
await ReceiveClientMessage(conv.ConversationId, model);
//await ReceiveClientMessage(model);
// Send typing-on to client
var action = new ConversationSenderActionModel
@ -71,7 +76,9 @@ public class ChatHubConversationHook : ConversationHookBase
ConversationId = conv.ConversationId,
SenderAction = SenderActionEnum.TypingOn
};
await GenerateSenderAction(action);
await GenerateSenderAction(conv.ConversationId, action);
//await GenerateSenderAction(action);
await base.OnMessageReceived(message);
}
@ -84,7 +91,9 @@ public class ChatHubConversationHook : ConversationHookBase
SenderAction = SenderActionEnum.TypingOn,
Indication = message.Indication
};
await GenerateSenderAction(action);
await GenerateSenderAction(conv.ConversationId, action);
//await GenerateSenderAction(action);
await base.OnFunctionExecuting(message);
}
@ -121,8 +130,10 @@ public class ChatHubConversationHook : ConversationHookBase
SenderAction = SenderActionEnum.TypingOff
};
await GenerateSenderAction(action);
await ReceiveAssistantMessage(json);
await GenerateSenderAction(conv.ConversationId, action);
await ReceiveAssistantMessage(conv.ConversationId, json);
//await GenerateSenderAction(action);
//await ReceiveAssistantMessage(json);
await base.OnResponseGenerated(message);
}
@ -146,7 +157,8 @@ public class ChatHubConversationHook : ConversationHookBase
}
}, _options.JsonSerializerOptions);
await GenerateNotification(json);
await GenerateNotification(conv.ConversationId, json);
//await GenerateNotification(json);
await base.OnNotificationGenerated(message);
}
@ -158,7 +170,9 @@ public class ChatHubConversationHook : ConversationHookBase
ConversationId = conversationId,
MessageId = messageId
};
await DeleteMessage(model);
await DeleteMessage(conversationId, model);
//await DeleteMessage(model);
await base.OnMessageDeleted(conversationId, messageId);
}
@ -198,5 +212,81 @@ public class ChatHubConversationHook : ConversationHookBase
{
await _chatHub.Clients.User(_user.Id).SendAsync(GENERATE_NOTIFICATION, json);
}
private async Task InitClientConversation(string conversationId, ConversationViewModel conversation)
{
if (_settings.EventDispatchBy == EventDispatchType.Group)
{
await _chatHub.Clients.Group(conversationId).SendAsync(INIT_CLIENT_CONVERSATION, conversation);
}
else
{
await _chatHub.Clients.User(_user.Id).SendAsync(INIT_CLIENT_CONVERSATION, conversation);
}
}
private async Task ReceiveClientMessage(string conversationId, ChatResponseModel model)
{
if (_settings.EventDispatchBy == EventDispatchType.Group)
{
await _chatHub.Clients.Group(conversationId).SendAsync(RECEIVE_CLIENT_MESSAGE, model);
}
else
{
await _chatHub.Clients.User(_user.Id).SendAsync(RECEIVE_CLIENT_MESSAGE, model);
}
}
private async Task ReceiveAssistantMessage(string conversationId, string? json)
{
if (_settings.EventDispatchBy == EventDispatchType.Group)
{
await _chatHub.Clients.Group(conversationId).SendAsync(RECEIVE_ASSISTANT_MESSAGE, json);
}
else
{
await _chatHub.Clients.User(_user.Id).SendAsync(RECEIVE_ASSISTANT_MESSAGE, json);
}
}
private async Task GenerateSenderAction(string conversationId, ConversationSenderActionModel action)
{
if (_settings.EventDispatchBy == EventDispatchType.Group)
{
await _chatHub.Clients.Group(conversationId).SendAsync(GENERATE_SENDER_ACTION, action);
}
else
{
await _chatHub.Clients.User(_user.Id).SendAsync(GENERATE_SENDER_ACTION, action);
}
}
private async Task DeleteMessage(string conversationId, ChatResponseModel model)
{
if (_settings.EventDispatchBy == EventDispatchType.Group)
{
await _chatHub.Clients.Group(conversationId).SendAsync(DELETE_MESSAGE, model);
}
else
{
await _chatHub.Clients.User(_user.Id).SendAsync(DELETE_MESSAGE, model);
}
}
private async Task GenerateNotification(string conversationId, string? json)
{
if (_settings.EventDispatchBy == EventDispatchType.Group)
{
await _chatHub.Clients.Group(conversationId).SendAsync(GENERATE_NOTIFICATION, json);
}
else
{
await _chatHub.Clients.User(_user.Id).SendAsync(GENERATE_NOTIFICATION, json);
}
}
#endregion
}

View file

@ -11,18 +11,25 @@ public class ChatHubCrontabHook : ICrontabHook
private readonly IUserIdentity _user;
private readonly IConversationStorage _storage;
private readonly BotSharpOptions _options;
private readonly ChatHubSettings _settings;
#region Events
private const string GENERATE_NOTIFICATION = "OnNotificationGenerated";
#endregion
public ChatHubCrontabHook(IServiceProvider services,
IHubContext<SignalRHub> chatHub,
IUserIdentity user,
IConversationStorage storage,
BotSharpOptions options)
BotSharpOptions options,
ChatHubSettings settings)
{
_services = services;
_chatHub = chatHub;
_user = user;
_storage = storage;
_options = options;
_settings = settings;
}
public async Task OnCronTriggered(CrontabItem item)
@ -41,6 +48,13 @@ public class ChatHubCrontabHook : ICrontabHook
}
}, _options.JsonSerializerOptions);
await _chatHub.Clients.User(item.UserId).SendAsync("OnNotificationGenerated", json);
if (_settings.EventDispatchBy == EventDispatchType.Group)
{
await _chatHub.Clients.Group(item.ConversationId).SendAsync(GENERATE_NOTIFICATION, json);
}
else
{
await _chatHub.Clients.User(item.UserId).SendAsync(GENERATE_NOTIFICATION, json);
}
}
}

View file

@ -9,6 +9,7 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR
private readonly ConversationSetting _convSettings;
private readonly BotSharpOptions _options;
private readonly JsonSerializerOptions _localJsonOptions;
private readonly ChatHubSettings _settings;
private readonly IServiceProvider _services;
private readonly IHubContext<SignalRHub> _chatHub;
private readonly IConversationStateService _state;
@ -16,7 +17,7 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR
private readonly IAgentService _agentService;
private readonly IRoutingContext _routingCtx;
#region Event
#region Events
private const string CONTENT_LOG_GENERATED = "OnConversationContentLogGenerated";
private const string STATE_LOG_GENERATED = "OnConversateStateLogGenerated";
private const string AGENT_QUEUE_CHANGED = "OnAgentQueueChanged";
@ -26,6 +27,7 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR
public StreamingLogHook(
ConversationSetting convSettings,
BotSharpOptions options,
ChatHubSettings settings,
IServiceProvider serivces,
IHubContext<SignalRHub> chatHub,
IConversationStateService state,
@ -35,6 +37,7 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR
{
_convSettings = convSettings;
_options = options;
_settings = settings;
_services = serivces;
_chatHub = chatHub;
_state = state;
@ -58,7 +61,7 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR
Source = ContentLogSource.UserInput,
Log = log
};
await SendContentLog(input);
await SendContentLog(conversationId, input);
}
public override async Task OnPostbackMessageReceived(RoleDialogModel message, PostbackMessageModel replyMsg)
@ -76,7 +79,7 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR
Source = ContentLogSource.UserInput,
Log = log
};
await SendContentLog(input);
await SendContentLog(conversationId, input);
}
public async Task OnRenderingTemplate(Agent agent, string name, string content)
@ -98,7 +101,7 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR
Source = ContentLogSource.HardRule,
Log = log
};
await SendContentLog(input);
await SendContentLog(conversationId, input);
}
public async Task BeforeGenerating(Agent agent, List<RoleDialogModel> conversations)
@ -126,7 +129,7 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR
Source = ContentLogSource.FunctionCall,
Log = log
};
await SendContentLog(input);
await SendContentLog(conversationId, input);
}
public override async Task OnFunctionExecuted(RoleDialogModel message)
@ -147,7 +150,7 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR
Source = ContentLogSource.FunctionCall,
Log = log
};
await SendContentLog(input);
await SendContentLog(conversationId, input);
}
/// <summary>
@ -174,7 +177,7 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR
Source = ContentLogSource.Prompt,
Log = log
};
await SendContentLog(input);
await SendContentLog(conversationId, input);
}
/// <summary>
@ -208,7 +211,7 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR
Source = ContentLogSource.AgentResponse,
Log = log
};
await SendContentLog(input);
await SendContentLog(conversationId, input);
}
}
@ -226,7 +229,7 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR
Source = ContentLogSource.FunctionCall,
Log = log
};
await SendContentLog(input);
await SendContentLog(conversationId, input);
}
public override async Task OnConversationEnding(RoleDialogModel message)
@ -243,7 +246,7 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR
Source = ContentLogSource.FunctionCall,
Log = log
};
await SendContentLog(input);
await SendContentLog(conversationId, input);
}
public override async Task OnBreakpointUpdated(string conversationId, bool resetStates)
@ -271,7 +274,7 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR
},
Log = log
};
await SendContentLog(input);
await SendContentLog(conversationId, input);
}
public override async Task OnStateChanged(StateChangeModel stateChange)
@ -281,7 +284,7 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR
if (stateChange == null) return;
await SendStateChange(stateChange);
await SendStateChange(conversationId, stateChange);
}
#endregion
@ -310,7 +313,7 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR
Source = ContentLogSource.HardRule,
Log = log
};
await SendContentLog(input);
await SendContentLog(conversationId, input);
}
public async Task OnAgentDequeued(string agentId, string currentAgentId, string? reason = null)
@ -338,7 +341,7 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR
Source = ContentLogSource.HardRule,
Log = log
};
await SendContentLog(input);
await SendContentLog(conversationId, input);
}
public async Task OnAgentReplaced(string fromAgentId, string toAgentId, string? reason = null)
@ -366,7 +369,7 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR
Source = ContentLogSource.HardRule,
Log = log
};
await SendContentLog(input);
await SendContentLog(conversationId, input);
}
public async Task OnAgentQueueEmptied(string agentId, string? reason = null)
@ -391,7 +394,7 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR
Source = ContentLogSource.HardRule,
Log = log
};
await SendContentLog(input);
await SendContentLog(conversationId, input);
}
public async Task OnRoutingInstructionReceived(FunctionCallFromLlm instruct, RoleDialogModel message)
@ -410,7 +413,7 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR
Source = ContentLogSource.AgentResponse,
Log = log
};
await SendContentLog(input);
await SendContentLog(conversationId, input);
}
public async Task OnRoutingInstructionRevised(FunctionCallFromLlm instruct, RoleDialogModel message)
@ -428,31 +431,82 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR
Source = ContentLogSource.HardRule,
Log = log
};
await SendContentLog(input);
await SendContentLog(conversationId, input);
}
#endregion
#region Private methods
private async Task SendContentLog(ContentLogInputModel input)
//private async Task SendContentLog(ContentLogInputModel input)
//{
// await _chatHub.Clients.User(_user.Id).SendAsync(CONTENT_LOG_GENERATED, BuildContentLog(input));
//}
//private async Task SendStateLog(string conversationId, string agentId, Dictionary<string, string> states, RoleDialogModel message)
//{
// await _chatHub.Clients.User(_user.Id).SendAsync(STATE_LOG_GENERATED, BuildStateLog(conversationId, agentId, states, message));
//}
//private async Task SendAgentQueueLog(string conversationId, string log)
//{
// await _chatHub.Clients.User(_user.Id).SendAsync(AGENT_QUEUE_CHANGED, BuildAgentQueueChangedLog(conversationId, log));
//}
//private async Task SendStateChange(StateChangeModel stateChange)
//{
// await _chatHub.Clients.User(_user.Id).SendAsync(STATE_CHANGED, BuildStateChangeLog(stateChange));
//}
private async Task SendContentLog(string conversationId, ContentLogInputModel input)
{
if (_settings.EventDispatchBy == EventDispatchType.Group)
{
await _chatHub.Clients.Group(conversationId).SendAsync(CONTENT_LOG_GENERATED, BuildContentLog(input));
}
else
{
await _chatHub.Clients.User(_user.Id).SendAsync(CONTENT_LOG_GENERATED, BuildContentLog(input));
}
}
private async Task SendStateLog(string conversationId, string agentId, Dictionary<string, string> states, RoleDialogModel message)
{
if (_settings.EventDispatchBy == EventDispatchType.Group)
{
await _chatHub.Clients.Group(conversationId).SendAsync(STATE_LOG_GENERATED, BuildStateLog(conversationId, agentId, states, message));
}
else
{
await _chatHub.Clients.User(_user.Id).SendAsync(STATE_LOG_GENERATED, BuildStateLog(conversationId, agentId, states, message));
}
}
private async Task SendAgentQueueLog(string conversationId, string log)
{
if (_settings.EventDispatchBy == EventDispatchType.Group)
{
await _chatHub.Clients.Group(conversationId).SendAsync(AGENT_QUEUE_CHANGED, BuildAgentQueueChangedLog(conversationId, log));
}
else
{
await _chatHub.Clients.User(_user.Id).SendAsync(AGENT_QUEUE_CHANGED, BuildAgentQueueChangedLog(conversationId, log));
}
}
private async Task SendStateChange(StateChangeModel stateChange)
private async Task SendStateChange(string conversationId, StateChangeModel stateChange)
{
if (_settings.EventDispatchBy == EventDispatchType.Group)
{
await _chatHub.Clients.Group(conversationId).SendAsync(STATE_CHANGED, BuildStateChangeLog(stateChange));
}
else
{
await _chatHub.Clients.User(_user.Id).SendAsync(STATE_CHANGED, BuildStateChangeLog(stateChange));
}
}
private string BuildContentLog(ContentLogInputModel input)
{

View file

@ -9,18 +9,25 @@ public class WelcomeHook : ConversationHookBase
private readonly IUserIdentity _user;
private readonly IConversationStorage _storage;
private readonly BotSharpOptions _options;
private readonly ChatHubSettings _settings;
#region Events
private const string RECEIVE_ASSISTANT_MESSAGE = "OnMessageReceivedFromAssistant";
#endregion
public WelcomeHook(IServiceProvider services,
IHubContext<SignalRHub> chatHub,
IUserIdentity user,
IConversationStorage storage,
BotSharpOptions options)
BotSharpOptions options,
ChatHubSettings settings)
{
_services = services;
_chatHub = chatHub;
_user = user;
_storage = storage;
_options = options;
_settings = settings;
}
public override async Task OnUserAgentConnectedInitially(Conversation conversation)
@ -71,7 +78,14 @@ public class WelcomeHook : ConversationHookBase
_storage.Append(conversation.Id, dialog);
await _chatHub.Clients.User(_user.Id).SendAsync("OnMessageReceivedFromAssistant", json);
if (_settings.EventDispatchBy == EventDispatchType.Group)
{
await _chatHub.Clients.Group(conversation.Id).SendAsync(RECEIVE_ASSISTANT_MESSAGE, json);
}
else
{
await _chatHub.Clients.User(_user.Id).SendAsync(RECEIVE_ASSISTANT_MESSAGE, json);
}
}
}

View file

@ -0,0 +1,6 @@
namespace BotSharp.Plugin.ChatHub.Settings;
public class ChatHubSettings
{
public string EventDispatchBy { get; set; } = EventDispatchType.Group;
}

View file

@ -33,6 +33,13 @@ public class SignalRHub : Hub
if (!string.IsNullOrEmpty(conversationId))
{
_logger.LogInformation($"Connection {Context.ConnectionId} is with conversation {conversationId}");
var settings = _services.GetRequiredService<ChatHubSettings>();
if (settings.EventDispatchBy == EventDispatchType.Group)
{
await Groups.AddToGroupAsync(Context.ConnectionId, conversationId);
}
var conv = await convService.GetConversation(conversationId);
if (conv != null)
{

View file

@ -32,3 +32,5 @@ global using BotSharp.Abstraction.Messaging;
global using BotSharp.Abstraction.Messaging.Enums;
global using BotSharp.Abstraction.Messaging.Models.RichContent;
global using BotSharp.Abstraction.Templating;
global using BotSharp.Plugin.ChatHub.Settings;
global using BotSharp.Plugin.ChatHub.Enums;

View file

@ -222,6 +222,10 @@
"Enabled": false
},
"ChatHub": {
"EventDispatchBy": "group"
},
"SharpCache": {
"Enabled": true,
"CacheType": 1,