Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Oct 10, 2025

Summary

Fixed local function name cleanup for AIFunctionFactory, ensuring both lambdas and local functions have unique names.

Changes Made

1. Enhanced SanitizeMemberName in AIFunctionFactory.cs

  • Local functions: <Method>g__LocalFunc|x_yMethod_LocalFunc_x_y (keeps ordinal for uniqueness)
  • Lambdas: <Method>b__x_yMethod_x_y (captures full ordinal suffix for uniqueness)
  • Regular methods: unchanged
  • Fallback: replace non-alphanumeric with underscores
  • Consolidated two separate regexes into a single more general pattern using \w__ for future-proofing

2. Updated Lambda and Local Function Name Handling

  • Changed from two regexes (LocalFunctionNameRegex and LambdaNameRegex) to a single CompilerGeneratedNameRegex
  • Pattern: ^<([^>]+)>\w__(.+) matches both local functions and lambdas
  • More future-proof by using \w instead of hardcoding specific letters
  • Keeps ordinal suffix for both local functions and lambdas to prevent name collisions
  • Enhanced "Async" suffix removal to handle cases where ordinal follows "Async" (e.g., FetchDataAsync_0_0FetchData_0_0)
  • Fixed compatibility with netstandard2.0 and net462 by using conditional compilation for string operations

3. Updated Tests

  • Changed all local function and lambda name assertions to use Assert.StartsWith since ordinals vary
  • Improved parameter names in LocalFunction_WithParameters test from a, b to firstNumber, secondNumber to avoid false positives in schema validation
  • Updated assertions to verify uniqueness using Assert.NotEqual
  • Added comments to empty test methods to satisfy analyzer requirements (S1186, S108)

Validation Results

✅ All 346 tests passed
✅ Multiple lambdas in same method now get unique names
✅ Multiple local functions with same name in nested scopes now get unique names
✅ No regressions in existing functionality
✅ Build succeeded with 0 warnings and 0 errors (including analyzer checks)
Compatible with all target frameworks including netstandard2.0 and net462

Addresses Review Feedback

  • Fixed @stephentoub's concern that multiple lambdas in the same method must have different names to allow the LLM to distinguish which lambda is being requested
  • Addressed potential ambiguity with local functions by keeping ordinal suffixes (nested scopes can have same function names with different ordinals)
  • Fixed analyzer errors (S1186, S108) by adding explanatory comments to empty test method bodies
  • Changed assertions to use Assert.StartsWith for more robust testing with compiler-generated ordinals
  • Consolidated two regexes into one more general pattern for better future-proofing
  • Improved test parameter names to avoid false positives in schema validation
  • Fixed build errors for older target frameworks (netstandard2.0, net462) by using appropriate API overloads

Fixes #6677

Original prompt

This section details on the original issue you should resolve

<issue_title>Local functions used as tool functions could use name cleanup</issue_title>
<issue_description>### Description

There's a bunch of .NET reflection-originating chars in a tool name when it's created from a local function. It would be nice to to additional cleanup to get cleaner tool name by default.

Reproduction Steps

    [Fact]
    public void DefaultToolName()
    {
        static void DoSomething() { }

        var tool = AIFunctionFactory.Create(DoSomething);

        // renders _DefaultToolName_g__DoSomething_4_0
        Assert.Equal("DefaultToolName_DoSomething", tool.Name);
    }

Expected behavior

Since there can't be two members with the same name (neither at the method as well as the local function level), the name could just be DefaultToolName_DoSomething.

Actual behavior

Returns _DefaultToolName_g__DoSomething_4_0

Regression?

No response

Known Workarounds

Set name explicitly

Configuration

net10 preview 6, MEAI 9.7.0

Other information

No response</issue_description>

<agent_instructions>Investigate whether there's a simple approach that employs a simple but robust transformation while also ensuring we don't inadvertently end up with two things with the same name. Any solution should involve a variety of tests that exercise local functions, lambdas, multiple in the same method, etc.</agent_instructions>

Comments on the Issue (you are @copilot in this section)

Fixes #6677


✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

Microsoft Reviewers: Open in CodeFlow

Copilot AI changed the title [WIP] Clean up tool function names for clarity Clean up local function names in AIFunctionFactory Oct 10, 2025
Copilot AI requested a review from stephentoub October 10, 2025 00:45
Copilot finished work on behalf of stephentoub October 10, 2025 00:45
@stephentoub
Copy link
Member

@copilot, there are also build failures in the tests from analyzers:

/mnt/vss/_work/1/s/test/Libraries/Microsoft.Extensions.AI.Tests/Functions/AIFunctionFactoryTest.cs(1047,21): error S1186: Add a nested comment explaining why this method is empty, throw a 'NotSupportedException' or complete the implementation. (https://rules.sonarsource.com/csharp/RSPEC-1186) [/mnt/vss/_work/1/s/test/Libraries/Microsoft.Extensions.AI.Tests/Microsoft.Extensions.AI.Tests.csproj::TargetFramework=net9.0]
/mnt/vss/_work/1/s/test/Libraries/Microsoft.Extensions.AI.Tests/Functions/AIFunctionFactoryTest.cs(1048,9): error S108: Either remove or fill this block of code. (https://rules.sonarsource.com/csharp/RSPEC-108) [/mnt/vss/_work/1/s/test/Libraries/Microsoft.Extensions.AI.Tests/Microsoft.Extensions.AI.Tests.csproj::TargetFramework=net9.0]
/mnt/vss/_work/1/s/test/Libraries/Microsoft.Extensions.AI.Tests/Functions/AIFunctionFactoryTest.cs(1060,21): error S1186: Add a nested comment explaining why this method is empty, throw a 'NotSupportedException' or complete the implementation. (https://rules.sonarsource.com/csharp/RSPEC-1186) [/mnt/vss/_work/1/s/test/Libraries/Microsoft.Extensions.AI.Tests/Microsoft.Extensions.AI.Tests.csproj::TargetFramework=net9.0]
/mnt/vss/_work/1/s/test/Libraries/Microsoft.Extensions.AI.Tests/Functions/AIFunctionFactoryTest.cs(1064,21): error S1186: Add a nested comment explaining why this method is empty, throw a 'NotSupportedException' or complete the implementation. (https://rules.sonarsource.com/csharp/RSPEC-1186) [/mnt/vss/_work/1/s/test/Libraries/Microsoft.Extensions.AI.Tests/Microsoft.Extensions.AI.Tests.csproj::TargetFramework=net9.0]
/mnt/vss/_work/1/s/test/Libraries/Microsoft.Extensions.AI.Tests/Functions/AIFunctionFactoryTest.cs(1061,9): error S108: Either remove or fill this block of code. (https://rules.sonarsource.com/csharp/RSPEC-108) [/mnt/vss/_work/1/s/test/Libraries/Microsoft.Extensions.AI.Tests/Microsoft.Extensions.AI.Tests.csproj::TargetFramework=net9.0]
/mnt/vss/_work/1/s/test/Libraries/Microsoft.Extensions.AI.Tests/Functions/AIFunctionFactoryTest.cs(1065,9): error S108: Either remove or fill this block of code. (https://rules.sonarsource.com/csharp/RSPEC-108) [/mnt/vss/_work/1/s/test/Libraries/Microsoft.Extensions.AI.Tests/Microsoft.Extensions.AI.Tests.csproj::TargetFramework=net9.0]
/mnt/vss/_work/1/s/test/Libraries/Microsoft.Extensions.AI.Tests/Functions/AIFunctionFactoryTest.cs(1143,21): error S1186: Add a nested comment explaining why this method is empty, throw a 'NotSupportedException' or complete the implementation. (https://rules.sonarsource.com/csharp/RSPEC-1186) [/mnt/vss/_work/1/s/test/Libraries/Microsoft.Extensions.AI.Tests/Microsoft.Extensions.AI.Tests.csproj::TargetFramework=net9.0]
/mnt/vss/_work/1/s/test/Libraries/Microsoft.Extensions.AI.Tests/Functions/AIFunctionFactoryTest.cs(1144,9): error S108: Either remove or fill this block of code. (https://rules.sonarsource.com/csharp/RSPEC-108) [/mnt/vss/_work/1/s/test/Libraries/Microsoft.Extensions.AI.Tests/Microsoft.Extensions.AI.Tests.csproj::TargetFramework=net9.0]
/mnt/vss/_work/1/s/test/Libraries/Microsoft.Extensions.AI.Tests/Functions/AIFunctionFactoryTest.cs(1047,21): error S1186: Add a nested comment explaining why this method is empty, throw a 'NotSupportedException' or complete the implementation. (https://rules.sonarsource.com/csharp/RSPEC-1186) [/mnt/vss/_work/1/s/test/Libraries/Microsoft.Extensions.AI.Tests/Microsoft.Extensions.AI.Tests.csproj::TargetFramework=net8.0]
/mnt/vss/_work/1/s/test/Libraries/Microsoft.Extensions.AI.Tests/Functions/AIFunctionFactoryTest.cs(1048,9): error S108: Either remove or fill this block of code. (https://rules.sonarsource.com/csharp/RSPEC-108) [/mnt/vss/_work/1/s/test/Libraries/Microsoft.Extensions.AI.Tests/Microsoft.Extensions.AI.Tests.csproj::TargetFramework=net8.0]
/mnt/vss/_work/1/s/test/Libraries/Microsoft.Extensions.AI.Tests/Functions/AIFunctionFactoryTest.cs(1060,21): error S1186: Add a nested comment explaining why this method is empty, throw a 'NotSupportedException' or complete the implementation. (https://rules.sonarsource.com/csharp/RSPEC-1186) [/mnt/vss/_work/1/s/test/Libraries/Microsoft.Extensions.AI.Tests/Microsoft.Extensions.AI.Tests.csproj::TargetFramework=net8.0]
/mnt/vss/_work/1/s/test/Libraries/Microsoft.Extensions.AI.Tests/Functions/AIFunctionFactoryTest.cs(1064,21): error S1186: Add a nested comment explaining why this method is empty, throw a 'NotSupportedException' or complete the implementation. (https://rules.sonarsource.com/csharp/RSPEC-1186) [/mnt/vss/_work/1/s/test/Libraries/Microsoft.Extensions.AI.Tests/Microsoft.Extensions.AI.Tests.csproj::TargetFramework=net8.0]
/mnt/vss/_work/1/s/test/Libraries/Microsoft.Extensions.AI.Tests/Functions/AIFunctionFactoryTest.cs(1061,9): error S108: Either remove or fill this block of code. (https://rules.sonarsource.com/csharp/RSPEC-108) [/mnt/vss/_work/1/s/test/Libraries/Microsoft.Extensions.AI.Tests/Microsoft.Extensions.AI.Tests.csproj::TargetFramework=net8.0]
/mnt/vss/_work/1/s/test/Libraries/Microsoft.Extensions.AI.Tests/Functions/AIFunctionFactoryTest.cs(1065,9): error S108: Either remove or fill this block of code. (https://rules.sonarsource.com/csharp/RSPEC-108) [/mnt/vss/_work/1/s/test/Libraries/Microsoft.Extensions.AI.Tests/Microsoft.Extensions.AI.Tests.csproj::TargetFramework=net8.0]
/mnt/vss/_work/1/s/test/Libraries/Microsoft.Extensions.AI.Tests/Functions/AIFunctionFactoryTest.cs(1143,21): error S1186: Add a nested comment explaining why this method is empty, throw a 'NotSupportedException' or complete the implementation. (https://rules.sonarsource.com/csharp/RSPEC-1186) [/mnt/vss/_work/1/s/test/Libraries/Microsoft.Extensions.AI.Tests/Microsoft.Extensions.AI.Tests.csproj::TargetFramework=net8.0]
/mnt/vss/_work/1/s/test/Libraries/Microsoft.Extensions.AI.Tests/Functions/AIFunctionFactoryTest.cs(1144,9): error S108: Either remove or fill this block of code. (https://rules.sonarsource.com/csharp/RSPEC-108) [/mnt/vss/_work/1/s/test/Libraries/Microsoft.Extensions.AI.Tests/Microsoft.Extensions.AI.Tests.csproj::TargetFramework=net8.0]
    0 Warning(s)
    16 Error(s)

Please make sure build is successful and all tests successfully complete before declaring success.

Copilot finished work on behalf of stephentoub October 10, 2025 01:21
Copilot AI requested a review from stephentoub October 10, 2025 01:21
@stephentoub stephentoub marked this pull request as ready for review October 10, 2025 01:27
@stephentoub stephentoub requested a review from a team as a code owner October 10, 2025 01:27
Copilot AI review requested due to automatic review settings October 10, 2025 01:27
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR enhances the SanitizeMemberName method in AIFunctionFactory to provide cleaner names for local functions and lambdas used as AI tools. The changes ensure that compiler-generated names are transformed into more readable formats while maintaining uniqueness for multiple functions in the same method.

  • Enhanced SanitizeMemberName to handle local functions and lambdas with specific regex patterns
  • Added comprehensive tests covering various scenarios including multiple functions in the same method
  • Ensured lambda names retain complete ordinal suffixes for uniqueness

Reviewed Changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.

File Description
src/Libraries/Microsoft.Extensions.AI.Abstractions/Functions/AIFunctionFactory.cs Enhanced SanitizeMemberName method with regex patterns for local functions and lambdas, added new regex methods
test/Libraries/Microsoft.Extensions.AI.Tests/Functions/AIFunctionFactoryTest.cs Added comprehensive test coverage for local functions, lambdas, and edge cases with a helper static method

Copy link
Contributor Author

Copilot AI commented Oct 10, 2025

@copilot, there are also build failures in the tests from analyzers:

/mnt/vss/_work/1/s/test/Libraries/Microsoft.Extensions.AI.Tests/Functions/AIFunctionFactoryTest.cs(1047,21): error S1186: Add...

Fixed in commit 3d7e842. Added explanatory comments to all empty test method bodies (local functions and lambdas) to satisfy analyzer requirements S1186 and S108. Build now succeeds with 0 warnings and 0 errors, and all 346 tests pass.

Copilot finished work on behalf of stephentoub October 10, 2025 01:29
@stephentoub stephentoub added the area-ai Microsoft.Extensions.AI libraries label Oct 10, 2025
@stephentoub
Copy link
Member

@jaredpar, I don't love taking a dependency here on the compiler's implementation detail around naming. Am I right to be nervous, or is what this PR is doing reasonable?

@eiriktsarpalis
Copy link
Member

Am I right to be nervous, or is what this PR is doing reasonable?

What's the worst thing that could happen if the naming convention changes? I take it it would just revert to using the original name.

@stephentoub
Copy link
Member

Am I right to be nervous, or is what this PR is doing reasonable?

What's the worst thing that could happen if the naming convention changes? I take it it would just revert to using the original name.

Probably... though if the compiler's scheme were to change in specific ways, in theory I think we could end up with some duplicated names in corner cases, which could lead to ambiguity in the names provided to the LLM. In practice, though, I don't think that would happen, and really a developer should be providing a better name explicitly.

Copilot AI requested a review from stephentoub October 10, 2025 16:10
Copilot finished work on behalf of stephentoub October 10, 2025 16:10
Copilot finished work on behalf of stephentoub October 10, 2025 16:20
Copilot AI requested a review from stephentoub October 10, 2025 16:32
Copilot finished work on behalf of stephentoub October 10, 2025 16:32
@stephentoub
Copy link
Member

Also, @copilot, there are build failures for netstandard2.0 / net462:

D:\a\_work\1\s\src\Libraries\Microsoft.Extensions.AI.Abstractions\Functions\AIFunctionFactory.cs(1120,53): error CS1503: Argument 2: cannot convert from 'System.StringComparison' to 'int' [D:\a\_work\1\s\src\Libraries\Microsoft.Extensions.AI.Abstractions\Microsoft.Extensions.AI.Abstractions.csproj::TargetFramework=netstandard2.0]
D:\a\_work\1\s\src\Libraries\Microsoft.Extensions.AI.Abstractions\Functions\AIFunctionFactory.cs(1120,53): error CS1503: Argument 2: cannot convert from 'System.StringComparison' to 'int' [D:\a\_work\1\s\src\Libraries\Microsoft.Extensions.AI.Abstractions\Microsoft.Extensions.AI.Abstractions.csproj::TargetFramework=net462]

Copy link
Contributor Author

Copilot AI commented Oct 10, 2025

Also, @copilot, there are build failures for netstandard2.0 / net462:

D:\a\_work\1\s\src\Libraries\Microsoft.Extensions.AI.Abstractions\Functions\AIFunctionFactory.cs(1120,53): error CS1503: Arg...

Fixed in commit 8b4c7e3. Used conditional compilation to handle the string operations differently for older frameworks. For netstandard2.0/net462, using IndexOf(string) without StringComparison parameter, and Substring concatenation instead of span-based operations. Added pragma suppressions for locale warnings since compiler-generated names are ASCII-only.

Copilot finished work on behalf of stephentoub October 10, 2025 18:31
@stephentoub stephentoub merged commit 4c8b35f into main Oct 10, 2025
6 checks passed
@stephentoub stephentoub deleted the copilot/cleanup-tool-function-names branch October 10, 2025 22:19
joperezr pushed a commit to joperezr/extensions that referenced this pull request Oct 14, 2025
* Initial plan

* Implement local function name cleanup for AIFunctionFactory

Co-authored-by: stephentoub <[email protected]>

* Fix lambda name uniqueness by capturing full ordinal suffix

Co-authored-by: stephentoub <[email protected]>

* Fix analyzer errors by adding comments to empty test methods

Co-authored-by: stephentoub <[email protected]>

* Change Assert.Contains to Assert.StartsWith for lambda name tests

Co-authored-by: stephentoub <[email protected]>

* Consolidate regexes and improve test parameter names

Co-authored-by: stephentoub <[email protected]>

* Keep ordinal suffix for local functions to ensure uniqueness

Co-authored-by: stephentoub <[email protected]>

* Fix build errors for netstandard2.0 and net462 targets

Co-authored-by: stephentoub <[email protected]>

* Cleanup

---------

Co-authored-by: copilot-swe-agent[bot] <[email protected]>
Co-authored-by: stephentoub <[email protected]>
Co-authored-by: Stephen Toub <[email protected]>
jeffhandley pushed a commit to jeffhandley/extensions that referenced this pull request Oct 21, 2025
* Initial plan

* Implement local function name cleanup for AIFunctionFactory

Co-authored-by: stephentoub <[email protected]>

* Fix lambda name uniqueness by capturing full ordinal suffix

Co-authored-by: stephentoub <[email protected]>

* Fix analyzer errors by adding comments to empty test methods

Co-authored-by: stephentoub <[email protected]>

* Change Assert.Contains to Assert.StartsWith for lambda name tests

Co-authored-by: stephentoub <[email protected]>

* Consolidate regexes and improve test parameter names

Co-authored-by: stephentoub <[email protected]>

* Keep ordinal suffix for local functions to ensure uniqueness

Co-authored-by: stephentoub <[email protected]>

* Fix build errors for netstandard2.0 and net462 targets

Co-authored-by: stephentoub <[email protected]>

* Cleanup

---------

Co-authored-by: copilot-swe-agent[bot] <[email protected]>
Co-authored-by: stephentoub <[email protected]>
Co-authored-by: Stephen Toub <[email protected]>
jeffhandley pushed a commit to jeffhandley/extensions that referenced this pull request Oct 21, 2025
* Initial plan

* Implement local function name cleanup for AIFunctionFactory

Co-authored-by: stephentoub <[email protected]>

* Fix lambda name uniqueness by capturing full ordinal suffix

Co-authored-by: stephentoub <[email protected]>

* Fix analyzer errors by adding comments to empty test methods

Co-authored-by: stephentoub <[email protected]>

* Change Assert.Contains to Assert.StartsWith for lambda name tests

Co-authored-by: stephentoub <[email protected]>

* Consolidate regexes and improve test parameter names

Co-authored-by: stephentoub <[email protected]>

* Keep ordinal suffix for local functions to ensure uniqueness

Co-authored-by: stephentoub <[email protected]>

* Fix build errors for netstandard2.0 and net462 targets

Co-authored-by: stephentoub <[email protected]>

* Cleanup

---------

Co-authored-by: copilot-swe-agent[bot] <[email protected]>
Co-authored-by: stephentoub <[email protected]>
Co-authored-by: Stephen Toub <[email protected]>
@github-actions github-actions bot locked and limited conversation to collaborators Nov 10, 2025
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-ai Microsoft.Extensions.AI libraries

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Local functions used as tool functions could use name cleanup

4 participants