diff --git a/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/RoleDialogModel.cs b/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/RoleDialogModel.cs index 96d933f5..dea85881 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/RoleDialogModel.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/RoleDialogModel.cs @@ -35,6 +35,9 @@ public class RoleDialogModel : ITrackableMessage [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? FunctionName { get; set; } + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public string? PostbackFunctionName { get; set; } + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? FunctionArgs { get; set; } @@ -95,6 +98,7 @@ public class RoleDialogModel : ITrackableMessage MessageId = source.MessageId, FunctionArgs = source.FunctionArgs, FunctionName = source.FunctionName, + PostbackFunctionName = source.PostbackFunctionName, RichContent = source.RichContent, StopCompletion = source.StopCompletion, Instruction = source.Instruction, diff --git a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.SendMessage.cs b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.SendMessage.cs index 9d01f355..eaa24fa3 100644 --- a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.SendMessage.cs +++ b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.SendMessage.cs @@ -72,10 +72,13 @@ public partial class ConversationService } // Persist to storage - _storage.Append(_conversationId, message); + if (!message.StopCompletion) + { + _storage.Append(_conversationId, message); - // Add to thread - dialogs.Add(RoleDialogModel.From(message)); + // Add to thread + dialogs.Add(RoleDialogModel.From(message)); + } if (!stopCompletion) { @@ -147,6 +150,12 @@ public partial class ConversationService Message = new TextMessage(response.Content) }; + // Patch return function name + if (response.PostbackFunctionName != null) + { + response.FunctionName = response.PostbackFunctionName; + } + var hooks = _services.GetServices().ToList(); if (response.Instruction != null) diff --git a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStorage.cs b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStorage.cs index e523ce48..532f56d4 100644 --- a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStorage.cs +++ b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStorage.cs @@ -28,11 +28,11 @@ public class ConversationStorage : IConversationStorage var dialogElements = new List(); // Prevent duplicate record to be inserted - var dialogs = db.GetConversationDialogs(conversationId); + /*var dialogs = db.GetConversationDialogs(conversationId); if (dialogs.Any(x => x.MetaData.MessageId == dialog.MessageId && x.Content == dialog.Content)) { return; - } + }*/ if (dialog.Role == AgentRole.Function) { diff --git a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.InvokeFunction.cs b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.InvokeFunction.cs index 6ad2ae75..86839b67 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.InvokeFunction.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.InvokeFunction.cs @@ -5,6 +5,8 @@ namespace BotSharp.Core.Routing; public partial class RoutingService { private List _functionCallStack = new List(); + public List FunctionCallStack => _functionCallStack; + public async Task InvokeFunction(string name, RoleDialogModel message) { var function = _services.GetServices().FirstOrDefault(x => x.Name == name); @@ -16,10 +18,9 @@ public partial class RoutingService return false; } - var originalFunctionName = message.FunctionName; - message.FunctionName = name; - message.Role = AgentRole.Function; - message.FunctionArgs = message.FunctionArgs; + // Clone message + var clonedMessage = RoleDialogModel.From(message); + clonedMessage.FunctionName = name; var hooks = _services.GetServices() .OrderBy(x => x.Priority) @@ -28,21 +29,36 @@ public partial class RoutingService // Before executing functions foreach (var hook in hooks) { - await hook.OnFunctionExecuting(message); + await hook.OnFunctionExecuting(clonedMessage); } bool result = false; try { - result = await function.Execute(message); + result = await function.Execute(clonedMessage); + _functionCallStack.Add(new FunctionCallingResponse { Role = AgentRole.Function, - FunctionName = message.FunctionName, - Args = JsonDocument.Parse(message.FunctionArgs ?? "{}"), - Content = message.Content + FunctionName = clonedMessage.FunctionName, + Args = JsonDocument.Parse(clonedMessage.FunctionArgs ?? "{}"), + Content = clonedMessage.Content }); + + // After functions have been executed + foreach (var hook in hooks) + { + await hook.OnFunctionExecuted(clonedMessage); + } + + // Set result to original message + message.PostbackFunctionName = clonedMessage.PostbackFunctionName; + message.CurrentAgentId = clonedMessage.CurrentAgentId; + message.Content = clonedMessage.Content; + message.StopCompletion = clonedMessage.StopCompletion; + message.RichContent = clonedMessage.RichContent; + message.Data = clonedMessage.Data; } catch (JsonException ex) { @@ -63,25 +79,12 @@ public partial class RoutingService message.Content = JsonSerializer.Serialize(message.Data); } - // After functions have been executed - foreach (var hook in hooks) - { - await hook.OnFunctionExecuted(message); - } - - // restore original function name - if (!message.StopCompletion && - message.FunctionName != originalFunctionName) - { - message.FunctionName = originalFunctionName; - } - // Save to Storage as well - if (!message.StopCompletion && message.FunctionName != "route_to_agent") + /*if (!message.StopCompletion && message.FunctionName != "route_to_agent") { var storage = _services.GetRequiredService(); storage.Append(Context.ConversationId, message); - } + }*/ return result; } diff --git a/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/StreamingLogHook.cs b/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/StreamingLogHook.cs index 2e2ed3d6..e1b5fd0f 100644 --- a/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/StreamingLogHook.cs +++ b/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/StreamingLogHook.cs @@ -98,7 +98,7 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR if (!_convSettings.ShowVerboseLog) return; } - public override async Task OnFunctionExecuted(RoleDialogModel message) + public override async Task OnFunctionExecuting(RoleDialogModel message) { if (message.FunctionName == "route_to_agent") { @@ -109,7 +109,30 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR var agent = await _agentService.LoadAgent(message.CurrentAgentId); message.FunctionArgs = message.FunctionArgs ?? "{}"; var args = JsonSerializer.Serialize(JsonDocument.Parse(message.FunctionArgs), _options.JsonSerializerOptions); - var log = $"*{message.FunctionName}*\r\n```json\r\n{args}\r\n```\r\n=> {message.Content?.Trim()}"; + var log = $"{message.FunctionName} executing\r\n```json\r\n{args}\r\n```"; + + var input = new ContentLogInputModel(conversationId, message) + { + Name = agent?.Name, + AgentId = agent?.Id, + Source = ContentLogSource.FunctionCall, + Log = log + }; + await _chatHub.Clients.User(_user.Id).SendAsync("OnConversationContentLogGenerated", BuildContentLog(input)); + } + + public override async Task OnFunctionExecuted(RoleDialogModel message) + { + if (message.FunctionName == "route_to_agent") + { + return; + } + + var conversationId = _state.GetConversationId(); + var agent = await _agentService.LoadAgent(message.CurrentAgentId); + message.FunctionArgs = message.FunctionArgs ?? "{}"; + // var args = JsonSerializer.Serialize(JsonDocument.Parse(message.FunctionArgs), _options.JsonSerializerOptions); + var log = $"{message.FunctionName} =>\r\n*{message.Content?.Trim()}*"; var input = new ContentLogInputModel(conversationId, message) {