// Copyright (c) Microsoft. All rights reserved. using System; using System.ClientModel.Primitives; using System.Net; using System.Net.Http; using System.Text; using System.Threading.Tasks; using Azure.AI.Projects; namespace Microsoft.Agents.AI.AzureAI.UnitTests; #pragma warning disable CS0618 [Obsolete("Uses obsolete AIProjectClient.GetAIAgentAsync compatibility extensions while validating chat-client behavior.")] public class AzureAIProjectChatClientTests { /// /// Verify that when the ChatOptions has a "conv_" prefixed conversation ID, the chat client uses conversation in the http requests via the chat client /// [Fact] public async Task ChatClient_UsesDefaultConversationIdAsync() { // Arrange var requestTriggered = false; using var httpHandler = new HttpHandlerAssert(async (request) => { if (request.Method == HttpMethod.Post && request.RequestUri!.PathAndQuery.Contains("/responses")) { requestTriggered = true; // Assert if (request.Content is not null) { var requestBody = await request.Content.ReadAsStringAsync().ConfigureAwait(false); Assert.Contains("conv_12345", requestBody); } return new HttpResponseMessage(HttpStatusCode.OK) { Content = new StringContent(TestDataUtil.GetOpenAIDefaultResponseJson(), Encoding.UTF8, "application/json") }; } return new HttpResponseMessage(HttpStatusCode.OK) { Content = new StringContent(TestDataUtil.GetAgentResponseJson(), Encoding.UTF8, "application/json") }; }); #pragma warning disable CA5399 using var httpClient = new HttpClient(httpHandler); #pragma warning restore CA5399 AIProjectClient projectClient = new( new Uri("https://test.openai.azure.com/"), new FakeAuthenticationTokenProvider(), new AIProjectClientOptions() { Transport = new HttpClientPipelineTransport(httpClient) }); var agent = await projectClient.GetAIAgentAsync( new ChatClientAgentOptions { Name = "test-agent", ChatOptions = new() { Instructions = "Test instructions", ConversationId = "conv_12345" } }); // Act var session = await agent.CreateSessionAsync(); await agent.RunAsync("Hello", session); Assert.True(requestTriggered); var chatClientSession = Assert.IsType(session); Assert.Equal("conv_12345", chatClientSession.ConversationId); } /// /// Verify that when the chat client doesn't have a default "conv_" conversation id, the chat client still uses the conversation ID in HTTP requests. /// [Fact] public async Task ChatClient_UsesPerRequestConversationId_WhenNoDefaultConversationIdIsProvidedAsync() { // Arrange var requestTriggered = false; using var httpHandler = new HttpHandlerAssert(async (request) => { if (request.Method == HttpMethod.Post && request.RequestUri!.PathAndQuery.Contains("/responses")) { requestTriggered = true; // Assert if (request.Content is not null) { var requestBody = await request.Content.ReadAsStringAsync().ConfigureAwait(false); Assert.Contains("conv_12345", requestBody); } return new HttpResponseMessage(HttpStatusCode.OK) { Content = new StringContent(TestDataUtil.GetOpenAIDefaultResponseJson(), Encoding.UTF8, "application/json") }; } return new HttpResponseMessage(HttpStatusCode.OK) { Content = new StringContent(TestDataUtil.GetAgentResponseJson(), Encoding.UTF8, "application/json") }; }); #pragma warning disable CA5399 using var httpClient = new HttpClient(httpHandler); #pragma warning restore CA5399 AIProjectClient projectClient = new( new Uri("https://test.openai.azure.com/"), new FakeAuthenticationTokenProvider(), new AIProjectClientOptions() { Transport = new HttpClientPipelineTransport(httpClient) }); var agent = await projectClient.GetAIAgentAsync( new ChatClientAgentOptions { Name = "test-agent", ChatOptions = new() { Instructions = "Test instructions" }, }); // Act var session = await agent.CreateSessionAsync(); await agent.RunAsync("Hello", session, options: new ChatClientAgentRunOptions() { ChatOptions = new() { ConversationId = "conv_12345" } }); Assert.True(requestTriggered); var chatClientSession = Assert.IsType(session); Assert.Equal("conv_12345", chatClientSession.ConversationId); } /// /// Verify that even when the chat client has a default conversation id, the chat client will prioritize the per-request conversation id provided in HTTP requests. /// [Fact] public async Task ChatClient_UsesPerRequestConversationId_EvenWhenDefaultConversationIdIsProvidedAsync() { // Arrange var requestTriggered = false; using var httpHandler = new HttpHandlerAssert(async (request) => { if (request.Method == HttpMethod.Post && request.RequestUri!.PathAndQuery.Contains("/responses")) { requestTriggered = true; // Assert if (request.Content is not null) { var requestBody = await request.Content.ReadAsStringAsync().ConfigureAwait(false); Assert.Contains("conv_12345", requestBody); } return new HttpResponseMessage(HttpStatusCode.OK) { Content = new StringContent(TestDataUtil.GetOpenAIDefaultResponseJson(), Encoding.UTF8, "application/json") }; } return new HttpResponseMessage(HttpStatusCode.OK) { Content = new StringContent(TestDataUtil.GetAgentResponseJson(), Encoding.UTF8, "application/json") }; }); #pragma warning disable CA5399 using var httpClient = new HttpClient(httpHandler); #pragma warning restore CA5399 AIProjectClient projectClient = new( new Uri("https://test.openai.azure.com/"), new FakeAuthenticationTokenProvider(), new AIProjectClientOptions() { Transport = new HttpClientPipelineTransport(httpClient) }); var agent = await projectClient.GetAIAgentAsync( new ChatClientAgentOptions { Name = "test-agent", ChatOptions = new() { Instructions = "Test instructions", ConversationId = "conv_should_not_use_default" } }); // Act var session = await agent.CreateSessionAsync(); await agent.RunAsync("Hello", session, options: new ChatClientAgentRunOptions() { ChatOptions = new() { ConversationId = "conv_12345" } }); Assert.True(requestTriggered); var chatClientSession = Assert.IsType(session); Assert.Equal("conv_12345", chatClientSession.ConversationId); } /// /// Verify that when the chat client is provided without a "conv_" prefixed conversation ID, the chat client uses the previous conversation ID in HTTP requests. /// [Fact] public async Task ChatClient_UsesPreviousResponseId_WhenConversationIsNotPrefixedAsConvAsync() { // Arrange var requestTriggered = false; using var httpHandler = new HttpHandlerAssert(async (request) => { if (request.Method == HttpMethod.Post && request.RequestUri!.PathAndQuery.Contains("/responses")) { requestTriggered = true; // Assert if (request.Content is not null) { var requestBody = await request.Content.ReadAsStringAsync().ConfigureAwait(false); Assert.Contains("resp_0888a", requestBody); } return new HttpResponseMessage(HttpStatusCode.OK) { Content = new StringContent(TestDataUtil.GetOpenAIDefaultResponseJson(), Encoding.UTF8, "application/json") }; } return new HttpResponseMessage(HttpStatusCode.OK) { Content = new StringContent(TestDataUtil.GetAgentResponseJson(), Encoding.UTF8, "application/json") }; }); #pragma warning disable CA5399 using var httpClient = new HttpClient(httpHandler); #pragma warning restore CA5399 AIProjectClient projectClient = new( new Uri("https://test.openai.azure.com/"), new FakeAuthenticationTokenProvider(), new AIProjectClientOptions() { Transport = new HttpClientPipelineTransport(httpClient) }); var agent = await projectClient.GetAIAgentAsync( new ChatClientAgentOptions { Name = "test-agent", ChatOptions = new() { Instructions = "Test instructions" }, }); // Act var session = await agent.CreateSessionAsync(); await agent.RunAsync("Hello", session, options: new ChatClientAgentRunOptions() { ChatOptions = new() { ConversationId = "resp_0888a" } }); Assert.True(requestTriggered); var chatClientSession = Assert.IsType(session); Assert.Equal("resp_0888a46cbf2b1ff3006914596e05d08195a77c3f5187b769a7", chatClientSession.ConversationId); } } #pragma warning restore CS0618