From 95fd02be200a42ac881906c99455bbe538f7bc0d Mon Sep 17 00:00:00 2001 From: Visagan Guruparan <103048@smsassist.com> Date: Mon, 21 Jul 2025 16:02:40 -0500 Subject: [PATCH 1/6] add channel to the instruct controller --- .../BotSharp.OpenAPI/Controllers/InstructModeController.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/InstructModeController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/InstructModeController.cs index e8c87c2c..de67fcab 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/InstructModeController.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/InstructModeController.cs @@ -30,7 +30,8 @@ public class InstructModeController : ControllerBase .SetState("model_id", input.ModelId, source: StateSource.External) .SetState("instruction", input.Instruction, source: StateSource.External) .SetState("input_text", input.Text, source: StateSource.External) - .SetState("template_name", input.Template, source: StateSource.External); + .SetState("template_name", input.Template, source: StateSource.External) + .SetState("channel", input.Channel, source: StateSource.External); var instructor = _services.GetRequiredService(); var result = await instructor.Execute(agentId, From 061ce95e353d05622da935a3a57b6bdff57a2d0b Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Mon, 21 Jul 2025 23:01:53 -0500 Subject: [PATCH 2/6] allow using template across agents --- .../FileRepository/FileRepository.Agent.cs | 2 +- .../Templating/TemplateRender.cs | 46 +++++++++++++++---- 2 files changed, 38 insertions(+), 10 deletions(-) diff --git a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Agent.cs b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Agent.cs index f7e70ca7..c5947708 100644 --- a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Agent.cs +++ b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Agent.cs @@ -475,7 +475,7 @@ namespace BotSharp.Core.Repository if (!filter.AgentNames.IsNullOrEmpty()) { - query = query.Where(x => filter.AgentNames.Contains(x.Name)); + query = query.Where(x => filter.AgentNames.Contains(x.Name, StringComparer.OrdinalIgnoreCase)); } if (!string.IsNullOrEmpty(filter.SimilarName)) diff --git a/src/Infrastructure/BotSharp.Core/Templating/TemplateRender.cs b/src/Infrastructure/BotSharp.Core/Templating/TemplateRender.cs index f7e5ecde..5b65465d 100644 --- a/src/Infrastructure/BotSharp.Core/Templating/TemplateRender.cs +++ b/src/Infrastructure/BotSharp.Core/Templating/TemplateRender.cs @@ -8,6 +8,7 @@ using System.Collections; using System.IO; using System.Reflection; using System.Text.Encodings.Web; +using System.Text.RegularExpressions; namespace BotSharp.Core.Templating; @@ -36,9 +37,9 @@ public class TemplateRender : ITemplateRender _options.MemberAccessStrategy.Register(); _options.MemberAccessStrategy.Register(); - _parser.RegisterIdentifierTag("link", (string identifier, TextWriter writer, TextEncoder encoder, TemplateContext context) => + _parser.RegisterExpressionTag("link", (Expression expression, TextWriter writer, TextEncoder encoder, TemplateContext context) => { - return RenderIdentifierTag("link", identifier, writer, encoder, context); + return RenderTag("link", expression, writer, encoder, context, services); }); } @@ -82,20 +83,47 @@ public class TemplateRender : ITemplateRender #region Private methods - private static async ValueTask RenderIdentifierTag(string tag, string identifier, TextWriter writer, TextEncoder encoder, TemplateContext context) + private static async ValueTask RenderTag( + string tag, + Expression expression, + TextWriter writer, + TextEncoder encoder, + TemplateContext context, + IServiceProvider services) { + try { - var value = await context.Model.GetValueAsync(TemplateRenderConstant.RENDER_AGENT, context); - var agent = value?.ToObjectValue() as Agent; - var found = agent?.Templates?.FirstOrDefault(x => x.Name.IsEqualTo(identifier)); - var key = $"{agent?.Id} | {tag} | {identifier}"; + var value = await expression.EvaluateAsync(context); + var expStr = value?.ToStringValue() ?? string.Empty; - if (found == null || (context.AmbientValues.TryGetValue(key, out var visited) && (bool)visited)) + value = await context.Model.GetValueAsync(TemplateRenderConstant.RENDER_AGENT, context); + var agent = value?.ToObjectValue() as Agent; + + var splited = Regex.Split(expStr, @"\s*from\s*", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant) + .Where(x => !string.IsNullOrWhiteSpace(x)) + .Select(x => x.Trim()) + .ToArray(); + + var templateName = splited.ElementAtOrDefault(0); + var agentName = splited.ElementAtOrDefault(1); + + if (splited.Length > 1 && !agentName.IsEqualTo(agent?.Name)) + { + using var scope = services.CreateScope(); + var agentService = scope.ServiceProvider.GetRequiredService(); + var result = await agentService.GetAgents(new() { SimilarName = agentName }); + agent = result?.Items?.FirstOrDefault(); + } + + var template = agent?.Templates?.FirstOrDefault(x => x.Name.IsEqualTo(templateName)); + var key = $"{tag} | {agent?.Id} | {templateName}"; + + if (template == null || (context.AmbientValues.TryGetValue(key, out var visited) && (bool)visited)) { writer.Write(string.Empty); } - else if (_parser.TryParse(found.Content, out var t, out _)) + else if (_parser.TryParse(template.Content, out var t, out _)) { context.AmbientValues[key] = true; var rendered = t.Render(context); From 1cabd91ec083fdce331a562e58c3a07db94ce180 Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Mon, 21 Jul 2025 23:02:44 -0500 Subject: [PATCH 3/6] minor change --- src/Infrastructure/BotSharp.Core/Templating/TemplateRender.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Infrastructure/BotSharp.Core/Templating/TemplateRender.cs b/src/Infrastructure/BotSharp.Core/Templating/TemplateRender.cs index 5b65465d..e2a0d029 100644 --- a/src/Infrastructure/BotSharp.Core/Templating/TemplateRender.cs +++ b/src/Infrastructure/BotSharp.Core/Templating/TemplateRender.cs @@ -91,7 +91,6 @@ public class TemplateRender : ITemplateRender TemplateContext context, IServiceProvider services) { - try { var value = await expression.EvaluateAsync(context); From b1eb7543d411025e287e02ee77d33eaf36326eaf Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Tue, 22 Jul 2025 10:26:00 -0500 Subject: [PATCH 4/6] add from agent filter --- .../BotSharp.Core/Templating/TemplateRender.cs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/Infrastructure/BotSharp.Core/Templating/TemplateRender.cs b/src/Infrastructure/BotSharp.Core/Templating/TemplateRender.cs index e2a0d029..2124da16 100644 --- a/src/Infrastructure/BotSharp.Core/Templating/TemplateRender.cs +++ b/src/Infrastructure/BotSharp.Core/Templating/TemplateRender.cs @@ -4,6 +4,7 @@ using BotSharp.Abstraction.Templating; using BotSharp.Abstraction.Translation.Models; using Fluid; using Fluid.Ast; +using Fluid.Values; using System.Collections; using System.IO; using System.Reflection; @@ -37,6 +38,8 @@ public class TemplateRender : ITemplateRender _options.MemberAccessStrategy.Register(); _options.MemberAccessStrategy.Register(); + _options.Filters.AddFilter("from_agent", FromAgentFilter); + _parser.RegisterExpressionTag("link", (Expression expression, TextWriter writer, TextEncoder encoder, TemplateContext context) => { return RenderTag("link", expression, writer, encoder, context, services); @@ -99,7 +102,7 @@ public class TemplateRender : ITemplateRender value = await context.Model.GetValueAsync(TemplateRenderConstant.RENDER_AGENT, context); var agent = value?.ToObjectValue() as Agent; - var splited = Regex.Split(expStr, @"\s*from\s*", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant) + var splited = Regex.Split(expStr, @"\s*from_agent\s*", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant) .Where(x => !string.IsNullOrWhiteSpace(x)) .Select(x => x.Trim()) .ToArray(); @@ -142,6 +145,16 @@ public class TemplateRender : ITemplateRender return Completion.Normal; } + private static ValueTask FromAgentFilter( + FluidValue input, + FilterArguments arguments, + TemplateContext context) + { + var inputStr = input?.ToStringValue() ?? string.Empty; + var fromAgent = arguments.At(0).ToStringValue(); + return new StringValue($"{inputStr} from_agent {fromAgent}"); + } + private static bool IsStringType(Type type) { return type == typeof(string); From ee491369b0126f77f1e04eaeb5606756b60e8305 Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Wed, 23 Jul 2025 15:29:44 -0500 Subject: [PATCH 5/6] update router instruction --- .../instructions/instruction.liquid | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/instructions/instruction.liquid b/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/instructions/instruction.liquid index 775de66c..341557bb 100644 --- a/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/instructions/instruction.liquid +++ b/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/instructions/instruction.liquid @@ -5,7 +5,8 @@ Follow these steps to handle user request: 2. Determine which agent is suitable to handle this conversation. Try to minimize the routing of human service. 3. Extract and populate agent required arguments, think carefully, leave it as blank object if user didn't provide the specific arguments. 4. You must include all required args for the selected agent, but you must not make up any parameters when there is no exact value provided, those parameters must set value as null if not declared. -5. If user is greeting, you can call function response_to_user with a greeting message. +5. Call function route_to_agent if user have specific requests, let agent handle and fill the required args, do not ask user to provide any args by yourself. +6. If user is greeting or do not have specific request, you can call function response_to_user with a greeting message. {% if routing_requirements and routing_requirements != empty %} [REQUIREMENTS] From 27dc42df8a058c23807afd1a8b348a6bfccbcc37 Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Wed, 23 Jul 2025 15:40:18 -0500 Subject: [PATCH 6/6] update router instruction --- .../instructions/instruction.liquid | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/instructions/instruction.liquid b/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/instructions/instruction.liquid index 341557bb..1ada1850 100644 --- a/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/instructions/instruction.liquid +++ b/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/instructions/instruction.liquid @@ -5,8 +5,8 @@ Follow these steps to handle user request: 2. Determine which agent is suitable to handle this conversation. Try to minimize the routing of human service. 3. Extract and populate agent required arguments, think carefully, leave it as blank object if user didn't provide the specific arguments. 4. You must include all required args for the selected agent, but you must not make up any parameters when there is no exact value provided, those parameters must set value as null if not declared. -5. Call function route_to_agent if user have specific requests, let agent handle and fill the required args, do not ask user to provide any args by yourself. -6. If user is greeting or do not have specific request, you can call function response_to_user with a greeting message. +5. Call function route_to_agent if user have specific requests and available agent to proceed. Do not ask user to provide any required args by yourself. The requested agent will handle and fill the required args internally. +6. If user is greeting or do not have specific request, then you can call function response_to_user with a greeting message. {% if routing_requirements and routing_requirements != empty %} [REQUIREMENTS]