Skip to content

Commit 110711f

Browse files
committed
Sanitize local function names in tools
1 parent 187745c commit 110711f

File tree

3 files changed

+42
-4
lines changed

3 files changed

+42
-4
lines changed

readme.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,11 @@ else
264264
}
265265
```
266266

267+
> [!IMPORTANT]
268+
> The `ToolFactory` will also automatically sanitize the tool name
269+
> when using local functions to avoid invalid characters and honor
270+
> its original name.
271+
267272
## Console Logging
268273

269274
Additional `UseJsonConsoleLogging` extension for rich JSON-formatted console logging of AI requests

src/AI.Tests/ToolsTests.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,16 @@ public class ToolsTests(ITestOutputHelper output)
99
{
1010
public record ToolResult(string Name, string Description, string Content);
1111

12+
[Fact]
13+
public void SanitizesToolName()
14+
{
15+
static void DoSomething() { }
16+
17+
var tool = ToolFactory.Create(DoSomething);
18+
19+
Assert.Equal("do_something", tool.Name);
20+
}
21+
1222
[SecretsFact("OPENAI_API_KEY")]
1323
public async Task RunToolResult()
1424
{

src/AI/ToolFactory.cs

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,37 @@ namespace Devlooped.Extensions.AI;
66
/// Creates tools for function calling that can leverage the <see cref="ToolExtensions"/>
77
/// extension methods for locating invocations and their results.
88
/// </summary>
9-
public static class ToolFactory
9+
public static partial class ToolFactory
1010
{
1111
/// <summary>
1212
/// Invokes <see cref="AIFunctionFactory.Create(Delegate, string?, string?, System.Text.Json.JsonSerializerOptions?)"/>
13-
/// using the method name following the naming convention and serialization options from <see cref="ToolJsonOptions.Default"/>.
13+
/// using the method name following the naming convention and serialization options from <see cref="ToolJsonOptions.Default"/>
14+
/// so that <c>FindCalls</c> extension methods on <see cref="ChatResponse"/> can be used.
1415
/// </summary>
15-
public static AIFunction Create(Delegate method)
16+
public static AIFunction Create(Delegate method, string? name = default)
1617
=> AIFunctionFactory.Create(method,
17-
ToolJsonOptions.Default.PropertyNamingPolicy!.ConvertName(method.Method.Name),
18+
name ?? ToolJsonOptions.Default.PropertyNamingPolicy!.ConvertName(SanitizeName(method.Method.Name)),
1819
serializerOptions: ToolJsonOptions.Default);
20+
21+
static string SanitizeName(string name)
22+
{
23+
if (!name.Contains('<'))
24+
return name;
25+
26+
// i.e.: <GetResponsesAsync>g__SetCandidates|0 > SetCandidates
27+
var match = AnonymousMethodExpr().Match(name);
28+
if (match.Success)
29+
{
30+
return match.Groups[1].Value;
31+
}
32+
33+
return name
34+
.Replace("<", string.Empty)
35+
.Replace(">", string.Empty)
36+
.Replace("g__", "_")
37+
.Replace("|", string.Empty);
38+
}
39+
40+
[System.Text.RegularExpressions.GeneratedRegex(@"__(.+?)\|")]
41+
private static partial System.Text.RegularExpressions.Regex AnonymousMethodExpr();
1942
}

0 commit comments

Comments
 (0)