diff --git a/dotnet/tests/Microsoft.Agents.AI.AzureAI.UnitTests/AzureAIProjectChatClientExtensionsTests.cs b/dotnet/tests/Microsoft.Agents.AI.AzureAI.UnitTests/AzureAIProjectChatClientExtensionsTests.cs
index eb2ea449e6..da65d53c30 100644
--- a/dotnet/tests/Microsoft.Agents.AI.AzureAI.UnitTests/AzureAIProjectChatClientExtensionsTests.cs
+++ b/dotnet/tests/Microsoft.Agents.AI.AzureAI.UnitTests/AzureAIProjectChatClientExtensionsTests.cs
@@ -1803,6 +1803,863 @@ public void GetService_WithAgentReference_ReturnsCorrectVersionInformation()
#endregion
+ #region GetAIAgentAsync - Empty Name Tests
+
+ ///
+ /// Verify that GetAIAgentAsync with ChatClientAgentOptions throws ArgumentException when name is null.
+ ///
+ [Fact]
+ public async Task GetAIAgentAsync_WithOptions_WithNullName_ThrowsArgumentExceptionAsync()
+ {
+ // Arrange
+ AIProjectClient client = this.CreateTestAgentClient();
+ var options = new ChatClientAgentOptions { Name = null };
+
+ // Act & Assert
+ ArgumentException exception = await Assert.ThrowsAsync(() =>
+ client.GetAIAgentAsync(options));
+
+ Assert.Equal("options", exception.ParamName);
+ Assert.Contains("Agent name must be provided", exception.Message);
+ }
+
+ ///
+ /// Verify that GetAIAgentAsync with ChatClientAgentOptions throws ArgumentException when name is empty.
+ ///
+ [Fact]
+ public async Task GetAIAgentAsync_WithOptions_WithEmptyName_ThrowsArgumentExceptionAsync()
+ {
+ // Arrange
+ AIProjectClient client = this.CreateTestAgentClient();
+ var options = new ChatClientAgentOptions { Name = string.Empty };
+
+ // Act & Assert
+ ArgumentException exception = await Assert.ThrowsAsync(() =>
+ client.GetAIAgentAsync(options));
+
+ Assert.Equal("options", exception.ParamName);
+ Assert.Contains("Agent name must be provided", exception.Message);
+ }
+
+ ///
+ /// Verify that GetAIAgentAsync with ChatClientAgentOptions throws ArgumentException when name is whitespace.
+ ///
+ [Fact]
+ public async Task GetAIAgentAsync_WithOptions_WithWhitespaceName_ThrowsArgumentExceptionAsync()
+ {
+ // Arrange
+ AIProjectClient client = this.CreateTestAgentClient();
+ var options = new ChatClientAgentOptions { Name = " " };
+
+ // Act & Assert
+ ArgumentException exception = await Assert.ThrowsAsync(() =>
+ client.GetAIAgentAsync(options));
+
+ Assert.Equal("options", exception.ParamName);
+ Assert.Contains("Agent name must be provided", exception.Message);
+ }
+
+ #endregion
+
+ #region CreateAIAgentAsync - Empty Name Tests
+
+ ///
+ /// Verify that CreateAIAgentAsync with model and options throws ArgumentException when name is null.
+ ///
+ [Fact]
+ public async Task CreateAIAgentAsync_WithModelAndOptions_WithNullName_ThrowsArgumentExceptionAsync()
+ {
+ // Arrange
+ AIProjectClient client = this.CreateTestAgentClient();
+ var options = new ChatClientAgentOptions
+ {
+ Name = null,
+ ChatOptions = new ChatOptions { Instructions = "Test" }
+ };
+
+ // Act & Assert
+ ArgumentException exception = await Assert.ThrowsAsync(() =>
+ client.CreateAIAgentAsync("test-model", options));
+
+ Assert.Equal("options", exception.ParamName);
+ Assert.Contains("Agent name must be provided", exception.Message);
+ }
+
+ ///
+ /// Verify that CreateAIAgentAsync with model and options throws ArgumentException when name is empty.
+ ///
+ [Fact]
+ public async Task CreateAIAgentAsync_WithModelAndOptions_WithEmptyName_ThrowsArgumentExceptionAsync()
+ {
+ // Arrange
+ AIProjectClient client = this.CreateTestAgentClient();
+ var options = new ChatClientAgentOptions
+ {
+ Name = string.Empty,
+ ChatOptions = new ChatOptions { Instructions = "Test" }
+ };
+
+ // Act & Assert
+ ArgumentException exception = await Assert.ThrowsAsync(() =>
+ client.CreateAIAgentAsync("test-model", options));
+
+ Assert.Equal("options", exception.ParamName);
+ Assert.Contains("Agent name must be provided", exception.Message);
+ }
+
+ ///
+ /// Verify that CreateAIAgentAsync with model and options throws ArgumentException when name is whitespace.
+ ///
+ [Fact]
+ public async Task CreateAIAgentAsync_WithModelAndOptions_WithWhitespaceName_ThrowsArgumentExceptionAsync()
+ {
+ // Arrange
+ AIProjectClient client = this.CreateTestAgentClient();
+ var options = new ChatClientAgentOptions
+ {
+ Name = " ",
+ ChatOptions = new ChatOptions { Instructions = "Test" }
+ };
+
+ // Act & Assert
+ ArgumentException exception = await Assert.ThrowsAsync(() =>
+ client.CreateAIAgentAsync("test-model", options));
+
+ Assert.Equal("options", exception.ParamName);
+ Assert.Contains("Agent name must be provided", exception.Message);
+ }
+
+ #endregion
+
+ #region CreateAIAgentAsync - Response Format Tests
+
+ ///
+ /// Verify that CreateAIAgentAsync with ChatResponseFormatText response format creates agent successfully.
+ ///
+ [Fact]
+ public async Task CreateAIAgentAsync_WithTextResponseFormat_CreatesAgentSuccessfullyAsync()
+ {
+ // Arrange
+ AIProjectClient client = this.CreateTestAgentClient();
+ var options = new ChatClientAgentOptions
+ {
+ Name = "test-agent",
+ ChatOptions = new ChatOptions
+ {
+ Instructions = "Test",
+ ResponseFormat = ChatResponseFormat.Text
+ }
+ };
+
+ // Act
+ ChatClientAgent agent = await client.CreateAIAgentAsync("test-model", options);
+
+ // Assert
+ Assert.NotNull(agent);
+ Assert.IsType(agent);
+ }
+
+ ///
+ /// Verify that CreateAIAgentAsync with ChatResponseFormatJson response format without schema creates agent successfully.
+ ///
+ [Fact]
+ public async Task CreateAIAgentAsync_WithJsonResponseFormatWithoutSchema_CreatesAgentSuccessfullyAsync()
+ {
+ // Arrange
+ AIProjectClient client = this.CreateTestAgentClient();
+ var options = new ChatClientAgentOptions
+ {
+ Name = "test-agent",
+ ChatOptions = new ChatOptions
+ {
+ Instructions = "Test",
+ ResponseFormat = ChatResponseFormat.Json
+ }
+ };
+
+ // Act
+ ChatClientAgent agent = await client.CreateAIAgentAsync("test-model", options);
+
+ // Assert
+ Assert.NotNull(agent);
+ Assert.IsType(agent);
+ }
+
+ ///
+ /// Verify that CreateAIAgentAsync with ChatResponseFormatJson with schema creates agent successfully.
+ ///
+ [Fact]
+ public async Task CreateAIAgentAsync_WithJsonResponseFormatWithSchema_CreatesAgentSuccessfullyAsync()
+ {
+ // Arrange
+ AIProjectClient client = this.CreateTestAgentClient();
+ JsonElement schemaElement = AIJsonUtilities.CreateJsonSchema(typeof(TestSchema));
+ var jsonFormat = ChatResponseFormat.ForJsonSchema(schemaElement, "test_schema", "A test schema");
+ var options = new ChatClientAgentOptions
+ {
+ Name = "test-agent",
+ ChatOptions = new ChatOptions
+ {
+ Instructions = "Test",
+ ResponseFormat = jsonFormat
+ }
+ };
+
+ // Act
+ ChatClientAgent agent = await client.CreateAIAgentAsync("test-model", options);
+
+ // Assert
+ Assert.NotNull(agent);
+ Assert.IsType(agent);
+ }
+
+ ///
+ /// Verify that CreateAIAgentAsync with ChatResponseFormatJson with schema and strict mode creates agent successfully.
+ ///
+ [Fact]
+ public async Task CreateAIAgentAsync_WithJsonResponseFormatWithSchemaAndStrictMode_CreatesAgentSuccessfullyAsync()
+ {
+ // Arrange
+ AIProjectClient client = this.CreateTestAgentClient();
+ JsonElement schemaElement = AIJsonUtilities.CreateJsonSchema(typeof(TestSchema));
+ var jsonFormat = ChatResponseFormat.ForJsonSchema(schemaElement, "test_schema", "A test schema");
+ var additionalProps = new AdditionalPropertiesDictionary
+ {
+ ["strictJsonSchema"] = true
+ };
+ var options = new ChatClientAgentOptions
+ {
+ Name = "test-agent",
+ ChatOptions = new ChatOptions
+ {
+ Instructions = "Test",
+ ResponseFormat = jsonFormat,
+ AdditionalProperties = additionalProps
+ }
+ };
+
+ // Act
+ ChatClientAgent agent = await client.CreateAIAgentAsync("test-model", options);
+
+ // Assert
+ Assert.NotNull(agent);
+ Assert.IsType(agent);
+ }
+
+ ///
+ /// Verify that CreateAIAgentAsync with ChatResponseFormatJson with schema and strict mode false creates agent successfully.
+ ///
+ [Fact]
+ public async Task CreateAIAgentAsync_WithJsonResponseFormatWithSchemaAndStrictModeFalse_CreatesAgentSuccessfullyAsync()
+ {
+ // Arrange
+ AIProjectClient client = this.CreateTestAgentClient();
+ JsonElement schemaElement = AIJsonUtilities.CreateJsonSchema(typeof(TestSchema));
+ var jsonFormat = ChatResponseFormat.ForJsonSchema(schemaElement, "test_schema", "A test schema");
+ var additionalProps = new AdditionalPropertiesDictionary
+ {
+ ["strictJsonSchema"] = false
+ };
+ var options = new ChatClientAgentOptions
+ {
+ Name = "test-agent",
+ ChatOptions = new ChatOptions
+ {
+ Instructions = "Test",
+ ResponseFormat = jsonFormat,
+ AdditionalProperties = additionalProps
+ }
+ };
+
+ // Act
+ ChatClientAgent agent = await client.CreateAIAgentAsync("test-model", options);
+
+ // Assert
+ Assert.NotNull(agent);
+ Assert.IsType(agent);
+ }
+
+ #endregion
+
+ #region CreateAIAgentAsync - RawRepresentationFactory Tests
+
+ ///
+ /// Verify that CreateAIAgentAsync with RawRepresentationFactory that returns CreateResponseOptions creates agent successfully.
+ ///
+ [Fact]
+ public async Task CreateAIAgentAsync_WithRawRepresentationFactory_CreatesAgentSuccessfullyAsync()
+ {
+ // Arrange
+ AIProjectClient client = this.CreateTestAgentClient();
+ var options = new ChatClientAgentOptions
+ {
+ Name = "test-agent",
+ ChatOptions = new ChatOptions
+ {
+ Instructions = "Test",
+ RawRepresentationFactory = _ => new CreateResponseOptions()
+ }
+ };
+
+ // Act
+ ChatClientAgent agent = await client.CreateAIAgentAsync("test-model", options);
+
+ // Assert
+ Assert.NotNull(agent);
+ Assert.IsType(agent);
+ }
+
+ ///
+ /// Verify that CreateAIAgentAsync with RawRepresentationFactory that returns null does not fail.
+ ///
+ [Fact]
+ public async Task CreateAIAgentAsync_WithRawRepresentationFactoryReturningNull_CreatesAgentSuccessfullyAsync()
+ {
+ // Arrange
+ AIProjectClient client = this.CreateTestAgentClient();
+ var options = new ChatClientAgentOptions
+ {
+ Name = "test-agent",
+ ChatOptions = new ChatOptions
+ {
+ Instructions = "Test",
+ RawRepresentationFactory = _ => null
+ }
+ };
+
+ // Act
+ ChatClientAgent agent = await client.CreateAIAgentAsync("test-model", options);
+
+ // Assert
+ Assert.NotNull(agent);
+ Assert.IsType(agent);
+ }
+
+ ///
+ /// Verify that CreateAIAgentAsync with RawRepresentationFactory that returns non-CreateResponseOptions does not fail.
+ ///
+ [Fact]
+ public async Task CreateAIAgentAsync_WithRawRepresentationFactoryReturningNonCreateResponseOptions_CreatesAgentSuccessfullyAsync()
+ {
+ // Arrange
+ AIProjectClient client = this.CreateTestAgentClient();
+ var options = new ChatClientAgentOptions
+ {
+ Name = "test-agent",
+ ChatOptions = new ChatOptions
+ {
+ Instructions = "Test",
+ RawRepresentationFactory = _ => new object()
+ }
+ };
+
+ // Act
+ ChatClientAgent agent = await client.CreateAIAgentAsync("test-model", options);
+
+ // Assert
+ Assert.NotNull(agent);
+ Assert.IsType(agent);
+ }
+
+ #endregion
+
+ #region CreateAIAgentAsync - Description Tests
+
+ ///
+ /// Verify that CreateAIAgentAsync with description sets description on the agent.
+ ///
+ [Fact]
+ public async Task CreateAIAgentAsync_WithDescription_SetsDescriptionAsync()
+ {
+ // Arrange
+ AIProjectClient client = this.CreateTestAgentClient(description: "Test description");
+ var options = new ChatClientAgentOptions
+ {
+ Name = "test-agent",
+ Description = "Test description",
+ ChatOptions = new ChatOptions { Instructions = "Test" }
+ };
+
+ // Act
+ ChatClientAgent agent = await client.CreateAIAgentAsync("test-model", options);
+
+ // Assert
+ Assert.NotNull(agent);
+ Assert.Equal("Test description", agent.Description);
+ }
+
+ ///
+ /// Verify that CreateAIAgentAsync without description still creates agent successfully.
+ ///
+ [Fact]
+ public async Task CreateAIAgentAsync_WithoutDescription_CreatesAgentSuccessfullyAsync()
+ {
+ // Arrange
+ AIProjectClient client = this.CreateTestAgentClient();
+ var options = new ChatClientAgentOptions
+ {
+ Name = "test-agent",
+ ChatOptions = new ChatOptions { Instructions = "Test" }
+ };
+
+ // Act
+ ChatClientAgent agent = await client.CreateAIAgentAsync("test-model", options);
+
+ // Assert
+ Assert.NotNull(agent);
+ }
+
+ #endregion
+
+ #region CreateChatClientAgentOptions - Missing Tools Tests
+
+ ///
+ /// Verify that when invocable tools are required but not provided, an exception is thrown.
+ ///
+ [Fact]
+ public async Task GetAIAgentAsync_WithToolsRequiredButNotProvided_ThrowsArgumentExceptionAsync()
+ {
+ // Arrange
+ PromptAgentDefinition definition = new("test-model") { Instructions = "Test" };
+ definition.Tools.Add(ResponseTool.CreateFunctionTool("required_function", BinaryData.FromString("{}"), strictModeEnabled: false));
+
+ AIProjectClient client = this.CreateTestAgentClient(agentDefinitionResponse: definition);
+ var options = new ChatClientAgentOptions
+ {
+ Name = "test-agent",
+ ChatOptions = new ChatOptions { Instructions = "Test" }
+ };
+
+ // Act & Assert
+ ArgumentException exception = await Assert.ThrowsAsync(() =>
+ client.GetAIAgentAsync(options));
+
+ Assert.Contains("in-process tools must be provided", exception.Message);
+ }
+
+ ///
+ /// Verify that when specific invocable tools are required but wrong ones are provided, InvalidOperationException is thrown.
+ ///
+ [Fact]
+ public async Task GetAIAgentAsync_WithWrongToolsProvided_ThrowsInvalidOperationExceptionAsync()
+ {
+ // Arrange
+ PromptAgentDefinition definition = new("test-model") { Instructions = "Test" };
+ definition.Tools.Add(ResponseTool.CreateFunctionTool("required_function", BinaryData.FromString("{}"), strictModeEnabled: false));
+
+ AIProjectClient client = this.CreateTestAgentClient(agentDefinitionResponse: definition);
+ var tools = new List
+ {
+ AIFunctionFactory.Create(() => "test", "wrong_function", "Wrong function")
+ };
+
+ var options = new ChatClientAgentOptions
+ {
+ Name = "test-agent",
+ ChatOptions = new ChatOptions
+ {
+ Instructions = "Test",
+ Tools = tools
+ }
+ };
+
+ // Act & Assert
+ InvalidOperationException exception = await Assert.ThrowsAsync(() =>
+ client.GetAIAgentAsync(options));
+
+ Assert.Contains("required_function", exception.Message);
+ Assert.Contains("were not provided", exception.Message);
+ }
+
+ ///
+ /// Verify that when tools are provided that match the definition, agent is created successfully.
+ ///
+ [Fact]
+ public async Task GetAIAgentAsync_WithMatchingToolsProvided_CreatesAgentSuccessfullyAsync()
+ {
+ // Arrange
+ PromptAgentDefinition definition = new("test-model") { Instructions = "Test" };
+ definition.Tools.Add(ResponseTool.CreateFunctionTool("required_function", BinaryData.FromString("{}"), strictModeEnabled: false));
+
+ AIProjectClient client = this.CreateTestAgentClient(agentDefinitionResponse: definition);
+ var tools = new List
+ {
+ AIFunctionFactory.Create(() => "test", "required_function", "Required function")
+ };
+
+ var options = new ChatClientAgentOptions
+ {
+ Name = "test-agent",
+ ChatOptions = new ChatOptions
+ {
+ Instructions = "Test",
+ Tools = tools
+ }
+ };
+
+ // Act
+ ChatClientAgent agent = await client.GetAIAgentAsync(options);
+
+ // Assert
+ Assert.NotNull(agent);
+ Assert.IsType(agent);
+ }
+
+ #endregion
+
+ #region CreateChatClientAgentOptions - Options Preservation Tests
+
+ ///
+ /// Verify that CreateChatClientAgentOptions preserves AIContextProviderFactory.
+ ///
+ [Fact]
+ public async Task GetAIAgentAsync_WithAIContextProviderFactory_PreservesFactoryAsync()
+ {
+ // Arrange
+ AIProjectClient client = this.CreateTestAgentClient();
+ bool factoryInvoked = false;
+ var options = new ChatClientAgentOptions
+ {
+ Name = "test-agent",
+ ChatOptions = new ChatOptions { Instructions = "Test" },
+ AIContextProviderFactory = (_, _) =>
+ {
+ factoryInvoked = true;
+ return new ValueTask(new TestAIContextProvider());
+ }
+ };
+
+ // Act
+ ChatClientAgent agent = await client.GetAIAgentAsync(options);
+
+ // Assert
+ Assert.NotNull(agent);
+ // Verify the factory was captured (though not necessarily invoked yet)
+ Assert.False(factoryInvoked); // Factory is not invoked during creation
+ }
+
+ ///
+ /// Verify that CreateChatClientAgentOptions preserves ChatHistoryProviderFactory.
+ ///
+ [Fact]
+ public async Task GetAIAgentAsync_WithChatHistoryProviderFactory_PreservesFactoryAsync()
+ {
+ // Arrange
+ AIProjectClient client = this.CreateTestAgentClient();
+ var options = new ChatClientAgentOptions
+ {
+ Name = "test-agent",
+ ChatOptions = new ChatOptions { Instructions = "Test" },
+ ChatHistoryProviderFactory = (_, _) => new ValueTask(new TestChatHistoryProvider())
+ };
+
+ // Act
+ ChatClientAgent agent = await client.GetAIAgentAsync(options);
+
+ // Assert
+ Assert.NotNull(agent);
+ }
+
+ ///
+ /// Verify that CreateChatClientAgentOptions preserves UseProvidedChatClientAsIs.
+ ///
+ [Fact]
+ public async Task GetAIAgentAsync_WithUseProvidedChatClientAsIs_PreservesSettingAsync()
+ {
+ // Arrange
+ AIProjectClient client = this.CreateTestAgentClient();
+ var options = new ChatClientAgentOptions
+ {
+ Name = "test-agent",
+ ChatOptions = new ChatOptions { Instructions = "Test" },
+ UseProvidedChatClientAsIs = true
+ };
+
+ // Act
+ ChatClientAgent agent = await client.GetAIAgentAsync(options);
+
+ // Assert
+ Assert.NotNull(agent);
+ }
+
+ #endregion
+
+ #region ApplyToolsToAgentDefinition Tests
+
+ ///
+ /// Verify that CreateAIAgentAsync with non-PromptAgentDefinition and tools throws ArgumentException.
+ ///
+ [Fact]
+ public async Task CreateAIAgentAsync_WithNonPromptAgentDefinitionAndTools_ThrowsArgumentExceptionAsync()
+ {
+ // Arrange
+ var tools = new List
+ {
+ AIFunctionFactory.Create(() => "test", "test_function", "A test function")
+ };
+
+ using HttpHandlerAssert httpHandler = new(_ => new HttpResponseMessage(HttpStatusCode.OK)
+ {
+ Content = new StringContent(TestDataUtil.GetAgentVersionResponseJson(), Encoding.UTF8, "application/json")
+ });
+
+#pragma warning disable CA5399
+ using HttpClient httpClient = new(httpHandler);
+#pragma warning restore CA5399
+
+ AIProjectClient client = new(new Uri("https://test.openai.azure.com/"), new FakeAuthenticationTokenProvider(), new() { Transport = new HttpClientPipelineTransport(httpClient) });
+
+ // Create a mock AgentDefinition that is not PromptAgentDefinition
+ // Since we can't easily create a non-PromptAgentDefinition in the public API, we test this path via the CreateAIAgentAsync that builds a PromptAgentDefinition
+ // The ApplyToolsToAgentDefinition is only called when tools.Count > 0, and we provide tools
+ // But PromptAgentDefinition is always created by CreateAIAgentAsync(name, model, instructions, tools)
+ // So this path is hard to hit without mocking. Let's test the declarative function rejection instead.
+ var declarativeFunction = AIFunctionFactory.CreateDeclaration("test_function", "A test function", JsonDocument.Parse("{}").RootElement);
+
+ // Act & Assert
+ InvalidOperationException exception = await Assert.ThrowsAsync(() =>
+ client.CreateAIAgentAsync(
+ name: "test-agent",
+ model: "test-model",
+ instructions: "Test",
+ tools: [declarativeFunction]));
+
+ Assert.Contains("invokable AIFunctions", exception.Message);
+ }
+
+ ///
+ /// Verify that CreateAIAgentAsync with AIFunctionDeclaration tools throws InvalidOperationException.
+ ///
+ [Fact]
+ public async Task CreateAIAgentAsync_WithAIFunctionDeclarationTool_ThrowsInvalidOperationExceptionAsync()
+ {
+ // Arrange
+ using var doc = JsonDocument.Parse("{}");
+ var declarativeFunction = AIFunctionFactory.CreateDeclaration("test_function", "A test function", doc.RootElement);
+
+ using HttpHandlerAssert httpHandler = new(_ => new HttpResponseMessage(HttpStatusCode.OK)
+ {
+ Content = new StringContent(TestDataUtil.GetAgentVersionResponseJson(), Encoding.UTF8, "application/json")
+ });
+
+#pragma warning disable CA5399
+ using HttpClient httpClient = new(httpHandler);
+#pragma warning restore CA5399
+
+ AIProjectClient client = new(new Uri("https://test.openai.azure.com/"), new FakeAuthenticationTokenProvider(), new() { Transport = new HttpClientPipelineTransport(httpClient) });
+
+ // Act & Assert
+ InvalidOperationException exception = await Assert.ThrowsAsync(() =>
+ client.CreateAIAgentAsync(
+ name: "test-agent",
+ model: "test-model",
+ instructions: "Test",
+ tools: [declarativeFunction]));
+
+ Assert.Contains("invokable AIFunctions", exception.Message);
+ }
+
+ ///
+ /// Verify that CreateAIAgentAsync with ResponseTool converted via AsAITool works.
+ ///
+ [Fact]
+ public async Task CreateAIAgentAsync_WithResponseToolAsAITool_CreatesAgentSuccessfullyAsync()
+ {
+ // Arrange
+ ResponseTool responseTool = ResponseTool.CreateFunctionTool("response_tool", BinaryData.FromString("{}"), strictModeEnabled: false);
+ AITool convertedTool = responseTool.AsAITool();
+
+ // Create a definition with the function tool already in it
+ PromptAgentDefinition definition = new("test-model") { Instructions = "Test" };
+ definition.Tools.Add(responseTool);
+
+ AIProjectClient client = this.CreateTestAgentClient(agentDefinitionResponse: definition);
+
+ // Matching invokable tool must be provided
+ var invokableTool = AIFunctionFactory.Create(() => "test", "response_tool", "Invokable version of the tool");
+
+ var options = new ChatClientAgentOptions
+ {
+ Name = "test-agent",
+ ChatOptions = new ChatOptions
+ {
+ Instructions = "Test",
+ Tools = [invokableTool]
+ }
+ };
+
+ // Act
+ ChatClientAgent agent = await client.GetAIAgentAsync(options);
+
+ // Assert
+ Assert.NotNull(agent);
+ Assert.IsType(agent);
+ }
+
+ ///
+ /// Verify that CreateAIAgentAsync with hosted tool types works correctly.
+ ///
+ [Fact]
+ public async Task CreateAIAgentAsync_WithHostedToolTypes_CreatesAgentSuccessfullyAsync()
+ {
+ // Arrange
+ AIProjectClient client = this.CreateTestAgentClient();
+ var webSearchTool = new HostedWebSearchTool();
+
+ var options = new ChatClientAgentOptions
+ {
+ Name = "test-agent",
+ ChatOptions = new ChatOptions
+ {
+ Instructions = "Test",
+ Tools = [webSearchTool]
+ }
+ };
+
+ // Act
+ ChatClientAgent agent = await client.CreateAIAgentAsync("test-model", options);
+
+ // Assert
+ Assert.NotNull(agent);
+ Assert.IsType(agent);
+ }
+
+ ///
+ /// Verify that when the server returns tools but matching tools are provided, the agent is created.
+ ///
+ [Fact]
+ public async Task GetAIAgentAsync_WithServerDefinedToolsAndMatchingProvidedTools_CreatesAgentAsync()
+ {
+ // Arrange
+ PromptAgentDefinition definition = new("test-model") { Instructions = "Test" };
+ // Add multiple function tools
+ definition.Tools.Add(ResponseTool.CreateFunctionTool("tool_one", BinaryData.FromString("{}"), strictModeEnabled: false));
+ definition.Tools.Add(ResponseTool.CreateFunctionTool("tool_two", BinaryData.FromString("{}"), strictModeEnabled: false));
+
+ AIProjectClient client = this.CreateTestAgentClient(agentDefinitionResponse: definition);
+
+ var tools = new List
+ {
+ AIFunctionFactory.Create(() => "one", "tool_one", "Tool one"),
+ AIFunctionFactory.Create(() => "two", "tool_two", "Tool two")
+ };
+
+ var options = new ChatClientAgentOptions
+ {
+ Name = "test-agent",
+ ChatOptions = new ChatOptions
+ {
+ Instructions = "Test",
+ Tools = tools
+ }
+ };
+
+ // Act
+ ChatClientAgent agent = await client.GetAIAgentAsync(options);
+
+ // Assert
+ Assert.NotNull(agent);
+ Assert.IsType(agent);
+ }
+
+ ///
+ /// Verify that when the server returns mixed tools (function and hosted), the agent handles them correctly.
+ ///
+ [Fact]
+ public async Task GetAIAgentAsync_WithMixedServerTools_MatchesFunctionToolsOnlyAsync()
+ {
+ // Arrange
+ PromptAgentDefinition definition = new("test-model") { Instructions = "Test" };
+ // Add a function tool
+ definition.Tools.Add(ResponseTool.CreateFunctionTool("function_tool", BinaryData.FromString("{}"), strictModeEnabled: false));
+ // Add a hosted tool
+ definition.Tools.Add(new HostedWebSearchTool().GetService() ?? new HostedWebSearchTool().AsOpenAIResponseTool());
+
+ AIProjectClient client = this.CreateTestAgentClient(agentDefinitionResponse: definition);
+
+ var tools = new List
+ {
+ AIFunctionFactory.Create(() => "result", "function_tool", "The function tool")
+ };
+
+ var options = new ChatClientAgentOptions
+ {
+ Name = "test-agent",
+ ChatOptions = new ChatOptions
+ {
+ Instructions = "Test",
+ Tools = tools
+ }
+ };
+
+ // Act
+ ChatClientAgent agent = await client.GetAIAgentAsync(options);
+
+ // Assert
+ Assert.NotNull(agent);
+ Assert.IsType(agent);
+ }
+
+ ///
+ /// Verify that when partial tools are provided (some missing), InvalidOperationException is thrown listing missing tools.
+ ///
+ [Fact]
+ public async Task GetAIAgentAsync_WithPartialToolsProvided_ThrowsInvalidOperationWithMissingToolNamesAsync()
+ {
+ // Arrange
+ PromptAgentDefinition definition = new("test-model") { Instructions = "Test" };
+ definition.Tools.Add(ResponseTool.CreateFunctionTool("provided_tool", BinaryData.FromString("{}"), strictModeEnabled: false));
+ definition.Tools.Add(ResponseTool.CreateFunctionTool("missing_tool", BinaryData.FromString("{}"), strictModeEnabled: false));
+
+ AIProjectClient client = this.CreateTestAgentClient(agentDefinitionResponse: definition);
+
+ var tools = new List
+ {
+ // Only providing one of two required tools
+ AIFunctionFactory.Create(() => "result", "provided_tool", "The provided tool")
+ };
+
+ var options = new ChatClientAgentOptions
+ {
+ Name = "test-agent",
+ ChatOptions = new ChatOptions
+ {
+ Instructions = "Test",
+ Tools = tools
+ }
+ };
+
+ // Act & Assert
+ InvalidOperationException exception = await Assert.ThrowsAsync(() =>
+ client.GetAIAgentAsync(options));
+
+ Assert.Contains("missing_tool", exception.Message);
+ Assert.DoesNotContain("provided_tool", exception.Message);
+ }
+
+ ///
+ /// Verify that when AsAIAgent is called without requireInvocableTools, hosted tools are correctly added.
+ ///
+ [Fact]
+ public void AsAIAgent_WithServerHostedTools_AddsToolsToAgentOptions()
+ {
+ // Arrange
+ PromptAgentDefinition definition = new("test-model") { Instructions = "Test" };
+ definition.Tools.Add(new HostedWebSearchTool().GetService() ?? new HostedWebSearchTool().AsOpenAIResponseTool());
+
+ AIProjectClient client = this.CreateTestAgentClient();
+ AgentVersion agentVersion = ModelReaderWriter.Read(BinaryData.FromString(TestDataUtil.GetAgentVersionResponseJson(agentDefinition: definition)))!;
+
+ // Act - no tools provided, but requireInvocableTools is false when no tools param is passed
+ ChatClientAgent agent = client.AsAIAgent(agentVersion);
+
+ // Assert
+ Assert.NotNull(agent);
+ Assert.IsType(agent);
+ }
+
+ #endregion
+
#region Helper Methods
///
@@ -2056,6 +2913,49 @@ public override IEnumerator> GetEnumerator()
return chatOptionsProperty?.GetValue(agent) as ChatOptions;
}
+
+ ///
+ /// Test schema for JSON response format tests.
+ ///
+#pragma warning disable CA1812 // Avoid uninstantiated internal classes - used via reflection by AIJsonUtilities
+ private sealed class TestSchema
+ {
+ public string? Name { get; set; }
+ public int Value { get; set; }
+ }
+#pragma warning restore CA1812
+
+ ///
+ /// Test AIContextProvider for options preservation tests.
+ ///
+ private sealed class TestAIContextProvider : AIContextProvider
+ {
+ public override ValueTask InvokingAsync(InvokingContext context, CancellationToken cancellationToken = default)
+ {
+ return new ValueTask(new AIContext());
+ }
+ }
+
+ ///
+ /// Test ChatHistoryProvider for options preservation tests.
+ ///
+ private sealed class TestChatHistoryProvider : ChatHistoryProvider
+ {
+ public override ValueTask> InvokingAsync(InvokingContext context, CancellationToken cancellationToken = default)
+ {
+ return new ValueTask>(Array.Empty());
+ }
+
+ public override ValueTask InvokedAsync(InvokedContext context, CancellationToken cancellationToken = default)
+ {
+ return default;
+ }
+
+ public override JsonElement Serialize(JsonSerializerOptions? jsonSerializerOptions = null)
+ {
+ return default;
+ }
+ }
}
///