From 23d0d5a763110ea1fc0e9129018795da6d33feba Mon Sep 17 00:00:00 2001 From: YouWeiDH Date: Sun, 6 Oct 2024 17:49:34 +0800 Subject: [PATCH 1/8] hdong: Add send verify code API with none login and login when reset password. --- .../Users/IUserService.cs | 3 +- .../Users/Services/UserService.cs | 56 +++++++++++++------ .../Controllers/UserController.cs | 12 +++- 3 files changed, 52 insertions(+), 19 deletions(-) diff --git a/src/Infrastructure/BotSharp.Abstraction/Users/IUserService.cs b/src/Infrastructure/BotSharp.Abstraction/Users/IUserService.cs index 750858a78..5a3315b77 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Users/IUserService.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Users/IUserService.cs @@ -13,7 +13,8 @@ public interface IUserService Task GetMyProfile(); Task VerifyUserNameExisting(string userName); Task VerifyEmailExisting(string email); - Task SendVerificationCodeResetPassword(User user); + Task SendVerificationCodeResetPasswordNoLogin(User user); + Task SendVerificationCodeResetPasswordLogin(); Task ResetUserPassword(User user); Task ModifyUserEmail(string email); Task ModifyUserPhone(string phone); diff --git a/src/Infrastructure/BotSharp.Core/Users/Services/UserService.cs b/src/Infrastructure/BotSharp.Core/Users/Services/UserService.cs index c0cf95888..bc6fc317a 100644 --- a/src/Infrastructure/BotSharp.Core/Users/Services/UserService.cs +++ b/src/Infrastructure/BotSharp.Core/Users/Services/UserService.cs @@ -412,32 +412,56 @@ public async Task VerifyEmailExisting(string email) return false; } - public async Task SendVerificationCodeResetPassword(User user) + public async Task SendVerificationCodeResetPasswordNoLogin(User user) { var db = _services.GetRequiredService(); User? record = null; - if (!string.IsNullOrWhiteSpace(_user.Id)) + if (!string.IsNullOrEmpty(user.Email) && !string.IsNullOrEmpty(user.Phone)) { - record = db.GetUserById(_user.Id); + return false; } - else + + if (!string.IsNullOrEmpty(user.Phone)) { - if (!string.IsNullOrEmpty(user.Email) && !string.IsNullOrEmpty(user.Phone)) - { - return false; - } + record = db.GetUserByPhone(user.Phone); + } - if (!string.IsNullOrEmpty(user.Email)) - { - record = db.GetUserByEmail(user.Email); - } + if (!string.IsNullOrEmpty(user.Email)) + { + record = db.GetUserByEmail(user.Email); + } - if (!string.IsNullOrEmpty(user.Phone)) - { - record = db.GetUserByPhone(user.Phone); - } + if (record == null) + { + return false; + } + + record.VerificationCode = Nanoid.Generate(alphabet: "0123456789", size: 6); + + //update current verification code. + db.UpdateUserVerificationCode(record.Id, record.VerificationCode); + + //send code to user Email. + var hooks = _services.GetServices(); + foreach (var hook in hooks) + { + hook.VerificationCodeResetPassword(record); + } + + return true; + } + + public async Task SendVerificationCodeResetPasswordLogin() + { + var db = _services.GetRequiredService(); + + User? record = null; + + if (!string.IsNullOrWhiteSpace(_user.Id)) + { + record = db.GetUserById(_user.Id); } if (record == null) diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/UserController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/UserController.cs index 281de3e85..bf15a33b5 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/UserController.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/UserController.cs @@ -108,12 +108,20 @@ public async Task VerifyEmailExisting([FromQuery] string email) { return await _userService.VerifyEmailExisting(email); } + [AllowAnonymous] - [HttpPost("/user/verifycode")] + [HttpPost("/user/verifycode-out")] public async Task SendVerificationCodeResetPassword([FromBody] UserCreationModel user) { - return await _userService.SendVerificationCodeResetPassword(user.ToUser()); + return await _userService.SendVerificationCodeResetPasswordNoLogin(user.ToUser()); } + + [HttpPost("/user/verifycode-in")] + public async Task SendVerificationCodeResetPasswordLogined() + { + return await _userService.SendVerificationCodeResetPasswordLogin(); + } + [AllowAnonymous] [HttpPost("/user/resetpassword")] public async Task ResetUserPassword([FromBody] UserResetPasswordModel user) From 468f84981c202511e63ee595b1803bd99dea73ef Mon Sep 17 00:00:00 2001 From: Haiping Chen Date: Mon, 7 Oct 2024 20:20:48 -0500 Subject: [PATCH 2/8] ReadInnerHTMLAsBody option --- .../Browsing/Models/PageActionArgs.cs | 2 ++ .../PlaywrightDriver/PlaywrightWebDriver.GoToPage.cs | 8 ++++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/Infrastructure/BotSharp.Abstraction/Browsing/Models/PageActionArgs.cs b/src/Infrastructure/BotSharp.Abstraction/Browsing/Models/PageActionArgs.cs index d260a09d0..aa4154ffd 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Browsing/Models/PageActionArgs.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Browsing/Models/PageActionArgs.cs @@ -43,4 +43,6 @@ public class PageActionArgs /// Wait time in seconds after page is opened /// public int WaitTime { get; set; } + + public bool ReadInnerHTMLAsBody { get; set; } = false; } diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.GoToPage.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.GoToPage.cs index cc1a00094..108ff4332 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.GoToPage.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.GoToPage.cs @@ -67,9 +67,13 @@ await _instance.NewPage(message, enableResponseCallback: args.EnableResponseCall result.ResponseStatusCode = response.Status; if (response.Status == 200) { - // Disable this due to performance issue, some page is too large - // result.Body = await page.InnerHTMLAsync("body"); result.IsSuccess = true; + + // Be careful if page is too large, it will cause performance issue + if (args.ReadInnerHTMLAsBody) + { + result.Body = await page.InnerHTMLAsync("body"); + } } else { From f919ad7c426295981e1d77dc52df2206687a9a18 Mon Sep 17 00:00:00 2001 From: "jason.wang" Date: Tue, 8 Oct 2024 20:59:55 +0800 Subject: [PATCH 3/8] add root role --- src/Infrastructure/BotSharp.Abstraction/Users/Enums/UserRole.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Infrastructure/BotSharp.Abstraction/Users/Enums/UserRole.cs b/src/Infrastructure/BotSharp.Abstraction/Users/Enums/UserRole.cs index 22a8eb956..cddabe10e 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Users/Enums/UserRole.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Users/Enums/UserRole.cs @@ -33,4 +33,6 @@ public class UserRole /// AI Assistant /// public const string Assistant = "assistant"; + + public const string Root = "root"; } From edc0df0f46ec35e6dd648f0af8737c70a6745778 Mon Sep 17 00:00:00 2001 From: Haiping Chen Date: Tue, 8 Oct 2024 19:22:00 -0500 Subject: [PATCH 4/8] translate long text --- .../Models/TranslationRequestModel.cs | 14 +++- .../Controllers/TranslationController.cs | 74 ++++++++++++++++++- 2 files changed, 84 insertions(+), 4 deletions(-) diff --git a/src/Infrastructure/BotSharp.Abstraction/Translation/Models/TranslationRequestModel.cs b/src/Infrastructure/BotSharp.Abstraction/Translation/Models/TranslationRequestModel.cs index 588dedfcf..d3d6a453f 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Translation/Models/TranslationRequestModel.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Translation/Models/TranslationRequestModel.cs @@ -1,5 +1,3 @@ -using BotSharp.Abstraction.Infrastructures.Enums; - namespace BotSharp.OpenAPI.ViewModels.Translations; public class TranslationRequestModel @@ -7,3 +5,15 @@ public class TranslationRequestModel public string Text { get; set; } = null!; public string ToLang { get; set; } = LanguageType.CHINESE; } + +public class TranslationScriptTimestamp +{ + public string Text { set; get; } = null!; + public string Timestamp { get; set; } = null!; +} + +public class TranslationLongTextRequestModel +{ + public TranslationScriptTimestamp[] Texts { get; set; } = null!; + public string ToLang { get; set; } = LanguageType.CHINESE; +} \ No newline at end of file diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/TranslationController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/TranslationController.cs index ac7686a1c..17e9ff535 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/TranslationController.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/TranslationController.cs @@ -1,4 +1,4 @@ -using BotSharp.Abstraction.Agents.Models; +using BotSharp.Abstraction.Options; using BotSharp.Abstraction.Translation; using BotSharp.OpenAPI.ViewModels.Translations; @@ -9,10 +9,13 @@ namespace BotSharp.OpenAPI.Controllers; public class TranslationController : ControllerBase { private readonly IServiceProvider _services; + private readonly JsonSerializerOptions _jsonOptions; - public TranslationController(IServiceProvider services) + public TranslationController(IServiceProvider services, + BotSharpOptions options) { _services = services; + _jsonOptions = InitJsonOptions(options); } [HttpPost("/translate")] @@ -27,4 +30,71 @@ public async Task Translate([FromBody] TranslationRequ Text = text }; } + + [HttpPost("/translate/long-text")] + public async Task SendMessageSse([FromBody] TranslationLongTextRequestModel model) + { + var agentService = _services.GetRequiredService(); + var agent = await agentService.LoadAgent(BuiltInAgentId.AIAssistant); + var translator = _services.GetRequiredService(); + + Response.StatusCode = 200; + Response.Headers.Append(Microsoft.Net.Http.Headers.HeaderNames.ContentType, "text/event-stream"); + Response.Headers.Append(Microsoft.Net.Http.Headers.HeaderNames.CacheControl, "no-cache"); + Response.Headers.Append(Microsoft.Net.Http.Headers.HeaderNames.Connection, "keep-alive"); + + foreach (var script in model.Texts) + { + var translatedText = await translator.Translate(agent, Guid.NewGuid().ToString(), script.Text, language: model.ToLang); + + var json = JsonSerializer.Serialize(new TranslationScriptTimestamp + { + Text = translatedText, + Timestamp = script.Timestamp + }, _jsonOptions); + + await OnChunkReceived(Response, json); + } + + await OnEventCompleted(Response); + } + + private async Task OnChunkReceived(HttpResponse response, string text) + { + var buffer = Encoding.UTF8.GetBytes($"data:{text}\n"); + await response.Body.WriteAsync(buffer, 0, buffer.Length); + await Task.Delay(10); + + buffer = Encoding.UTF8.GetBytes("\n"); + await response.Body.WriteAsync(buffer, 0, buffer.Length); + } + + private async Task OnEventCompleted(HttpResponse response) + { + var buffer = Encoding.UTF8.GetBytes("data:[DONE]\n"); + await response.Body.WriteAsync(buffer, 0, buffer.Length); + + buffer = Encoding.UTF8.GetBytes("\n"); + await response.Body.WriteAsync(buffer, 0, buffer.Length); + } + + private JsonSerializerOptions InitJsonOptions(BotSharpOptions options) + { + var jsonOption = new JsonSerializerOptions + { + PropertyNameCaseInsensitive = true, + PropertyNamingPolicy = JsonNamingPolicy.CamelCase, + AllowTrailingCommas = true + }; + + if (options?.JsonSerializerOptions != null) + { + foreach (var option in options.JsonSerializerOptions.Converters) + { + jsonOption.Converters.Add(option); + } + } + + return jsonOption; + } } From 730cc8b143cf8260acde461a4d4ebb080309c289 Mon Sep 17 00:00:00 2001 From: Haiping Chen Date: Thu, 10 Oct 2024 19:32:22 -0500 Subject: [PATCH 5/8] Translate --- .../Translation/TranslationService.cs | 10 +++++++--- .../templates/translation_prompt.liquid | 17 +++++++++++++++-- .../Controllers/TranslationController.cs | 6 ++++-- 3 files changed, 26 insertions(+), 7 deletions(-) diff --git a/src/Infrastructure/BotSharp.Core/Translation/TranslationService.cs b/src/Infrastructure/BotSharp.Core/Translation/TranslationService.cs index f9aa73e3b..453b22774 100644 --- a/src/Infrastructure/BotSharp.Core/Translation/TranslationService.cs +++ b/src/Infrastructure/BotSharp.Core/Translation/TranslationService.cs @@ -101,12 +101,12 @@ public async Task Translate(Agent router, string messageId, T data, string { var translatedStringList = await InnerTranslate(texts, language, template); - int retry = 0; + /*int retry = 0; while (translatedStringList.Texts.Length != texts.Count && retry < 3) { translatedStringList = await InnerTranslate(texts, language, template); retry++; - } + }*/ // Override language if it's Unknown, it's used to output the corresponding language. var states = _services.GetRequiredService(); @@ -119,7 +119,7 @@ public async Task Translate(Agent router, string messageId, T data, string var translatedTexts = translatedStringList.Texts; var memoryInputs = new List(); - for (var i = 0; i < texts.Count; i++) + for (var i = 0; i < Math.Min(texts.Count, translatedTexts.Length); i++) { map[outOfMemoryList[i].OriginalText] = translatedTexts[i].Text; memoryInputs.Add(new TranslationMemoryInput @@ -375,6 +375,8 @@ private async Task InnerTranslate(List text var render = _services.GetRequiredService(); var prompt = render.Render(template, translator.TemplateDict); + _logger.LogInformation($"Translation prompt: {prompt}"); + var translationDialogs = new List { new RoleDialogModel(AgentRole.User, prompt) @@ -384,6 +386,8 @@ private async Task InnerTranslate(List text } }; var response = await _completion.GetChatCompletions(translator, translationDialogs); + + _logger.LogInformation(response.Content); return response.Content.JsonContent(); } diff --git a/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/translation_prompt.liquid b/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/translation_prompt.liquid index 6b3a8677e..fd67a5fd1 100644 --- a/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/translation_prompt.liquid +++ b/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/translation_prompt.liquid @@ -1,6 +1,19 @@ {{ text_list }} ===== +{% if language == "Chinese" %} +将以上所有句子翻译成中文。 + +要求: +* 以 JSON 格式输出翻译后的文本 {"input_lang":"原始文本语言", "output_count": {{ text_list_size }}, "output_lang":"{{ language }}", "texts":[{"id": 1, "text":""},{"id": 2, "text":""}]}。 +* output_count 必须等于输出中texts数组的长度。 +{% else %} Translate all the above sentences into {{ language }}. -Output the translated text in JSON {"input_lang":"original text language", "output_count": {{ text_list_size }}, "output_lang":"{{ language }}", "texts":[{"id": 1, "text":""},{"id": 2, "text":""}]}. -The "output_count" must equal the length of the "texts" array in the output. + +Requirements: +* Output the translated text in JSON {"input_lang":"original text language", "output_count": {{ text_list_size }}, "output_lang":"{{ language }}", "texts":[{"id": 1, "text":""},{"id": 2, "text":""}]}. +* The "output_count" must equal the length of the "texts" array in the output. +{% endif %} + + + diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/TranslationController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/TranslationController.cs index 17e9ff535..7845e8cf6 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/TranslationController.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/TranslationController.cs @@ -24,10 +24,12 @@ public async Task Translate([FromBody] TranslationRequ var agentService = _services.GetRequiredService(); var agent = await agentService.LoadAgent(BuiltInAgentId.AIAssistant); var translator = _services.GetRequiredService(); - var text = await translator.Translate(agent, Guid.NewGuid().ToString(), model.Text, language: model.ToLang); + var states = _services.GetRequiredService(); + states.SetState("max_tokens", "8192"); + var text = await translator.Translate(agent, Guid.NewGuid().ToString(), model.Text.Split("\r\n"), language: model.ToLang); return new TranslationResponseModel { - Text = text + Text = string.Join("\r\n", text) }; } From 836fa0e7233746a6b9b3b26c856694c1cb4e0ef7 Mon Sep 17 00:00:00 2001 From: AnonymousDotNet <18776095145@163.com> Date: Fri, 11 Oct 2024 14:31:54 +0800 Subject: [PATCH 6/8] fix: Error caused by not including prefix when modifying user's mobile number. --- .../BotSharp.Core/Users/Services/UserService.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Infrastructure/BotSharp.Core/Users/Services/UserService.cs b/src/Infrastructure/BotSharp.Core/Users/Services/UserService.cs index c0cf95888..dcfaa084b 100644 --- a/src/Infrastructure/BotSharp.Core/Users/Services/UserService.cs +++ b/src/Infrastructure/BotSharp.Core/Users/Services/UserService.cs @@ -520,6 +520,11 @@ public async Task ModifyUserPhone(string phone) return false; } + if ((record.UserName.Substring(0, 3) == "+86" || record.FirstName.Substring(0, 3) == "+86") && phone.Substring(0, 3) != "+86") + { + phone = $"+86{phone}"; + } + db.UpdateUserPhone(record.Id, phone); return true; } From 878618c9267b045d3273267f1a79cdc63bf47018 Mon Sep 17 00:00:00 2001 From: YouWeiDH Date: Fri, 11 Oct 2024 19:58:54 +0800 Subject: [PATCH 7/8] hdong: When use verify fail, they need to re-register again. --- .../Repositories/IBotSharpRepository.cs | 1 + .../BotSharp.Core/Users/Services/UserService.cs | 17 +++++++++++++---- .../Repository/MongoRepository.User.cs | 12 ++++++++++++ 3 files changed, 26 insertions(+), 4 deletions(-) diff --git a/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs b/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs index de3516499..62888accc 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs @@ -27,6 +27,7 @@ public interface IBotSharpRepository User? GetUserByAffiliateId(string affiliateId) => throw new NotImplementedException(); User? GetUserByUserName(string userName) => throw new NotImplementedException(); void CreateUser(User user) => throw new NotImplementedException(); + void UpdateExistUser(string userId, User user) => throw new NotImplementedException(); void UpdateUserVerified(string userId) => throw new NotImplementedException(); void UpdateUserVerificationCode(string userId, string verficationCode) => throw new NotImplementedException(); void UpdateUserPassword(string userId, string password) => throw new NotImplementedException(); diff --git a/src/Infrastructure/BotSharp.Core/Users/Services/UserService.cs b/src/Infrastructure/BotSharp.Core/Users/Services/UserService.cs index bc6fc317a..5adfd668b 100644 --- a/src/Infrastructure/BotSharp.Core/Users/Services/UserService.cs +++ b/src/Infrastructure/BotSharp.Core/Users/Services/UserService.cs @@ -32,6 +32,7 @@ public UserService(IServiceProvider services, public async Task CreateUser(User user) { + string hasRegisterId = null; if (string.IsNullOrEmpty(user.UserName)) { // generate unique name @@ -48,7 +49,7 @@ public async Task CreateUser(User user) if (record != null) { - return record; + hasRegisterId = record.Id; } if (string.IsNullOrEmpty(user.Id)) @@ -71,7 +72,14 @@ record = user; record.Verified = false; } - db.CreateUser(record); + if (hasRegisterId == null) + { + db.CreateUser(record); + } + else + { + db.UpdateExistUser(hasRegisterId, record); + } _logger.LogWarning($"Created new user account: {record.Id} {record.UserName}"); Utilities.ClearCache(); @@ -386,8 +394,9 @@ public async Task VerifyUserNameExisting(string userName) } var db = _services.GetRequiredService(); + var user = db.GetUserByUserName(userName); - if (user != null) + if (user != null && user.Verified) { return true; } @@ -404,7 +413,7 @@ public async Task VerifyEmailExisting(string email) var db = _services.GetRequiredService(); var emailName = db.GetUserByEmail(email); - if (emailName != null) + if (emailName != null && emailName.Verified) { return true; } diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.User.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.User.cs index 9b6b62c1a..db40705db 100644 --- a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.User.cs +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.User.cs @@ -79,6 +79,18 @@ public void CreateUser(User user) _dc.Users.InsertOne(userCollection); } + public void UpdateExistUser(string userId, User user) + { + var filter = Builders.Filter.Eq(x => x.Id, userId); + var update = Builders.Update + .Set(x => x.Email, user.Email) + .Set(x => x.Phone, user.Phone) + .Set(x => x.Salt, user.Salt) + .Set(x => x.Password, user.Password) + .Set(x => x.VerificationCode, user.VerificationCode); + _dc.Users.UpdateOne(filter, update); + } + public void UpdateUserVerified(string userId) { var filter = Builders.Filter.Eq(x => x.Id, userId); From 30591f40d36f637551782382a4401286c52fd105 Mon Sep 17 00:00:00 2001 From: Haiping Chen Date: Fri, 11 Oct 2024 20:18:45 -0500 Subject: [PATCH 8/8] optimize sql driver --- .../Functions/SecondaryStagePlanFn.cs | 15 ++- .../Functions/SummaryPlanFn.cs | 2 - .../Hooks/PlannerAgentHook.cs | 21 ++++- .../instructions/instruction.liquid | 3 + .../templates/two_stage.2nd.plan.liquid | 1 - .../BotSharp.Plugin.SqlDriver.csproj | 4 + .../Functions/ExecuteQueryFn.cs | 93 ++++++++++++++++--- .../Hooks/SqlDriverPlanningHook.cs | 11 ++- .../Models/ExecuteQueryArgs.cs | 4 + .../Settings/SqlDriverSetting.cs | 1 + .../agent.json | 2 +- .../functions/execute_sql.json | 16 +++- .../templates/query_result_formatting.liquid | 6 +- .../sql_statement_correctness.liquid | 11 +++ 14 files changed, 163 insertions(+), 27 deletions(-) create mode 100644 src/Plugins/BotSharp.Plugin.SqlDriver/data/agents/beda4c12-e1ec-4b4b-b328-3df4a6687c4f/templates/sql_statement_correctness.liquid diff --git a/src/Plugins/BotSharp.Plugin.Planner/Functions/SecondaryStagePlanFn.cs b/src/Plugins/BotSharp.Plugin.Planner/Functions/SecondaryStagePlanFn.cs index 918b33e59..e228d6075 100644 --- a/src/Plugins/BotSharp.Plugin.Planner/Functions/SecondaryStagePlanFn.cs +++ b/src/Plugins/BotSharp.Plugin.Planner/Functions/SecondaryStagePlanFn.cs @@ -1,4 +1,5 @@ using BotSharp.Plugin.Planner.TwoStaging.Models; +using System.Threading.Tasks; namespace BotSharp.Plugin.Planner.Functions; @@ -27,14 +28,18 @@ public async Task Execute(RoleDialogModel message) var planPrimary = states.GetState("planning_result"); var taskSecondary = JsonSerializer.Deserialize(msgSecondary.FunctionArgs); - + // Search knowledgebase - var knowledges = await knowledgeService.SearchVectorKnowledge(taskSecondary.SolutionQuestion, collectionName, new VectorSearchOptions + var hooks = _services.GetServices(); + var knowledges = new List(); + foreach (var hook in hooks) { - Confidence = 0.7f - }); + var k = await hook.GetRelevantKnowledges(message, taskSecondary.SolutionQuestion); + knowledges.AddRange(k); + } + knowledges = knowledges.Distinct().ToList(); - var knowledgeResults = string.Join("\r\n\r\n=====\r\n", knowledges.Select(x => x.ToQuestionAnswer())); + var knowledgeResults = string.Join("\r\n\r\n=====\r\n", knowledges); // Get second stage planning prompt var currentAgent = await agentService.LoadAgent(message.CurrentAgentId); diff --git a/src/Plugins/BotSharp.Plugin.Planner/Functions/SummaryPlanFn.cs b/src/Plugins/BotSharp.Plugin.Planner/Functions/SummaryPlanFn.cs index 22ddc3c32..e9fd3b378 100644 --- a/src/Plugins/BotSharp.Plugin.Planner/Functions/SummaryPlanFn.cs +++ b/src/Plugins/BotSharp.Plugin.Planner/Functions/SummaryPlanFn.cs @@ -1,8 +1,6 @@ -using BotSharp.Abstraction.Knowledges; using BotSharp.Abstraction.Planning; using BotSharp.Plugin.Planner.TwoStaging; using BotSharp.Plugin.Planner.TwoStaging.Models; -using Microsoft.EntityFrameworkCore.Metadata.Internal; namespace BotSharp.Plugin.Planner.Functions; diff --git a/src/Plugins/BotSharp.Plugin.Planner/Hooks/PlannerAgentHook.cs b/src/Plugins/BotSharp.Plugin.Planner/Hooks/PlannerAgentHook.cs index 1a85e1c0a..503de3d53 100644 --- a/src/Plugins/BotSharp.Plugin.Planner/Hooks/PlannerAgentHook.cs +++ b/src/Plugins/BotSharp.Plugin.Planner/Hooks/PlannerAgentHook.cs @@ -2,13 +2,32 @@ namespace BotSharp.Plugin.Planner.Hooks; public class PlannerAgentHook : AgentHookBase { - public override string SelfId => string.Empty; + public override string SelfId => BuiltInAgentId.Planner; public PlannerAgentHook(IServiceProvider services, AgentSettings settings) : base(services, settings) { } + public override bool OnInstructionLoaded(string template, Dictionary dict) + { + var knowledgeHooks = _services.GetServices(); + + // Get global knowledges + var Knowledges = new List(); + foreach (var hook in knowledgeHooks) + { + var k = hook.GetGlobalKnowledges(new RoleDialogModel(AgentRole.User, template) + { + CurrentAgentId = BuiltInAgentId.Planner + }).Result; + Knowledges.AddRange(k); + } + dict["global_knowledges"] = Knowledges; + + return true; + } + public override void OnAgentLoaded(Agent agent) { var conv = _services.GetRequiredService(); diff --git a/src/Plugins/BotSharp.Plugin.Planner/data/agents/282a7128-69a1-44b0-878c-a9159b88f3b9/instructions/instruction.liquid b/src/Plugins/BotSharp.Plugin.Planner/data/agents/282a7128-69a1-44b0-878c-a9159b88f3b9/instructions/instruction.liquid index 5dda81e3a..d15329a89 100644 --- a/src/Plugins/BotSharp.Plugin.Planner/data/agents/282a7128-69a1-44b0-878c-a9159b88f3b9/instructions/instruction.liquid +++ b/src/Plugins/BotSharp.Plugin.Planner/data/agents/282a7128-69a1-44b0-878c-a9159b88f3b9/instructions/instruction.liquid @@ -1,4 +1,6 @@ +The user is dealing with a complex problem, and you need to break this complex problem into several small tasks to more easily solve the user's needs. Use the TwoStagePlanner approach to plan the overall implementation steps, follow the below steps strictly. + 1. Call plan_primary_stage to generate the primary plan. If you've already got the plan to meet the user goal, directly go to step 5. 2. If need_lookup_dictionary is True, call verify_dictionary_term to verify or get the enum/term/dictionary value. Pull id and name. @@ -15,6 +17,7 @@ Don't run the planning process repeatedly if you have already got the result of {% if global_knowledges != empty -%} ===== Global Knowledge: +Current date time is: {{ "now" | date: "%Y-%m-%d %H:%M" }} {% for k in global_knowledges %} {{ k }} {% endfor %} diff --git a/src/Plugins/BotSharp.Plugin.Planner/data/agents/282a7128-69a1-44b0-878c-a9159b88f3b9/templates/two_stage.2nd.plan.liquid b/src/Plugins/BotSharp.Plugin.Planner/data/agents/282a7128-69a1-44b0-878c-a9159b88f3b9/templates/two_stage.2nd.plan.liquid index 113767aed..81e22fe8b 100644 --- a/src/Plugins/BotSharp.Plugin.Planner/data/agents/282a7128-69a1-44b0-878c-a9159b88f3b9/templates/two_stage.2nd.plan.liquid +++ b/src/Plugins/BotSharp.Plugin.Planner/data/agents/282a7128-69a1-44b0-878c-a9159b88f3b9/templates/two_stage.2nd.plan.liquid @@ -6,7 +6,6 @@ Reference to "Primary Planning" and the additional knowledge included. Breakdown * If need_lookup_dictionary is true, call verify_dictionary_term to verify or get the enum/term/dictionary value. Pull id and name/code. * Output all the steps as much detail as possible in JSON: [{{ response_format }}] - Additional Requirements: * "output_results" is variable name that needed to be used in the next step. diff --git a/src/Plugins/BotSharp.Plugin.SqlDriver/BotSharp.Plugin.SqlDriver.csproj b/src/Plugins/BotSharp.Plugin.SqlDriver/BotSharp.Plugin.SqlDriver.csproj index 4f93597a9..d4d0a2439 100644 --- a/src/Plugins/BotSharp.Plugin.SqlDriver/BotSharp.Plugin.SqlDriver.csproj +++ b/src/Plugins/BotSharp.Plugin.SqlDriver/BotSharp.Plugin.SqlDriver.csproj @@ -30,6 +30,7 @@ + @@ -69,6 +70,9 @@ PreserveNewest + + PreserveNewest + PreserveNewest diff --git a/src/Plugins/BotSharp.Plugin.SqlDriver/Functions/ExecuteQueryFn.cs b/src/Plugins/BotSharp.Plugin.SqlDriver/Functions/ExecuteQueryFn.cs index 78ab85414..e3d8e57e0 100644 --- a/src/Plugins/BotSharp.Plugin.SqlDriver/Functions/ExecuteQueryFn.cs +++ b/src/Plugins/BotSharp.Plugin.SqlDriver/Functions/ExecuteQueryFn.cs @@ -1,8 +1,10 @@ using BotSharp.Abstraction.Agents.Enums; +using BotSharp.Abstraction.Routing; using BotSharp.Core.Infrastructures; using BotSharp.Plugin.SqlDriver.Models; using Dapper; using Microsoft.Data.SqlClient; +using Microsoft.Extensions.Logging; using MySqlConnector; namespace BotSharp.Plugin.SqlDriver.Functions; @@ -13,32 +15,48 @@ public class ExecuteQueryFn : IFunctionCallback public string Indication => "Performing data retrieval operation."; private readonly SqlDriverSetting _setting; private readonly IServiceProvider _services; + private readonly ILogger _logger; - public ExecuteQueryFn(IServiceProvider services, SqlDriverSetting setting) + public ExecuteQueryFn(IServiceProvider services, SqlDriverSetting setting, ILogger logger) { _services = services; _setting = setting; + _logger = logger; } public async Task Execute(RoleDialogModel message) { var args = JsonSerializer.Deserialize(message.FunctionArgs); + + var refinedArgs = await RefineSqlStatement(message, args); + var settings = _services.GetRequiredService(); - var results = settings.DatabaseType switch + + try { - "MySql" => RunQueryInMySql(args.SqlStatements), - "SqlServer" => RunQueryInSqlServer(args.SqlStatements), - _ => throw new NotImplementedException($"Database type {settings.DatabaseType} is not supported.") - }; - - if (results.Count() == 0) + var results = settings.DatabaseType switch + { + "MySql" => RunQueryInMySql(refinedArgs.SqlStatements), + "SqlServer" => RunQueryInSqlServer(refinedArgs.SqlStatements), + _ => throw new NotImplementedException($"Database type {settings.DatabaseType} is not supported.") + }; + + if (results.Count() == 0) + { + message.Content = "No record found"; + return true; + } + + message.Content = JsonSerializer.Serialize(results); + } + catch (Exception ex) { - message.Content = "No record found"; - return true; + _logger.LogError(ex, "Error occurred while executing SQL query."); + message.Content = "Error occurred while retrieving information."; + message.StopCompletion = true; + return false; } - message.Content = JsonSerializer.Serialize(results); - if (args.FormattingResult) { var conv = _services.GetRequiredService(); @@ -59,6 +77,7 @@ public async Task Execute(RoleDialogModel message) }); message.Content = result.Content; + message.StopCompletion = true; } return true; @@ -77,4 +96,54 @@ private IEnumerable RunQueryInSqlServer(string[] sqlTexts) using var connection = new SqlConnection(settings.SqlServerExecutionConnectionString ?? settings.SqlServerConnectionString); return connection.Query(string.Join("\r\n", sqlTexts)); } + + private async Task RefineSqlStatement(RoleDialogModel message, ExecuteQueryArgs args) + { + // get table DDL + var fn = _services.GetRequiredService(); + var msg = RoleDialogModel.From(message); + await fn.InvokeFunction("sql_table_definition", msg); + + // refine SQL + var agentService = _services.GetRequiredService(); + var currentAgent = await agentService.LoadAgent(message.CurrentAgentId); + var dictionarySqlPrompt = await GetDictionarySQLPrompt(string.Join("\r\n\r\n", args.SqlStatements), msg.Content); + var agent = new Agent + { + Id = message.CurrentAgentId ?? string.Empty, + Name = "sqlDriver_ExecuteQuery", + Instruction = dictionarySqlPrompt, + TemplateDict = new Dictionary(), + LlmConfig = currentAgent.LlmConfig + }; + + var completion = CompletionProvider.GetChatCompletion(_services, + provider: agent.LlmConfig.Provider, + model: agent.LlmConfig.Model); + + var refinedMessage = await completion.GetChatCompletions(agent, new List + { + new RoleDialogModel(AgentRole.User, "Check and output the correct SQL statements") + }); + + return refinedMessage.Content.JsonContent(); + } + + private async Task GetDictionarySQLPrompt(string originalSql, string tableStructure) + { + var agentService = _services.GetRequiredService(); + var render = _services.GetRequiredService(); + var knowledgeHooks = _services.GetServices(); + + var agent = await agentService.GetAgent(BuiltInAgentId.SqlDriver); + var template = agent.Templates.FirstOrDefault(x => x.Name == "sql_statement_correctness")?.Content ?? string.Empty; + var responseFormat = JsonSerializer.Serialize(new ExecuteQueryArgs { }); + + return render.Render(template, new Dictionary + { + { "original_sql", originalSql }, + { "table_structure", tableStructure }, + { "response_format", responseFormat } + }); + } } diff --git a/src/Plugins/BotSharp.Plugin.SqlDriver/Hooks/SqlDriverPlanningHook.cs b/src/Plugins/BotSharp.Plugin.SqlDriver/Hooks/SqlDriverPlanningHook.cs index ac99c699f..291607809 100644 --- a/src/Plugins/BotSharp.Plugin.SqlDriver/Hooks/SqlDriverPlanningHook.cs +++ b/src/Plugins/BotSharp.Plugin.SqlDriver/Hooks/SqlDriverPlanningHook.cs @@ -33,20 +33,25 @@ public async Task OnPlanningCompleted(string planner, RoleDialogModel msg) var conv = _services.GetRequiredService(); var wholeDialogs = conv.GetDialogHistory(); wholeDialogs.Add(RoleDialogModel.From(msg)); - wholeDialogs.Add(RoleDialogModel.From(msg, AgentRole.User, "use execute_sql to run query")); - - var agent = await _services.GetRequiredService().LoadAgent("beda4c12-e1ec-4b4b-b328-3df4a6687c4f"); + wholeDialogs.Add(RoleDialogModel.From(msg, AgentRole.User, $"call execute_sql to run query, set formatting_result as {settings.FormattingResult}")); + var agent = await _services.GetRequiredService().LoadAgent(BuiltInAgentId.SqlDriver); var completion = CompletionProvider.GetChatCompletion(_services, provider: agent.LlmConfig.Provider, model: agent.LlmConfig.Model); var response = await completion.GetChatCompletions(agent, wholeDialogs); + + // Invoke "execute_sql" var routing = _services.GetRequiredService(); await routing.InvokeFunction(response.FunctionName, response); msg.CurrentAgentId = agent.Id; msg.FunctionName = response.FunctionName; msg.FunctionArgs = response.FunctionArgs; msg.Content = response.Content; + msg.StopCompletion = response.StopCompletion; + + /*var routing = _services.GetRequiredService(); + await routing.InvokeAgent(BuiltInAgentId.SqlDriver, wholeDialogs);*/ } } diff --git a/src/Plugins/BotSharp.Plugin.SqlDriver/Models/ExecuteQueryArgs.cs b/src/Plugins/BotSharp.Plugin.SqlDriver/Models/ExecuteQueryArgs.cs index ac2279ea5..bc5c6fbdd 100644 --- a/src/Plugins/BotSharp.Plugin.SqlDriver/Models/ExecuteQueryArgs.cs +++ b/src/Plugins/BotSharp.Plugin.SqlDriver/Models/ExecuteQueryArgs.cs @@ -7,8 +7,12 @@ public class ExecuteQueryArgs [JsonPropertyName("sql_statements")] public string[] SqlStatements { get; set; } = []; + [JsonPropertyName("tables")] + public string[] Tables { get; set; } = []; + /// /// Beautifying query result /// + [JsonPropertyName("formatting_result")] public bool FormattingResult { get; set; } } diff --git a/src/Plugins/BotSharp.Plugin.SqlDriver/Settings/SqlDriverSetting.cs b/src/Plugins/BotSharp.Plugin.SqlDriver/Settings/SqlDriverSetting.cs index 5815151c6..3a4095a69 100644 --- a/src/Plugins/BotSharp.Plugin.SqlDriver/Settings/SqlDriverSetting.cs +++ b/src/Plugins/BotSharp.Plugin.SqlDriver/Settings/SqlDriverSetting.cs @@ -10,4 +10,5 @@ public class SqlDriverSetting public string SqlServerExecutionConnectionString { get; set; } = null!; public string SqlLiteConnectionString { get; set; } = null!; public bool ExecuteSqlSelectAutonomous { get; set; } = false; + public bool FormattingResult { get; set; } = true; } diff --git a/src/Plugins/BotSharp.Plugin.SqlDriver/data/agents/beda4c12-e1ec-4b4b-b328-3df4a6687c4f/agent.json b/src/Plugins/BotSharp.Plugin.SqlDriver/data/agents/beda4c12-e1ec-4b4b-b328-3df4a6687c4f/agent.json index 60309dffa..eb79816f8 100644 --- a/src/Plugins/BotSharp.Plugin.SqlDriver/data/agents/beda4c12-e1ec-4b4b-b328-3df4a6687c4f/agent.json +++ b/src/Plugins/BotSharp.Plugin.SqlDriver/data/agents/beda4c12-e1ec-4b4b-b328-3df4a6687c4f/agent.json @@ -10,7 +10,7 @@ "profiles": [ "database" ], "llmConfig": { "provider": "openai", - "model": "gpt-4o-mini" + "model": "gpt-4o" }, "routingRules": [ { 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 15e6d281d..9cdafc04a 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 @@ -11,8 +11,22 @@ "type": "string", "description": "sql statement" } + }, + + "formatting_result": { + "type": "boolean", + "description": "formatting the results" + }, + + "tables": { + "type": "array", + "description": "all related tables", + "items": { + "type": "string", + "description": "table name" + } } }, - "required": [ "sql_statement" ] + "required": [ "sql_statement", "tables", "formatting_result" ] } } \ No newline at end of file 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 7c40b11a1..5d07a941e 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 +1,5 @@ -Output in human readable format. If there is large amount of information, shape it in tabular. \ No newline at end of file +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. + +User Task Description: +{{ requirement_detail }} \ No newline at end of file diff --git a/src/Plugins/BotSharp.Plugin.SqlDriver/data/agents/beda4c12-e1ec-4b4b-b328-3df4a6687c4f/templates/sql_statement_correctness.liquid b/src/Plugins/BotSharp.Plugin.SqlDriver/data/agents/beda4c12-e1ec-4b4b-b328-3df4a6687c4f/templates/sql_statement_correctness.liquid new file mode 100644 index 000000000..cf61eb1b6 --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.SqlDriver/data/agents/beda4c12-e1ec-4b4b-b328-3df4a6687c4f/templates/sql_statement_correctness.liquid @@ -0,0 +1,11 @@ +You are a sql statement corrector. You will need to refer to the table structure and rewrite the original sql statement so it's using the correct information, e.g. column name. +Output the sql statement only without comments, in JSON format: {{ response_format }} +Make sure all the column names are defined in the Table Structure. + +===== +Original SQL statements: +{{ original_sql }} + +===== +Table Structure: +{{ table_structure }}