From 450bef891c3b97aae99767ec407f99b6c5f0815c Mon Sep 17 00:00:00 2001 From: Haiping Chen Date: Wed, 4 Sep 2024 12:00:27 -0500 Subject: [PATCH 1/6] Hints --- .../Controllers/TwilioVoiceController.cs | 14 ++++++++++---- .../Services/TwilioService.cs | 5 +++-- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/Plugins/BotSharp.Plugin.Twilio/Controllers/TwilioVoiceController.cs b/src/Plugins/BotSharp.Plugin.Twilio/Controllers/TwilioVoiceController.cs index 068fdbb6..207a0ff2 100644 --- a/src/Plugins/BotSharp.Plugin.Twilio/Controllers/TwilioVoiceController.cs +++ b/src/Plugins/BotSharp.Plugin.Twilio/Controllers/TwilioVoiceController.cs @@ -1,9 +1,11 @@ +using Azure.Messaging; using BotSharp.Abstraction.Files; using BotSharp.Core.Infrastructures; using BotSharp.Plugin.Twilio.Models; using BotSharp.Plugin.Twilio.Services; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; +using System.Security.Cryptography; using Twilio.Http; namespace BotSharp.Plugin.Twilio.Controllers; @@ -36,7 +38,7 @@ public class TwilioVoiceController : TwilioController string conversationId = $"TwilioVoice_{request.CallSid}"; var twilio = _services.GetRequiredService(); var url = $"twilio/voice/{conversationId}/receive/0?states={states}"; - var response = twilio.ReturnNoninterruptedInstructions(new List { "twilio/welcome.mp3" }, url, true); + var response = twilio.ReturnNoninterruptedInstructions(new List { "twilio/welcome.mp3" }, url, true, timeout: 2); return TwiML(response); } @@ -81,9 +83,10 @@ public class TwilioVoiceController : TwilioController } else { - if (attempts >= 3) + if (attempts >= 2) { var speechPaths = new List(); + if (seqNum == 0) { speechPaths.Add("twilio/welcome.mp3"); @@ -91,6 +94,7 @@ public class TwilioVoiceController : TwilioController else { var lastRepy = await sessionManager.GetAssistantReplyAsync(conversationId, seqNum - 1); + speechPaths.Add($"twilio/say-it-again-{Random.Shared.Next(1, 5)}.mp3"); speechPaths.Add($"twilio/voice/speeches/{conversationId}/{lastRepy.SpeechFileName}"); } response = twilio.ReturnInstructions(speechPaths, $"twilio/voice/{conversationId}/receive/{seqNum}?states={states}", true); @@ -139,6 +143,8 @@ public class TwilioVoiceController : TwilioController var fileName = $"indication_{seqNum}_{segIndex}.mp3"; await fileService.SaveSpeechFileAsync(conversationId, fileName, data); speechPaths.Add($"twilio/voice/speeches/{conversationId}/{fileName}"); + // add typing + speechPaths.Add($"twilio/typing-{Random.Shared.Next(1, 4)}.mp3"); segIndex++; } } @@ -149,8 +155,8 @@ public class TwilioVoiceController : TwilioController { response = twilio.ReturnInstructions(new List { - $"twilio/hold-on-{Random.Shared.Next(1, 5)}.mp3", - $"twilio/typing-{Random.Shared.Next(2, 4)}.mp3" + $"twilio/hold-on-{Random.Shared.Next(1, 6)}.mp3", + $"twilio/typing-{Random.Shared.Next(1, 4)}.mp3" }, $"twilio/voice/{conversationId}/reply/{seqNum}?states={states}", true); } } diff --git a/src/Plugins/BotSharp.Plugin.Twilio/Services/TwilioService.cs b/src/Plugins/BotSharp.Plugin.Twilio/Services/TwilioService.cs index ee1a31e7..3f568811 100644 --- a/src/Plugins/BotSharp.Plugin.Twilio/Services/TwilioService.cs +++ b/src/Plugins/BotSharp.Plugin.Twilio/Services/TwilioService.cs @@ -78,7 +78,8 @@ public class TwilioService SpeechModel = Gather.SpeechModelEnum.PhoneCall, SpeechTimeout = "auto", // timeout > 0 ? timeout.ToString() : "3", Timeout = timeout > 0 ? timeout : 3, - ActionOnEmptyResult = actionOnEmptyResult + ActionOnEmptyResult = actionOnEmptyResult, + Hints = "Yes, No, Correct" }; if (speechPaths != null && speechPaths.Any()) { @@ -110,7 +111,7 @@ public class TwilioService }, Action = new Uri($"{_settings.CallbackHost}/{callbackPath}"), SpeechModel = Gather.SpeechModelEnum.PhoneCall, - SpeechTimeout = "auto", // timeout > 0 ? timeout.ToString() : "3", + SpeechTimeout = timeout > 0 ? timeout.ToString() : "3", Timeout = timeout > 0 ? timeout : 3, ActionOnEmptyResult = actionOnEmptyResult }; From b9a06bd6bab2a26b55ee8581f812f4aefe86cec5 Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Thu, 5 Sep 2024 10:34:35 -0500 Subject: [PATCH 2/6] relocate --- .../Conversations/ConversationPlugin.cs | 1 - .../Helpers/KnowledgeSettingHelper.cs | 5 +---- .../Helpers/TextChopper.cs | 3 +-- .../KnowledgeBasePlugin.cs | 2 +- .../Services/KnowledgeService.Create.cs | 6 +----- .../Services/KnowledgeService.Delete.cs | 2 +- .../Services/KnowledgeService.Get.cs | 6 +----- .../Services/KnowledgeService.Update.cs | 4 +--- .../Services/KnowledgeService.cs | 8 +------- src/Plugins/BotSharp.Plugin.KnowledgeBase/Using.cs | 1 + .../Functions/GetTableDefinitionFn.cs | 12 ++---------- .../Services/DbKnowledgeService.cs | 6 +++--- 12 files changed, 14 insertions(+), 42 deletions(-) rename src/{Infrastructure/BotSharp.Core/Knowledges => Plugins/BotSharp.Plugin.KnowledgeBase}/Helpers/KnowledgeSettingHelper.cs (89%) rename src/{Infrastructure/BotSharp.Core/Knowledges => Plugins/BotSharp.Plugin.KnowledgeBase}/Helpers/TextChopper.cs (94%) rename src/{Infrastructure/BotSharp.Core/Knowledges => Plugins/BotSharp.Plugin.KnowledgeBase}/Services/KnowledgeService.Create.cs (92%) rename src/{Infrastructure/BotSharp.Core/Knowledges => Plugins/BotSharp.Plugin.KnowledgeBase}/Services/KnowledgeService.Delete.cs (95%) rename src/{Infrastructure/BotSharp.Core/Knowledges => Plugins/BotSharp.Plugin.KnowledgeBase}/Services/KnowledgeService.Get.cs (95%) rename src/{Infrastructure/BotSharp.Core/Knowledges => Plugins/BotSharp.Plugin.KnowledgeBase}/Services/KnowledgeService.Update.cs (91%) rename src/{Infrastructure/BotSharp.Core/Knowledges => Plugins/BotSharp.Plugin.KnowledgeBase}/Services/KnowledgeService.cs (80%) diff --git a/src/Infrastructure/BotSharp.Core/Conversations/ConversationPlugin.cs b/src/Infrastructure/BotSharp.Core/Conversations/ConversationPlugin.cs index 70c91d9f..f06e2ba2 100644 --- a/src/Infrastructure/BotSharp.Core/Conversations/ConversationPlugin.cs +++ b/src/Infrastructure/BotSharp.Core/Conversations/ConversationPlugin.cs @@ -6,7 +6,6 @@ using BotSharp.Abstraction.Routing.Planning; using BotSharp.Abstraction.Settings; using BotSharp.Abstraction.Templating; using BotSharp.Core.Instructs; -using BotSharp.Core.Knowledges.Services; using BotSharp.Core.Messaging; using BotSharp.Core.Routing.Planning; using BotSharp.Core.Templating; diff --git a/src/Infrastructure/BotSharp.Core/Knowledges/Helpers/KnowledgeSettingHelper.cs b/src/Plugins/BotSharp.Plugin.KnowledgeBase/Helpers/KnowledgeSettingHelper.cs similarity index 89% rename from src/Infrastructure/BotSharp.Core/Knowledges/Helpers/KnowledgeSettingHelper.cs rename to src/Plugins/BotSharp.Plugin.KnowledgeBase/Helpers/KnowledgeSettingHelper.cs index a278d7bf..48033c91 100644 --- a/src/Infrastructure/BotSharp.Core/Knowledges/Helpers/KnowledgeSettingHelper.cs +++ b/src/Plugins/BotSharp.Plugin.KnowledgeBase/Helpers/KnowledgeSettingHelper.cs @@ -1,7 +1,4 @@ -using BotSharp.Abstraction.Knowledges.Settings; -using BotSharp.Abstraction.MLTasks; - -namespace BotSharp.Core.Knowledges.Helpers; +namespace BotSharp.Plugin.KnowledgeBase.Helpers; public static class KnowledgeSettingHelper { diff --git a/src/Infrastructure/BotSharp.Core/Knowledges/Helpers/TextChopper.cs b/src/Plugins/BotSharp.Plugin.KnowledgeBase/Helpers/TextChopper.cs similarity index 94% rename from src/Infrastructure/BotSharp.Core/Knowledges/Helpers/TextChopper.cs rename to src/Plugins/BotSharp.Plugin.KnowledgeBase/Helpers/TextChopper.cs index 68b58539..9e9b5f9d 100644 --- a/src/Infrastructure/BotSharp.Core/Knowledges/Helpers/TextChopper.cs +++ b/src/Plugins/BotSharp.Plugin.KnowledgeBase/Helpers/TextChopper.cs @@ -1,7 +1,6 @@ -using BotSharp.Abstraction.Knowledges.Models; using System.Text.RegularExpressions; -namespace BotSharp.Core.Knowledges.Helpers; +namespace BotSharp.Plugin.KnowledgeBase.Helpers; public static class TextChopper { diff --git a/src/Plugins/BotSharp.Plugin.KnowledgeBase/KnowledgeBasePlugin.cs b/src/Plugins/BotSharp.Plugin.KnowledgeBase/KnowledgeBasePlugin.cs index 0e7bcc63..0f28aa5e 100644 --- a/src/Plugins/BotSharp.Plugin.KnowledgeBase/KnowledgeBasePlugin.cs +++ b/src/Plugins/BotSharp.Plugin.KnowledgeBase/KnowledgeBasePlugin.cs @@ -1,8 +1,8 @@ using BotSharp.Abstraction.Plugins.Models; using BotSharp.Abstraction.Settings; -using BotSharp.Core.Knowledges.Services; using BotSharp.Plugin.KnowledgeBase.Converters; using BotSharp.Plugin.KnowledgeBase.Hooks; +using BotSharp.Plugin.KnowledgeBase.Services; using Microsoft.Extensions.Configuration; namespace BotSharp.Plugin.KnowledgeBase; diff --git a/src/Infrastructure/BotSharp.Core/Knowledges/Services/KnowledgeService.Create.cs b/src/Plugins/BotSharp.Plugin.KnowledgeBase/Services/KnowledgeService.Create.cs similarity index 92% rename from src/Infrastructure/BotSharp.Core/Knowledges/Services/KnowledgeService.Create.cs rename to src/Plugins/BotSharp.Plugin.KnowledgeBase/Services/KnowledgeService.Create.cs index d8b82fb5..2c4004fc 100644 --- a/src/Infrastructure/BotSharp.Core/Knowledges/Services/KnowledgeService.Create.cs +++ b/src/Plugins/BotSharp.Plugin.KnowledgeBase/Services/KnowledgeService.Create.cs @@ -1,8 +1,4 @@ -using BotSharp.Abstraction.Knowledges.Models; -using BotSharp.Abstraction.VectorStorage.Models; -using BotSharp.Core.Knowledges.Helpers; - -namespace BotSharp.Core.Knowledges.Services; +namespace BotSharp.Plugin.KnowledgeBase.Services; public partial class KnowledgeService { diff --git a/src/Infrastructure/BotSharp.Core/Knowledges/Services/KnowledgeService.Delete.cs b/src/Plugins/BotSharp.Plugin.KnowledgeBase/Services/KnowledgeService.Delete.cs similarity index 95% rename from src/Infrastructure/BotSharp.Core/Knowledges/Services/KnowledgeService.Delete.cs rename to src/Plugins/BotSharp.Plugin.KnowledgeBase/Services/KnowledgeService.Delete.cs index 404b4e2c..b0973a99 100644 --- a/src/Infrastructure/BotSharp.Core/Knowledges/Services/KnowledgeService.Delete.cs +++ b/src/Plugins/BotSharp.Plugin.KnowledgeBase/Services/KnowledgeService.Delete.cs @@ -1,4 +1,4 @@ -namespace BotSharp.Core.Knowledges.Services; +namespace BotSharp.Plugin.KnowledgeBase.Services; public partial class KnowledgeService { diff --git a/src/Infrastructure/BotSharp.Core/Knowledges/Services/KnowledgeService.Get.cs b/src/Plugins/BotSharp.Plugin.KnowledgeBase/Services/KnowledgeService.Get.cs similarity index 95% rename from src/Infrastructure/BotSharp.Core/Knowledges/Services/KnowledgeService.Get.cs rename to src/Plugins/BotSharp.Plugin.KnowledgeBase/Services/KnowledgeService.Get.cs index 69093659..dcffa944 100644 --- a/src/Infrastructure/BotSharp.Core/Knowledges/Services/KnowledgeService.Get.cs +++ b/src/Plugins/BotSharp.Plugin.KnowledgeBase/Services/KnowledgeService.Get.cs @@ -1,8 +1,4 @@ -using BotSharp.Abstraction.Graph.Models; -using BotSharp.Abstraction.Knowledges.Models; -using BotSharp.Abstraction.VectorStorage.Models; - -namespace BotSharp.Core.Knowledges.Services; +namespace BotSharp.Plugin.KnowledgeBase.Services; public partial class KnowledgeService { diff --git a/src/Infrastructure/BotSharp.Core/Knowledges/Services/KnowledgeService.Update.cs b/src/Plugins/BotSharp.Plugin.KnowledgeBase/Services/KnowledgeService.Update.cs similarity index 91% rename from src/Infrastructure/BotSharp.Core/Knowledges/Services/KnowledgeService.Update.cs rename to src/Plugins/BotSharp.Plugin.KnowledgeBase/Services/KnowledgeService.Update.cs index b9652f61..0fa1f5cd 100644 --- a/src/Infrastructure/BotSharp.Core/Knowledges/Services/KnowledgeService.Update.cs +++ b/src/Plugins/BotSharp.Plugin.KnowledgeBase/Services/KnowledgeService.Update.cs @@ -1,6 +1,4 @@ -using BotSharp.Abstraction.VectorStorage.Models; - -namespace BotSharp.Core.Knowledges.Services; +namespace BotSharp.Plugin.KnowledgeBase.Services; public partial class KnowledgeService { diff --git a/src/Infrastructure/BotSharp.Core/Knowledges/Services/KnowledgeService.cs b/src/Plugins/BotSharp.Plugin.KnowledgeBase/Services/KnowledgeService.cs similarity index 80% rename from src/Infrastructure/BotSharp.Core/Knowledges/Services/KnowledgeService.cs rename to src/Plugins/BotSharp.Plugin.KnowledgeBase/Services/KnowledgeService.cs index 2c0853d0..c668e07e 100644 --- a/src/Infrastructure/BotSharp.Core/Knowledges/Services/KnowledgeService.cs +++ b/src/Plugins/BotSharp.Plugin.KnowledgeBase/Services/KnowledgeService.cs @@ -1,10 +1,4 @@ -using BotSharp.Abstraction.Graph; -using BotSharp.Abstraction.Knowledges.Settings; -using BotSharp.Abstraction.MLTasks; -using BotSharp.Abstraction.VectorStorage; -using BotSharp.Core.Knowledges.Helpers; - -namespace BotSharp.Core.Knowledges.Services; +namespace BotSharp.Plugin.KnowledgeBase.Services; public partial class KnowledgeService : IKnowledgeService { diff --git a/src/Plugins/BotSharp.Plugin.KnowledgeBase/Using.cs b/src/Plugins/BotSharp.Plugin.KnowledgeBase/Using.cs index 1437f41e..53ed55a2 100644 --- a/src/Plugins/BotSharp.Plugin.KnowledgeBase/Using.cs +++ b/src/Plugins/BotSharp.Plugin.KnowledgeBase/Using.cs @@ -20,6 +20,7 @@ global using BotSharp.Abstraction.Knowledges.Settings; global using BotSharp.Abstraction.Knowledges.Enums; global using BotSharp.Abstraction.VectorStorage; global using BotSharp.Abstraction.VectorStorage.Models; +global using BotSharp.Abstraction.Graph.Models; global using BotSharp.Abstraction.Knowledges.Models; global using BotSharp.Abstraction.MLTasks; global using BotSharp.Abstraction.Functions; diff --git a/src/Plugins/BotSharp.Plugin.SqlDriver/Functions/GetTableDefinitionFn.cs b/src/Plugins/BotSharp.Plugin.SqlDriver/Functions/GetTableDefinitionFn.cs index 31c3a31e..a9fb5005 100644 --- a/src/Plugins/BotSharp.Plugin.SqlDriver/Functions/GetTableDefinitionFn.cs +++ b/src/Plugins/BotSharp.Plugin.SqlDriver/Functions/GetTableDefinitionFn.cs @@ -36,22 +36,14 @@ public class GetTableDefinitionFn : IFunctionCallback { try { - var sql = $"select * from information_schema.tables where table_name = @tableName"; var escapedTableName = MySqlHelper.EscapeString(table); + var sql = $"SHOW CREATE TABLE `{escapedTableName}`"; - var result = connection.QueryFirstOrDefault(sql, new - { - tableName = escapedTableName - }); - - if (result == null) continue; - - sql = $"SHOW CREATE TABLE `{escapedTableName}`"; using var command = new MySqlCommand(sql, connection); using var reader = command.ExecuteReader(); if (reader.Read()) { - result = reader.GetString(1); + var result = reader.GetString(1); tableDdls.Add(result); } diff --git a/src/Plugins/BotSharp.Plugin.SqlDriver/Services/DbKnowledgeService.cs b/src/Plugins/BotSharp.Plugin.SqlDriver/Services/DbKnowledgeService.cs index 746798c9..129da04c 100644 --- a/src/Plugins/BotSharp.Plugin.SqlDriver/Services/DbKnowledgeService.cs +++ b/src/Plugins/BotSharp.Plugin.SqlDriver/Services/DbKnowledgeService.cs @@ -91,13 +91,13 @@ public class DbKnowledgeService private string GetTableStructure(string table) { var settings = _services.GetRequiredService(); - using var connection = new MySqlConnection(settings.MySqlConnectionString); - connection.Open(); - + var ddl = string.Empty; var escapedTableName = MySqlHelper.EscapeString(table); var sql = $"SHOW CREATE TABLE `{escapedTableName}`"; + using var connection = new MySqlConnection(settings.MySqlConnectionString); + connection.Open(); using var command = new MySqlCommand(sql, connection); using var reader = command.ExecuteReader(); if (reader.Read()) From 95736016b4a36e907a192977cca298c6ec35be56 Mon Sep 17 00:00:00 2001 From: Haiping Chen Date: Thu, 5 Sep 2024 10:42:48 -0500 Subject: [PATCH 3/6] Fix voice enqueue. --- .../BotSharp.Plugin.Twilio/Controllers/TwilioVoiceController.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Plugins/BotSharp.Plugin.Twilio/Controllers/TwilioVoiceController.cs b/src/Plugins/BotSharp.Plugin.Twilio/Controllers/TwilioVoiceController.cs index 190d5835..c820e40c 100644 --- a/src/Plugins/BotSharp.Plugin.Twilio/Controllers/TwilioVoiceController.cs +++ b/src/Plugins/BotSharp.Plugin.Twilio/Controllers/TwilioVoiceController.cs @@ -84,6 +84,8 @@ public class TwilioVoiceController : TwilioController } } + await messageQueue.EnqueueAsync(callerMessage); + response = new VoiceResponse().Redirect(new Uri($"{_settings.CallbackHost}/twilio/voice/{conversationId}/reply/{seqNum}?states={states}"), HttpMethod.Post); } else From c7365c3ca472b81ca18e5d6cbb9ad279396d265a Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Thu, 5 Sep 2024 11:51:43 -0500 Subject: [PATCH 4/6] refine file dir --- .../VectorStorage/Extensions/VectorStorageExtension.cs | 6 +++++- .../BotSharp.Core/Agents/Services/AgentService.cs | 2 +- .../Routing/Handlers/RouteToAgentRoutingHandler.cs | 6 ++---- .../instructions/instruction.liquid | 1 + 4 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/Infrastructure/BotSharp.Abstraction/VectorStorage/Extensions/VectorStorageExtension.cs b/src/Infrastructure/BotSharp.Abstraction/VectorStorage/Extensions/VectorStorageExtension.cs index ec519f04..8c977cfb 100644 --- a/src/Infrastructure/BotSharp.Abstraction/VectorStorage/Extensions/VectorStorageExtension.cs +++ b/src/Infrastructure/BotSharp.Abstraction/VectorStorage/Extensions/VectorStorageExtension.cs @@ -9,7 +9,11 @@ public static class VectorStorageExtension { if (data?.Data == null) return string.Empty; - return $"Question: {data.Data[KnowledgePayloadName.Text]}\r\nAnswer: {data.Data[KnowledgePayloadName.Answer]}"; + if (data.Data.TryGetValue(KnowledgePayloadName.Text, out var question)) { } + + if (data.Data.TryGetValue(KnowledgePayloadName.Answer, out var answer)) { } + + return $"Question: {question}\r\nAnswer: {answer}"; } public static string ToPayloadPair(this VectorSearchResult data, IList payloads) diff --git a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.cs b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.cs index 40b80e50..b69f7529 100644 --- a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.cs +++ b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.cs @@ -41,7 +41,7 @@ public partial class AgentService : IAgentService public string GetAgentDataDir(string agentId) { var dbSettings = _services.GetRequiredService(); - var dir = Path.Combine(dbSettings.FileRepository, _agentSettings.DataDir, agentId); + var dir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, dbSettings.FileRepository, _agentSettings.DataDir, agentId); if (!Directory.Exists(dir)) { Directory.CreateDirectory(dir); diff --git a/src/Infrastructure/BotSharp.Core/Routing/Handlers/RouteToAgentRoutingHandler.cs b/src/Infrastructure/BotSharp.Core/Routing/Handlers/RouteToAgentRoutingHandler.cs index 94539b9b..adc1a1c3 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/Handlers/RouteToAgentRoutingHandler.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/Handlers/RouteToAgentRoutingHandler.cs @@ -64,7 +64,7 @@ public class RouteToAgentRoutingHandler : RoutingHandlerBase, IRoutingHandler if (message.FunctionName != null) { var msg = RoleDialogModel.From(message, role: AgentRole.Function); - var ret = await routing.InvokeFunction(message.FunctionName, msg); + await routing.InvokeFunction(message.FunctionName, msg); } var agentId = routing.Context.GetCurrentAgentId(); @@ -83,9 +83,7 @@ public class RouteToAgentRoutingHandler : RoutingHandlerBase, IRoutingHandler { var content = $"This agent ({agent.Name}) is disabled, please install the corresponding plugin ({agent.Plugin.Name}) to activate this agent."; - message = RoleDialogModel.From(message, - role: AgentRole.Assistant, - content: content); + message = RoleDialogModel.From(message, role: AgentRole.Assistant, content: content); _dialogs.Add(message); } else diff --git a/src/Plugins/BotSharp.Plugin.Planner/data/agents/282a7128-69a1-44b0-878c-a9159b88f3b9/instructions/instruction.liquid b/src/Plugins/BotSharp.Plugin.Planner/data/agents/282a7128-69a1-44b0-878c-a9159b88f3b9/instructions/instruction.liquid index f6571d18..e5013463 100644 --- a/src/Plugins/BotSharp.Plugin.Planner/data/agents/282a7128-69a1-44b0-878c-a9159b88f3b9/instructions/instruction.liquid +++ b/src/Plugins/BotSharp.Plugin.Planner/data/agents/282a7128-69a1-44b0-878c-a9159b88f3b9/instructions/instruction.liquid @@ -8,4 +8,5 @@ Global Knowledge: {% for k in global_knowledges %} {{ k }} {% endfor %} +===== {%- endif %} \ No newline at end of file From 10e626ab8aa285463d118e2d89bb71fe0fc0b5f8 Mon Sep 17 00:00:00 2001 From: Haiping Chen <101423@smsassist.com> Date: Thu, 5 Sep 2024 14:11:05 -0500 Subject: [PATCH 5/6] clean code --- .../Users/Enums/UserRole.cs | 12 ++ .../TwoStagePlanner.FirstStage.cs | 82 ----------- .../TwoStagePlanner.GetContext.cs | 14 -- .../TwoStagePlanner.SecondStage.cs | 83 ----------- .../TwoStagePlanner/TwoStagePlanner.cs | 136 ------------------ .../TwoStaging/TwoStageTaskPlanner.cs | 63 ++------ .../agent.json | 2 +- .../agent.json | 2 +- 8 files changed, 28 insertions(+), 366 deletions(-) delete mode 100644 src/Infrastructure/BotSharp.Core/Routing/Planning/TwoStagePlanner/TwoStagePlanner.FirstStage.cs delete mode 100644 src/Infrastructure/BotSharp.Core/Routing/Planning/TwoStagePlanner/TwoStagePlanner.GetContext.cs delete mode 100644 src/Infrastructure/BotSharp.Core/Routing/Planning/TwoStagePlanner/TwoStagePlanner.SecondStage.cs delete mode 100644 src/Infrastructure/BotSharp.Core/Routing/Planning/TwoStagePlanner/TwoStagePlanner.cs diff --git a/src/Infrastructure/BotSharp.Abstraction/Users/Enums/UserRole.cs b/src/Infrastructure/BotSharp.Abstraction/Users/Enums/UserRole.cs index 02c095bc..620a696e 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Users/Enums/UserRole.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Users/Enums/UserRole.cs @@ -17,6 +17,18 @@ public class UserRole /// public const string Client = "client"; + /// + /// Back office operations + /// + public const string Operation = "operation"; + + public const string Technician = "technician"; + + /// + /// Software Developers, Data Engineer, Business Analyst + /// + public const string Engineer = "engineer"; + /// /// AI Assistant /// diff --git a/src/Infrastructure/BotSharp.Core/Routing/Planning/TwoStagePlanner/TwoStagePlanner.FirstStage.cs b/src/Infrastructure/BotSharp.Core/Routing/Planning/TwoStagePlanner/TwoStagePlanner.FirstStage.cs deleted file mode 100644 index 98efbacc..00000000 --- a/src/Infrastructure/BotSharp.Core/Routing/Planning/TwoStagePlanner/TwoStagePlanner.FirstStage.cs +++ /dev/null @@ -1,82 +0,0 @@ -using BotSharp.Abstraction.MLTasks; -using BotSharp.Abstraction.Templating; - -namespace BotSharp.Core.Routing.Planning; - -public partial class TwoStagePlanner -{ - private async Task GetFirstStagePlanAsync(Agent router, string messageId, List dialogs) - { - var firstStagePlanPrompt = await GetFirstStagePlanPrompt(router); - - var plan = new FirstStagePlan[0]; - - var llmProviderService = _services.GetRequiredService(); - var provider = router.LlmConfig.Provider ?? "openai"; - var model = llmProviderService.GetProviderModel(provider, router.LlmConfig.Model ?? "gpt-4o"); - - // chat completion - var completion = CompletionProvider.GetChatCompletion(_services, - provider: provider, - model: model.Name); - - string text = string.Empty; - - try - { - var response = await completion.GetChatCompletions(new Agent - { - Id = router.Id, - Name = nameof(TwoStagePlanner), - Instruction = firstStagePlanPrompt - }, dialogs); - - text = response.Content; - plan = response.Content.JsonArrayContent(); - } - catch (Exception ex) - { - _logger.LogError($"{ex.Message}: {text}"); - } - - return plan; - } - - private async Task GetFirstStagePlanPrompt(Agent router) - { - var template = router.Templates.First(x => x.Name == "two_stage.1st.plan").Content; - var responseFormat = JsonSerializer.Serialize(new FirstStagePlan - { - Parameters = new JsonDocument[]{ JsonDocument.Parse("{}") }, - Results = new string[] { "" } - }); - - var relevantKnowledges = new List(); - var hooks = _services.GetServices(); - foreach (var hook in hooks) - { - var k = await hook.GetRelevantKnowledges(); - relevantKnowledges.AddRange(k); - } - - var render = _services.GetRequiredService(); - return render.Render(template, new Dictionary - { - { "response_format", responseFormat }, - { "relevant_knowledges", relevantKnowledges.ToArray() } - }); - } - - private string GetFirstStageNextPrompt(Agent router) - { - var template = router.Templates.First(x => x.Name == "first_stage.next").Content; - var responseFormat = JsonSerializer.Serialize(new FirstStagePlan - { - }); - var render = _services.GetRequiredService(); - return render.Render(template, new Dictionary - { - { "response_format", responseFormat }, - }); - } -} diff --git a/src/Infrastructure/BotSharp.Core/Routing/Planning/TwoStagePlanner/TwoStagePlanner.GetContext.cs b/src/Infrastructure/BotSharp.Core/Routing/Planning/TwoStagePlanner/TwoStagePlanner.GetContext.cs deleted file mode 100644 index 998bcd91..00000000 --- a/src/Infrastructure/BotSharp.Core/Routing/Planning/TwoStagePlanner/TwoStagePlanner.GetContext.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace BotSharp.Core.Routing.Planning; - -public partial class TwoStagePlanner -{ - public string GetContext() - { - var content = ""; - foreach (var c in _executionContext) - { - content += $"* {c}\r\n"; - } - return content; - } -} diff --git a/src/Infrastructure/BotSharp.Core/Routing/Planning/TwoStagePlanner/TwoStagePlanner.SecondStage.cs b/src/Infrastructure/BotSharp.Core/Routing/Planning/TwoStagePlanner/TwoStagePlanner.SecondStage.cs deleted file mode 100644 index 31ad6608..00000000 --- a/src/Infrastructure/BotSharp.Core/Routing/Planning/TwoStagePlanner/TwoStagePlanner.SecondStage.cs +++ /dev/null @@ -1,83 +0,0 @@ -using BotSharp.Abstraction.Agents.Models; -using BotSharp.Abstraction.MLTasks; -using BotSharp.Abstraction.Templating; - -namespace BotSharp.Core.Routing.Planning; - -public partial class TwoStagePlanner -{ - private async Task GetSecondStagePlanAsync(Agent router, string messageId, FirstStagePlan plan1st, List dialogs) - { - var secondStagePrompt = GetSecondStagePlanPrompt(router, plan1st); - var firstStageSystemPrompt = await GetFirstStagePlanPrompt(router); - - var plan = new SecondStagePlan[0]; - - var llmProviderService = _services.GetRequiredService(); - var model = llmProviderService.GetProviderModel("azure-openai", "gpt-4"); - - // chat completion - var completion = CompletionProvider.GetChatCompletion(_services, - provider: "azure-openai", - model: model.Name); - - string text = string.Empty; - - var conversations = dialogs.Where(x => x.Role != AgentRole.Function).ToList(); - conversations.Add(new RoleDialogModel(AgentRole.User, secondStagePrompt) - { - CurrentAgentId = router.Id, - MessageId = messageId, - }); - - try - { - var response = await completion.GetChatCompletions(new Agent - { - Id = router.Id, - Name = nameof(TwoStagePlanner), - Instruction = firstStageSystemPrompt - }, conversations); - - text = response.Content; - plan = response.Content.JsonArrayContent(); - } - catch (Exception ex) - { - _logger.LogError($"{ex.Message}: {text}"); - } - - return plan; - } - - private string GetSecondStageTaskPrompt(Agent router, SecondStagePlan plan) - { - var template = router.Templates.First(x => x.Name == "planner_prompt.two_stage.2nd.task").Content; - var render = _services.GetRequiredService(); - return render.Render(template, new Dictionary - { - { "task_description", plan.Description }, - { "related_tables", plan.Tables }, - { "input_arguments", JsonSerializer.Serialize(plan.Parameters) }, - { "output_results", JsonSerializer.Serialize(plan.Results) }, - }); - } - - private string GetSecondStagePlanPrompt(Agent router, FirstStagePlan plan) - { - var template = router.Templates.First(x => x.Name == "planner_prompt.two_stage.2nd.plan").Content; - var responseFormat = JsonSerializer.Serialize(new SecondStagePlan - { - Tool = "tool name if task solution provided", - Parameters = new JsonDocument[] { JsonDocument.Parse("{}") }, - Results = new string[] { "" } - }); - var context = GetContext(); - var render = _services.GetRequiredService(); - return render.Render(template, new Dictionary - { - { "task_description", plan.Task }, - { "response_format", responseFormat } - }); - } -} diff --git a/src/Infrastructure/BotSharp.Core/Routing/Planning/TwoStagePlanner/TwoStagePlanner.cs b/src/Infrastructure/BotSharp.Core/Routing/Planning/TwoStagePlanner/TwoStagePlanner.cs deleted file mode 100644 index 8d65f566..00000000 --- a/src/Infrastructure/BotSharp.Core/Routing/Planning/TwoStagePlanner/TwoStagePlanner.cs +++ /dev/null @@ -1,136 +0,0 @@ -using BotSharp.Abstraction.Routing.Planning; - -namespace BotSharp.Core.Routing.Planning; - -public partial class TwoStagePlanner : IRoutingPlaner -{ - private readonly IServiceProvider _services; - private readonly ILogger _logger; - public int MaxLoopCount => 100; - private bool _isTaskCompleted; - private string _md5; - - private Queue _plan1st = new Queue(); - private Queue _plan2nd = new Queue(); - - private List _executionContext = new List(); - - public TwoStagePlanner(IServiceProvider services, ILogger logger) - { - _services = services; - _logger = logger; - } - - public async Task GetNextInstruction(Agent router, string messageId, List dialogs) - { - if (_plan1st.IsNullOrEmpty() && _plan2nd.IsNullOrEmpty()) - { - FirstStagePlan[] items = await GetFirstStagePlanAsync(router, messageId, dialogs); - - foreach (var item in items) - { - _plan1st.Enqueue(item); - }; - } - - // Get Second Stage Plan - if (_plan2nd.IsNullOrEmpty()) - { - var plan1 = _plan1st.Dequeue(); - - if (plan1.ContainMultipleSteps) - { - SecondStagePlan[] items = await GetSecondStagePlanAsync(router, messageId, plan1, dialogs); - - foreach (var item in items) - { - _plan2nd.Enqueue(item); - } - } - else - { - _plan2nd.Enqueue(new SecondStagePlan - { - Description = plan1.Task, - Tables = plan1.Tables, - Parameters = plan1.Parameters, - Results = plan1.Results, - }); - } - } - - var plan2 = _plan2nd.Dequeue(); - - var secondStagePrompt = GetSecondStageTaskPrompt(router, plan2); - var inst = new FunctionCallFromLlm - { - AgentName = "SQL Driver", - Response = secondStagePrompt, - Function = "route_to_agent" - }; - - inst.HandleDialogsByPlanner = true; - _isTaskCompleted = _plan1st.IsNullOrEmpty() && _plan2nd.IsNullOrEmpty(); - - return inst; - } - - public List BeforeHandleContext(FunctionCallFromLlm inst, RoleDialogModel message, List dialogs) - { - var question = inst.Response; - if (_executionContext.Count > 0) - { - var content = GetContext(); - question = $"CONTEXT:\r\n{content}\r\n" + inst.Response; - } - else - { - question = $"CONTEXT:\r\n{question}"; - } - - var taskAgentDialogs = new List - { - new RoleDialogModel(AgentRole.User, question) - { - MessageId = message.MessageId, - } - }; - - return taskAgentDialogs; - } - - public bool AfterHandleContext(List dialogs, List taskAgentDialogs) - { - dialogs.AddRange(taskAgentDialogs.Skip(1)); - - // Keep execution context - _executionContext.Add(taskAgentDialogs.Last().Content); - - return true; - } - - public async Task AgentExecuting(Agent router, FunctionCallFromLlm inst, RoleDialogModel message, List dialogs) - { - dialogs.Add(new RoleDialogModel(AgentRole.User, inst.Response) - { - MessageId = message.MessageId, - CurrentAgentId = router.Id - }); - return true; - } - - public async Task AgentExecuted(Agent router, FunctionCallFromLlm inst, RoleDialogModel message, List dialogs) - { - var context = _services.GetRequiredService(); - - if (message.StopCompletion || _isTaskCompleted) - { - context.Empty(reason: $"Agent queue is cleared by {nameof(TwoStagePlanner)}"); - return false; - } - - var routing = _services.GetRequiredService(); - routing.ResetRecursiveCounter(); - return true; - } -} diff --git a/src/Plugins/BotSharp.Plugin.Planner/TwoStaging/TwoStageTaskPlanner.cs b/src/Plugins/BotSharp.Plugin.Planner/TwoStaging/TwoStageTaskPlanner.cs index c6f819e5..55543230 100644 --- a/src/Plugins/BotSharp.Plugin.Planner/TwoStaging/TwoStageTaskPlanner.cs +++ b/src/Plugins/BotSharp.Plugin.Planner/TwoStaging/TwoStageTaskPlanner.cs @@ -1,6 +1,7 @@ using BotSharp.Abstraction.MLTasks; using BotSharp.Abstraction.Routing.Planning; using BotSharp.Core.Routing.Planning; +using Microsoft.EntityFrameworkCore; namespace BotSharp.Plugin.Planner.TwoStaging; @@ -8,7 +9,7 @@ public partial class TwoStageTaskPlanner : IRoutingPlaner { private readonly IServiceProvider _services; private readonly ILogger _logger; - public int MaxLoopCount => 100; + public int MaxLoopCount => 10; private bool _isTaskCompleted; private Queue _plan1st = new Queue(); @@ -16,7 +17,7 @@ public partial class TwoStageTaskPlanner : IRoutingPlaner private List _executionContext = new List(); - public TwoStageTaskPlanner(IServiceProvider services, ILogger logger) + public TwoStageTaskPlanner(IServiceProvider services, ILogger logger) { _services = services; _logger = logger; @@ -27,15 +28,14 @@ public partial class TwoStageTaskPlanner : IRoutingPlaner // push agent to routing context var routing = _services.GetRequiredService(); routing.Context.Push(BuiltInAgentId.Planner, "Make plan in TwoStage planner"); - return new FunctionCallFromLlm { - AgentName = "Planner", - UserGoal = "", - Response = "", + AgentName = router.Name, + Response = dialogs.Last().Content, Function = "route_to_agent" }; + /*FirstStagePlan[] items = await GetFirstStagePlanAsync(router, messageId, dialogs); foreach (var item in items) @@ -130,7 +130,13 @@ public partial class TwoStageTaskPlanner : IRoutingPlaner if (message.StopCompletion || _isTaskCompleted) { - context.Empty(reason: $"Agent queue is cleared by {nameof(TwoStagePlanner)}"); + context.Empty(reason: $"Agent queue is cleared by {nameof(TwoStageTaskPlanner)}"); + return false; + } + + if (dialogs.Last().Role == AgentRole.Assistant) + { + context.Empty(); return false; } @@ -149,47 +155,6 @@ public partial class TwoStageTaskPlanner : IRoutingPlaner return content; } - private async Task GetFirstStagePlanAsync(Agent router, string messageId, List dialogs) - { - /*var fn = _services.GetRequiredService(); - await fn.InvokeFunction("plan_primary_stage", message); - var items = message.Content.JsonArrayContent();*/ - - var firstStagePlanPrompt = await GetFirstStagePlanPrompt(router); - - var plan = new FirstStagePlan[0]; - - var llmProviderService = _services.GetRequiredService(); - var provider = router.LlmConfig.Provider ?? "openai"; - var model = llmProviderService.GetProviderModel(provider, router.LlmConfig.Model ?? "gpt-4o"); - - // chat completion - var completion = CompletionProvider.GetChatCompletion(_services, - provider: provider, - model: model.Name); - - string text = string.Empty; - - try - { - var response = await completion.GetChatCompletions(new Agent - { - Id = router.Id, - Name = nameof(TwoStagePlanner), - Instruction = firstStagePlanPrompt - }, dialogs); - - text = response.Content; - plan = response.Content.JsonArrayContent(); - } - catch (Exception ex) - { - _logger.LogError($"{ex.Message}: {text}"); - } - - return plan; - } - private async Task GetFirstStagePlanPrompt(Agent router) { var template = router.Templates.First(x => x.Name == "two_stage.1st.plan").Content; @@ -257,7 +222,7 @@ public partial class TwoStageTaskPlanner : IRoutingPlaner var response = await completion.GetChatCompletions(new Agent { Id = router.Id, - Name = nameof(TwoStagePlanner), + Name = nameof(TwoStageTaskPlanner), Instruction = firstStageSystemPrompt }, conversations); diff --git a/src/Plugins/BotSharp.Plugin.Planner/data/agents/282a7128-69a1-44b0-878c-a9159b88f3b9/agent.json b/src/Plugins/BotSharp.Plugin.Planner/data/agents/282a7128-69a1-44b0-878c-a9159b88f3b9/agent.json index f689f24f..aeabfb9a 100644 --- a/src/Plugins/BotSharp.Plugin.Planner/data/agents/282a7128-69a1-44b0-878c-a9159b88f3b9/agent.json +++ b/src/Plugins/BotSharp.Plugin.Planner/data/agents/282a7128-69a1-44b0-878c-a9159b88f3b9/agent.json @@ -8,7 +8,7 @@ "iconUrl": "https://e7.pngegg.com/pngimages/775/350/png-clipart-action-plan-computer-icons-plan-miscellaneous-text-thumbnail.png", "disabled": false, "isPublic": true, - "profiles": [ "tool" ], + "profiles": [ "planning" ], "utilities": [ "two-stage-planner" ], "llmConfig": { "provider": "openai", diff --git a/src/Plugins/BotSharp.Plugin.SqlDriver/data/agents/beda4c12-e1ec-4b4b-b328-3df4a6687c4f/agent.json b/src/Plugins/BotSharp.Plugin.SqlDriver/data/agents/beda4c12-e1ec-4b4b-b328-3df4a6687c4f/agent.json index 4f19d591..e75c4709 100644 --- a/src/Plugins/BotSharp.Plugin.SqlDriver/data/agents/beda4c12-e1ec-4b4b-b328-3df4a6687c4f/agent.json +++ b/src/Plugins/BotSharp.Plugin.SqlDriver/data/agents/beda4c12-e1ec-4b4b-b328-3df4a6687c4f/agent.json @@ -7,7 +7,7 @@ "updatedDateTime": "2023-11-15T13:49:00Z", "disabled": false, "isPublic": true, - "profiles": [ "tool", "sql" ], + "profiles": [ "database" ], "llmConfig": { "model": "gpt-4-0125", "model3": "gpt-35-turbo-1106", From c5d83fbd240f6e82b79de8f3bd975aa03a2a41ca Mon Sep 17 00:00:00 2001 From: Bo Yin Date: Fri, 6 Sep 2024 10:14:10 -0500 Subject: [PATCH 6/6] support dialCSR --- .../Controllers/TwilioVoiceController.cs | 16 +++++++++------- .../Models/AssistantMessage.cs | 1 + .../Services/TwilioMessageQueueService.cs | 1 + .../Services/TwilioService.cs | 11 +++++++++++ .../Settings/TwilioSetting.cs | 1 + 5 files changed, 23 insertions(+), 7 deletions(-) diff --git a/src/Plugins/BotSharp.Plugin.Twilio/Controllers/TwilioVoiceController.cs b/src/Plugins/BotSharp.Plugin.Twilio/Controllers/TwilioVoiceController.cs index c820e40c..4e37ac78 100644 --- a/src/Plugins/BotSharp.Plugin.Twilio/Controllers/TwilioVoiceController.cs +++ b/src/Plugins/BotSharp.Plugin.Twilio/Controllers/TwilioVoiceController.cs @@ -1,11 +1,9 @@ -using Azure.Messaging; using BotSharp.Abstraction.Files; using BotSharp.Core.Infrastructures; using BotSharp.Plugin.Twilio.Models; using BotSharp.Plugin.Twilio.Services; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; -using System.Security.Cryptography; using Twilio.Http; namespace BotSharp.Plugin.Twilio.Controllers; @@ -109,14 +107,14 @@ public class TwilioVoiceController : TwilioController else { response = twilio.ReturnInstructions(null, $"twilio/voice/{conversationId}/receive/{seqNum}?states={states}&attempts={++attempts}", true); - } + } } return TwiML(response); } [ValidateRequest] [HttpPost("twilio/voice/{conversationId}/reply/{seqNum}")] - public async Task ReplyCallerMessage([FromRoute] string conversationId, [FromRoute] int seqNum, + public async Task ReplyCallerMessage([FromRoute] string conversationId, [FromRoute] int seqNum, [FromQuery] string states, VoiceRequest request) { var nextSeqNum = seqNum + 1; @@ -164,16 +162,20 @@ public class TwilioVoiceController : TwilioController } else { - response = twilio.ReturnInstructions(new List + response = twilio.ReturnInstructions(new List { $"twilio/hold-on-{Random.Shared.Next(1, 6)}.mp3", - $"twilio/typing-{Random.Shared.Next(1, 4)}.mp3" + $"twilio/typing-{Random.Shared.Next(1, 4)}.mp3" }, $"twilio/voice/{conversationId}/reply/{seqNum}?states={states}", true); } } else { - if (reply.ConversationEnd) + if (reply.HumanIntervationNeeded) + { + response = twilio.DialCsrAgent($"twilio/voice/speeches/{conversationId}/{reply.SpeechFileName}"); + } + else if (reply.ConversationEnd) { response = twilio.HangUp($"twilio/voice/speeches/{conversationId}/{reply.SpeechFileName}"); } diff --git a/src/Plugins/BotSharp.Plugin.Twilio/Models/AssistantMessage.cs b/src/Plugins/BotSharp.Plugin.Twilio/Models/AssistantMessage.cs index d53a9b0b..521dba43 100644 --- a/src/Plugins/BotSharp.Plugin.Twilio/Models/AssistantMessage.cs +++ b/src/Plugins/BotSharp.Plugin.Twilio/Models/AssistantMessage.cs @@ -3,6 +3,7 @@ namespace BotSharp.Plugin.Twilio.Models public class AssistantMessage { public bool ConversationEnd { get; set; } + public bool HumanIntervationNeeded { get; set; } public string Content { get; set; } public string MessageId { get; set; } public string SpeechFileName { get; set; } diff --git a/src/Plugins/BotSharp.Plugin.Twilio/Services/TwilioMessageQueueService.cs b/src/Plugins/BotSharp.Plugin.Twilio/Services/TwilioMessageQueueService.cs index 39eac1cf..4679c653 100644 --- a/src/Plugins/BotSharp.Plugin.Twilio/Services/TwilioMessageQueueService.cs +++ b/src/Plugins/BotSharp.Plugin.Twilio/Services/TwilioMessageQueueService.cs @@ -86,6 +86,7 @@ namespace BotSharp.Plugin.Twilio.Services reply = new AssistantMessage() { ConversationEnd = msg.Instruction?.ConversationEnd ?? false, + HumanIntervationNeeded = string.Equals("human_intervention_needed", msg.FunctionName), Content = msg.Content, MessageId = msg.MessageId }; diff --git a/src/Plugins/BotSharp.Plugin.Twilio/Services/TwilioService.cs b/src/Plugins/BotSharp.Plugin.Twilio/Services/TwilioService.cs index 285d3321..d72f549b 100644 --- a/src/Plugins/BotSharp.Plugin.Twilio/Services/TwilioService.cs +++ b/src/Plugins/BotSharp.Plugin.Twilio/Services/TwilioService.cs @@ -133,6 +133,17 @@ public class TwilioService return response; } + public VoiceResponse DialCsrAgent(string speechPath) + { + var response = new VoiceResponse(); + if (!string.IsNullOrEmpty(speechPath)) + { + response.Play(new Uri($"{_settings.CallbackHost}/{speechPath}")); + } + response.Dial(_settings.CsrAgentNumber); + return response; + } + public VoiceResponse HoldOn(int interval, string message = null) { var twilioSetting = _services.GetRequiredService(); diff --git a/src/Plugins/BotSharp.Plugin.Twilio/Settings/TwilioSetting.cs b/src/Plugins/BotSharp.Plugin.Twilio/Settings/TwilioSetting.cs index c7170ee2..bbf4bab3 100644 --- a/src/Plugins/BotSharp.Plugin.Twilio/Settings/TwilioSetting.cs +++ b/src/Plugins/BotSharp.Plugin.Twilio/Settings/TwilioSetting.cs @@ -10,4 +10,5 @@ public class TwilioSetting public string ApiSecret { get; set; } public string CallbackHost { get; set; } public string AgentId { get; set; } + public string CsrAgentNumber { get; set; } }