diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
new file mode 100644
index 00000000..ed4cf565
--- /dev/null
+++ b/.github/workflows/build.yml
@@ -0,0 +1,44 @@
+name: build
+
+on:
+ push:
+ branches:
+ - master
+ pull_request:
+ branches:
+ - master
+
+jobs:
+ build:
+ strategy:
+ matrix:
+ os:
+ - ubuntu-latest
+ - windows-latest
+ - macos-latest
+ runs-on: ${{matrix.os}}
+ steps:
+ - uses: actions/checkout@v1
+ - name: Setup .NET Core
+ uses: actions/setup-dotnet@v3
+ with:
+ dotnet-version: '8.0.x'
+ - name: Set env
+ run: |
+ echo "DOTNET_CLI_TELEMETRY_OPTOUT=1" >> $GITHUB_ENV
+ echo "DOTNET_hostBuilder:reloadConfigOnChange=false" >> $GITHUB_ENV
+ - name: Install required workloads
+ run: |
+ dotnet workload install aspire --source https://aka.ms/dotnet8/nuget/index.json --source https://api.nuget.org/v3/index.json
+ - name: Clean
+ run: |
+ dotnet clean ./BotSharp.sln --configuration Release
+ dotnet nuget locals all --clear
+ - name: Build
+ run: dotnet build ./BotSharp.sln -c Release
+ - name: Test
+ run: |
+ cd ./tests/UnitTest
+ dotnet test --logger "console;verbosity=detailed"
+ cd ../BotSharp.Plugin.SemanticKernel.UnitTests
+ dotnet test --logger "console;verbosity=detailed"
\ No newline at end of file
diff --git a/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationService.cs b/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationService.cs
index 01aba146..6d84103e 100644
--- a/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationService.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationService.cs
@@ -71,5 +71,5 @@ public interface IConversationService
/// conversation limit
/// if pre-loading, then keys are not filter by the search query
///
- Task> GetConversationStateSearhKeys(string query, int convlimit = 100, int keyLimit = 10, bool preLoad = false);
+ Task> GetConversationStateSearhKeys(string query, int convLimit = 100, int keyLimit = 10, bool preload = false);
}
diff --git a/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/Conversation.cs b/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/Conversation.cs
index a485a9c4..eb5adacd 100644
--- a/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/Conversation.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/Conversation.cs
@@ -58,13 +58,17 @@ public class DialogElement
[JsonPropertyName("payload")]
public string? Payload { get; set; }
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ [JsonPropertyName("data")]
+ public object? Data { get; set; }
+
public DialogElement()
{
}
public DialogElement(DialogMetaData meta, string content, string? richContent = null,
- string? secondaryContent = null, string? secondaryRichContent = null, string? payload = null)
+ string? secondaryContent = null, string? secondaryRichContent = null, string? payload = null, object? data = null)
{
MetaData = meta;
Content = content;
@@ -72,6 +76,7 @@ public class DialogElement
SecondaryContent = secondaryContent;
SecondaryRichContent = secondaryRichContent;
Payload = payload;
+ Data = data;
}
public override string ToString()
diff --git a/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs b/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs
index a58600e5..06582d45 100644
--- a/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs
@@ -148,7 +148,7 @@ public interface IBotSharpRepository : IHaveServiceProvider
=> throw new NotImplementedException();
List TruncateConversation(string conversationId, string messageId, bool cleanLog = false)
=> throw new NotImplementedException();
- List GetConversationStateSearchKeys(int messageLowerLimit = 2, int convUpperlimit = 100)
+ List GetConversationStateSearchKeys(int messageLowerLimit = 2, int convUpperLimit = 100)
=> throw new NotImplementedException();
#endregion
diff --git a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.cs b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.cs
index 63a257e5..0cec37d8 100644
--- a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.cs
+++ b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.cs
@@ -222,17 +222,17 @@ public partial class ConversationService : IConversationService
_state.Save();
}
- public async Task> GetConversationStateSearhKeys(string query, int convlimit = 100, int keyLimit = 10, bool preLoad = false)
+ public async Task> GetConversationStateSearhKeys(string query, int convLimit = 100, int keyLimit = 10, bool preload = false)
{
var keys = new List();
- if (!preLoad && string.IsNullOrWhiteSpace(query))
+ if (!preload && string.IsNullOrWhiteSpace(query))
{
return keys;
}
var db = _services.GetRequiredService();
- keys = db.GetConversationStateSearchKeys(convUpperlimit: convlimit);
- keys = preLoad ? keys : keys.Where(x => x.Contains(query, StringComparison.OrdinalIgnoreCase)).ToList();
+ keys = db.GetConversationStateSearchKeys(convUpperLimit: convLimit);
+ keys = preload ? keys : keys.Where(x => x.Contains(query, StringComparison.OrdinalIgnoreCase)).ToList();
return keys.OrderBy(x => x).Take(keyLimit).ToList();
}
}
diff --git a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStateService.cs b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStateService.cs
index 52dcd032..7cdd40bd 100644
--- a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStateService.cs
+++ b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStateService.cs
@@ -15,6 +15,7 @@
******************************************************************************/
using BotSharp.Abstraction.Conversations.Enums;
+using BotSharp.Abstraction.SideCar;
namespace BotSharp.Core.Conversations.Services;
@@ -26,6 +27,7 @@ public class ConversationStateService : IConversationStateService, IDisposable
private readonly ILogger _logger;
private readonly IServiceProvider _services;
private readonly IBotSharpRepository _db;
+ private readonly IConversationSideCar _sidecar;
private string _conversationId;
///
/// States in the current round of conversation
@@ -39,10 +41,12 @@ public class ConversationStateService : IConversationStateService, IDisposable
public ConversationStateService(
IServiceProvider services,
IBotSharpRepository db,
+ IConversationSideCar sidecar,
ILogger logger)
{
_services = services;
_db = db;
+ _sidecar = sidecar;
_logger = logger;
_curStates = new ConversationState();
_historyStates = new ConversationState();
@@ -138,15 +142,20 @@ public class ConversationStateService : IConversationStateService, IDisposable
_conversationId = !isReadOnly ? conversationId : null;
Reset();
- var routingCtx = _services.GetRequiredService();
- var curMsgId = routingCtx.MessageId;
+ var endNodes = new Dictionary();
+ if (_sidecar?.IsEnabled() == true)
+ {
+ return endNodes;
+ }
_historyStates = _db.GetConversationStates(conversationId);
+ if (_historyStates.IsNullOrEmpty())
+ {
+ return endNodes;
+ }
- var endNodes = new Dictionary();
-
- if (_historyStates.IsNullOrEmpty()) return endNodes;
-
+ var routingCtx = _services.GetRequiredService();
+ var curMsgId = routingCtx.MessageId;
var dialogs = _db.GetConversationDialogs(conversationId);
var userDialogs = dialogs.Where(x => x.MetaData?.Role == AgentRole.User)
.GroupBy(x => x.MetaData?.MessageId)
@@ -210,7 +219,7 @@ public class ConversationStateService : IConversationStateService, IDisposable
public void Save()
{
- if (_conversationId == null)
+ if (_conversationId == null || _sidecar?.IsEnabled() == true)
{
Reset();
return;
diff --git a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStorage.cs b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStorage.cs
index f9ba5b12..1836ef23 100644
--- a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStorage.cs
+++ b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStorage.cs
@@ -52,7 +52,8 @@ public class ConversationStorage : IConversationStorage
MetaData = meta,
Content = dialog.Content,
SecondaryContent = dialog.SecondaryContent,
- Payload = dialog.Payload
+ Payload = dialog.Payload,
+ Data = dialog.Data
});
}
else
@@ -83,7 +84,8 @@ public class ConversationStorage : IConversationStorage
SecondaryContent = dialog.SecondaryContent,
RichContent = richContent,
SecondaryRichContent = secondaryRichContent,
- Payload = dialog.Payload
+ Payload = dialog.Payload,
+ Data = dialog.Data
});
}
@@ -121,7 +123,8 @@ public class ConversationStorage : IConversationStorage
MetaData = meta,
Content = dialog.Content,
SecondaryContent = dialog.SecondaryContent,
- Payload = dialog.Payload
+ Payload = dialog.Payload,
+ Data = dialog.Data
});
}
else
@@ -152,7 +155,8 @@ public class ConversationStorage : IConversationStorage
SecondaryContent = dialog.SecondaryContent,
RichContent = richContent,
SecondaryRichContent = secondaryRichContent,
- Payload = dialog.Payload
+ Payload = dialog.Payload,
+ Data = dialog.Data
});
}
}
@@ -196,7 +200,8 @@ public class ConversationStorage : IConversationStorage
RichContent = richContent,
SecondaryContent = secondaryContent,
SecondaryRichContent = secondaryRichContent,
- Payload = payload
+ Payload = payload,
+ Data = dialog.Data
};
results.Add(record);
diff --git a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Conversation.cs b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Conversation.cs
index 25682662..932bb9ba 100644
--- a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Conversation.cs
+++ b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Conversation.cs
@@ -605,7 +605,7 @@ public partial class FileRepository
#if !DEBUG
[SharpCache(10)]
#endif
- public List GetConversationStateSearchKeys(int messageLowerLimit = 2, int convUpperlimit = 100)
+ public List GetConversationStateSearchKeys(int messageLowerLimit = 2, int convUpperLimit = 100)
{
var dir = Path.Combine(_dbSettings.FileRepository, _conversationSettings.DataDir);
if (!Directory.Exists(dir)) return [];
@@ -635,7 +635,7 @@ public partial class FileRepository
keys.AddRange(stateKeys);
count++;
- if (count >= convUpperlimit)
+ if (count >= convUpperLimit)
{
break;
}
diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs
index 7461518d..f41c69fe 100644
--- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs
+++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs
@@ -79,9 +79,8 @@ public class ConversationController : ControllerBase
[HttpGet("/conversation/{conversationId}/dialogs")]
public async Task> GetDialogs([FromRoute] string conversationId)
{
- var conv = _services.GetRequiredService();
- conv.SetConversationId(conversationId, new List(), isReadOnly: true);
- var history = conv.GetDialogHistory(fromBreakpoint: false);
+ var storage = _services.GetRequiredService();
+ var history = storage.GetDialogs(conversationId);
var userService = _services.GetRequiredService();
var agentService = _services.GetRequiredService();
@@ -555,10 +554,10 @@ public class ConversationController : ControllerBase
#region Search state keys
[HttpGet("/conversation/state/keys")]
- public async Task> GetConversationStateKeys([FromQuery] string query, [FromQuery] int keyLimit = 10, [FromQuery] bool preLoad = false)
+ public async Task> GetConversationStateKeys([FromQuery] string query, [FromQuery] int keyLimit = 10, [FromQuery] int convLimit = 100, [FromQuery] bool preload = false)
{
var convService = _services.GetRequiredService();
- var keys = await convService.GetConversationStateSearhKeys(query, keyLimit: keyLimit, preLoad: preLoad);
+ var keys = await convService.GetConversationStateSearhKeys(query, keyLimit: keyLimit, convLimit: convLimit, preload: preload);
return keys;
}
#endregion
diff --git a/src/Plugins/BotSharp.Plugin.ChatHub/BotSharp.Plugin.ChatHub.csproj b/src/Plugins/BotSharp.Plugin.ChatHub/BotSharp.Plugin.ChatHub.csproj
index 4268cf38..bf8d28a6 100644
--- a/src/Plugins/BotSharp.Plugin.ChatHub/BotSharp.Plugin.ChatHub.csproj
+++ b/src/Plugins/BotSharp.Plugin.ChatHub/BotSharp.Plugin.ChatHub.csproj
@@ -1,4 +1,4 @@
-
+
$(TargetFramework)
diff --git a/src/Plugins/BotSharp.Plugin.ChatHub/ChatHubPlugin.cs b/src/Plugins/BotSharp.Plugin.ChatHub/ChatHubPlugin.cs
index e7cf082f..41d1851b 100644
--- a/src/Plugins/BotSharp.Plugin.ChatHub/ChatHubPlugin.cs
+++ b/src/Plugins/BotSharp.Plugin.ChatHub/ChatHubPlugin.cs
@@ -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();
services.AddScoped();
diff --git a/src/Plugins/BotSharp.Plugin.ChatHub/Enums/EventDispatchType.cs b/src/Plugins/BotSharp.Plugin.ChatHub/Enums/EventDispatchType.cs
new file mode 100644
index 00000000..14115808
--- /dev/null
+++ b/src/Plugins/BotSharp.Plugin.ChatHub/Enums/EventDispatchType.cs
@@ -0,0 +1,7 @@
+namespace BotSharp.Plugin.ChatHub.Enums;
+
+public static class EventDispatchType
+{
+ public const string Group = "group";
+ public const string User = "user";
+}
diff --git a/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/ChatHubConversationHook.cs b/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/ChatHubConversationHook.cs
index 83cb2746..9f2c1008 100644
--- a/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/ChatHubConversationHook.cs
+++ b/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/ChatHubConversationHook.cs
@@ -9,8 +9,9 @@ public class ChatHubConversationHook : ConversationHookBase
private readonly IHubContext _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 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,7 @@ public class ChatHubConversationHook : ConversationHookBase
var user = await userService.GetUser(conv.User.Id);
conv.User = UserViewModel.FromUser(user);
- await InitClientConversation(conv);
+ await InitClientConversation(conv.Id, conv);
await base.OnConversationInitialized(conversation);
}
@@ -63,7 +66,7 @@ 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);
// Send typing-on to client
var action = new ConversationSenderActionModel
@@ -71,7 +74,8 @@ public class ChatHubConversationHook : ConversationHookBase
ConversationId = conv.ConversationId,
SenderAction = SenderActionEnum.TypingOn
};
- await GenerateSenderAction(action);
+
+ await GenerateSenderAction(conv.ConversationId, action);
await base.OnMessageReceived(message);
}
@@ -84,7 +88,8 @@ public class ChatHubConversationHook : ConversationHookBase
SenderAction = SenderActionEnum.TypingOn,
Indication = message.Indication
};
- await GenerateSenderAction(action);
+
+ await GenerateSenderAction(conv.ConversationId, action);
await base.OnFunctionExecuting(message);
}
@@ -121,8 +126,8 @@ public class ChatHubConversationHook : ConversationHookBase
SenderAction = SenderActionEnum.TypingOff
};
- await GenerateSenderAction(action);
- await ReceiveAssistantMessage(json);
+ await GenerateSenderAction(conv.ConversationId, action);
+ await ReceiveAssistantMessage(conv.ConversationId, json);
await base.OnResponseGenerated(message);
}
@@ -146,7 +151,7 @@ public class ChatHubConversationHook : ConversationHookBase
}
}, _options.JsonSerializerOptions);
- await GenerateNotification(json);
+ await GenerateNotification(conv.ConversationId, json);
await base.OnNotificationGenerated(message);
}
@@ -158,7 +163,8 @@ public class ChatHubConversationHook : ConversationHookBase
ConversationId = conversationId,
MessageId = messageId
};
- await DeleteMessage(model);
+
+ await DeleteMessage(conversationId, model);
await base.OnMessageDeleted(conversationId, messageId);
}
@@ -169,34 +175,77 @@ public class ChatHubConversationHook : ConversationHookBase
return sidecar == null || !sidecar.IsEnabled();
}
- private async Task InitClientConversation(ConversationViewModel conversation)
+ private async Task InitClientConversation(string conversationId, ConversationViewModel conversation)
{
- await _chatHub.Clients.User(_user.Id).SendAsync(INIT_CLIENT_CONVERSATION, 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(ChatResponseModel model)
+ private async Task ReceiveClientMessage(string conversationId, ChatResponseModel model)
{
- await _chatHub.Clients.User(_user.Id).SendAsync(RECEIVE_CLIENT_MESSAGE, 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? json)
+ private async Task ReceiveAssistantMessage(string conversationId, string? json)
{
- await _chatHub.Clients.User(_user.Id).SendAsync(RECEIVE_ASSISTANT_MESSAGE, 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(ConversationSenderActionModel action)
+ private async Task GenerateSenderAction(string conversationId, ConversationSenderActionModel action)
{
- await _chatHub.Clients.User(_user.Id).SendAsync(GENERATE_SENDER_ACTION, 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(ChatResponseModel model)
+ private async Task DeleteMessage(string conversationId, ChatResponseModel model)
{
- await _chatHub.Clients.User(_user.Id).SendAsync(DELETE_MESSAGE, 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? json)
+ private async Task GenerateNotification(string conversationId, string? json)
{
- await _chatHub.Clients.User(_user.Id).SendAsync(GENERATE_NOTIFICATION, 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
}
diff --git a/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/ChatHubCrontabHook.cs b/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/ChatHubCrontabHook.cs
index 4f7ef3d2..a00dae90 100644
--- a/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/ChatHubCrontabHook.cs
+++ b/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/ChatHubCrontabHook.cs
@@ -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 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);
+ }
}
}
diff --git a/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/StreamingLogHook.cs b/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/StreamingLogHook.cs
index 410589e7..5d28ee2e 100644
--- a/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/StreamingLogHook.cs
+++ b/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/StreamingLogHook.cs
@@ -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 _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 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 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);
}
///
@@ -174,7 +177,7 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR
Source = ContentLogSource.Prompt,
Log = log
};
- await SendContentLog(input);
+ await SendContentLog(conversationId, input);
}
///
@@ -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,32 +431,62 @@ 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(string conversationId, ContentLogInputModel input)
{
- await _chatHub.Clients.User(_user.Id).SendAsync(CONTENT_LOG_GENERATED, BuildContentLog(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 states, RoleDialogModel message)
{
- await _chatHub.Clients.User(_user.Id).SendAsync(STATE_LOG_GENERATED, BuildStateLog(conversationId, agentId, states, 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)
{
- await _chatHub.Clients.User(_user.Id).SendAsync(AGENT_QUEUE_CHANGED, BuildAgentQueueChangedLog(conversationId, 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)
{
- await _chatHub.Clients.User(_user.Id).SendAsync(STATE_CHANGED, BuildStateChangeLog(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)
{
var output = new ContentLogOutputModel
diff --git a/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/WelcomeHook.cs b/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/WelcomeHook.cs
index 43bc839a..c7aedd83 100644
--- a/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/WelcomeHook.cs
+++ b/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/WelcomeHook.cs
@@ -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 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);
+ }
}
}
diff --git a/src/Plugins/BotSharp.Plugin.ChatHub/Settings/ChatHubSettings.cs b/src/Plugins/BotSharp.Plugin.ChatHub/Settings/ChatHubSettings.cs
new file mode 100644
index 00000000..70eaa6a5
--- /dev/null
+++ b/src/Plugins/BotSharp.Plugin.ChatHub/Settings/ChatHubSettings.cs
@@ -0,0 +1,6 @@
+namespace BotSharp.Plugin.ChatHub.Settings;
+
+public class ChatHubSettings
+{
+ public string EventDispatchBy { get; set; } = EventDispatchType.Group;
+}
diff --git a/src/Plugins/BotSharp.Plugin.ChatHub/SignalRHub.cs b/src/Plugins/BotSharp.Plugin.ChatHub/SignalRHub.cs
index 0593b94f..240b5993 100644
--- a/src/Plugins/BotSharp.Plugin.ChatHub/SignalRHub.cs
+++ b/src/Plugins/BotSharp.Plugin.ChatHub/SignalRHub.cs
@@ -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();
+ if (settings.EventDispatchBy == EventDispatchType.Group)
+ {
+ await Groups.AddToGroupAsync(Context.ConnectionId, conversationId);
+ }
+
var conv = await convService.GetConversation(conversationId);
if (conv != null)
{
diff --git a/src/Plugins/BotSharp.Plugin.ChatHub/Using.cs b/src/Plugins/BotSharp.Plugin.ChatHub/Using.cs
index 7cc89c9d..bc50d257 100644
--- a/src/Plugins/BotSharp.Plugin.ChatHub/Using.cs
+++ b/src/Plugins/BotSharp.Plugin.ChatHub/Using.cs
@@ -31,4 +31,6 @@ global using BotSharp.Abstraction.Routing;
global using BotSharp.Abstraction.Messaging;
global using BotSharp.Abstraction.Messaging.Enums;
global using BotSharp.Abstraction.Messaging.Models.RichContent;
-global using BotSharp.Abstraction.Templating;
\ No newline at end of file
+global using BotSharp.Abstraction.Templating;
+global using BotSharp.Plugin.ChatHub.Settings;
+global using BotSharp.Plugin.ChatHub.Enums;
\ No newline at end of file
diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Models/DialogMongoElement.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Models/DialogMongoElement.cs
index 126dfee8..643806a5 100644
--- a/src/Plugins/BotSharp.Plugin.MongoStorage/Models/DialogMongoElement.cs
+++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Models/DialogMongoElement.cs
@@ -11,6 +11,7 @@ public class DialogMongoElement
public string? RichContent { get; set; }
public string? SecondaryRichContent { get; set; }
public string? Payload { get; set; }
+ public object? Data { get; set; }
public DialogMongoElement()
{
@@ -26,7 +27,8 @@ public class DialogMongoElement
SecondaryContent = dialog.SecondaryContent,
RichContent = dialog.RichContent,
SecondaryRichContent = dialog.SecondaryRichContent,
- Payload = dialog.Payload
+ Payload = dialog.Payload,
+ Data = dialog.Data
};
}
@@ -39,7 +41,8 @@ public class DialogMongoElement
SecondaryContent = dialog.SecondaryContent,
RichContent = dialog.RichContent,
SecondaryRichContent = dialog.SecondaryRichContent,
- Payload = dialog.Payload
+ Payload = dialog.Payload,
+ Data = dialog.Data
};
}
}
diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Conversation.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Conversation.cs
index cabaecd2..64c8bb53 100644
--- a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Conversation.cs
+++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Conversation.cs
@@ -614,20 +614,20 @@ public partial class MongoRepository
#if !DEBUG
[SharpCache(10)]
#endif
- public List GetConversationStateSearchKeys(int messageLowerLimit = 2, int convUpperlimit = 100)
+ public List GetConversationStateSearchKeys(int messageLowerLimit = 2, int convUpperLimit = 100)
{
- var convFilter = Builders.Filter.Gte(x => x.DialogCount, messageLowerLimit);
- var conversations = _dc.Conversations.Find(convFilter)
- .SortByDescending(x => x.UpdatedTime)
- .Limit(convUpperlimit)
- .ToList();
+ var stateBuilder = Builders.Filter;
+ var sortDef = Builders.Sort.Descending(x => x.UpdatedTime);
+ var stateFilters = new List>()
+ {
+ stateBuilder.Exists(x => x.States),
+ stateBuilder.Ne(x => x.States, [])
+ };
- if (conversations.IsNullOrEmpty()) return [];
-
- var convIds = conversations.Select(x => x.Id).ToList();
- var stateFilter = Builders.Filter.In(x => x.ConversationId, convIds);
-
- var states = _dc.ConversationStates.Find(stateFilter).ToList();
+ var states = _dc.ConversationStates.Find(stateBuilder.And(stateFilters))
+ .Sort(sortDef)
+ .Limit(convUpperLimit)
+ .ToList();
var keys = states.SelectMany(x => x.States.Select(x => x.Key)).Distinct().ToList();
return keys;
}
diff --git a/src/WebStarter/appsettings.json b/src/WebStarter/appsettings.json
index 655fa954..a0f02893 100644
--- a/src/WebStarter/appsettings.json
+++ b/src/WebStarter/appsettings.json
@@ -222,6 +222,10 @@
"Enabled": false
},
+ "ChatHub": {
+ "EventDispatchBy": "group"
+ },
+
"SharpCache": {
"Enabled": true,
"CacheType": 1,