Skip to content

Commit 2634f4b

Browse files
authored
.Net: Add fix for plugin naming collisions (#12371)
### Motivation and Context #12320 ### Description Add code to ensure that naming collisions are avoided when using ai context provider tools. ### Contribution Checklist <!-- Before submitting this PR, please make sure: --> - [ ] The code builds clean without any errors or warnings - [ ] The PR follows the [SK Contribution Guidelines](https://github.com/microsoft/semantic-kernel/blob/main/CONTRIBUTING.md) and the [pre-submission formatting script](https://github.com/microsoft/semantic-kernel/blob/main/CONTRIBUTING.md#development-scripts) raises no violations - [ ] All unit tests pass, and I have added new tests where possible - [ ] I didn't break anyone 😄
1 parent e6565e5 commit 2634f4b

File tree

3 files changed

+53
-2
lines changed

3 files changed

+53
-2
lines changed

dotnet/src/SemanticKernel.Core/CompatibilitySuppressions.xml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
11
<?xml version="1.0" encoding="utf-8"?>
22
<!-- https://learn.microsoft.com/dotnet/fundamentals/package-validation/diagnostic-ids -->
33
<Suppressions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
4+
<Suppression>
5+
<DiagnosticId>CP0002</DiagnosticId>
6+
<Target>M:Microsoft.SemanticKernel.AIContextExtensions.AddFromAIContext(System.Collections.Generic.ICollection{Microsoft.SemanticKernel.KernelPlugin},Microsoft.SemanticKernel.AIContext,System.String)</Target>
7+
<Left>lib/net8.0/Microsoft.SemanticKernel.Core.dll</Left>
8+
<Right>lib/net8.0/Microsoft.SemanticKernel.Core.dll</Right>
9+
<IsBaselineSuppression>true</IsBaselineSuppression>
10+
</Suppression>
411
<Suppression>
512
<DiagnosticId>CP0002</DiagnosticId>
613
<Target>M:Microsoft.SemanticKernel.Data.TextSearchProvider.#ctor(Microsoft.SemanticKernel.Data.ITextSearch,Microsoft.SemanticKernel.Data.TextSearchProviderOptions)</Target>
@@ -22,6 +29,13 @@
2229
<Right>lib/net8.0/Microsoft.SemanticKernel.Core.dll</Right>
2330
<IsBaselineSuppression>true</IsBaselineSuppression>
2431
</Suppression>
32+
<Suppression>
33+
<DiagnosticId>CP0002</DiagnosticId>
34+
<Target>M:Microsoft.SemanticKernel.AIContextExtensions.AddFromAIContext(System.Collections.Generic.ICollection{Microsoft.SemanticKernel.KernelPlugin},Microsoft.SemanticKernel.AIContext,System.String)</Target>
35+
<Left>lib/netstandard2.0/Microsoft.SemanticKernel.Core.dll</Left>
36+
<Right>lib/netstandard2.0/Microsoft.SemanticKernel.Core.dll</Right>
37+
<IsBaselineSuppression>true</IsBaselineSuppression>
38+
</Suppression>
2539
<Suppression>
2640
<DiagnosticId>CP0002</DiagnosticId>
2741
<Target>M:Microsoft.SemanticKernel.Data.TextSearchProvider.#ctor(Microsoft.SemanticKernel.Data.ITextSearch,Microsoft.SemanticKernel.Data.TextSearchProviderOptions)</Target>

dotnet/src/SemanticKernel.Core/Memory/AIContextExtensions.cs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,24 @@ public static class AIContextExtensions
1818
/// </summary>
1919
/// <param name="plugins">The plugins collection to register the <see cref="AIFunction"/> objects on.</param>
2020
/// <param name="aiContext">The <see cref="AIContext"/> to get plugins from.</param>
21-
/// <param name="pluginName">The name to give to the plugin.</param>
22-
public static void AddFromAIContext(this ICollection<KernelPlugin> plugins, AIContext aiContext, string pluginName)
21+
/// <param name="pluginName">The name to give to the plugin. This will be appended with _x where x is an ascending number, until a unique plugin name is found.</param>
22+
/// <returns>The chosen plugin name.</returns>
23+
public static string AddFromAIContext(this ICollection<KernelPlugin> plugins, AIContext aiContext, string pluginName)
2324
{
2425
if (aiContext.AIFunctions is { Count: > 0 })
2526
{
27+
var originalPluginName = pluginName;
28+
var counter = 1;
29+
30+
// Find a unique plugin name by appending a counter if necessary.
31+
while (plugins.Any(x => x.Name == pluginName))
32+
{
33+
pluginName = $"{originalPluginName}_{counter++}";
34+
}
35+
2636
plugins.AddFromFunctions(pluginName, aiContext.AIFunctions.Select(x => x.AsKernelFunction()));
2737
}
38+
39+
return pluginName;
2840
}
2941
}

dotnet/src/SemanticKernel.UnitTests/Memory/AIContextProviderTests.cs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// Copyright (c) Microsoft. All rights reserved.
22

3+
using System.Collections.Generic;
34
using System.Threading;
45
using System.Threading.Tasks;
56
using Microsoft.Extensions.AI;
@@ -64,4 +65,28 @@ public async Task ResumingAsyncBaseImplementationSucceeds()
6465
// Act & Assert.
6566
await mockPart.Object.ResumingAsync("threadId", CancellationToken.None);
6667
}
68+
69+
[Fact]
70+
public void ExtensionCanAddPluginsFromAIContextProvider()
71+
{
72+
var plugins = new List<KernelPlugin>();
73+
74+
var aiContext = new AIContext
75+
{
76+
AIFunctions = new List<AIFunction>
77+
{
78+
AIFunctionFactory.Create(() => Task.FromResult("Function1 Result"), "Function1"),
79+
AIFunctionFactory.Create(() => Task.FromResult("Function2 Result"), "Function2")
80+
}
81+
};
82+
83+
Assert.Equal("TestPlugin", plugins.AddFromAIContext(aiContext, "TestPlugin"));
84+
Assert.Equal("TestPlugin_1", plugins.AddFromAIContext(aiContext, "TestPlugin"));
85+
Assert.Equal("TestPlugin_2", plugins.AddFromAIContext(aiContext, "TestPlugin"));
86+
87+
Assert.Equal(3, plugins.Count);
88+
Assert.Contains(plugins, p => p.Name == "TestPlugin");
89+
Assert.Contains(plugins, p => p.Name == "TestPlugin_1");
90+
Assert.Contains(plugins, p => p.Name == "TestPlugin_2");
91+
}
6792
}

0 commit comments

Comments
 (0)