.NET: Align file tool descriptions with generated schema argument names - #7675
Open
Ruiming Zhao (uuzzrm) wants to merge 1 commit into
Open
.NET: Align file tool descriptions with generated schema argument names#7675Ruiming Zhao (uuzzrm) wants to merge 1 commit into
Ruiming Zhao (uuzzrm) wants to merge 1 commit into
Conversation
The file_access and file_memory tool descriptions referenced arguments by snake_case names (glob_pattern, old_string, new_string, replace_all) while AIFunctionFactory generates the tool schema from the camelCase C# parameter names. A model that follows the description ends up emitting argument names that never bind, so filters were silently dropped and replace calls failed for reasons unrelated to the file. Renamed the references to match the schema (globPattern, oldString, newString, replaceAll). The replace_lines descriptions are untouched: line_number and new_line are explicitly mapped via JsonPropertyName and are genuinely snake_case in the schema. Added regression tests asserting that each affected tool description and its generated schema agree on argument names for both providers.
Ruiming Zhao (uuzzrm)
requested review from
SergeyMenshykh,
chetantoshniwal,
Peter Ibekwe (peibekwe),
Roger Barreto (rogerbarreto) and
westey (westey-m)
as code owners
August 14, 2026 23:18
Ruiming Zhao (uuzzrm)
deployed
to
github-app-auth
August 14, 2026 23:19 — with
GitHub Actions
Active
Ruiming Zhao (uuzzrm)
deployed
to
github-app-auth
August 14, 2026 23:19 — with
GitHub Actions
Active
Ruiming Zhao (uuzzrm)
deployed
to
github-app-auth
August 14, 2026 23:19 — with
GitHub Actions
Active
Ruiming Zhao (uuzzrm)
deployed
to
github-app-auth
August 14, 2026 23:19 — with
GitHub Actions
Active
Contributor
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
Adds regression tests to ensure file tool descriptions reference the same argument names as the JSON schema generated by AIFunctionFactory, and updates tool [Description] strings to match the schema naming.
Changes:
- Added unit tests validating tool description argument names for FileAccess and FileMemory tools.
- Updated
[Description]attributes in FileAccess/FileMemory providers to use schema-aligned camelCase argument names (e.g.,globPattern,oldString). - Ensured explicitly-mapped snake_case argument names (via
JsonPropertyName) remain referenced as-is in descriptions.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| dotnet/tests/Microsoft.Agents.AI.UnitTests/Harness/FileMemory/FileMemoryToolDescriptionSchemaTests.cs | New tests asserting FileMemory tool descriptions match JSON schema parameter names. |
| dotnet/tests/Microsoft.Agents.AI.UnitTests/Harness/FileAccess/FileAccessToolDescriptionSchemaTests.cs | New tests asserting FileAccess tool descriptions match JSON schema parameter names. |
| dotnet/src/Microsoft.Agents.AI/Harness/FileMemory/FileMemoryProvider.cs | Updated tool descriptions to use schema argument names (globPattern, oldString, etc.). |
| dotnet/src/Microsoft.Agents.AI/Harness/FileAccess/FileAccessProvider.cs | Updated tool descriptions to use schema argument names (globPattern, oldString, etc.). |
💡 Add a code-review agent skill for context-aware, tailored reviews. Learn more in the docs.
Comment on lines
+71
to
+123
| private static async Task<AIFunction> GetToolAsync(string toolName) | ||
| { | ||
| // Arrange | ||
| var provider = new FileAccessProvider(new InMemoryAgentFileStore()); | ||
| var agent = new Mock<AIAgent>().Object; | ||
| var session = new ChatClientAgentSession(); | ||
| #pragma warning disable MAAI001 | ||
| var context = new AIContextProvider.InvokingContext(agent, session, new AIContext()); | ||
| #pragma warning restore MAAI001 | ||
|
|
||
| AIContext result = await provider.InvokingAsync(context); | ||
| return (AIFunction)result.Tools!.First(t => t is AIFunction f && f.Name == toolName); | ||
| } | ||
|
|
||
| private static List<string> GetSchemaArgumentNames(AIFunction tool) | ||
| { | ||
| var names = new List<string>(); | ||
| if (tool.JsonSchema is JsonElement schema) | ||
| { | ||
| CollectPropertyNames(schema, names); | ||
| } | ||
|
|
||
| return names.Distinct().ToList(); | ||
| } | ||
|
|
||
| private static void CollectPropertyNames(JsonElement element, List<string> names) | ||
| { | ||
| if (element.ValueKind == JsonValueKind.Object) | ||
| { | ||
| foreach (JsonProperty property in element.EnumerateObject()) | ||
| { | ||
| if (property.Name == "properties") | ||
| { | ||
| foreach (JsonProperty item in property.Value.EnumerateObject()) | ||
| { | ||
| names.Add(item.Name); | ||
| CollectPropertyNames(item.Value, names); | ||
| } | ||
| } | ||
| else | ||
| { | ||
| CollectPropertyNames(property.Value, names); | ||
| } | ||
| } | ||
| } | ||
| else if (element.ValueKind == JsonValueKind.Array) | ||
| { | ||
| foreach (JsonElement item in element.EnumerateArray()) | ||
| { | ||
| CollectPropertyNames(item, names); | ||
| } | ||
| } | ||
| } |
Comment on lines
+87
to
+122
| var names = new List<string>(); | ||
| if (tool.JsonSchema is JsonElement schema) | ||
| { | ||
| CollectPropertyNames(schema, names); | ||
| } | ||
|
|
||
| return names.Distinct().ToList(); | ||
| } | ||
|
|
||
| private static void CollectPropertyNames(JsonElement element, List<string> names) | ||
| { | ||
| if (element.ValueKind == JsonValueKind.Object) | ||
| { | ||
| foreach (JsonProperty property in element.EnumerateObject()) | ||
| { | ||
| if (property.Name == "properties") | ||
| { | ||
| foreach (JsonProperty item in property.Value.EnumerateObject()) | ||
| { | ||
| names.Add(item.Name); | ||
| CollectPropertyNames(item.Value, names); | ||
| } | ||
| } | ||
| else | ||
| { | ||
| CollectPropertyNames(property.Value, names); | ||
| } | ||
| } | ||
| } | ||
| else if (element.ValueKind == JsonValueKind.Array) | ||
| { | ||
| foreach (JsonElement item in element.EnumerateArray()) | ||
| { | ||
| CollectPropertyNames(item, names); | ||
| } | ||
| } |
| #pragma warning restore MAAI001 | ||
|
|
||
| AIContext result = await provider.InvokingAsync(context); | ||
| return (AIFunction)result.Tools!.First(t => t is AIFunction f && f.Name == toolName); |
Ruiming Zhao (uuzzrm)
deployed
to
github-app-auth
August 15, 2026 00:20 — with
GitHub Actions
Active
Ruiming Zhao (uuzzrm)
deployed
to
github-app-auth
August 15, 2026 00:20 — with
GitHub Actions
Active
Ruiming Zhao (uuzzrm)
deployed
to
github-app-auth
August 15, 2026 00:20 — with
GitHub Actions
Active
Ruiming Zhao (uuzzrm)
deployed
to
github-app-auth
August 15, 2026 00:21 — with
GitHub Actions
Active
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Motivation & Context
The
file_accessandfile_memorytool descriptions refer to their arguments by snake_case names (glob_pattern,old_string,new_string,replace_all) whileAIFunctionFactorygenerates the tool schema from the camelCase C# parameter names (globPattern,oldString,newString,replaceAll). A model that follows the description emits argument names that never bind, sofile_access_lsruns without its filter andfile_access_replacefails its own "old_string not found" check for reasons unrelated to the file.Description & Review Guide
What are the major changes?
Renamed the argument references in the
[Description]text of the affected tools so they match the generated schema:file_access_ls,file_memory_ls,file_memory_grep:glob_pattern→globPatternfile_access_replace,file_memory_replace:old_string/new_string/replace_all→oldString/newString/replaceAllThe
replace_linesdescriptions are intentionally left alone:line_numberandnew_lineare mapped explicitly via[JsonPropertyName]and are genuinely snake_case in the schema.Added
FileAccessToolDescriptionSchemaTestsandFileMemoryToolDescriptionSchemaTestsasserting each affected description and its generated schema agree on argument names, plus a guard for thereplace_linesnames.What is the impact of these changes?
Models that follow the descriptions will emit argument names that bind, so filtering and replace behave as described instead of silently dropping arguments.
What do you want reviewers to focus on?
That every affected tool is covered, and that the
replace_linesJsonPropertyNamenames were intentionally kept as-is.Related Issue
Fixes #7672
Contribution Checklist
breaking changelabel (or add "[BREAKING]" to the title prefix, before or after any language prefix) — a workflow keeps the label and title prefix in sync automatically.