Merge pull request #421 from hchen2020/master

translation prompt.
This commit is contained in:
C. Oceania 2024-04-23 14:03:28 -05:00 committed by GitHub
commit bef26ad542
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 155 additions and 44 deletions

View file

@ -0,0 +1,9 @@
namespace BotSharp.Abstraction.Infrastructures.Enums;
public class LanguageType
{
public const string UNKNOWN = "Unknown";
public const string ENGLISH = "English";
public const string SPANISH = "Spanish";
public const string CHINESE = "Chinese";
}

View file

@ -29,5 +29,6 @@ public class ElementButton
[JsonPropertyName("post_action_disclaimer")]
[JsonProperty("post_action_disclaimer")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public string? PostActionDisclaimer { get; set; }
}

View file

@ -11,6 +11,7 @@ public class GenericTemplateMessage<T> : IRichMessage, ITemplateMessage
[JsonPropertyName("text")]
[JsonProperty("text")]
[Translate]
public string Text { get; set; } = string.Empty;
[JsonPropertyName("template_type")]
@ -36,6 +37,7 @@ public class GenericTemplateMessage<T> : IRichMessage, ITemplateMessage
public class GenericElement
{
[Translate]
public string Title { get; set; }
public string Subtitle { get; set; }

View file

@ -50,6 +50,12 @@ public class RoutingArgs
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public string UserGoal { get; set; }
[JsonPropertyName("user_message_in_english")]
public string UserMessageInEnglish { get; set; }
[JsonPropertyName("language")]
public string Language { get; set; } = LanguageType.UNKNOWN;
public override string ToString()
{
var route = string.IsNullOrEmpty(AgentName) ? "" : $"<Route to {AgentName.ToUpper()} because {NextActionReason}>";

View file

@ -2,5 +2,5 @@ namespace BotSharp.Abstraction.Translation;
public interface ITranslationService
{
T Translate<T>(T data, string language = "Spanish", bool clone = true) where T : class;
Task<T> Translate<T>(Agent router, string messageId, T data, string language = "Spanish", bool clone = true) where T : class;
}

View file

@ -62,6 +62,7 @@
<None Remove="data\agents\01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a\templates\planner_prompt.two_stage.2nd.plan.liquid" />
<None Remove="data\agents\01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a\templates\planner_prompt.two_stage.2nd.task.liquid" />
<None Remove="data\agents\01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a\templates\response_with_function.liquid" />
<None Remove="data\agents\01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a\templates\translation_prompt.liquid" />
<None Remove="data\agents\dfd9b46d-d00c-40af-8a75-3fbdc2b89869\agent.json" />
<None Remove="data\agents\dfd9b46d-d00c-40af-8a75-3fbdc2b89869\instruction.liquid" />
<None Remove="data\agents\dfd9b46d-d00c-40af-8a75-3fbdc2b89869\templates\instruction.executor.liquid" />
@ -118,6 +119,9 @@
<Content Include="data\agents\01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a\templates\.welcome.liquid">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="data\agents\01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a\templates\translation_prompt.liquid">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="data\agents\dfd9b46d-d00c-40af-8a75-3fbdc2b89869\agent.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>

View file

@ -71,15 +71,6 @@ public partial class ConversationService
}
}
// Persist to storage
if (!message.StopCompletion)
{
_storage.Append(_conversationId, message);
// Add to thread
dialogs.Add(RoleDialogModel.From(message));
}
if (!stopCompletion)
{
// Routing with reasoning

View file

@ -10,10 +10,21 @@ public class ResponseToUserRoutingHandler : RoutingHandlerBase, IRoutingHandler
public List<ParameterPropertyDef> Parameters => new List<ParameterPropertyDef>
{
new ParameterPropertyDef("reason", "why response to user directly without go to other agents"),
new ParameterPropertyDef("response", "response content to user in courteous words. If the user wants to end the conversation, you must set conversation_end to true and response politely."),
new ParameterPropertyDef("conversation_end", "whether to end this conversation", type: "boolean"),
new ParameterPropertyDef("task_completed ", "whether the user's task request has been completed.", type: "boolean")
new ParameterPropertyDef("reason",
"why response to user directly without go to other agents"),
new ParameterPropertyDef("response",
"response content to user in courteous words with language English. If the user wants to end the conversation, you must set conversation_end to true and response politely."),
new ParameterPropertyDef("conversation_end",
"whether to end this conversation",
type: "boolean"),
new ParameterPropertyDef("task_completed ",
"whether the user's task request has been completed.",
type: "boolean"),
new ParameterPropertyDef("user_message_in_english",
"Translate user message from non-English to English"),
new ParameterPropertyDef("language",
"Language name of the message user sent, the name may be English, Spanish or Chinese.",
required: true),
};
public ResponseToUserRoutingHandler(IServiceProvider services, ILogger<ResponseToUserRoutingHandler> logger, RoutingSettings settings)

View file

@ -28,7 +28,12 @@ public class RouteToAgentRoutingHandler : RoutingHandlerBase, IRoutingHandler
type: "object"),
new ParameterPropertyDef("is_new_task",
"whether the user is requesting a new task that is different from the previous topic.",
type: "boolean")
type: "boolean"),
new ParameterPropertyDef("user_message_in_english",
"Translate user message from non-English to English"),
new ParameterPropertyDef("language",
"Language name of the message user sent, the name may be English, Spanish or Chinese.",
required: true),
};
public RouteToAgentRoutingHandler(IServiceProvider services, ILogger<RouteToAgentRoutingHandler> logger, RoutingSettings settings)

View file

@ -1,7 +1,19 @@
using BotSharp.Abstraction.Agents.Models;
using BotSharp.Abstraction.Infrastructures.Enums;
using BotSharp.Abstraction.Routing.Models;
using BotSharp.Abstraction.Routing.Planning;
using BotSharp.Abstraction.Routing.Settings;
using BotSharp.Abstraction.Templating;
using BotSharp.Core.Routing.Planning;
using Fluid.Ast;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.Diagnostics.Metrics;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Security.Cryptography.X509Certificates;
using ThirdParty.Json.LitJson;
using static System.Net.Mime.MediaTypeNames;
namespace BotSharp.Core.Routing;
@ -39,15 +51,10 @@ public partial class RoutingService : IRoutingService
var handler = handlers.FirstOrDefault(x => x.Name == "route_to_agent");
var conv = _services.GetRequiredService<IConversationService>();
var dialogs = new List<RoleDialogModel>();
if (conv.States.GetState("hide_context", "false") == "true")
{
dialogs.Add(message);
}
else
{
dialogs = conv.GetDialogHistory();
}
var storage = _services.GetRequiredService<IConversationStorage>();
storage.Append(conv.ConversationId, message);
var dialogs = conv.GetDialogHistory();
handler.SetDialogs(dialogs);
var inst = new FunctionCallFromLlm
@ -71,11 +78,14 @@ public partial class RoutingService : IRoutingService
public async Task<RoleDialogModel> InstructLoop(RoleDialogModel message, List<RoleDialogModel> dialogs)
{
var agentService = _services.GetRequiredService<IAgentService>();
_router = await agentService.LoadAgent(message.CurrentAgentId);
RoleDialogModel response = default;
var agentService = _services.GetRequiredService<IAgentService>();
var convService = _services.GetRequiredService<IConversationService>();
var storage = _services.GetRequiredService<IConversationStorage>();
_router = await agentService.LoadAgent(message.CurrentAgentId);
var states = _services.GetRequiredService<IConversationStateService>();
var executor = _services.GetRequiredService<IExecutor>();
@ -83,17 +93,23 @@ public partial class RoutingService : IRoutingService
_context.Push(_router.Id);
int loopCount = 0;
while (loopCount < planner.MaxLoopCount && !_context.IsEmpty)
dialogs.Add(message);
// Get first instruction
_router.TemplateDict["conversation"] = await GetConversationContent(dialogs);
var inst = await planner.GetNextInstruction(_router, message.MessageId, dialogs);
// Handle multi-language for input
if (inst.Language != LanguageType.UNKNOWN && inst.Language != LanguageType.ENGLISH)
{
loopCount++;
var conversation = await GetConversationContent(dialogs);
_router.TemplateDict["conversation"] = conversation;
// Get instruction from Planner
var inst = await planner.GetNextInstruction(_router, message.MessageId, dialogs);
message.Content = inst.UserMessageInEnglish;
}
storage.Append(convService.ConversationId, message);
int loopCount = 1;
while (true)
{
await HookEmitter.Emit<IRoutingHook>(_services, async hook =>
await hook.OnRoutingInstructionReceived(inst, message)
);
@ -121,6 +137,29 @@ public partial class RoutingService : IRoutingService
}
await planner.AgentExecuted(_router, inst, response, dialogs);
if (loopCount >= planner.MaxLoopCount || _context.IsEmpty)
{
break;
}
// Get next instruction from Planner
_router.TemplateDict["conversation"] = await GetConversationContent(dialogs);
inst = await planner.GetNextInstruction(_router, message.MessageId, dialogs);
loopCount++;
}
// Handle multi-language for output
if (inst.Language != LanguageType.UNKNOWN && inst.Language != LanguageType.ENGLISH)
{
if (response.RichContent != null)
{
var translator = _services.GetRequiredService<ITranslationService>();
response.RichContent.Message = await translator.Translate(_router,
message.MessageId,
response.RichContent.Message,
language: inst.Language);
}
}
return response;

View file

@ -1,4 +1,6 @@
using Amazon.Runtime.Internal.Transform;
using BotSharp.Abstraction.Options;
using BotSharp.Abstraction.Templating;
using BotSharp.Abstraction.Translation.Attributes;
using Newtonsoft.Json;
using System.Reflection;
@ -10,6 +12,8 @@ public class TranslationService : ITranslationService
private readonly IServiceProvider _services;
private readonly ILogger<TranslationService> _logger;
private readonly BotSharpOptions _options;
private Agent _router;
private string _messageId;
public TranslationService(IServiceProvider services,
ILogger<TranslationService> logger,
@ -20,8 +24,10 @@ public class TranslationService : ITranslationService
_options = options;
}
public T Translate<T>(T data, string language = "Spanish", bool clone = true) where T : class
public async Task<T> Translate<T>(Agent router, string messageId, T data, string language = "Spanish", bool clone = true) where T : class
{
_router = router;
_messageId = messageId;
var cloned = data;
if (clone)
{
@ -30,7 +36,7 @@ public class TranslationService : ITranslationService
var unique = new HashSet<string>();
Collect(cloned, ref unique);
var map = InnerTranslate(unique, language);
var map = await InnerTranslate(unique, language);
cloned = Assign(cloned, map);
return cloned;
}
@ -250,14 +256,44 @@ public class TranslationService : ITranslationService
/// <param name="list"></param>
/// <param name="language"></param>
/// <returns></returns>
private Dictionary<string, string> InnerTranslate(HashSet<string> list, string language)
private async Task<Dictionary<string, string>> InnerTranslate(HashSet<string> list, string language)
{
var map = new Dictionary<string, string>();
if (list == null || !list.Any()) return map;
// chat completion
var completion = CompletionProvider.GetChatCompletion(_services,
provider: _router?.LlmConfig?.Provider,
model: _router?.LlmConfig?.Model);
foreach (var item in list)
var texts = list.ToArray();
var translator = new Agent
{
map.Add(item, "hello world");
Id = Guid.Empty.ToString(),
Name = "Translator",
TemplateDict = new Dictionary<string, object>
{
{ "text_list", JsonConvert.SerializeObject(texts) },
{ "language", language }
}
};
var template = _router.Templates.First(x => x.Name == "translation_prompt").Content;
var render = _services.GetRequiredService<ITemplateRender>();
var prompt = render.Render(template, translator.TemplateDict);
var translationDialogs = new List<RoleDialogModel>
{
new RoleDialogModel(AgentRole.User, prompt)
{
FunctionName = "translation_prompt",
MessageId = _messageId
}
};
var translationResponse = await completion.GetChatCompletions(translator, translationDialogs);
var translatedTexts = translationResponse.Content.JsonArrayContent<string>();
var map = new Dictionary<string, string>();
for (var i = 0; i < list.Count; i++)
{
map.Add(texts[i], translatedTexts[i]);
}
return map;

View file

@ -1,4 +1,6 @@
You're {{router.name}} ({{router.description}}). Follow these steps to handle user request:
You're {{router.name}} ({{router.description}}).
You can understand messages sent by users in different languages.
Follow these steps to handle user request:
1. Read the [CONVERSATION] content.
2. Select a appropriate function from [FUNCTIONS].
3. Determine which agent is suitable to handle this conversation.

View file

@ -0,0 +1,5 @@
{{ text_list }}
=====
Translate the sentences in the list into {{ language }}, output the translated text list.