From 9c4fcb18e33d5efcc8469425b40255d7085e2068 Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Tue, 18 Aug 2026 10:31:06 -0500 Subject: [PATCH] fix inbound translation --- .../Routing/RoutingService.InstructLoop.cs | 15 +----- .../Routing/RoutingService.Translation.cs | 53 +++++++++++++++++++ .../BotSharp.Core/Routing/RoutingService.cs | 5 ++ 3 files changed, 59 insertions(+), 14 deletions(-) create mode 100644 src/Infrastructure/BotSharp.Core/Routing/RoutingService.Translation.cs diff --git a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.InstructLoop.cs b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.InstructLoop.cs index 9fff55c92..63d588a45 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.InstructLoop.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.InstructLoop.cs @@ -24,20 +24,7 @@ public async Task InstructLoop(Agent agent, RoleDialogModel mes await _context.Push(_router.Id); // Handle multi-language for input - var agentSettings = _services.GetRequiredService(); - if (agentSettings.EnableTranslator) - { - var translator = _services.GetRequiredService(); - - var language = states.GetState(StateConst.LANGUAGE, LanguageType.ENGLISH); - if (language != LanguageType.ENGLISH) - { - message.SecondaryContent = message.Content; - message.Content = await translator.Translate(_router, message.MessageId, message.Content, - language: LanguageType.ENGLISH, - clone: false); - } - } + await TranslateInboundMessage(_router, message); dialogs.Add(message); Context.SetDialogs(dialogs); diff --git a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.Translation.cs b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.Translation.cs new file mode 100644 index 000000000..8aa297d3f --- /dev/null +++ b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.Translation.cs @@ -0,0 +1,53 @@ +using BotSharp.Abstraction.Infrastructures.Enums; + +namespace BotSharp.Core.Routing; + +public partial class RoutingService +{ + private const string TranslationPromptName = "translation_prompt"; + + /// + /// Normalize an inbound user message to English so routing rules, agent instructions and + /// function arguments are always evaluated in English. The user's original text is kept in + /// SecondaryContent. Shared by InstructLoop and InstructDirect so every entry point into the + /// routing service behaves the same way. + /// + private async Task TranslateInboundMessage(Agent agent, RoleDialogModel message) + { + var agentSettings = _services.GetRequiredService(); + if (!agentSettings.EnableTranslator) + { + return; + } + + var states = _services.GetRequiredService(); + + // The caller supplies the language through the request states; the server does not detect it. + // TranslationService back-fills StateConst.LANGUAGE only when the state is absent, which cannot + // happen here - an absent state defaults to English and returns early. That back-fill serves + // the /translate endpoint instead. + // Unknown is excluded to stay in sync with TranslationResponseHook: it means the language has + // not been resolved, so translating would only paraphrase the user's own words. + var language = states.GetState(StateConst.LANGUAGE, LanguageType.ENGLISH); + if (language == LanguageType.ENGLISH || language == LanguageType.UNKNOWN) + { + return; + } + + // TranslationService reads the prompt template off the agent it is handed, and only the + // AI Assistant defines it. Fall back to that agent when the executing one has no template, + // which is the normal case for the task agents reaching us through InstructDirect. + var host = agent; + if (host?.Templates?.Any(x => x.Name == TranslationPromptName) != true) + { + var agentService = _services.GetRequiredService(); + host = await agentService.LoadAgent(BuiltInAgentId.AIAssistant); + } + + var translator = _services.GetRequiredService(); + message.SecondaryContent = message.Content; + message.Content = await translator.Translate(host, message.MessageId, message.Content, + language: LanguageType.ENGLISH, + clone: false); + } +} diff --git a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.cs b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.cs index 273f19913..73569f756 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.cs @@ -30,6 +30,11 @@ public async Task InstructDirect(Agent agent, RoleDialogModel m { var conv = _services.GetRequiredService(); var storage = _services.GetRequiredService(); + + // Must run before the message is persisted so the stored record matches InstructLoop: + // Content in English, the user's original text in SecondaryContent. + await TranslateInboundMessage(agent, message); + await storage.Append(conv.ConversationId, message); dialogs.Add(message);