.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 <stoub@microsoft.com>

* Update dotnet/src/Microsoft.Agents.AI/AgentExtensions.cs

Co-authored-by: Stephen Toub <stoub@microsoft.com>

* Update dotnet/tests/Microsoft.Agents.AI.UnitTests/AgentExtensionsTests.cs

Co-authored-by: Stephen Toub <stoub@microsoft.com>

* 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 <stoub@microsoft.com>
This commit is contained in:
SergeyMenshykh
2025-10-10 17:49:56 +01:00
committed by GitHub
Unverified
parent 00a124dae6
commit 3730db3e94
2 changed files with 51 additions and 2 deletions
@@ -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;
/// <summary>
/// Provides extensions for <see cref="AIAgent"/>.
/// </summary>
public static class AIAgentExtensions
public static partial class AIAgentExtensions
{
/// <summary>
/// Creates a new <see cref="AIAgentBuilder"/> 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);
}
/// <summary>
/// Removes characters from AI agent name that shouldn't be used in an AI function name.
/// </summary>
/// <param name="agentName">The AI agent name to sanitize.</param>
/// <returns>
/// The sanitized agent name with invalid characters replaced by underscores, or <c>null</c> if the input is <c>null</c>.
/// </returns>
private static string? SanitizeAgentName(string? agentName)
{
return agentName is null
? agentName
: InvalidNameCharsRegex().Replace(agentName, "_");
}
/// <summary>Regex that flags any character other than ASCII digits or letters.</summary>
#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
}
@@ -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<AIAgent>();
mockAgent.Setup(a => a.Name).Returns(agentName);
// Act
var result = mockAgent.Object.AsAIFunction();
// Assert
Assert.NotNull(result);
Assert.Equal(expectedFunctionName, result.Name);
}
/// <summary>
/// Test implementation of AIAgent for testing purposes.
/// </summary>