From 60ce930d4c689cb29d2cf27de2ef066edff45ef8 Mon Sep 17 00:00:00 2001 From: hchen Date: Thu, 27 Jul 2023 16:56:57 -0500 Subject: [PATCH] Function execution status. --- .../ConversationCompletionHookBase.cs | 5 ++ .../IConversationCompletionHook.cs | 1 + .../Conversations/Models/FunctionDef.cs | 5 ++ .../Models/FunctionExecutionStatus.cs | 7 +++ .../FunctionExecutionValidationResult.cs | 21 ++++++++ .../Models/IFunctionExecutionResult.cs | 9 ++++ .../Conversations/Models/RoleDialogModel.cs | 7 ++- .../MLTasks/IChatCompletion.cs | 1 + .../Services/ConversationService.cs | 48 ++++++++++++++----- .../LLamaSharp/ChatCompletionProvider.cs | 5 ++ .../Providers/ChatCompletionProvider.cs | 46 ++++++++++++++++++ 11 files changed, 143 insertions(+), 12 deletions(-) create mode 100644 src/Infrastructure/BotSharp.Abstraction/Conversations/Models/FunctionExecutionStatus.cs create mode 100644 src/Infrastructure/BotSharp.Abstraction/Conversations/Models/FunctionExecutionValidationResult.cs create mode 100644 src/Infrastructure/BotSharp.Abstraction/Conversations/Models/IFunctionExecutionResult.cs diff --git a/src/Infrastructure/BotSharp.Abstraction/Conversations/ConversationCompletionHookBase.cs b/src/Infrastructure/BotSharp.Abstraction/Conversations/ConversationCompletionHookBase.cs index d885e7f1..32f22aa0 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Conversations/ConversationCompletionHookBase.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Conversations/ConversationCompletionHookBase.cs @@ -46,6 +46,11 @@ public abstract class ConversationCompletionHookBase : IConversationCompletionHo return Task.CompletedTask; } + public virtual async Task OnFunctionExecution(string name, string args) + { + return new FunctionExecutionValidationResult("true", ""); + } + public virtual Task AfterCompletion(RoleDialogModel message) { return Task.CompletedTask; diff --git a/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationCompletionHook.cs b/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationCompletionHook.cs index ece8d785..766ad824 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationCompletionHook.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationCompletionHook.cs @@ -18,5 +18,6 @@ public interface IConversationCompletionHook IConversationCompletionHook SetChatCompletion(IChatCompletion chatCompletion); Task BeforeCompletion(); + Task OnFunctionExecution(string name, string args); Task AfterCompletion(RoleDialogModel message); } diff --git a/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/FunctionDef.cs b/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/FunctionDef.cs index 5c050b5a..719f581d 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/FunctionDef.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/FunctionDef.cs @@ -7,4 +7,9 @@ public class FunctionDef public string Name { get; set; } public string Description { get; set; } public JsonDocument Parameters { get; set; } + + public override string ToString() + { + return $"{Name}: {Description}"; + } } diff --git a/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/FunctionExecutionStatus.cs b/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/FunctionExecutionStatus.cs new file mode 100644 index 00000000..3d8224cd --- /dev/null +++ b/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/FunctionExecutionStatus.cs @@ -0,0 +1,7 @@ +namespace BotSharp.Abstraction.Conversations.Models; + +public enum FunctionExecutionStatus +{ + Success = 1, + Failure = 2 +} diff --git a/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/FunctionExecutionValidationResult.cs b/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/FunctionExecutionValidationResult.cs new file mode 100644 index 00000000..81e250e4 --- /dev/null +++ b/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/FunctionExecutionValidationResult.cs @@ -0,0 +1,21 @@ +using System.Text.Json.Serialization; + +namespace BotSharp.Abstraction.Conversations.Models; + +public class FunctionExecutionValidationResult : IFunctionExecutionResult +{ + private string _validationStatus; + public string _validationMessage; + + public FunctionExecutionValidationResult(string validationStatus, string validationMessage = "") + { + _validationStatus = validationStatus; + _validationMessage = validationMessage; + } + + [JsonPropertyName("validation_status")] + public string ValidationStatus => _validationStatus; + + [JsonPropertyName("validation_message")] + public string ValidationMessage => _validationMessage; +} diff --git a/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/IFunctionExecutionResult.cs b/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/IFunctionExecutionResult.cs new file mode 100644 index 00000000..636b13ca --- /dev/null +++ b/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/IFunctionExecutionResult.cs @@ -0,0 +1,9 @@ +using System.Text.Json.Serialization; + +namespace BotSharp.Abstraction.Conversations.Models; + +public class IFunctionExecutionResult +{ + [JsonPropertyName("execution_status")] + public FunctionExecutionStatus ExecutionStatus { get; set; } +} diff --git a/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/RoleDialogModel.cs b/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/RoleDialogModel.cs index 3f1d6530..b5ef0338 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/RoleDialogModel.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/RoleDialogModel.cs @@ -11,7 +11,12 @@ public class RoleDialogModel /// /// Function name if LLM response function call /// - public string? Name { get; set; } + public string? Function { get; set; } + + /// + /// Function execution result + /// + public string? ExecutionResult { get; set; } public RoleDialogModel(string role, string text) { diff --git a/src/Infrastructure/BotSharp.Abstraction/MLTasks/IChatCompletion.cs b/src/Infrastructure/BotSharp.Abstraction/MLTasks/IChatCompletion.cs index 8c997ef8..2e8f6438 100644 --- a/src/Infrastructure/BotSharp.Abstraction/MLTasks/IChatCompletion.cs +++ b/src/Infrastructure/BotSharp.Abstraction/MLTasks/IChatCompletion.cs @@ -5,5 +5,6 @@ namespace BotSharp.Abstraction.MLTasks; public interface IChatCompletion { string GetChatCompletions(Agent agent, List conversations); + Task GetChatCompletionsAsync(Agent agent, List conversations, Func onMessageReceived); Task GetChatCompletionsStreamingAsync(Agent agent, List conversations, Func onMessageReceived); } diff --git a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.cs b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.cs index d608524b..513ae48f 100644 --- a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.cs +++ b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.cs @@ -2,6 +2,7 @@ using BotSharp.Abstraction.Conversations.Models; using BotSharp.Abstraction.Conversations.Settings; using BotSharp.Abstraction.Knowledges.Models; using BotSharp.Abstraction.MLTasks; +using System.Text.Json; namespace BotSharp.Core.Conversations.Services; @@ -75,8 +76,17 @@ public class ConversationService : IConversationService var response = await SendMessage(agentId, conversationId, wholeDialogs, async msg => { - await onMessageReceived(msg); - _storage.Append(agentId, conversationId, new RoleDialogModel(msg.Role, msg.Content)); + var content = msg.Content.Replace("\r", " ").Replace("\n", " "); + if (msg.Role == "function") + { + content += $"[{msg.Function}] {content}"; + _storage.Append(agentId, conversationId, new RoleDialogModel(msg.Role, content)); + } + else + { + await onMessageReceived(msg); + _storage.Append(agentId, conversationId, new RoleDialogModel(msg.Role, content)); + } }); return response; @@ -100,23 +110,39 @@ public class ConversationService : IConversationService var chatCompletion = GetChatCompletion(); - // Before chat completion hook var hooks = _services.GetServices().ToList(); - hooks.ForEach(hook => + // Before chat completion hook + foreach (var hook in hooks) { - hook.SetAgent(agent) + await hook.SetAgent(agent) .SetConversation(converation) .SetDialogs(wholeDialogs) .SetChatCompletion(chatCompletion) .BeforeCompletion(); - }); - - var result = await chatCompletion.GetChatCompletionsStreamingAsync(agent, wholeDialogs, async msg => + } + + var result = await chatCompletion.GetChatCompletionsAsync(agent, wholeDialogs, async msg => { - // After chat completion hook - hooks.ForEach(async hook => await hook.AfterCompletion(msg)); - await onMessageReceived(msg); + if (msg.Role == "function") + { + // Execute functions + foreach (var hook in hooks) + { + var executionResult = await hook.OnFunctionExecution(msg.Function, msg.Content); + msg.ExecutionResult = JsonSerializer.Serialize(executionResult); + } + } + else + { + // After chat completion hook + foreach (var hook in hooks) + { + await hook.AfterCompletion(msg); + } + + await onMessageReceived(msg); + } }); return result; diff --git a/src/Infrastructure/BotSharp.Core/Plugins/LLamaSharp/ChatCompletionProvider.cs b/src/Infrastructure/BotSharp.Core/Plugins/LLamaSharp/ChatCompletionProvider.cs index 01636aff..ea32da04 100644 --- a/src/Infrastructure/BotSharp.Core/Plugins/LLamaSharp/ChatCompletionProvider.cs +++ b/src/Infrastructure/BotSharp.Core/Plugins/LLamaSharp/ChatCompletionProvider.cs @@ -19,6 +19,11 @@ public class ChatCompletionProvider : IChatCompletion throw new NotImplementedException(); } + public Task GetChatCompletionsAsync(Agent agent, List conversations, Func onMessageReceived) + { + throw new NotImplementedException(); + } + public async Task GetChatCompletionsStreamingAsync(Agent agent, List conversations, Func onMessageReceived) { string totalResponse = ""; diff --git a/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/ChatCompletionProvider.cs b/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/ChatCompletionProvider.cs index cf1c6de7..e0a9fb5c 100644 --- a/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/ChatCompletionProvider.cs +++ b/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/ChatCompletionProvider.cs @@ -86,6 +86,41 @@ public class ChatCompletionProvider : IChatCompletion return functions; } + public async Task GetChatCompletionsAsync(Agent agent, List conversations, Func onMessageReceived) + { + var client = new OpenAIClient(new Uri(_settings.Endpoint), new AzureKeyCredential(_settings.ApiKey)); + var chatCompletionsOptions = PrepareOptions(agent, conversations); + + var response = await client.GetChatCompletionsAsync(_settings.DeploymentModel.ChatCompletionModel, chatCompletionsOptions); + var choice = response.Value.Choices[0]; + var message = choice.Message; + + if (choice.FinishReason == CompletionsFinishReason.FunctionCall) + { + if (message.FunctionCall == null || message.FunctionCall.Arguments == null) + { + return false; + } + Console.Write(message.FunctionCall.Name); + Console.Write(message.FunctionCall.Arguments); + var funcContextIn = new RoleDialogModel(ChatRole.Function.ToString(), message.FunctionCall.Arguments) + { + Function = message.FunctionCall.Name + }; + await onMessageReceived(funcContextIn); + + // After function is executed, pass the result to LLM + throw new NotImplementedException(); + } + else + { + Console.Write(message.Content); + await onMessageReceived(new RoleDialogModel(ChatRole.Assistant.ToString(), message.Content)); + } + + return true; + } + public async Task GetChatCompletionsStreamingAsync(Agent agent, List conversations, Func onMessageReceived) { var client = new OpenAIClient(new Uri(_settings.Endpoint), new AzureKeyCredential(_settings.ApiKey)); @@ -99,6 +134,17 @@ public class ChatCompletionProvider : IChatCompletion { if (choice.FinishReason == CompletionsFinishReason.FunctionCall) { + var args = ""; + await foreach (var message in choice.GetMessageStreaming()) + { + if (message.FunctionCall == null || message.FunctionCall.Arguments == null) + continue; + Console.Write(message.FunctionCall.Arguments); + args += message.FunctionCall.Arguments; + + } + await onMessageReceived(new RoleDialogModel(ChatRole.Assistant.ToString(), args)); + continue; } await foreach (var message in choice.GetMessageStreaming())