From d55b15903d5d2b7ec99d9a6a414b24f19179f718 Mon Sep 17 00:00:00 2001
From: Roger Barreto <19890735+rogerbarreto@users.noreply.github.com>
Date: Thu, 6 Nov 2025 16:04:31 +0000
Subject: [PATCH] .NET: AgentDefinition extensions method simplification
(#1967)
* Update extensions methods that accepts AgentDefinition type to not be restrictive
* Update Unit Tests
* Revert yarn/package-lock
* Revert yarn/package-lock
* Address copilot feedback
---
.../AgentsClientExtensions.cs | 149 ++++---
.../AgentsClientExtensionsTests.cs | 386 ++++++++++--------
2 files changed, 311 insertions(+), 224 deletions(-)
diff --git a/dotnet/src/Microsoft.Agents.AI.AzureAI/AgentsClientExtensions.cs b/dotnet/src/Microsoft.Agents.AI.AzureAI/AgentsClientExtensions.cs
index 48a2485bbf..1ded97f29a 100644
--- a/dotnet/src/Microsoft.Agents.AI.AzureAI/AgentsClientExtensions.cs
+++ b/dotnet/src/Microsoft.Agents.AI.AzureAI/AgentsClientExtensions.cs
@@ -279,8 +279,8 @@ public static class AgentsClientExtensions
return CreateAIAgent(
agentsClient,
name,
- new PromptAgentDefinition(model) { Instructions = instructions },
tools,
+ new PromptAgentDefinition(model) { Instructions = instructions },
creationOptions,
clientFactory,
openAIClientOptions,
@@ -323,8 +323,8 @@ public static class AgentsClientExtensions
return CreateAIAgentAsync(
agentsClient,
name,
- new PromptAgentDefinition(model) { Instructions = instructions },
tools,
+ new PromptAgentDefinition(model) { Instructions = instructions },
creationOptions,
clientFactory,
openAIClientOptions,
@@ -450,31 +450,106 @@ public static class AgentsClientExtensions
/// The client used to manage and interact with AI agents. Cannot be .
/// The name for the agent.
/// The definition that specifies the configuration and behavior of the agent to create. Cannot be .
- /// The tools to use when interacting with the agent, this is required when using prompt agent definitions with tools.
/// Settings that control the creation of the agent.
/// A factory function to customize the creation of the chat client used by the agent.
/// An optional for configuring the underlying OpenAI client.
- /// This defaults to true and indicates whether to enforce the presence of invocable tools when the AIAgent is created with an agent definition that uses them. Setting this to false will require manual handling of in-proc tool invocations by the caller.
/// A token to monitor for cancellation requests.
/// A instance that can be used to perform operations on the newly created agent.
/// Thrown when or is .
- /// When using prompt agent definitions with tools the parameter needs to be provided.
+ ///
+ /// When using this extension method with a the tools are only declarative and not invocable.
+ /// Invocation of any in-process tools will need to be handled manually.
+ ///
public static ChatClientAgent CreateAIAgent(
this AgentsClient agentsClient,
string name,
AgentDefinition agentDefinition,
- IList? tools = null,
AgentVersionCreationOptions? creationOptions = null,
Func? clientFactory = null,
OpenAIClientOptions? openAIClientOptions = null,
- bool requireInvocableTools = true,
CancellationToken cancellationToken = default)
{
Throw.IfNull(agentsClient);
Throw.IfNullOrWhitespace(name);
Throw.IfNull(agentDefinition);
- ValidateUsingToolsParameter(agentDefinition, tools);
+ var tools = (agentDefinition as PromptAgentDefinition)?.Tools.Select(t => t.AsAITool()).ToList();
+
+ return CreateAIAgent(
+ agentsClient,
+ name,
+ tools,
+ agentDefinition,
+ creationOptions,
+ clientFactory,
+ openAIClientOptions,
+ requireInvocableTools: false,
+ cancellationToken);
+ }
+
+ ///
+ /// Asynchronously creates a new AI agent using the specified agent definition and optional configuration
+ /// parameters.
+ ///
+ /// The client used to manage and interact with AI agents. Cannot be .
+ /// The name for the agent.
+ /// The definition that specifies the configuration and behavior of the agent to create. Cannot be .
+ /// Settings that control the creation of the agent.
+ /// A factory function to customize the creation of the chat client used by the agent.
+ /// An optional for configuring the underlying OpenAI client.
+ /// A token to monitor for cancellation requests.
+ /// A instance that can be used to perform operations on the newly created agent.
+ /// Thrown when or is .
+ ///
+ /// When using this extension method with a the tools are only declarative and not invocable.
+ /// Invocation of any in-process tools will need to be handled manually.
+ ///
+ public static Task CreateAIAgentAsync(
+ this AgentsClient agentsClient,
+ string name,
+ AgentDefinition agentDefinition,
+ AgentVersionCreationOptions? agentVersionCreationOptions = null,
+ Func? clientFactory = null,
+ OpenAIClientOptions? openAIClientOptions = null,
+ CancellationToken cancellationToken = default)
+ {
+ Throw.IfNullOrWhitespace(name);
+ Throw.IfNull(agentsClient);
+ Throw.IfNull(agentDefinition);
+
+ var tools = (agentDefinition as PromptAgentDefinition)?.Tools.Select(t => t.AsAITool()).ToList();
+
+ return CreateAIAgentAsync(
+ agentsClient,
+ name,
+ tools,
+ agentDefinition,
+ agentVersionCreationOptions,
+ clientFactory,
+ openAIClientOptions,
+ requireInvocableTools: false,
+ cancellationToken);
+ }
+
+ #region Private
+
+ private static ChatClientAgent CreateAIAgent(
+ this AgentsClient agentsClient,
+ string name,
+ IList? tools,
+ AgentDefinition agentDefinition,
+ AgentVersionCreationOptions? creationOptions,
+ Func? clientFactory,
+ OpenAIClientOptions? openAIClientOptions,
+ bool requireInvocableTools,
+ CancellationToken cancellationToken)
+ {
+ Throw.IfNull(agentsClient);
+ Throw.IfNullOrWhitespace(name);
+ Throw.IfNull(agentDefinition);
+
+ tools ??= (agentDefinition as PromptAgentDefinition)?.Tools.Select(t => t.AsAITool()).ToList();
+
ApplyToolsToAgentDefinition(agentDefinition, tools, requireInvocableTools);
AgentVersion agentVersion = agentsClient.CreateAgentVersion(name, agentDefinition, creationOptions, cancellationToken).Value;
@@ -488,38 +563,23 @@ public static class AgentsClientExtensions
requireInvocableTools);
}
- ///
- /// Asynchronously creates a new AI agent using the specified agent definition and optional configuration
- /// parameters.
- ///
- /// The client used to manage and interact with AI agents. Cannot be .
- /// The name for the agent.
- /// The definition that specifies the configuration and behavior of the agent to create. Cannot be .
- /// The tools to use when interacting with the agent. This is required when using prompt agent definitions with tools.
- /// Settings that control the creation of the agent.
- /// A factory function to customize the creation of the chat client used by the agent.
- /// An optional for configuring the underlying OpenAI client.
- /// This defaults to true and indicates whether to enforce the presence of invocable tools when the AIAgent is created with an agent definition that uses them. Setting this to false will require manual handling of in-proc tool invocations by the caller.
- /// A token to monitor for cancellation requests.
- /// A instance that can be used to perform operations on the newly created agent.
- /// Thrown when or is .
- /// When using prompt agent definitions with tools the parameter needs to be provided.
- public static async Task CreateAIAgentAsync(
+ private static async Task CreateAIAgentAsync(
this AgentsClient agentsClient,
string name,
+ IList? tools,
AgentDefinition agentDefinition,
- IList? tools = null,
- AgentVersionCreationOptions? agentVersionCreationOptions = null,
- Func? clientFactory = null,
- OpenAIClientOptions? openAIClientOptions = null,
- bool requireInvocableTools = true,
- CancellationToken cancellationToken = default)
+ AgentVersionCreationOptions? agentVersionCreationOptions,
+ Func? clientFactory,
+ OpenAIClientOptions? openAIClientOptions,
+ bool requireInvocableTools,
+ CancellationToken cancellationToken)
{
Throw.IfNullOrWhitespace(name);
Throw.IfNull(agentsClient);
Throw.IfNull(agentDefinition);
- ValidateUsingToolsParameter(agentDefinition, tools);
+ tools ??= (agentDefinition as PromptAgentDefinition)?.Tools.Select(t => t.AsAITool()).ToList();
+
ApplyToolsToAgentDefinition(agentDefinition, tools, requireInvocableTools);
AgentVersion agentVersion = await agentsClient.CreateAgentVersionAsync(name, agentDefinition, agentVersionCreationOptions, cancellationToken).ConfigureAwait(false);
@@ -533,8 +593,6 @@ public static class AgentsClientExtensions
requireInvocableTools);
}
- #region Private
-
/// This method creates an with the specified ChatClientAgentOptions.
private static ChatClientAgent CreateChatClientAgent(
AgentsClient agentsClient,
@@ -608,7 +666,8 @@ public static class AgentsClientExtensions
var matchingTool = tools?.FirstOrDefault(t =>
requireInvocableTools
? t is AIFunction tf && functionTool.FunctionName == tf.Name // When invocable tools are required, match only AIFunction.
- : t is AIFunctionDeclaration tfd && functionTool.FunctionName == tfd.Name); // Otherwise, matching only AIFunctionDeclaration is sufficient.
+ : (t is AIFunctionDeclaration tfd && functionTool.FunctionName == tfd.Name) ? true // When not required, match AIFunctionDeclaration OR
+ : (t.GetService() is FunctionTool ft && functionTool.FunctionName == ft.FunctionName)); // Match a FunctionTool converted AsAITool.
if (matchingTool is null)
{
@@ -680,20 +739,6 @@ public static class AgentsClientExtensions
return agentOptions;
}
- /// When creating agents, validates the parameter is used instead of providing via .
- /// The parameter should be used instead of .
- ///
- /// Because doesn't support in-proc tools (only declarative/definitions),
- /// the parameter needs to be the single source of truth for tools, and must be provided when using tools.
- ///
- private static void ValidateUsingToolsParameter(AgentDefinition agentDefinition, IList? tools)
- {
- if (agentDefinition is PromptAgentDefinition { Tools.Count: > 0 })
- {
- throw new ArgumentException("When creating agents with prompt agent definitions use the dedicated tools parameter to provide the necessary tools instead of PromptAgentDefinition.Tools.", nameof(tools));
- }
- }
-
/// For already created agent versions, retrieve the definition and validate the tools parameter.
/// cannot be used. The parameter should be used instead.
///
@@ -734,9 +779,11 @@ public static class AgentsClientExtensions
foreach (var tool in tools)
{
// Ensure that any AIFunctions provided are In-Proc, not just the declarations.
- if (requireInvocableTools && tool is AIFunctionDeclaration and not AIFunction)
+ if (requireInvocableTools && tool is not AIFunction && (
+ tool.GetService() is not null // Declarative FunctionTool converted as AsAITool()
+ || tool is AIFunctionDeclaration)) // AIFunctionDeclaration type
{
- throw new InvalidOperationException("When providing functions, they need to be invokable AIFunctions and not non-invokable AIFunctionDeclarations. AIFunctions can be created correctly using AIFunctionFactory.Create");
+ throw new InvalidOperationException("When providing functions, they need to be invokable AIFunctions. AIFunctions can be created correctly using AIFunctionFactory.Create");
}
promptAgentDefinition.Tools.Add(
diff --git a/dotnet/tests/Microsoft.Agents.AI.AzureAI.UnitTests/AgentsClientExtensionsTests.cs b/dotnet/tests/Microsoft.Agents.AI.AzureAI.UnitTests/AgentsClientExtensionsTests.cs
index f7ca437d69..497340b298 100644
--- a/dotnet/tests/Microsoft.Agents.AI.AzureAI.UnitTests/AgentsClientExtensionsTests.cs
+++ b/dotnet/tests/Microsoft.Agents.AI.AzureAI.UnitTests/AgentsClientExtensionsTests.cs
@@ -5,6 +5,7 @@ using System.ClientModel;
using System.ClientModel.Primitives;
using System.Collections.Generic;
using System.IO;
+using System.Linq;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
@@ -867,41 +868,17 @@ public sealed class AgentsClientExtensionsTests
#region Tool Validation Tests
///
- /// Verify that CreateAIAgent throws ArgumentException when agent definition contains inline tools.
+ /// Verify that CreateAIAgent creates an agent successfully.
///
[Fact]
- public void CreateAIAgent_WithInlineToolsInDefinition_ThrowsArgumentException()
+ public void CreateAIAgent_WithDefinition_CreatesAgentSuccessfully()
{
// Arrange
AgentsClient client = this.CreateTestAgentsClient();
- var definition = new PromptAgentDefinition("test-model");
- definition.Tools.Add(ResponseTool.CreateFunctionTool("inline_tool", BinaryData.FromString("{}"), strictModeEnabled: false));
-
- // Act & Assert
- var exception = Assert.Throws(() =>
- client.CreateAIAgent("test-agent", definition));
-
- Assert.Contains("dedicated tools parameter", exception.Message);
- }
-
- ///
- /// Verify that CreateAIAgent with requireInvocableTools=true enforces invocable tools.
- ///
- [Fact]
- public void CreateAIAgent_WithRequireInvocableToolsTrue_EnforcesInvocableTools()
- {
- // Arrange
var definition = new PromptAgentDefinition("test-model") { Instructions = "Test" };
- var tools = new List
- {
- AIFunctionFactory.Create(() => "test", "test_function", "A test function")
- };
-
- var definitionResponse = GeneratePromptDefinitionResponse(definition, tools);
- AgentsClient client = this.CreateTestAgentsClient(agentName: "test-agent", agentDefinitionResponse: definitionResponse);
// Act
- var agent = client.CreateAIAgent("test-agent", definition, tools: tools, requireInvocableTools: true);
+ var agent = client.CreateAIAgent("test-agent", definition);
// Assert
Assert.NotNull(agent);
@@ -909,17 +886,37 @@ public sealed class AgentsClientExtensionsTests
}
///
- /// Verify that CreateAIAgent with requireInvocableTools=false allows declarative functions.
+ /// Verify that CreateAIAgent without tools parameter creates an agent successfully.
///
[Fact]
- public void CreateAIAgent_WithRequireInvocableToolsFalse_AllowsDeclarativeFunctions()
+ public void CreateAIAgent_WithoutToolsParameter_CreatesAgentSuccessfully()
+ {
+ // Arrange
+ var definition = new PromptAgentDefinition("test-model") { Instructions = "Test" };
+
+ var definitionResponse = GeneratePromptDefinitionResponse(definition, null);
+ AgentsClient client = this.CreateTestAgentsClient(agentName: "test-agent", agentDefinitionResponse: definitionResponse);
+
+ // Act
+ var agent = client.CreateAIAgent("test-agent", definition);
+
+ // Assert
+ Assert.NotNull(agent);
+ Assert.IsType(agent);
+ }
+
+ ///
+ /// Verify that CreateAIAgent without tools in definition creates an agent successfully.
+ ///
+ [Fact]
+ public void CreateAIAgent_WithoutToolsInDefinition_CreatesAgentSuccessfully()
{
// Arrange
var definition = new PromptAgentDefinition("test-model") { Instructions = "Test" };
AgentsClient client = this.CreateTestAgentsClient(agentName: "test-agent", agentDefinitionResponse: definition);
- // Act - should not throw even without tools when requireInvocableTools is false
- var agent = client.CreateAIAgent("test-agent", definition, requireInvocableTools: false);
+ // Act
+ var agent = client.CreateAIAgent("test-agent", definition);
// Assert
Assert.NotNull(agent);
@@ -927,43 +924,23 @@ public sealed class AgentsClientExtensionsTests
}
///
- /// Verify that CreateAIAgent throws ArgumentException when agent definition has tools but none provided as parameter.
+ /// Verify that CreateAIAgent uses tools from the definition when no separate tools parameter is provided.
///
[Fact]
- public void CreateAIAgent_WithDefinitionToolsButNoToolsParameter_ThrowsArgumentException()
+ public void CreateAIAgent_WithDefinitionTools_UsesDefinitionTools()
{
// Arrange
- AgentsClient client = this.CreateTestAgentsClient();
var definition = new PromptAgentDefinition("test-model") { Instructions = "Test" };
- // Add a function tool to the definition to simulate a tool requirement
+ // Add a function tool to the definition
definition.Tools.Add(ResponseTool.CreateFunctionTool("required_tool", BinaryData.FromString("{}"), strictModeEnabled: false));
- // Act & Assert
- var exception = Assert.Throws(() =>
- client.CreateAIAgent("test-agent", definition, tools: null, requireInvocableTools: true));
-
- Assert.Contains("dedicated tools parameter", exception.Message);
- }
-
- ///
- /// Verify that CreateAIAgent with tools parameter applies tools to the agent definition.
- ///
- [Fact]
- public void CreateAIAgent_WithToolsParameter_AppliesToolsToDefinition()
- {
- // Arrange
- var definition = new PromptAgentDefinition("test-model");
- var tools = new List
- {
- AIFunctionFactory.Create(() => "test", "test_function", "A test function")
- };
-
- var agentDefinitionResponse = GeneratePromptDefinitionResponse(definition, tools);
- AgentsClient client = this.CreateTestAgentsClient(agentName: "test-agent", agentDefinitionResponse: agentDefinitionResponse);
+ // Create a response definition with the same tool
+ var definitionResponse = GeneratePromptDefinitionResponse(definition, definition.Tools.Select(t => t.AsAITool()).ToList());
+ AgentsClient client = this.CreateTestAgentsClient(agentName: "test-agent", agentDefinitionResponse: definitionResponse);
// Act
- var agent = client.CreateAIAgent("test-agent", definition, tools: tools);
+ var agent = client.CreateAIAgent("test-agent", definition);
// Assert
Assert.NotNull(agent);
@@ -974,9 +951,30 @@ public sealed class AgentsClientExtensionsTests
{
Assert.NotEmpty(promptDef.Tools);
Assert.Single(promptDef.Tools);
+ Assert.Equal("required_tool", (promptDef.Tools.First() as FunctionTool)?.FunctionName);
}
}
+ ///
+ /// Verify that CreateAIAgent without tools creates an agent successfully.
+ ///
+ [Fact]
+ public void CreateAIAgent_WithoutTools_CreatesAgentSuccessfully()
+ {
+ // Arrange
+ var definition = new PromptAgentDefinition("test-model");
+
+ var agentDefinitionResponse = GeneratePromptDefinitionResponse(definition, null);
+ AgentsClient client = this.CreateTestAgentsClient(agentName: "test-agent", agentDefinitionResponse: agentDefinitionResponse);
+
+ // Act
+ var agent = client.CreateAIAgent("test-agent", definition);
+
+ // Assert
+ Assert.NotNull(agent);
+ Assert.IsType(agent);
+ }
+
///
/// Verify that GetAIAgent with inline tools in agent definition throws ArgumentException.
///
@@ -1032,25 +1030,22 @@ public sealed class AgentsClientExtensionsTests
}
///
- /// Verify that CreateAIAgent with parameter tools creates an agent successfully.
+ /// Verify that CreateAIAgent with tools in definition creates an agent successfully.
///
[Fact]
- public void CreateAIAgent_WithParameterTools_CreatesAgentSuccessfully()
+ public void CreateAIAgent_WithDefinitionTools_CreatesAgentSuccessfully()
{
// Arrange
var definition = new PromptAgentDefinition("test-model") { Instructions = "Test instructions" };
- var tools = new List
- {
- AIFunctionFactory.Create(() => "result", "create_tool", "A tool for creation")
- };
+ definition.Tools.Add(ResponseTool.CreateFunctionTool("create_tool", BinaryData.FromString("{}"), strictModeEnabled: false));
// Simulate agent definition response with the tools
- var definitionResponse = GeneratePromptDefinitionResponse(definition, tools);
+ var definitionResponse = GeneratePromptDefinitionResponse(definition, definition.Tools.Select(t => t.AsAITool()).ToList());
AgentsClient client = this.CreateTestAgentsClient(agentDefinitionResponse: definitionResponse);
// Act
- var agent = client.CreateAIAgent("test-agent", definition, tools: tools);
+ var agent = client.CreateAIAgent("test-agent", definition);
// Assert
Assert.NotNull(agent);
@@ -1065,31 +1060,28 @@ public sealed class AgentsClientExtensionsTests
}
///
- /// Verify that CreateAIAgent creates an agent successfully when provided with a mix of custom and hosted tools.
+ /// Verify that CreateAIAgent creates an agent successfully when definition has a mix of custom and hosted tools.
///
[Fact]
- public void CreateAIAgent_WithMixedTools_CreatesAgentSuccessfully()
+ public void CreateAIAgent_WithMixedToolsInDefinition_CreatesAgentSuccessfully()
{
// Arrange
var definition = new PromptAgentDefinition("test-model") { Instructions = "Test instructions" };
- var tools = new List
- {
- AIFunctionFactory.Create(() => "result", "create_tool", "A tool for creation"),
- new HostedWebSearchTool(),
- new HostedFileSearchTool(),
- };
+ definition.Tools.Add(ResponseTool.CreateFunctionTool("create_tool", BinaryData.FromString("{}"), strictModeEnabled: false));
+ definition.Tools.Add(new HostedWebSearchTool().GetService() ?? new HostedWebSearchTool().AsOpenAIResponseTool());
+ definition.Tools.Add(new HostedFileSearchTool().GetService() ?? new HostedFileSearchTool().AsOpenAIResponseTool());
// Simulate agent definition response with the tools
var definitionResponse = new PromptAgentDefinition("test-model") { Instructions = "Test instructions" };
- foreach (var tool in tools)
+ foreach (var tool in definition.Tools)
{
- definitionResponse.Tools.Add(tool.GetService() ?? tool.AsOpenAIResponseTool());
+ definitionResponse.Tools.Add(tool);
}
AgentsClient client = this.CreateTestAgentsClient(agentDefinitionResponse: definitionResponse);
// Act
- var agent = client.CreateAIAgent("test-agent", definition, tools: tools);
+ var agent = client.CreateAIAgent("test-agent", definition);
// Assert
Assert.NotNull(agent);
@@ -1104,10 +1096,10 @@ public sealed class AgentsClientExtensionsTests
}
///
- /// Verifies that CreateAIAgent accepts tools provided as ResponseTool instances and correctly converts them to AITool instances, resulting in successful agent creation.
+ /// Verifies that CreateAIAgent uses tools from definition when they are ResponseTool instances, resulting in successful agent creation.
///
[Fact]
- public void CreateAIAgent_WithResponseToolsAsAITools_CreatesAgentSuccessfully()
+ public void CreateAIAgent_WithResponseToolsInDefinition_CreatesAgentSuccessfully()
{
// Arrange
var definition = new PromptAgentDefinition("test-model") { Instructions = "Test instructions" };
@@ -1123,33 +1115,25 @@ public sealed class AgentsClientExtensionsTests
["structured-1"] = BinaryData.FromString(AIJsonUtilities.CreateJsonSchema(new { id = "test" }.GetType()).ToString())
}, false);
- ResponseTool openAIResponseTool = (ResponseTool)new AzureAISearchAgentTool(new());
-
- var tools = new List
- {
- AIFunctionFactory.Create(() => "result", "create_tool", "A tool for creation"),
- ((ResponseTool)AgentTool.CreateBingCustomSearchTool(new BingCustomSearchToolParameters([new BingCustomSearchConfiguration("connection-id", "instance-name")]))).AsAITool(),
- ((ResponseTool)AgentTool.CreateBrowserAutomationTool(new BrowserAutomationToolParameters(new BrowserAutomationToolConnectionParameters("id")))).AsAITool(),
- AgentTool.CreateA2ATool(new Uri("https://test-uri.microsoft.com")).AsAITool(),
- ((ResponseTool)AgentTool.CreateBingGroundingTool(new BingGroundingSearchToolParameters([new BingGroundingSearchConfiguration("connection-id")]))).AsAITool(),
- ((ResponseTool)AgentTool.CreateMicrosoftFabricTool(fabricParameters)).AsAITool(),
- ((ResponseTool)AgentTool.CreateOpenApiTool(new OpenApiFunctionDefinition("name", BinaryData.FromString(OpenAPISpec), new OpenApiAnonymousAuthDetails()))).AsAITool(),
- ((ResponseTool)AgentTool.CreateSharepointTool(sharepointParameters)).AsAITool(),
- ((ResponseTool)AgentTool.CreateStructuredOutputsTool(structuredOutputs)).AsAITool(),
-
- // Workaround the bug with the AgentTool.CreateAzureAISearchTool() extension
- // Using the extension method AgentTool.CreateAzureAISearchTool() fails serialization,
- // TODO: Revert back once bug fix is applied: https://github.com/Azure/azure-sdk-for-net/pull/53656
- ((ResponseTool)new AzureAISearchAgentTool(new())).AsAITool()
- };
+ // Add tools to the definition
+ definition.Tools.Add(ResponseTool.CreateFunctionTool("create_tool", BinaryData.FromString("{}"), strictModeEnabled: false));
+ definition.Tools.Add((ResponseTool)AgentTool.CreateBingCustomSearchTool(new BingCustomSearchToolParameters([new BingCustomSearchConfiguration("connection-id", "instance-name")])));
+ definition.Tools.Add((ResponseTool)AgentTool.CreateBrowserAutomationTool(new BrowserAutomationToolParameters(new BrowserAutomationToolConnectionParameters("id"))));
+ definition.Tools.Add(AgentTool.CreateA2ATool(new Uri("https://test-uri.microsoft.com")));
+ definition.Tools.Add((ResponseTool)AgentTool.CreateBingGroundingTool(new BingGroundingSearchToolParameters([new BingGroundingSearchConfiguration("connection-id")])));
+ definition.Tools.Add((ResponseTool)AgentTool.CreateMicrosoftFabricTool(fabricParameters));
+ definition.Tools.Add((ResponseTool)AgentTool.CreateOpenApiTool(new OpenApiFunctionDefinition("name", BinaryData.FromString(OpenAPISpec), new OpenApiAnonymousAuthDetails())));
+ definition.Tools.Add((ResponseTool)AgentTool.CreateSharepointTool(sharepointParameters));
+ definition.Tools.Add((ResponseTool)AgentTool.CreateStructuredOutputsTool(structuredOutputs));
+ definition.Tools.Add((ResponseTool)new AzureAISearchAgentTool(new()));
// Generate agent definition response with the tools
- var definitionResponse = GeneratePromptDefinitionResponse(definition, tools);
+ var definitionResponse = GeneratePromptDefinitionResponse(definition, definition.Tools.Select(t => t.AsAITool()).ToList());
AgentsClient client = this.CreateTestAgentsClient(agentDefinitionResponse: definitionResponse);
// Act
- var agent = client.CreateAIAgent("test-agent", definition, tools: tools);
+ var agent = client.CreateAIAgent("test-agent", definition);
// Assert
Assert.NotNull(agent);
@@ -1199,21 +1183,18 @@ public sealed class AgentsClientExtensionsTests
}
///
- /// Verify that CreateAIAgentAsync with tools parameter creates an agent.
+ /// Verify that CreateAIAgentAsync with tools in definition creates an agent.
///
[Fact]
- public async Task CreateAIAgentAsync_WithToolsParameter_CreatesAgentAsync()
+ public async Task CreateAIAgentAsync_WithDefinitionTools_CreatesAgentAsync()
{
// Arrange
AgentsClient client = this.CreateTestAgentsClient();
var definition = new PromptAgentDefinition("test-model") { Instructions = "Test instructions" };
- var tools = new List
- {
- AIFunctionFactory.Create(() => "async_result", "async_tool", "An async tool")
- };
+ definition.Tools.Add(ResponseTool.CreateFunctionTool("async_tool", BinaryData.FromString("{}"), strictModeEnabled: false));
// Act
- var agent = await client.CreateAIAgentAsync("test-agent", definition, tools: tools);
+ var agent = await client.CreateAIAgentAsync("test-agent", definition);
// Assert
Assert.NotNull(agent);
@@ -1246,10 +1227,10 @@ public sealed class AgentsClientExtensionsTests
#region Declarative Function Handling Tests
///
- /// Verify that CreateAIAgent throws InvalidOperationException when provided with non-invocable AIFunctionDeclaration when requireInvocableTools=true.
+ /// Verify that CreateAIAgent accepts declarative functions from definition.
///
[Fact]
- public void CreateAIAgent_WithDeclarativeFunctionAndRequireInvocableTrue_ThrowsInvalidOperationException()
+ public void CreateAIAgent_WithDeclarativeFunctionInDefinition_AcceptsDeclarativeFunction()
{
// Arrange
AgentsClient client = this.CreateTestAgentsClient();
@@ -1259,38 +1240,11 @@ public sealed class AgentsClientExtensionsTests
using var doc = JsonDocument.Parse("{}");
var declarativeFunction = AIFunctionFactory.CreateDeclaration("test_function", "A test function", doc.RootElement);
- var tools = new List { declarativeFunction };
-
- // Act & Assert
- var exception = Assert.Throws(() =>
- client.CreateAIAgent("test-agent", definition, tools: tools, requireInvocableTools: true));
-
- Assert.Contains("invokable AIFunctions", exception.Message);
- }
-
- ///
- /// Verify that CreateAIAgent accepts declarative functions when requireInvocableTools=false.
- ///
- [Fact]
- public void CreateAIAgent_WithDeclarativeFunctionAndRequireInvocableFalse_AcceptsDeclarativeFunction()
- {
- // Arrange
- var definition = new PromptAgentDefinition("test-model") { Instructions = "Test" };
-
- // Create a declarative function (not invocable) using AIFunctionFactory.CreateDeclaration
- using var doc = JsonDocument.Parse("{}");
- var declarativeFunction = AIFunctionFactory.CreateDeclaration("test_function", "A test function", doc.RootElement);
-
- var tools = new List { declarativeFunction };
-
- // Generate response with the declarative function
- var definitionResponse = new PromptAgentDefinition("test-model") { Instructions = "Test" };
- definitionResponse.Tools.Add(declarativeFunction.AsOpenAIResponseTool() ?? throw new InvalidOperationException());
-
- AgentsClient client = this.CreateTestAgentsClient(agentName: "test-agent", agentDefinitionResponse: definitionResponse);
+ // Add to definition
+ definition.Tools.Add(declarativeFunction.AsOpenAIResponseTool() ?? throw new InvalidOperationException());
// Act
- var agent = client.CreateAIAgent("test-agent", definition, tools: tools, requireInvocableTools: false);
+ var agent = client.CreateAIAgent("test-agent", definition);
// Assert
Assert.NotNull(agent);
@@ -1298,10 +1252,104 @@ public sealed class AgentsClientExtensionsTests
}
///
- /// Verify that CreateAIAgentAsync throws InvalidOperationException when provided with non-invocable AIFunctionDeclaration when requireInvocableTools=true.
+ /// Verify that CreateAIAgent accepts declarative functions from definition.
///
[Fact]
- public async Task CreateAIAgentAsync_WithDeclarativeFunctionAndRequireInvocableTrue_ThrowsInvalidOperationExceptionAsync()
+ public void CreateAIAgent_WithDeclarativeFunctionFromDefinition_AcceptsDeclarativeFunction()
+ {
+ // Arrange
+ var definition = new PromptAgentDefinition("test-model") { Instructions = "Test" };
+
+ // Create a declarative function (not invocable) using AIFunctionFactory.CreateDeclaration
+ using var doc = JsonDocument.Parse("{}");
+ var declarativeFunction = AIFunctionFactory.CreateDeclaration("test_function", "A test function", doc.RootElement);
+
+ // Add to definition
+ definition.Tools.Add(declarativeFunction.AsOpenAIResponseTool() ?? throw new InvalidOperationException());
+
+ // Generate response with the declarative function
+ var definitionResponse = new PromptAgentDefinition("test-model") { Instructions = "Test" };
+ definitionResponse.Tools.Add(declarativeFunction.AsOpenAIResponseTool() ?? throw new InvalidOperationException());
+
+ AgentsClient client = this.CreateTestAgentsClient(agentName: "test-agent", agentDefinitionResponse: definitionResponse);
+
+ // Act
+ var agent = client.CreateAIAgent("test-agent", definition);
+
+ // Assert
+ Assert.NotNull(agent);
+ Assert.IsType(agent);
+ }
+
+ ///
+ /// Verify that CreateAIAgent accepts FunctionTools from definition.
+ ///
+ [Fact]
+ public void CreateAIAgent_WithFunctionToolsInDefinition_AcceptsDeclarativeFunction()
+ {
+ // Arrange
+ var functionTool = ResponseTool.CreateFunctionTool(
+ functionName: "get_user_name",
+ functionParameters: BinaryData.FromString("{}"),
+ strictModeEnabled: false,
+ functionDescription: "Gets the user's name, as used for friendly address."
+ );
+
+ var definition = new PromptAgentDefinition("test-model") { Instructions = "Test" };
+ definition.Tools.Add(functionTool);
+
+ // Generate response with the declarative function
+ var definitionResponse = new PromptAgentDefinition("test-model") { Instructions = "Test" };
+ definitionResponse.Tools.Add(functionTool);
+
+ AgentsClient client = this.CreateTestAgentsClient(agentName: "test-agent", agentDefinitionResponse: definitionResponse);
+
+ // Act
+ var agent = client.CreateAIAgent("test-agent", definition);
+
+ // Assert
+ Assert.NotNull(agent);
+ Assert.IsType(agent);
+ var definitionFromAgent = Assert.IsType(agent.GetService()?.Definition);
+ Assert.Single(definitionFromAgent.Tools);
+ }
+
+ ///
+ /// Verify that CreateAIAgentAsync accepts FunctionTools from definition.
+ ///
+ [Fact]
+ public async Task CreateAIAgentAsync_WithFunctionToolsInDefinition_AcceptsDeclarativeFunctionAsync()
+ {
+ // Arrange
+ var functionTool = ResponseTool.CreateFunctionTool(
+ functionName: "get_user_name",
+ functionParameters: BinaryData.FromString("{}"),
+ strictModeEnabled: false,
+ functionDescription: "Gets the user's name, as used for friendly address."
+ );
+
+ var definition = new PromptAgentDefinition("test-model") { Instructions = "Test" };
+ definition.Tools.Add(functionTool);
+
+ // Generate response with the declarative function
+ var definitionResponse = new PromptAgentDefinition("test-model") { Instructions = "Test" };
+ definitionResponse.Tools.Add(functionTool);
+
+ AgentsClient client = this.CreateTestAgentsClient(agentName: "test-agent", agentDefinitionResponse: definitionResponse);
+
+ // Act
+ var agent = await client.CreateAIAgentAsync("test-agent", definition);
+
+ // Assert
+ Assert.NotNull(agent);
+ Assert.IsType(agent);
+ }
+
+ ///
+ /// Verify that CreateAIAgentAsync accepts declarative functions from definition.
+ ///
+ [Fact]
+ public async Task CreateAIAgentAsync_WithDeclarativeFunctionFromDefinition_AcceptsDeclarativeFunctionAsync()
{
// Arrange
AgentsClient client = this.CreateTestAgentsClient();
@@ -1311,20 +1359,22 @@ public sealed class AgentsClientExtensionsTests
using var doc = JsonDocument.Parse("{}");
var declarativeFunction = AIFunctionFactory.CreateDeclaration("test_function", "A test function", doc.RootElement);
- var tools = new List { declarativeFunction };
+ // Add to definition
+ definition.Tools.Add(declarativeFunction.AsOpenAIResponseTool() ?? throw new InvalidOperationException());
- // Act & Assert
- var exception = await Assert.ThrowsAsync(() =>
- client.CreateAIAgentAsync("test-agent", definition, tools: tools, requireInvocableTools: true));
+ // Act
+ var agent = await client.CreateAIAgentAsync("test-agent", definition);
- Assert.Contains("invokable AIFunctions", exception.Message);
+ // Assert
+ Assert.NotNull(agent);
+ Assert.IsType(agent);
}
///
- /// Verify that CreateAIAgentAsync accepts declarative functions when requireInvocableTools=false.
+ /// Verify that CreateAIAgentAsync accepts declarative functions from definition.
///
[Fact]
- public async Task CreateAIAgentAsync_WithDeclarativeFunctionAndRequireInvocableFalse_AcceptsDeclarativeFunctionAsync()
+ public async Task CreateAIAgentAsync_WithDeclarativeFunctionInDefinition_AcceptsDeclarativeFunctionAsync()
{
// Arrange
var definition = new PromptAgentDefinition("test-model") { Instructions = "Test" };
@@ -1333,7 +1383,8 @@ public sealed class AgentsClientExtensionsTests
using var doc = JsonDocument.Parse("{}");
var declarativeFunction = AIFunctionFactory.CreateDeclaration("test_function", "A test function", doc.RootElement);
- var tools = new List { declarativeFunction };
+ // Add to definition
+ definition.Tools.Add(declarativeFunction.AsOpenAIResponseTool() ?? throw new InvalidOperationException());
// Generate response with the declarative function
var definitionResponse = new PromptAgentDefinition("test-model") { Instructions = "Test" };
@@ -1342,7 +1393,7 @@ public sealed class AgentsClientExtensionsTests
AgentsClient client = this.CreateTestAgentsClient(agentName: "test-agent", agentDefinitionResponse: definitionResponse);
// Act
- var agent = await client.CreateAIAgentAsync("test-agent", definition, tools: tools, requireInvocableTools: false);
+ var agent = await client.CreateAIAgentAsync("test-agent", definition);
// Assert
Assert.NotNull(agent);
@@ -1354,23 +1405,19 @@ public sealed class AgentsClientExtensionsTests
#region Options Generation Validation Tests
///
- /// Verify that ChatClientAgentOptions are generated correctly with proper tool matching.
+ /// Verify that ChatClientAgentOptions are generated correctly without tools.
///
[Fact]
public void CreateAIAgent_GeneratesCorrectChatClientAgentOptions()
{
// Arrange
var definition = new PromptAgentDefinition("test-model") { Instructions = "Test instructions" };
- var tools = new List
- {
- AIFunctionFactory.Create(() => "result", "test_tool", "A test tool")
- };
- var definitionResponse = GeneratePromptDefinitionResponse(definition, tools);
+ var definitionResponse = GeneratePromptDefinitionResponse(definition, null);
AgentsClient client = this.CreateTestAgentsClient(agentName: "test-agent", agentDefinitionResponse: definitionResponse);
// Act
- var agent = client.CreateAIAgent("test-agent", definition, tools: tools);
+ var agent = client.CreateAIAgent("test-agent", definition);
// Assert
Assert.NotNull(agent);
@@ -1560,26 +1607,21 @@ public sealed class AgentsClientExtensionsTests
}
///
- /// Verify that agent created with tools and clientFactory is created successfully.
+ /// Verify that agent created with clientFactory is created successfully.
///
[Fact]
- public void CreateAIAgent_WithToolsAndClientFactory_CreatesAgentSuccessfully()
+ public void CreateAIAgent_WithClientFactory_CreatesAgentSuccessfully()
{
// Arrange
var definition = new PromptAgentDefinition("test-model") { Instructions = "Test" };
- var tools = new List
- {
- AIFunctionFactory.Create(() => "test", "test_tool", "A test tool")
- };
- var agentDefinitionResponse = GeneratePromptDefinitionResponse(definition, tools);
+ var agentDefinitionResponse = GeneratePromptDefinitionResponse(definition, null);
AgentsClient client = this.CreateTestAgentsClient(agentName: "test-agent", agentDefinitionResponse: agentDefinitionResponse);
// Act
var agent = client.CreateAIAgent(
"test-agent",
definition,
- tools: tools,
clientFactory: (innerClient) => new TestChatClient(innerClient));
// Assert
@@ -1588,11 +1630,6 @@ public sealed class AgentsClientExtensionsTests
Assert.NotNull(wrappedClient);
var agentVersion = agent.GetService();
Assert.NotNull(agentVersion);
- if (agentVersion.Definition is PromptAgentDefinition promptDef)
- {
- Assert.NotEmpty(promptDef.Tools);
- Assert.Single(promptDef.Tools);
- }
}
#endregion
@@ -1824,12 +1861,15 @@ public sealed class AgentsClientExtensionsTests
}
}
- private static PromptAgentDefinition GeneratePromptDefinitionResponse(PromptAgentDefinition inputDefinition, List tools)
+ private static PromptAgentDefinition GeneratePromptDefinitionResponse(PromptAgentDefinition inputDefinition, List? tools)
{
var definitionResponse = new PromptAgentDefinition(inputDefinition.Model) { Instructions = inputDefinition.Instructions };
- foreach (var tool in tools)
+ if (tools is not null)
{
- definitionResponse.Tools.Add(tool.GetService() ?? tool.AsOpenAIResponseTool());
+ foreach (var tool in tools)
+ {
+ definitionResponse.Tools.Add(tool.GetService() ?? tool.AsOpenAIResponseTool());
+ }
}
return definitionResponse;