diff --git a/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/Conversation.cs b/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/Conversation.cs index 675c0885a..c4235a965 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/Conversation.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/Conversation.cs @@ -114,6 +114,10 @@ public class DialogMetaData [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? ToolCallId { get; set; } + [JsonPropertyName("meta_data")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public Dictionary? MetaData { get; set; } + [JsonPropertyName("sender_id")] public string? SenderId { get; set; } diff --git a/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/RoleDialogModel.cs b/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/RoleDialogModel.cs index 90cc44889..796ee93d7 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/RoleDialogModel.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/RoleDialogModel.cs @@ -62,15 +62,18 @@ public class RoleDialogModel : ITrackableMessage [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? FunctionName { get; set; } - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - public string? ToolCallId { get; set; } - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? PostbackFunctionName { get; set; } [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? FunctionArgs { get; set; } + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public string? ToolCallId { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public Dictionary? MetaData { get; set; } + /// /// Set this flag is in OnFunctionExecuting, if true, it won't be executed by InvokeFunction. /// @@ -189,9 +192,9 @@ public static RoleDialogModel From(RoleDialogModel source, MessageId = source.MessageId, MessageType = source.MessageType, MessageLabel = source.MessageLabel, - FunctionArgs = source.FunctionArgs, - FunctionName = source.FunctionName, ToolCallId = source.ToolCallId, + FunctionName = source.FunctionName, + FunctionArgs = source.FunctionArgs, Indication = source.Indication, PostbackFunctionName = source.PostbackFunctionName, RichContent = source.RichContent, @@ -200,7 +203,8 @@ public static RoleDialogModel From(RoleDialogModel source, Instruction = source.Instruction, Data = source.Data, IsStreaming = source.IsStreaming, - Annotations = source.Annotations + Annotations = source.Annotations, + MetaData = source.MetaData != null ? new(source.MetaData) : null }; } } \ No newline at end of file diff --git a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStorage.cs b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStorage.cs index a673ea64f..3a8b3cdca 100644 --- a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStorage.cs +++ b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStorage.cs @@ -73,9 +73,10 @@ public List GetDialogs(string conversationId) MessageLabel = meta?.MessageLabel, CreatedAt = meta?.CreatedTime ?? default, SenderId = senderId, + ToolCallId = meta?.ToolCallId, FunctionName = meta?.FunctionName, FunctionArgs = meta?.FunctionArgs, - ToolCallId = meta?.ToolCallId, + MetaData = meta?.MetaData, RichContent = richContent, SecondaryContent = secondaryContent, SecondaryRichContent = secondaryRichContent, @@ -110,9 +111,10 @@ public List GetDialogs(string conversationId) MessageId = dialog.MessageId, MessageType = dialog.MessageType, MessageLabel = dialog.MessageLabel, + ToolCallId = dialog.ToolCallId, FunctionName = dialog.FunctionName, FunctionArgs = dialog.FunctionArgs, - ToolCallId = dialog.ToolCallId, + MetaData = dialog.MetaData, CreatedTime = dialog.CreatedAt }; @@ -139,6 +141,7 @@ public List GetDialogs(string conversationId) MessageLabel = dialog.MessageLabel, SenderId = dialog.SenderId, FunctionName = dialog.FunctionName, + MetaData = dialog.MetaData, CreatedTime = dialog.CreatedAt }; diff --git a/src/Infrastructure/BotSharp.Core/Plugins/PluginLoader.cs b/src/Infrastructure/BotSharp.Core/Plugins/PluginLoader.cs index 96c703121..583f8b69a 100644 --- a/src/Infrastructure/BotSharp.Core/Plugins/PluginLoader.cs +++ b/src/Infrastructure/BotSharp.Core/Plugins/PluginLoader.cs @@ -160,6 +160,11 @@ public PluginDef UpdatePluginStatus(IServiceProvider services, string id, bool e foreach (var agentId in dependentAgentIds) { var agent = agentService.LoadAgent(agentId).ConfigureAwait(false).GetAwaiter().GetResult(); + if (agent == null) + { + continue; + } + agent.Disabled = false; agentService.UpdateAgent(agent, AgentField.Disabled); @@ -184,11 +189,13 @@ public PluginDef UpdatePluginStatus(IServiceProvider services, string id, bool e foreach (var agentId in plugin.AgentIds) { var agent = agentService.LoadAgent(agentId).ConfigureAwait(false).GetAwaiter().GetResult(); - if (agent != null) + if (agent == null) { - agent.Disabled = true; - agentService.UpdateAgent(agent, AgentField.Disabled); + continue; } + + agent.Disabled = true; + agentService.UpdateAgent(agent, AgentField.Disabled); } } return plugin; diff --git a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.InvokeAgent.cs b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.InvokeAgent.cs index e0175a70d..fdaddc85e 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.InvokeAgent.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.InvokeAgent.cs @@ -56,6 +56,7 @@ public async Task InvokeAgent( message.ToolCallId = response.ToolCallId; message.FunctionName = response.FunctionName; message.FunctionArgs = response.FunctionArgs; + message.MetaData = response.MetaData != null ? new(response.MetaData) : null; message.Indication = response.Indication; message.CurrentAgentId = agent.Id; message.IsStreaming = response.IsStreaming; @@ -74,6 +75,7 @@ public async Task InvokeAgent( message = RoleDialogModel.From(message, role: AgentRole.Assistant, content: response.Content); message.CurrentAgentId = agent.Id; + message.MetaData = response.MetaData != null ? new(response.MetaData) : null; message.IsStreaming = response.IsStreaming; message.MessageLabel = response.MessageLabel; dialogs.Add(message); diff --git a/src/Plugins/BotSharp.Plugin.GoogleAI/Constants/Constants.cs b/src/Plugins/BotSharp.Plugin.GoogleAI/Constants/Constants.cs new file mode 100644 index 000000000..e0aa12b6c --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.GoogleAI/Constants/Constants.cs @@ -0,0 +1,6 @@ +namespace BotSharp.Plugin.GoogleAI.Constants; + +internal static class Constants +{ + internal const string ThoughtSignature = "thought_signature"; +} diff --git a/src/Plugins/BotSharp.Plugin.GoogleAI/Models/Chat/ChatThoughtModel.cs b/src/Plugins/BotSharp.Plugin.GoogleAI/Models/Chat/ChatThoughtModel.cs new file mode 100644 index 000000000..e00b7d922 --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.GoogleAI/Models/Chat/ChatThoughtModel.cs @@ -0,0 +1,9 @@ +using GenerativeAI.Types; + +namespace BotSharp.Plugin.GoogleAI.Models.Chat; + +internal class ChatThoughtModel +{ + internal FunctionCall? ToolCall { get; set; } + internal string? ThoughtSignature { get; set; } +} diff --git a/src/Plugins/BotSharp.Plugin.GoogleAI/Providers/Chat/ChatCompletionProvider.cs b/src/Plugins/BotSharp.Plugin.GoogleAI/Providers/Chat/ChatCompletionProvider.cs index 54089050d..085f5e590 100644 --- a/src/Plugins/BotSharp.Plugin.GoogleAI/Providers/Chat/ChatCompletionProvider.cs +++ b/src/Plugins/BotSharp.Plugin.GoogleAI/Providers/Chat/ChatCompletionProvider.cs @@ -1,4 +1,3 @@ -using Azure; using BotSharp.Abstraction.Files; using BotSharp.Abstraction.Files.Models; using BotSharp.Abstraction.Files.Utilities; @@ -6,7 +5,6 @@ using BotSharp.Abstraction.MessageHub.Models; using BotSharp.Core.Infrastructures.Streams; using BotSharp.Core.MessageHub; -using Fluid; using GenerativeAI; using GenerativeAI.Core; using GenerativeAI.Types; @@ -26,6 +24,7 @@ public class ChatCompletionProvider : IChatCompletion public string Model => _model; private GoogleAiSettings _settings; + public ChatCompletionProvider( IServiceProvider services, GoogleAiSettings googleSettings, @@ -58,13 +57,18 @@ public async Task GetChatCompletions(Agent agent, List + { + [Constants.ThoughtSignature] = part?.ThoughtSignature + }, RenderedInstruction = string.Join("\r\n", renderedInstructions) }; } @@ -74,6 +78,10 @@ public async Task GetChatCompletions(Agent agent, List + { + [Constants.ThoughtSignature] = part?.ThoughtSignature + }, RenderedInstruction = string.Join("\r\n", renderedInstructions) }; } @@ -117,6 +125,10 @@ public async Task GetChatCompletionsAsync(Agent agent, List + { + [Constants.ThoughtSignature] = part?.ThoughtSignature + }, RenderedInstruction = string.Join("\r\n", renderedInstructions) }; @@ -145,6 +157,10 @@ public async Task GetChatCompletionsAsync(Agent agent, List + { + [Constants.ThoughtSignature] = part?.ThoughtSignature + }, RenderedInstruction = string.Join("\r\n", renderedInstructions) }; @@ -195,6 +211,7 @@ public async Task GetChatCompletionsStreamingAsync(Agent agent, }); using var textStream = new RealtimeTextStream(); + ChatThoughtModel? thoughtModel = null; UsageMetadata? tokenUsage = null; var responseMessage = new RoleDialogModel(AgentRole.Assistant, string.Empty) @@ -212,36 +229,47 @@ public async Task GetChatCompletionsStreamingAsync(Agent agent, } var part = candidate?.Content?.Parts?.FirstOrDefault(); + thoughtModel = part?.FunctionCall != null + ? new() { ToolCall = part.FunctionCall, ThoughtSignature = part.ThoughtSignature } + : thoughtModel; + if (!string.IsNullOrEmpty(part?.Text)) { var text = part.Text; textStream.Collect(text); - var content = new RoleDialogModel(AgentRole.Assistant, text) - { - CurrentAgentId = agent.Id, - MessageId = messageId - }; hub.Push(new() { EventName = ChatEvent.OnReceiveLlmStreamMessage, RefId = conv.ConversationId, - Data = content + Data = new RoleDialogModel(AgentRole.Assistant, text) + { + CurrentAgentId = agent.Id, + MessageId = messageId + } }); } - if (candidate.FinishReason == FinishReason.STOP) + if (candidate!.FinishReason == FinishReason.STOP) { - if (part?.FunctionCall != null) + var thought = part?.FunctionCall != null + ? new() { ToolCall = part.FunctionCall, ThoughtSignature = part.ThoughtSignature } + : thoughtModel; + var functionCall = thought?.ToolCall; + + if (functionCall != null) { - var functionCall = part.FunctionCall; responseMessage = new RoleDialogModel(AgentRole.Function, string.Empty) { CurrentAgentId = agent.Id, MessageId = messageId, ToolCallId = functionCall.Id, FunctionName = functionCall.Name, - FunctionArgs = functionCall.Args?.ToString() ?? string.Empty + FunctionArgs = functionCall.Args?.ToJsonString(), + MetaData = new Dictionary + { + [Constants.ThoughtSignature] = thought?.ThoughtSignature + } }; #if DEBUG @@ -259,7 +287,11 @@ public async Task GetChatCompletionsStreamingAsync(Agent agent, { CurrentAgentId = agent.Id, MessageId = messageId, - IsStreaming = true + IsStreaming = true, + MetaData = new Dictionary + { + [Constants.ThoughtSignature] = part?.ThoughtSignature + } }; } @@ -272,7 +304,11 @@ public async Task GetChatCompletionsStreamingAsync(Agent agent, { CurrentAgentId = agent.Id, MessageId = messageId, - IsStreaming = true + IsStreaming = true, + MetaData = new Dictionary + { + [Constants.ThoughtSignature] = part?.ThoughtSignature + } }; tokenUsage = response?.UsageMetadata; @@ -381,6 +417,7 @@ public void SetModelName(string model) contents.Add(new Content([ new Part() { + ThoughtSignature = message.MetaData?.GetValueOrDefault(Constants.ThoughtSignature, null), FunctionCall = new FunctionCall { Id = message.ToolCallId, @@ -393,6 +430,7 @@ public void SetModelName(string model) contents.Add(new Content([ new Part() { + ThoughtSignature = message.MetaData?.GetValueOrDefault(Constants.ThoughtSignature, null), FunctionResponse = new FunctionResponse { Id = message.ToolCallId, @@ -410,7 +448,14 @@ public void SetModelName(string model) else if (message.Role == AgentRole.User) { var text = message.LlmContent; - var contentParts = new List { new() { Text = text } }; + var contentParts = new List + { + new() + { + Text = text, + ThoughtSignature = message.MetaData?.GetValueOrDefault(Constants.ThoughtSignature, null) + } + }; if (allowMultiModal && !message.Files.IsNullOrEmpty()) { @@ -422,7 +467,14 @@ public void SetModelName(string model) else if (message.Role == AgentRole.Assistant) { var text = message.LlmContent; - var contentParts = new List { new() { Text = text } }; + var contentParts = new List + { + new() + { + Text = text, + ThoughtSignature = message.MetaData?.GetValueOrDefault(Constants.ThoughtSignature, null) + } + }; if (allowMultiModal && !message.Files.IsNullOrEmpty()) { diff --git a/src/Plugins/BotSharp.Plugin.GoogleAI/Using.cs b/src/Plugins/BotSharp.Plugin.GoogleAI/Using.cs index daff1e0b0..c69582963 100644 --- a/src/Plugins/BotSharp.Plugin.GoogleAI/Using.cs +++ b/src/Plugins/BotSharp.Plugin.GoogleAI/Using.cs @@ -26,5 +26,7 @@ global using BotSharp.Abstraction.Functions.Models; global using BotSharp.Abstraction.Loggers; +global using BotSharp.Plugin.GoogleAI.Constants; +global using BotSharp.Plugin.GoogleAI.Models.Chat; global using BotSharp.Plugin.GoogleAi.Settings; global using BotSharp.Plugin.GoogleAi.Providers.Chat; \ 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 7dd7ece77..ff70bdab8 100644 --- a/src/Plugins/BotSharp.Plugin.MongoStorage/Models/DialogMongoElement.cs +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Models/DialogMongoElement.cs @@ -47,9 +47,10 @@ public class DialogMetaDataMongoElement public string MessageId { get; set; } = default!; public string MessageType { get; set; } = default!; public string? MessageLabel { get; set; } + public string? ToolCallId { get; set; } public string? FunctionName { get; set; } public string? FunctionArgs { get; set; } - public string? ToolCallId { get; set; } + public Dictionary? MetaData { get; set; } public string? SenderId { get; set; } public DateTime CreateTime { get; set; } @@ -62,11 +63,12 @@ public static DialogMetaData ToDomainElement(DialogMetaDataMongoElement meta) MessageId = meta.MessageId, MessageType = meta.MessageType, MessageLabel = meta.MessageLabel, + ToolCallId = meta.ToolCallId, FunctionName = meta.FunctionName, FunctionArgs = meta.FunctionArgs, - ToolCallId = meta.ToolCallId, + MetaData = meta.MetaData, SenderId = meta.SenderId, - CreatedTime = meta.CreateTime, + CreatedTime = meta.CreateTime }; } @@ -79,11 +81,12 @@ public static DialogMetaDataMongoElement ToMongoElement(DialogMetaData meta) MessageId = meta.MessageId, MessageType = meta.MessageType, MessageLabel = meta.MessageLabel, + ToolCallId = meta.ToolCallId, FunctionName = meta.FunctionName, FunctionArgs = meta.FunctionArgs, - ToolCallId = meta.ToolCallId, + MetaData = meta.MetaData, SenderId = meta.SenderId, - CreateTime = meta.CreatedTime, + CreateTime = meta.CreatedTime }; } } diff --git a/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Chat/ChatCompletionProvider.cs b/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Chat/ChatCompletionProvider.cs index cffe6e6b0..195a1e803 100644 --- a/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Chat/ChatCompletionProvider.cs +++ b/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Chat/ChatCompletionProvider.cs @@ -260,16 +260,15 @@ public async Task GetChatCompletionsStreamingAsync(Agent agent, _logger.LogDebug($"Stream Content update: {text}"); #endif - var content = new RoleDialogModel(AgentRole.Assistant, text) - { - CurrentAgentId = agent.Id, - MessageId = messageId - }; hub.Push(new() { EventName = ChatEvent.OnReceiveLlmStreamMessage, RefId = conv.ConversationId, - Data = content + Data = new RoleDialogModel(AgentRole.Assistant, text) + { + CurrentAgentId = agent.Id, + MessageId = messageId + } }); }