mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
88ea9d08c7
* Initial plan * Update code for Microsoft.Extensions.AI.Abstractions 10.4.0 breaking changes - Rename FunctionApprovalRequestContent → ToolApprovalRequestContent - Rename FunctionApprovalResponseContent → ToolApprovalResponseContent - Rename UserInputRequestContent → ToolApprovalRequestContent - Rename UserInputResponseContent → ToolApprovalResponseContent - Update .FunctionCall property → .ToolCall with FunctionCallContent casts where needed - Update .Id property → .RequestId on the renamed types - Rename FunctionApprovalRequestEventGenerator → ToolApprovalRequestEventGenerator - Rename FunctionApprovalResponseEventGenerator → ToolApprovalResponseEventGenerator Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Update OpenAI 2.9.1, ME.AI 10.4.0, fix breaking API changes Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com> * Fix remaining ME.AI 10.4.0 breaking changes: MCP approval types, .Output→.Outputs Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com> * Use pattern matching with `when` for ToolApprovalRequestContent/FunctionCallContent Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com> * Update Azure.AI.OpenAI to 2.9.0-beta.1 Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com> * Fix remaining GetResponsesClient(model) build failures for Azure.AI.OpenAI 2.9.0-beta.1 Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com> * Address review feedback: remove redundant type checks in TestRequestAgent.cs and fix error message in AIAgentHostExecutor.cs Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com> * Update Azure.AI.Projects to 2.0.0-beta.2 with namespace migration - Azure.AI.Projects 2.0.0-beta.1 → 2.0.0-beta.2 - Azure.AI.Projects.OpenAI → Azure.AI.Extensions.OpenAI (transitive) - Agent types moved to Azure.AI.Projects.Agents namespace - AgentRecord.Versions.Latest → AgentRecord.GetLatestVersion() - OpenAPIFunctionDefinition → OpenApiFunctionDefinition - BingCustomSearchToolParameters → BingCustomSearchToolOptions - MemorySearchPreviewTool.UpdateDelay → UpdateDelayInSecs - Azure.Identity 1.17.1 → 1.19.0 - Microsoft.Identity.Client.Extensions.Msal 4.78.0 → 4.83.1 Co-authored-by: rogerbarreto <19890735+rogerbarreto@users.noreply.github.com> * Fix remaining type renames for Azure.AI.Projects 2.0.0-beta.2 - BrowserAutomationToolParameters → BrowserAutomationToolOptions - MemoryUpdateOptions.UpdateDelay stays as UpdateDelay (not renamed) - WaitForMemoriesUpdateAsync parameter order: pollingInterval before options - AIProjectAgentsOperations → AgentsClient Co-authored-by: rogerbarreto <19890735+rogerbarreto@users.noreply.github.com> * Fix format errors and OpenTelemetry test for ME.AI 10.4.0 - Remove unused 'using Azure.AI.Extensions.OpenAI' and fix import ordering in Agent_With_AzureAIProject/Program.cs - Update OpenTelemetryAgentTests: gen_ai.tool.definitions is now always emitted regardless of EnableSensitiveData per ME.AI 10.4.0 change (dotnet/extensions#7346). Tool definitions are not considered sensitive. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix GetRepoFolder() to work in git worktrees Use 'workflow-samples' directory as repo root marker instead of '.git', which fails in worktrees (.git is a file) and also matches too early when a '.github' folder exists in subdirectories. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix formatting: remove unused usings and fix import ordering dotnet format applied across 59 impacted projects. Primarily removes unnecessary 'using Azure.AI.Projects' where Azure.AI.Projects.Agents provides all needed types, and fixes import ordering per editorconfig. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Disable AzureAIAgentsPersistent integration tests (#4769) Azure.AI.Agents.Persistent 1.2.0-beta.9 references McpServerToolApprovalResponseContent which was removed in ME.AI 10.4.0 (renamed to ToolApprovalResponseContent), causing TypeLoadException at runtime. Mark all 6 test classes with IntegrationDisabled trait until Persistent ships a version targeting ME.AI 10.4.0+. Upstream fix: https://github.com/Azure/azure-sdk-for-net/pull/56929 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Add README with compatibility note for AzureAI.Persistent (#4769) Documents that Azure.AI.Agents.Persistent 1.2.0-beta.9 is only compatible with ME.AI ≤10.3.0 and OpenAI ≤2.8.0 due to type renames in ME.AI 10.4.0. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix file encoding: restore UTF-8 BOM on Persistent test files Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Mark AzureAI.Persistent as IsPackable=false (#4769) Prevent shipping until Azure.AI.Agents.Persistent targets ME.AI 10.4.0+. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Moving IsPackable after import --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com> Co-authored-by: rogerbarreto <19890735+rogerbarreto@users.noreply.github.com>
182 lines
7.4 KiB
C#
182 lines
7.4 KiB
C#
// Copyright (c) Microsoft. All rights reserved.
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Threading.Tasks;
|
|
using AgentConformance.IntegrationTests;
|
|
using AgentConformance.IntegrationTests.Support;
|
|
using Azure.AI.Extensions.OpenAI;
|
|
using Azure.AI.Projects;
|
|
using Microsoft.Agents.AI;
|
|
using Microsoft.Extensions.AI;
|
|
using OpenAI.Responses;
|
|
using Shared.IntegrationTests;
|
|
|
|
namespace AzureAI.IntegrationTests;
|
|
|
|
public class AIProjectClientFixture : IChatClientAgentFixture
|
|
{
|
|
private ChatClientAgent _agent = null!;
|
|
private AIProjectClient _client = null!;
|
|
|
|
public IChatClient ChatClient => this._agent.ChatClient;
|
|
|
|
public AIAgent Agent => this._agent;
|
|
|
|
public async Task<string> CreateConversationAsync()
|
|
{
|
|
var response = await this._client.GetProjectOpenAIClient().GetProjectConversationsClient().CreateProjectConversationAsync();
|
|
return response.Value.Id;
|
|
}
|
|
|
|
public async Task<List<ChatMessage>> GetChatHistoryAsync(AIAgent agent, AgentSession session)
|
|
{
|
|
var chatClientSession = (ChatClientAgentSession)session;
|
|
|
|
if (chatClientSession.ConversationId?.StartsWith("conv_", StringComparison.OrdinalIgnoreCase) == true)
|
|
{
|
|
// Conversation sessions do not persist message history.
|
|
return await this.GetChatHistoryFromConversationAsync(chatClientSession.ConversationId);
|
|
}
|
|
|
|
if (chatClientSession.ConversationId?.StartsWith("resp_", StringComparison.OrdinalIgnoreCase) == true)
|
|
{
|
|
return await this.GetChatHistoryFromResponsesChainAsync(chatClientSession.ConversationId);
|
|
}
|
|
|
|
var chatHistoryProvider = agent.GetService<ChatHistoryProvider>();
|
|
|
|
if (chatHistoryProvider is null)
|
|
{
|
|
return [];
|
|
}
|
|
|
|
return (await chatHistoryProvider.InvokingAsync(new(agent, session, []))).ToList();
|
|
}
|
|
|
|
private async Task<List<ChatMessage>> GetChatHistoryFromResponsesChainAsync(string conversationId)
|
|
{
|
|
var openAIResponseClient = this._client.GetProjectOpenAIClient().GetProjectResponsesClient();
|
|
var inputItems = await openAIResponseClient.GetResponseInputItemsAsync(conversationId).ToListAsync();
|
|
var response = await openAIResponseClient.GetResponseAsync(conversationId);
|
|
var responseItem = response.Value.OutputItems.FirstOrDefault()!;
|
|
|
|
// Take the messages that were the chat history leading up to the current response
|
|
// remove the instruction messages, and reverse the order so that the most recent message is last.
|
|
var previousMessages = inputItems
|
|
.Select(ConvertToChatMessage)
|
|
.Where(x => x.Text != "You are a helpful assistant.")
|
|
.Reverse();
|
|
|
|
// Convert the response item to a chat message.
|
|
var responseMessage = ConvertToChatMessage(responseItem);
|
|
|
|
// Concatenate the previous messages with the response message to get a full chat history
|
|
// that includes the current response.
|
|
return [.. previousMessages, responseMessage];
|
|
}
|
|
|
|
private static ChatMessage ConvertToChatMessage(ResponseItem item)
|
|
{
|
|
if (item is MessageResponseItem messageResponseItem)
|
|
{
|
|
var role = messageResponseItem.Role == MessageRole.User ? ChatRole.User : ChatRole.Assistant;
|
|
return new ChatMessage(role, messageResponseItem.Content.FirstOrDefault()?.Text);
|
|
}
|
|
|
|
throw new NotSupportedException("This test currently only supports text messages");
|
|
}
|
|
|
|
private async Task<List<ChatMessage>> GetChatHistoryFromConversationAsync(string conversationId)
|
|
{
|
|
List<ChatMessage> messages = [];
|
|
await foreach (AgentResponseItem item in this._client.GetProjectOpenAIClient().GetProjectConversationsClient().GetProjectConversationItemsAsync(conversationId, order: "asc"))
|
|
{
|
|
var openAIItem = item.AsResponseResultItem();
|
|
if (openAIItem is MessageResponseItem messageItem)
|
|
{
|
|
messages.Add(new ChatMessage
|
|
{
|
|
Role = new ChatRole(messageItem.Role.ToString()),
|
|
Contents = messageItem.Content
|
|
.Where(c => c.Kind is ResponseContentPartKind.OutputText or ResponseContentPartKind.InputText)
|
|
.Select(c => new TextContent(c.Text))
|
|
.ToList<AIContent>()
|
|
});
|
|
}
|
|
}
|
|
|
|
return messages;
|
|
}
|
|
|
|
public async Task<ChatClientAgent> CreateChatClientAgentAsync(
|
|
string name = "HelpfulAssistant",
|
|
string instructions = "You are a helpful assistant.",
|
|
IList<AITool>? aiTools = null)
|
|
{
|
|
return await this._client.CreateAIAgentAsync(GenerateUniqueAgentName(name), model: TestConfiguration.GetRequiredValue(TestSettings.AzureAIModelDeploymentName), instructions: instructions, tools: aiTools);
|
|
}
|
|
|
|
public async Task<ChatClientAgent> CreateChatClientAgentAsync(ChatClientAgentOptions options)
|
|
{
|
|
options.Name ??= GenerateUniqueAgentName("HelpfulAssistant");
|
|
|
|
return await this._client.CreateAIAgentAsync(model: TestConfiguration.GetRequiredValue(TestSettings.AzureAIModelDeploymentName), options);
|
|
}
|
|
|
|
public static string GenerateUniqueAgentName(string baseName) =>
|
|
$"{baseName}-{Guid.NewGuid().ToString("N").Substring(0, 8)}";
|
|
|
|
public Task DeleteAgentAsync(ChatClientAgent agent) =>
|
|
this._client.Agents.DeleteAgentAsync(agent.Name);
|
|
|
|
public async Task DeleteSessionAsync(AgentSession session)
|
|
{
|
|
var typedSession = (ChatClientAgentSession)session;
|
|
if (typedSession.ConversationId?.StartsWith("conv_", StringComparison.OrdinalIgnoreCase) == true)
|
|
{
|
|
await this._client.GetProjectOpenAIClient().GetProjectConversationsClient().DeleteConversationAsync(typedSession.ConversationId);
|
|
}
|
|
else if (typedSession.ConversationId?.StartsWith("resp_", StringComparison.OrdinalIgnoreCase) == true)
|
|
{
|
|
await this.DeleteResponseChainAsync(typedSession.ConversationId!);
|
|
}
|
|
}
|
|
|
|
private async Task DeleteResponseChainAsync(string lastResponseId)
|
|
{
|
|
var response = await this._client.GetProjectOpenAIClient().GetProjectResponsesClient().GetResponseAsync(lastResponseId);
|
|
await this._client.GetProjectOpenAIClient().GetProjectResponsesClient().DeleteResponseAsync(lastResponseId);
|
|
|
|
if (response.Value.PreviousResponseId is not null)
|
|
{
|
|
await this.DeleteResponseChainAsync(response.Value.PreviousResponseId);
|
|
}
|
|
}
|
|
|
|
public ValueTask DisposeAsync()
|
|
{
|
|
GC.SuppressFinalize(this);
|
|
|
|
if (this._client is not null && this._agent is not null)
|
|
{
|
|
return new ValueTask(this._client.Agents.DeleteAgentAsync(this._agent.Name));
|
|
}
|
|
|
|
return default;
|
|
}
|
|
|
|
public virtual async ValueTask InitializeAsync()
|
|
{
|
|
this._client = new(new Uri(TestConfiguration.GetRequiredValue(TestSettings.AzureAIProjectEndpoint)), TestAzureCliCredentials.CreateAzureCliCredential());
|
|
this._agent = await this.CreateChatClientAgentAsync();
|
|
}
|
|
|
|
public async Task InitializeAsync(ChatClientAgentOptions options)
|
|
{
|
|
this._client = new(new Uri(TestConfiguration.GetRequiredValue(TestSettings.AzureAIProjectEndpoint)), TestAzureCliCredentials.CreateAzureCliCredential());
|
|
this._agent = await this.CreateChatClientAgentAsync(options);
|
|
}
|
|
}
|