From 76dc9080ad2a50132dd794e77c80afc6a6e2e603 Mon Sep 17 00:00:00 2001 From: Haiping Chen Date: Thu, 9 May 2024 15:59:59 -0500 Subject: [PATCH 1/3] Fix translator. --- .../Agents/Settings/AgentSettings.cs | 1 + .../Translation/Models/TranslationOutput.cs | 13 ++++++++ .../Handlers/RouteToAgentRoutingHandler.cs | 5 +-- .../RoutingService.GetConversationContent.cs | 2 +- .../BotSharp.Core/Routing/RoutingService.cs | 31 ++++++++++--------- .../Translation/TranslationResponseHook.cs | 6 ++++ .../Translation/TranslationService.cs | 14 +++++++-- .../templates/translation_prompt.liquid | 3 +- 8 files changed, 52 insertions(+), 23 deletions(-) create mode 100644 src/Infrastructure/BotSharp.Abstraction/Translation/Models/TranslationOutput.cs diff --git a/src/Infrastructure/BotSharp.Abstraction/Agents/Settings/AgentSettings.cs b/src/Infrastructure/BotSharp.Abstraction/Agents/Settings/AgentSettings.cs index e95f72bc..232e7b60 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Agents/Settings/AgentSettings.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Agents/Settings/AgentSettings.cs @@ -5,6 +5,7 @@ public class AgentSettings public string DataDir { get; set; } = string.Empty; public string TemplateFormat { get; set; } = "liquid"; public string HostAgentId { get; set; } = string.Empty; + public bool EnableTranslator { get; set; } = false; /// /// This is the default LLM config for agent diff --git a/src/Infrastructure/BotSharp.Abstraction/Translation/Models/TranslationOutput.cs b/src/Infrastructure/BotSharp.Abstraction/Translation/Models/TranslationOutput.cs new file mode 100644 index 00000000..52ad54ec --- /dev/null +++ b/src/Infrastructure/BotSharp.Abstraction/Translation/Models/TranslationOutput.cs @@ -0,0 +1,13 @@ +namespace BotSharp.Abstraction.Translation.Models; + +public class TranslationOutput +{ + [JsonPropertyName("input_lang")] + public string InputLanguage { get; set; } = null!; + + [JsonPropertyName("output_lang")] + public string OutputLanguage { get; set; } = LanguageType.ENGLISH; + + [JsonPropertyName("texts")] + public string[] Texts { get; set; } = Array.Empty(); +} diff --git a/src/Infrastructure/BotSharp.Core/Routing/Handlers/RouteToAgentRoutingHandler.cs b/src/Infrastructure/BotSharp.Core/Routing/Handlers/RouteToAgentRoutingHandler.cs index 6afc67c9..9f768696 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/Handlers/RouteToAgentRoutingHandler.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/Handlers/RouteToAgentRoutingHandler.cs @@ -28,10 +28,7 @@ public class RouteToAgentRoutingHandler : RoutingHandlerBase, IRoutingHandler required: true), new ParameterPropertyDef("is_new_task", "whether the user is requesting a new task that is different from the previous topic.", - type: "boolean"), - new ParameterPropertyDef("language", - "User preferred language, considering the whole conversation. Language could be English, Spanish or Chinese.", - required: true) + type: "boolean") }; public RouteToAgentRoutingHandler(IServiceProvider services, ILogger logger, RoutingSettings settings) diff --git a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.GetConversationContent.cs b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.GetConversationContent.cs index eb690b9c..2757d5d8 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.GetConversationContent.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.GetConversationContent.cs @@ -16,7 +16,7 @@ public partial class RoutingService role = agent.Name; } - conversation += $"{role}: {dialog.Payload ?? dialog.SecondaryContent ?? dialog.Content}\r\n"; + conversation += $"{role}: {dialog.Payload ?? dialog.Content}\r\n"; } return conversation; diff --git a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.cs b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.cs index eb2496bf..885728a1 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.cs @@ -82,26 +82,29 @@ public partial class RoutingService : IRoutingService _context.Push(_router.Id); + // Handle multi-language for input + var agentSettings = _services.GetRequiredService(); + if (agentSettings.EnableTranslator) + { + var translator = _services.GetRequiredService(); + + var language = states.GetState("language", LanguageType.UNKNOWN); + if (language != LanguageType.ENGLISH) + { + message.SecondaryContent = message.Content; + message.Content = await translator.Translate(_router, message.MessageId, message.Content, + language: LanguageType.ENGLISH, + clone: false); + } + } + dialogs.Add(message); + storage.Append(convService.ConversationId, message); // Get first instruction _router.TemplateDict["conversation"] = await GetConversationContent(dialogs); var inst = await planner.GetNextInstruction(_router, message.MessageId, dialogs); - // Handle multi-language for input - var translator = _services.GetRequiredService(); - - var language = states.GetState("language", inst.Language); - if (language != LanguageType.UNKNOWN && language != LanguageType.ENGLISH) - { - message.SecondaryContent = message.Content; - message.Content = await translator.Translate(_router, message.MessageId, message.Content, - language: LanguageType.ENGLISH, - clone: false); - } - - storage.Append(convService.ConversationId, message); - int loopCount = 1; while (true) { diff --git a/src/Infrastructure/BotSharp.Core/Translation/TranslationResponseHook.cs b/src/Infrastructure/BotSharp.Core/Translation/TranslationResponseHook.cs index fef6ee17..d79c73a0 100644 --- a/src/Infrastructure/BotSharp.Core/Translation/TranslationResponseHook.cs +++ b/src/Infrastructure/BotSharp.Core/Translation/TranslationResponseHook.cs @@ -21,6 +21,12 @@ namespace BotSharp.Logger.Hooks } public override async Task OnResponseGenerated(RoleDialogModel message) { + var agentSettings = _services.GetRequiredService(); + if (!agentSettings.EnableTranslator) + { + return; + } + // Handle multi-language for output var agentService = _services.GetRequiredService(); var router = await agentService.LoadAgent(AIAssistant); diff --git a/src/Infrastructure/BotSharp.Core/Translation/TranslationService.cs b/src/Infrastructure/BotSharp.Core/Translation/TranslationService.cs index 01ae898f..b18c7ab1 100644 --- a/src/Infrastructure/BotSharp.Core/Translation/TranslationService.cs +++ b/src/Infrastructure/BotSharp.Core/Translation/TranslationService.cs @@ -1,6 +1,8 @@ +using BotSharp.Abstraction.Infrastructures.Enums; using BotSharp.Abstraction.MLTasks; using BotSharp.Abstraction.Options; using BotSharp.Abstraction.Templating; +using BotSharp.Abstraction.Translation.Models; using System.Collections; using System.Reflection; @@ -57,7 +59,13 @@ public class TranslationService : ITranslationService try { - var translatedTexts = translatedStringList.JsonArrayContent(); + // Override language if it's Unknown, it's used to output the corresponding language. + var states = _services.GetRequiredService(); + var inputLanguage = string.IsNullOrEmpty(translatedStringList.InputLanguage) ? LanguageType.ENGLISH : translatedStringList.InputLanguage; + var languageState = states.GetState("language", inputLanguage); + states.SetState("language", languageState, activeRounds: 1); + + var translatedTexts = translatedStringList.Texts; var map = new Dictionary(); for (var i = 0; i < texts.Length; i++) @@ -283,7 +291,7 @@ public class TranslationService : ITranslationService /// /// /// - private async Task InnerTranslate(string texts, string language, string template) + private async Task InnerTranslate(string texts, string language, string template) { var translator = new Agent { @@ -308,7 +316,7 @@ public class TranslationService : ITranslationService } }; var response = await _completion.GetChatCompletions(translator, translationDialogs); - return response.Content; + return response.Content.JsonContent(); } #region Type methods 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 a3052187..4cd12bd0 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,4 +1,5 @@ {{ text_list }} ===== -Translate the above sentences in the list into {{ language }}, output the translated text in JSON array [""]. \ No newline at end of file +Translate the above sentences in the list into {{ language }}. +Output the translated text in JSON {"input_lang":"", "output_lang":"{{ language }}", "texts":[""]}, input_lang is based on the original sentences. \ No newline at end of file From 9a93fd10e6291905deb45acc78c081a9dd4fc624 Mon Sep 17 00:00:00 2001 From: "C. Oceania" Date: Thu, 9 May 2024 16:30:30 -0500 Subject: [PATCH 2/3] Update RoutingArgs.cs --- .../BotSharp.Abstraction/Routing/Models/RoutingArgs.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/Infrastructure/BotSharp.Abstraction/Routing/Models/RoutingArgs.cs b/src/Infrastructure/BotSharp.Abstraction/Routing/Models/RoutingArgs.cs index f68b7231..dad45cb9 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Routing/Models/RoutingArgs.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Routing/Models/RoutingArgs.cs @@ -50,9 +50,6 @@ public class RoutingArgs [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string UserGoal { get; set; } = string.Empty; - [JsonPropertyName("language")] - public string Language { get; set; } = LanguageType.ENGLISH; - public override string ToString() { var route = string.IsNullOrEmpty(AgentName) ? "" : $""; From 1360adbb46aa224ad9f59da4da29ac7571dc4f48 Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Thu, 9 May 2024 17:06:03 -0500 Subject: [PATCH 3/3] add agent template update endpoint --- .../Agents/IAgentService.cs | 7 +++ .../Repositories/IBotSharpRepository.cs | 2 +- .../Services/AgentService.UpdateAgent.cs | 56 ++++++++++++++++++- .../Repository/BotSharpDbContext.cs | 3 + .../FileRepository/FileRepository.Agent.cs | 19 +++++++ .../Controllers/AgentController.cs | 8 +++ .../Agents/AgentTemplatePatchModel.cs | 23 ++++++++ .../Repository/MongoRepository.Agent.cs | 17 ++++++ 8 files changed, 131 insertions(+), 4 deletions(-) create mode 100644 src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentTemplatePatchModel.cs diff --git a/src/Infrastructure/BotSharp.Abstraction/Agents/IAgentService.cs b/src/Infrastructure/BotSharp.Abstraction/Agents/IAgentService.cs index 5ab249b8..db4db62a 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Agents/IAgentService.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Agents/IAgentService.cs @@ -37,6 +37,13 @@ public interface IAgentService Task DeleteAgent(string id); Task UpdateAgent(Agent agent, AgentField updateField); + + /// + /// Path existing templates of agent, cannot create new or delete templates + /// + /// + /// + Task PatchAgentTemplate(Agent agent); Task UpdateAgentFromFile(string id); string GetDataDir(); string GetAgentDataDir(string agentId); diff --git a/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs b/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs index c301c738..a451071e 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs @@ -1,7 +1,6 @@ using BotSharp.Abstraction.Loggers.Models; using BotSharp.Abstraction.Plugins.Models; using BotSharp.Abstraction.Repositories.Filters; -using BotSharp.Abstraction.Repositories.Models; using BotSharp.Abstraction.Tasks.Models; using BotSharp.Abstraction.Users.Models; @@ -35,6 +34,7 @@ public interface IBotSharpRepository bool DeleteAgent(string agentId); List GetAgentResponses(string agentId, string prefix, string intent); string GetAgentTemplate(string agentId, string templateName); + bool PatchAgentTemplate(string agentId, AgentTemplate template); #endregion #region Agent Task diff --git a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.UpdateAgent.cs b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.UpdateAgent.cs index 0cce4209..154d7398 100644 --- a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.UpdateAgent.cs +++ b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.UpdateAgent.cs @@ -1,6 +1,3 @@ -using BotSharp.Abstraction.Agents.Models; -using BotSharp.Abstraction.Functions.Models; -using BotSharp.Abstraction.Repositories; using BotSharp.Abstraction.Repositories.Enums; using BotSharp.Abstraction.Routing.Models; using System.IO; @@ -106,6 +103,59 @@ public partial class AgentService } } + + public async Task PatchAgentTemplate(Agent agent) + { + var patchResult = string.Empty; + if (agent == null || agent.Templates.IsNullOrEmpty()) + { + patchResult = $"Null agent instance or empty input templates"; + _logger.LogWarning(patchResult); + return patchResult; + } + + var record = _db.GetAgent(agent.Id); + if (record == null) + { + patchResult = $"Cannot find agent {agent.Id}"; + _logger.LogWarning(patchResult); + return patchResult; + } + + var successTemplates = new List(); + var failTemplates = new List(); + foreach (var template in agent.Templates) + { + if (template == null) continue; + + var result = _db.PatchAgentTemplate(agent.Id, template); + if (result) + { + successTemplates.Add(template.Name); + _logger.LogInformation($"Template {template.Name} is updated successfully!"); + } + else + { + failTemplates.Add(template.Name); + _logger.LogWarning($"Template {template.Name} is failed to be updated!"); + } + } + + Utilities.ClearCache(); + + if (!successTemplates.IsNullOrEmpty()) + { + patchResult += $"Success templates:\n{string.Join('\n', successTemplates)}\n\n"; + } + + if (!failTemplates.IsNullOrEmpty()) + { + patchResult += $"Failed templates:\n{string.Join('\n', failTemplates)}"; + } + + return patchResult; + } + private Agent? FetchAgentFileById(string agentId, string filePath) { if (!Directory.Exists(filePath)) return null; diff --git a/src/Infrastructure/BotSharp.Core/Repository/BotSharpDbContext.cs b/src/Infrastructure/BotSharp.Core/Repository/BotSharpDbContext.cs index 650c9c4f..28aac06b 100644 --- a/src/Infrastructure/BotSharp.Core/Repository/BotSharpDbContext.cs +++ b/src/Infrastructure/BotSharp.Core/Repository/BotSharpDbContext.cs @@ -87,6 +87,9 @@ public class BotSharpDbContext : Database, IBotSharpRepository public string GetAgentTemplate(string agentId, string templateName) => throw new NotImplementedException(); + public bool PatchAgentTemplate(string agentId, AgentTemplate template) + => throw new NotImplementedException(); + public List GetAgentResponses(string agentId, string prefix, string intent) => throw new NotImplementedException(); diff --git a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Agent.cs b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Agent.cs index 20f8bcdd..a46669a5 100644 --- a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Agent.cs +++ b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Agent.cs @@ -401,6 +401,25 @@ namespace BotSharp.Core.Repository return string.Empty; } + public bool PatchAgentTemplate(string agentId, AgentTemplate template) + { + if (string.IsNullOrEmpty(agentId) || template == null) return false; + + var dir = Path.Combine(_dbSettings.FileRepository, _agentSettings.DataDir, agentId, "templates"); + if (!Directory.Exists(dir)) return false; + + var foundTemplate = Directory.GetFiles(dir).FirstOrDefault(f => + { + var fileName = Path.GetFileNameWithoutExtension(f); + var extension = Path.GetExtension(f).Substring(1); + return fileName.IsEqualTo(template.Name) && extension.IsEqualTo(_agentSettings.TemplateFormat); + }); + + if (foundTemplate == null) return false; + + File.WriteAllText(foundTemplate, template.Content); + return true; + } public void BulkInsertAgents(List agents) { diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/AgentController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/AgentController.cs index 77f73061..bb67de2c 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/AgentController.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/AgentController.cs @@ -110,4 +110,12 @@ public class AgentController : ControllerBase model.Id = agentId; await _agentService.UpdateAgent(model, field); } + + [HttpPatch("/agent/{agentId}/templates")] + public async Task PatchAgentTemplates([FromRoute] string agentId, [FromBody] AgentTemplatePatchModel agent) + { + var model = agent.ToAgent(); + model.Id = agentId; + return await _agentService.PatchAgentTemplate(model); + } } \ No newline at end of file diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentTemplatePatchModel.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentTemplatePatchModel.cs new file mode 100644 index 00000000..2d1beba6 --- /dev/null +++ b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentTemplatePatchModel.cs @@ -0,0 +1,23 @@ +using BotSharp.Abstraction.Agents.Models; + +namespace BotSharp.OpenAPI.ViewModels.Agents; + +public class AgentTemplatePatchModel +{ + public List? Templates { get; set; } + + public AgentTemplatePatchModel() + { + + } + + public Agent ToAgent() + { + var agent = new Agent() + { + Templates = Templates ?? new List(), + }; + + return agent; + } +} diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Agent.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Agent.cs index 07d30984..8cffb61f 100644 --- a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Agent.cs +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Agent.cs @@ -332,6 +332,23 @@ public partial class MongoRepository return agent.Templates?.FirstOrDefault(x => x.Name == templateName.ToLower())?.Content ?? string.Empty; } + public bool PatchAgentTemplate(string agentId, AgentTemplate template) + { + if (string.IsNullOrEmpty(agentId) || template == null) return false; + + var filter = Builders.Filter.Eq(x => x.Id, agentId); + var agent = _dc.Agents.Find(filter).FirstOrDefault(); + if (agent == null || agent.Templates.IsNullOrEmpty()) return false; + + var foundTemplate = agent.Templates.FirstOrDefault(x => x.Name.IsEqualTo(template.Name)); + if (foundTemplate == null) return false; + + foundTemplate.Content = template.Content; + var update = Builders.Update.Set(x => x.Templates, agent.Templates); + _dc.Agents.UpdateOne(filter, update); + return true; + } + public void BulkInsertAgents(List agents) { if (agents.IsNullOrEmpty()) return;