From 3730db3e941df03aee238dd86ba2ef940351a359 Mon Sep 17 00:00:00 2001 From: SergeyMenshykh <68852919+SergeyMenshykh@users.noreply.github.com> Date: Fri, 10 Oct 2025 17:49:56 +0100 Subject: [PATCH] .NET: Sanitize agent name (#1368) * sanitize agent name * simplify * Update dotnet/src/Microsoft.Agents.AI/AgentExtensions.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update dotnet/src/Microsoft.Agents.AI/AgentExtensions.cs Co-authored-by: Stephen Toub * Update dotnet/src/Microsoft.Agents.AI/AgentExtensions.cs Co-authored-by: Stephen Toub * Update dotnet/tests/Microsoft.Agents.AI.UnitTests/AgentExtensionsTests.cs Co-authored-by: Stephen Toub * change regex to flag underscores as well so their sequence can be replaced with a single one. --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: Stephen Toub --- .../Microsoft.Agents.AI/AgentExtensions.cs | 28 +++++++++++++++++-- .../AgentExtensionsTests.cs | 25 +++++++++++++++++ 2 files changed, 51 insertions(+), 2 deletions(-) diff --git a/dotnet/src/Microsoft.Agents.AI/AgentExtensions.cs b/dotnet/src/Microsoft.Agents.AI/AgentExtensions.cs index e58fbf4920..097b789a84 100644 --- a/dotnet/src/Microsoft.Agents.AI/AgentExtensions.cs +++ b/dotnet/src/Microsoft.Agents.AI/AgentExtensions.cs @@ -2,6 +2,7 @@ using System; using System.ComponentModel; +using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.AI; @@ -12,7 +13,7 @@ namespace Microsoft.Agents.AI; /// /// Provides extensions for . /// -public static class AIAgentExtensions +public static partial class AIAgentExtensions { /// /// Creates a new using the specified agent as the foundation for the builder pipeline. @@ -77,9 +78,32 @@ public static class AIAgentExtensions } options ??= new(); - options.Name ??= agent.Name; + options.Name ??= SanitizeAgentName(agent.Name); options.Description ??= agent.Description; return AIFunctionFactory.Create(InvokeAgentAsync, options); } + + /// + /// Removes characters from AI agent name that shouldn't be used in an AI function name. + /// + /// The AI agent name to sanitize. + /// + /// The sanitized agent name with invalid characters replaced by underscores, or null if the input is null. + /// + private static string? SanitizeAgentName(string? agentName) + { + return agentName is null + ? agentName + : InvalidNameCharsRegex().Replace(agentName, "_"); + } + + /// Regex that flags any character other than ASCII digits or letters. +#if NET + [GeneratedRegex("[^0-9A-Za-z]+")] + private static partial Regex InvalidNameCharsRegex(); +#else + private static Regex InvalidNameCharsRegex() => s_invalidNameCharsRegex; + private static readonly Regex s_invalidNameCharsRegex = new("[^0-9A-Za-z]+", RegexOptions.Compiled); +#endif } diff --git a/dotnet/tests/Microsoft.Agents.AI.UnitTests/AgentExtensionsTests.cs b/dotnet/tests/Microsoft.Agents.AI.UnitTests/AgentExtensionsTests.cs index f7ad1ebcdc..f2b2bcfd6a 100644 --- a/dotnet/tests/Microsoft.Agents.AI.UnitTests/AgentExtensionsTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.UnitTests/AgentExtensionsTests.cs @@ -277,6 +277,31 @@ public class AgentExtensionsTests Assert.Equal("Complex response", result.ToString()); } + [Theory] + [InlineData("MyAgent", "MyAgent")] + [InlineData("Agent123", "Agent123")] + [InlineData("Agent_With_Underscores", "Agent_With_Underscores")] + [InlineData("Agent_With_________@@@@_Underscores", "Agent_With_Underscores")] + [InlineData("123Agent", "123Agent")] + [InlineData("My-Agent", "My_Agent")] + [InlineData("My Agent", "My_Agent")] + [InlineData("Agent@123", "Agent_123")] + [InlineData("Agent/With\\Slashes", "Agent_With_Slashes")] + [InlineData("Agent.With.Dots", "Agent_With_Dots")] + public void CreateFromAgent_SanitizesAgentName(string agentName, string expectedFunctionName) + { + // Arrange + var mockAgent = new Mock(); + mockAgent.Setup(a => a.Name).Returns(agentName); + + // Act + var result = mockAgent.Object.AsAIFunction(); + + // Assert + Assert.NotNull(result); + Assert.Equal(expectedFunctionName, result.Name); + } + /// /// Test implementation of AIAgent for testing purposes. ///