From 742c4d583b87495bb696fe2e28a49d8df5a1def6 Mon Sep 17 00:00:00 2001 From: Haiping Chen <101423@smsassist.com> Date: Wed, 16 Oct 2024 14:44:18 -0500 Subject: [PATCH 01/23] Skip refine if tables is empty --- .../BotSharp.Plugin.SqlDriver/Functions/ExecuteQueryFn.cs | 5 +++++ .../functions/execute_sql.json | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/Plugins/BotSharp.Plugin.SqlDriver/Functions/ExecuteQueryFn.cs b/src/Plugins/BotSharp.Plugin.SqlDriver/Functions/ExecuteQueryFn.cs index e3d8e57e..99b40a65 100644 --- a/src/Plugins/BotSharp.Plugin.SqlDriver/Functions/ExecuteQueryFn.cs +++ b/src/Plugins/BotSharp.Plugin.SqlDriver/Functions/ExecuteQueryFn.cs @@ -99,6 +99,11 @@ public class ExecuteQueryFn : IFunctionCallback private async Task RefineSqlStatement(RoleDialogModel message, ExecuteQueryArgs args) { + if (args.Tables == null || args.Tables.Length == 0) + { + return args; + } + // get table DDL var fn = _services.GetRequiredService(); var msg = RoleDialogModel.From(message); diff --git a/src/Plugins/BotSharp.Plugin.SqlDriver/data/agents/beda4c12-e1ec-4b4b-b328-3df4a6687c4f/functions/execute_sql.json b/src/Plugins/BotSharp.Plugin.SqlDriver/data/agents/beda4c12-e1ec-4b4b-b328-3df4a6687c4f/functions/execute_sql.json index 9cdafc04..6587cef5 100644 --- a/src/Plugins/BotSharp.Plugin.SqlDriver/data/agents/beda4c12-e1ec-4b4b-b328-3df4a6687c4f/functions/execute_sql.json +++ b/src/Plugins/BotSharp.Plugin.SqlDriver/data/agents/beda4c12-e1ec-4b4b-b328-3df4a6687c4f/functions/execute_sql.json @@ -20,13 +20,13 @@ "tables": { "type": "array", - "description": "all related tables", + "description": "all related tables in the sql statements", "items": { "type": "string", "description": "table name" } } }, - "required": [ "sql_statement", "tables", "formatting_result" ] + "required": [ "sql_statements", "tables", "formatting_result" ] } } \ No newline at end of file From eaf647677e524d4b55c6b5212ec1fe3446713190 Mon Sep 17 00:00:00 2001 From: Joanna Ren <101223@smsassist.com> Date: Wed, 16 Oct 2024 17:05:59 -0500 Subject: [PATCH 02/23] Add knowledge generation refine --- .../Knowledges/Models/GenerateKnowledge.cs | 19 ++++++++++++ .../BotSharp.Plugin.KnowledgeBase.csproj | 4 +++ .../Functions/GenerateKnowledgeFn.cs | 31 +++++++++++++++++-- .../templates/knowledge.generation.liquid | 2 +- .../knowledge.generation.refine.liquid | 15 +++++++++ .../functions/sql_dictionary_lookup.json | 6 +++- 6 files changed, 72 insertions(+), 5 deletions(-) create mode 100644 src/Infrastructure/BotSharp.Abstraction/Knowledges/Models/GenerateKnowledge.cs create mode 100644 src/Plugins/BotSharp.Plugin.KnowledgeBase/data/agents/01acc3e5-0af7-49e6-ad7a-a760bd12dc40/templates/knowledge.generation.refine.liquid diff --git a/src/Infrastructure/BotSharp.Abstraction/Knowledges/Models/GenerateKnowledge.cs b/src/Infrastructure/BotSharp.Abstraction/Knowledges/Models/GenerateKnowledge.cs new file mode 100644 index 00000000..a5698dc0 --- /dev/null +++ b/src/Infrastructure/BotSharp.Abstraction/Knowledges/Models/GenerateKnowledge.cs @@ -0,0 +1,19 @@ +namespace BotSharp.Abstraction.Knowledges.Models; + +public class GenerateKnowledge +{ + [JsonPropertyName("question")] + public string Question { get; set; } = string.Empty; + + [JsonPropertyName("answer")] + public string Answer { get; set; } = string.Empty; + + [JsonPropertyName("refined_collection")] + public string RefinedCollection { get; set; } = string.Empty; + + [JsonPropertyName("refine_answer")] + public Boolean RefineAnswer { get; set; } = false; + + [JsonPropertyName("existing_answer")] + public string ExistingAnswer { get; set; } = string.Empty; +} diff --git a/src/Plugins/BotSharp.Plugin.KnowledgeBase/BotSharp.Plugin.KnowledgeBase.csproj b/src/Plugins/BotSharp.Plugin.KnowledgeBase/BotSharp.Plugin.KnowledgeBase.csproj index 1cd8144f..3216564e 100644 --- a/src/Plugins/BotSharp.Plugin.KnowledgeBase/BotSharp.Plugin.KnowledgeBase.csproj +++ b/src/Plugins/BotSharp.Plugin.KnowledgeBase/BotSharp.Plugin.KnowledgeBase.csproj @@ -22,6 +22,7 @@ + @@ -38,6 +39,9 @@ PreserveNewest + + PreserveNewest + PreserveNewest diff --git a/src/Plugins/BotSharp.Plugin.KnowledgeBase/Functions/GenerateKnowledgeFn.cs b/src/Plugins/BotSharp.Plugin.KnowledgeBase/Functions/GenerateKnowledgeFn.cs index cd43fca1..114ce7e4 100644 --- a/src/Plugins/BotSharp.Plugin.KnowledgeBase/Functions/GenerateKnowledgeFn.cs +++ b/src/Plugins/BotSharp.Plugin.KnowledgeBase/Functions/GenerateKnowledgeFn.cs @@ -1,5 +1,6 @@ using BotSharp.Abstraction.Templating; using BotSharp.Core.Infrastructures; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; namespace BotSharp.Plugin.KnowledgeBase.Functions; @@ -20,14 +21,23 @@ public class GenerateKnowledgeFn : IFunctionCallback public async Task Execute(RoleDialogModel message) { - var args = JsonSerializer.Deserialize(message.FunctionArgs ?? "{}"); + var args = JsonSerializer.Deserialize(message.FunctionArgs ?? "{}"); var agentService = _services.GetRequiredService(); var llmAgent = await agentService.GetAgent(BuiltInAgentId.Planner); - var generateKnowledgePrompt = await GetGenerateKnowledgePrompt(args.Question, args.Answer); + var refineKnowledge = args.RefinedCollection; + String generateKnowledgePrompt; + if (args.RefineAnswer == true) + { + generateKnowledgePrompt = await GetRefineKnowledgePrompt(args.Question, args.Answer, args.ExistingAnswer); + } + else + { + generateKnowledgePrompt = await GetGenerateKnowledgePrompt(args.Question, args.Answer); + } var agent = new Agent { Id = message.CurrentAgentId ?? string.Empty, - Name = "sqlDriver_DictionarySearch", + Name = "knowledge_generator", Instruction = generateKnowledgePrompt, LlmConfig = llmAgent.LlmConfig }; @@ -51,6 +61,21 @@ public class GenerateKnowledgeFn : IFunctionCallback { "sql_answer", sqlAnswer }, }); } + private async Task GetRefineKnowledgePrompt(string userQuestion, string sqlAnswer, string existionAnswer) + { + var agentService = _services.GetRequiredService(); + var render = _services.GetRequiredService(); + + var agent = await agentService.GetAgent(BuiltInAgentId.Learner); + var template = agent.Templates.FirstOrDefault(x => x.Name == "knowledge.generation.refine")?.Content ?? string.Empty; + + return render.Render(template, new Dictionary + { + { "user_question", userQuestion }, + { "new_answer", sqlAnswer }, + { "existing_answer", existionAnswer} + }); + } private async Task GetAiResponse(Agent agent) { var text = "Generate question and answer pair"; diff --git a/src/Plugins/BotSharp.Plugin.KnowledgeBase/data/agents/01acc3e5-0af7-49e6-ad7a-a760bd12dc40/templates/knowledge.generation.liquid b/src/Plugins/BotSharp.Plugin.KnowledgeBase/data/agents/01acc3e5-0af7-49e6-ad7a-a760bd12dc40/templates/knowledge.generation.liquid index 49e374a1..2792595a 100644 --- a/src/Plugins/BotSharp.Plugin.KnowledgeBase/data/agents/01acc3e5-0af7-49e6-ad7a-a760bd12dc40/templates/knowledge.generation.liquid +++ b/src/Plugins/BotSharp.Plugin.KnowledgeBase/data/agents/01acc3e5-0af7-49e6-ad7a-a760bd12dc40/templates/knowledge.generation.liquid @@ -1,4 +1,4 @@ -You are a knowledge generator for knowledge base. Extract the answer in "SQL Answer" to answer the User Questions. +You are a knowledge extractor for knowledge base. Extract the answer in "SQL Answer" to answer the User Questions. * Replace alias with the actual table name. Output json array only, formatting as [{"question":"string", "answer":""}]. * Skip the question/answer for tmp table. * Don't include tmp table in the answer. diff --git a/src/Plugins/BotSharp.Plugin.KnowledgeBase/data/agents/01acc3e5-0af7-49e6-ad7a-a760bd12dc40/templates/knowledge.generation.refine.liquid b/src/Plugins/BotSharp.Plugin.KnowledgeBase/data/agents/01acc3e5-0af7-49e6-ad7a-a760bd12dc40/templates/knowledge.generation.refine.liquid new file mode 100644 index 00000000..4a7af975 --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.KnowledgeBase/data/agents/01acc3e5-0af7-49e6-ad7a-a760bd12dc40/templates/knowledge.generation.refine.liquid @@ -0,0 +1,15 @@ +You are a knowledge extractor for knowledge base. Utilize the new answer and existing answer to generate the final integrated answer. +Output json array only, formatting as [{"question":"", "answer":""}]. Replace the new line with \r\n. +* Don't loss any knowledge in the existing answer. + +===== +User Question: +{{ user_question }} + +===== +New Answer: +{{ new_answer }} + +===== +Existing Answer: +{{ existing_answer }} diff --git a/src/Plugins/BotSharp.Plugin.SqlDriver/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/functions/sql_dictionary_lookup.json b/src/Plugins/BotSharp.Plugin.SqlDriver/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/functions/sql_dictionary_lookup.json index 6ad2a917..3dc21e01 100644 --- a/src/Plugins/BotSharp.Plugin.SqlDriver/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/functions/sql_dictionary_lookup.json +++ b/src/Plugins/BotSharp.Plugin.SqlDriver/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/functions/sql_dictionary_lookup.json @@ -1,6 +1,6 @@ { "name": "verify_dictionary_term", - "description": "Get id from dictionary table by keyword. Call this function only if need_lookup_dictionary is True", + "description": "Get id from dictionary table by keyword. Call this function only if need_lookup_dictionary is true and is_insert is false", "parameters": { "type": "object", "properties": { @@ -12,6 +12,10 @@ "type": "string", "description": "the reason why you need to call verify_dictionary_term" }, + "is_insert": { + "type": "boolean", + "description": "if SQL statement is inserting." + }, "tables": { "type": "array", "description": "all related tables", From 28e15b4b98364280df57c2ab18a0aeaecfb71d84 Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Thu, 17 Oct 2024 13:16:31 -0500 Subject: [PATCH 03/23] change terms --- .../Functions/PrimaryStagePlanFn.cs | 11 ++++++---- .../Functions/SecondaryStagePlanFn.cs | 20 ++++++++++--------- .../Functions/SummaryPlanFn.cs | 13 ++++++------ .../BotSharp.Plugin.SqlDriver.csproj | 8 ++++---- ...ictionaryFn.cs => VerifyDictionaryTerm.cs} | 7 +++++-- ...ookup.json => verify_dictionary_term.json} | 0 ...iquid => verify_dictionary_term.fn.liquid} | 0 7 files changed, 34 insertions(+), 25 deletions(-) rename src/Plugins/BotSharp.Plugin.SqlDriver/Functions/{LookupDictionaryFn.cs => VerifyDictionaryTerm.cs} (96%) rename src/Plugins/BotSharp.Plugin.SqlDriver/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/functions/{sql_dictionary_lookup.json => verify_dictionary_term.json} (100%) rename src/Plugins/BotSharp.Plugin.SqlDriver/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/{sql_dictionary_lookup.fn.liquid => verify_dictionary_term.fn.liquid} (100%) diff --git a/src/Plugins/BotSharp.Plugin.Planner/Functions/PrimaryStagePlanFn.cs b/src/Plugins/BotSharp.Plugin.Planner/Functions/PrimaryStagePlanFn.cs index f854a51d..5cf4f4f7 100644 --- a/src/Plugins/BotSharp.Plugin.Planner/Functions/PrimaryStagePlanFn.cs +++ b/src/Plugins/BotSharp.Plugin.Planner/Functions/PrimaryStagePlanFn.cs @@ -6,10 +6,13 @@ public class PrimaryStagePlanFn : IFunctionCallback { public string Name => "plan_primary_stage"; public string Indication => "Currently analyzing and breaking down user requirements."; + private readonly IServiceProvider _services; private readonly ILogger _logger; - public PrimaryStagePlanFn(IServiceProvider services, ILogger logger) + public PrimaryStagePlanFn( + IServiceProvider services, + ILogger logger) { _services = services; _logger = logger; @@ -38,12 +41,12 @@ public class PrimaryStagePlanFn : IFunctionCallback // Get first stage planning prompt var currentAgent = await agentService.LoadAgent(message.CurrentAgentId); - var firstPlanningPrompt = await GetFirstStagePlanPrompt(message, task.Requirements, knowledges); + var prompt = await GetFirstStagePlanPrompt(message, task.Requirements, knowledges); var plannerAgent = new Agent { Id = BuiltInAgentId.Planner, - Name = "planning_1st", - Instruction = firstPlanningPrompt, + Name = "FirstStagePlanner", + Instruction = prompt, TemplateDict = new Dictionary(), LlmConfig = currentAgent.LlmConfig }; diff --git a/src/Plugins/BotSharp.Plugin.Planner/Functions/SecondaryStagePlanFn.cs b/src/Plugins/BotSharp.Plugin.Planner/Functions/SecondaryStagePlanFn.cs index e228d607..8279f7e2 100644 --- a/src/Plugins/BotSharp.Plugin.Planner/Functions/SecondaryStagePlanFn.cs +++ b/src/Plugins/BotSharp.Plugin.Planner/Functions/SecondaryStagePlanFn.cs @@ -1,5 +1,4 @@ using BotSharp.Plugin.Planner.TwoStaging.Models; -using System.Threading.Tasks; namespace BotSharp.Plugin.Planner.Functions; @@ -7,10 +6,13 @@ public class SecondaryStagePlanFn : IFunctionCallback { public string Name => "plan_secondary_stage"; public string Indication => "Further analyzing and breaking down user sub-needs."; + private readonly IServiceProvider _services; private readonly ILogger _logger; - public SecondaryStagePlanFn(IServiceProvider services, ILogger logger) + public SecondaryStagePlanFn( + IServiceProvider services, + ILogger logger) { _services = services; _logger = logger; @@ -25,7 +27,7 @@ public class SecondaryStagePlanFn : IFunctionCallback var msgSecondary = RoleDialogModel.From(message); var collectionName = knowledgeSettings.Default.CollectionName; - var planPrimary = states.GetState("planning_result"); + var planResult = states.GetState("planning_result"); var taskSecondary = JsonSerializer.Deserialize(msgSecondary.FunctionArgs); @@ -43,14 +45,14 @@ public class SecondaryStagePlanFn : IFunctionCallback // Get second stage planning prompt var currentAgent = await agentService.LoadAgent(message.CurrentAgentId); - var secondPlanningPrompt = await GetSecondStagePlanPrompt(taskSecondary.TaskDescription, planPrimary, knowledgeResults, message); - _logger.LogInformation(secondPlanningPrompt); + var prompt = await GetSecondStagePlanPrompt(taskSecondary.TaskDescription, planResult, knowledgeResults, message); + _logger.LogInformation(prompt); var plannerAgent = new Agent { Id = BuiltInAgentId.Planner, - Name = "planning_2nd", - Instruction = secondPlanningPrompt, + Name = "SecondStagePlanner", + Instruction = prompt, TemplateDict = new Dictionary(), LlmConfig = currentAgent.LlmConfig }; @@ -63,7 +65,7 @@ public class SecondaryStagePlanFn : IFunctionCallback return true; } - private async Task GetSecondStagePlanPrompt(string taskDescription, string planPrimary, string knowledgeResults, RoleDialogModel message) + private async Task GetSecondStagePlanPrompt(string taskDescription, string planResult, string knowledgeResults, RoleDialogModel message) { var agentService = _services.GetRequiredService(); var render = _services.GetRequiredService(); @@ -79,7 +81,7 @@ public class SecondaryStagePlanFn : IFunctionCallback return render.Render(template, new Dictionary { { "task_description", taskDescription }, - { "primary_plan", planPrimary }, + { "primary_plan", planResult }, { "additional_knowledge", knowledgeResults }, { "response_format", responseFormat } }); diff --git a/src/Plugins/BotSharp.Plugin.Planner/Functions/SummaryPlanFn.cs b/src/Plugins/BotSharp.Plugin.Planner/Functions/SummaryPlanFn.cs index e08e6f6f..ef634392 100644 --- a/src/Plugins/BotSharp.Plugin.Planner/Functions/SummaryPlanFn.cs +++ b/src/Plugins/BotSharp.Plugin.Planner/Functions/SummaryPlanFn.cs @@ -8,6 +8,7 @@ public class SummaryPlanFn : IFunctionCallback { public string Name => "plan_summary"; public string Indication => "Organizing and summarizing the final output results."; + private readonly IServiceProvider _services; private readonly ILogger _logger; @@ -34,7 +35,7 @@ public class SummaryPlanFn : IFunctionCallback var steps = states.GetState("planning_result").JsonArrayContent(); var allTables = new List(); var ddlStatements = string.Empty; - var relevantKnowledge = states.GetState("planning_result"); + var planResult = states.GetState("planning_result"); var dictionaryItems = states.GetState("dictionary_items"); var excelImportResult = states.GetState("excel_import_result"); @@ -53,14 +54,14 @@ public class SummaryPlanFn : IFunctionCallback ddlStatements += "\r\n" + msgCopy.Content; // Summarize and generate query - var summaryPlanPrompt = await GetSummaryPlanPrompt(msgCopy, taskRequirement, relevantKnowledge, dictionaryItems, ddlStatements, excelImportResult); - _logger.LogInformation($"Summary plan prompt:\r\n{summaryPlanPrompt}"); + var prompt = await GetSummaryPlanPrompt(msgCopy, taskRequirement, planResult, dictionaryItems, ddlStatements, excelImportResult); + _logger.LogInformation($"Summary plan prompt:\r\n{prompt}"); var plannerAgent = new Agent { Id = BuiltInAgentId.Planner, - Name = "Planner Summary", - Instruction = summaryPlanPrompt, + Name = "SummaryPlanner", + Instruction = prompt, LlmConfig = currentAgent.LlmConfig }; @@ -105,7 +106,7 @@ public class SummaryPlanFn : IFunctionCallback { "relevant_knowledges", relevantKnowledge }, { "dictionary_items", dictionaryItems }, { "table_structure", ddlStatement }, - { "excel_import_result",excelImportResult } + { "excel_import_result", excelImportResult } }); } private async Task GetAiResponse(Agent plannerAgent) diff --git a/src/Plugins/BotSharp.Plugin.SqlDriver/BotSharp.Plugin.SqlDriver.csproj b/src/Plugins/BotSharp.Plugin.SqlDriver/BotSharp.Plugin.SqlDriver.csproj index d4d0a243..c0b755f6 100644 --- a/src/Plugins/BotSharp.Plugin.SqlDriver/BotSharp.Plugin.SqlDriver.csproj +++ b/src/Plugins/BotSharp.Plugin.SqlDriver/BotSharp.Plugin.SqlDriver.csproj @@ -17,12 +17,12 @@ - - + + @@ -37,10 +37,10 @@ PreserveNewest - + PreserveNewest - + PreserveNewest diff --git a/src/Plugins/BotSharp.Plugin.SqlDriver/Functions/LookupDictionaryFn.cs b/src/Plugins/BotSharp.Plugin.SqlDriver/Functions/VerifyDictionaryTerm.cs similarity index 96% rename from src/Plugins/BotSharp.Plugin.SqlDriver/Functions/LookupDictionaryFn.cs rename to src/Plugins/BotSharp.Plugin.SqlDriver/Functions/VerifyDictionaryTerm.cs index c94c12db..134ef8e4 100644 --- a/src/Plugins/BotSharp.Plugin.SqlDriver/Functions/LookupDictionaryFn.cs +++ b/src/Plugins/BotSharp.Plugin.SqlDriver/Functions/VerifyDictionaryTerm.cs @@ -7,12 +7,15 @@ using static Dapper.SqlMapper; namespace BotSharp.Plugin.SqlDriver.Functions; -public class LookupDictionaryFn : IFunctionCallback +public class VerifyDictionaryTerm : IFunctionCallback { public string Name => "verify_dictionary_term"; + public string Indication => "Verifying dictionary term"; + + private readonly IServiceProvider _services; - public LookupDictionaryFn(IServiceProvider services) + public VerifyDictionaryTerm(IServiceProvider services) { _services = services; } diff --git a/src/Plugins/BotSharp.Plugin.SqlDriver/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/functions/sql_dictionary_lookup.json b/src/Plugins/BotSharp.Plugin.SqlDriver/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/functions/verify_dictionary_term.json similarity index 100% rename from src/Plugins/BotSharp.Plugin.SqlDriver/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/functions/sql_dictionary_lookup.json rename to src/Plugins/BotSharp.Plugin.SqlDriver/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/functions/verify_dictionary_term.json diff --git a/src/Plugins/BotSharp.Plugin.SqlDriver/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/sql_dictionary_lookup.fn.liquid b/src/Plugins/BotSharp.Plugin.SqlDriver/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/verify_dictionary_term.fn.liquid similarity index 100% rename from src/Plugins/BotSharp.Plugin.SqlDriver/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/sql_dictionary_lookup.fn.liquid rename to src/Plugins/BotSharp.Plugin.SqlDriver/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/verify_dictionary_term.fn.liquid From e62a38eb5be2d880916b3731894c01520460c707 Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Thu, 17 Oct 2024 18:17:38 -0500 Subject: [PATCH 04/23] add instruct --- .../Instructs/IInstructService.cs | 11 ++ .../Instructs/Models/InstructOptions.cs | 29 +++++ .../Knowledges/Models/GenerateKnowledge.cs | 19 --- .../Services/ConversationStateService.cs | 6 +- .../Instructs/InstructService.Execute.cs | 100 ++++++++++++++++ .../Instructs/InstructService.Instruct.cs | 110 +++++++++++++++++ .../Instructs/InstructService.cs | 113 ++---------------- .../BotSharp.Plugin.KnowledgeBase.csproj | 6 +- .../Functions/GenerateKnowledgeFn.cs | 90 -------------- ...quid => knowledge.generation.plain.liquid} | 0 10 files changed, 264 insertions(+), 220 deletions(-) create mode 100644 src/Infrastructure/BotSharp.Abstraction/Instructs/Models/InstructOptions.cs delete mode 100644 src/Infrastructure/BotSharp.Abstraction/Knowledges/Models/GenerateKnowledge.cs create mode 100644 src/Infrastructure/BotSharp.Core/Instructs/InstructService.Execute.cs create mode 100644 src/Infrastructure/BotSharp.Core/Instructs/InstructService.Instruct.cs delete mode 100644 src/Plugins/BotSharp.Plugin.KnowledgeBase/Functions/GenerateKnowledgeFn.cs rename src/Plugins/BotSharp.Plugin.KnowledgeBase/data/agents/01acc3e5-0af7-49e6-ad7a-a760bd12dc40/templates/{knowledge.generation.liquid => knowledge.generation.plain.liquid} (100%) diff --git a/src/Infrastructure/BotSharp.Abstraction/Instructs/IInstructService.cs b/src/Infrastructure/BotSharp.Abstraction/Instructs/IInstructService.cs index 0669a267..f1d17b93 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Instructs/IInstructService.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Instructs/IInstructService.cs @@ -4,5 +4,16 @@ namespace BotSharp.Abstraction.Instructs; public interface IInstructService { + /// + /// Execute completion by using specified instruction or template + /// + /// Agent (static agent) + /// Additional message provided by user + /// Template name + /// System prompt + /// Task Execute(string agentId, RoleDialogModel message, string? templateName = null, string? instruction = null); + + + Task Instruct(string instruction, string agentId, InstructOptions options) where T : class; } diff --git a/src/Infrastructure/BotSharp.Abstraction/Instructs/Models/InstructOptions.cs b/src/Infrastructure/BotSharp.Abstraction/Instructs/Models/InstructOptions.cs new file mode 100644 index 00000000..46c6c900 --- /dev/null +++ b/src/Infrastructure/BotSharp.Abstraction/Instructs/Models/InstructOptions.cs @@ -0,0 +1,29 @@ +namespace BotSharp.Abstraction.Instructs.Models; + +public class InstructOptions +{ + /// + /// Llm provider + /// + public string Provider { get; set; } = null!; + + /// + /// Llm model + /// + public string Model { get; set; } = null!; + + /// + /// Conversation id. When this field is not null, it will get dialogs from conversation. + /// + public string? ConversationId { get; set; } + + /// + /// The single message. It can be append to the whole dialogs or sent alone. + /// + public string? Message { get; set; } + + /// + /// Data to fill in prompt + /// + public Dictionary Data { get; set; } = new(); +} diff --git a/src/Infrastructure/BotSharp.Abstraction/Knowledges/Models/GenerateKnowledge.cs b/src/Infrastructure/BotSharp.Abstraction/Knowledges/Models/GenerateKnowledge.cs deleted file mode 100644 index a5698dc0..00000000 --- a/src/Infrastructure/BotSharp.Abstraction/Knowledges/Models/GenerateKnowledge.cs +++ /dev/null @@ -1,19 +0,0 @@ -namespace BotSharp.Abstraction.Knowledges.Models; - -public class GenerateKnowledge -{ - [JsonPropertyName("question")] - public string Question { get; set; } = string.Empty; - - [JsonPropertyName("answer")] - public string Answer { get; set; } = string.Empty; - - [JsonPropertyName("refined_collection")] - public string RefinedCollection { get; set; } = string.Empty; - - [JsonPropertyName("refine_answer")] - public Boolean RefineAnswer { get; set; } = false; - - [JsonPropertyName("existing_answer")] - public string ExistingAnswer { get; set; } = string.Empty; -} diff --git a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStateService.cs b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStateService.cs index d74f71ac..7a3d1d5d 100644 --- a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStateService.cs +++ b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStateService.cs @@ -372,10 +372,10 @@ public class ConversationStateService : IConversationStateService, IDisposable private bool CheckArgType(string name, string value) { var agentTypes = AgentService.AgentParameterTypes.SelectMany(p => p.Value).ToList(); - var filed = agentTypes.FirstOrDefault(t => t.Key == name); - if (filed.Key != null) + var found = agentTypes.FirstOrDefault(t => t.Key == name); + if (found.Key != null) { - return filed.Value switch + return found.Value switch { "boolean" => bool.TryParse(value, out _), "number" => long.TryParse(value, out _), diff --git a/src/Infrastructure/BotSharp.Core/Instructs/InstructService.Execute.cs b/src/Infrastructure/BotSharp.Core/Instructs/InstructService.Execute.cs new file mode 100644 index 00000000..1d9dd1d0 --- /dev/null +++ b/src/Infrastructure/BotSharp.Core/Instructs/InstructService.Execute.cs @@ -0,0 +1,100 @@ +using BotSharp.Abstraction.Instructs; +using BotSharp.Abstraction.Instructs.Models; +using BotSharp.Abstraction.MLTasks; + +namespace BotSharp.Core.Instructs; + +public partial class InstructService +{ + public async Task Execute(string agentId, RoleDialogModel message, string? templateName = null, string? instruction = null) + { + var agentService = _services.GetRequiredService(); + Agent agent = await agentService.LoadAgent(agentId); + + if (agent.Disabled) + { + var content = $"This agent ({agent.Name}) is disabled, please install the corresponding plugin ({agent.Plugin.Name}) to activate this agent."; + return new InstructResult + { + MessageId = message.MessageId, + Text = content + }; + } + + // Trigger before completion hooks + var hooks = _services.GetServices(); + foreach (var hook in hooks) + { + if (!string.IsNullOrEmpty(hook.SelfId) && hook.SelfId != agentId) + { + continue; + } + + await hook.BeforeCompletion(agent, message); + + // Interrupted by hook + if (message.StopCompletion) + { + return new InstructResult + { + MessageId = message.MessageId, + Text = message.Content + }; + } + } + + // Render prompt + var prompt = string.IsNullOrEmpty(templateName) ? + agentService.RenderedInstruction(agent) : + agentService.RenderedTemplate(agent, templateName); + + var completer = CompletionProvider.GetCompletion(_services, + agentConfig: agent.LlmConfig); + + var response = new InstructResult + { + MessageId = message.MessageId + }; + if (completer is ITextCompletion textCompleter) + { + var result = await textCompleter.GetCompletion(prompt, agentId, message.MessageId); + response.Text = result; + } + else if (completer is IChatCompletion chatCompleter) + { + if (instruction == "#TEMPLATE#") + { + instruction = prompt; + prompt = message.Content; + } + + var result = await chatCompleter.GetChatCompletions(new Agent + { + Id = agentId, + Name = agent.Name, + Instruction = instruction + }, new List + { + new RoleDialogModel(AgentRole.User, prompt) + { + CurrentAgentId = agentId, + MessageId = message.MessageId + } + }); + response.Text = result.Content; + } + + + foreach (var hook in hooks) + { + if (!string.IsNullOrEmpty(hook.SelfId) && hook.SelfId != agentId) + { + continue; + } + + await hook.AfterCompletion(agent, response); + } + + return response; + } +} diff --git a/src/Infrastructure/BotSharp.Core/Instructs/InstructService.Instruct.cs b/src/Infrastructure/BotSharp.Core/Instructs/InstructService.Instruct.cs new file mode 100644 index 00000000..40297400 --- /dev/null +++ b/src/Infrastructure/BotSharp.Core/Instructs/InstructService.Instruct.cs @@ -0,0 +1,110 @@ +using BotSharp.Abstraction.Instructs.Models; +using BotSharp.Abstraction.Templating; +using System.Collections; +using System.Reflection; + +namespace BotSharp.Core.Instructs; + +public partial class InstructService +{ + public async Task Instruct(string instruction, string agentId, InstructOptions options) where T : class + { + var prompt = GetPrompt(instruction, options.Data); + var response = await GetAiResponse(agentId, prompt, options); + + if (string.IsNullOrWhiteSpace(response.Content)) return null; + + var type = typeof(T); + T? result = null; + + try + { + if (IsStringType(type)) + { + result = response.Content as T; + } + else if (IsListType(type)) + { + var text = response.Content.JsonArrayContent(); + if (!string.IsNullOrWhiteSpace(text)) + { + result = JsonSerializer.Deserialize(text, _options.JsonSerializerOptions); + } + } + else + { + var text = response.Content.JsonContent(); + if (!string.IsNullOrWhiteSpace(text)) + { + result = JsonSerializer.Deserialize(text, _options.JsonSerializerOptions); + } + } + } + catch (Exception ex) + { + _logger.LogWarning($"Error when getting ai response, {ex.Message}\r\n{ex.InnerException}"); + } + + return result; + } + + private string GetPrompt(string instruction, Dictionary data) + { + var render = _services.GetRequiredService(); + + return render.Render(instruction, data ?? new Dictionary()); + } + + private async Task GetAiResponse(string agentId, string prompt, InstructOptions options) + { + var agentService = _services.GetRequiredService(); + var agent = await agentService.LoadAgent(agentId); + + var localAgent = new Agent + { + Id = agentId, + Name = agent.Name, + Instruction = prompt, + TemplateDict = new() + }; + + var messages = BuildDialogs(options); + var completion = CompletionProvider.GetChatCompletion(_services, provider: options.Provider, model: options.Model); + + return await completion.GetChatCompletions(localAgent, messages); + } + + private List BuildDialogs(InstructOptions options) + { + var messages = new List(); + + if (!string.IsNullOrWhiteSpace(options.ConversationId)) + { + var conv = _services.GetRequiredService(); + var dialogs = conv.GetDialogHistory(); + messages.AddRange(dialogs); + } + + if (!string.IsNullOrWhiteSpace(options.Message)) + { + messages.Add(new RoleDialogModel(AgentRole.User, options.Message)); + } + + return messages; + } + + private bool IsStringType(Type? type) + { + if (type == null) return false; + + return type == typeof(string); + } + + private bool IsListType(Type? type) + { + if (type == null) return false; + + var interfaces = type.GetTypeInfo().ImplementedInterfaces; + return type.IsArray || interfaces.Any(x => x.Name == typeof(IEnumerable).Name); + } +} diff --git a/src/Infrastructure/BotSharp.Core/Instructs/InstructService.cs b/src/Infrastructure/BotSharp.Core/Instructs/InstructService.cs index b05889e1..a1a31e0e 100644 --- a/src/Infrastructure/BotSharp.Core/Instructs/InstructService.cs +++ b/src/Infrastructure/BotSharp.Core/Instructs/InstructService.cs @@ -1,118 +1,21 @@ -using BotSharp.Abstraction.Agents.Models; using BotSharp.Abstraction.Instructs; -using BotSharp.Abstraction.Instructs.Models; -using BotSharp.Abstraction.MLTasks; +using BotSharp.Abstraction.Options; namespace BotSharp.Core.Instructs; public partial class InstructService : IInstructService { private readonly IServiceProvider _services; - private readonly ILogger _logger; + private readonly BotSharpOptions _options; + private readonly ILogger _logger; - public InstructService(IServiceProvider services, ILogger logger) + public InstructService( + IServiceProvider services, + BotSharpOptions options, + ILogger logger) { _services = services; + _options = options; _logger = logger; } - - /// - /// Execute completion by using specified instruction or template - /// - /// Agent (static agent) - /// Additional message provided by user - /// Template name - /// System prompt - /// - public async Task Execute(string agentId, RoleDialogModel message, string? templateName = null, string? instruction = null) - { - var agentService = _services.GetRequiredService(); - Agent agent = await agentService.LoadAgent(agentId); - - if (agent.Disabled) - { - var content = $"This agent ({agent.Name}) is disabled, please install the corresponding plugin ({agent.Plugin.Name}) to activate this agent."; - return new InstructResult - { - MessageId = message.MessageId, - Text = content - }; - } - - // Trigger before completion hooks - var hooks = _services.GetServices(); - foreach (var hook in hooks) - { - if (!string.IsNullOrEmpty(hook.SelfId) && hook.SelfId != agentId) - { - continue; - } - - await hook.BeforeCompletion(agent, message); - - // Interrupted by hook - if (message.StopCompletion) - { - return new InstructResult - { - MessageId = message.MessageId, - Text = message.Content - }; - } - } - - // Render prompt - var prompt = string.IsNullOrEmpty(templateName) ? - agentService.RenderedInstruction(agent) : - agentService.RenderedTemplate(agent, templateName); - - var completer = CompletionProvider.GetCompletion(_services, - agentConfig: agent.LlmConfig); - - var response = new InstructResult - { - MessageId = message.MessageId - }; - if (completer is ITextCompletion textCompleter) - { - var result = await textCompleter.GetCompletion(prompt, agentId, message.MessageId); - response.Text = result; - } - else if (completer is IChatCompletion chatCompleter) - { - if (instruction == "#TEMPLATE#") - { - instruction = prompt; - prompt = message.Content; - } - - var result = await chatCompleter.GetChatCompletions(new Agent - { - Id = agentId, - Name = agent.Name, - Instruction = instruction - }, new List - { - new RoleDialogModel(AgentRole.User, prompt) - { - CurrentAgentId = agentId, - MessageId = message.MessageId - } - }); - response.Text = result.Content; - } - - - foreach (var hook in hooks) - { - if (!string.IsNullOrEmpty(hook.SelfId) && hook.SelfId != agentId) - { - continue; - } - - await hook.AfterCompletion(agent, response); - } - - return response; - } } diff --git a/src/Plugins/BotSharp.Plugin.KnowledgeBase/BotSharp.Plugin.KnowledgeBase.csproj b/src/Plugins/BotSharp.Plugin.KnowledgeBase/BotSharp.Plugin.KnowledgeBase.csproj index 3216564e..283d8b32 100644 --- a/src/Plugins/BotSharp.Plugin.KnowledgeBase/BotSharp.Plugin.KnowledgeBase.csproj +++ b/src/Plugins/BotSharp.Plugin.KnowledgeBase/BotSharp.Plugin.KnowledgeBase.csproj @@ -1,4 +1,4 @@ - + $(TargetFramework) @@ -21,7 +21,7 @@ - + @@ -42,7 +42,7 @@ PreserveNewest - + PreserveNewest diff --git a/src/Plugins/BotSharp.Plugin.KnowledgeBase/Functions/GenerateKnowledgeFn.cs b/src/Plugins/BotSharp.Plugin.KnowledgeBase/Functions/GenerateKnowledgeFn.cs deleted file mode 100644 index 114ce7e4..00000000 --- a/src/Plugins/BotSharp.Plugin.KnowledgeBase/Functions/GenerateKnowledgeFn.cs +++ /dev/null @@ -1,90 +0,0 @@ -using BotSharp.Abstraction.Templating; -using BotSharp.Core.Infrastructures; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; - -namespace BotSharp.Plugin.KnowledgeBase.Functions; - -public class GenerateKnowledgeFn : IFunctionCallback -{ - public string Name => "generate_knowledge"; - - public string Indication => "generating knowledge"; - - private readonly IServiceProvider _services; - private readonly KnowledgeBaseSettings _settings; - - public GenerateKnowledgeFn(IServiceProvider services, KnowledgeBaseSettings settings) - { - _services = services; - _settings = settings; - } - - public async Task Execute(RoleDialogModel message) - { - var args = JsonSerializer.Deserialize(message.FunctionArgs ?? "{}"); - var agentService = _services.GetRequiredService(); - var llmAgent = await agentService.GetAgent(BuiltInAgentId.Planner); - var refineKnowledge = args.RefinedCollection; - String generateKnowledgePrompt; - if (args.RefineAnswer == true) - { - generateKnowledgePrompt = await GetRefineKnowledgePrompt(args.Question, args.Answer, args.ExistingAnswer); - } - else - { - generateKnowledgePrompt = await GetGenerateKnowledgePrompt(args.Question, args.Answer); - } - var agent = new Agent - { - Id = message.CurrentAgentId ?? string.Empty, - Name = "knowledge_generator", - Instruction = generateKnowledgePrompt, - LlmConfig = llmAgent.LlmConfig - }; - var response = await GetAiResponse(agent); - message.Data = response.Content.JsonArrayContent(); - message.Content = response.Content; - return true; - } - - private async Task GetGenerateKnowledgePrompt(string userQuestions, string sqlAnswer) - { - var agentService = _services.GetRequiredService(); - var render = _services.GetRequiredService(); - - var agent = await agentService.GetAgent(BuiltInAgentId.Learner); - var template = agent.Templates.FirstOrDefault(x => x.Name == "knowledge.generation")?.Content ?? string.Empty; - - return render.Render(template, new Dictionary - { - { "user_questions", userQuestions }, - { "sql_answer", sqlAnswer }, - }); - } - private async Task GetRefineKnowledgePrompt(string userQuestion, string sqlAnswer, string existionAnswer) - { - var agentService = _services.GetRequiredService(); - var render = _services.GetRequiredService(); - - var agent = await agentService.GetAgent(BuiltInAgentId.Learner); - var template = agent.Templates.FirstOrDefault(x => x.Name == "knowledge.generation.refine")?.Content ?? string.Empty; - - return render.Render(template, new Dictionary - { - { "user_question", userQuestion }, - { "new_answer", sqlAnswer }, - { "existing_answer", existionAnswer} - }); - } - private async Task GetAiResponse(Agent agent) - { - var text = "Generate question and answer pair"; - var message = new RoleDialogModel(AgentRole.User, text); - - var completion = CompletionProvider.GetChatCompletion(_services, - provider: agent.LlmConfig.Provider, - model: agent.LlmConfig.Model); - - return await completion.GetChatCompletions(agent, new List { message }); - } -} diff --git a/src/Plugins/BotSharp.Plugin.KnowledgeBase/data/agents/01acc3e5-0af7-49e6-ad7a-a760bd12dc40/templates/knowledge.generation.liquid b/src/Plugins/BotSharp.Plugin.KnowledgeBase/data/agents/01acc3e5-0af7-49e6-ad7a-a760bd12dc40/templates/knowledge.generation.plain.liquid similarity index 100% rename from src/Plugins/BotSharp.Plugin.KnowledgeBase/data/agents/01acc3e5-0af7-49e6-ad7a-a760bd12dc40/templates/knowledge.generation.liquid rename to src/Plugins/BotSharp.Plugin.KnowledgeBase/data/agents/01acc3e5-0af7-49e6-ad7a-a760bd12dc40/templates/knowledge.generation.plain.liquid From b9127358490e9a29e899d7e71b691855405acae1 Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Thu, 17 Oct 2024 18:19:15 -0500 Subject: [PATCH 05/23] add comments --- .../BotSharp.Abstraction/Instructs/IInstructService.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/Infrastructure/BotSharp.Abstraction/Instructs/IInstructService.cs b/src/Infrastructure/BotSharp.Abstraction/Instructs/IInstructService.cs index f1d17b93..7e59ecea 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Instructs/IInstructService.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Instructs/IInstructService.cs @@ -14,6 +14,13 @@ public interface IInstructService /// Task Execute(string agentId, RoleDialogModel message, string? templateName = null, string? instruction = null); - + /// + /// A generic way to execute completion by using specified instruction or template + /// + /// + /// + /// + /// + /// Task Instruct(string instruction, string agentId, InstructOptions options) where T : class; } From 84d43c1907815675ad2132cb7f65262baad2a150 Mon Sep 17 00:00:00 2001 From: Jicheng Lu Date: Thu, 17 Oct 2024 19:18:40 -0500 Subject: [PATCH 06/23] minor fix --- .../BotSharp.Core/Instructs/InstructService.Instruct.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Infrastructure/BotSharp.Core/Instructs/InstructService.Instruct.cs b/src/Infrastructure/BotSharp.Core/Instructs/InstructService.Instruct.cs index 40297400..76ad3608 100644 --- a/src/Infrastructure/BotSharp.Core/Instructs/InstructService.Instruct.cs +++ b/src/Infrastructure/BotSharp.Core/Instructs/InstructService.Instruct.cs @@ -63,14 +63,13 @@ public partial class InstructService var localAgent = new Agent { Id = agentId, - Name = agent.Name, + Name = agent?.Name ?? "Unknown", Instruction = prompt, TemplateDict = new() }; var messages = BuildDialogs(options); var completion = CompletionProvider.GetChatCompletion(_services, provider: options.Provider, model: options.Model); - return await completion.GetChatCompletions(localAgent, messages); } From b77fb87a5edda6a4e340768898c927c9fe428566 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=98=93=E7=A3=8A?= Date: Fri, 18 Oct 2024 11:08:55 +0800 Subject: [PATCH 07/23] Optimize TwilioMessageQueueService.cs Optimize TwilioMessageQueueService.cs --- .../Services/TwilioMessageQueueService.cs | 72 ++++++++++--------- 1 file changed, 39 insertions(+), 33 deletions(-) diff --git a/src/Plugins/BotSharp.Plugin.Twilio/Services/TwilioMessageQueueService.cs b/src/Plugins/BotSharp.Plugin.Twilio/Services/TwilioMessageQueueService.cs index e3476ea2..33cf800d 100644 --- a/src/Plugins/BotSharp.Plugin.Twilio/Services/TwilioMessageQueueService.cs +++ b/src/Plugins/BotSharp.Plugin.Twilio/Services/TwilioMessageQueueService.cs @@ -28,22 +28,19 @@ namespace BotSharp.Plugin.Twilio.Services await foreach (var message in _queue.Reader.ReadAllAsync(stoppingToken)) { await _throttler.WaitAsync(stoppingToken); - _ = Task.Run(async () => + try { - try - { - Console.WriteLine($"Start processing {message}."); - await ProcessUserMessageAsync(message); - } - catch (Exception ex) - { - Console.WriteLine($"Processing {message} failed due to {ex.Message}."); - } - finally - { - _throttler.Release(); - } - }); + Console.WriteLine($"Start processing {message}."); + await ProcessUserMessageAsync(message); + } + catch (Exception ex) + { + Console.WriteLine($"Processing {message} failed due to {ex.Message}."); + } + finally + { + _throttler.Release(); + } } } @@ -66,19 +63,7 @@ namespace BotSharp.Plugin.Twilio.Services var sessionManager = sp.GetRequiredService(); var progressService = sp.GetRequiredService(); InitProgressService(message, sessionManager, progressService); - - routing.Context.SetMessageId(message.ConversationId, inputMsg.MessageId); - var states = new List - { - new MessageState("channel", ConversationChannel.Phone), - new MessageState("calling_phone", message.From) - }; - - foreach (var kvp in message.States) - { - states.Add(new MessageState(kvp.Key, kvp.Value)); - } - conv.SetConversationId(message.ConversationId, states); + InitConversation(message, inputMsg, conv, routing); var result = await conv.SendMessage(config.AgentId, inputMsg, @@ -94,13 +79,36 @@ namespace BotSharp.Plugin.Twilio.Services }; } ); + reply.SpeechFileName = await GetReplySpeechFileName(message.ConversationId, reply, sp); + reply.Hints = GetHints(reply); ; + reply.Content = null; + await sessionManager.SetAssistantReplyAsync(message.ConversationId, message.SeqNumber, reply); + } + private static void InitConversation(CallerMessage message, RoleDialogModel inputMsg, IConversationService conv, IRoutingService routing) + { + routing.Context.SetMessageId(message.ConversationId, inputMsg.MessageId); + var states = new List + { + new("channel", ConversationChannel.Phone), + new("calling_phone", message.From) + }; + states.AddRange(message.States.Select(kvp => new MessageState(kvp.Key, kvp.Value))); + conv.SetConversationId(message.ConversationId, states); + } + + private static async Task GetReplySpeechFileName(string conversationId, AssistantMessage reply, IServiceProvider sp) + { var completion = CompletionProvider.GetAudioCompletion(sp, "openai", "tts-1"); var fileStorage = sp.GetRequiredService(); var data = await completion.GenerateAudioFromTextAsync(reply.Content); var fileName = $"reply_{reply.MessageId}.mp3"; - fileStorage.SaveSpeechFile(message.ConversationId, fileName, data); - reply.SpeechFileName = fileName; + fileStorage.SaveSpeechFile(conversationId, fileName, data); + return fileName; + } + + private static string GetHints(AssistantMessage reply) + { var phrases = reply.Content.Split(',', StringSplitOptions.RemoveEmptyEntries); int capcity = 100; var hints = new List(capcity); @@ -122,9 +130,7 @@ namespace BotSharp.Plugin.Twilio.Services } // add frequency short words hints.AddRange(["yes", "no", "correct", "right"]); - reply.Hints = string.Join(", ", hints.Select(x => x.ToLower()).Distinct().Reverse()); - reply.Content = null; - await sessionManager.SetAssistantReplyAsync(message.ConversationId, message.SeqNumber, reply); + return string.Join(", ", hints.Select(x => x.ToLower()).Distinct().Reverse()); } private static void InitProgressService(CallerMessage message, ITwilioSessionManager sessionManager, IConversationProgressService progressService) From e9194b82472637f2228abbb2e4329cb052df1f91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=98=93=E7=A3=8A?= Date: Fri, 18 Oct 2024 13:58:57 +0800 Subject: [PATCH 08/23] optimize TwilioMessageQueueService.cs optimize TwilioMessageQueueService.cs --- .../Services/TwilioMessageQueueService.cs | 27 ++++++++++--------- 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/src/Plugins/BotSharp.Plugin.Twilio/Services/TwilioMessageQueueService.cs b/src/Plugins/BotSharp.Plugin.Twilio/Services/TwilioMessageQueueService.cs index 33cf800d..fb215e71 100644 --- a/src/Plugins/BotSharp.Plugin.Twilio/Services/TwilioMessageQueueService.cs +++ b/src/Plugins/BotSharp.Plugin.Twilio/Services/TwilioMessageQueueService.cs @@ -28,19 +28,22 @@ namespace BotSharp.Plugin.Twilio.Services await foreach (var message in _queue.Reader.ReadAllAsync(stoppingToken)) { await _throttler.WaitAsync(stoppingToken); - try + _ = Task.Run(async () => { - Console.WriteLine($"Start processing {message}."); - await ProcessUserMessageAsync(message); - } - catch (Exception ex) - { - Console.WriteLine($"Processing {message} failed due to {ex.Message}."); - } - finally - { - _throttler.Release(); - } + try + { + Console.WriteLine($"Start processing {message}."); + await ProcessUserMessageAsync(message); + } + catch (Exception ex) + { + Console.WriteLine($"Processing {message} failed due to {ex.Message}."); + } + finally + { + _throttler.Release(); + } + }); } } From effe49457144918847a103603d875ca252cc4b7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=98=93=E7=A3=8A?= Date: Fri, 18 Oct 2024 14:24:57 +0800 Subject: [PATCH 09/23] voice copilot support postback voice copilot support postback --- .../Services/TwilioMessageQueueService.cs | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/Plugins/BotSharp.Plugin.Twilio/Services/TwilioMessageQueueService.cs b/src/Plugins/BotSharp.Plugin.Twilio/Services/TwilioMessageQueueService.cs index fb215e71..454b7166 100644 --- a/src/Plugins/BotSharp.Plugin.Twilio/Services/TwilioMessageQueueService.cs +++ b/src/Plugins/BotSharp.Plugin.Twilio/Services/TwilioMessageQueueService.cs @@ -67,10 +67,10 @@ namespace BotSharp.Plugin.Twilio.Services var progressService = sp.GetRequiredService(); InitProgressService(message, sessionManager, progressService); InitConversation(message, inputMsg, conv, routing); - + var result = await conv.SendMessage(config.AgentId, inputMsg, - replyMessage: null, + replyMessage: BuildPostbackMessageModel(conv), async msg => { reply = new AssistantMessage() @@ -88,6 +88,20 @@ namespace BotSharp.Plugin.Twilio.Services await sessionManager.SetAssistantReplyAsync(message.ConversationId, message.SeqNumber, reply); } + private PostbackMessageModel BuildPostbackMessageModel(IConversationService conv) + { + var messages = conv.GetDialogHistory(1); + if (!messages.Any()) return null; + var lastMessage = messages[0]; + if (string.IsNullOrEmpty(lastMessage.PostbackFunctionName)) return null; + return new PostbackMessageModel + { + FunctionName = lastMessage.PostbackFunctionName, + ParentId = lastMessage.MessageId, + Payload = lastMessage.Payload + }; + } + private static void InitConversation(CallerMessage message, RoleDialogModel inputMsg, IConversationService conv, IRoutingService routing) { routing.Context.SetMessageId(message.ConversationId, inputMsg.MessageId); From b99b66e4890eb9acddf5661234414a43be853c9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=98=93=E7=A3=8A?= Date: Fri, 18 Oct 2024 16:40:06 +0800 Subject: [PATCH 10/23] voice copilot playload use tel Digits --- .../Controllers/TwilioVoiceController.cs | 1 + src/Plugins/BotSharp.Plugin.Twilio/Models/CallerMessage.cs | 1 + .../Services/TwilioMessageQueueService.cs | 6 +++--- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/Plugins/BotSharp.Plugin.Twilio/Controllers/TwilioVoiceController.cs b/src/Plugins/BotSharp.Plugin.Twilio/Controllers/TwilioVoiceController.cs index d9ff3402..cf318e35 100644 --- a/src/Plugins/BotSharp.Plugin.Twilio/Controllers/TwilioVoiceController.cs +++ b/src/Plugins/BotSharp.Plugin.Twilio/Controllers/TwilioVoiceController.cs @@ -72,6 +72,7 @@ public class TwilioVoiceController : TwilioController ConversationId = conversationId, SeqNumber = seqNum, Content = messageContent, + Digits = request.Digits, From = request.From }; diff --git a/src/Plugins/BotSharp.Plugin.Twilio/Models/CallerMessage.cs b/src/Plugins/BotSharp.Plugin.Twilio/Models/CallerMessage.cs index a6339c7b..c74addd0 100644 --- a/src/Plugins/BotSharp.Plugin.Twilio/Models/CallerMessage.cs +++ b/src/Plugins/BotSharp.Plugin.Twilio/Models/CallerMessage.cs @@ -5,6 +5,7 @@ namespace BotSharp.Plugin.Twilio.Models public string ConversationId { get; set; } public int SeqNumber { get; set; } public string Content { get; set; } + public string Digits { get; set; } public string From { get; set; } public Dictionary States { get; set; } = new(); diff --git a/src/Plugins/BotSharp.Plugin.Twilio/Services/TwilioMessageQueueService.cs b/src/Plugins/BotSharp.Plugin.Twilio/Services/TwilioMessageQueueService.cs index 454b7166..ef6ce4a3 100644 --- a/src/Plugins/BotSharp.Plugin.Twilio/Services/TwilioMessageQueueService.cs +++ b/src/Plugins/BotSharp.Plugin.Twilio/Services/TwilioMessageQueueService.cs @@ -70,7 +70,7 @@ namespace BotSharp.Plugin.Twilio.Services var result = await conv.SendMessage(config.AgentId, inputMsg, - replyMessage: BuildPostbackMessageModel(conv), + replyMessage: BuildPostbackMessageModel(conv, message), async msg => { reply = new AssistantMessage() @@ -88,7 +88,7 @@ namespace BotSharp.Plugin.Twilio.Services await sessionManager.SetAssistantReplyAsync(message.ConversationId, message.SeqNumber, reply); } - private PostbackMessageModel BuildPostbackMessageModel(IConversationService conv) + private PostbackMessageModel BuildPostbackMessageModel(IConversationService conv, CallerMessage message) { var messages = conv.GetDialogHistory(1); if (!messages.Any()) return null; @@ -98,7 +98,7 @@ namespace BotSharp.Plugin.Twilio.Services { FunctionName = lastMessage.PostbackFunctionName, ParentId = lastMessage.MessageId, - Payload = lastMessage.Payload + Payload = message.Digits }; } From 0e16cd36ae5f41eaa40428ecc691048b8a7379cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=98=93=E7=A3=8A?= Date: Fri, 18 Oct 2024 22:48:16 +0800 Subject: [PATCH 11/23] format TwilioMessageQueueService.cs --- .../Services/TwilioMessageQueueService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Plugins/BotSharp.Plugin.Twilio/Services/TwilioMessageQueueService.cs b/src/Plugins/BotSharp.Plugin.Twilio/Services/TwilioMessageQueueService.cs index ef6ce4a3..fe63ba82 100644 --- a/src/Plugins/BotSharp.Plugin.Twilio/Services/TwilioMessageQueueService.cs +++ b/src/Plugins/BotSharp.Plugin.Twilio/Services/TwilioMessageQueueService.cs @@ -83,7 +83,7 @@ namespace BotSharp.Plugin.Twilio.Services } ); reply.SpeechFileName = await GetReplySpeechFileName(message.ConversationId, reply, sp); - reply.Hints = GetHints(reply); ; + reply.Hints = GetHints(reply); reply.Content = null; await sessionManager.SetAssistantReplyAsync(message.ConversationId, message.SeqNumber, reply); } From 78098874badb26695f84ec96d464ba964ba4fc2d Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Fri, 18 Oct 2024 10:00:05 -0500 Subject: [PATCH 12/23] add comments --- .../BotSharp.Abstraction/Instructs/IInstructService.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Infrastructure/BotSharp.Abstraction/Instructs/IInstructService.cs b/src/Infrastructure/BotSharp.Abstraction/Instructs/IInstructService.cs index 7e59ecea..e6a659cc 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Instructs/IInstructService.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Instructs/IInstructService.cs @@ -18,9 +18,9 @@ public interface IInstructService /// A generic way to execute completion by using specified instruction or template /// /// - /// - /// - /// + /// Prompt + /// Agent id + /// Llm Provider, model, message, prompt data /// Task Instruct(string instruction, string agentId, InstructOptions options) where T : class; } From 7db73c19e27863e3c9669a05d23452519f9ad315 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=98=93=E7=A3=8A?= Date: Fri, 18 Oct 2024 23:04:33 +0800 Subject: [PATCH 13/23] optimize assistant message payload --- .../Services/ConversationService.SendMessage.cs | 8 ++++---- .../Routing/RoutingService.GetConversationContent.cs | 10 +--------- .../Providers/Chat/ChatCompletionProvider.cs | 2 +- .../Providers/Chat/ChatCompletionProvider.cs | 2 +- 4 files changed, 7 insertions(+), 15 deletions(-) diff --git a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.SendMessage.cs b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.SendMessage.cs index b0410750..0112304d 100644 --- a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.SendMessage.cs +++ b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.SendMessage.cs @@ -40,10 +40,10 @@ public partial class ConversationService routing.Context.Push(agent.Id, reason: "request started"); // Save payload in order to assign the payload before hook is invoked - if (replyMessage != null && !string.IsNullOrEmpty(replyMessage.Payload)) - { - message.Payload = replyMessage.Payload; - } + // if (replyMessage != null && !string.IsNullOrEmpty(replyMessage.Payload)) + // { + // message.Payload = replyMessage.Payload; + // } // Before chat completion hook foreach (var hook in hooks) diff --git a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.GetConversationContent.cs b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.GetConversationContent.cs index f95b3f37..f8efab46 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.GetConversationContent.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.GetConversationContent.cs @@ -16,15 +16,7 @@ public partial class RoutingService role = agent.Name; } - if (role == AgentRole.User) - { - conversation += $"{role}: {dialog.Payload ?? dialog.Content}\r\n"; - } - else - { - // Assistant reply deosn't need help with payload - conversation += $"{role}: {dialog.Content}\r\n"; - } + conversation += $"{role}: {dialog.Payload ?? dialog.Content}\r\n"; } return conversation; diff --git a/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/Chat/ChatCompletionProvider.cs b/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/Chat/ChatCompletionProvider.cs index 678ee82b..cac71e1d 100644 --- a/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/Chat/ChatCompletionProvider.cs +++ b/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/Chat/ChatCompletionProvider.cs @@ -288,7 +288,7 @@ public class ChatCompletionProvider : IChatCompletion } else if (message.Role == AgentRole.Assistant) { - messages.Add(new AssistantChatMessage(message.Content)); + messages.Add(new AssistantChatMessage(message.Payload ?? message.Content)); } } diff --git a/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Chat/ChatCompletionProvider.cs b/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Chat/ChatCompletionProvider.cs index 57f7ac13..553d1490 100644 --- a/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Chat/ChatCompletionProvider.cs +++ b/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Chat/ChatCompletionProvider.cs @@ -289,7 +289,7 @@ public class ChatCompletionProvider : IChatCompletion } else if (message.Role == AgentRole.Assistant) { - messages.Add(new AssistantChatMessage(message.Content)); + messages.Add(new AssistantChatMessage(message.Payload ?? message.Content)); } } From 99192c8eacb4a6720cf11ea6a374ca7b44a7bbbc Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Fri, 18 Oct 2024 10:58:22 -0500 Subject: [PATCH 14/23] fix routing context --- src/Infrastructure/BotSharp.Core/Routing/RoutingContext.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Infrastructure/BotSharp.Core/Routing/RoutingContext.cs b/src/Infrastructure/BotSharp.Core/Routing/RoutingContext.cs index 31caa932..a77397d0 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/RoutingContext.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/RoutingContext.cs @@ -42,11 +42,10 @@ public class RoutingContext : IRoutingContext _routerAgentIds = agentService.GetAgents(new AgentFilter { Type = AgentType.Routing - }).Result.Items - .Select(x => x.Id).ToArray(); + }).Result.Items.Select(x => x.Id).ToArray(); } - return _stack.Where(x => !_routerAgentIds.Contains(x)).Last(); + return _stack.Where(x => !_routerAgentIds.Contains(x)).LastOrDefault() ?? string.Empty; } } From d481928f168916d6e2852fa5774302fe8ede4b75 Mon Sep 17 00:00:00 2001 From: Haiping Chen Date: Fri, 18 Oct 2024 11:32:41 -0500 Subject: [PATCH 15/23] Product V3 --- .../Browsing/Models/WebPageResponseFilter.cs | 10 +++++++ .../PlaywrightDriver/PlaywrightInstance.cs | 28 +++++++++++-------- 2 files changed, 26 insertions(+), 12 deletions(-) diff --git a/src/Infrastructure/BotSharp.Abstraction/Browsing/Models/WebPageResponseFilter.cs b/src/Infrastructure/BotSharp.Abstraction/Browsing/Models/WebPageResponseFilter.cs index cd97a01d..935d2282 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Browsing/Models/WebPageResponseFilter.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Browsing/Models/WebPageResponseFilter.cs @@ -4,4 +4,14 @@ public class WebPageResponseFilter { public string Url { get; set; } = null!; public string[]? QueryParameters { get; set; } + + /// + /// contains, starts, ends, equals + /// + public string UrlMatchPattern { get; set; } = "contains"; + + /// + /// Handle Content-Type: text/x-component + /// + public int PartIndex { get; set; } = -1; } diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightInstance.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightInstance.cs index 4a67ffeb..2bbb0acf 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightInstance.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightInstance.cs @@ -146,37 +146,41 @@ public class PlaywrightInstance : IDisposable { if (e.Status != 204 && e.Headers.ContainsKey("content-type") && - e.Headers["content-type"].Contains("application/json") && (e.Request.ResourceType == "fetch" || e.Request.ResourceType == "xhr") && (excludeResponseUrls == null || !excludeResponseUrls.Any(url => e.Url.ToLower().Contains(url))) && (includeResponseUrls == null || includeResponseUrls.Any(url => e.Url.ToLower().Contains(url)))) { Serilog.Log.Information($"{e.Request.Method}: {e.Url}"); - JsonElement? json = null; + try { - if (e.Status == 200 && e.Ok) - { - json = await e.JsonAsync(); - } - else - { - Serilog.Log.Warning($"Response status: {e.Status} {e.StatusText}, OK: {e.Ok}"); - } - var result = new WebPageResponseData { Url = e.Url.ToLower(), PostData = e.Request?.PostData ?? string.Empty, - ResponseData = JsonSerializer.Serialize(json), ResponseInMemory = responseInMemory }; + if (e.Headers["content-type"].Contains("application/json")) + { + if (e.Status == 200 && e.Ok) + { + var json = await e.JsonAsync(); + result.ResponseData = JsonSerializer.Serialize(json); + } + } + else + { + var html = await e.TextAsync(); + result.ResponseData = html; + } + if (responseContainer != null && responseInMemory) { responseContainer.Add(result); } + Serilog.Log.Warning($"Response status: {e.Status} {e.StatusText}, OK: {e.Ok}"); var webPageResponseHooks = _services.GetServices(); foreach (var hook in webPageResponseHooks) { From 0dd943beddbd3f27ddf8df923b25d96cdb3f7b45 Mon Sep 17 00:00:00 2001 From: Joanna Ren <101223@smsassist.com> Date: Fri, 18 Oct 2024 12:02:16 -0500 Subject: [PATCH 16/23] Add relevant knowledge to Summary Plan --- .../templates/handle_excel_request.fn.liquid | 3 ++- .../Functions/PrimaryStagePlanFn.cs | 8 +++----- .../Functions/SecondaryStagePlanFn.cs | 5 ++++- .../Functions/SummaryPlanFn.cs | 5 +++-- .../TwoStaging/Models/FirstStagePlan.cs | 16 ++++++++-------- .../templates/query_result_formatting.liquid | 2 +- 6 files changed, 21 insertions(+), 18 deletions(-) diff --git a/src/Plugins/BotSharp.Plugin.ExcelHandler/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/handle_excel_request.fn.liquid b/src/Plugins/BotSharp.Plugin.ExcelHandler/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/handle_excel_request.fn.liquid index b9444c61..a3f27694 100644 --- a/src/Plugins/BotSharp.Plugin.ExcelHandler/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/handle_excel_request.fn.liquid +++ b/src/Plugins/BotSharp.Plugin.ExcelHandler/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/handle_excel_request.fn.liquid @@ -1 +1,2 @@ -Please call handle_excel_request if user wants to load the data from a excel/csv file. \ No newline at end of file +Please call handle_excel_request if user wants to load the data from a excel/csv file. +handle_excel_request can NOT generate excel/csv. \ No newline at end of file diff --git a/src/Plugins/BotSharp.Plugin.Planner/Functions/PrimaryStagePlanFn.cs b/src/Plugins/BotSharp.Plugin.Planner/Functions/PrimaryStagePlanFn.cs index 5cf4f4f7..0024c916 100644 --- a/src/Plugins/BotSharp.Plugin.Planner/Functions/PrimaryStagePlanFn.cs +++ b/src/Plugins/BotSharp.Plugin.Planner/Functions/PrimaryStagePlanFn.cs @@ -38,6 +38,8 @@ public class PrimaryStagePlanFn : IFunctionCallback } } knowledges = knowledges.Distinct().ToList(); + var knowledgeState = String.Join("\r\n", knowledges); + state.SetState("relevant_knowledges", knowledgeState); // Get first stage planning prompt var currentAgent = await agentService.LoadAgent(message.CurrentAgentId); @@ -67,11 +69,7 @@ public class PrimaryStagePlanFn : IFunctionCallback var agent = await agentService.GetAgent(BuiltInAgentId.Planner); var template = agent.Templates.FirstOrDefault(x => x.Name == "two_stage.1st.plan")?.Content ?? string.Empty; - var responseFormat = JsonSerializer.Serialize(new FirstStagePlan - { - Parameters = [ JsonDocument.Parse("{}") ], - Results = [ string.Empty ] - }); + var responseFormat = JsonSerializer.Serialize(new FirstStagePlan{}); // Get global knowledges var globalKnowledges = new List(); diff --git a/src/Plugins/BotSharp.Plugin.Planner/Functions/SecondaryStagePlanFn.cs b/src/Plugins/BotSharp.Plugin.Planner/Functions/SecondaryStagePlanFn.cs index 8279f7e2..df4a879f 100644 --- a/src/Plugins/BotSharp.Plugin.Planner/Functions/SecondaryStagePlanFn.cs +++ b/src/Plugins/BotSharp.Plugin.Planner/Functions/SecondaryStagePlanFn.cs @@ -40,9 +40,12 @@ public class SecondaryStagePlanFn : IFunctionCallback knowledges.AddRange(k); } knowledges = knowledges.Distinct().ToList(); - var knowledgeResults = string.Join("\r\n\r\n=====\r\n", knowledges); + var knowledgeState = states.GetState("relevant_knowledges"); + knowledgeState += String.Join("\r\n", knowledges); + states.SetState("relevant_knowledges", knowledgeState); + // Get second stage planning prompt var currentAgent = await agentService.LoadAgent(message.CurrentAgentId); var prompt = await GetSecondStagePlanPrompt(taskSecondary.TaskDescription, planResult, knowledgeResults, message); diff --git a/src/Plugins/BotSharp.Plugin.Planner/Functions/SummaryPlanFn.cs b/src/Plugins/BotSharp.Plugin.Planner/Functions/SummaryPlanFn.cs index ef634392..bcf7588a 100644 --- a/src/Plugins/BotSharp.Plugin.Planner/Functions/SummaryPlanFn.cs +++ b/src/Plugins/BotSharp.Plugin.Planner/Functions/SummaryPlanFn.cs @@ -35,7 +35,8 @@ public class SummaryPlanFn : IFunctionCallback var steps = states.GetState("planning_result").JsonArrayContent(); var allTables = new List(); var ddlStatements = string.Empty; - var planResult = states.GetState("planning_result"); + var relevantKnowledge = states.GetState("planning_result"); + relevantKnowledge += "\r\n" + states.GetState("relevant_knowledges"); var dictionaryItems = states.GetState("dictionary_items"); var excelImportResult = states.GetState("excel_import_result"); @@ -54,7 +55,7 @@ public class SummaryPlanFn : IFunctionCallback ddlStatements += "\r\n" + msgCopy.Content; // Summarize and generate query - var prompt = await GetSummaryPlanPrompt(msgCopy, taskRequirement, planResult, dictionaryItems, ddlStatements, excelImportResult); + var prompt = await GetSummaryPlanPrompt(msgCopy, taskRequirement, relevantKnowledge, dictionaryItems, ddlStatements, excelImportResult); _logger.LogInformation($"Summary plan prompt:\r\n{prompt}"); var plannerAgent = new Agent diff --git a/src/Plugins/BotSharp.Plugin.Planner/TwoStaging/Models/FirstStagePlan.cs b/src/Plugins/BotSharp.Plugin.Planner/TwoStaging/Models/FirstStagePlan.cs index 9588b811..10d26e05 100644 --- a/src/Plugins/BotSharp.Plugin.Planner/TwoStaging/Models/FirstStagePlan.cs +++ b/src/Plugins/BotSharp.Plugin.Planner/TwoStaging/Models/FirstStagePlan.cs @@ -5,8 +5,8 @@ public class FirstStagePlan [JsonPropertyName("task_detail")] public string Task { get; set; } = ""; - [JsonPropertyName("reason")] - public string Reason { get; set; } = ""; + //[JsonPropertyName("reason")] + //public string Reason { get; set; } = ""; [JsonPropertyName("step")] public int Step { get; set; } = -1; @@ -20,14 +20,14 @@ public class FirstStagePlan [JsonPropertyName("related_tables")] public string[] Tables { get; set; } = []; - [JsonPropertyName("related_urls")] - public string[] Urls { get; set; } = []; + //[JsonPropertyName("related_urls")] + //public string[] Urls { get; set; } = []; - [JsonPropertyName("input_args")] - public JsonDocument[] Parameters { get; set; } = []; + //[JsonPropertyName("input_args")] + //public JsonDocument[] Parameters { get; set; } = []; - [JsonPropertyName("output_results")] - public string[] Results { get; set; } = []; + //[JsonPropertyName("output_results")] + //public string[] Results { get; set; } = []; public override string ToString() { diff --git a/src/Plugins/BotSharp.Plugin.SqlDriver/data/agents/beda4c12-e1ec-4b4b-b328-3df4a6687c4f/templates/query_result_formatting.liquid b/src/Plugins/BotSharp.Plugin.SqlDriver/data/agents/beda4c12-e1ec-4b4b-b328-3df4a6687c4f/templates/query_result_formatting.liquid index 5d07a941..5c6ad05c 100644 --- a/src/Plugins/BotSharp.Plugin.SqlDriver/data/agents/beda4c12-e1ec-4b4b-b328-3df4a6687c4f/templates/query_result_formatting.liquid +++ b/src/Plugins/BotSharp.Plugin.SqlDriver/data/agents/beda4c12-e1ec-4b4b-b328-3df4a6687c4f/templates/query_result_formatting.liquid @@ -1,5 +1,5 @@ Output in human readable format. If there is large amount of rows, shape it in tabular, otherwise, output in plain text. -Put user task description in the first line in the same language, for example, user is using Chinese, you have to output the result in Chinese. +Put user task description in the first line in the same language, for example, if user is using Chinese, you have to output the result in Chinese. User Task Description: {{ requirement_detail }} \ No newline at end of file From 14bd97d088ed187f1ebd5b03f5145c0ed241bc31 Mon Sep 17 00:00:00 2001 From: Haiping Chen Date: Fri, 18 Oct 2024 14:21:11 -0500 Subject: [PATCH 17/23] Set default Direction as 'down' --- .../BotSharp.Abstraction/Browsing/Models/PageActionArgs.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Infrastructure/BotSharp.Abstraction/Browsing/Models/PageActionArgs.cs b/src/Infrastructure/BotSharp.Abstraction/Browsing/Models/PageActionArgs.cs index aa4154ff..0964389b 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Browsing/Models/PageActionArgs.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Browsing/Models/PageActionArgs.cs @@ -7,7 +7,7 @@ public class PageActionArgs public BroswerActionEnum Action { get; set; } public string? Content { get; set; } - public string? Direction { get; set; } + public string Direction { get; set; } = "down"; public string Url { get; set; } = null!; From 37acf2cf2fddb0b06ec11496bed573561a3639c3 Mon Sep 17 00:00:00 2001 From: Joanna Ren <101223@smsassist.com> Date: Fri, 18 Oct 2024 14:25:15 -0500 Subject: [PATCH 18/23] minor change --- .../BotSharp.Plugin.ExcelHandler/Services/MySqlService.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Plugins/BotSharp.Plugin.ExcelHandler/Services/MySqlService.cs b/src/Plugins/BotSharp.Plugin.ExcelHandler/Services/MySqlService.cs index 70ce855e..f85b5ace 100644 --- a/src/Plugins/BotSharp.Plugin.ExcelHandler/Services/MySqlService.cs +++ b/src/Plugins/BotSharp.Plugin.ExcelHandler/Services/MySqlService.cs @@ -139,11 +139,11 @@ namespace BotSharp.Plugin.ExcelHandler.Services string insertDataSql = ProcessInsertSqlQuery(dataSql); ExecuteSqlQueryForInsertion(insertDataSql); - return (true, $"{_currentFileName}: \r\n {_excelRowSize} records have been successfully inserted into `{_tableName}` table"); + return (true, $"{_currentFileName}: \r\n {_excelRowSize} records have been successfully inserted into `{_database}`.`{_tableName}` table"); } catch (Exception ex) { - return (false, $"{_currentFileName}: Failed to parse excel data into `{_tableName}` table. ####Error: {ex.Message}"); + return (false, $"{_currentFileName}: Failed to parse excel data into `{_database}`.`{_tableName}` table. ####Error: {ex.Message}"); } } private string ParseSheetData(ISheet singleSheet) From d84c8995d10ac6935901f4bc964eefe27ae7e71c Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Fri, 18 Oct 2024 15:50:39 -0500 Subject: [PATCH 19/23] add tags --- .../Conversations/IConversationService.cs | 1 + .../Conversations/Models/Conversation.cs | 6 +++-- .../Filters/ConversationFilter.cs | 4 ++- .../Repositories/IBotSharpRepository.cs | 1 + .../Services/ConversationService.cs | 6 +++++ .../Repository/BotSharpDbContext.cs | 3 +++ .../FileRepository.Conversation.cs | 26 ++++++++++++++++--- .../Controllers/ConversationController.cs | 13 +++++++--- .../Conversations/ConversationViewModel.cs | 3 +++ .../UpdateConversationRequest.cs | 6 +++++ .../Collections/ConversationDocument.cs | 1 + .../BotSharp.Plugin.MongoStorage/MongoBase.cs | 2 -- .../MongoRepository.Conversation.cs | 21 +++++++++++++++ 13 files changed, 82 insertions(+), 11 deletions(-) create mode 100644 src/Infrastructure/BotSharp.OpenAPI/ViewModels/Conversations/UpdateConversationRequest.cs diff --git a/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationService.cs b/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationService.cs index 85f41e57..ffb4986a 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationService.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationService.cs @@ -12,6 +12,7 @@ public interface IConversationService Task GetConversation(string id); Task> GetConversations(ConversationFilter filter); Task UpdateConversationTitle(string id, string title); + Task UpdateConversationTags(string conversationId, List tags); Task UpdateConversationMessage(string conversationId, UpdateMessageRequest request); Task> GetLastConversations(); Task> GetIdleConversations(int batchSize, int messageLimit, int bufferHours, IEnumerable excludeAgentIds); diff --git a/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/Conversation.cs b/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/Conversation.cs index 890f3211..38734d75 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/Conversation.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/Conversation.cs @@ -15,10 +15,10 @@ public class Conversation public string Title { get; set; } = string.Empty; [JsonIgnore] - public List Dialogs { get; set; } = new List(); + public List Dialogs { get; set; } = new(); [JsonIgnore] - public Dictionary States { get; set; } = new Dictionary(); + public Dictionary States { get; set; } = new(); public string Status { get; set; } = ConversationStatus.Open; @@ -26,6 +26,8 @@ public class Conversation public int DialogCount { get; set; } + public List Tags { get; set; } = new(); + public DateTime UpdatedTime { get; set; } = DateTime.UtcNow; public DateTime CreatedTime { get; set; } = DateTime.UtcNow; } diff --git a/src/Infrastructure/BotSharp.Abstraction/Repositories/Filters/ConversationFilter.cs b/src/Infrastructure/BotSharp.Abstraction/Repositories/Filters/ConversationFilter.cs index 12864ac3..6ebedc5b 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Repositories/Filters/ConversationFilter.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Repositories/Filters/ConversationFilter.cs @@ -22,5 +22,7 @@ public class ConversationFilter /// /// Check whether each key in the list is in the conversation states and its value equals to target value if not empty /// - public IEnumerable States { get; set; } = new List(); + public IEnumerable? States { get; set; } = []; + + public IEnumerable? Tags { get; set; } = []; } \ No newline at end of file diff --git a/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs b/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs index dd2648d8..1471093b 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs @@ -72,6 +72,7 @@ public interface IBotSharpRepository Conversation GetConversation(string conversationId); PagedItems GetConversations(ConversationFilter filter); void UpdateConversationTitle(string conversationId, string title); + bool UpdateConversationTags(string conversationId, List tags); bool UpdateConversationMessage(string conversationId, UpdateMessageRequest request); void UpdateConversationBreakpoint(string conversationId, ConversationBreakpoint breakpoint); ConversationBreakpoint? GetConversationBreakpoint(string conversationId); diff --git a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.cs b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.cs index 4d3ed7af..97b69d44 100644 --- a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.cs +++ b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.cs @@ -51,6 +51,12 @@ public partial class ConversationService : IConversationService return conversation; } + public async Task UpdateConversationTags(string conversationId, List tags) + { + var db = _services.GetRequiredService(); + return db.UpdateConversationTags(conversationId, tags); + } + public async Task UpdateConversationMessage(string conversationId, UpdateMessageRequest request) { var db = _services.GetRequiredService(); diff --git a/src/Infrastructure/BotSharp.Core/Repository/BotSharpDbContext.cs b/src/Infrastructure/BotSharp.Core/Repository/BotSharpDbContext.cs index 8e647694..a4131e64 100644 --- a/src/Infrastructure/BotSharp.Core/Repository/BotSharpDbContext.cs +++ b/src/Infrastructure/BotSharp.Core/Repository/BotSharpDbContext.cs @@ -161,6 +161,9 @@ public class BotSharpDbContext : Database, IBotSharpRepository public void UpdateConversationTitle(string conversationId, string title) => throw new NotImplementedException(); + public bool UpdateConversationTags(string conversationId, List tags) + => throw new NotImplementedException(); + public bool UpdateConversationMessage(string conversationId, UpdateMessageRequest request) => throw new NotImplementedException(); diff --git a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Conversation.cs b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Conversation.cs index d61fda41..bc9912b3 100644 --- a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Conversation.cs +++ b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Conversation.cs @@ -1,8 +1,5 @@ using BotSharp.Abstraction.Loggers.Models; -using BotSharp.Abstraction.Repositories.Models; -using System.Globalization; using System.IO; -using System.Xml.Linq; namespace BotSharp.Core.Repository { @@ -13,6 +10,7 @@ namespace BotSharp.Core.Repository var utcNow = DateTime.UtcNow; conversation.CreatedTime = utcNow; conversation.UpdatedTime = utcNow; + conversation.Tags = conversation.Tags ?? new(); var dir = Path.Combine(_dbSettings.FileRepository, _conversationSettings.DataDir, conversation.Id); if (!Directory.Exists(dir)) @@ -134,6 +132,24 @@ namespace BotSharp.Core.Repository } } + public bool UpdateConversationTags(string conversationId, List tags) + { + if (string.IsNullOrEmpty(conversationId)) return false; + + var convDir = FindConversationDirectory(conversationId); + if (string.IsNullOrEmpty(convDir)) return false; + + var convFile = Path.Combine(convDir, CONVERSATION_FILE); + if (!File.Exists(convFile)) return false; + + var json = File.ReadAllText(convFile); + var conv = JsonSerializer.Deserialize(json, _options); + conv.Tags = tags ?? new(); + conv.UpdatedTime = DateTime.UtcNow; + File.WriteAllText(convFile, JsonSerializer.Serialize(conv, _options)); + return true; + } + public bool UpdateConversationMessage(string conversationId, UpdateMessageRequest request) { if (string.IsNullOrEmpty(conversationId)) return false; @@ -354,6 +370,10 @@ namespace BotSharp.Core.Repository { matched = matched && record.CreatedTime >= filter.StartTime.Value; } + if (filter?.Tags != null && filter.Tags.Any()) + { + matched = matched && !record.Tags.IsNullOrEmpty() && record.Tags.Exists(t => filter.Tags.Contains(t)); + } // Check states if (filter != null && !filter.States.IsNullOrEmpty()) diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs index 600c0dec..f5cfc7c8 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs @@ -202,7 +202,7 @@ public class ConversationController : ControllerBase public async Task UpdateConversationTitle([FromRoute] string conversationId, [FromBody] UpdateConversationTitleModel newTile) { var userService = _services.GetRequiredService(); - var conversationService = _services.GetRequiredService(); + var conv = _services.GetRequiredService(); var user = await userService.GetUser(_user.Id); var filter = new ConversationFilter @@ -210,17 +210,24 @@ public class ConversationController : ControllerBase Id = conversationId, UserId = user.Role != UserRole.Admin ? user.Id : null }; - var conversations = await conversationService.GetConversations(filter); + var conversations = await conv.GetConversations(filter); if (conversations.Items.IsNullOrEmpty()) { return false; } - var response = await conversationService.UpdateConversationTitle(conversationId, newTile.NewTitle); + var response = await conv.UpdateConversationTitle(conversationId, newTile.NewTitle); return response != null; } + [HttpPut("/conversation/{conversationId}/update-tags")] + public async Task UpdateConversationTags([FromRoute] string conversationId, [FromBody] UpdateConversationRequest request) + { + var conv = _services.GetRequiredService(); + return await conv.UpdateConversationTags(conversationId, request.Tags); + } + [HttpPut("/conversation/{conversationId}/update-message")] public async Task UpdateConversationMessage([FromRoute] string conversationId, [FromBody] UpdateMessageModel model) { diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Conversations/ConversationViewModel.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Conversations/ConversationViewModel.cs index c50ecc88..05f8cb89 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Conversations/ConversationViewModel.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Conversations/ConversationViewModel.cs @@ -29,6 +29,8 @@ public class ConversationViewModel public string Status { get; set; } public Dictionary States { get; set; } + public List Tags { get; set; } = new(); + [JsonPropertyName("updated_time")] public DateTime UpdatedTime { get; set; } = DateTime.UtcNow; [JsonPropertyName("created_time")] @@ -48,6 +50,7 @@ public class ConversationViewModel Channel = sess.Channel, Status = sess.Status, TaskId = sess.TaskId, + Tags = sess.Tags ?? new(), CreatedTime = sess.CreatedTime, UpdatedTime = sess.UpdatedTime }; diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Conversations/UpdateConversationRequest.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Conversations/UpdateConversationRequest.cs new file mode 100644 index 00000000..c9b89747 --- /dev/null +++ b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Conversations/UpdateConversationRequest.cs @@ -0,0 +1,6 @@ +namespace BotSharp.OpenAPI.ViewModels.Conversations; + +public class UpdateConversationRequest +{ + public List Tags { get; set; } = []; +} diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/ConversationDocument.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/ConversationDocument.cs index e684e34d..609d637d 100644 --- a/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/ConversationDocument.cs +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/ConversationDocument.cs @@ -9,6 +9,7 @@ public class ConversationDocument : MongoBase public string Channel { get; set; } public string Status { get; set; } public int DialogCount { get; set; } + public List Tags { get; set; } public DateTime CreatedTime { get; set; } public DateTime UpdatedTime { get; set; } } diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/MongoBase.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/MongoBase.cs index 0af038a5..0c3c12b0 100644 --- a/src/Plugins/BotSharp.Plugin.MongoStorage/MongoBase.cs +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/MongoBase.cs @@ -6,5 +6,3 @@ public abstract class MongoBase [BsonId(IdGenerator = typeof(StringGuidIdGenerator))] public string Id { get; set; } } - - diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Conversation.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Conversation.cs index f710a237..ff7cefcd 100644 --- a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Conversation.cs +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Conversation.cs @@ -19,6 +19,7 @@ public partial class MongoRepository Channel = conversation.Channel, TaskId = conversation.TaskId, Status = conversation.Status, + Tags = conversation.Tags ?? new(), CreatedTime = utcNow, UpdatedTime = utcNow }; @@ -108,6 +109,19 @@ public partial class MongoRepository _dc.Conversations.UpdateOne(filterConv, updateConv); } + public bool UpdateConversationTags(string conversationId, List tags) + { + if (string.IsNullOrEmpty(conversationId)) return false; + + var filter = Builders.Filter.Eq(x => x.Id, conversationId); + var update = Builders.Update + .Set(x => x.Tags, tags ?? new()) + .Set(x => x.UpdatedTime, DateTime.UtcNow); + + var res = _dc.Conversations.UpdateOne(filter, update); + return res.ModifiedCount > 0; + } + public bool UpdateConversationMessage(string conversationId, UpdateMessageRequest request) { if (string.IsNullOrEmpty(conversationId)) return false; @@ -254,6 +268,7 @@ public partial class MongoRepository Dialogs = dialogElements, States = curStates, DialogCount = conv.DialogCount, + Tags = conv.Tags, CreatedTime = conv.CreatedTime, UpdatedTime = conv.UpdatedTime }; @@ -297,6 +312,10 @@ public partial class MongoRepository { convFilters.Add(convBuilder.Gte(x => x.CreatedTime, filter.StartTime.Value)); } + if (filter?.Tags != null && filter.Tags.Any()) + { + convFilters.Add(convBuilder.AnyIn(x => x.Tags, filter.Tags)); + } // Filter states var stateFilters = new List>(); @@ -349,6 +368,7 @@ public partial class MongoRepository Channel = x.Channel, Status = x.Status, DialogCount = x.DialogCount, + Tags = x.Tags ?? new(), CreatedTime = x.CreatedTime, UpdatedTime = x.UpdatedTime }).ToList(); @@ -375,6 +395,7 @@ public partial class MongoRepository Channel = c.Channel, Status = c.Status, DialogCount = c.DialogCount, + Tags = c.Tags ?? new(), CreatedTime = c.CreatedTime, UpdatedTime = c.UpdatedTime }).ToList(); From 91250af6ffef6d91c395622d89fb9fc6482c0647 Mon Sep 17 00:00:00 2001 From: Haiping Chen Date: Fri, 18 Oct 2024 18:36:48 -0500 Subject: [PATCH 20/23] PartSearch --- .../Browsing/Models/WebPageResponseData.cs | 5 +++++ .../Browsing/Models/WebPageResponseFilter.cs | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Infrastructure/BotSharp.Abstraction/Browsing/Models/WebPageResponseData.cs b/src/Infrastructure/BotSharp.Abstraction/Browsing/Models/WebPageResponseData.cs index 1d03b0b7..14932118 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Browsing/Models/WebPageResponseData.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Browsing/Models/WebPageResponseData.cs @@ -7,4 +7,9 @@ public class WebPageResponseData public string ResponseData { get; set; } = null!; public bool ResponseInMemory { get; set; } public DateTime CreatedAt { get; set; } = DateTime.UtcNow; + + public override string ToString() + { + return $"{Url} {ResponseData.Length}"; + } } diff --git a/src/Infrastructure/BotSharp.Abstraction/Browsing/Models/WebPageResponseFilter.cs b/src/Infrastructure/BotSharp.Abstraction/Browsing/Models/WebPageResponseFilter.cs index 935d2282..d9561847 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Browsing/Models/WebPageResponseFilter.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Browsing/Models/WebPageResponseFilter.cs @@ -13,5 +13,5 @@ public class WebPageResponseFilter /// /// Handle Content-Type: text/x-component /// - public int PartIndex { get; set; } = -1; + public Func? PartSearch { get; set; } = null; } From bf84d4a87ada08dca81d53ad6b08ed2f7fdcc3f3 Mon Sep 17 00:00:00 2001 From: Haiping Chen Date: Sat, 19 Oct 2024 10:06:24 -0500 Subject: [PATCH 21/23] UserAuthenticated --- .../BotSharp.Abstraction.csproj | 1 + .../Users/IAuthenticationHook.cs | 49 ++++++++++++++++++- .../Users/Services/UserService.cs | 2 +- 3 files changed, 49 insertions(+), 3 deletions(-) diff --git a/src/Infrastructure/BotSharp.Abstraction/BotSharp.Abstraction.csproj b/src/Infrastructure/BotSharp.Abstraction/BotSharp.Abstraction.csproj index e94cf839..474dcf56 100644 --- a/src/Infrastructure/BotSharp.Abstraction/BotSharp.Abstraction.csproj +++ b/src/Infrastructure/BotSharp.Abstraction/BotSharp.Abstraction.csproj @@ -32,6 +32,7 @@ + diff --git a/src/Infrastructure/BotSharp.Abstraction/Users/IAuthenticationHook.cs b/src/Infrastructure/BotSharp.Abstraction/Users/IAuthenticationHook.cs index afb0e40e..36b07f11 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Users/IAuthenticationHook.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Users/IAuthenticationHook.cs @@ -1,15 +1,60 @@ using BotSharp.Abstraction.Users.Models; +using System.IdentityModel.Tokens.Jwt; using System.Security.Claims; namespace BotSharp.Abstraction.Users; public interface IAuthenticationHook { + /// + /// Interupt the authentication process, and return the user object if the user is authenticated + /// + /// + /// + /// Task Authenticate(string id, string password); - void AddClaims(List claims); - void BeforeSending(Token token); + + /// + /// Add extra claims to user + /// + /// + /// + bool AddClaims(List claims) + => true; + + /// + /// User authenticated successfully + /// + /// + /// + bool UserAuthenticated(JwtSecurityToken token) + => true; + + /// + /// Bfore user updating + /// + /// + /// Task UserUpdating(User user); + + /// + /// After user created + /// + /// + /// Task UserCreated(User user); + + /// + /// Reset password + /// + /// + /// Task VerificationCodeResetPassword(User user); + + /// + /// Delete users + /// + /// + /// Task DelUsers(List userIds); } diff --git a/src/Infrastructure/BotSharp.Core/Users/Services/UserService.cs b/src/Infrastructure/BotSharp.Core/Users/Services/UserService.cs index 10dbe8de..5d08c51f 100644 --- a/src/Infrastructure/BotSharp.Core/Users/Services/UserService.cs +++ b/src/Infrastructure/BotSharp.Core/Users/Services/UserService.cs @@ -275,7 +275,7 @@ public class UserService : IUserService foreach (var hook in hooks) { - hook.BeforeSending(token); + hook.UserAuthenticated(jwt); } return token; From 780718ef978c9495ce420a403d42ccccc974fa93 Mon Sep 17 00:00:00 2001 From: Haiping Chen Date: Sat, 19 Oct 2024 12:39:42 -0500 Subject: [PATCH 22/23] ScrollPage --- .../PlaywrightWebDriver.ScrollPage.cs | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.ScrollPage.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.ScrollPage.cs index c50bf48b..48538ce8 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.ScrollPage.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.ScrollPage.cs @@ -12,12 +12,20 @@ public partial class PlaywrightWebDriver if (args.Direction == "down") { // Get the total page height - int scrollY = await page.EvaluateAsync("document.body.scrollHeight"); + int scrollY = await page.EvaluateAsync("window.screen.height"); - // Scroll to the bottom + // Scroll a page down await page.Mouse.WheelAsync(0, scrollY); } else if (args.Direction == "up") + { + // Get the total page height + int scrollY = await page.EvaluateAsync("window.screen.height"); + + // Scroll a page up + await page.Mouse.WheelAsync(0, -scrollY); + } + else if (args.Direction == "bottom") { // Get the total page height int scrollY = await page.EvaluateAsync("document.body.scrollHeight"); @@ -25,6 +33,14 @@ public partial class PlaywrightWebDriver // Scroll to the bottom await page.Mouse.WheelAsync(0, -scrollY); } + else if (args.Direction == "top") + { + // Get the total page height + int scrollY = await page.EvaluateAsync("document.body.scrollHeight"); + + // Scroll to the top + await page.Mouse.WheelAsync(0, -scrollY); + } else if (args.Direction == "left") { await page.EvaluateAsync(@" From 78f5cd1751ecea360fb4ae8bdbc2c6982d569d8d Mon Sep 17 00:00:00 2001 From: Haiping Chen Date: Sun, 20 Oct 2024 15:24:21 +0000 Subject: [PATCH 23/23] category --- .../Browsing/Models/ElementLocatingArgs.cs | 3 +++ .../Browsing/Models/WebPageResponseFilter.cs | 3 +++ .../PlaywrightWebDriver.LocateElement.cs | 10 +++++++++- 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/Infrastructure/BotSharp.Abstraction/Browsing/Models/ElementLocatingArgs.cs b/src/Infrastructure/BotSharp.Abstraction/Browsing/Models/ElementLocatingArgs.cs index 53d72520..b50f2366 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Browsing/Models/ElementLocatingArgs.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Browsing/Models/ElementLocatingArgs.cs @@ -1,5 +1,8 @@ +using System.Diagnostics; + namespace BotSharp.Abstraction.Browsing.Models; +[DebuggerStepThrough] public class ElementLocatingArgs { [JsonPropertyName("match_rule")] diff --git a/src/Infrastructure/BotSharp.Abstraction/Browsing/Models/WebPageResponseFilter.cs b/src/Infrastructure/BotSharp.Abstraction/Browsing/Models/WebPageResponseFilter.cs index d9561847..54951115 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Browsing/Models/WebPageResponseFilter.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Browsing/Models/WebPageResponseFilter.cs @@ -1,5 +1,8 @@ +using System.Diagnostics; + namespace BotSharp.Abstraction.Browsing.Models; +[DebuggerStepThrough] public class WebPageResponseFilter { public string Url { get; set; } = null!; diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.LocateElement.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.LocateElement.cs index a39da9c0..ff43864e 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.LocateElement.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.LocateElement.cs @@ -128,7 +128,15 @@ public partial class PlaywrightWebDriver } else { - result.Selector = locator.ToString().Split("Locator@").Last(); + foreach (var element in await locator.AllAsync()) + { + var html = await element.InnerHTMLAsync(); + _logger.LogWarning(html); + // fix if html has & + result.Body = HttpUtility.HtmlDecode(html); + break; + } + result.IsSuccess = true; } }