diff --git a/src/Infrastructure/BotSharp.Abstraction/Conversations/ConversationHookBase.cs b/src/Infrastructure/BotSharp.Abstraction/Conversations/ConversationHookBase.cs
index 06a97174..9239b865 100644
--- a/src/Infrastructure/BotSharp.Abstraction/Conversations/ConversationHookBase.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/Conversations/ConversationHookBase.cs
@@ -79,4 +79,7 @@ public abstract class ConversationHookBase : IConversationHook
public virtual Task OnBreakpointUpdated(string conversationId, bool resetStates)
=> Task.CompletedTask;
+
+ public virtual Task OnNotificationGenerated(RoleDialogModel message)
+ => Task.CompletedTask;
}
diff --git a/src/Infrastructure/BotSharp.Abstraction/Conversations/Enums/MessageTypeName.cs b/src/Infrastructure/BotSharp.Abstraction/Conversations/Enums/MessageTypeName.cs
new file mode 100644
index 00000000..c4e73d69
--- /dev/null
+++ b/src/Infrastructure/BotSharp.Abstraction/Conversations/Enums/MessageTypeName.cs
@@ -0,0 +1,7 @@
+namespace BotSharp.Abstraction.Conversations.Enums;
+
+public static class MessageTypeName
+{
+ public const string Plain = "plain";
+ public const string Notification = "notification";
+}
diff --git a/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationHook.cs b/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationHook.cs
index 9ed47c61..34371ba5 100644
--- a/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationHook.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationHook.cs
@@ -1,5 +1,3 @@
-using BotSharp.Abstraction.Functions.Models;
-
namespace BotSharp.Abstraction.Conversations;
public interface IConversationHook
@@ -107,4 +105,11 @@ public interface IConversationHook
///
///
Task OnBreakpointUpdated(string conversationId, bool resetStates);
+
+ ///
+ /// Generate a notification
+ ///
+ ///
+ ///
+ Task OnNotificationGenerated(RoleDialogModel message);
}
diff --git a/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationService.cs b/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationService.cs
index bddc5426..a52b9f07 100644
--- a/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationService.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationService.cs
@@ -8,7 +8,7 @@ public interface IConversationService
IConversationStateService States { get; }
string ConversationId { get; }
Task NewConversation(Conversation conversation);
- void SetConversationId(string conversationId, List states);
+ void SetConversationId(string conversationId, List states, bool isReadOnly = false);
Task GetConversation(string id);
Task> GetConversations(ConversationFilter filter);
Task UpdateConversationTitle(string id, string title);
@@ -41,7 +41,7 @@ public interface IConversationService
PostbackMessageModel? replyMessage,
Func onResponseReceived);
- List GetDialogHistory(int lastCount = 100, bool fromBreakpoint = true);
+ List GetDialogHistory(int lastCount = 100, bool fromBreakpoint = true, IEnumerable? excludeMessageTypes = null);
Task CleanHistory(string agentId);
///
diff --git a/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationStorage.cs b/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationStorage.cs
index 5f7066d2..a586e217 100644
--- a/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationStorage.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationStorage.cs
@@ -2,7 +2,6 @@ namespace BotSharp.Abstraction.Conversations;
public interface IConversationStorage
{
- void InitStorage(string conversationId);
void Append(string conversationId, RoleDialogModel dialog);
List GetDialogs(string conversationId);
}
diff --git a/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/Conversation.cs b/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/Conversation.cs
index ad7ffd04..890f3211 100644
--- a/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/Conversation.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/Conversation.cs
@@ -83,6 +83,9 @@ public class DialogMetaData
[JsonPropertyName("message_id")]
public string MessageId { get; set; }
+ [JsonPropertyName("message_type")]
+ public string MessageType { get; set; }
+
[JsonPropertyName("function_name")]
public string? FunctionName { get; set; }
diff --git a/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/RoleDialogModel.cs b/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/RoleDialogModel.cs
index 5b8f7017..679527a3 100644
--- a/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/RoleDialogModel.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/RoleDialogModel.cs
@@ -1,3 +1,4 @@
+using BotSharp.Abstraction.Conversations.Enums;
using BotSharp.Abstraction.Functions.Models;
using BotSharp.Abstraction.Messaging;
using BotSharp.Abstraction.Messaging.Models.RichContent;
@@ -11,6 +12,11 @@ public class RoleDialogModel : ITrackableMessage
///
public string MessageId { get; set; }
+ ///
+ /// The message type
+ ///
+ public string MessageType { get; set; } = MessageTypeName.Plain;
+
///
/// user, system, assistant, function
///
@@ -101,6 +107,7 @@ public class RoleDialogModel : ITrackableMessage
public List GeneratedImages { get; set; } = new List();
+
private RoleDialogModel()
{
}
@@ -110,6 +117,7 @@ public class RoleDialogModel : ITrackableMessage
Role = role;
Content = text;
MessageId = Guid.NewGuid().ToString();
+ MessageType = MessageTypeName.Plain;
}
public override string ToString()
@@ -132,6 +140,7 @@ public class RoleDialogModel : ITrackableMessage
{
CurrentAgentId = source.CurrentAgentId,
MessageId = source.MessageId,
+ MessageType = source.MessageType,
FunctionArgs = source.FunctionArgs,
FunctionName = source.FunctionName,
ToolCallId = source.ToolCallId,
diff --git a/src/Infrastructure/BotSharp.Abstraction/Loggers/Enums/ContentLogSource.cs b/src/Infrastructure/BotSharp.Abstraction/Loggers/Enums/ContentLogSource.cs
index 45d74a51..78ab19ad 100644
--- a/src/Infrastructure/BotSharp.Abstraction/Loggers/Enums/ContentLogSource.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/Loggers/Enums/ContentLogSource.cs
@@ -7,4 +7,5 @@ public static class ContentLogSource
public const string FunctionCall = "function call";
public const string AgentResponse = "agent response";
public const string HardRule = "hard rule";
+ public const string Notification = "notification";
}
diff --git a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.Summary.cs b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.Summary.cs
index 0983e2ed..88d3a52d 100644
--- a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.Summary.cs
+++ b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.Summary.cs
@@ -1,3 +1,4 @@
+using BotSharp.Abstraction.Conversations.Enums;
using BotSharp.Abstraction.MLTasks;
using BotSharp.Abstraction.Templating;
@@ -21,6 +22,7 @@ public partial class ConversationService
if (dialogs.IsNullOrEmpty()) continue;
+ dialogs = dialogs.Where(x => x.MessageType != MessageTypeName.Notification).ToList();
var content = GetConversationContent(dialogs);
if (string.IsNullOrWhiteSpace(content)) continue;
diff --git a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.cs b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.cs
index 6e0f97d1..40f412d9 100644
--- a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.cs
+++ b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.cs
@@ -106,7 +106,7 @@ public partial class ConversationService : IConversationService
throw new NotImplementedException();
}
- public List GetDialogHistory(int lastCount = 100, bool fromBreakpoint = true)
+ public List GetDialogHistory(int lastCount = 100, bool fromBreakpoint = true, IEnumerable? excludeMessageTypes = null)
{
if (string.IsNullOrEmpty(_conversationId))
{
@@ -115,6 +115,11 @@ public partial class ConversationService : IConversationService
var dialogs = _storage.GetDialogs(_conversationId);
+ if (!excludeMessageTypes.IsNullOrEmpty())
+ {
+ dialogs = dialogs.Where(x => !excludeMessageTypes.Contains(x.MessageType)).ToList();
+ }
+
if (fromBreakpoint)
{
var db = _services.GetRequiredService();
@@ -134,7 +139,7 @@ public partial class ConversationService : IConversationService
.ToList();
}
- public void SetConversationId(string conversationId, List states)
+ public void SetConversationId(string conversationId, List states, bool isReadOnly = false)
{
_conversationId = conversationId;
_state.Load(_conversationId);
diff --git a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStorage.cs b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStorage.cs
index 5abc4fc5..ea145ac0 100644
--- a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStorage.cs
+++ b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStorage.cs
@@ -41,6 +41,7 @@ public class ConversationStorage : IConversationStorage
Role = dialog.Role,
AgentId = agentId,
MessageId = dialog.MessageId,
+ MessageType = dialog.MessageType,
FunctionName = dialog.FunctionName,
CreateTime = dialog.CreatedAt
};
@@ -65,6 +66,7 @@ public class ConversationStorage : IConversationStorage
Role = dialog.Role,
AgentId = agentId,
MessageId = dialog.MessageId,
+ MessageType = dialog.MessageType,
SenderId = dialog.SenderId,
FunctionName = dialog.FunctionName,
CreateTime = dialog.CreatedAt
@@ -108,6 +110,7 @@ public class ConversationStorage : IConversationStorage
var role = meta.Role;
var currentAgentId = meta.AgentId;
var messageId = meta.MessageId;
+ var messageType = meta.MessageType;
var function = meta.FunctionName;
var senderId = role == AgentRole.Function ? currentAgentId : meta.SenderId;
var createdAt = meta.CreateTime;
@@ -120,6 +123,7 @@ public class ConversationStorage : IConversationStorage
{
CurrentAgentId = currentAgentId,
MessageId = messageId,
+ MessageType = messageType,
CreatedAt = createdAt,
SenderId = senderId,
FunctionName = function,
@@ -143,23 +147,4 @@ public class ConversationStorage : IConversationStorage
return results;
}
-
- public void InitStorage(string conversationId)
- {
- var file = GetStorageFile(conversationId);
- if (!File.Exists(file))
- {
- File.WriteAllLines(file, new string[0]);
- }
- }
-
- private string GetStorageFile(string conversationId)
- {
- var dir = Path.Combine(_dbSettings.FileRepository, "conversations", conversationId);
- if (!Directory.Exists(dir))
- {
- Directory.CreateDirectory(dir);
- }
- return Path.Combine(dir, "dialogs.txt");
- }
}
diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs
index 5521b5e6..a020a490 100644
--- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs
+++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs
@@ -1,8 +1,10 @@
+using Azure;
using BotSharp.Abstraction.Files.Constants;
using BotSharp.Abstraction.Files.Enums;
using BotSharp.Abstraction.Options;
using BotSharp.Abstraction.Routing;
using BotSharp.Abstraction.Users.Enums;
+using BotSharp.Core.Infrastructures;
namespace BotSharp.OpenAPI.Controllers;
@@ -251,6 +253,44 @@ public class ConversationController : ControllerBase
return isSuccess ? newMessageId : string.Empty;
}
+ #region Send notification
+ [HttpPost("/conversation/{conversationId}/notification")]
+ public async Task SendNotification([FromRoute] string conversationId, [FromBody] NewMessageModel input)
+ {
+ var conv = _services.GetRequiredService();
+ var routing = _services.GetRequiredService();
+ var userService = _services.GetRequiredService();
+
+ conv.SetConversationId(conversationId, new List(), isReadOnly: true);
+
+ var inputMsg = new RoleDialogModel(AgentRole.User, input.Text)
+ {
+ MessageId = Guid.NewGuid().ToString(),
+ CreatedAt = DateTime.UtcNow
+ };
+
+ var user = await userService.GetUser(_user.Id);
+ var response = new ChatResponseModel()
+ {
+ ConversationId = conversationId,
+ MessageId = inputMsg.MessageId,
+ Sender = new UserViewModel
+ {
+ Id = user?.Id ?? string.Empty,
+ FirstName = user?.FirstName ?? string.Empty,
+ LastName = user?.LastName ?? string.Empty
+ },
+ CreatedAt = DateTime.UtcNow
+ };
+
+ await HookEmitter.Emit(_services, async hook =>
+ await hook.OnNotificationGenerated(inputMsg)
+ );
+
+ return response;
+ }
+ #endregion
+
#region Send message
[HttpPost("/conversation/{agentId}/{conversationId}")]
public async Task SendMessage([FromRoute] string agentId,
diff --git a/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/ChatHubConversationHook.cs b/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/ChatHubConversationHook.cs
index bf2292ea..80ee2299 100644
--- a/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/ChatHubConversationHook.cs
+++ b/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/ChatHubConversationHook.cs
@@ -15,6 +15,7 @@ public class ChatHubConversationHook : ConversationHookBase
private const string RECEIVE_ASSISTANT_MESSAGE = "OnMessageReceivedFromAssistant";
private const string GENERATE_SENDER_ACTION = "OnSenderActionGenerated";
private const string DELETE_MESSAGE = "OnMessageDeleted";
+ private const string GENERATE_NOTIFICATION = "OnNotificationGenerated";
#endregion
public ChatHubConversationHook(
@@ -117,6 +118,31 @@ public class ChatHubConversationHook : ConversationHookBase
await base.OnResponseGenerated(message);
}
+
+ public override async Task OnNotificationGenerated(RoleDialogModel message)
+ {
+ var conv = _services.GetRequiredService();
+ var json = JsonSerializer.Serialize(new ChatResponseModel()
+ {
+ 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,
+ Sender = new UserViewModel()
+ {
+ FirstName = "AI",
+ LastName = "Assistant",
+ Role = AgentRole.Assistant
+ }
+ }, _options.JsonSerializerOptions);
+
+ await GenerateNotification(json);
+ await base.OnNotificationGenerated(message);
+ }
+
+
public override async Task OnMessageDeleted(string conversationId, string messageId)
{
var model = new ChatResponseModel
@@ -153,5 +179,10 @@ public class ChatHubConversationHook : ConversationHookBase
{
await _chatHub.Clients.User(_user.Id).SendAsync(DELETE_MESSAGE, model);
}
+
+ private async Task GenerateNotification(string? json)
+ {
+ await _chatHub.Clients.User(_user.Id).SendAsync(GENERATE_NOTIFICATION, json);
+ }
#endregion
}
diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Models/DialogMongoElement.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Models/DialogMongoElement.cs
index 5c2a1698..1179d3cd 100644
--- a/src/Plugins/BotSharp.Plugin.MongoStorage/Models/DialogMongoElement.cs
+++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Models/DialogMongoElement.cs
@@ -48,6 +48,7 @@ public class DialogMetaDataMongoElement
public string Role { get; set; }
public string AgentId { get; set; }
public string MessageId { get; set; }
+ public string MessageType { get; set; }
public string? FunctionName { get; set; }
public string? SenderId { get; set; }
public DateTime CreateTime { get; set; }
@@ -64,6 +65,7 @@ public class DialogMetaDataMongoElement
Role = meta.Role,
AgentId = meta.AgentId,
MessageId = meta.MessageId,
+ MessageType = meta.MessageType,
FunctionName = meta.FunctionName,
SenderId = meta.SenderId,
CreateTime = meta.CreateTime,
@@ -77,6 +79,7 @@ public class DialogMetaDataMongoElement
Role = meta.Role,
AgentId = meta.AgentId,
MessageId = meta.MessageId,
+ MessageType = meta.MessageType,
FunctionName = meta.FunctionName,
SenderId = meta.SenderId,
CreateTime = meta.CreateTime,