Compare commits

..
240 changed files with 4954 additions and 3325 deletions
+1 -2
View File
@@ -29,8 +29,7 @@ using types like `IChatClient`, `FunctionInvokingChatClient`, `AITool`, `AIFunct
## Key Conventions
- **Command output capture**: When running `dotnet build`, `dotnet test`, `dotnet format`, or similar commands, redirect output to a temp file first (e.g., `dotnet build --tl:off 2>&1 | Out-File $env:TEMP\build.log`), then analyze the file as needed. This avoids re-running expensive commands when the initial analysis misses something.
- **Encoding**: All new files must be saved with UTF-8 encoding with BOM (Byte Order Mark). This is required for `dotnet format` to work correctly. When using PowerShell `Set-Content`, always pass `-Encoding UTF8BOM` to preserve the BOM (e.g., `Set-Content $file $content -NoNewline -Encoding UTF8BOM`).
- **Encoding**: All new files must be saved with UTF-8 encoding with BOM (Byte Order Mark). This is required for `dotnet format` to work correctly.
- **Copyright header**: `// Copyright (c) Microsoft. All rights reserved.` at top of all `.cs` files
- **XML docs**: Required for all public methods and classes
- **Async**: Use `Async` suffix for methods returning `Task`/`ValueTask`
+1 -1
View File
@@ -17,7 +17,7 @@
<PropertyGroup>
<IsReleaseCandidate>false</IsReleaseCandidate>
<IsReleased>false</IsReleased>
<IsGenerallyAvailable>false</IsGenerallyAvailable>
</PropertyGroup>
<PropertyGroup>
+3 -3
View File
@@ -19,10 +19,10 @@
<PackageVersion Include="Aspire.Microsoft.Azure.Cosmos" Version="$(AspireAppHostSdkVersion)" />
<PackageVersion Include="CommunityToolkit.Aspire.OllamaSharp" Version="13.0.0" />
<!-- Azure.* -->
<PackageVersion Include="Azure.AI.Projects" Version="2.0.0" />
<PackageVersion Include="Azure.AI.Projects" Version="2.0.0-beta.2" />
<PackageVersion Include="Azure.AI.Agents.Persistent" Version="1.2.0-beta.10" />
<PackageVersion Include="Azure.AI.OpenAI" Version="2.9.0-beta.1" />
<PackageVersion Include="Azure.Identity" Version="1.20.0" />
<PackageVersion Include="Azure.Identity" Version="1.19.0" />
<PackageVersion Include="Azure.Monitor.OpenTelemetry.Exporter" Version="1.4.0" />
<!-- Google Gemini -->
<PackageVersion Include="Google.GenAI" Version="1.6.0" />
@@ -35,7 +35,7 @@
<PackageVersion Include="Microsoft.Bcl.AsyncInterfaces" Version="10.0.4" />
<PackageVersion Include="Microsoft.Bcl.HashCode" Version="6.0.0" />
<PackageVersion Include="Microsoft.Bcl.Memory" Version="10.0.4" />
<PackageVersion Include="System.ClientModel" Version="1.10.0" />
<PackageVersion Include="System.ClientModel" Version="1.9.0" />
<PackageVersion Include="System.CodeDom" Version="10.0.0" />
<PackageVersion Include="System.Collections.Immutable" Version="10.0.1" />
<PackageVersion Include="System.CommandLine" Version="2.0.0-rc.2.25502.107" />
+1
View File
@@ -34,6 +34,7 @@
<Project Path="samples/02-agents/AgentProviders/Agent_With_GoogleGemini/Agent_With_GoogleGemini.csproj" />
<Project Path="samples/02-agents/AgentProviders/Agent_With_Ollama/Agent_With_Ollama.csproj" />
<Project Path="samples/02-agents/AgentProviders/Agent_With_ONNX/Agent_With_ONNX.csproj" />
<Project Path="samples/02-agents/AgentProviders/Agent_With_OpenAIAssistants/Agent_With_OpenAIAssistants.csproj" />
<Project Path="samples/02-agents/AgentProviders/Agent_With_OpenAIChatCompletion/Agent_With_OpenAIChatCompletion.csproj" />
<Project Path="samples/02-agents/AgentProviders/Agent_With_OpenAIResponses/Agent_With_OpenAIResponses.csproj" />
</Folder>
@@ -781,6 +781,19 @@ internal static class AgentsSamples
SkipReason = "Requires local Ollama server.",
},
new SampleDefinition
{
Name = "Agent_With_OpenAIAssistants",
ProjectPath = "samples/02-agents/AgentProviders/Agent_With_OpenAIAssistants",
RequiredEnvironmentVariables = ["OPENAI_API_KEY"],
OptionalEnvironmentVariables = ["OPENAI_CHAT_MODEL_NAME"],
ExpectedOutputDescription =
[
"The output should contain a joke about a pirate from the OpenAI Assistants API.",
"The output should not contain error messages or stack traces.",
],
},
new SampleDefinition
{
Name = "Agent_With_OpenAIChatCompletion",
+6 -7
View File
@@ -2,20 +2,19 @@
<PropertyGroup>
<!-- Central version prefix - applies to all nuget packages. -->
<VersionPrefix>1.0.0</VersionPrefix>
<RCNumber>6</RCNumber>
<RCNumber>5</RCNumber>
<PackageVersion Condition="'$(IsReleaseCandidate)' == 'true'">$(VersionPrefix)-rc$(RCNumber)</PackageVersion>
<PackageVersion Condition="'$(IsReleaseCandidate)' != 'true' AND '$(VersionSuffix)' != ''">$(VersionPrefix)-$(VersionSuffix).260402.1</PackageVersion>
<PackageVersion Condition="'$(IsReleaseCandidate)' != 'true' AND '$(VersionSuffix)' == ''">$(VersionPrefix)-preview.260402.1</PackageVersion>
<PackageVersion Condition="'$(IsReleased)' == 'true'">$(VersionPrefix)</PackageVersion>
<GitTag>1.0.0</GitTag>
<PackageVersion Condition="'$(IsReleaseCandidate)' != 'true' AND '$(VersionSuffix)' != ''">$(VersionPrefix)-$(VersionSuffix).260330.1</PackageVersion>
<PackageVersion Condition="'$(IsReleaseCandidate)' != 'true' AND '$(VersionSuffix)' == ''">$(VersionPrefix)-preview.260330.1</PackageVersion>
<GitTag>1.0.0-rc5</GitTag>
<Configurations>Debug;Release;Publish</Configurations>
<IsPackable>true</IsPackable>
<!-- Package validation. Baseline Version should be the latest version available on NuGet. -->
<PackageValidationBaselineVersion>1.0.0-rc5</PackageValidationBaselineVersion>
<PackageValidationBaselineVersion>1.0.0-rc4</PackageValidationBaselineVersion>
<!-- Enable validation for RC packages and GA packages -->
<EnablePackageValidation Condition="'$(IsReleaseCandidate)' == 'true' OR '$(IsReleased)' == 'true'">true</EnablePackageValidation>
<EnablePackageValidation Condition="'$(IsReleaseCandidate)' == 'true' OR '$(IsGenerallyAvailable)' == 'true'">true</EnablePackageValidation>
<!-- Validate assembly attributes only for Publish builds -->
<NoWarn Condition="'$(Configuration)' != 'Publish'">$(NoWarn);CP0003</NoWarn>
<!-- Do not validate reference assemblies -->
@@ -20,10 +20,10 @@ const string JokerName = "JokerAgent";
var aiProjectClient = new AIProjectClient(new Uri(endpoint), new DefaultAzureCredential());
// Define the agent you want to create. (Prompt Agent in this case)
var agentVersionCreationOptions = new ProjectsAgentVersionCreationOptions(new DeclarativeAgentDefinition(model: deploymentName) { Instructions = "You are good at telling jokes." });
var agentVersionCreationOptions = new AgentVersionCreationOptions(new PromptAgentDefinition(model: deploymentName) { Instructions = "You are good at telling jokes." });
// Azure.AI.Agents SDK creates and manages agent by name and versions.
// You can create a server side agent version with the Azure.AI.Agents SDK client below.
var createdAgentVersion = aiProjectClient.AgentAdministrationClient.CreateAgentVersion(agentName: JokerName, options: agentVersionCreationOptions);
var createdAgentVersion = aiProjectClient.Agents.CreateAgentVersion(agentName: JokerName, options: agentVersionCreationOptions);
// Note:
// agentVersion.Id = "<agentName>:<versionNumber>",
@@ -34,15 +34,15 @@ var createdAgentVersion = aiProjectClient.AgentAdministrationClient.CreateAgentV
FoundryAgent existingJokerAgent = aiProjectClient.AsAIAgent(createdAgentVersion);
// You can also create another AIAgent version by providing the same name with a different definition.
ProjectsAgentVersion newJokerAgentVersion = await aiProjectClient.AgentAdministrationClient.CreateAgentVersionAsync(
AgentVersion newJokerAgentVersion = await aiProjectClient.Agents.CreateAgentVersionAsync(
JokerName,
new ProjectsAgentVersionCreationOptions(new DeclarativeAgentDefinition(model: deploymentName) { Instructions = "You are extremely hilarious at telling jokes." }));
new AgentVersionCreationOptions(new PromptAgentDefinition(model: deploymentName) { Instructions = "You are extremely hilarious at telling jokes." }));
FoundryAgent newJokerAgent = aiProjectClient.AsAIAgent(newJokerAgentVersion);
// You can also get the AIAgent latest version just providing its name.
ProjectsAgentRecord jokerAgentRecord = await aiProjectClient.AgentAdministrationClient.GetAgentAsync(JokerName);
AgentRecord jokerAgentRecord = await aiProjectClient.Agents.GetAgentAsync(JokerName);
FoundryAgent jokerAgentLatest = aiProjectClient.AsAIAgent(jokerAgentRecord);
ProjectsAgentVersion latestAgentVersion = jokerAgentRecord.GetLatestVersion();
AgentVersion latestAgentVersion = jokerAgentRecord.GetLatestVersion();
// The AIAgent version can be accessed via the GetService method.
Console.WriteLine($"Latest agent version id: {latestAgentVersion.Id}");
@@ -55,4 +55,4 @@ Console.WriteLine(await jokerAgentLatest.RunAsync("Tell me a joke about a pirate
Console.WriteLine(await jokerAgentLatest.RunAsync("Now tell me a joke about a cat and a dog using last joke as the anchor.", session));
// Cleanup by agent name removes both agent versions created.
aiProjectClient.AgentAdministrationClient.DeleteAgent(existingJokerAgent.Name);
aiProjectClient.Agents.DeleteAgent(existingJokerAgent.Name);
@@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFrameworks>net10.0</TargetFrameworks>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\..\src\Microsoft.Agents.AI.OpenAI\Microsoft.Agents.AI.OpenAI.csproj" />
</ItemGroup>
</Project>
@@ -0,0 +1,41 @@
// Copyright (c) Microsoft. All rights reserved.
// This sample shows how to create and use a simple AI agent with OpenAI Assistants as the backend.
// WARNING: The Assistants API is deprecated and will be shut down.
// For more information see the OpenAI documentation: https://platform.openai.com/docs/assistants/migration
#pragma warning disable CS0618 // Type or member is obsolete - OpenAI Assistants API is deprecated but still used in this sample
using Microsoft.Agents.AI;
using OpenAI;
using OpenAI.Assistants;
var apiKey = Environment.GetEnvironmentVariable("OPENAI_API_KEY") ?? throw new InvalidOperationException("OPENAI_API_KEY is not set.");
var model = Environment.GetEnvironmentVariable("OPENAI_CHAT_MODEL_NAME") ?? "gpt-4o-mini";
const string JokerName = "Joker";
const string JokerInstructions = "You are good at telling jokes.";
// Get a client to create/retrieve server side agents with.
var assistantClient = new OpenAIClient(apiKey).GetAssistantClient();
// You can create a server side assistant with the OpenAI SDK.
var createResult = await assistantClient.CreateAssistantAsync(model, new() { Name = JokerName, Instructions = JokerInstructions });
// You can retrieve an already created server side assistant as an AIAgent.
AIAgent agent1 = await assistantClient.GetAIAgentAsync(createResult.Value.Id);
// You can also create a server side assistant and return it as an AIAgent directly.
AIAgent agent2 = await assistantClient.CreateAIAgentAsync(
model: model,
name: JokerName,
instructions: JokerInstructions);
// You can invoke the agent like any other AIAgent.
AgentSession session = await agent1.CreateSessionAsync();
Console.WriteLine(await agent1.RunAsync("Tell me a joke about a pirate.", session));
// Cleanup for sample purposes.
await assistantClient.DeleteAssistantAsync(agent1.Id);
await assistantClient.DeleteAssistantAsync(agent2.Id);
@@ -0,0 +1,16 @@
# Prerequisites
WARNING: The Assistants API is deprecated and will be shut down.
For more information see the OpenAI documentation: https://platform.openai.com/docs/assistants/migration
Before you begin, ensure you have the following prerequisites:
- .NET 10 SDK or later
- OpenAI API key
Set the following environment variables:
```powershell
$env:OPENAI_API_KEY="*****" # Replace with your OpenAI API key
$env:OPENAI_CHAT_MODEL_NAME="gpt-4o-mini" # Optional, defaults to gpt-4o-mini
```
@@ -25,6 +25,7 @@ See the README.md for each sample for the prerequisites for that sample.
|[Creating an AIAgent with GitHub Copilot](./Agent_With_GitHubCopilot/)|This sample demonstrates how to create an AIAgent using GitHub Copilot SDK as the underlying inference service|
|[Creating an AIAgent with Ollama](./Agent_With_Ollama/)|This sample demonstrates how to create an AIAgent using Ollama as the underlying inference service|
|[Creating an AIAgent with ONNX](./Agent_With_ONNX/)|This sample demonstrates how to create an AIAgent using ONNX as the underlying inference service|
|[Creating an AIAgent with OpenAI Assistants](./Agent_With_OpenAIAssistants/)|This sample demonstrates how to create an AIAgent using OpenAI Assistants as the underlying inference service.</br>WARNING: The Assistants API is deprecated and will be shut down. For more information see the OpenAI documentation: https://platform.openai.com/docs/assistants/migration|
|[Creating an AIAgent with OpenAI ChatCompletion](./Agent_With_OpenAIChatCompletion/)|This sample demonstrates how to create an AIAgent using OpenAI ChatCompletion as the underlying inference service|
|[Creating an AIAgent with OpenAI Responses](./Agent_With_OpenAIResponses/)|This sample demonstrates how to create an AIAgent using OpenAI Responses as the underlying inference service|
@@ -44,10 +44,10 @@ ClientResult<VectorStore> vectorStoreCreate = await vectorStoreClient.CreateVect
FileSearchTool fileSearchTool = new([vectorStoreCreate.Value.Id]);
#pragma warning restore OPENAI001
ProjectsAgentVersion agentVersion = await aiProjectClient.AgentAdministrationClient.CreateAgentVersionAsync(
AgentVersion agentVersion = await aiProjectClient.Agents.CreateAgentVersionAsync(
"AskContoso",
new ProjectsAgentVersionCreationOptions(
new DeclarativeAgentDefinition(model: deploymentName)
new AgentVersionCreationOptions(
new PromptAgentDefinition(model: deploymentName)
{
Instructions = "You are a helpful support specialist for Contoso Outdoors. Answer questions using the provided context and cite the source document when available.",
Tools = { fileSearchTool }
@@ -68,4 +68,4 @@ Console.WriteLine(await agent.RunAsync("What is the best way to maintain the Tra
// Cleanup
await fileClient.DeleteFileAsync(uploadResult.Value.Id);
await vectorStoreClient.DeleteVectorStoreAsync(vectorStoreCreate.Value.Id);
await aiProjectClient.AgentAdministrationClient.DeleteAgentAsync(agent.Name);
await aiProjectClient.Agents.DeleteAgentAsync(agent.Name);
@@ -19,10 +19,10 @@ var deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYME
var aiProjectClient = new AIProjectClient(new Uri(endpoint), new DefaultAzureCredential());
// Create a server side agent and expose it as an AIAgent.
ProjectsAgentVersion agentVersion = await aiProjectClient.AgentAdministrationClient.CreateAgentVersionAsync(
AgentVersion agentVersion = await aiProjectClient.Agents.CreateAgentVersionAsync(
"Joker",
new ProjectsAgentVersionCreationOptions(
new DeclarativeAgentDefinition(model: deploymentName)
new AgentVersionCreationOptions(
new PromptAgentDefinition(model: deploymentName)
{
Instructions = "You are good at telling jokes, and you always start each joke with 'Aye aye, captain!'.",
})
@@ -18,10 +18,10 @@ const string JokerName = "JokerAgent";
AIProjectClient aiProjectClient = new(new Uri(endpoint), new AzureCliCredential());
// Create a server-side agent version using the native SDK.
ProjectsAgentVersion agentVersion = await aiProjectClient.AgentAdministrationClient.CreateAgentVersionAsync(
AgentVersion agentVersion = await aiProjectClient.Agents.CreateAgentVersionAsync(
JokerName,
new ProjectsAgentVersionCreationOptions(
new DeclarativeAgentDefinition(model: deploymentName)
new AgentVersionCreationOptions(
new PromptAgentDefinition(model: deploymentName)
{
Instructions = "You are good at telling jokes.",
}));
@@ -33,4 +33,4 @@ FoundryAgent agent = aiProjectClient.AsAIAgent(agentVersion);
Console.WriteLine(await agent.RunAsync("Tell me a joke about a pirate."));
// Cleanup: deletes the agent and all its versions.
await aiProjectClient.AgentAdministrationClient.DeleteAgentAsync(agent.Name);
await aiProjectClient.Agents.DeleteAgentAsync(agent.Name);
@@ -7,7 +7,6 @@
using Azure.AI.Extensions.OpenAI;
using Azure.AI.Projects;
using Azure.AI.Projects.Agents;
using Azure.AI.Projects.Memory;
using Azure.Identity;
using Microsoft.Agents.AI;
using Microsoft.Agents.AI.Foundry;
@@ -31,10 +31,10 @@ var mcpTool = ResponseTool.CreateMcpTool(
toolCallApprovalPolicy: new McpToolCallApprovalPolicy(GlobalMcpToolCallApprovalPolicy.NeverRequireApproval));
// Create a server side agent with the mcp tool, and expose it as an AIAgent.
ProjectsAgentVersion agentVersion = await aiProjectClient.AgentAdministrationClient.CreateAgentVersionAsync(
AgentVersion agentVersion = await aiProjectClient.Agents.CreateAgentVersionAsync(
"MicrosoftLearnAgent",
new ProjectsAgentVersionCreationOptions(
new DeclarativeAgentDefinition(model: model)
new AgentVersionCreationOptions(
new PromptAgentDefinition(model: model)
{
Instructions = "You answer questions by searching the Microsoft Learn content only.",
Tools = { mcpTool }
@@ -47,7 +47,7 @@ AgentSession session = await agent.CreateSessionAsync();
Console.WriteLine(await agent.RunAsync("Please summarize the Azure AI Agent documentation related to MCP Tool calling?", session));
// Cleanup for sample purposes.
aiProjectClient.AgentAdministrationClient.DeleteAgent(agent.Name);
aiProjectClient.Agents.DeleteAgent(agent.Name);
// **** MCP Tool with Approval Required ****
// *****************************************
@@ -61,10 +61,10 @@ var mcpToolWithApproval = ResponseTool.CreateMcpTool(
toolCallApprovalPolicy: new McpToolCallApprovalPolicy(GlobalMcpToolCallApprovalPolicy.AlwaysRequireApproval));
// Create an agent with the MCP tool that requires approval.
ProjectsAgentVersion agentVersionWithApproval = await aiProjectClient.AgentAdministrationClient.CreateAgentVersionAsync(
AgentVersion agentVersionWithApproval = await aiProjectClient.Agents.CreateAgentVersionAsync(
"MicrosoftLearnAgentWithApproval",
new ProjectsAgentVersionCreationOptions(
new DeclarativeAgentDefinition(model: model)
new AgentVersionCreationOptions(
new PromptAgentDefinition(model: model)
{
Instructions = "You answer questions by searching the Microsoft Learn content only.",
Tools = { mcpToolWithApproval }
@@ -58,9 +58,9 @@ public static class Program
finally
{
// Cleanup the agents created for the sample.
await aiProjectClient.AgentAdministrationClient.DeleteAgentAsync(frenchAgent.Name);
await aiProjectClient.AgentAdministrationClient.DeleteAgentAsync(spanishAgent.Name);
await aiProjectClient.AgentAdministrationClient.DeleteAgentAsync(englishAgent.Name);
await aiProjectClient.Agents.DeleteAgentAsync(frenchAgent.Name);
await aiProjectClient.Agents.DeleteAgentAsync(spanishAgent.Name);
await aiProjectClient.Agents.DeleteAgentAsync(englishAgent.Name);
}
}
@@ -76,10 +76,10 @@ public static class Program
AIProjectClient aiProjectClient,
string model)
{
ProjectsAgentVersion agentVersion = await aiProjectClient.AgentAdministrationClient.CreateAgentVersionAsync(
AgentVersion agentVersion = await aiProjectClient.Agents.CreateAgentVersionAsync(
$"{targetLanguage} Translator",
new ProjectsAgentVersionCreationOptions(
new DeclarativeAgentDefinition(model: model)
new AgentVersionCreationOptions(
new PromptAgentDefinition(model: model)
{
Instructions = $"You are a translation assistant that translates the provided text to {targetLanguage}.",
}));
@@ -97,7 +97,7 @@ internal sealed class Program
agentDescription: "Escalate agent for human support");
}
private static DeclarativeAgentDefinition DefineSelfServiceAgent(IConfiguration configuration) =>
private static PromptAgentDefinition DefineSelfServiceAgent(IConfiguration configuration) =>
new(configuration.GetValue(Application.Settings.FoundryModel))
{
Instructions =
@@ -144,7 +144,7 @@ internal sealed class Program
}
};
private static DeclarativeAgentDefinition DefineTicketingAgent(IConfiguration configuration, TicketingPlugin plugin) =>
private static PromptAgentDefinition DefineTicketingAgent(IConfiguration configuration, TicketingPlugin plugin) =>
new(configuration.GetValue(Application.Settings.FoundryModel))
{
Instructions =
@@ -208,7 +208,7 @@ internal sealed class Program
}
};
private static DeclarativeAgentDefinition DefineTicketRoutingAgent(IConfiguration configuration, TicketingPlugin plugin) =>
private static PromptAgentDefinition DefineTicketRoutingAgent(IConfiguration configuration, TicketingPlugin plugin) =>
new(configuration.GetValue(Application.Settings.FoundryModel))
{
Instructions =
@@ -253,7 +253,7 @@ internal sealed class Program
}
};
private static DeclarativeAgentDefinition DefineWindowsSupportAgent(IConfiguration configuration, TicketingPlugin plugin) =>
private static PromptAgentDefinition DefineWindowsSupportAgent(IConfiguration configuration, TicketingPlugin plugin) =>
new(configuration.GetValue(Application.Settings.FoundryModel))
{
Instructions =
@@ -323,7 +323,7 @@ internal sealed class Program
}
};
private static DeclarativeAgentDefinition DefineResolutionAgent(IConfiguration configuration, TicketingPlugin plugin) =>
private static PromptAgentDefinition DefineResolutionAgent(IConfiguration configuration, TicketingPlugin plugin) =>
new(configuration.GetValue(Application.Settings.FoundryModel))
{
Instructions =
@@ -357,7 +357,7 @@ internal sealed class Program
}
};
private static DeclarativeAgentDefinition TicketEscalationAgent(IConfiguration configuration, TicketingPlugin plugin) =>
private static PromptAgentDefinition TicketEscalationAgent(IConfiguration configuration, TicketingPlugin plugin) =>
new(configuration.GetValue(Application.Settings.FoundryModel))
{
Instructions =
@@ -88,7 +88,7 @@ internal sealed class Program
agentDescription: "Weather agent for DeepResearch workflow");
}
private static DeclarativeAgentDefinition DefineResearchAgent(IConfiguration configuration) =>
private static PromptAgentDefinition DefineResearchAgent(IConfiguration configuration) =>
new(configuration.GetValue(Application.Settings.FoundryModel))
{
Instructions =
@@ -114,13 +114,13 @@ internal sealed class Program
""",
Tools =
{
//ProjectsAgentTool.CreateBingGroundingTool( // TODO: Use Bing Grounding when available
//AgentTool.CreateBingGroundingTool( // TODO: Use Bing Grounding when available
// new BingGroundingSearchToolParameters(
// [new BingGroundingSearchConfiguration(this.GetSetting(Settings.FoundryGroundingTool))]))
}
};
private static DeclarativeAgentDefinition DefinePlannerAgent(IConfiguration configuration) =>
private static PromptAgentDefinition DefinePlannerAgent(IConfiguration configuration) =>
new(configuration.GetValue(Application.Settings.FoundryModel))
{
Instructions = // TODO: Use Structured Inputs / Prompt Template
@@ -139,7 +139,7 @@ internal sealed class Program
"""
};
private static DeclarativeAgentDefinition DefineManagerAgent(IConfiguration configuration) =>
private static PromptAgentDefinition DefineManagerAgent(IConfiguration configuration) =>
new(configuration.GetValue(Application.Settings.FoundryModel))
{
Instructions = // TODO: Use Structured Inputs / Prompt Template
@@ -225,7 +225,7 @@ internal sealed class Program
}
};
private static DeclarativeAgentDefinition DefineSummaryAgent(IConfiguration configuration) =>
private static PromptAgentDefinition DefineSummaryAgent(IConfiguration configuration) =>
new(configuration.GetValue(Application.Settings.FoundryModel))
{
Instructions =
@@ -240,18 +240,18 @@ internal sealed class Program
"""
};
private static DeclarativeAgentDefinition DefineKnowledgeAgent(IConfiguration configuration) =>
private static PromptAgentDefinition DefineKnowledgeAgent(IConfiguration configuration) =>
new(configuration.GetValue(Application.Settings.FoundryModel))
{
Tools =
{
//ProjectsAgentTool.CreateBingGroundingTool( // TODO: Use Bing Grounding when available
//AgentTool.CreateBingGroundingTool( // TODO: Use Bing Grounding when available
// new BingGroundingSearchToolParameters(
// [new BingGroundingSearchConfiguration(this.GetSetting(Settings.FoundryGroundingTool))]))
}
};
private static DeclarativeAgentDefinition DefineCoderAgent(IConfiguration configuration) =>
private static PromptAgentDefinition DefineCoderAgent(IConfiguration configuration) =>
new(configuration.GetValue(Application.Settings.FoundryModel))
{
Instructions =
@@ -265,7 +265,7 @@ internal sealed class Program
}
};
private static DeclarativeAgentDefinition DefineWeatherAgent(IConfiguration configuration) =>
private static PromptAgentDefinition DefineWeatherAgent(IConfiguration configuration) =>
new(configuration.GetValue(Application.Settings.FoundryModel))
{
Instructions =
@@ -274,7 +274,7 @@ internal sealed class Program
""",
Tools =
{
ProjectsAgentTool.CreateOpenApiTool(
AgentTool.CreateOpenApiTool(
new OpenApiFunctionDefinition(
"weather-forecast",
BinaryData.FromString(File.ReadAllText(Path.Combine(AppContext.BaseDirectory, "wttr.json"))),
@@ -67,9 +67,9 @@ internal sealed class Program
agentDescription: "Provides information about the restaurant menu");
}
private static DeclarativeAgentDefinition DefineMenuAgent(IConfiguration configuration, AIFunction[] functions)
private static PromptAgentDefinition DefineMenuAgent(IConfiguration configuration, AIFunction[] functions)
{
DeclarativeAgentDefinition agentDefinition =
PromptAgentDefinition agentDefinition =
new(configuration.GetValue(Application.Settings.FoundryModel))
{
Instructions =
@@ -46,7 +46,7 @@ internal sealed class Program
await CreateAgentsAsync(aiProjectClient, configuration);
// Ensure workflow agent exists in Foundry.
ProjectsAgentVersion agentVersion = await CreateWorkflowAsync(aiProjectClient, configuration);
AgentVersion agentVersion = await CreateWorkflowAsync(aiProjectClient, configuration);
string workflowInput = GetWorkflowInput(args);
@@ -86,7 +86,7 @@ internal sealed class Program
}
}
private static async Task<ProjectsAgentVersion> CreateWorkflowAsync(AIProjectClient agentClient, IConfiguration configuration)
private static async Task<AgentVersion> CreateWorkflowAsync(AIProjectClient agentClient, IConfiguration configuration)
{
string workflowYaml = File.ReadAllText("MathChat.yaml");
@@ -114,7 +114,7 @@ internal sealed class Program
agentDescription: "Teacher agent for MathChat workflow");
}
private static DeclarativeAgentDefinition DefineStudentAgent(IConfiguration configuration) =>
private static PromptAgentDefinition DefineStudentAgent(IConfiguration configuration) =>
new(configuration.GetValue(Application.Settings.FoundryModel))
{
Instructions =
@@ -127,7 +127,7 @@ internal sealed class Program
"""
};
private static DeclarativeAgentDefinition DefineTeacherAgent(IConfiguration configuration) =>
private static PromptAgentDefinition DefineTeacherAgent(IConfiguration configuration) =>
new(configuration.GetValue(Application.Settings.FoundryModel))
{
Instructions =
@@ -68,7 +68,7 @@ internal sealed class Program
agentDescription: "Chats with the user with location awareness.");
}
private static DeclarativeAgentDefinition DefineLocationTriageAgent(IConfiguration configuration) =>
private static PromptAgentDefinition DefineLocationTriageAgent(IConfiguration configuration) =>
new(configuration.GetValue(Application.Settings.FoundryModel))
{
Instructions =
@@ -79,7 +79,7 @@ internal sealed class Program
"""
};
private static DeclarativeAgentDefinition DefineLocationCaptureAgent(IConfiguration configuration) =>
private static PromptAgentDefinition DefineLocationCaptureAgent(IConfiguration configuration) =>
new(configuration.GetValue(Application.Settings.FoundryModel))
{
Instructions =
@@ -128,7 +128,7 @@ internal sealed class Program
}
};
private static DeclarativeAgentDefinition DefineLocationAwareAgent(IConfiguration configuration) =>
private static PromptAgentDefinition DefineLocationAwareAgent(IConfiguration configuration) =>
new(configuration.GetValue(Application.Settings.FoundryModel))
{
// Parameterized instructions reference the "location" input argument.
@@ -63,9 +63,9 @@ internal sealed class Program
agentDescription: "Provides information about the restaurant menu");
}
private static DeclarativeAgentDefinition DefineMenuAgent(IConfiguration configuration, AIFunction[] functions)
private static PromptAgentDefinition DefineMenuAgent(IConfiguration configuration, AIFunction[] functions)
{
DeclarativeAgentDefinition agentDefinition =
PromptAgentDefinition agentDefinition =
new(configuration.GetValue(Application.Settings.FoundryModel))
{
Instructions =
@@ -125,9 +125,9 @@ internal sealed class Program
agentDescription: "Provides information based on search results");
}
private static DeclarativeAgentDefinition DefineSearchAgent(IConfiguration configuration)
private static PromptAgentDefinition DefineSearchAgent(IConfiguration configuration)
{
return new DeclarativeAgentDefinition(configuration.GetValue(Application.Settings.FoundryModel))
return new PromptAgentDefinition(configuration.GetValue(Application.Settings.FoundryModel))
{
Instructions =
"""
@@ -67,7 +67,7 @@ internal sealed class Program
agentDescription: "Editor agent for Marketing workflow");
}
private static DeclarativeAgentDefinition DefineAnalystAgent(IConfiguration configuration) =>
private static PromptAgentDefinition DefineAnalystAgent(IConfiguration configuration) =>
new(configuration.GetValue(Application.Settings.FoundryModel))
{
Instructions =
@@ -79,13 +79,13 @@ internal sealed class Program
""",
Tools =
{
//ProjectsAgentTool.CreateBingGroundingTool( // TODO: Use Bing Grounding when available
//AgentTool.CreateBingGroundingTool( // TODO: Use Bing Grounding when available
// new BingGroundingSearchToolParameters(
// [new BingGroundingSearchConfiguration(configuration[Application.Settings.FoundryGroundingTool])]))
}
};
private static DeclarativeAgentDefinition DefineWriterAgent(IConfiguration configuration) =>
private static PromptAgentDefinition DefineWriterAgent(IConfiguration configuration) =>
new(configuration.GetValue(Application.Settings.FoundryModel))
{
Instructions =
@@ -96,7 +96,7 @@ internal sealed class Program
"""
};
private static DeclarativeAgentDefinition DefineEditorAgent(IConfiguration configuration) =>
private static PromptAgentDefinition DefineEditorAgent(IConfiguration configuration) =>
new(configuration.GetValue(Application.Settings.FoundryModel))
{
Instructions =
@@ -62,7 +62,7 @@ internal sealed class Program
agentDescription: "Teacher agent for MathChat workflow");
}
private static DeclarativeAgentDefinition DefineStudentAgent(IConfiguration configuration) =>
private static PromptAgentDefinition DefineStudentAgent(IConfiguration configuration) =>
new(configuration.GetValue(Application.Settings.FoundryModel))
{
Instructions =
@@ -75,7 +75,7 @@ internal sealed class Program
"""
};
private static DeclarativeAgentDefinition DefineTeacherAgent(IConfiguration configuration) =>
private static PromptAgentDefinition DefineTeacherAgent(IConfiguration configuration) =>
new(configuration.GetValue(Application.Settings.FoundryModel))
{
Instructions =
@@ -58,7 +58,7 @@ internal sealed class Program
agentDescription: "Searches documents on Microsoft Learn");
}
private static DeclarativeAgentDefinition DefineSearchAgent(IConfiguration configuration) =>
private static PromptAgentDefinition DefineSearchAgent(IConfiguration configuration) =>
new(configuration.GetValue(Application.Settings.FoundryModel))
{
Instructions =
@@ -6,7 +6,6 @@
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<NoWarn>$(NoWarn);MAAIW001</NoWarn> <!-- Handoff Orchestrations are Experimental -->
</PropertyGroup>
<ItemGroup>
@@ -20,7 +20,7 @@ internal static class HostAgentFactory
// latency issues, unintended credential probing, and potential security risks from fallback mechanisms.
var aiProjectClient = new AIProjectClient(new Uri(endpoint), new DefaultAzureCredential());
ProjectsAgentRecord agentRecord = await aiProjectClient.AgentAdministrationClient.GetAgentAsync(agentName);
AgentRecord agentRecord = await aiProjectClient.Agents.GetAgentAsync(agentName);
AIAgent agent = aiProjectClient.AsAIAgent(agentRecord, tools: tools);
AgentCard agentCard = agentType.ToUpperInvariant() switch
@@ -3,7 +3,7 @@
<PropertyGroup>
<RootNamespace>Microsoft.Agents.AI</RootNamespace>
<NoWarn>$(NoWarn);MEAI001</NoWarn>
<IsReleased>true</IsReleased>
<IsReleaseCandidate>true</IsReleaseCandidate>
</PropertyGroup>
<PropertyGroup>
@@ -25,8 +25,8 @@ internal sealed class AzureAIProjectChatClient : DelegatingChatClient
{
private readonly ChatClientMetadata? _metadata;
private readonly AIProjectClient _agentClient;
private readonly ProjectsAgentVersion? _agentVersion;
private readonly ProjectsAgentRecord? _agentRecord;
private readonly AgentVersion? _agentVersion;
private readonly AgentRecord? _agentRecord;
private readonly ChatOptions? _chatOptions;
private readonly AgentReference _agentReference;
@@ -56,34 +56,34 @@ internal sealed class AzureAIProjectChatClient : DelegatingChatClient
/// Initializes a new instance of the <see cref="AzureAIProjectChatClient"/> class.
/// </summary>
/// <param name="aiProjectClient">An instance of <see cref="AIProjectClient"/> to interact with Azure AI Agents services.</param>
/// <param name="agentRecord">An instance of <see cref="ProjectsAgentRecord"/> representing the specific agent to use.</param>
/// <param name="agentRecord">An instance of <see cref="AgentRecord"/> representing the specific agent to use.</param>
/// <param name="chatOptions">An instance of <see cref="ChatOptions"/> representing the options on how the agent was predefined.</param>
/// <remarks>
/// The <see cref="IChatClient"/> provided should be decorated with a <see cref="AzureAIProjectChatClient"/> for proper functionality.
/// </remarks>
internal AzureAIProjectChatClient(AIProjectClient aiProjectClient, ProjectsAgentRecord agentRecord, ChatOptions? chatOptions)
internal AzureAIProjectChatClient(AIProjectClient aiProjectClient, AgentRecord agentRecord, ChatOptions? chatOptions)
: this(aiProjectClient, Throw.IfNull(agentRecord).GetLatestVersion(), chatOptions)
{
this._agentRecord = agentRecord;
}
internal AzureAIProjectChatClient(AIProjectClient aiProjectClient, ProjectsAgentVersion agentVersion, ChatOptions? chatOptions)
internal AzureAIProjectChatClient(AIProjectClient aiProjectClient, AgentVersion agentVersion, ChatOptions? chatOptions)
: this(
aiProjectClient,
CreateAgentReference(Throw.IfNull(agentVersion)),
(agentVersion.Definition as DeclarativeAgentDefinition)?.Model,
(agentVersion.Definition as PromptAgentDefinition)?.Model,
chatOptions)
{
this._agentVersion = agentVersion;
}
/// <summary>
/// Creates an <see cref="AgentReference"/> from an <see cref="ProjectsAgentVersion"/>.
/// Creates an <see cref="AgentReference"/> from an <see cref="AgentVersion"/>.
/// Uses the agent version's version if available, otherwise defaults to "latest".
/// </summary>
/// <param name="agentVersion">The agent version to create a reference from.</param>
/// <returns>An <see cref="AgentReference"/> for the specified agent version.</returns>
private static AgentReference CreateAgentReference(ProjectsAgentVersion agentVersion)
private static AgentReference CreateAgentReference(AgentVersion agentVersion)
{
// If the version is null, empty, or whitespace, use "latest" as the default.
// This handles cases where hosted agents (like MCP agents) may not have a version assigned.
@@ -98,9 +98,9 @@ internal sealed class AzureAIProjectChatClient : DelegatingChatClient
? this._metadata
: (serviceKey is null && serviceType == typeof(AIProjectClient))
? this._agentClient
: (serviceKey is null && serviceType == typeof(ProjectsAgentVersion))
: (serviceKey is null && serviceType == typeof(AgentVersion))
? this._agentVersion
: (serviceKey is null && serviceType == typeof(ProjectsAgentRecord))
: (serviceKey is null && serviceType == typeof(AgentRecord))
? this._agentRecord
: (serviceKey is null && serviceType == typeof(AgentReference))
? this._agentReference
@@ -38,7 +38,7 @@ public static partial class AzureAIProjectChatClientExtensions
/// <exception cref="InvalidOperationException">The agent with the specified name was not found.</exception>
/// <remarks>
/// When instantiating a <see cref="ChatClientAgent"/> by using an <see cref="AgentReference"/>, minimal information will be available about the agent in the instance level, and any logic that relies
/// on <see cref="AIAgent.GetService{TService}(object?)"/> to retrieve information about the agent like <see cref="ProjectsAgentVersion" /> will receive <see langword="null"/> as the result.
/// on <see cref="AIAgent.GetService{TService}(object?)"/> to retrieve information about the agent like <see cref="AgentVersion" /> will receive <see langword="null"/> as the result.
/// </remarks>
public static FoundryAgent AsAIAgent(
this AIProjectClient aiProjectClient,
@@ -67,7 +67,7 @@ public static partial class AzureAIProjectChatClientExtensions
}
/// <summary>
/// Uses an existing server side agent, wrapped as a <see cref="ChatClientAgent"/> using the provided <see cref="AIProjectClient"/> and <see cref="ProjectsAgentRecord"/>.
/// Uses an existing server side agent, wrapped as a <see cref="ChatClientAgent"/> using the provided <see cref="AIProjectClient"/> and <see cref="AgentRecord"/>.
/// </summary>
/// <param name="aiProjectClient">The client used to interact with Azure AI Agents. Cannot be <see langword="null"/>.</param>
/// <param name="agentRecord">The agent record to be converted. The latest version will be used. Cannot be <see langword="null"/>.</param>
@@ -78,7 +78,7 @@ public static partial class AzureAIProjectChatClientExtensions
/// <exception cref="ArgumentNullException">Thrown when <paramref name="aiProjectClient"/> or <paramref name="agentRecord"/> is <see langword="null"/>.</exception>
public static FoundryAgent AsAIAgent(
this AIProjectClient aiProjectClient,
ProjectsAgentRecord agentRecord,
AgentRecord agentRecord,
IList<AITool>? tools = null,
Func<IChatClient, IChatClient>? clientFactory = null,
IServiceProvider? services = null)
@@ -100,7 +100,7 @@ public static partial class AzureAIProjectChatClientExtensions
}
/// <summary>
/// Uses an existing server side agent, wrapped as a <see cref="ChatClientAgent"/> using the provided <see cref="AIProjectClient"/> and <see cref="ProjectsAgentVersion"/>.
/// Uses an existing server side agent, wrapped as a <see cref="ChatClientAgent"/> using the provided <see cref="AIProjectClient"/> and <see cref="AgentVersion"/>.
/// </summary>
/// <param name="aiProjectClient">The client used to interact with Azure AI Agents. Cannot be <see langword="null"/>.</param>
/// <param name="agentVersion">The agent version to be converted. Cannot be <see langword="null"/>.</param>
@@ -111,7 +111,7 @@ public static partial class AzureAIProjectChatClientExtensions
/// <exception cref="ArgumentNullException">Thrown when <paramref name="aiProjectClient"/> or <paramref name="agentVersion"/> is <see langword="null"/>.</exception>
public static FoundryAgent AsAIAgent(
this AIProjectClient aiProjectClient,
ProjectsAgentVersion agentVersion,
AgentVersion agentVersion,
IList<AITool>? tools = null,
Func<IChatClient, IChatClient>? clientFactory = null,
IServiceProvider? services = null)
@@ -206,7 +206,7 @@ public static partial class AzureAIProjectChatClientExtensions
/// <summary>Creates a <see cref="ChatClientAgent"/> with the specified options.</summary>
private static ChatClientAgent CreateChatClientAgent(
AIProjectClient aiProjectClient,
ProjectsAgentVersion agentVersion,
AgentVersion agentVersion,
ChatClientAgentOptions agentOptions,
Func<IChatClient, IChatClient>? clientFactory,
IServiceProvider? services)
@@ -249,7 +249,7 @@ public static partial class AzureAIProjectChatClientExtensions
/// <summary>This method creates an <see cref="ChatClientAgent"/> with the specified ChatClientAgentOptions.</summary>
private static ChatClientAgent AsChatClientAgent(
AIProjectClient aiProjectClient,
ProjectsAgentVersion agentVersion,
AgentVersion agentVersion,
ChatClientAgentOptions agentOptions,
Func<IChatClient, IChatClient>? clientFactory,
IServiceProvider? services)
@@ -258,7 +258,7 @@ public static partial class AzureAIProjectChatClientExtensions
/// <summary>This method creates an <see cref="ChatClientAgent"/> with the specified ChatClientAgentOptions.</summary>
private static ChatClientAgent AsChatClientAgent(
AIProjectClient aiProjectClient,
ProjectsAgentRecord agentRecord,
AgentRecord agentRecord,
ChatClientAgentOptions agentOptions,
Func<IChatClient, IChatClient>? clientFactory,
IServiceProvider? services)
@@ -293,14 +293,14 @@ public static partial class AzureAIProjectChatClientExtensions
/// <summary>This method creates an <see cref="ChatClientAgent"/> with a auto-generated ChatClientAgentOptions from the specified configuration parameters.</summary>
private static ChatClientAgent AsChatClientAgent(
AIProjectClient aiProjectClient,
ProjectsAgentVersion agentVersion,
AIProjectClient AIProjectClient,
AgentVersion agentVersion,
IList<AITool>? tools,
Func<IChatClient, IChatClient>? clientFactory,
bool requireInvocableTools,
IServiceProvider? services)
=> AsChatClientAgent(
aiProjectClient,
AIProjectClient,
agentVersion,
CreateChatClientAgentOptions(agentVersion, new ChatOptions() { Tools = tools }, requireInvocableTools),
clientFactory,
@@ -308,21 +308,21 @@ public static partial class AzureAIProjectChatClientExtensions
/// <summary>This method creates an <see cref="ChatClientAgent"/> with a auto-generated ChatClientAgentOptions from the specified configuration parameters.</summary>
private static ChatClientAgent AsChatClientAgent(
AIProjectClient aiProjectClient,
ProjectsAgentRecord agentRecord,
AIProjectClient AIProjectClient,
AgentRecord agentRecord,
IList<AITool>? tools,
Func<IChatClient, IChatClient>? clientFactory,
bool requireInvocableTools,
IServiceProvider? services)
=> AsChatClientAgent(
aiProjectClient,
AIProjectClient,
agentRecord,
CreateChatClientAgentOptions(agentRecord.GetLatestVersion(), new ChatOptions() { Tools = tools }, requireInvocableTools),
clientFactory,
services);
/// <summary>
/// This method creates <see cref="ChatClientAgentOptions"/> for the specified <see cref="ProjectsAgentVersion"/> and the provided tools.
/// This method creates <see cref="ChatClientAgentOptions"/> for the specified <see cref="AgentVersion"/> and the provided tools.
/// </summary>
/// <param name="agentVersion">The agent version.</param>
/// <param name="chatOptions">The <see cref="ChatOptions"/> to use when interacting with the agent.</param>
@@ -334,12 +334,12 @@ public static partial class AzureAIProjectChatClientExtensions
/// This method rebuilds the agent options from the agent definition returned by the version and combine with the in-proc tools when provided
/// this ensures that all required tools are provided and the definition of the agent options are consistent with the agent definition coming from the server.
/// </remarks>
private static ChatClientAgentOptions CreateChatClientAgentOptions(ProjectsAgentVersion agentVersion, ChatOptions? chatOptions, bool requireInvocableTools)
private static ChatClientAgentOptions CreateChatClientAgentOptions(AgentVersion agentVersion, ChatOptions? chatOptions, bool requireInvocableTools)
{
var agentDefinition = agentVersion.Definition;
List<AITool>? agentTools = null;
if (agentDefinition is DeclarativeAgentDefinition { Tools: { Count: > 0 } definitionTools })
if (agentDefinition is PromptAgentDefinition { Tools: { Count: > 0 } definitionTools })
{
// Check if no tools were provided while the agent definition requires in-proc tools.
if (requireInvocableTools && chatOptions?.Tools is not { Count: > 0 } && definitionTools.Any(t => t is FunctionTool))
@@ -395,7 +395,7 @@ public static partial class AzureAIProjectChatClientExtensions
Description = agentVersion.Description,
};
if (agentDefinition is DeclarativeAgentDefinition promptAgentDefinition)
if (agentDefinition is PromptAgentDefinition promptAgentDefinition)
{
agentOptions.ChatOptions ??= chatOptions?.Clone() ?? new();
agentOptions.ChatOptions.Instructions = promptAgentDefinition.Instructions;
@@ -17,12 +17,12 @@ namespace Microsoft.Agents.AI.Foundry;
/// </summary>
/// <remarks>
/// <para>
/// This class wraps <see cref="ProjectsAgentTool"/> (Azure.AI.Projects.Agents) and <see cref="ResponseTool"/> (OpenAI SDK) factory methods,
/// This class wraps <see cref="AgentTool"/> (Azure.AI.Projects.OpenAI) and <see cref="ResponseTool"/> (OpenAI SDK) factory methods,
/// returning <see cref="AITool"/> directly — eliminating the need for manual casting and <c>.AsAITool()</c> calls.
/// </para>
/// <para>
/// Instead of writing:
/// <c>((ResponseTool)ProjectsAgentTool.CreateOpenApiTool(definition)).AsAITool()</c>
/// <c>((ResponseTool)AgentTool.CreateOpenApiTool(definition)).AsAITool()</c>
/// You can write:
/// <c>FoundryAITool.CreateOpenApiTool(definition)</c>
/// </para>
@@ -37,7 +37,7 @@ public static class FoundryAITool
/// <returns>An <see cref="AITool"/> wrapping the provided response tool.</returns>
public static AITool FromResponseTool(ResponseTool responseTool) => responseTool.AsAITool();
// --- Azure.AI.Projects.OpenAI ProjectsAgentTool factories ---
// --- Azure.AI.Projects.OpenAI AgentTool factories ---
/// <summary>
/// Creates an <see cref="AITool"/> for OpenAPI tool invocations.
@@ -45,7 +45,7 @@ public static class FoundryAITool
/// <param name="definition">The OpenAPI function definition specifying the API endpoint, schema, and authentication.</param>
/// <returns>An <see cref="AITool"/> that calls the specified OpenAPI endpoint.</returns>
public static AITool CreateOpenApiTool(OpenApiFunctionDefinition definition)
=> ((ResponseTool)ProjectsAgentTool.CreateOpenApiTool(definition)).AsAITool();
=> ((ResponseTool)AgentTool.CreateOpenApiTool(definition)).AsAITool();
/// <summary>
/// Creates an <see cref="AITool"/> for Bing Grounding search.
@@ -53,7 +53,7 @@ public static class FoundryAITool
/// <param name="options">The Bing Grounding search configuration options.</param>
/// <returns>An <see cref="AITool"/> for Bing Grounding search.</returns>
public static AITool CreateBingGroundingTool(BingGroundingSearchToolOptions options)
=> ((ResponseTool)ProjectsAgentTool.CreateBingGroundingTool(options)).AsAITool();
=> ((ResponseTool)AgentTool.CreateBingGroundingTool(options)).AsAITool();
/// <summary>
/// Creates an <see cref="AITool"/> for Bing Custom Search.
@@ -61,7 +61,7 @@ public static class FoundryAITool
/// <param name="parameters">The Bing Custom Search configuration parameters.</param>
/// <returns>An <see cref="AITool"/> for Bing Custom Search.</returns>
public static AITool CreateBingCustomSearchTool(BingCustomSearchToolOptions parameters)
=> ((ResponseTool)ProjectsAgentTool.CreateBingCustomSearchTool(parameters)).AsAITool();
=> ((ResponseTool)AgentTool.CreateBingCustomSearchTool(parameters)).AsAITool();
/// <summary>
/// Creates an <see cref="AITool"/> for Microsoft Fabric data agent.
@@ -69,7 +69,7 @@ public static class FoundryAITool
/// <param name="options">The Fabric data agent configuration options.</param>
/// <returns>An <see cref="AITool"/> for Microsoft Fabric.</returns>
public static AITool CreateMicrosoftFabricTool(FabricDataAgentToolOptions options)
=> ((ResponseTool)ProjectsAgentTool.CreateMicrosoftFabricTool(options)).AsAITool();
=> ((ResponseTool)AgentTool.CreateMicrosoftFabricTool(options)).AsAITool();
/// <summary>
/// Creates an <see cref="AITool"/> for SharePoint grounding.
@@ -77,7 +77,7 @@ public static class FoundryAITool
/// <param name="options">The SharePoint grounding configuration options.</param>
/// <returns>An <see cref="AITool"/> for SharePoint grounding.</returns>
public static AITool CreateSharepointTool(SharePointGroundingToolOptions options)
=> ((ResponseTool)ProjectsAgentTool.CreateSharepointTool(options)).AsAITool();
=> ((ResponseTool)AgentTool.CreateSharepointTool(options)).AsAITool();
/// <summary>
/// Creates an <see cref="AITool"/> for Azure AI Search.
@@ -85,7 +85,7 @@ public static class FoundryAITool
/// <param name="options">Optional Azure AI Search configuration options.</param>
/// <returns>An <see cref="AITool"/> for Azure AI Search.</returns>
public static AITool CreateAzureAISearchTool(AzureAISearchToolOptions? options = null)
=> ((ResponseTool)ProjectsAgentTool.CreateAzureAISearchTool(options)).AsAITool();
=> ((ResponseTool)AgentTool.CreateAzureAISearchTool(options)).AsAITool();
/// <summary>
/// Creates an <see cref="AITool"/> for browser automation.
@@ -93,7 +93,7 @@ public static class FoundryAITool
/// <param name="parameters">The browser automation configuration parameters.</param>
/// <returns>An <see cref="AITool"/> for browser automation.</returns>
public static AITool CreateBrowserAutomationTool(BrowserAutomationToolOptions parameters)
=> ((ResponseTool)ProjectsAgentTool.CreateBrowserAutomationTool(parameters)).AsAITool();
=> ((ResponseTool)AgentTool.CreateBrowserAutomationTool(parameters)).AsAITool();
/// <summary>
/// Creates an <see cref="AITool"/> for structured output capture.
@@ -101,7 +101,7 @@ public static class FoundryAITool
/// <param name="outputs">The structured output definition.</param>
/// <returns>An <see cref="AITool"/> for structured output capture.</returns>
public static AITool CreateStructuredOutputsTool(StructuredOutputDefinition outputs)
=> ((ResponseTool)ProjectsAgentTool.CreateStructuredOutputsTool(outputs)).AsAITool();
=> ((ResponseTool)AgentTool.CreateStructuredOutputsTool(outputs)).AsAITool();
/// <summary>
/// Creates an <see cref="AITool"/> for Agent-to-Agent (A2A) communication.
@@ -110,7 +110,7 @@ public static class FoundryAITool
/// <param name="agentCardPath">Optional path to the agent card.</param>
/// <returns>An <see cref="AITool"/> for A2A communication.</returns>
public static AITool CreateA2ATool(Uri baseUri, string? agentCardPath = null)
=> ProjectsAgentTool.CreateA2ATool(baseUri, agentCardPath).AsAITool();
=> AgentTool.CreateA2ATool(baseUri, agentCardPath).AsAITool();
// --- OpenAI SDK ResponseTool factories ---
@@ -9,7 +9,6 @@ using System.Text.Json.Serialization;
using System.Threading;
using System.Threading.Tasks;
using Azure.AI.Projects;
using Azure.AI.Projects.Memory;
using Microsoft.Extensions.AI;
using Microsoft.Extensions.Compliance.Redaction;
using Microsoft.Extensions.Logging;
@@ -4,7 +4,6 @@ using System.ClientModel;
using System.Threading;
using System.Threading.Tasks;
using Azure.AI.Projects;
using Azure.AI.Projects.Memory;
namespace Microsoft.Agents.AI.Foundry;
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<IsReleased>true</IsReleased>
<IsReleaseCandidate>true</IsReleaseCandidate>
<InjectSharedThrow>true</InjectSharedThrow>
<NoWarn>$(NoWarn);OPENAI001</NoWarn>
</PropertyGroup>
@@ -2,36 +2,106 @@
<!-- https://learn.microsoft.com/dotnet/fundamentals/package-validation/diagnostic-ids -->
<Suppressions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:OpenAI.Assistants.OpenAIAssistantClientExtensions</Target>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:OpenAI.Responses.OpenAIResponseClientExtensions.AsAIAgent(OpenAI.Responses.ResponsesClient,Microsoft.Agents.AI.ChatClientAgentOptions,System.Func{Microsoft.Extensions.AI.IChatClient,Microsoft.Extensions.AI.IChatClient},Microsoft.Extensions.Logging.ILoggerFactory,System.IServiceProvider)</Target>
<Left>lib/net10.0/Microsoft.Agents.AI.OpenAI.dll</Left>
<Right>lib/net10.0/Microsoft.Agents.AI.OpenAI.dll</Right>
<IsBaselineSuppression>true</IsBaselineSuppression>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:OpenAI.Assistants.OpenAIAssistantClientExtensions</Target>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:OpenAI.Responses.OpenAIResponseClientExtensions.AsAIAgent(OpenAI.Responses.ResponsesClient,System.String,System.String,System.String,System.Collections.Generic.IList{Microsoft.Extensions.AI.AITool},System.Func{Microsoft.Extensions.AI.IChatClient,Microsoft.Extensions.AI.IChatClient},Microsoft.Extensions.Logging.ILoggerFactory,System.IServiceProvider)</Target>
<Left>lib/net10.0/Microsoft.Agents.AI.OpenAI.dll</Left>
<Right>lib/net10.0/Microsoft.Agents.AI.OpenAI.dll</Right>
<IsBaselineSuppression>true</IsBaselineSuppression>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:OpenAI.Responses.OpenAIResponseClientExtensions.AsIChatClientWithStoredOutputDisabled(OpenAI.Responses.ResponsesClient)</Target>
<Left>lib/net10.0/Microsoft.Agents.AI.OpenAI.dll</Left>
<Right>lib/net10.0/Microsoft.Agents.AI.OpenAI.dll</Right>
<IsBaselineSuppression>true</IsBaselineSuppression>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:OpenAI.Responses.OpenAIResponseClientExtensions.AsAIAgent(OpenAI.Responses.ResponsesClient,Microsoft.Agents.AI.ChatClientAgentOptions,System.Func{Microsoft.Extensions.AI.IChatClient,Microsoft.Extensions.AI.IChatClient},Microsoft.Extensions.Logging.ILoggerFactory,System.IServiceProvider)</Target>
<Left>lib/net472/Microsoft.Agents.AI.OpenAI.dll</Left>
<Right>lib/net472/Microsoft.Agents.AI.OpenAI.dll</Right>
<IsBaselineSuppression>true</IsBaselineSuppression>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:OpenAI.Assistants.OpenAIAssistantClientExtensions</Target>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:OpenAI.Responses.OpenAIResponseClientExtensions.AsAIAgent(OpenAI.Responses.ResponsesClient,System.String,System.String,System.String,System.Collections.Generic.IList{Microsoft.Extensions.AI.AITool},System.Func{Microsoft.Extensions.AI.IChatClient,Microsoft.Extensions.AI.IChatClient},Microsoft.Extensions.Logging.ILoggerFactory,System.IServiceProvider)</Target>
<Left>lib/net472/Microsoft.Agents.AI.OpenAI.dll</Left>
<Right>lib/net472/Microsoft.Agents.AI.OpenAI.dll</Right>
<IsBaselineSuppression>true</IsBaselineSuppression>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:OpenAI.Responses.OpenAIResponseClientExtensions.AsIChatClientWithStoredOutputDisabled(OpenAI.Responses.ResponsesClient)</Target>
<Left>lib/net472/Microsoft.Agents.AI.OpenAI.dll</Left>
<Right>lib/net472/Microsoft.Agents.AI.OpenAI.dll</Right>
<IsBaselineSuppression>true</IsBaselineSuppression>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:OpenAI.Responses.OpenAIResponseClientExtensions.AsAIAgent(OpenAI.Responses.ResponsesClient,Microsoft.Agents.AI.ChatClientAgentOptions,System.Func{Microsoft.Extensions.AI.IChatClient,Microsoft.Extensions.AI.IChatClient},Microsoft.Extensions.Logging.ILoggerFactory,System.IServiceProvider)</Target>
<Left>lib/net8.0/Microsoft.Agents.AI.OpenAI.dll</Left>
<Right>lib/net8.0/Microsoft.Agents.AI.OpenAI.dll</Right>
<IsBaselineSuppression>true</IsBaselineSuppression>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:OpenAI.Assistants.OpenAIAssistantClientExtensions</Target>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:OpenAI.Responses.OpenAIResponseClientExtensions.AsAIAgent(OpenAI.Responses.ResponsesClient,System.String,System.String,System.String,System.Collections.Generic.IList{Microsoft.Extensions.AI.AITool},System.Func{Microsoft.Extensions.AI.IChatClient,Microsoft.Extensions.AI.IChatClient},Microsoft.Extensions.Logging.ILoggerFactory,System.IServiceProvider)</Target>
<Left>lib/net8.0/Microsoft.Agents.AI.OpenAI.dll</Left>
<Right>lib/net8.0/Microsoft.Agents.AI.OpenAI.dll</Right>
<IsBaselineSuppression>true</IsBaselineSuppression>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:OpenAI.Responses.OpenAIResponseClientExtensions.AsIChatClientWithStoredOutputDisabled(OpenAI.Responses.ResponsesClient)</Target>
<Left>lib/net8.0/Microsoft.Agents.AI.OpenAI.dll</Left>
<Right>lib/net8.0/Microsoft.Agents.AI.OpenAI.dll</Right>
<IsBaselineSuppression>true</IsBaselineSuppression>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:OpenAI.Responses.OpenAIResponseClientExtensions.AsAIAgent(OpenAI.Responses.ResponsesClient,Microsoft.Agents.AI.ChatClientAgentOptions,System.Func{Microsoft.Extensions.AI.IChatClient,Microsoft.Extensions.AI.IChatClient},Microsoft.Extensions.Logging.ILoggerFactory,System.IServiceProvider)</Target>
<Left>lib/net9.0/Microsoft.Agents.AI.OpenAI.dll</Left>
<Right>lib/net9.0/Microsoft.Agents.AI.OpenAI.dll</Right>
<IsBaselineSuppression>true</IsBaselineSuppression>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:OpenAI.Assistants.OpenAIAssistantClientExtensions</Target>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:OpenAI.Responses.OpenAIResponseClientExtensions.AsAIAgent(OpenAI.Responses.ResponsesClient,System.String,System.String,System.String,System.Collections.Generic.IList{Microsoft.Extensions.AI.AITool},System.Func{Microsoft.Extensions.AI.IChatClient,Microsoft.Extensions.AI.IChatClient},Microsoft.Extensions.Logging.ILoggerFactory,System.IServiceProvider)</Target>
<Left>lib/net9.0/Microsoft.Agents.AI.OpenAI.dll</Left>
<Right>lib/net9.0/Microsoft.Agents.AI.OpenAI.dll</Right>
<IsBaselineSuppression>true</IsBaselineSuppression>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:OpenAI.Responses.OpenAIResponseClientExtensions.AsIChatClientWithStoredOutputDisabled(OpenAI.Responses.ResponsesClient)</Target>
<Left>lib/net9.0/Microsoft.Agents.AI.OpenAI.dll</Left>
<Right>lib/net9.0/Microsoft.Agents.AI.OpenAI.dll</Right>
<IsBaselineSuppression>true</IsBaselineSuppression>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:OpenAI.Responses.OpenAIResponseClientExtensions.AsAIAgent(OpenAI.Responses.ResponsesClient,Microsoft.Agents.AI.ChatClientAgentOptions,System.Func{Microsoft.Extensions.AI.IChatClient,Microsoft.Extensions.AI.IChatClient},Microsoft.Extensions.Logging.ILoggerFactory,System.IServiceProvider)</Target>
<Left>lib/netstandard2.0/Microsoft.Agents.AI.OpenAI.dll</Left>
<Right>lib/netstandard2.0/Microsoft.Agents.AI.OpenAI.dll</Right>
<IsBaselineSuppression>true</IsBaselineSuppression>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:OpenAI.Responses.OpenAIResponseClientExtensions.AsAIAgent(OpenAI.Responses.ResponsesClient,System.String,System.String,System.String,System.Collections.Generic.IList{Microsoft.Extensions.AI.AITool},System.Func{Microsoft.Extensions.AI.IChatClient,Microsoft.Extensions.AI.IChatClient},Microsoft.Extensions.Logging.ILoggerFactory,System.IServiceProvider)</Target>
<Left>lib/netstandard2.0/Microsoft.Agents.AI.OpenAI.dll</Left>
<Right>lib/netstandard2.0/Microsoft.Agents.AI.OpenAI.dll</Right>
<IsBaselineSuppression>true</IsBaselineSuppression>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:OpenAI.Responses.OpenAIResponseClientExtensions.AsIChatClientWithStoredOutputDisabled(OpenAI.Responses.ResponsesClient)</Target>
<Left>lib/netstandard2.0/Microsoft.Agents.AI.OpenAI.dll</Left>
<Right>lib/netstandard2.0/Microsoft.Agents.AI.OpenAI.dll</Right>
<IsBaselineSuppression>true</IsBaselineSuppression>
@@ -0,0 +1,433 @@
// Copyright (c) Microsoft. All rights reserved.
using System.ClientModel;
using System.Diagnostics.CodeAnalysis;
using Microsoft.Agents.AI;
using Microsoft.Extensions.AI;
using Microsoft.Extensions.Logging;
using Microsoft.Shared.DiagnosticIds;
using Microsoft.Shared.Diagnostics;
namespace OpenAI.Assistants;
/// <summary>
/// Provides extension methods for OpenAI <see cref="AssistantClient"/>
/// to simplify the creation of AI agents that work with OpenAI services.
/// </summary>
/// <remarks>
/// These extensions bridge the gap between OpenAI SDK client objects and the Microsoft Agent Framework,
/// allowing developers to easily create AI agents that leverage OpenAI's chat completion and response services.
/// The methods handle the conversion from OpenAI clients to <see cref="IChatClient"/> instances and then wrap them
/// in <see cref="ChatClientAgent"/> objects that implement the <see cref="AIAgent"/> interface.
/// </remarks>
[Experimental(DiagnosticIds.Experiments.AIOpenAIAssistants)]
public static class OpenAIAssistantClientExtensions
{
/// <summary>
/// Gets a <see cref="ChatClientAgent"/> from a <see cref="ClientResult{Assistant}"/>.
/// </summary>
/// <param name="assistantClient">The assistant client.</param>
/// <param name="assistantClientResult">The client result containing the assistant.</param>
/// <param name="chatOptions">Optional chat options.</param>
/// <param name="clientFactory">Provides a way to customize the creation of the underlying <see cref="IChatClient"/> used by the agent.</param>
/// <param name="services">An optional <see cref="IServiceProvider"/> to use for resolving services required by the <see cref="AIFunction"/> instances being invoked.</param>
/// <returns>A <see cref="ChatClientAgent"/> instance that can be used to perform operations on the assistant.</returns>
[Obsolete("The Assistants API has been deprecated. Please use the Responses API instead.")]
public static ChatClientAgent AsAIAgent(
this AssistantClient assistantClient,
ClientResult<Assistant> assistantClientResult,
ChatOptions? chatOptions = null,
Func<IChatClient, IChatClient>? clientFactory = null,
IServiceProvider? services = null)
{
if (assistantClientResult is null)
{
throw new ArgumentNullException(nameof(assistantClientResult));
}
return assistantClient.AsAIAgent(assistantClientResult.Value, chatOptions, clientFactory, services);
}
/// <summary>
/// Gets a <see cref="ChatClientAgent"/> from an <see cref="Assistant"/>.
/// </summary>
/// <param name="assistantClient">The assistant client.</param>
/// <param name="assistantMetadata">The assistant metadata.</param>
/// <param name="chatOptions">Optional chat options.</param>
/// <param name="clientFactory">Provides a way to customize the creation of the underlying <see cref="IChatClient"/> used by the agent.</param>
/// <param name="services">An optional <see cref="IServiceProvider"/> to use for resolving services required by the <see cref="AIFunction"/> instances being invoked.</param>
/// <returns>A <see cref="ChatClientAgent"/> instance that can be used to perform operations on the assistant.</returns>
[Obsolete("The Assistants API has been deprecated. Please use the Responses API instead.")]
public static ChatClientAgent AsAIAgent(
this AssistantClient assistantClient,
Assistant assistantMetadata,
ChatOptions? chatOptions = null,
Func<IChatClient, IChatClient>? clientFactory = null,
IServiceProvider? services = null)
{
if (assistantMetadata is null)
{
throw new ArgumentNullException(nameof(assistantMetadata));
}
if (assistantClient is null)
{
throw new ArgumentNullException(nameof(assistantClient));
}
var chatClient = assistantClient.AsIChatClient(assistantMetadata.Id);
if (clientFactory is not null)
{
chatClient = clientFactory(chatClient);
}
if (!string.IsNullOrWhiteSpace(assistantMetadata.Instructions) && chatOptions?.Instructions is null)
{
chatOptions ??= new ChatOptions();
chatOptions.Instructions = assistantMetadata.Instructions;
}
return new ChatClientAgent(chatClient, options: new()
{
Id = assistantMetadata.Id,
Name = assistantMetadata.Name,
Description = assistantMetadata.Description,
ChatOptions = chatOptions
}, services: services);
}
/// <summary>
/// Retrieves an existing server side agent, wrapped as a <see cref="ChatClientAgent"/> using the provided <see cref="AssistantClient"/>.
/// </summary>
/// <param name="assistantClient">The <see cref="AssistantClient"/> to create the <see cref="ChatClientAgent"/> with.</param>
/// <param name="agentId"> The ID of the server side agent to create a <see cref="ChatClientAgent"/> for.</param>
/// <param name="chatOptions">Options that should apply to all runs of the agent.</param>
/// <param name="clientFactory">Provides a way to customize the creation of the underlying <see cref="IChatClient"/> used by the agent.</param>
/// <param name="services">An optional <see cref="IServiceProvider"/> to use for resolving services required by the <see cref="AIFunction"/> instances being invoked.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
/// <returns>A <see cref="ChatClientAgent"/> instance that can be used to perform operations on the assistant agent.</returns>
[Obsolete("The Assistants API has been deprecated. Please use the Responses API instead.")]
public static async Task<ChatClientAgent> GetAIAgentAsync(
this AssistantClient assistantClient,
string agentId,
ChatOptions? chatOptions = null,
Func<IChatClient, IChatClient>? clientFactory = null,
IServiceProvider? services = null,
CancellationToken cancellationToken = default)
{
if (assistantClient is null)
{
throw new ArgumentNullException(nameof(assistantClient));
}
if (string.IsNullOrWhiteSpace(agentId))
{
throw new ArgumentException($"{nameof(agentId)} should not be null or whitespace.", nameof(agentId));
}
var assistantResponse = await assistantClient.GetAssistantAsync(agentId, cancellationToken).ConfigureAwait(false);
return assistantClient.AsAIAgent(assistantResponse, chatOptions, clientFactory, services);
}
/// <summary>
/// Gets a <see cref="ChatClientAgent"/> from a <see cref="ClientResult{Assistant}"/>.
/// </summary>
/// <param name="assistantClient">The assistant client.</param>
/// <param name="assistantClientResult">The client result containing the assistant.</param>
/// <param name="options">Full set of options to configure the agent.</param>
/// <param name="clientFactory">Provides a way to customize the creation of the underlying <see cref="IChatClient"/> used by the agent.</param>
/// <param name="services">An optional <see cref="IServiceProvider"/> to use for resolving services required by the <see cref="AIFunction"/> instances being invoked.</param>
/// <returns>A <see cref="ChatClientAgent"/> instance that can be used to perform operations on the assistant.</returns>
/// <exception cref="ArgumentNullException"><paramref name="assistantClientResult"/> or <paramref name="options"/> is <see langword="null"/>.</exception>
[Obsolete("The Assistants API has been deprecated. Please use the Responses API instead.")]
public static ChatClientAgent AsAIAgent(
this AssistantClient assistantClient,
ClientResult<Assistant> assistantClientResult,
ChatClientAgentOptions options,
Func<IChatClient, IChatClient>? clientFactory = null,
IServiceProvider? services = null)
{
if (assistantClientResult is null)
{
throw new ArgumentNullException(nameof(assistantClientResult));
}
return assistantClient.AsAIAgent(assistantClientResult.Value, options, clientFactory, services);
}
/// <summary>
/// Gets a <see cref="ChatClientAgent"/> from an <see cref="Assistant"/>.
/// </summary>
/// <param name="assistantClient">The assistant client.</param>
/// <param name="assistantMetadata">The assistant metadata.</param>
/// <param name="options">Full set of options to configure the agent.</param>
/// <param name="clientFactory">Provides a way to customize the creation of the underlying <see cref="IChatClient"/> used by the agent.</param>
/// <param name="services">An optional <see cref="IServiceProvider"/> to use for resolving services required by the <see cref="AIFunction"/> instances being invoked.</param>
/// <returns>A <see cref="ChatClientAgent"/> instance that can be used to perform operations on the assistant.</returns>
/// <exception cref="ArgumentNullException"><paramref name="assistantMetadata"/> or <paramref name="options"/> is <see langword="null"/>.</exception>
[Obsolete("The Assistants API has been deprecated. Please use the Responses API instead.")]
public static ChatClientAgent AsAIAgent(
this AssistantClient assistantClient,
Assistant assistantMetadata,
ChatClientAgentOptions options,
Func<IChatClient, IChatClient>? clientFactory = null,
IServiceProvider? services = null)
{
if (assistantMetadata is null)
{
throw new ArgumentNullException(nameof(assistantMetadata));
}
if (assistantClient is null)
{
throw new ArgumentNullException(nameof(assistantClient));
}
if (options is null)
{
throw new ArgumentNullException(nameof(options));
}
var chatClient = assistantClient.AsIChatClient(assistantMetadata.Id);
if (clientFactory is not null)
{
chatClient = clientFactory(chatClient);
}
if (string.IsNullOrWhiteSpace(options.ChatOptions?.Instructions) && !string.IsNullOrWhiteSpace(assistantMetadata.Instructions))
{
options.ChatOptions ??= new ChatOptions();
options.ChatOptions.Instructions = assistantMetadata.Instructions;
}
var mergedOptions = new ChatClientAgentOptions()
{
Id = assistantMetadata.Id,
Name = options.Name ?? assistantMetadata.Name,
Description = options.Description ?? assistantMetadata.Description,
ChatOptions = options.ChatOptions,
AIContextProviders = options.AIContextProviders,
ChatHistoryProvider = options.ChatHistoryProvider,
UseProvidedChatClientAsIs = options.UseProvidedChatClientAsIs
};
return new ChatClientAgent(chatClient, mergedOptions, services: services);
}
/// <summary>
/// Retrieves an existing server side agent, wrapped as a <see cref="ChatClientAgent"/> using the provided <see cref="AssistantClient"/>.
/// </summary>
/// <param name="assistantClient">The <see cref="AssistantClient"/> to create the <see cref="ChatClientAgent"/> with.</param>
/// <param name="agentId"> The ID of the server side agent to create a <see cref="ChatClientAgent"/> for.</param>
/// <param name="options">Full set of options to configure the agent.</param>
/// <param name="clientFactory">Provides a way to customize the creation of the underlying <see cref="IChatClient"/> used by the agent.</param>
/// <param name="services">An optional <see cref="IServiceProvider"/> to use for resolving services required by the <see cref="AIFunction"/> instances being invoked.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
/// <returns>A <see cref="ChatClientAgent"/> instance that can be used to perform operations on the assistant agent.</returns>
/// <exception cref="ArgumentNullException"><paramref name="assistantClient"/> or <paramref name="options"/> is <see langword="null"/>.</exception>
/// <exception cref="ArgumentException"><paramref name="agentId"/> is empty or whitespace.</exception>
[Obsolete("The Assistants API has been deprecated. Please use the Responses API instead.")]
public static async Task<ChatClientAgent> GetAIAgentAsync(
this AssistantClient assistantClient,
string agentId,
ChatClientAgentOptions options,
Func<IChatClient, IChatClient>? clientFactory = null,
IServiceProvider? services = null,
CancellationToken cancellationToken = default)
{
if (assistantClient is null)
{
throw new ArgumentNullException(nameof(assistantClient));
}
if (string.IsNullOrWhiteSpace(agentId))
{
throw new ArgumentException($"{nameof(agentId)} should not be null or whitespace.", nameof(agentId));
}
if (options is null)
{
throw new ArgumentNullException(nameof(options));
}
var assistantResponse = await assistantClient.GetAssistantAsync(agentId, cancellationToken).ConfigureAwait(false);
return assistantClient.AsAIAgent(assistantResponse, options, clientFactory, services);
}
/// <summary>
/// Creates an AI agent from an <see cref="AssistantClient"/> using the OpenAI Assistant API.
/// </summary>
/// <param name="client">The OpenAI <see cref="AssistantClient" /> to use for the agent.</param>
/// <param name="model">The model identifier to use (e.g., "gpt-4").</param>
/// <param name="instructions">Optional system instructions that define the agent's behavior and personality.</param>
/// <param name="name">Optional name for the agent for identification purposes.</param>
/// <param name="description">Optional description of the agent's capabilities and purpose.</param>
/// <param name="tools">Optional collection of AI tools that the agent can use during conversations.</param>
/// <param name="clientFactory">Provides a way to customize the creation of the underlying <see cref="IChatClient"/> used by the agent.</param>
/// <param name="loggerFactory">Optional logger factory for enabling logging within the agent.</param>
/// <param name="services">An optional <see cref="IServiceProvider"/> to use for resolving services required by the <see cref="AIFunction"/> instances being invoked.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
/// <returns>An <see cref="ChatClientAgent"/> instance backed by the OpenAI Assistant service.</returns>
/// <exception cref="ArgumentNullException">Thrown when <paramref name="client"/> or <paramref name="model"/> is <see langword="null"/>.</exception>
/// <exception cref="ArgumentException">Thrown when <paramref name="model"/> is empty or whitespace.</exception>
[Obsolete("The Assistants API has been deprecated. Please use the Responses API instead.")]
public static async Task<ChatClientAgent> CreateAIAgentAsync(
this AssistantClient client,
string model,
string? instructions = null,
string? name = null,
string? description = null,
IList<AITool>? tools = null,
Func<IChatClient, IChatClient>? clientFactory = null,
ILoggerFactory? loggerFactory = null,
IServiceProvider? services = null,
CancellationToken cancellationToken = default) =>
await client.CreateAIAgentAsync(model,
new ChatClientAgentOptions()
{
Name = name,
Description = description,
ChatOptions = tools is null && string.IsNullOrWhiteSpace(instructions) ? null : new ChatOptions()
{
Tools = tools,
Instructions = instructions,
}
},
clientFactory,
loggerFactory,
services,
cancellationToken).ConfigureAwait(false);
/// <summary>
/// Creates an AI agent from an <see cref="AssistantClient"/> using the OpenAI Assistant API.
/// </summary>
/// <param name="client">The OpenAI <see cref="AssistantClient" /> to use for the agent.</param>
/// <param name="model">The model identifier to use (e.g., "gpt-4").</param>
/// <param name="options">Full set of options to configure the agent.</param>
/// <param name="clientFactory">Provides a way to customize the creation of the underlying <see cref="IChatClient"/> used by the agent.</param>
/// <param name="loggerFactory">Optional logger factory for enabling logging within the agent.</param>
/// <param name="services">An optional <see cref="IServiceProvider"/> to use for resolving services required by the <see cref="AIFunction"/> instances being invoked.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
/// <returns>An <see cref="ChatClientAgent"/> instance backed by the OpenAI Assistant service.</returns>
/// <exception cref="ArgumentNullException">Thrown when <paramref name="client"/> or <paramref name="model"/> is <see langword="null"/>.</exception>
/// <exception cref="ArgumentException">Thrown when <paramref name="model"/> is empty or whitespace.</exception>
[Obsolete("The Assistants API has been deprecated. Please use the Responses API instead.")]
public static async Task<ChatClientAgent> CreateAIAgentAsync(
this AssistantClient client,
string model,
ChatClientAgentOptions options,
Func<IChatClient, IChatClient>? clientFactory = null,
ILoggerFactory? loggerFactory = null,
IServiceProvider? services = null,
CancellationToken cancellationToken = default)
{
Throw.IfNull(client);
Throw.IfNull(model);
Throw.IfNull(options);
var assistantOptions = new AssistantCreationOptions()
{
Name = options.Name,
Description = options.Description,
Instructions = options.ChatOptions?.Instructions,
};
// Convert AITools to ToolDefinitions and ToolResources
var toolDefinitionsAndResources = ConvertAIToolsToToolDefinitions(options.ChatOptions?.Tools);
if (toolDefinitionsAndResources.ToolDefinitions is { Count: > 0 } toolDefinitions)
{
toolDefinitions.ForEach(x => assistantOptions.Tools.Add(x));
}
if (toolDefinitionsAndResources.ToolResources is not null)
{
assistantOptions.ToolResources = toolDefinitionsAndResources.ToolResources;
}
// Create the assistant in the assistant service.
var assistantCreateResult = await client.CreateAssistantAsync(model, assistantOptions, cancellationToken).ConfigureAwait(false);
var assistantId = assistantCreateResult.Value.Id;
// Build the local agent object.
var chatClient = client.AsIChatClient(assistantId);
if (clientFactory is not null)
{
chatClient = clientFactory(chatClient);
}
var agentOptions = options.Clone();
agentOptions.Id = assistantId;
options.ChatOptions ??= new ChatOptions();
options.ChatOptions!.Tools = toolDefinitionsAndResources.FunctionToolsAndOtherTools;
return new ChatClientAgent(chatClient, agentOptions, loggerFactory, services);
}
private static (List<ToolDefinition>? ToolDefinitions, ToolResources? ToolResources, List<AITool>? FunctionToolsAndOtherTools) ConvertAIToolsToToolDefinitions(IList<AITool>? tools)
{
List<ToolDefinition>? toolDefinitions = null;
ToolResources? toolResources = null;
List<AITool>? functionToolsAndOtherTools = null;
if (tools is not null)
{
foreach (AITool tool in tools)
{
switch (tool)
{
case HostedCodeInterpreterTool codeTool:
toolDefinitions ??= [];
toolDefinitions.Add(new CodeInterpreterToolDefinition());
if (codeTool.Inputs is { Count: > 0 })
{
foreach (var input in codeTool.Inputs)
{
switch (input)
{
case HostedFileContent hostedFile:
// If the input is a HostedFileContent, we can use its ID directly.
toolResources ??= new();
toolResources.CodeInterpreter ??= new();
toolResources.CodeInterpreter.FileIds.Add(hostedFile.FileId);
break;
}
}
}
break;
case HostedFileSearchTool fileSearchTool:
toolDefinitions ??= [];
toolDefinitions.Add(new FileSearchToolDefinition
{
MaxResults = fileSearchTool.MaximumResultCount,
});
if (fileSearchTool.Inputs is { Count: > 0 })
{
foreach (var input in fileSearchTool.Inputs)
{
switch (input)
{
case HostedVectorStoreContent hostedVectorStore:
toolResources ??= new();
toolResources.FileSearch ??= new();
toolResources.FileSearch.VectorStoreIds.Add(hostedVectorStore.VectorStoreId);
break;
}
}
}
break;
default:
functionToolsAndOtherTools ??= [];
functionToolsAndOtherTools.Add(tool);
break;
}
}
}
return (toolDefinitions, toolResources, functionToolsAndOtherTools);
}
}
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<IsReleased>true</IsReleased>
<IsReleaseCandidate>true</IsReleaseCandidate>
<ImplicitUsings>enable</ImplicitUsings>
<InjectSharedThrow>true</InjectSharedThrow>
</PropertyGroup>
@@ -28,7 +28,7 @@ namespace Microsoft.Agents.AI.Workflows.Declarative;
/// <param name="projectCredentials">The credentials used to authenticate with the Foundry project. This must be a valid instance of <see cref="TokenCredential"/>.</param>
public sealed class AzureAgentProvider(Uri projectEndpoint, TokenCredential projectCredentials) : ResponseAgentProvider
{
private readonly Dictionary<string, ProjectsAgentVersion> _versionCache = [];
private readonly Dictionary<string, AgentVersion> _versionCache = [];
private readonly Dictionary<string, AIAgent> _agentCache = [];
private AIProjectClient? _agentClient;
@@ -99,7 +99,7 @@ public sealed class AzureAgentProvider(Uri projectEndpoint, TokenCredential proj
IDictionary<string, object?>? inputArguments,
[EnumeratorCancellation] CancellationToken cancellationToken = default)
{
ProjectsAgentVersion agentVersionResult = await this.QueryAgentAsync(agentId, agentVersion, cancellationToken).ConfigureAwait(false);
AgentVersion agentVersionResult = await this.QueryAgentAsync(agentId, agentVersion, cancellationToken).ConfigureAwait(false);
AIAgent agent = await this.GetAgentAsync(agentVersionResult, cancellationToken).ConfigureAwait(false);
ChatOptions chatOptions =
@@ -133,10 +133,10 @@ public sealed class AzureAgentProvider(Uri projectEndpoint, TokenCredential proj
}
}
private async Task<ProjectsAgentVersion> QueryAgentAsync(string agentName, string? agentVersion, CancellationToken cancellationToken = default)
private async Task<AgentVersion> QueryAgentAsync(string agentName, string? agentVersion, CancellationToken cancellationToken = default)
{
string agentKey = $"{agentName}:{agentVersion}";
if (this._versionCache.TryGetValue(agentKey, out ProjectsAgentVersion? targetAgent))
if (this._versionCache.TryGetValue(agentKey, out AgentVersion? targetAgent))
{
return targetAgent;
}
@@ -145,8 +145,8 @@ public sealed class AzureAgentProvider(Uri projectEndpoint, TokenCredential proj
if (string.IsNullOrEmpty(agentVersion))
{
ProjectsAgentRecord agentRecord =
await client.AgentAdministrationClient.GetAgentAsync(
AgentRecord agentRecord =
await client.Agents.GetAgentAsync(
agentName,
cancellationToken).ConfigureAwait(false);
@@ -155,7 +155,7 @@ public sealed class AzureAgentProvider(Uri projectEndpoint, TokenCredential proj
else
{
targetAgent =
await client.AgentAdministrationClient.GetAgentVersionAsync(
await client.Agents.GetAgentVersionAsync(
agentName,
agentVersion,
cancellationToken).ConfigureAwait(false);
@@ -166,7 +166,7 @@ public sealed class AzureAgentProvider(Uri projectEndpoint, TokenCredential proj
return targetAgent;
}
private async Task<AIAgent> GetAgentAsync(ProjectsAgentVersion agentVersion, CancellationToken cancellationToken = default)
private async Task<AIAgent> GetAgentAsync(AgentVersion agentVersion, CancellationToken cancellationToken = default)
{
if (this._agentCache.TryGetValue(agentVersion.Id, out AIAgent? agent))
{
@@ -29,7 +29,7 @@
</PropertyGroup>
<PropertyGroup>
<IsReleased>true</IsReleased>
<IsReleaseCandidate>true</IsReleaseCandidate>
</PropertyGroup>
<Import Project="$(RepoRoot)/dotnet/nuget/nuget-package.props" />
@@ -2,7 +2,6 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Agents.AI.Workflows.Specialized;
@@ -155,7 +154,6 @@ public static partial class AgentWorkflowBuilder
/// The <see cref="AIAgent"/> must be capable of understanding those <see cref="AgentRunOptions"/> provided. If the agent
/// ignores the tools or is otherwise unable to advertize them to the underlying provider, handoffs will not occur.
/// </remarks>
[Experimental(DiagnosticConstants.ExperimentalFeatureDiagnostic)]
public static HandoffWorkflowBuilder CreateHandoffBuilderWith(AIAgent initialAgent)
{
Throw.IfNull(initialAgent);
@@ -0,0 +1,319 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- https://learn.microsoft.com/dotnet/fundamentals/package-validation/diagnostic-ids -->
<Suppressions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Microsoft.Agents.AI.Workflows.Config</Target>
<Left>lib/net10.0/Microsoft.Agents.AI.Workflows.dll</Left>
<Right>lib/net10.0/Microsoft.Agents.AI.Workflows.dll</Right>
<IsBaselineSuppression>true</IsBaselineSuppression>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Microsoft.Agents.AI.Workflows.Config`1</Target>
<Left>lib/net10.0/Microsoft.Agents.AI.Workflows.dll</Left>
<Right>lib/net10.0/Microsoft.Agents.AI.Workflows.dll</Right>
<IsBaselineSuppression>true</IsBaselineSuppression>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Microsoft.Agents.AI.Workflows.ConfigurationExtensions</Target>
<Left>lib/net10.0/Microsoft.Agents.AI.Workflows.dll</Left>
<Right>lib/net10.0/Microsoft.Agents.AI.Workflows.dll</Right>
<IsBaselineSuppression>true</IsBaselineSuppression>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Microsoft.Agents.AI.Workflows.Configured</Target>
<Left>lib/net10.0/Microsoft.Agents.AI.Workflows.dll</Left>
<Right>lib/net10.0/Microsoft.Agents.AI.Workflows.dll</Right>
<IsBaselineSuppression>true</IsBaselineSuppression>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Microsoft.Agents.AI.Workflows.Configured`1</Target>
<Left>lib/net10.0/Microsoft.Agents.AI.Workflows.dll</Left>
<Right>lib/net10.0/Microsoft.Agents.AI.Workflows.dll</Right>
<IsBaselineSuppression>true</IsBaselineSuppression>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Microsoft.Agents.AI.Workflows.Configured`2</Target>
<Left>lib/net10.0/Microsoft.Agents.AI.Workflows.dll</Left>
<Right>lib/net10.0/Microsoft.Agents.AI.Workflows.dll</Right>
<IsBaselineSuppression>true</IsBaselineSuppression>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Microsoft.Agents.AI.Workflows.Config</Target>
<Left>lib/net472/Microsoft.Agents.AI.Workflows.dll</Left>
<Right>lib/net472/Microsoft.Agents.AI.Workflows.dll</Right>
<IsBaselineSuppression>true</IsBaselineSuppression>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Microsoft.Agents.AI.Workflows.Config`1</Target>
<Left>lib/net472/Microsoft.Agents.AI.Workflows.dll</Left>
<Right>lib/net472/Microsoft.Agents.AI.Workflows.dll</Right>
<IsBaselineSuppression>true</IsBaselineSuppression>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Microsoft.Agents.AI.Workflows.ConfigurationExtensions</Target>
<Left>lib/net472/Microsoft.Agents.AI.Workflows.dll</Left>
<Right>lib/net472/Microsoft.Agents.AI.Workflows.dll</Right>
<IsBaselineSuppression>true</IsBaselineSuppression>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Microsoft.Agents.AI.Workflows.Configured</Target>
<Left>lib/net472/Microsoft.Agents.AI.Workflows.dll</Left>
<Right>lib/net472/Microsoft.Agents.AI.Workflows.dll</Right>
<IsBaselineSuppression>true</IsBaselineSuppression>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Microsoft.Agents.AI.Workflows.Configured`1</Target>
<Left>lib/net472/Microsoft.Agents.AI.Workflows.dll</Left>
<Right>lib/net472/Microsoft.Agents.AI.Workflows.dll</Right>
<IsBaselineSuppression>true</IsBaselineSuppression>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Microsoft.Agents.AI.Workflows.Configured`2</Target>
<Left>lib/net472/Microsoft.Agents.AI.Workflows.dll</Left>
<Right>lib/net472/Microsoft.Agents.AI.Workflows.dll</Right>
<IsBaselineSuppression>true</IsBaselineSuppression>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Microsoft.Agents.AI.Workflows.Config</Target>
<Left>lib/net8.0/Microsoft.Agents.AI.Workflows.dll</Left>
<Right>lib/net8.0/Microsoft.Agents.AI.Workflows.dll</Right>
<IsBaselineSuppression>true</IsBaselineSuppression>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Microsoft.Agents.AI.Workflows.Config`1</Target>
<Left>lib/net8.0/Microsoft.Agents.AI.Workflows.dll</Left>
<Right>lib/net8.0/Microsoft.Agents.AI.Workflows.dll</Right>
<IsBaselineSuppression>true</IsBaselineSuppression>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Microsoft.Agents.AI.Workflows.ConfigurationExtensions</Target>
<Left>lib/net8.0/Microsoft.Agents.AI.Workflows.dll</Left>
<Right>lib/net8.0/Microsoft.Agents.AI.Workflows.dll</Right>
<IsBaselineSuppression>true</IsBaselineSuppression>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Microsoft.Agents.AI.Workflows.Configured</Target>
<Left>lib/net8.0/Microsoft.Agents.AI.Workflows.dll</Left>
<Right>lib/net8.0/Microsoft.Agents.AI.Workflows.dll</Right>
<IsBaselineSuppression>true</IsBaselineSuppression>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Microsoft.Agents.AI.Workflows.Configured`1</Target>
<Left>lib/net8.0/Microsoft.Agents.AI.Workflows.dll</Left>
<Right>lib/net8.0/Microsoft.Agents.AI.Workflows.dll</Right>
<IsBaselineSuppression>true</IsBaselineSuppression>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Microsoft.Agents.AI.Workflows.Configured`2</Target>
<Left>lib/net8.0/Microsoft.Agents.AI.Workflows.dll</Left>
<Right>lib/net8.0/Microsoft.Agents.AI.Workflows.dll</Right>
<IsBaselineSuppression>true</IsBaselineSuppression>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Microsoft.Agents.AI.Workflows.Config</Target>
<Left>lib/net9.0/Microsoft.Agents.AI.Workflows.dll</Left>
<Right>lib/net9.0/Microsoft.Agents.AI.Workflows.dll</Right>
<IsBaselineSuppression>true</IsBaselineSuppression>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Microsoft.Agents.AI.Workflows.Config`1</Target>
<Left>lib/net9.0/Microsoft.Agents.AI.Workflows.dll</Left>
<Right>lib/net9.0/Microsoft.Agents.AI.Workflows.dll</Right>
<IsBaselineSuppression>true</IsBaselineSuppression>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Microsoft.Agents.AI.Workflows.ConfigurationExtensions</Target>
<Left>lib/net9.0/Microsoft.Agents.AI.Workflows.dll</Left>
<Right>lib/net9.0/Microsoft.Agents.AI.Workflows.dll</Right>
<IsBaselineSuppression>true</IsBaselineSuppression>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Microsoft.Agents.AI.Workflows.Configured</Target>
<Left>lib/net9.0/Microsoft.Agents.AI.Workflows.dll</Left>
<Right>lib/net9.0/Microsoft.Agents.AI.Workflows.dll</Right>
<IsBaselineSuppression>true</IsBaselineSuppression>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Microsoft.Agents.AI.Workflows.Configured`1</Target>
<Left>lib/net9.0/Microsoft.Agents.AI.Workflows.dll</Left>
<Right>lib/net9.0/Microsoft.Agents.AI.Workflows.dll</Right>
<IsBaselineSuppression>true</IsBaselineSuppression>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Microsoft.Agents.AI.Workflows.Configured`2</Target>
<Left>lib/net9.0/Microsoft.Agents.AI.Workflows.dll</Left>
<Right>lib/net9.0/Microsoft.Agents.AI.Workflows.dll</Right>
<IsBaselineSuppression>true</IsBaselineSuppression>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Microsoft.Agents.AI.Workflows.Config</Target>
<Left>lib/netstandard2.0/Microsoft.Agents.AI.Workflows.dll</Left>
<Right>lib/netstandard2.0/Microsoft.Agents.AI.Workflows.dll</Right>
<IsBaselineSuppression>true</IsBaselineSuppression>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Microsoft.Agents.AI.Workflows.Config`1</Target>
<Left>lib/netstandard2.0/Microsoft.Agents.AI.Workflows.dll</Left>
<Right>lib/netstandard2.0/Microsoft.Agents.AI.Workflows.dll</Right>
<IsBaselineSuppression>true</IsBaselineSuppression>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Microsoft.Agents.AI.Workflows.ConfigurationExtensions</Target>
<Left>lib/netstandard2.0/Microsoft.Agents.AI.Workflows.dll</Left>
<Right>lib/netstandard2.0/Microsoft.Agents.AI.Workflows.dll</Right>
<IsBaselineSuppression>true</IsBaselineSuppression>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Microsoft.Agents.AI.Workflows.Configured</Target>
<Left>lib/netstandard2.0/Microsoft.Agents.AI.Workflows.dll</Left>
<Right>lib/netstandard2.0/Microsoft.Agents.AI.Workflows.dll</Right>
<IsBaselineSuppression>true</IsBaselineSuppression>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Microsoft.Agents.AI.Workflows.Configured`1</Target>
<Left>lib/netstandard2.0/Microsoft.Agents.AI.Workflows.dll</Left>
<Right>lib/netstandard2.0/Microsoft.Agents.AI.Workflows.dll</Right>
<IsBaselineSuppression>true</IsBaselineSuppression>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Microsoft.Agents.AI.Workflows.Configured`2</Target>
<Left>lib/netstandard2.0/Microsoft.Agents.AI.Workflows.dll</Left>
<Right>lib/netstandard2.0/Microsoft.Agents.AI.Workflows.dll</Right>
<IsBaselineSuppression>true</IsBaselineSuppression>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Microsoft.Agents.AI.Workflows.AgentWorkflowBuilder.CreateHandoffBuilderWith(Microsoft.Agents.AI.AIAgent)</Target>
<Left>lib/net10.0/Microsoft.Agents.AI.Workflows.dll</Left>
<Right>lib/net10.0/Microsoft.Agents.AI.Workflows.dll</Right>
<IsBaselineSuppression>true</IsBaselineSuppression>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Microsoft.Agents.AI.Workflows.ExecutorBindingExtensions.BindExecutor``2(System.Func{Microsoft.Agents.AI.Workflows.Config{``1},System.String,System.Threading.Tasks.ValueTask{``0}},System.String,``1)</Target>
<Left>lib/net10.0/Microsoft.Agents.AI.Workflows.dll</Left>
<Right>lib/net10.0/Microsoft.Agents.AI.Workflows.dll</Right>
<IsBaselineSuppression>true</IsBaselineSuppression>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Microsoft.Agents.AI.Workflows.ExecutorBindingExtensions.ConfigureFactory``2(System.Func{Microsoft.Agents.AI.Workflows.Config{``1},System.String,System.Threading.Tasks.ValueTask{``0}},System.String,``1)</Target>
<Left>lib/net10.0/Microsoft.Agents.AI.Workflows.dll</Left>
<Right>lib/net10.0/Microsoft.Agents.AI.Workflows.dll</Right>
<IsBaselineSuppression>true</IsBaselineSuppression>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Microsoft.Agents.AI.Workflows.AgentWorkflowBuilder.CreateHandoffBuilderWith(Microsoft.Agents.AI.AIAgent)</Target>
<Left>lib/net472/Microsoft.Agents.AI.Workflows.dll</Left>
<Right>lib/net472/Microsoft.Agents.AI.Workflows.dll</Right>
<IsBaselineSuppression>true</IsBaselineSuppression>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Microsoft.Agents.AI.Workflows.ExecutorBindingExtensions.BindExecutor``2(System.Func{Microsoft.Agents.AI.Workflows.Config{``1},System.String,System.Threading.Tasks.ValueTask{``0}},System.String,``1)</Target>
<Left>lib/net472/Microsoft.Agents.AI.Workflows.dll</Left>
<Right>lib/net472/Microsoft.Agents.AI.Workflows.dll</Right>
<IsBaselineSuppression>true</IsBaselineSuppression>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Microsoft.Agents.AI.Workflows.ExecutorBindingExtensions.ConfigureFactory``2(System.Func{Microsoft.Agents.AI.Workflows.Config{``1},System.String,System.Threading.Tasks.ValueTask{``0}},System.String,``1)</Target>
<Left>lib/net472/Microsoft.Agents.AI.Workflows.dll</Left>
<Right>lib/net472/Microsoft.Agents.AI.Workflows.dll</Right>
<IsBaselineSuppression>true</IsBaselineSuppression>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Microsoft.Agents.AI.Workflows.AgentWorkflowBuilder.CreateHandoffBuilderWith(Microsoft.Agents.AI.AIAgent)</Target>
<Left>lib/net8.0/Microsoft.Agents.AI.Workflows.dll</Left>
<Right>lib/net8.0/Microsoft.Agents.AI.Workflows.dll</Right>
<IsBaselineSuppression>true</IsBaselineSuppression>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Microsoft.Agents.AI.Workflows.ExecutorBindingExtensions.BindExecutor``2(System.Func{Microsoft.Agents.AI.Workflows.Config{``1},System.String,System.Threading.Tasks.ValueTask{``0}},System.String,``1)</Target>
<Left>lib/net8.0/Microsoft.Agents.AI.Workflows.dll</Left>
<Right>lib/net8.0/Microsoft.Agents.AI.Workflows.dll</Right>
<IsBaselineSuppression>true</IsBaselineSuppression>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Microsoft.Agents.AI.Workflows.ExecutorBindingExtensions.ConfigureFactory``2(System.Func{Microsoft.Agents.AI.Workflows.Config{``1},System.String,System.Threading.Tasks.ValueTask{``0}},System.String,``1)</Target>
<Left>lib/net8.0/Microsoft.Agents.AI.Workflows.dll</Left>
<Right>lib/net8.0/Microsoft.Agents.AI.Workflows.dll</Right>
<IsBaselineSuppression>true</IsBaselineSuppression>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Microsoft.Agents.AI.Workflows.AgentWorkflowBuilder.CreateHandoffBuilderWith(Microsoft.Agents.AI.AIAgent)</Target>
<Left>lib/net9.0/Microsoft.Agents.AI.Workflows.dll</Left>
<Right>lib/net9.0/Microsoft.Agents.AI.Workflows.dll</Right>
<IsBaselineSuppression>true</IsBaselineSuppression>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Microsoft.Agents.AI.Workflows.ExecutorBindingExtensions.BindExecutor``2(System.Func{Microsoft.Agents.AI.Workflows.Config{``1},System.String,System.Threading.Tasks.ValueTask{``0}},System.String,``1)</Target>
<Left>lib/net9.0/Microsoft.Agents.AI.Workflows.dll</Left>
<Right>lib/net9.0/Microsoft.Agents.AI.Workflows.dll</Right>
<IsBaselineSuppression>true</IsBaselineSuppression>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Microsoft.Agents.AI.Workflows.ExecutorBindingExtensions.ConfigureFactory``2(System.Func{Microsoft.Agents.AI.Workflows.Config{``1},System.String,System.Threading.Tasks.ValueTask{``0}},System.String,``1)</Target>
<Left>lib/net9.0/Microsoft.Agents.AI.Workflows.dll</Left>
<Right>lib/net9.0/Microsoft.Agents.AI.Workflows.dll</Right>
<IsBaselineSuppression>true</IsBaselineSuppression>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Microsoft.Agents.AI.Workflows.AgentWorkflowBuilder.CreateHandoffBuilderWith(Microsoft.Agents.AI.AIAgent)</Target>
<Left>lib/netstandard2.0/Microsoft.Agents.AI.Workflows.dll</Left>
<Right>lib/netstandard2.0/Microsoft.Agents.AI.Workflows.dll</Right>
<IsBaselineSuppression>true</IsBaselineSuppression>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Microsoft.Agents.AI.Workflows.ExecutorBindingExtensions.BindExecutor``2(System.Func{Microsoft.Agents.AI.Workflows.Config{``1},System.String,System.Threading.Tasks.ValueTask{``0}},System.String,``1)</Target>
<Left>lib/netstandard2.0/Microsoft.Agents.AI.Workflows.dll</Left>
<Right>lib/netstandard2.0/Microsoft.Agents.AI.Workflows.dll</Right>
<IsBaselineSuppression>true</IsBaselineSuppression>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Microsoft.Agents.AI.Workflows.ExecutorBindingExtensions.ConfigureFactory``2(System.Func{Microsoft.Agents.AI.Workflows.Config{``1},System.String,System.Threading.Tasks.ValueTask{``0}},System.String,``1)</Target>
<Left>lib/netstandard2.0/Microsoft.Agents.AI.Workflows.dll</Left>
<Right>lib/netstandard2.0/Microsoft.Agents.AI.Workflows.dll</Right>
<IsBaselineSuppression>true</IsBaselineSuppression>
</Suppression>
</Suppressions>
@@ -23,8 +23,7 @@ internal sealed class StreamingRunEventStream : IRunEventStream
private readonly CancellationTokenSource _runLoopCancellation;
private readonly bool _disableRunLoop;
private Task? _runLoopTask;
private volatile RunStatus _runStatus = RunStatus.NotStarted;
private RunStatus _runStatus = RunStatus.NotStarted;
private int _completionEpoch; // Tracks which completion signal belongs to which consumer iteration
public StreamingRunEventStream(ISuperStepRunner stepRunner, bool disableRunLoop = false)
@@ -128,7 +127,7 @@ internal sealed class StreamingRunEventStream : IRunEventStream
// Wait for next input from the consumer
// Works for both Idle (no work) and PendingRequests (waiting for responses)
await this._inputWaiter.WaitForInputAsync(linkedSource.Token).ConfigureAwait(false);
await this._inputWaiter.WaitForInputAsync(TimeSpan.FromSeconds(1), linkedSource.Token).ConfigureAwait(false);
// When signaled, resume running
this._runStatus = RunStatus.Running;
@@ -210,10 +209,7 @@ internal sealed class StreamingRunEventStream : IRunEventStream
[EnumeratorCancellation] CancellationToken cancellationToken = default)
{
// Get the current epoch - we'll only respond to completion signals from this epoch or later
int currentEpoch = Volatile.Read(ref this._completionEpoch);
bool expectingFreshWork = this._stepRunner.HasUnprocessedMessages || this._runStatus == RunStatus.Running;
int myEpoch = expectingFreshWork ? currentEpoch + 1 : currentEpoch;
int myEpoch = Volatile.Read(ref this._completionEpoch) + 1;
// Use custom async enumerable to avoid exceptions on cancellation.
NonThrowingChannelReaderAsyncEnumerable<WorkflowEvent> eventStream = new(this._eventChannel.Reader);
@@ -2,7 +2,6 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Microsoft.Agents.AI.Workflows.Specialized;
using Microsoft.Extensions.AI;
@@ -10,21 +9,13 @@ using Microsoft.Shared.Diagnostics;
namespace Microsoft.Agents.AI.Workflows;
internal static class DiagnosticConstants
{
public const string ExperimentalFeatureDiagnostic = "MAAIW001";
}
/// <inheritdoc/>
[Obsolete("Prefer HandoffWorkflowBuilder (no 's') instead, which has the same API but the preferred name. This will be removed in a future release before GA.")]
#pragma warning disable MAAIW001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
public sealed class HandoffsWorkflowBuilder(AIAgent initialAgent) : HandoffWorkflowBuilderCore<HandoffsWorkflowBuilder>(initialAgent)
#pragma warning restore MAAIW001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
{
}
/// <inheritdoc/>
[Experimental(DiagnosticConstants.ExperimentalFeatureDiagnostic)]
public sealed class HandoffWorkflowBuilder(AIAgent initialAgent) : HandoffWorkflowBuilderCore<HandoffWorkflowBuilder>(initialAgent)
{
}
@@ -32,7 +23,6 @@ public sealed class HandoffWorkflowBuilder(AIAgent initialAgent) : HandoffWorkfl
/// <summary>
/// Provides a builder for specifying the handoff relationships between agents and building the resulting workflow.
/// </summary>
[Experimental(DiagnosticConstants.ExperimentalFeatureDiagnostic)]
public class HandoffWorkflowBuilderCore<TBuilder> where TBuilder : HandoffWorkflowBuilderCore<TBuilder>
{
/// <summary>
@@ -1,14 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<IsReleased>true</IsReleased>
<IsReleaseCandidate>true</IsReleaseCandidate>
<NoWarn>$(NoWarn);MEAI001</NoWarn>
</PropertyGroup>
<PropertyGroup>
<InjectSharedThrow>true</InjectSharedThrow>
<InjectIsExternalInitOnLegacy>true</InjectIsExternalInitOnLegacy>
<InjectExperimentalAttributeOnLegacy>true</InjectExperimentalAttributeOnLegacy>
<InjectTrimAttributesOnLegacy>true</InjectTrimAttributesOnLegacy>
</PropertyGroup>
@@ -4,7 +4,6 @@ using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Text.Json;
using System.Threading;
@@ -32,7 +31,6 @@ internal sealed class HandoffAgentExecutorOptions
public HandoffToolCallFilteringBehavior ToolCallFilteringBehavior { get; set; } = HandoffToolCallFilteringBehavior.HandoffOnly;
}
[Experimental(DiagnosticConstants.ExperimentalFeatureDiagnostic)]
internal sealed class HandoffMessagesFilter
{
private readonly HandoffToolCallFilteringBehavior _filteringBehavior;
@@ -42,7 +40,6 @@ internal sealed class HandoffMessagesFilter
this._filteringBehavior = filteringBehavior;
}
[Experimental(DiagnosticConstants.ExperimentalFeatureDiagnostic)]
internal static bool IsHandoffFunctionName(string name)
{
return name.StartsWith(HandoffWorkflowBuilder.FunctionPrefix, StringComparison.Ordinal);
@@ -167,7 +164,6 @@ internal sealed class HandoffMessagesFilter
}
/// <summary>Executor used to represent an agent in a handoffs workflow, responding to <see cref="HandoffState"/> events.</summary>
[Experimental(DiagnosticConstants.ExperimentalFeatureDiagnostic)]
internal sealed class HandoffAgentExecutor(
AIAgent agent,
HandoffAgentExecutorOptions options) : Executor<HandoffState, HandoffState>(agent.GetDescriptiveId(), declareCrossRunShareable: true), IResettableExecutor
@@ -2,106 +2,71 @@
<!-- https://learn.microsoft.com/dotnet/fundamentals/package-validation/diagnostic-ids -->
<Suppressions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Microsoft.Agents.AI.ChatClientAgentOptions.get_SimulateServiceStoredChatHistory</Target>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Microsoft.Agents.AI.FileAgentSkillsProvider</Target>
<Left>lib/net10.0/Microsoft.Agents.AI.dll</Left>
<Right>lib/net10.0/Microsoft.Agents.AI.dll</Right>
<IsBaselineSuppression>true</IsBaselineSuppression>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Microsoft.Agents.AI.ChatClientAgentOptions.set_SimulateServiceStoredChatHistory(System.Boolean)</Target>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Microsoft.Agents.AI.FileAgentSkillsProviderOptions</Target>
<Left>lib/net10.0/Microsoft.Agents.AI.dll</Left>
<Right>lib/net10.0/Microsoft.Agents.AI.dll</Right>
<IsBaselineSuppression>true</IsBaselineSuppression>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Microsoft.Extensions.AI.ChatClientBuilderExtensions.UseServiceStoredChatHistorySimulation(Microsoft.Extensions.AI.ChatClientBuilder)</Target>
<Left>lib/net10.0/Microsoft.Agents.AI.dll</Left>
<Right>lib/net10.0/Microsoft.Agents.AI.dll</Right>
<IsBaselineSuppression>true</IsBaselineSuppression>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Microsoft.Agents.AI.ChatClientAgentOptions.get_SimulateServiceStoredChatHistory</Target>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Microsoft.Agents.AI.FileAgentSkillsProvider</Target>
<Left>lib/net472/Microsoft.Agents.AI.dll</Left>
<Right>lib/net472/Microsoft.Agents.AI.dll</Right>
<IsBaselineSuppression>true</IsBaselineSuppression>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Microsoft.Agents.AI.ChatClientAgentOptions.set_SimulateServiceStoredChatHistory(System.Boolean)</Target>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Microsoft.Agents.AI.FileAgentSkillsProviderOptions</Target>
<Left>lib/net472/Microsoft.Agents.AI.dll</Left>
<Right>lib/net472/Microsoft.Agents.AI.dll</Right>
<IsBaselineSuppression>true</IsBaselineSuppression>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Microsoft.Extensions.AI.ChatClientBuilderExtensions.UseServiceStoredChatHistorySimulation(Microsoft.Extensions.AI.ChatClientBuilder)</Target>
<Left>lib/net472/Microsoft.Agents.AI.dll</Left>
<Right>lib/net472/Microsoft.Agents.AI.dll</Right>
<IsBaselineSuppression>true</IsBaselineSuppression>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Microsoft.Agents.AI.ChatClientAgentOptions.get_SimulateServiceStoredChatHistory</Target>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Microsoft.Agents.AI.FileAgentSkillsProvider</Target>
<Left>lib/net8.0/Microsoft.Agents.AI.dll</Left>
<Right>lib/net8.0/Microsoft.Agents.AI.dll</Right>
<IsBaselineSuppression>true</IsBaselineSuppression>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Microsoft.Agents.AI.ChatClientAgentOptions.set_SimulateServiceStoredChatHistory(System.Boolean)</Target>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Microsoft.Agents.AI.FileAgentSkillsProviderOptions</Target>
<Left>lib/net8.0/Microsoft.Agents.AI.dll</Left>
<Right>lib/net8.0/Microsoft.Agents.AI.dll</Right>
<IsBaselineSuppression>true</IsBaselineSuppression>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Microsoft.Extensions.AI.ChatClientBuilderExtensions.UseServiceStoredChatHistorySimulation(Microsoft.Extensions.AI.ChatClientBuilder)</Target>
<Left>lib/net8.0/Microsoft.Agents.AI.dll</Left>
<Right>lib/net8.0/Microsoft.Agents.AI.dll</Right>
<IsBaselineSuppression>true</IsBaselineSuppression>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Microsoft.Agents.AI.ChatClientAgentOptions.get_SimulateServiceStoredChatHistory</Target>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Microsoft.Agents.AI.FileAgentSkillsProvider</Target>
<Left>lib/net9.0/Microsoft.Agents.AI.dll</Left>
<Right>lib/net9.0/Microsoft.Agents.AI.dll</Right>
<IsBaselineSuppression>true</IsBaselineSuppression>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Microsoft.Agents.AI.ChatClientAgentOptions.set_SimulateServiceStoredChatHistory(System.Boolean)</Target>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Microsoft.Agents.AI.FileAgentSkillsProviderOptions</Target>
<Left>lib/net9.0/Microsoft.Agents.AI.dll</Left>
<Right>lib/net9.0/Microsoft.Agents.AI.dll</Right>
<IsBaselineSuppression>true</IsBaselineSuppression>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Microsoft.Extensions.AI.ChatClientBuilderExtensions.UseServiceStoredChatHistorySimulation(Microsoft.Extensions.AI.ChatClientBuilder)</Target>
<Left>lib/net9.0/Microsoft.Agents.AI.dll</Left>
<Right>lib/net9.0/Microsoft.Agents.AI.dll</Right>
<IsBaselineSuppression>true</IsBaselineSuppression>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Microsoft.Agents.AI.ChatClientAgentOptions.get_SimulateServiceStoredChatHistory</Target>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Microsoft.Agents.AI.FileAgentSkillsProvider</Target>
<Left>lib/netstandard2.0/Microsoft.Agents.AI.dll</Left>
<Right>lib/netstandard2.0/Microsoft.Agents.AI.dll</Right>
<IsBaselineSuppression>true</IsBaselineSuppression>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Microsoft.Agents.AI.ChatClientAgentOptions.set_SimulateServiceStoredChatHistory(System.Boolean)</Target>
<Left>lib/netstandard2.0/Microsoft.Agents.AI.dll</Left>
<Right>lib/netstandard2.0/Microsoft.Agents.AI.dll</Right>
<IsBaselineSuppression>true</IsBaselineSuppression>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Microsoft.Extensions.AI.ChatClientBuilderExtensions.UseServiceStoredChatHistorySimulation(Microsoft.Extensions.AI.ChatClientBuilder)</Target>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Microsoft.Agents.AI.FileAgentSkillsProviderOptions</Target>
<Left>lib/netstandard2.0/Microsoft.Agents.AI.dll</Left>
<Right>lib/netstandard2.0/Microsoft.Agents.AI.dll</Right>
<IsBaselineSuppression>true</IsBaselineSuppression>
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<IsReleased>true</IsReleased>
<IsReleaseCandidate>true</IsReleaseCandidate>
<NoWarn>$(NoWarn);MEAI001;MAAI001</NoWarn>
</PropertyGroup>
@@ -26,6 +26,7 @@ internal static class DiagnosticIds
// We use the same IDs so consumers do not need to suppress additional diagnostics
// when using the experimental OpenAI APIs.
internal const string AIOpenAIResponses = "OPENAI001";
internal const string AIOpenAIAssistants = "OPENAI001";
private const string MEAIExperiments = "MEAI001";
}
@@ -12,13 +12,13 @@ namespace Shared.Foundry;
internal static class AgentFactory
{
public static async ValueTask<ProjectsAgentVersion> CreateAgentAsync(
public static async ValueTask<AgentVersion> CreateAgentAsync(
this AIProjectClient aiProjectClient,
string agentName,
ProjectsAgentDefinition agentDefinition,
AgentDefinition agentDefinition,
string agentDescription)
{
ProjectsAgentVersionCreationOptions options =
AgentVersionCreationOptions options =
new(agentDefinition)
{
Description = agentDescription,
@@ -29,7 +29,7 @@ internal static class AgentFactory
},
};
ProjectsAgentVersion agentVersion = await aiProjectClient.AgentAdministrationClient.CreateAgentVersionAsync(agentName, options).ConfigureAwait(false);
AgentVersion agentVersion = await aiProjectClient.Agents.CreateAgentVersionAsync(agentName, options).ConfigureAwait(false);
Console.ForegroundColor = ConsoleColor.Cyan;
try
@@ -17,7 +17,7 @@ namespace Foundry.IntegrationTests;
/// <summary>
/// Integration tests for versioned <see cref="FoundryAgent"/> creation via
/// <c>AIProjectClient.AgentAdministrationClient.CreateAgentVersionAsync</c> and <c>AIProjectClient.AsAIAgent(ProjectsAgentVersion)</c>.
/// <c>AIProjectClient.Agents.CreateAgentVersionAsync</c> and <c>AIProjectClient.AsAIAgent(AgentVersion)</c>.
/// </summary>
public class FoundryVersionedAgentCreateTests
{
@@ -32,10 +32,10 @@ public class FoundryVersionedAgentCreateTests
const string AgentInstructions = "You are an integration test agent";
// Act.
var agentVersion = await this._client.AgentAdministrationClient.CreateAgentVersionAsync(
var agentVersion = await this._client.Agents.CreateAgentVersionAsync(
AgentName,
new ProjectsAgentVersionCreationOptions(
new DeclarativeAgentDefinition(TestConfiguration.GetRequiredValue(TestSettings.AzureAIModelDeploymentName))
new AgentVersionCreationOptions(
new PromptAgentDefinition(TestConfiguration.GetRequiredValue(TestSettings.AzureAIModelDeploymentName))
{
Instructions = AgentInstructions
})
@@ -53,17 +53,17 @@ public class FoundryVersionedAgentCreateTests
Assert.Equal(AgentDescription, agent.Description);
Assert.Equal(AgentInstructions, agent.GetService<ChatClientAgent>()!.Instructions);
var agentRecord = await this._client.AgentAdministrationClient.GetAgentAsync(agent.Name);
var agentRecord = await this._client.Agents.GetAgentAsync(agent.Name);
Assert.NotNull(agentRecord);
Assert.Equal(AgentName, agentRecord.Value.Name);
var definition = Assert.IsType<DeclarativeAgentDefinition>(agentRecord.Value.GetLatestVersion().Definition);
var definition = Assert.IsType<PromptAgentDefinition>(agentRecord.Value.GetLatestVersion().Definition);
Assert.Equal(AgentDescription, agentRecord.Value.GetLatestVersion().Description);
Assert.Equal(AgentInstructions, definition.Instructions);
}
finally
{
// Cleanup.
await this._client.AgentAdministrationClient.DeleteAgentAsync(agent.Name);
await this._client.Agents.DeleteAgentAsync(agent.Name);
}
}
@@ -95,15 +95,15 @@ public class FoundryVersionedAgentCreateTests
var vectorStoreMetadata = await projectOpenAIClient.GetProjectVectorStoresClient().CreateVectorStoreAsync(options: new() { FileIds = { uploadedAgentFile.Id }, Name = "WordCodeLookup_VectorStore" });
// Act — create agent version with FileSearch tool via native SDK, then wrap with AsAIAgent.
var definition = new DeclarativeAgentDefinition(TestConfiguration.GetRequiredValue(TestSettings.AzureAIModelDeploymentName))
var definition = new PromptAgentDefinition(TestConfiguration.GetRequiredValue(TestSettings.AzureAIModelDeploymentName))
{
Instructions = AgentInstructions,
Tools = { ResponseTool.CreateFileSearchTool(vectorStoreIds: [vectorStoreMetadata.Value.Id]) }
};
var agentVersion = await this._client.AgentAdministrationClient.CreateAgentVersionAsync(
var agentVersion = await this._client.Agents.CreateAgentVersionAsync(
AgentName,
new ProjectsAgentVersionCreationOptions(definition));
new AgentVersionCreationOptions(definition));
var agent = this._client.AsAIAgent(agentVersion);
@@ -117,7 +117,7 @@ public class FoundryVersionedAgentCreateTests
finally
{
// Cleanup.
await this._client.AgentAdministrationClient.DeleteAgentAsync(agent.Name);
await this._client.Agents.DeleteAgentAsync(agent.Name);
await projectOpenAIClient.GetProjectVectorStoresClient().DeleteVectorStoreAsync(vectorStoreMetadata.Value.Id);
await projectOpenAIClient.GetProjectFilesClient().DeleteFileAsync(uploadedAgentFile.Id);
File.Delete(searchFilePath);
@@ -149,15 +149,15 @@ public class FoundryVersionedAgentCreateTests
);
// Act — create agent version with CodeInterpreter tool via native SDK, then wrap with AsAIAgent.
var definition = new DeclarativeAgentDefinition(TestConfiguration.GetRequiredValue(TestSettings.AzureAIModelDeploymentName))
var definition = new PromptAgentDefinition(TestConfiguration.GetRequiredValue(TestSettings.AzureAIModelDeploymentName))
{
Instructions = AgentInstructions,
Tools = { ResponseTool.CreateCodeInterpreterTool(new CodeInterpreterToolContainer(CodeInterpreterToolContainerConfiguration.CreateAutomaticContainerConfiguration([uploadedCodeFile.Id]))) }
};
var agentVersion = await this._client.AgentAdministrationClient.CreateAgentVersionAsync(
var agentVersion = await this._client.Agents.CreateAgentVersionAsync(
AgentName,
new ProjectsAgentVersionCreationOptions(definition));
new AgentVersionCreationOptions(definition));
var agent = this._client.AsAIAgent(agentVersion);
@@ -171,7 +171,7 @@ public class FoundryVersionedAgentCreateTests
finally
{
// Cleanup.
await this._client.AgentAdministrationClient.DeleteAgentAsync(agent.Name);
await this._client.Agents.DeleteAgentAsync(agent.Name);
await projectOpenAIClient.GetProjectFilesClient().DeleteFileAsync(uploadedCodeFile.Id);
File.Delete(codeFilePath);
}
@@ -252,14 +252,14 @@ public class FoundryVersionedAgentCreateTests
Description = "Retrieve information about countries by currency code"
};
var definition = new DeclarativeAgentDefinition(model: TestConfiguration.GetRequiredValue(TestSettings.AzureAIModelDeploymentName))
var definition = new PromptAgentDefinition(model: TestConfiguration.GetRequiredValue(TestSettings.AzureAIModelDeploymentName))
{
Instructions = AgentInstructions,
Tools = { (ResponseTool)ProjectsAgentTool.CreateOpenApiTool(openApiFunction) }
Tools = { (ResponseTool)AgentTool.CreateOpenApiTool(openApiFunction) }
};
ProjectsAgentVersionCreationOptions creationOptions = new(definition);
ProjectsAgentVersion agentVersion = await this._client.AgentAdministrationClient.CreateAgentVersionAsync(AgentName, creationOptions);
AgentVersionCreationOptions creationOptions = new(definition);
AgentVersion agentVersion = await this._client.Agents.CreateAgentVersionAsync(AgentName, creationOptions);
try
{
@@ -269,7 +269,7 @@ public class FoundryVersionedAgentCreateTests
// Assert the agent was created correctly and retains version metadata.
Assert.NotNull(agent);
Assert.Equal(AgentName, agent.Name);
var retrievedVersion = agent.GetService<ProjectsAgentVersion>();
var retrievedVersion = agent.GetService<AgentVersion>();
Assert.NotNull(retrievedVersion);
// Step 3: Call RunAsync to trigger the server-side OpenAPI function.
@@ -301,7 +301,7 @@ public class FoundryVersionedAgentCreateTests
finally
{
// Cleanup.
await this._client.AgentAdministrationClient.DeleteAgentAsync(AgentName);
await this._client.Agents.DeleteAgentAsync(AgentName);
}
}
@@ -317,15 +317,15 @@ public class FoundryVersionedAgentCreateTests
// Create agent version with the function tool registered in the server-side definition,
// then wrap with AsAIAgent passing the local AIFunction implementation.
var definition = new DeclarativeAgentDefinition(TestConfiguration.GetRequiredValue(TestSettings.AzureAIModelDeploymentName))
var definition = new PromptAgentDefinition(TestConfiguration.GetRequiredValue(TestSettings.AzureAIModelDeploymentName))
{
Instructions = AgentInstructions,
};
definition.Tools.Add(weatherFunction.AsOpenAIResponseTool());
var agentVersion = await this._client.AgentAdministrationClient.CreateAgentVersionAsync(
var agentVersion = await this._client.Agents.CreateAgentVersionAsync(
AgentName,
new ProjectsAgentVersionCreationOptions(definition));
new AgentVersionCreationOptions(definition));
FoundryAgent agent = this._client.AsAIAgent(agentVersion, tools: [weatherFunction]);
@@ -342,7 +342,7 @@ public class FoundryVersionedAgentCreateTests
}
finally
{
await this._client.AgentAdministrationClient.DeleteAgentAsync(agent.Name);
await this._client.Agents.DeleteAgentAsync(agent.Name);
}
}
}
@@ -19,8 +19,8 @@ namespace Foundry.IntegrationTests;
/// <summary>
/// Integration test fixture that creates versioned Foundry agents via
/// <c>AIProjectClient.AgentAdministrationClient.CreateAgentVersionAsync</c> and wraps them
/// with <c>AIProjectClient.AsAIAgent(ProjectsAgentVersion)</c>.
/// <c>AIProjectClient.Agents.CreateAgentVersionAsync</c> and wraps them
/// with <c>AIProjectClient.AsAIAgent(AgentVersion)</c>.
/// </summary>
public class FoundryVersionedAgentFixture : IChatClientAgentFixture
{
@@ -121,7 +121,7 @@ public class FoundryVersionedAgentFixture : IChatClientAgentFixture
string instructions = "You are a helpful assistant.",
IList<AITool>? aiTools = null)
{
var definition = new DeclarativeAgentDefinition(TestConfiguration.GetRequiredValue(TestSettings.AzureAIModelDeploymentName))
var definition = new PromptAgentDefinition(TestConfiguration.GetRequiredValue(TestSettings.AzureAIModelDeploymentName))
{
Instructions = instructions
};
@@ -139,9 +139,9 @@ public class FoundryVersionedAgentFixture : IChatClientAgentFixture
}
}
var agentVersion = await this._client.AgentAdministrationClient.CreateAgentVersionAsync(
var agentVersion = await this._client.Agents.CreateAgentVersionAsync(
GenerateUniqueAgentName(name),
new ProjectsAgentVersionCreationOptions(definition));
new AgentVersionCreationOptions(definition));
return this._client.AsAIAgent(agentVersion, tools: aiTools).GetService<ChatClientAgent>()!;
}
@@ -150,15 +150,15 @@ public class FoundryVersionedAgentFixture : IChatClientAgentFixture
{
options.Name ??= GenerateUniqueAgentName("HelpfulAssistant");
var definition = new DeclarativeAgentDefinition(
var definition = new PromptAgentDefinition(
options.ChatOptions?.ModelId ?? TestConfiguration.GetRequiredValue(TestSettings.AzureAIModelDeploymentName))
{
Instructions = options.ChatOptions?.Instructions
};
var agentVersion = await this._client.AgentAdministrationClient.CreateAgentVersionAsync(
var agentVersion = await this._client.Agents.CreateAgentVersionAsync(
options.Name,
new ProjectsAgentVersionCreationOptions(definition) { Description = options.Description });
new AgentVersionCreationOptions(definition) { Description = options.Description });
var agent = this._client.AsAIAgent(agentVersion, tools: options.ChatOptions?.Tools);
@@ -169,7 +169,7 @@ public class FoundryVersionedAgentFixture : IChatClientAgentFixture
$"{baseName}-{Guid.NewGuid().ToString("N").Substring(0, 8)}";
public Task DeleteAgentAsync(ChatClientAgent agent) =>
this._client.AgentAdministrationClient.DeleteAgentAsync(agent.Name);
this._client.Agents.DeleteAgentAsync(agent.Name);
public async Task DeleteSessionAsync(AgentSession session)
{
@@ -201,7 +201,7 @@ public class FoundryVersionedAgentFixture : IChatClientAgentFixture
if (this._client is not null && this._agent is not null)
{
return new ValueTask(this._client.AgentAdministrationClient.DeleteAgentAsync(this._agent.Name));
return new ValueTask(this._client.Agents.DeleteAgentAsync(this._agent.Name));
}
return default;
@@ -211,10 +211,10 @@ public class FoundryVersionedAgentFixture : IChatClientAgentFixture
{
this._client = new(new Uri(TestConfiguration.GetRequiredValue(TestSettings.AzureAIProjectEndpoint)), TestAzureCliCredentials.CreateAzureCliCredential());
var agentVersion = await this._client.AgentAdministrationClient.CreateAgentVersionAsync(
var agentVersion = await this._client.Agents.CreateAgentVersionAsync(
GenerateUniqueAgentName("HelpfulAssistant"),
new ProjectsAgentVersionCreationOptions(
new DeclarativeAgentDefinition(TestConfiguration.GetRequiredValue(TestSettings.AzureAIModelDeploymentName))
new AgentVersionCreationOptions(
new PromptAgentDefinition(TestConfiguration.GetRequiredValue(TestSettings.AzureAIModelDeploymentName))
{
Instructions = "You are a helpful assistant."
}));
@@ -227,15 +227,15 @@ public class FoundryVersionedAgentFixture : IChatClientAgentFixture
this._client = new(new Uri(TestConfiguration.GetRequiredValue(TestSettings.AzureAIProjectEndpoint)), TestAzureCliCredentials.CreateAzureCliCredential());
options.Name ??= GenerateUniqueAgentName("HelpfulAssistant");
var definition = new DeclarativeAgentDefinition(
var definition = new PromptAgentDefinition(
options.ChatOptions?.ModelId ?? TestConfiguration.GetRequiredValue(TestSettings.AzureAIModelDeploymentName))
{
Instructions = options.ChatOptions?.Instructions
};
var agentVersion = await this._client.AgentAdministrationClient.CreateAgentVersionAsync(
var agentVersion = await this._client.Agents.CreateAgentVersionAsync(
options.Name,
new ProjectsAgentVersionCreationOptions(definition) { Description = options.Description });
new AgentVersionCreationOptions(definition) { Description = options.Description });
this._agent = this._client.AsAIAgent(agentVersion, tools: options.ChatOptions?.Tools);
}
@@ -3,7 +3,6 @@
using System;
using System.Threading.Tasks;
using Azure.AI.Projects;
using Azure.AI.Projects.Memory;
using Azure.Identity;
using Microsoft.Agents.AI;
using Microsoft.Agents.AI.Foundry;
@@ -187,7 +187,7 @@ public sealed class AzureAIProjectChatClientExtensionsTests
#endregion
#region AsAIAgent(AIProjectClient, ProjectsAgentRecord) Tests
#region AsAIAgent(AIProjectClient, AgentRecord) Tests
/// <summary>
/// Verify that AsAIAgent throws ArgumentNullException when AIProjectClient is null.
@@ -197,7 +197,7 @@ public sealed class AzureAIProjectChatClientExtensionsTests
{
// Arrange
AIProjectClient? client = null;
ProjectsAgentRecord agentRecord = this.CreateTestAgentRecord();
AgentRecord agentRecord = this.CreateTestAgentRecord();
// Act & Assert
var exception = Assert.Throws<ArgumentNullException>(() =>
@@ -217,20 +217,20 @@ public sealed class AzureAIProjectChatClientExtensionsTests
// Act & Assert
var exception = Assert.Throws<ArgumentNullException>(() =>
mockClient.Object.AsAIAgent((ProjectsAgentRecord)null!));
mockClient.Object.AsAIAgent((AgentRecord)null!));
Assert.Equal("agentRecord", exception.ParamName);
}
/// <summary>
/// Verify that AsAIAgent with ProjectsAgentRecord creates a valid agent.
/// Verify that AsAIAgent with AgentRecord creates a valid agent.
/// </summary>
[Fact]
public void AsAIAgent_WithAgentRecord_CreatesValidAgent()
{
// Arrange
AIProjectClient client = this.CreateTestAgentClient();
ProjectsAgentRecord agentRecord = this.CreateTestAgentRecord();
AgentRecord agentRecord = this.CreateTestAgentRecord();
// Act
var agent = client.AsAIAgent(agentRecord);
@@ -243,14 +243,14 @@ public sealed class AzureAIProjectChatClientExtensionsTests
}
/// <summary>
/// Verify that AsAIAgent with ProjectsAgentRecord and clientFactory applies the factory.
/// Verify that AsAIAgent with AgentRecord and clientFactory applies the factory.
/// </summary>
[Fact]
public void AsAIAgent_WithAgentRecord_WithClientFactory_AppliesFactoryCorrectly()
{
// Arrange
AIProjectClient client = this.CreateTestAgentClient();
ProjectsAgentRecord agentRecord = this.CreateTestAgentRecord();
AgentRecord agentRecord = this.CreateTestAgentRecord();
TestChatClient? testChatClient = null;
// Act
@@ -267,7 +267,7 @@ public sealed class AzureAIProjectChatClientExtensionsTests
#endregion
#region AsAIAgent(AIProjectClient, ProjectsAgentVersion) Tests
#region AsAIAgent(AIProjectClient, AgentVersion) Tests
/// <summary>
/// Verify that AsAIAgent throws ArgumentNullException when AIProjectClient is null.
@@ -277,7 +277,7 @@ public sealed class AzureAIProjectChatClientExtensionsTests
{
// Arrange
AIProjectClient? client = null;
ProjectsAgentVersion agentVersion = this.CreateTestAgentVersion();
AgentVersion agentVersion = this.CreateTestAgentVersion();
// Act & Assert
var exception = Assert.Throws<ArgumentNullException>(() =>
@@ -297,20 +297,20 @@ public sealed class AzureAIProjectChatClientExtensionsTests
// Act & Assert
var exception = Assert.Throws<ArgumentNullException>(() =>
mockClient.Object.AsAIAgent((ProjectsAgentVersion)null!));
mockClient.Object.AsAIAgent((AgentVersion)null!));
Assert.Equal("agentVersion", exception.ParamName);
}
/// <summary>
/// Verify that AsAIAgent with ProjectsAgentVersion creates a valid agent.
/// Verify that AsAIAgent with AgentVersion creates a valid agent.
/// </summary>
[Fact]
public void AsAIAgent_WithAgentVersion_CreatesValidAgent()
{
// Arrange
AIProjectClient client = this.CreateTestAgentClient();
ProjectsAgentVersion agentVersion = this.CreateTestAgentVersion();
AgentVersion agentVersion = this.CreateTestAgentVersion();
// Act
var agent = client.AsAIAgent(agentVersion);
@@ -323,14 +323,14 @@ public sealed class AzureAIProjectChatClientExtensionsTests
}
/// <summary>
/// Verify that AsAIAgent with ProjectsAgentVersion and clientFactory applies the factory.
/// Verify that AsAIAgent with AgentVersion and clientFactory applies the factory.
/// </summary>
[Fact]
public void AsAIAgent_WithAgentVersion_WithClientFactory_AppliesFactoryCorrectly()
{
// Arrange
AIProjectClient client = this.CreateTestAgentClient();
ProjectsAgentVersion agentVersion = this.CreateTestAgentVersion();
AgentVersion agentVersion = this.CreateTestAgentVersion();
TestChatClient? testChatClient = null;
// Act
@@ -353,7 +353,7 @@ public sealed class AzureAIProjectChatClientExtensionsTests
{
// Arrange
AIProjectClient client = this.CreateTestAgentClient();
ProjectsAgentVersion agentVersion = this.CreateTestAgentVersion();
AgentVersion agentVersion = this.CreateTestAgentVersion();
var tools = new List<AITool>
{
AIFunctionFactory.Create(() => "test", "test_function", "A test function")
@@ -375,7 +375,7 @@ public sealed class AzureAIProjectChatClientExtensionsTests
{
// Arrange
AIProjectClient client = this.CreateTestAgentClient();
ProjectsAgentVersion agentVersion = this.CreateTestAgentVersion();
AgentVersion agentVersion = this.CreateTestAgentVersion();
// Act - should not throw even without tools when requireInvocableTools is false
var agent = client.AsAIAgent(agentVersion);
@@ -439,7 +439,7 @@ public sealed class AzureAIProjectChatClientExtensionsTests
#endregion
#region AsAIAgent(AIProjectClient, ProjectsAgentRecord) with tools Tests
#region AsAIAgent(AIProjectClient, AgentRecord) with tools Tests
/// <summary>
/// Verify that AsAIAgent with additional tools when the definition has no tools does not throw and results in an agent with no tools.
@@ -449,7 +449,7 @@ public sealed class AzureAIProjectChatClientExtensionsTests
{
// Arrange
AIProjectClient client = this.CreateTestAgentClient();
ProjectsAgentRecord agentRecord = this.CreateTestAgentRecord();
AgentRecord agentRecord = this.CreateTestAgentRecord();
var tools = new List<AITool>
{
AIFunctionFactory.Create(() => "test", "test_function", "A test function")
@@ -463,9 +463,9 @@ public sealed class AzureAIProjectChatClientExtensionsTests
Assert.IsType<FoundryAgent>(agent);
var chatClient = agent.GetService<IChatClient>();
Assert.NotNull(chatClient);
var agentVersion = chatClient.GetService<ProjectsAgentVersion>();
var agentVersion = chatClient.GetService<AgentVersion>();
Assert.NotNull(agentVersion);
var definition = Assert.IsType<DeclarativeAgentDefinition>(agentVersion.Definition);
var definition = Assert.IsType<PromptAgentDefinition>(agentVersion.Definition);
Assert.Empty(definition.Tools);
}
@@ -477,7 +477,7 @@ public sealed class AzureAIProjectChatClientExtensionsTests
{
// Arrange
AIProjectClient client = this.CreateTestAgentClient();
ProjectsAgentRecord agentRecord = this.CreateTestAgentRecord();
AgentRecord agentRecord = this.CreateTestAgentRecord();
// Act
var agent = client.AsAIAgent(agentRecord, tools: null);
@@ -502,7 +502,7 @@ public sealed class AzureAIProjectChatClientExtensionsTests
var agentVersion = this.CreateTestAgentVersion();
// Manually add tools to the definition to simulate inline tools
if (agentVersion.Definition is DeclarativeAgentDefinition promptDef)
if (agentVersion.Definition is PromptAgentDefinition promptDef)
{
promptDef.Tools.Add(ResponseTool.CreateFunctionTool("inline_tool", BinaryData.FromString("{}"), strictModeEnabled: false));
}
@@ -513,9 +513,9 @@ public sealed class AzureAIProjectChatClientExtensionsTests
// Act & Assert
var agent = client.AsAIAgent(agentVersion, tools: [invocableInlineAITool, shouldBeIgnoredTool]);
Assert.NotNull(agent);
var version = agent.GetService<ProjectsAgentVersion>();
var version = agent.GetService<AgentVersion>();
Assert.NotNull(version);
var definition = Assert.IsType<DeclarativeAgentDefinition>(version.Definition);
var definition = Assert.IsType<PromptAgentDefinition>(version.Definition);
Assert.NotEmpty(definition.Tools);
Assert.NotNull(GetAgentChatOptions(agent));
Assert.NotNull(GetAgentChatOptions(agent)!.Tools);
@@ -535,7 +535,7 @@ public sealed class AzureAIProjectChatClientExtensionsTests
{
// Arrange
AIProjectClient client = this.CreateTestAgentClient();
ProjectsAgentRecord agentRecord = this.CreateTestAgentRecord();
AgentRecord agentRecord = this.CreateTestAgentRecord();
var tools = new List<AITool>
{
AIFunctionFactory.Create(() => "tool1", "param_tool_1", "First parameter tool"),
@@ -550,7 +550,7 @@ public sealed class AzureAIProjectChatClientExtensionsTests
Assert.IsType<FoundryAgent>(agent);
var chatClient = agent.GetService<IChatClient>();
Assert.NotNull(chatClient);
var agentVersion = chatClient.GetService<ProjectsAgentVersion>();
var agentVersion = chatClient.GetService<AgentVersion>();
Assert.NotNull(agentVersion);
}
@@ -565,7 +565,7 @@ public sealed class AzureAIProjectChatClientExtensionsTests
public async Task CreateAIAgentAsync_WithResponseToolsInDefinition_CreatesAgentSuccessfullyAsync()
{
// Arrange
var definition = new DeclarativeAgentDefinition("test-model") { Instructions = "Test instructions" };
var definition = new PromptAgentDefinition("test-model") { Instructions = "Test instructions" };
var fabricToolOptions = new FabricDataAgentToolOptions();
fabricToolOptions.ProjectConnections.Add(new ToolProjectConnection("connection-id"));
@@ -577,33 +577,33 @@ public sealed class AzureAIProjectChatClientExtensionsTests
// Add tools to the definition
definition.Tools.Add(ResponseTool.CreateFunctionTool("create_tool", BinaryData.FromString("{}"), strictModeEnabled: false));
definition.Tools.Add((ResponseTool)ProjectsAgentTool.CreateBingCustomSearchTool(new BingCustomSearchToolOptions([new BingCustomSearchConfiguration("connection-id", "instance-name")])));
definition.Tools.Add((ResponseTool)ProjectsAgentTool.CreateBrowserAutomationTool(new BrowserAutomationToolOptions(new BrowserAutomationToolConnectionParameters("id"))));
definition.Tools.Add(ProjectsAgentTool.CreateA2ATool(new Uri("https://test-uri.microsoft.com")));
definition.Tools.Add((ResponseTool)ProjectsAgentTool.CreateBingGroundingTool(new BingGroundingSearchToolOptions([new BingGroundingSearchConfiguration("connection-id")])));
definition.Tools.Add((ResponseTool)ProjectsAgentTool.CreateMicrosoftFabricTool(fabricToolOptions));
definition.Tools.Add((ResponseTool)ProjectsAgentTool.CreateOpenApiTool(new OpenApiFunctionDefinition("name", BinaryData.FromString(OpenAPISpec), new OpenAPIAnonymousAuthenticationDetails())));
definition.Tools.Add((ResponseTool)ProjectsAgentTool.CreateSharepointTool(sharepointOptions));
definition.Tools.Add((ResponseTool)ProjectsAgentTool.CreateStructuredOutputsTool(structuredOutputs));
definition.Tools.Add((ResponseTool)ProjectsAgentTool.CreateAzureAISearchTool(new AzureAISearchToolOptions([new AzureAISearchToolIndex() { IndexName = "name" }])));
definition.Tools.Add((ResponseTool)AgentTool.CreateBingCustomSearchTool(new BingCustomSearchToolOptions([new BingCustomSearchConfiguration("connection-id", "instance-name")])));
definition.Tools.Add((ResponseTool)AgentTool.CreateBrowserAutomationTool(new BrowserAutomationToolOptions(new BrowserAutomationToolConnectionParameters("id"))));
definition.Tools.Add(AgentTool.CreateA2ATool(new Uri("https://test-uri.microsoft.com")));
definition.Tools.Add((ResponseTool)AgentTool.CreateBingGroundingTool(new BingGroundingSearchToolOptions([new BingGroundingSearchConfiguration("connection-id")])));
definition.Tools.Add((ResponseTool)AgentTool.CreateMicrosoftFabricTool(fabricToolOptions));
definition.Tools.Add((ResponseTool)AgentTool.CreateOpenApiTool(new OpenApiFunctionDefinition("name", BinaryData.FromString(OpenAPISpec), new OpenAPIAnonymousAuthenticationDetails())));
definition.Tools.Add((ResponseTool)AgentTool.CreateSharepointTool(sharepointOptions));
definition.Tools.Add((ResponseTool)AgentTool.CreateStructuredOutputsTool(structuredOutputs));
definition.Tools.Add((ResponseTool)AgentTool.CreateAzureAISearchTool(new AzureAISearchToolOptions([new AzureAISearchToolIndex() { IndexName = "name" }])));
// Generate agent definition response with the tools
var definitionResponse = GeneratePromptDefinitionResponse(definition, definition.Tools.Select(t => t.AsAITool()).ToList());
using var testClient = CreateTestAgentClientWithHandler(agentDefinitionResponse: definitionResponse);
var options = new ProjectsAgentVersionCreationOptions(definition);
var options = new AgentVersionCreationOptions(definition);
// Act
var agentVersion = (await testClient.Client.AgentAdministrationClient.CreateAgentVersionAsync("test-agent", options)).Value;
var agentVersion = (await testClient.Client.Agents.CreateAgentVersionAsync("test-agent", options)).Value;
var agent = testClient.Client.AsAIAgent(agentVersion);
// Assert
Assert.NotNull(agent);
Assert.IsType<FoundryAgent>(agent);
var agentVersion2 = agent.GetService<ProjectsAgentVersion>()!;
var agentVersion2 = agent.GetService<AgentVersion>()!;
Assert.NotNull(agentVersion);
if (agentVersion2.Definition is DeclarativeAgentDefinition promptDef)
if (agentVersion2.Definition is PromptAgentDefinition promptDef)
{
Assert.NotEmpty(promptDef.Tools);
Assert.Equal(10, promptDef.Tools.Count);
@@ -624,19 +624,19 @@ public sealed class AzureAIProjectChatClientExtensionsTests
functionDescription: "Gets the user's name, as used for friendly address."
);
var definition = new DeclarativeAgentDefinition("test-model") { Instructions = "Test" };
var definition = new PromptAgentDefinition("test-model") { Instructions = "Test" };
definition.Tools.Add(functionTool);
// Generate response with the declarative function
var definitionResponse = new DeclarativeAgentDefinition("test-model") { Instructions = "Test" };
var definitionResponse = new PromptAgentDefinition("test-model") { Instructions = "Test" };
definitionResponse.Tools.Add(functionTool);
using var testClient = CreateTestAgentClientWithHandler(agentName: "test-agent", agentDefinitionResponse: definitionResponse);
var options = new ProjectsAgentVersionCreationOptions(definition);
var options = new AgentVersionCreationOptions(definition);
// Act
var agentVersion = (await testClient.Client.AgentAdministrationClient.CreateAgentVersionAsync("test-agent", options)).Value;
var agentVersion = (await testClient.Client.Agents.CreateAgentVersionAsync("test-agent", options)).Value;
var agent = testClient.Client.AsAIAgent(agentVersion);
// Assert
@@ -652,7 +652,7 @@ public sealed class AzureAIProjectChatClientExtensionsTests
{
// Arrange
using var testClient = CreateTestAgentClientWithHandler();
var definition = new DeclarativeAgentDefinition("test-model") { Instructions = "Test" };
var definition = new PromptAgentDefinition("test-model") { Instructions = "Test" };
// Create a declarative function (not invocable) using AIFunctionFactory.CreateDeclaration
using var doc = JsonDocument.Parse("{}");
@@ -661,10 +661,10 @@ public sealed class AzureAIProjectChatClientExtensionsTests
// Add to definition
definition.Tools.Add(declarativeFunction.AsOpenAIResponseTool() ?? throw new InvalidOperationException());
var options = new ProjectsAgentVersionCreationOptions(definition);
var options = new AgentVersionCreationOptions(definition);
// Act
var agentVersion = (await testClient.Client.AgentAdministrationClient.CreateAgentVersionAsync("test-agent", options)).Value;
var agentVersion = (await testClient.Client.Agents.CreateAgentVersionAsync("test-agent", options)).Value;
var agent = testClient.Client.AsAIAgent(agentVersion);
// Assert
@@ -679,7 +679,7 @@ public sealed class AzureAIProjectChatClientExtensionsTests
public async Task AsAIAgent_WithDeclarativeFunctionInDefinition_AcceptsDeclarativeFunctionAsync()
{
// Arrange
var definition = new DeclarativeAgentDefinition("test-model") { Instructions = "Test" };
var definition = new PromptAgentDefinition("test-model") { Instructions = "Test" };
// Create a declarative function (not invocable) using AIFunctionFactory.CreateDeclaration
using var doc = JsonDocument.Parse("{}");
@@ -689,15 +689,15 @@ public sealed class AzureAIProjectChatClientExtensionsTests
definition.Tools.Add(declarativeFunction.AsOpenAIResponseTool() ?? throw new InvalidOperationException());
// Generate response with the declarative function
var definitionResponse = new DeclarativeAgentDefinition("test-model") { Instructions = "Test" };
var definitionResponse = new PromptAgentDefinition("test-model") { Instructions = "Test" };
definitionResponse.Tools.Add(declarativeFunction.AsOpenAIResponseTool() ?? throw new InvalidOperationException());
using var testClient = CreateTestAgentClientWithHandler(agentName: "test-agent", agentDefinitionResponse: definitionResponse);
var options = new ProjectsAgentVersionCreationOptions(definition);
var options = new AgentVersionCreationOptions(definition);
// Act
var agentVersion = (await testClient.Client.AgentAdministrationClient.CreateAgentVersionAsync("test-agent", options)).Value;
var agentVersion = (await testClient.Client.Agents.CreateAgentVersionAsync("test-agent", options)).Value;
var agent = testClient.Client.AsAIAgent(agentVersion);
// Assert
@@ -758,7 +758,7 @@ public sealed class AzureAIProjectChatClientExtensionsTests
{
// Arrange
AIProjectClient client = this.CreateTestAgentClient();
ProjectsAgentRecord agentRecord = this.CreateTestAgentRecord();
AgentRecord agentRecord = this.CreateTestAgentRecord();
int factoryCallCount = 0;
// Act
@@ -785,7 +785,7 @@ public sealed class AzureAIProjectChatClientExtensionsTests
{
// Arrange
AIProjectClient client = this.CreateTestAgentClient();
ProjectsAgentRecord agentRecord = this.CreateTestAgentRecord();
AgentRecord agentRecord = this.CreateTestAgentRecord();
// Act
var agent1 = client.AsAIAgent(
@@ -904,7 +904,7 @@ public sealed class AzureAIProjectChatClientExtensionsTests
// Arrange
var aiProjectClient = new AIProjectClient(new Uri("https://test.openai.azure.com/"), new FakeAuthenticationTokenProvider(), new() { Transport = new HttpClientPipelineTransport(httpClient) });
var agentVersion = (await aiProjectClient.AgentAdministrationClient.CreateAgentVersionAsync("test-agent", new ProjectsAgentVersionCreationOptions(new DeclarativeAgentDefinition("test-model") { Instructions = "Test instructions" }))).Value;
var agentVersion = (await aiProjectClient.Agents.CreateAgentVersionAsync("test-agent", new AgentVersionCreationOptions(new PromptAgentDefinition("test-model") { Instructions = "Test instructions" }))).Value;
// Act
var agent = aiProjectClient.AsAIAgent(agentVersion);
@@ -1043,21 +1043,21 @@ public sealed class AzureAIProjectChatClientExtensionsTests
#endregion
#region GetService<ProjectsAgentRecord> Tests
#region GetService<AgentRecord> Tests
/// <summary>
/// Verify that GetService returns ProjectsAgentRecord for agents created from ProjectsAgentRecord.
/// Verify that GetService returns AgentRecord for agents created from AgentRecord.
/// </summary>
[Fact]
public void GetService_WithAgentRecord_ReturnsAgentRecord()
{
// Arrange
AIProjectClient client = this.CreateTestAgentClient();
ProjectsAgentRecord agentRecord = this.CreateTestAgentRecord();
AgentRecord agentRecord = this.CreateTestAgentRecord();
// Act
var agent = client.AsAIAgent(agentRecord);
var retrievedRecord = agent.GetService<ProjectsAgentRecord>();
var retrievedRecord = agent.GetService<AgentRecord>();
// Assert
Assert.NotNull(retrievedRecord);
@@ -1065,7 +1065,7 @@ public sealed class AzureAIProjectChatClientExtensionsTests
}
/// <summary>
/// Verify that GetService returns null for ProjectsAgentRecord when agent is created from AgentReference.
/// Verify that GetService returns null for AgentRecord when agent is created from AgentReference.
/// </summary>
[Fact]
public void GetService_WithAgentReference_ReturnsNullForAgentRecord()
@@ -1076,7 +1076,7 @@ public sealed class AzureAIProjectChatClientExtensionsTests
// Act
var agent = client.AsAIAgent(agentReference);
var retrievedRecord = agent.GetService<ProjectsAgentRecord>();
var retrievedRecord = agent.GetService<AgentRecord>();
// Assert
Assert.Null(retrievedRecord);
@@ -1084,21 +1084,21 @@ public sealed class AzureAIProjectChatClientExtensionsTests
#endregion
#region GetService<ProjectsAgentVersion> Tests
#region GetService<AgentVersion> Tests
/// <summary>
/// Verify that GetService returns ProjectsAgentVersion for agents created from ProjectsAgentVersion.
/// Verify that GetService returns AgentVersion for agents created from AgentVersion.
/// </summary>
[Fact]
public void GetService_WithAgentVersion_ReturnsAgentVersion()
{
// Arrange
AIProjectClient client = this.CreateTestAgentClient();
ProjectsAgentVersion agentVersion = this.CreateTestAgentVersion();
AgentVersion agentVersion = this.CreateTestAgentVersion();
// Act
var agent = client.AsAIAgent(agentVersion);
var retrievedVersion = agent.GetService<ProjectsAgentVersion>();
var retrievedVersion = agent.GetService<AgentVersion>();
// Assert
Assert.NotNull(retrievedVersion);
@@ -1106,7 +1106,7 @@ public sealed class AzureAIProjectChatClientExtensionsTests
}
/// <summary>
/// Verify that GetService returns null for ProjectsAgentVersion when agent is created from AgentReference.
/// Verify that GetService returns null for AgentVersion when agent is created from AgentReference.
/// </summary>
[Fact]
public void GetService_WithAgentReference_ReturnsNullForAgentVersion()
@@ -1117,7 +1117,7 @@ public sealed class AzureAIProjectChatClientExtensionsTests
// Act
var agent = client.AsAIAgent(agentReference);
var retrievedVersion = agent.GetService<ProjectsAgentVersion>();
var retrievedVersion = agent.GetService<AgentVersion>();
// Assert
Assert.Null(retrievedVersion);
@@ -1128,14 +1128,14 @@ public sealed class AzureAIProjectChatClientExtensionsTests
#region ChatClientMetadata Tests
/// <summary>
/// Verify that ChatClientMetadata is properly populated for agents created from ProjectsAgentRecord.
/// Verify that ChatClientMetadata is properly populated for agents created from AgentRecord.
/// </summary>
[Fact]
public void ChatClientMetadata_WithAgentRecord_IsPopulatedCorrectly()
{
// Arrange
AIProjectClient client = this.CreateTestAgentClient();
ProjectsAgentRecord agentRecord = this.CreateTestAgentRecord();
AgentRecord agentRecord = this.CreateTestAgentRecord();
// Act
var agent = client.AsAIAgent(agentRecord);
@@ -1147,18 +1147,18 @@ public sealed class AzureAIProjectChatClientExtensionsTests
}
/// <summary>
/// Verify that ChatClientMetadata.DefaultModelId is set from DeclarativeAgentDefinition model property.
/// Verify that ChatClientMetadata.DefaultModelId is set from PromptAgentDefinition model property.
/// </summary>
[Fact]
public void ChatClientMetadata_WithDeclarativeAgentDefinition_SetsDefaultModelIdFromModel()
public void ChatClientMetadata_WithPromptAgentDefinition_SetsDefaultModelIdFromModel()
{
// Arrange
AIProjectClient client = this.CreateTestAgentClient();
var definition = new DeclarativeAgentDefinition("gpt-4-turbo")
var definition = new PromptAgentDefinition("gpt-4-turbo")
{
Instructions = "Test instructions"
};
ProjectsAgentRecord agentRecord = this.CreateTestAgentRecord(definition);
AgentRecord agentRecord = this.CreateTestAgentRecord(definition);
// Act
var agent = client.AsAIAgent(agentRecord);
@@ -1172,14 +1172,14 @@ public sealed class AzureAIProjectChatClientExtensionsTests
}
/// <summary>
/// Verify that ChatClientMetadata is properly populated for agents created from ProjectsAgentVersion.
/// Verify that ChatClientMetadata is properly populated for agents created from AgentVersion.
/// </summary>
[Fact]
public void ChatClientMetadata_WithAgentVersion_IsPopulatedCorrectly()
{
// Arrange
AIProjectClient client = this.CreateTestAgentClient();
ProjectsAgentVersion agentVersion = this.CreateTestAgentVersion();
AgentVersion agentVersion = this.CreateTestAgentVersion();
// Act
var agent = client.AsAIAgent(agentVersion);
@@ -1188,7 +1188,7 @@ public sealed class AzureAIProjectChatClientExtensionsTests
// Assert
Assert.NotNull(metadata);
Assert.NotNull(metadata.DefaultModelId);
Assert.Equal((agentVersion.Definition as DeclarativeAgentDefinition)!.Model, metadata.DefaultModelId);
Assert.Equal((agentVersion.Definition as PromptAgentDefinition)!.Model, metadata.DefaultModelId);
}
#endregion
@@ -1216,14 +1216,14 @@ public sealed class AzureAIProjectChatClientExtensionsTests
}
/// <summary>
/// Verify that GetService returns null for AgentReference when agent is created from ProjectsAgentRecord.
/// Verify that GetService returns null for AgentReference when agent is created from AgentRecord.
/// </summary>
[Fact]
public void GetService_WithAgentRecord_ReturnsAlsoAgentReference()
{
// Arrange
AIProjectClient client = this.CreateTestAgentClient();
ProjectsAgentRecord agentRecord = this.CreateTestAgentRecord();
AgentRecord agentRecord = this.CreateTestAgentRecord();
// Act
var agent = client.AsAIAgent(agentRecord);
@@ -1235,14 +1235,14 @@ public sealed class AzureAIProjectChatClientExtensionsTests
}
/// <summary>
/// Verify that GetService returns null for AgentReference when agent is created from ProjectsAgentVersion.
/// Verify that GetService returns null for AgentReference when agent is created from AgentVersion.
/// </summary>
[Fact]
public void GetService_WithAgentVersion_ReturnsAlsoAgentReference()
{
// Arrange
AIProjectClient client = this.CreateTestAgentClient();
ProjectsAgentVersion agentVersion = this.CreateTestAgentVersion();
AgentVersion agentVersion = this.CreateTestAgentVersion();
// Act
var agent = client.AsAIAgent(agentVersion);
@@ -1278,14 +1278,14 @@ public sealed class AzureAIProjectChatClientExtensionsTests
#region Empty Version and ID Handling Tests
/// <summary>
/// Verify that AsAIAgent with ProjectsAgentRecord handles empty version by using "latest" as fallback.
/// Verify that AsAIAgent with AgentRecord handles empty version by using "latest" as fallback.
/// </summary>
[Fact]
public void AsAIAgent_WithAgentRecordEmptyVersion_CreatesAgentWithGeneratedId()
{
// Arrange
AIProjectClient client = this.CreateTestAgentClientWithEmptyVersion();
ProjectsAgentRecord agentRecord = this.CreateTestAgentRecordWithEmptyVersion();
AgentRecord agentRecord = this.CreateTestAgentRecordWithEmptyVersion();
// Act
var agent = client.AsAIAgent(agentRecord);
@@ -1297,14 +1297,14 @@ public sealed class AzureAIProjectChatClientExtensionsTests
}
/// <summary>
/// Verify that AsAIAgent with ProjectsAgentVersion handles empty version by using "latest" as fallback.
/// Verify that AsAIAgent with AgentVersion handles empty version by using "latest" as fallback.
/// </summary>
[Fact]
public void AsAIAgent_WithAgentVersionEmptyVersion_CreatesAgentWithGeneratedId()
{
// Arrange
AIProjectClient client = this.CreateTestAgentClientWithEmptyVersion();
ProjectsAgentVersion agentVersion = this.CreateTestAgentVersionWithEmptyVersion();
AgentVersion agentVersion = this.CreateTestAgentVersionWithEmptyVersion();
// Act
var agent = client.AsAIAgent(agentVersion);
@@ -1316,14 +1316,14 @@ public sealed class AzureAIProjectChatClientExtensionsTests
}
/// <summary>
/// Verify that AsAIAgent with ProjectsAgentRecord handles whitespace-only version by using "latest" as fallback.
/// Verify that AsAIAgent with AgentRecord handles whitespace-only version by using "latest" as fallback.
/// </summary>
[Fact]
public void AsAIAgent_WithAgentRecordWhitespaceVersion_CreatesAgentWithGeneratedId()
{
// Arrange
AIProjectClient client = this.CreateTestAgentClientWithWhitespaceVersion();
ProjectsAgentRecord agentRecord = this.CreateTestAgentRecordWithWhitespaceVersion();
AgentRecord agentRecord = this.CreateTestAgentRecordWithWhitespaceVersion();
// Act
var agent = client.AsAIAgent(agentRecord);
@@ -1335,14 +1335,14 @@ public sealed class AzureAIProjectChatClientExtensionsTests
}
/// <summary>
/// Verify that AsAIAgent with ProjectsAgentVersion handles whitespace-only version by using "latest" as fallback.
/// Verify that AsAIAgent with AgentVersion handles whitespace-only version by using "latest" as fallback.
/// </summary>
[Fact]
public void AsAIAgent_WithAgentVersionWhitespaceVersion_CreatesAgentWithGeneratedId()
{
// Arrange
AIProjectClient client = this.CreateTestAgentClientWithWhitespaceVersion();
ProjectsAgentVersion agentVersion = this.CreateTestAgentVersionWithWhitespaceVersion();
AgentVersion agentVersion = this.CreateTestAgentVersionWithWhitespaceVersion();
// Act
var agent = client.AsAIAgent(agentVersion);
@@ -1364,11 +1364,11 @@ public sealed class AzureAIProjectChatClientExtensionsTests
public void AsAIAgent_WithServerHostedTools_AddsToolsToAgentOptions()
{
// Arrange
DeclarativeAgentDefinition definition = new("test-model") { Instructions = "Test" };
PromptAgentDefinition definition = new("test-model") { Instructions = "Test" };
definition.Tools.Add(new HostedWebSearchTool().GetService<ResponseTool>() ?? new HostedWebSearchTool().AsOpenAIResponseTool());
AIProjectClient client = this.CreateTestAgentClient();
ProjectsAgentVersion agentVersion = ModelReaderWriter.Read<ProjectsAgentVersion>(BinaryData.FromString(TestDataUtil.GetAgentVersionResponseJson(agentDefinition: definition)))!;
AgentVersion agentVersion = ModelReaderWriter.Read<AgentVersion>(BinaryData.FromString(TestDataUtil.GetAgentVersionResponseJson(agentDefinition: definition)))!;
// Act - no tools provided, but requireInvocableTools is false when no tools param is passed
FoundryAgent agent = client.AsAIAgent(agentVersion);
@@ -1385,7 +1385,7 @@ public sealed class AzureAIProjectChatClientExtensionsTests
/// <summary>
/// Creates a test AIProjectClient with fake behavior.
/// </summary>
private FakeAgentClient CreateTestAgentClient(string? agentName = null, string? instructions = null, string? description = null, ProjectsAgentDefinition? agentDefinitionResponse = null)
private FakeAgentClient CreateTestAgentClient(string? agentName = null, string? instructions = null, string? description = null, AgentDefinition? agentDefinitionResponse = null)
{
return new FakeAgentClient(agentName, instructions, description, agentDefinitionResponse);
}
@@ -1395,7 +1395,7 @@ public sealed class AzureAIProjectChatClientExtensionsTests
/// Used for tests that exercise the protocol-method code path (CreateAgentVersion).
/// The returned client must be disposed to clean up the underlying HttpClient/handler.
/// </summary>
private static DisposableTestClient CreateTestAgentClientWithHandler(string? agentName = null, string? instructions = null, string? description = null, ProjectsAgentDefinition? agentDefinitionResponse = null)
private static DisposableTestClient CreateTestAgentClientWithHandler(string? agentName = null, string? instructions = null, string? description = null, AgentDefinition? agentDefinitionResponse = null)
{
var responseJson = TestDataUtil.GetAgentVersionResponseJson(agentName, agentDefinitionResponse, instructions, description);
@@ -1439,59 +1439,59 @@ public sealed class AzureAIProjectChatClientExtensionsTests
}
/// <summary>
/// Creates a test ProjectsAgentRecord for testing.
/// Creates a test AgentRecord for testing.
/// </summary>
private ProjectsAgentRecord CreateTestAgentRecord(ProjectsAgentDefinition? agentDefinition = null)
private AgentRecord CreateTestAgentRecord(AgentDefinition? agentDefinition = null)
{
return ModelReaderWriter.Read<ProjectsAgentRecord>(BinaryData.FromString(TestDataUtil.GetAgentResponseJson(agentDefinition: agentDefinition)))!;
return ModelReaderWriter.Read<AgentRecord>(BinaryData.FromString(TestDataUtil.GetAgentResponseJson(agentDefinition: agentDefinition)))!;
}
/// <summary>
/// Creates a test AIProjectClient with empty version fields for testing hosted MCP agents.
/// </summary>
private FakeAgentClient CreateTestAgentClientWithEmptyVersion(string? agentName = null, string? instructions = null, string? description = null, ProjectsAgentDefinition? agentDefinitionResponse = null)
private FakeAgentClient CreateTestAgentClientWithEmptyVersion(string? agentName = null, string? instructions = null, string? description = null, AgentDefinition? agentDefinitionResponse = null)
{
return new FakeAgentClient(agentName, instructions, description, agentDefinitionResponse, useEmptyVersion: true);
}
/// <summary>
/// Creates a test ProjectsAgentRecord with empty version for testing hosted MCP agents.
/// Creates a test AgentRecord with empty version for testing hosted MCP agents.
/// </summary>
private ProjectsAgentRecord CreateTestAgentRecordWithEmptyVersion(ProjectsAgentDefinition? agentDefinition = null)
private AgentRecord CreateTestAgentRecordWithEmptyVersion(AgentDefinition? agentDefinition = null)
{
return ModelReaderWriter.Read<ProjectsAgentRecord>(BinaryData.FromString(TestDataUtil.GetAgentResponseJsonWithEmptyVersion(agentDefinition: agentDefinition)))!;
return ModelReaderWriter.Read<AgentRecord>(BinaryData.FromString(TestDataUtil.GetAgentResponseJsonWithEmptyVersion(agentDefinition: agentDefinition)))!;
}
/// <summary>
/// Creates a test ProjectsAgentVersion with empty version for testing hosted MCP agents.
/// Creates a test AgentVersion with empty version for testing hosted MCP agents.
/// </summary>
private ProjectsAgentVersion CreateTestAgentVersionWithEmptyVersion()
private AgentVersion CreateTestAgentVersionWithEmptyVersion()
{
return ModelReaderWriter.Read<ProjectsAgentVersion>(BinaryData.FromString(TestDataUtil.GetAgentVersionResponseJsonWithEmptyVersion()))!;
return ModelReaderWriter.Read<AgentVersion>(BinaryData.FromString(TestDataUtil.GetAgentVersionResponseJsonWithEmptyVersion()))!;
}
/// <summary>
/// Creates a test AIProjectClient with whitespace-only version fields for testing hosted MCP agents.
/// </summary>
private FakeAgentClient CreateTestAgentClientWithWhitespaceVersion(string? agentName = null, string? instructions = null, string? description = null, ProjectsAgentDefinition? agentDefinitionResponse = null)
private FakeAgentClient CreateTestAgentClientWithWhitespaceVersion(string? agentName = null, string? instructions = null, string? description = null, AgentDefinition? agentDefinitionResponse = null)
{
return new FakeAgentClient(agentName, instructions, description, agentDefinitionResponse, versionMode: VersionMode.Whitespace);
}
/// <summary>
/// Creates a test ProjectsAgentRecord with whitespace-only version for testing hosted MCP agents.
/// Creates a test AgentRecord with whitespace-only version for testing hosted MCP agents.
/// </summary>
private ProjectsAgentRecord CreateTestAgentRecordWithWhitespaceVersion(ProjectsAgentDefinition? agentDefinition = null)
private AgentRecord CreateTestAgentRecordWithWhitespaceVersion(AgentDefinition? agentDefinition = null)
{
return ModelReaderWriter.Read<ProjectsAgentRecord>(BinaryData.FromString(TestDataUtil.GetAgentResponseJsonWithWhitespaceVersion(agentDefinition: agentDefinition)))!;
return ModelReaderWriter.Read<AgentRecord>(BinaryData.FromString(TestDataUtil.GetAgentResponseJsonWithWhitespaceVersion(agentDefinition: agentDefinition)))!;
}
/// <summary>
/// Creates a test ProjectsAgentVersion with whitespace-only version for testing hosted MCP agents.
/// Creates a test AgentVersion with whitespace-only version for testing hosted MCP agents.
/// </summary>
private ProjectsAgentVersion CreateTestAgentVersionWithWhitespaceVersion()
private AgentVersion CreateTestAgentVersionWithWhitespaceVersion()
{
return ModelReaderWriter.Read<ProjectsAgentVersion>(BinaryData.FromString(TestDataUtil.GetAgentVersionResponseJsonWithWhitespaceVersion()))!;
return ModelReaderWriter.Read<AgentVersion>(BinaryData.FromString(TestDataUtil.GetAgentVersionResponseJsonWithWhitespaceVersion()))!;
}
private const string OpenAPISpec = """
@@ -1525,11 +1525,11 @@ public sealed class AzureAIProjectChatClientExtensionsTests
""";
/// <summary>
/// Creates a test ProjectsAgentVersion for testing.
/// Creates a test AgentVersion for testing.
/// </summary>
private ProjectsAgentVersion CreateTestAgentVersion()
private AgentVersion CreateTestAgentVersion()
{
return ModelReaderWriter.Read<ProjectsAgentVersion>(BinaryData.FromString(TestDataUtil.GetAgentVersionResponseJson()))!;
return ModelReaderWriter.Read<AgentVersion>(BinaryData.FromString(TestDataUtil.GetAgentVersionResponseJson()))!;
}
/// <summary>
@@ -1547,11 +1547,11 @@ public sealed class AzureAIProjectChatClientExtensionsTests
/// </summary>
private sealed class FakeAgentClient : AIProjectClient
{
public FakeAgentClient(string? agentName = null, string? instructions = null, string? description = null, ProjectsAgentDefinition? agentDefinitionResponse = null, bool useEmptyVersion = false, VersionMode versionMode = VersionMode.Normal)
public FakeAgentClient(string? agentName = null, string? instructions = null, string? description = null, AgentDefinition? agentDefinitionResponse = null, bool useEmptyVersion = false, VersionMode versionMode = VersionMode.Normal)
{
// Handle backward compatibility with bool parameter
var effectiveVersionMode = useEmptyVersion ? VersionMode.Empty : versionMode;
this.AgentAdministrationClient = new FakeAgentsClient(agentName, instructions, description, agentDefinitionResponse, effectiveVersionMode);
this.Agents = new FakeAgentsClient(agentName, instructions, description, agentDefinitionResponse, effectiveVersionMode);
}
public override ClientConnection GetConnection(string connectionId)
@@ -1559,17 +1559,17 @@ public sealed class AzureAIProjectChatClientExtensionsTests
return new ClientConnection("fake-connection-id", "http://localhost", ClientPipeline.Create(), CredentialKind.None);
}
public override AgentAdministrationClient AgentAdministrationClient { get; }
public override AgentsClient Agents { get; }
private sealed class FakeAgentsClient : AgentAdministrationClient
private sealed class FakeAgentsClient : AgentsClient
{
private readonly string? _agentName;
private readonly string? _instructions;
private readonly string? _description;
private readonly ProjectsAgentDefinition? _agentDefinition;
private readonly AgentDefinition? _agentDefinition;
private readonly VersionMode _versionMode;
public FakeAgentsClient(string? agentName = null, string? instructions = null, string? description = null, ProjectsAgentDefinition? agentDefinitionResponse = null, VersionMode versionMode = VersionMode.Normal)
public FakeAgentsClient(string? agentName = null, string? instructions = null, string? description = null, AgentDefinition? agentDefinitionResponse = null, VersionMode versionMode = VersionMode.Normal)
{
this._agentName = agentName;
this._instructions = instructions;
@@ -1601,44 +1601,44 @@ public sealed class AzureAIProjectChatClientExtensionsTests
public override ClientResult GetAgent(string agentName, RequestOptions options)
{
var responseJson = this.GetAgentResponseJson();
return ClientResult.FromValue(ModelReaderWriter.Read<ProjectsAgentRecord>(BinaryData.FromString(responseJson))!, new MockPipelineResponse(200, BinaryData.FromString(responseJson)));
return ClientResult.FromValue(ModelReaderWriter.Read<AgentRecord>(BinaryData.FromString(responseJson))!, new MockPipelineResponse(200, BinaryData.FromString(responseJson)));
}
public override ClientResult<ProjectsAgentRecord> GetAgent(string agentName, CancellationToken cancellationToken = default)
public override ClientResult<AgentRecord> GetAgent(string agentName, CancellationToken cancellationToken = default)
{
var responseJson = this.GetAgentResponseJson();
return ClientResult.FromValue(ModelReaderWriter.Read<ProjectsAgentRecord>(BinaryData.FromString(responseJson))!, new MockPipelineResponse(200));
return ClientResult.FromValue(ModelReaderWriter.Read<AgentRecord>(BinaryData.FromString(responseJson))!, new MockPipelineResponse(200));
}
public override Task<ClientResult> GetAgentAsync(string agentName, RequestOptions options)
{
var responseJson = this.GetAgentResponseJson();
return Task.FromResult<ClientResult>(ClientResult.FromValue(ModelReaderWriter.Read<ProjectsAgentRecord>(BinaryData.FromString(responseJson))!, new MockPipelineResponse(200, BinaryData.FromString(responseJson))));
return Task.FromResult<ClientResult>(ClientResult.FromValue(ModelReaderWriter.Read<AgentRecord>(BinaryData.FromString(responseJson))!, new MockPipelineResponse(200, BinaryData.FromString(responseJson))));
}
public override Task<ClientResult<ProjectsAgentRecord>> GetAgentAsync(string agentName, CancellationToken cancellationToken = default)
public override Task<ClientResult<AgentRecord>> GetAgentAsync(string agentName, CancellationToken cancellationToken = default)
{
var responseJson = this.GetAgentResponseJson();
return Task.FromResult(ClientResult.FromValue(ModelReaderWriter.Read<ProjectsAgentRecord>(BinaryData.FromString(responseJson))!, new MockPipelineResponse(200)));
return Task.FromResult(ClientResult.FromValue(ModelReaderWriter.Read<AgentRecord>(BinaryData.FromString(responseJson))!, new MockPipelineResponse(200)));
}
public override ClientResult<ProjectsAgentVersion> CreateAgentVersion(string agentName, ProjectsAgentVersionCreationOptions? options = null, string? foundryFeatures = null, CancellationToken cancellationToken = default)
public override ClientResult<AgentVersion> CreateAgentVersion(string agentName, AgentVersionCreationOptions? options = null, string? foundryFeatures = null, CancellationToken cancellationToken = default)
{
var responseJson = this.GetAgentVersionResponseJson();
return ClientResult.FromValue(ModelReaderWriter.Read<ProjectsAgentVersion>(BinaryData.FromString(responseJson))!, new MockPipelineResponse(200));
return ClientResult.FromValue(ModelReaderWriter.Read<AgentVersion>(BinaryData.FromString(responseJson))!, new MockPipelineResponse(200));
}
public override Task<ClientResult<ProjectsAgentVersion>> CreateAgentVersionAsync(string agentName, ProjectsAgentVersionCreationOptions? options = null, string? foundryFeatures = null, CancellationToken cancellationToken = default)
public override Task<ClientResult<AgentVersion>> CreateAgentVersionAsync(string agentName, AgentVersionCreationOptions? options = null, string? foundryFeatures = null, CancellationToken cancellationToken = default)
{
var responseJson = this.GetAgentVersionResponseJson();
return Task.FromResult(ClientResult.FromValue(ModelReaderWriter.Read<ProjectsAgentVersion>(BinaryData.FromString(responseJson))!, new MockPipelineResponse(200)));
return Task.FromResult(ClientResult.FromValue(ModelReaderWriter.Read<AgentVersion>(BinaryData.FromString(responseJson))!, new MockPipelineResponse(200)));
}
}
}
private static DeclarativeAgentDefinition GeneratePromptDefinitionResponse(DeclarativeAgentDefinition inputDefinition, List<AITool>? tools)
private static PromptAgentDefinition GeneratePromptDefinitionResponse(PromptAgentDefinition inputDefinition, List<AITool>? tools)
{
var definitionResponse = new DeclarativeAgentDefinition(inputDefinition.Model) { Instructions = inputDefinition.Instructions };
var definitionResponse = new PromptAgentDefinition(inputDefinition.Model) { Instructions = inputDefinition.Instructions };
if (tools is not null)
{
foreach (var tool in tools)
@@ -29,7 +29,7 @@ internal static class TestDataUtil
/// <summary>
/// Gets the agent response JSON with optional placeholder replacements applied.
/// </summary>
public static string GetAgentResponseJson(string? agentName = null, ProjectsAgentDefinition? agentDefinition = null, string? instructions = null, string? description = null)
public static string GetAgentResponseJson(string? agentName = null, AgentDefinition? agentDefinition = null, string? instructions = null, string? description = null)
{
var json = s_agentResponseJson;
json = ApplyAgentName(json, agentName);
@@ -42,7 +42,7 @@ internal static class TestDataUtil
/// <summary>
/// Gets the agent version response JSON with optional placeholder replacements applied.
/// </summary>
public static string GetAgentVersionResponseJson(string? agentName = null, ProjectsAgentDefinition? agentDefinition = null, string? instructions = null, string? description = null)
public static string GetAgentVersionResponseJson(string? agentName = null, AgentDefinition? agentDefinition = null, string? instructions = null, string? description = null)
{
var json = s_agentVersionResponseJson;
json = ApplyAgentName(json, agentName);
@@ -55,7 +55,7 @@ internal static class TestDataUtil
/// <summary>
/// Gets the agent version response JSON with empty version and ID fields for testing hosted agents like MCP agents.
/// </summary>
public static string GetAgentVersionResponseJsonWithEmptyVersion(string? agentName = null, ProjectsAgentDefinition? agentDefinition = null, string? instructions = null, string? description = null)
public static string GetAgentVersionResponseJsonWithEmptyVersion(string? agentName = null, AgentDefinition? agentDefinition = null, string? instructions = null, string? description = null)
{
var json = s_agentVersionResponseJson;
json = ApplyAgentName(json, agentName);
@@ -71,7 +71,7 @@ internal static class TestDataUtil
/// <summary>
/// Gets the agent response JSON with empty version and ID fields in the latest version for testing hosted agents like MCP agents.
/// </summary>
public static string GetAgentResponseJsonWithEmptyVersion(string? agentName = null, ProjectsAgentDefinition? agentDefinition = null, string? instructions = null, string? description = null)
public static string GetAgentResponseJsonWithEmptyVersion(string? agentName = null, AgentDefinition? agentDefinition = null, string? instructions = null, string? description = null)
{
var json = s_agentResponseJson;
json = ApplyAgentName(json, agentName);
@@ -87,7 +87,7 @@ internal static class TestDataUtil
/// <summary>
/// Gets the agent version response JSON with whitespace-only version and ID fields for testing hosted agents like MCP agents.
/// </summary>
public static string GetAgentVersionResponseJsonWithWhitespaceVersion(string? agentName = null, ProjectsAgentDefinition? agentDefinition = null, string? instructions = null, string? description = null)
public static string GetAgentVersionResponseJsonWithWhitespaceVersion(string? agentName = null, AgentDefinition? agentDefinition = null, string? instructions = null, string? description = null)
{
var json = s_agentVersionResponseJson;
json = ApplyAgentName(json, agentName);
@@ -103,7 +103,7 @@ internal static class TestDataUtil
/// <summary>
/// Gets the agent response JSON with whitespace-only version and ID fields in the latest version for testing hosted agents like MCP agents.
/// </summary>
public static string GetAgentResponseJsonWithWhitespaceVersion(string? agentName = null, ProjectsAgentDefinition? agentDefinition = null, string? instructions = null, string? description = null)
public static string GetAgentResponseJsonWithWhitespaceVersion(string? agentName = null, AgentDefinition? agentDefinition = null, string? instructions = null, string? description = null)
{
var json = s_agentResponseJson;
json = ApplyAgentName(json, agentName);
@@ -119,7 +119,7 @@ internal static class TestDataUtil
/// <summary>
/// Gets the OpenAI default response JSON with optional placeholder replacements applied.
/// </summary>
public static string GetOpenAIDefaultResponseJson(string? agentName = null, ProjectsAgentDefinition? agentDefinition = null, string? instructions = null, string? description = null)
public static string GetOpenAIDefaultResponseJson(string? agentName = null, AgentDefinition? agentDefinition = null, string? instructions = null, string? description = null)
{
var json = s_openAIDefaultResponseJson;
json = ApplyAgentName(json, agentName);
@@ -138,7 +138,7 @@ internal static class TestDataUtil
return json;
}
private static string ApplyAgentDefinition(string json, ProjectsAgentDefinition? definition)
private static string ApplyAgentDefinition(string json, AgentDefinition? definition)
{
return (definition is not null)
? json.Replace(AgentDefinitionPlaceholder, ModelReaderWriter.Write(definition).ToString())
@@ -35,13 +35,13 @@ internal abstract class AgentProvider(IConfiguration configuration)
{
Uri foundryEndpoint = new(this.GetSetting(TestSettings.AzureAIProjectEndpoint));
await foreach (ProjectsAgentVersion agent in this.CreateAgentsAsync(foundryEndpoint))
await foreach (AgentVersion agent in this.CreateAgentsAsync(foundryEndpoint))
{
Console.WriteLine($"Created agent: {agent.Name}:{agent.Version}");
}
}
protected abstract IAsyncEnumerable<ProjectsAgentVersion> CreateAgentsAsync(Uri foundryEndpoint);
protected abstract IAsyncEnumerable<AgentVersion> CreateAgentsAsync(Uri foundryEndpoint);
protected string GetSetting(string settingName) =>
configuration[settingName] ??
@@ -14,7 +14,7 @@ namespace Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests.Agents;
internal sealed class FunctionToolAgentProvider(IConfiguration configuration) : AgentProvider(configuration)
{
protected override async IAsyncEnumerable<ProjectsAgentVersion> CreateAgentsAsync(Uri foundryEndpoint)
protected override async IAsyncEnumerable<AgentVersion> CreateAgentsAsync(Uri foundryEndpoint)
{
MenuPlugin menuPlugin = new();
AIFunction[] functions =
@@ -33,9 +33,9 @@ internal sealed class FunctionToolAgentProvider(IConfiguration configuration) :
agentDescription: "Provides information about the restaurant menu");
}
private DeclarativeAgentDefinition DefineMenuAgent(AIFunction[] functions)
private PromptAgentDefinition DefineMenuAgent(AIFunction[] functions)
{
DeclarativeAgentDefinition agentDefinition =
PromptAgentDefinition agentDefinition =
new(this.GetSetting(TestSettings.AzureAIModelDeploymentName))
{
Instructions =
@@ -12,7 +12,7 @@ namespace Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests.Agents;
internal sealed class MarketingAgentProvider(IConfiguration configuration) : AgentProvider(configuration)
{
protected override async IAsyncEnumerable<ProjectsAgentVersion> CreateAgentsAsync(Uri foundryEndpoint)
protected override async IAsyncEnumerable<AgentVersion> CreateAgentsAsync(Uri foundryEndpoint)
{
AIProjectClient aiProjectClient = new(foundryEndpoint, TestAzureCliCredentials.CreateAzureCliCredential());
@@ -35,7 +35,7 @@ internal sealed class MarketingAgentProvider(IConfiguration configuration) : Age
agentDescription: "Editor agent for Marketing workflow");
}
private DeclarativeAgentDefinition DefineAnalystAgent() =>
private PromptAgentDefinition DefineAnalystAgent() =>
new(this.GetSetting(TestSettings.AzureAIModelDeploymentName))
{
Instructions =
@@ -47,13 +47,13 @@ internal sealed class MarketingAgentProvider(IConfiguration configuration) : Age
""",
Tools =
{
//ProjectsAgentTool.CreateBingGroundingTool( // TODO: Use Bing Grounding when available
//AgentTool.CreateBingGroundingTool( // TODO: Use Bing Grounding when available
// new BingGroundingSearchToolParameters(
// [new BingGroundingSearchConfiguration(this.GetSetting(Settings.FoundryGroundingTool))]))
}
};
private DeclarativeAgentDefinition DefineWriterAgent() =>
private PromptAgentDefinition DefineWriterAgent() =>
new(this.GetSetting(TestSettings.AzureAIModelDeploymentName))
{
Instructions =
@@ -64,7 +64,7 @@ internal sealed class MarketingAgentProvider(IConfiguration configuration) : Age
"""
};
private DeclarativeAgentDefinition DefineEditorAgent() =>
private PromptAgentDefinition DefineEditorAgent() =>
new(this.GetSetting(TestSettings.AzureAIModelDeploymentName))
{
Instructions =
@@ -12,7 +12,7 @@ namespace Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests.Agents;
internal sealed class MathChatAgentProvider(IConfiguration configuration) : AgentProvider(configuration)
{
protected override async IAsyncEnumerable<ProjectsAgentVersion> CreateAgentsAsync(Uri foundryEndpoint)
protected override async IAsyncEnumerable<AgentVersion> CreateAgentsAsync(Uri foundryEndpoint)
{
AIProjectClient aiProjectClient = new(foundryEndpoint, TestAzureCliCredentials.CreateAzureCliCredential());
@@ -29,7 +29,7 @@ internal sealed class MathChatAgentProvider(IConfiguration configuration) : Agen
agentDescription: "Teacher agent for MathChat workflow");
}
private DeclarativeAgentDefinition DefineStudentAgent() =>
private PromptAgentDefinition DefineStudentAgent() =>
new(this.GetSetting(TestSettings.AzureAIModelDeploymentName))
{
Instructions =
@@ -41,7 +41,7 @@ internal sealed class MathChatAgentProvider(IConfiguration configuration) : Agen
"""
};
private DeclarativeAgentDefinition DefineTeacherAgent() =>
private PromptAgentDefinition DefineTeacherAgent() =>
new(this.GetSetting(TestSettings.AzureAIModelDeploymentName))
{
Instructions =
@@ -12,7 +12,7 @@ namespace Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests.Agents;
internal sealed class PoemAgentProvider(IConfiguration configuration) : AgentProvider(configuration)
{
protected override async IAsyncEnumerable<ProjectsAgentVersion> CreateAgentsAsync(Uri foundryEndpoint)
protected override async IAsyncEnumerable<AgentVersion> CreateAgentsAsync(Uri foundryEndpoint)
{
AIProjectClient aiProjectClient = new(foundryEndpoint, TestAzureCliCredentials.CreateAzureCliCredential());
@@ -23,7 +23,7 @@ internal sealed class PoemAgentProvider(IConfiguration configuration) : AgentPro
agentDescription: "Authors original poems");
}
private DeclarativeAgentDefinition DefinePoemAgent() =>
private PromptAgentDefinition DefinePoemAgent() =>
new(this.GetSetting(TestSettings.AzureAIModelDeploymentName))
{
Instructions =
@@ -12,7 +12,7 @@ namespace Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests.Agents;
internal sealed class TestAgentProvider(IConfiguration configuration) : AgentProvider(configuration)
{
protected override async IAsyncEnumerable<ProjectsAgentVersion> CreateAgentsAsync(Uri foundryEndpoint)
protected override async IAsyncEnumerable<AgentVersion> CreateAgentsAsync(Uri foundryEndpoint)
{
AIProjectClient aiProjectClient = new(foundryEndpoint, TestAzureCliCredentials.CreateAzureCliCredential());
@@ -23,6 +23,6 @@ internal sealed class TestAgentProvider(IConfiguration configuration) : AgentPro
agentDescription: "Basic agent");
}
private DeclarativeAgentDefinition DefineMenuAgent() =>
private PromptAgentDefinition DefineMenuAgent() =>
new(this.GetSetting(TestSettings.AzureAIModelDeploymentName));
}
@@ -12,7 +12,7 @@ namespace Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests.Agents;
internal sealed class VisionAgentProvider(IConfiguration configuration) : AgentProvider(configuration)
{
protected override async IAsyncEnumerable<ProjectsAgentVersion> CreateAgentsAsync(Uri foundryEndpoint)
protected override async IAsyncEnumerable<AgentVersion> CreateAgentsAsync(Uri foundryEndpoint)
{
AIProjectClient aiProjectClient = new(foundryEndpoint, TestAzureCliCredentials.CreateAzureCliCredential());
@@ -23,7 +23,7 @@ internal sealed class VisionAgentProvider(IConfiguration configuration) : AgentP
agentDescription: "Use computer vision to describe an image or document.");
}
private DeclarativeAgentDefinition DefineVisionAgent() =>
private PromptAgentDefinition DefineVisionAgent() =>
new(this.GetSetting(TestSettings.AzureAIModelDeploymentName))
{
Instructions =
@@ -132,53 +132,6 @@ public class InProcessExecutionTests
"both versions should produce the same number of agent events");
}
/// <summary>
/// This test checks that the logic around waiting for input and halting appropriately works right when the
/// workflow runs to halting before the EventStream is watched by the user.
/// </summary>
[Fact]
public async Task RunStreamingAsyncWaitToTakeStreamAsync()
{
// Arrange: Create a simple agent that responds to messages
var agent = new SimpleTestAgent("test-agent");
var workflow = AgentWorkflowBuilder.BuildSequential(agent);
var inputMessage = new ChatMessage(ChatRole.User, "Hello");
// Act: Execute using streaming version with TurnToken
await using StreamingRun run = await InProcessExecution.RunStreamingAsync(workflow, new List<ChatMessage> { inputMessage });
// Send TurnToken to actually trigger execution (this is the key step)
bool messageSent = await run.TrySendMessageAsync(new TurnToken(emitEvents: true));
messageSent.Should().BeTrue("TurnToken should be accepted");
while (await run.GetStatusAsync() != RunStatus.Idle)
{
await Task.Delay(200);
}
// Collect events
List<WorkflowEvent> events = [];
await foreach (WorkflowEvent evt in run.WatchStreamAsync())
{
events.Add(evt);
}
// Assert: The workflow should have executed and produced events
RunStatus status = await run.GetStatusAsync();
status.Should().Be(RunStatus.Idle, "workflow should complete execution");
events.Should().NotBeEmpty("workflow should produce events during execution");
// Check that we have agent execution events
var agentEvents = events.OfType<AgentResponseUpdateEvent>().ToList();
agentEvents.Should().NotBeEmpty("agent should have executed and produced update events");
// Check that we have output events
var outputEvents = events.OfType<WorkflowOutputEvent>().ToList();
outputEvents.Should().NotBeEmpty("workflow should produce output events");
}
/// <summary>
/// Simple test agent that echoes back the input message.
/// </summary>
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<NoWarn>$(NoWarn);MEAI001;MAAIW001</NoWarn>
<NoWarn>$(NoWarn);MEAI001</NoWarn>
</PropertyGroup>
<ItemGroup>
@@ -0,0 +1,268 @@
// Copyright (c) Microsoft. All rights reserved.
#pragma warning disable CS0618 // Type or member is obsolete - Testing deprecated OpenAI Assistants API extension methods
using System;
using System.Diagnostics;
using System.IO;
using System.Threading.Tasks;
using AgentConformance.IntegrationTests.Support;
using Microsoft.Agents.AI;
using Microsoft.Extensions.AI;
using OpenAI;
using OpenAI.Assistants;
using OpenAI.Files;
using OpenAI.VectorStores;
using Shared.IntegrationTests;
namespace OpenAIAssistant.IntegrationTests;
public class OpenAIAssistantClientExtensionsTests
{
private const string SkipCodeInterpreterReason = "OpenAI Assistant Code Interpreter intermittently fails in CI";
private readonly AssistantClient _assistantClient = new OpenAIClient(TestConfiguration.GetRequiredValue(TestSettings.OpenAIApiKey)).GetAssistantClient();
private readonly OpenAIFileClient _fileClient = new OpenAIClient(TestConfiguration.GetRequiredValue(TestSettings.OpenAIApiKey)).GetOpenAIFileClient();
[Theory]
[InlineData("CreateWithChatClientAgentOptionsAsync")]
[InlineData("CreateWithChatClientAgentOptionsSync")]
[InlineData("CreateWithParamsAsync")]
public async Task CreateAIAgentAsync_WithAIFunctionTool_InvokesFunctionAsync(string createMechanism)
{
// Arrange
const string AgentInstructions = "You are a helpful weather assistant. Always call the GetWeather function to answer questions about weather.";
static string GetWeather(string location) => $"The weather in {location} is sunny with a high of 23C.";
var weatherFunction = AIFunctionFactory.Create(GetWeather, nameof(GetWeather));
// Act
var agent = createMechanism switch
{
"CreateWithChatClientAgentOptionsAsync" => await this._assistantClient.CreateAIAgentAsync(
model: TestConfiguration.GetRequiredValue(TestSettings.OpenAIChatModelName),
options: new ChatClientAgentOptions()
{
ChatOptions = new()
{
Instructions = AgentInstructions,
Tools = [weatherFunction]
}
}),
"CreateWithChatClientAgentOptionsSync" => await this._assistantClient.CreateAIAgentAsync(
model: TestConfiguration.GetRequiredValue(TestSettings.OpenAIChatModelName),
options: new ChatClientAgentOptions()
{
ChatOptions = new()
{
Instructions = AgentInstructions,
Tools = [weatherFunction]
}
}),
"CreateWithParamsAsync" => await this._assistantClient.CreateAIAgentAsync(
model: TestConfiguration.GetRequiredValue(TestSettings.OpenAIChatModelName),
instructions: AgentInstructions,
tools: [weatherFunction]),
_ => throw new InvalidOperationException($"Unknown create mechanism: {createMechanism}")
};
try
{
// Trigger function call.
var response = await agent.RunAsync("What is the weather like in Amsterdam?");
var text = response.Text;
// Assert
Assert.Contains("Amsterdam", text, StringComparison.OrdinalIgnoreCase);
Assert.Contains("sunny", text, StringComparison.OrdinalIgnoreCase);
Assert.Contains("23", text, StringComparison.OrdinalIgnoreCase);
}
finally
{
await this._assistantClient.DeleteAssistantAsync(agent.Id);
}
}
[Theory(Skip = SkipCodeInterpreterReason)]
[InlineData("CreateWithChatClientAgentOptionsAsync")]
[InlineData("CreateWithChatClientAgentOptionsSync")]
[InlineData("CreateWithParamsAsync")]
public async Task CreateAIAgentAsync_WithHostedCodeInterpreter_RunsCodeAsync(string createMechanism)
{
// Arrange
const string Instructions = "Use the Code Interpreter Tool to run the uploaded python file and respond only with the secret number.";
// Create a python file that prints a known value.
var codeFilePath = Path.GetTempFileName() + "openai_secret_number.py";
File.WriteAllText(
path: codeFilePath,
contents: "print(\"OPENAI_SECRET=13579\")" // Deterministic output we will look for.
);
// Upload file to OpenAI Assistants file store for use with the Code Interpreter.
var uploadResult = await this._fileClient.UploadFileAsync(codeFilePath, FileUploadPurpose.Assistants);
string uploadedFileId = uploadResult.Value.Id;
var codeInterpreterTool = new HostedCodeInterpreterTool() { Inputs = [new HostedFileContent(uploadedFileId)] };
var agent = createMechanism switch
{
"CreateWithChatClientAgentOptionsAsync" => await this._assistantClient.CreateAIAgentAsync(
model: TestConfiguration.GetRequiredValue(TestSettings.OpenAIChatModelName),
options: new ChatClientAgentOptions()
{
ChatOptions = new()
{
Instructions = Instructions,
Tools = [codeInterpreterTool]
}
}),
"CreateWithChatClientAgentOptionsSync" => await this._assistantClient.CreateAIAgentAsync(
model: TestConfiguration.GetRequiredValue(TestSettings.OpenAIChatModelName),
options: new ChatClientAgentOptions()
{
ChatOptions = new()
{
Instructions = Instructions,
Tools = [codeInterpreterTool]
}
}),
"CreateWithParamsAsync" => await this._assistantClient.CreateAIAgentAsync(
model: TestConfiguration.GetRequiredValue(TestSettings.OpenAIChatModelName),
instructions: Instructions,
tools: [codeInterpreterTool]),
_ => throw new InvalidOperationException($"Unknown create mechanism: {createMechanism}")
};
try
{
var response = await agent.RunAsync("What is the OPENAI_SECRET number?");
var text = response.ToString();
Assert.Contains("13579", text);
}
finally
{
await this._assistantClient.DeleteAssistantAsync(agent.Id);
await this._fileClient.DeleteFileAsync(uploadedFileId);
File.Delete(codeFilePath);
}
}
[Theory(Skip = "For manual testing only")]
[InlineData("CreateWithChatClientAgentOptionsAsync")]
[InlineData("CreateWithChatClientAgentOptionsSync")]
[InlineData("CreateWithParamsAsync")]
public async Task CreateAIAgentAsync_WithHostedFileSearchTool_SearchesFilesAsync(string createMechanism)
{
// Arrange.
const string Instructions = """
You are a helpful agent that can help fetch data from files you know about.
Use the File Search Tool to look up codes for words.
Do not answer a question unless you can find the answer using the File Search Tool.
""";
// Create a local file with deterministic content and upload it.
var searchFilePath = Path.GetTempFileName() + "wordcodelookup.txt";
File.WriteAllText(
path: searchFilePath,
contents: "The word 'apple' uses the code 442345, while the word 'banana' uses the code 673457.");
var uploadResult = await this._fileClient.UploadFileAsync(searchFilePath, FileUploadPurpose.Assistants);
string uploadedFileId = uploadResult.Value.Id;
// Create a vector store backing the file search (HostedFileSearchTool requires a vector store id).
var vectorStoreClient = new OpenAIClient(TestConfiguration.GetRequiredValue(TestSettings.OpenAIApiKey)).GetVectorStoreClient();
var vectorStoreCreate = await vectorStoreClient.CreateVectorStoreAsync(options: new VectorStoreCreationOptions()
{
Name = "WordCodeLookup_VectorStore",
FileIds = { uploadedFileId }
});
string vectorStoreId = vectorStoreCreate.Value.Id;
// Wait for vector store indexing to complete before using it
await WaitForVectorStoreReadyAsync(vectorStoreClient, vectorStoreId);
var fileSearchTool = new HostedFileSearchTool() { Inputs = [new HostedVectorStoreContent(vectorStoreId)] };
var agent = createMechanism switch
{
"CreateWithChatClientAgentOptionsAsync" => await this._assistantClient.CreateAIAgentAsync(
model: TestConfiguration.GetRequiredValue(TestSettings.OpenAIChatModelName),
options: new ChatClientAgentOptions()
{
ChatOptions = new()
{
Instructions = Instructions,
Tools = [fileSearchTool]
}
}),
"CreateWithChatClientAgentOptionsSync" => await this._assistantClient.CreateAIAgentAsync(
model: TestConfiguration.GetRequiredValue(TestSettings.OpenAIChatModelName),
options: new ChatClientAgentOptions()
{
ChatOptions = new()
{
Instructions = Instructions,
Tools = [fileSearchTool]
}
}),
"CreateWithParamsAsync" => await this._assistantClient.CreateAIAgentAsync(
model: TestConfiguration.GetRequiredValue(TestSettings.OpenAIChatModelName),
instructions: Instructions,
tools: [fileSearchTool]),
_ => throw new InvalidOperationException($"Unknown create mechanism: {createMechanism}")
};
try
{
// Act - ask about banana code which must be retrieved via file search.
var response = await agent.RunAsync("Can you give me the documented code for 'banana'?");
var text = response.ToString();
Assert.Contains("673457", text);
}
finally
{
await this._assistantClient.DeleteAssistantAsync(agent.Id);
await vectorStoreClient.DeleteVectorStoreAsync(vectorStoreId);
await this._fileClient.DeleteFileAsync(uploadedFileId);
File.Delete(searchFilePath);
}
}
/// <summary>
/// Waits for a vector store to complete indexing by polling its status.
/// </summary>
/// <param name="client">The vector store client.</param>
/// <param name="vectorStoreId">The ID of the vector store.</param>
/// <param name="maxWaitSeconds">Maximum time to wait in seconds (default: 30).</param>
/// <returns>A task that completes when the vector store is ready or throws on timeout/failure.</returns>
private static async Task WaitForVectorStoreReadyAsync(
VectorStoreClient client,
string vectorStoreId,
int maxWaitSeconds = 30)
{
Stopwatch sw = Stopwatch.StartNew();
while (sw.Elapsed.TotalSeconds < maxWaitSeconds)
{
VectorStore vectorStore = await client.GetVectorStoreAsync(vectorStoreId);
VectorStoreStatus status = vectorStore.Status;
if (status == VectorStoreStatus.Completed)
{
if (vectorStore.FileCounts.Failed > 0)
{
throw new InvalidOperationException("Vector store indexing failed for some files");
}
return;
}
if (status == VectorStoreStatus.Expired)
{
throw new InvalidOperationException("Vector store has expired");
}
await Task.Delay(1000);
}
throw new TimeoutException($"Vector store did not complete indexing within {maxWaitSeconds}s");
}
}
+8 -85
View File
@@ -97,20 +97,13 @@ def __getattr__(name: str) -> Any:
**Important:** Do not create a new package unless approved by the core team.
Every new package starts as `alpha`.
### Alpha package checklist
### Initial Release (Preview)
1. Create directory under `packages/` (e.g., `packages/my-connector/`)
2. Add the package to `tool.uv.sources` in root `pyproject.toml`
3. Set the package version to the alpha pattern: `1.0.0a<date>`
4. Set the package classifier to `Development Status :: 3 - Alpha`
5. Include samples inside the package (e.g., `packages/my-connector/samples/`)
6. Do **NOT** add to `[all]` extra in `packages/core/pyproject.toml`
7. Do **NOT** create lazy loading in core yet
8. Add the package to `python/PACKAGE_STATUS.md` and keep that file updated when packages are added,
removed, renamed, or promoted. If the package exposes individually staged APIs, keep the feature list
there current too.
3. Include samples inside the package (e.g., `packages/my-connector/samples/`)
4. Do **NOT** add to `[all]` extra in `packages/core/pyproject.toml`
5. Do **NOT** create lazy loading in core yet
Recommended dependency workflow during connector implementation:
@@ -123,83 +116,17 @@ Recommended dependency workflow during connector implementation:
`uv run poe add-dependency-and-validate-bounds --package core --dependency "<dependency-spec>"`
If compatibility checks are not in place yet, add the dependency first, then implement tests before running bound validation.
### Promotion path
### Promotion to Stable
Promotion work is not isolated to the package being promoted. If a promotion changes dependency
metadata for downstream packages, also update the dependent packages' own versions so they publish
new metadata alongside the promoted dependency bounds.
Apply the internal package dependency update rules from the versioning section below during
promotions as well as standalone version update work.
#### Alpha -> Beta
Move a package to `beta` when it is stable enough to be part of the main install surface.
1. Update the package version to the beta pattern: `1.0.0b<date>`
2. Update the classifier to `Development Status :: 4 - Beta`
3. Add the package to `[all]` in `packages/core/pyproject.toml`
4. Move samples to the root `samples/` tree and remove package-local samples
5. Create or update the relevant lazy-loading namespace in core when the package belongs under one
6. Update `python/PACKAGE_STATUS.md`
After `alpha`, there should be no samples left inside a package folder.
#### Beta -> RC
Move a package to `rc` when its API is close to the final released shape.
1. Update the package version to the release-candidate pattern: `1.0.0rc<number>`
2. Keep the classifier at `Development Status :: 4 - Beta` because PyPI does not have a separate
release-candidate classifier
3. Keep the package in `core[all]`
4. Keep samples only in the root `samples/` tree
5. Update `python/PACKAGE_STATUS.md` to show the package as `rc`
#### RC -> Released
Move a package to `released` when it no longer carries a prerelease qualifier.
1. Update the package version to the stable pattern: `1.0.0`
2. Update the classifier to `Development Status :: 5 - Production/Stable`
3. Keep the package in `core[all]`
4. Keep samples only in the root `samples/` tree
5. Update `python/PACKAGE_STATUS.md` to show the package as `released`
6. Update all `README.md` files that install that package with
`pip install agent-framework-... --pre` so they use `pip install agent-framework-...` without
the `--pre` suffix
1. Move samples to root `samples/` folder
2. Add to `[all]` extra in `packages/core/pyproject.toml`
3. Create provider folder in `agent_framework/` with lazy loading `__init__.py`
## Versioning
### Internal package dependency updates
- If package A depends on package B within this repository, only update package A's dependency
declaration when the work on package B actually affects package A.
- If package A does not need anything from the package B change, leave package A's dependency
declaration unchanged.
- If package A does need something from the package B change, update package A's dependency
declaration to the version or versioning scheme that matches what package A now requires.
- If package B is promoted to a different lifecycle stage, update package A's dependency
declaration to the new versioning scheme for package B even when the only change is the stage
transition itself.
- Use this guidance both for ordinary version updates and for package promotion work.
- All non-core packages declare a lower bound on `agent-framework-core`
- When core version bumps with breaking changes, update the lower bound in all packages
- Non-core packages version independently; only raise core bound when using new core APIs
- If promoting a package changes a dependent package's published dependency metadata, bump the
dependent package's own version in the correct lifecycle pattern for its current stage
- Lifecycle version patterns:
- `alpha`: `1.0.0a<date>`
- `beta`: `1.0.0b<date>`
- `rc`: `1.0.0rc<number>`
- `released`: `1.0.0`
- Keep the `Development Status` classifier in `pyproject.toml` aligned with the lifecycle stage:
- `alpha` -> `Development Status :: 3 - Alpha`
- `beta` -> `Development Status :: 4 - Beta`
- `rc` -> `Development Status :: 4 - Beta`
- `released` -> `Development Status :: 5 - Production/Stable`
- See the PyPI classifier list for the available classifier values:
`https://pypi.org/classifiers/`
## Installation Options
@@ -217,10 +144,6 @@ When changing a package, check if its `AGENTS.md` needs updates:
- Changing the package's purpose or architecture
- Modifying import paths or usage patterns
Keep `python/PACKAGE_STATUS.md` updated when:
- A package is added, removed, renamed, or promoted between lifecycle stages
- A package starts or stops exposing individually staged experimental or release-candidate APIs
When a package adds, removes, or renames environment variables, update the related documentation in the same
change:
- The package's `README.md` for package-level configuration/env var guidance
-71
View File
@@ -1,71 +0,0 @@
# Python Package Status
This file tracks the current lifecycle state of the Python packages in this workspace. Some packages at later stages might have features within them that are not ready yet, these have feature stage decorators on the relevant APIs, and for `experimental` features warnings are raised. See the [Feature-level staged APIs](#feature-level-staged-apis) section below for details on which features are in which stage and where to find them.
Status is grouped into these buckets:
- `alpha` - initial release and early development packages that are not yet ready for general use
- `beta` - prerelease packages that are not currently release candidates
- `rc` - release candidate packages, these are close to ready for release but may still have some breaking changes before the final release
- `released` - stable packages without a prerelease suffix, these are stable packages that should not have breaking changes between versions
- `deprecated` - removed or deprecated packages that should not be used for new work
## Current packages
| Package | Path | State |
| --- | --- | --- |
| `agent-framework` | `python/` | `released` |
| `agent-framework-a2a` | `python/packages/a2a` | `beta` |
| `agent-framework-ag-ui` | `python/packages/ag-ui` | `beta` |
| `agent-framework-anthropic` | `python/packages/anthropic` | `beta` |
| `agent-framework-azure-ai-search` | `python/packages/azure-ai-search` | `beta` |
| `agent-framework-azure-cosmos` | `python/packages/azure-cosmos` | `beta` |
| `agent-framework-azurefunctions` | `python/packages/azurefunctions` | `beta` |
| `agent-framework-bedrock` | `python/packages/bedrock` | `beta` |
| `agent-framework-chatkit` | `python/packages/chatkit` | `beta` |
| `agent-framework-claude` | `python/packages/claude` | `beta` |
| `agent-framework-copilotstudio` | `python/packages/copilotstudio` | `beta` |
| `agent-framework-core` | `python/packages/core` | `released` |
| `agent-framework-declarative` | `python/packages/declarative` | `beta` |
| `agent-framework-devui` | `python/packages/devui` | `beta` |
| `agent-framework-durabletask` | `python/packages/durabletask` | `beta` |
| `agent-framework-foundry` | `python/packages/foundry` | `released` |
| `agent-framework-foundry-local` | `python/packages/foundry_local` | `beta` |
| `agent-framework-github-copilot` | `python/packages/github_copilot` | `beta` |
| `agent-framework-lab` | `python/packages/lab` | `beta` |
| `agent-framework-mem0` | `python/packages/mem0` | `beta` |
| `agent-framework-ollama` | `python/packages/ollama` | `beta` |
| `agent-framework-openai` | `python/packages/openai` | `released` |
| `agent-framework-orchestrations` | `python/packages/orchestrations` | `beta` |
| `agent-framework-purview` | `python/packages/purview` | `beta` |
| `agent-framework-redis` | `python/packages/redis` | `beta` |
## Deprecated / removed packages
| Package | Previous path | State | Notes |
| --- | --- | --- | --- |
| `agent-framework-azure-ai` | `python/packages/azure-ai` | `deprecated` | The client classes within the `azure-ai` package were renamed, sometimes changed, and moved to `agent-framework-foundry`. |
## Feature-level staged APIs
The following feature IDs have explicit feature-stage decorators on public APIs in the packages
listed below.
### Experimental features
#### `EVALS`
- `agent-framework-core`: exported evaluation APIs from `agent_framework`, including
`LocalEvaluator`, `evaluate_agent`, `evaluate_workflow`, and the related evaluation types and
helper checks defined in `agent_framework/_evaluation.py`
- `agent-framework-foundry`: `FoundryEvals`, `evaluate_traces`, and `evaluate_foundry_target`
#### `SKILLS`
- `agent-framework-core`: exported skills APIs from `agent_framework`, including `Skill`,
`SkillResource`, `SkillScript`, `SkillScriptRunner`, and `SkillsProvider` from
`agent_framework/_skills.py`
### Release-candidate features
There are currently no feature-level `rc` APIs.
+7 -7
View File
@@ -9,10 +9,10 @@ We recommend two common installation paths depending on your use case.
If you are exploring or developing locally, install the entire framework with all sub-packages:
```bash
pip install agent-framework
pip install agent-framework --pre
```
This installs the core and every integration package, making sure that all features are available without additional steps. This is the simplest way to get started.
This installs the core and every integration package, making sure that all features are available without additional steps. The `--pre` flag is required while Agent Framework is in preview. This is the simplest way to get started.
### 2. Selective install
@@ -22,19 +22,19 @@ If you only need specific integrations, you can install at a more granular level
# Core only
# includes Azure OpenAI and OpenAI support by default
# also includes workflows and orchestrations
pip install agent-framework-core
pip install agent-framework-core --pre
# Core + Azure AI Foundry integration
pip install agent-framework-foundry
pip install agent-framework-foundry --pre
# Core + Microsoft Copilot Studio integration (preview package)
# Core + Microsoft Copilot Studio integration
pip install agent-framework-copilotstudio --pre
# Core + both Microsoft Copilot Studio and Azure AI Foundry integration
pip install --pre agent-framework-copilotstudio agent-framework-foundry
pip install agent-framework-microsoft agent-framework-foundry --pre
```
This selective approach is useful when you know which integrations you need, and it is the recommended way to set up lightweight environments. Released packages such as `agent-framework`, `agent-framework-core`, and `agent-framework-foundry` no longer require `--pre`, while preview connectors such as `agent-framework-copilotstudio` still do.
This selective approach is useful when you know which integrations you need, and it is the recommended way to set up lightweight environments.
Supported Platforms:
@@ -365,7 +365,6 @@ class A2AAgent(AgentTelemetryLayer, BaseAgent):
)
all_updates: list[AgentResponseUpdate] = []
streamed_artifact_ids_by_task: dict[str, set[str]] = {}
async for item in a2a_stream:
if isinstance(item, A2AMessage):
# Process A2A Message
@@ -379,21 +378,12 @@ class A2AAgent(AgentTelemetryLayer, BaseAgent):
all_updates.append(update)
yield update
elif isinstance(item, tuple) and len(item) == 2 and isinstance(item[0], Task):
task, update_event = item
updates = self._updates_from_task(
task, _update_event = item
for update in self._updates_from_task(
task,
update_event=update_event,
background=background,
emit_intermediate=emit_intermediate,
streamed_artifact_ids=streamed_artifact_ids_by_task.get(task.id),
)
if isinstance(update_event, TaskArtifactUpdateEvent) and any(
update.raw_representation is update_event for update in updates
):
streamed_artifact_ids_by_task.setdefault(task.id, set()).add(update_event.artifact.artifact_id)
if task.status.state in TERMINAL_TASK_STATES:
streamed_artifact_ids_by_task.pop(task.id, None)
for update in updates:
all_updates.append(update)
yield update
else:
@@ -413,10 +403,8 @@ class A2AAgent(AgentTelemetryLayer, BaseAgent):
self,
task: Task,
*,
update_event: TaskStatusUpdateEvent | TaskArtifactUpdateEvent | None = None,
background: bool = False,
emit_intermediate: bool = False,
streamed_artifact_ids: set[str] | None = None,
) -> list[AgentResponseUpdate]:
"""Convert an A2A Task into AgentResponseUpdate(s).
@@ -430,21 +418,8 @@ class A2AAgent(AgentTelemetryLayer, BaseAgent):
"""
status = task.status
if (
emit_intermediate
and update_event is not None
and (event_updates := self._updates_from_task_update_event(update_event))
):
return event_updates
if status.state in TERMINAL_TASK_STATES:
task_messages = self._parse_messages_from_task(task)
if task.artifacts is not None and streamed_artifact_ids:
task_messages = [
message
for message in task_messages
if getattr(message.raw_representation, "artifact_id", None) not in streamed_artifact_ids
]
if task_messages:
return [
AgentResponseUpdate(
@@ -456,8 +431,6 @@ class A2AAgent(AgentTelemetryLayer, BaseAgent):
)
for message in task_messages
]
if task.artifacts is not None:
return []
return [AgentResponseUpdate(contents=[], role="assistant", response_id=task.id, raw_representation=task)]
if background and status.state in IN_PROGRESS_TASK_STATES:
@@ -494,44 +467,6 @@ class A2AAgent(AgentTelemetryLayer, BaseAgent):
return []
def _updates_from_task_update_event(
self, update_event: TaskStatusUpdateEvent | TaskArtifactUpdateEvent
) -> list[AgentResponseUpdate]:
"""Convert A2A task update events into streaming AgentResponseUpdates."""
if isinstance(update_event, TaskArtifactUpdateEvent):
contents = self._parse_contents_from_a2a(update_event.artifact.parts)
if not contents:
return []
return [
AgentResponseUpdate(
contents=contents,
role="assistant",
response_id=update_event.task_id,
message_id=update_event.artifact.artifact_id,
raw_representation=update_event,
)
]
if not isinstance(update_event, TaskStatusUpdateEvent):
return []
message = update_event.status.message
if message is None or not message.parts:
return []
contents = self._parse_contents_from_a2a(message.parts)
if not contents:
return []
return [
AgentResponseUpdate(
contents=contents,
role="assistant" if message.role == A2ARole.agent else "user",
response_id=update_event.task_id,
raw_representation=update_event,
)
]
@staticmethod
def _build_continuation_token(task: Task) -> A2AContinuationToken | None:
"""Build an A2AContinuationToken from an A2A Task if it is still in progress."""
+2 -2
View File
@@ -4,7 +4,7 @@ description = "A2A integration for Microsoft Agent Framework."
authors = [{ name = "Microsoft", email = "af-support@microsoft.com"}]
readme = "README.md"
requires-python = ">=3.10"
version = "1.0.0b260402"
version = "1.0.0b260330"
license-files = ["LICENSE"]
urls.homepage = "https://aka.ms/agent-framework"
urls.source = "https://github.com/microsoft/agent-framework/tree/main/python"
@@ -23,7 +23,7 @@ classifiers = [
"Typing :: Typed",
]
dependencies = [
"agent-framework-core>=1.0.0,<2",
"agent-framework-core>=1.0.0rc6",
"a2a-sdk>=0.3.5,<0.3.24",
]
-199
View File
@@ -14,10 +14,8 @@ from a2a.types import (
FileWithUri,
Part,
Task,
TaskArtifactUpdateEvent,
TaskState,
TaskStatus,
TaskStatusUpdateEvent,
TextPart,
)
from a2a.types import Message as A2AMessage
@@ -1191,201 +1189,4 @@ async def test_streaming_working_update_with_empty_parts_is_skipped(
assert updates[0].contents[0].text == "Result"
async def test_streaming_artifact_update_event_yields_content(
a2a_agent: A2AAgent, mock_a2a_client: MockA2AClient
) -> None:
"""Test that streaming artifact update events yield incremental content."""
task = Task(id="task-art", context_id="ctx-art", status=TaskStatus(state=TaskState.working, message=None))
artifact = Artifact(
artifact_id="artifact-1",
parts=[Part(root=TextPart(text="Hello"))],
)
update_event = TaskArtifactUpdateEvent(task_id="task-art", context_id="ctx-art", artifact=artifact, append=False)
mock_a2a_client.responses.append((task, update_event))
updates: list[AgentResponseUpdate] = []
async for update in a2a_agent.run("Hello", stream=True):
updates.append(update)
assert len(updates) == 1
assert updates[0].text == "Hello"
assert updates[0].message_id == "artifact-1"
assert updates[0].raw_representation == update_event
async def test_streaming_status_update_event_yields_content(
a2a_agent: A2AAgent, mock_a2a_client: MockA2AClient
) -> None:
"""Test that streaming status update events surface message content directly from the update event."""
update_event = TaskStatusUpdateEvent(
task_id="task-status",
context_id="ctx-status",
status=TaskStatus(
state=TaskState.working,
message=A2AMessage(
message_id=str(uuid4()),
role=A2ARole.agent,
parts=[Part(root=TextPart(text="Still working"))],
),
),
final=False,
)
task = Task(id="task-status", context_id="ctx-status", status=TaskStatus(state=TaskState.working, message=None))
mock_a2a_client.responses.append((task, update_event))
updates: list[AgentResponseUpdate] = []
async for update in a2a_agent.run("Hello", stream=True):
updates.append(update)
assert len(updates) == 1
assert updates[0].text == "Still working"
assert updates[0].role == "assistant"
assert updates[0].raw_representation == update_event
async def test_streaming_artifact_update_event_does_not_duplicate_terminal_task_artifacts(
a2a_agent: A2AAgent, mock_a2a_client: MockA2AClient
) -> None:
"""Test that streamed artifact chunks are not re-emitted from the final terminal task."""
working_task = Task(id="task-art-dup", context_id="ctx-art-dup", status=TaskStatus(state=TaskState.working))
first_chunk = TaskArtifactUpdateEvent(
task_id="task-art-dup",
context_id="ctx-art-dup",
artifact=Artifact(
artifact_id="artifact-dup",
parts=[Part(root=TextPart(text="Hello "))],
),
append=False,
)
second_chunk = TaskArtifactUpdateEvent(
task_id="task-art-dup",
context_id="ctx-art-dup",
artifact=Artifact(
artifact_id="artifact-dup",
parts=[Part(root=TextPart(text="world"))],
),
append=True,
)
terminal_task = Task(
id="task-art-dup",
context_id="ctx-art-dup",
status=TaskStatus(state=TaskState.completed, message=None),
artifacts=[
Artifact(
artifact_id="artifact-dup",
parts=[Part(root=TextPart(text="Hello world"))],
)
],
)
terminal_event = TaskStatusUpdateEvent(
task_id="task-art-dup",
context_id="ctx-art-dup",
status=TaskStatus(state=TaskState.completed, message=None),
final=True,
)
mock_a2a_client.responses.extend(
[
(working_task, first_chunk),
(working_task, second_chunk),
(terminal_task, terminal_event),
]
)
stream = a2a_agent.run("Hello", stream=True)
updates: list[AgentResponseUpdate] = []
async for update in stream:
updates.append(update)
response = await stream.get_final_response()
assert [update.text for update in updates] == ["Hello ", "world"]
assert response.text == "Hello world"
assert len(response.messages) == 1
async def test_streaming_terminal_task_artifacts_are_emitted_when_terminal_event_has_no_content(
a2a_agent: A2AAgent, mock_a2a_client: MockA2AClient
) -> None:
"""Test that terminal task artifacts are still emitted when the final status event has no message."""
terminal_task = Task(
id="task-art-final",
context_id="ctx-art-final",
status=TaskStatus(state=TaskState.completed, message=None),
artifacts=[
Artifact(
artifact_id="artifact-final",
parts=[Part(root=TextPart(text="Final artifact"))],
)
],
)
terminal_event = TaskStatusUpdateEvent(
task_id="task-art-final",
context_id="ctx-art-final",
status=TaskStatus(state=TaskState.completed, message=None),
final=True,
)
mock_a2a_client.responses.append((terminal_task, terminal_event))
updates: list[AgentResponseUpdate] = []
async for update in a2a_agent.run("Hello", stream=True):
updates.append(update)
assert len(updates) == 1
assert updates[0].text == "Final artifact"
assert updates[0].message_id == "artifact-final"
async def test_streaming_terminal_task_only_emits_unstreamed_artifacts(
a2a_agent: A2AAgent, mock_a2a_client: MockA2AClient
) -> None:
"""Test that the terminal task only emits artifacts that were not already streamed incrementally."""
working_task = Task(id="task-art-mixed", context_id="ctx-art-mixed", status=TaskStatus(state=TaskState.working))
streamed_chunk = TaskArtifactUpdateEvent(
task_id="task-art-mixed",
context_id="ctx-art-mixed",
artifact=Artifact(
artifact_id="artifact-streamed",
parts=[Part(root=TextPart(text="Hello"))],
),
append=False,
)
terminal_task = Task(
id="task-art-mixed",
context_id="ctx-art-mixed",
status=TaskStatus(state=TaskState.completed, message=None),
artifacts=[
Artifact(
artifact_id="artifact-streamed",
parts=[Part(root=TextPart(text="Hello"))],
),
Artifact(
artifact_id="artifact-final",
parts=[Part(root=TextPart(text="Goodbye"))],
),
],
)
terminal_event = TaskStatusUpdateEvent(
task_id="task-art-mixed",
context_id="ctx-art-mixed",
status=TaskStatus(state=TaskState.completed, message=None),
final=True,
)
mock_a2a_client.responses.extend(
[
(working_task, streamed_chunk),
(terminal_task, terminal_event),
]
)
stream = a2a_agent.run("Hello", stream=True)
updates: list[AgentResponseUpdate] = []
async for update in stream:
updates.append(update)
response = await stream.get_final_response()
assert [update.text for update in updates] == ["Hello", "Goodbye"]
assert [message.text for message in response.messages] == ["Hello", "Goodbye"]
# endregion
@@ -44,7 +44,7 @@ async def main():
metadata = {"thread_id": thread_id} if thread_id else None
stream = client.get_response(
[Message(role="user", contents=[message])],
[Message(role="user", text=message)],
stream=True,
options={"metadata": metadata} if metadata else None,
)
@@ -73,7 +73,7 @@ async def streaming_example(client: AGUIChatClient, thread_id: str | None = None
print("Assistant: ", end="", flush=True)
stream = client.get_response(
[Message(role="user", contents=["Tell me a short joke"])],
[Message(role="user", text="Tell me a short joke")],
stream=True,
options={"metadata": metadata} if metadata else None,
)
@@ -100,7 +100,7 @@ async def non_streaming_example(client: AGUIChatClient, thread_id: str | None =
print("\nUser: What is 2 + 2?\n")
response = await client.get_response([Message(role="user", contents=["What is 2 + 2?"])], metadata=metadata)
response = await client.get_response([Message(role="user", text="What is 2 + 2?")], metadata=metadata)
print(f"Assistant: {response.text}")
@@ -139,9 +139,7 @@ async def tool_example(client: AGUIChatClient, thread_id: str | None = None):
print("(Server must be configured with matching tools to execute them)\n")
response = await client.get_response(
[Message(role="user", contents=["What's the weather in Seattle?"])],
tools=[get_weather, calculate],
metadata=metadata,
[Message(role="user", text="What's the weather in Seattle?")], tools=[get_weather, calculate], metadata=metadata
)
print(f"Assistant: {response.text}")
@@ -176,7 +174,7 @@ async def conversation_example(client: AGUIChatClient):
# First turn
print("User: My name is Alice\n")
response1 = await client.get_response([Message(role="user", contents=["My name is Alice"])])
response1 = await client.get_response([Message(role="user", text="My name is Alice")])
print(f"Assistant: {response1.text}")
thread_id = response1.additional_properties.get("thread_id")
print(f"\n[Thread: {thread_id}]")
@@ -184,7 +182,7 @@ async def conversation_example(client: AGUIChatClient):
# Second turn - using same thread
print("\nUser: What's my name?\n")
response2 = await client.get_response(
[Message(role="user", contents=["What's my name?"])], options={"metadata": {"thread_id": thread_id}}
[Message(role="user", text="What's my name?")], options={"metadata": {"thread_id": thread_id}}
)
print(f"Assistant: {response2.text}")
@@ -195,7 +193,7 @@ async def conversation_example(client: AGUIChatClient):
# Third turn
print("\nUser: Can you also tell me what 10 * 5 is?\n")
response3 = await client.get_response(
[Message(role="user", contents=["Can you also tell me what 10 * 5 is?"])],
[Message(role="user", text="Can you also tell me what 10 * 5 is?")],
options={"metadata": {"thread_id": thread_id}},
tools=[calculate],
)
+2 -2
View File
@@ -1,6 +1,6 @@
[project]
name = "agent-framework-ag-ui"
version = "1.0.0b260402"
version = "1.0.0b260330"
description = "AG-UI protocol integration for Agent Framework"
readme = "README.md"
license-files = ["LICENSE"]
@@ -22,7 +22,7 @@ classifiers = [
"Typing :: Typed",
]
dependencies = [
"agent-framework-core>=1.0.0,<2",
"agent-framework-core>=1.0.0rc6",
"ag-ui-protocol==0.1.13",
"fastapi>=0.115.0,<0.133.1",
"uvicorn[standard]>=0.30.0,<0.42.0"
@@ -67,8 +67,8 @@ class TestAGUIChatClient:
"""Test state extraction when no state is present."""
client = StubAGUIChatClient(endpoint="http://localhost:8888/")
messages = [
Message(role="user", contents=["Hello"]),
Message(role="assistant", contents=["Hi there"]),
Message(role="user", text="Hello"),
Message(role="assistant", text="Hi there"),
]
result_messages, state = client.extract_state_from_messages(messages)
@@ -87,7 +87,7 @@ class TestAGUIChatClient:
state_b64 = base64.b64encode(state_json.encode("utf-8")).decode("utf-8")
messages = [
Message(role="user", contents=["Hello"]),
Message(role="user", text="Hello"),
Message(
role="user",
contents=[Content.from_uri(uri=f"data:application/json;base64,{state_b64}")],
@@ -125,8 +125,8 @@ class TestAGUIChatClient:
"""Test message conversion to AG-UI format."""
client = StubAGUIChatClient(endpoint="http://localhost:8888/")
messages = [
Message(role="user", contents=["What is the weather?"]),
Message(role="assistant", contents=["Let me check."], message_id="msg_123"),
Message(role="user", text="What is the weather?"),
Message(role="assistant", text="Let me check.", message_id="msg_123"),
]
agui_messages = client.convert_messages_to_agui_format(messages)
@@ -173,7 +173,7 @@ class TestAGUIChatClient:
client = StubAGUIChatClient(endpoint="http://localhost:8888/")
monkeypatch.setattr(client.http_service, "post_run", mock_post_run)
messages = [Message(role="user", contents=["Test message"])]
messages = [Message(role="user", text="Test message")]
chat_options = ChatOptions()
updates: list[ChatResponseUpdate] = []
@@ -206,7 +206,7 @@ class TestAGUIChatClient:
client = StubAGUIChatClient(endpoint="http://localhost:8888/")
monkeypatch.setattr(client.http_service, "post_run", mock_post_run)
messages = [Message(role="user", contents=["Test message"])]
messages = [Message(role="user", text="Test message")]
chat_options = {}
response = await client.inner_get_response(messages=messages, options=chat_options)
@@ -249,7 +249,7 @@ class TestAGUIChatClient:
client = StubAGUIChatClient(endpoint="http://localhost:8888/")
monkeypatch.setattr(client.http_service, "post_run", mock_post_run)
messages = [Message(role="user", contents=["Test with tools"])]
messages = [Message(role="user", text="Test with tools")]
chat_options = ChatOptions(tools=[test_tool])
response = await client.inner_get_response(messages=messages, options=chat_options)
@@ -273,7 +273,7 @@ class TestAGUIChatClient:
client = StubAGUIChatClient(endpoint="http://localhost:8888/")
monkeypatch.setattr(client.http_service, "post_run", mock_post_run)
messages = [Message(role="user", contents=["Test server tool execution"])]
messages = [Message(role="user", text="Test server tool execution")]
updates: list[ChatResponseUpdate] = []
async for update in client.get_response(messages, stream=True):
@@ -315,7 +315,7 @@ class TestAGUIChatClient:
client = StubAGUIChatClient(endpoint="http://localhost:8888/")
monkeypatch.setattr(client.http_service, "post_run", mock_post_run)
messages = [Message(role="user", contents=["Test server tool execution"])]
messages = [Message(role="user", text="Test server tool execution")]
async for _ in client.get_response(
messages, stream=True, options={"tool_choice": "auto", "tools": [client_tool]}
@@ -331,7 +331,7 @@ class TestAGUIChatClient:
state_b64 = base64.b64encode(state_json.encode("utf-8")).decode("utf-8")
messages = [
Message(role="user", contents=["Hello"]),
Message(role="user", text="Hello"),
Message(
role="user",
contents=[Content.from_uri(uri=f"data:application/json;base64,{state_b64}")],
@@ -388,7 +388,7 @@ class TestAGUIChatClient:
client = StubAGUIChatClient(endpoint="http://localhost:8888/")
monkeypatch.setattr(client.http_service, "post_run", mock_post_run)
messages = [Message(role="user", contents=["Test"])]
messages = [Message(role="user", text="Test")]
response = await client.inner_get_response(messages=messages, options={}, stream=False)
assert response is not None
@@ -416,7 +416,7 @@ class TestAGUIChatClient:
client = StubAGUIChatClient(endpoint="http://localhost:8888/")
monkeypatch.setattr(client.http_service, "post_run", mock_post_run)
messages = [Message(role="user", contents=["Test"])]
messages = [Message(role="user", text="Test")]
updates: list[ChatResponseUpdate] = []
async for update in client._inner_get_response(messages=messages, stream=True, options={"tools": [my_tool]}):
updates.append(update)
@@ -451,7 +451,7 @@ class TestAGUIChatClient:
client = StubAGUIChatClient(endpoint="http://localhost:8888/")
monkeypatch.setattr(client.http_service, "post_run", mock_post_run)
messages = [Message(role="user", contents=["continue"])]
messages = [Message(role="user", text="continue")]
options = {
"available_interrupts": available_interrupts,
"resume": resume_payload,
+2 -2
View File
@@ -4,7 +4,7 @@ description = "Anthropic integration for Microsoft Agent Framework."
authors = [{ name = "Microsoft", email = "af-support@microsoft.com"}]
readme = "README.md"
requires-python = ">=3.10"
version = "1.0.0b260402"
version = "1.0.0b260330"
license-files = ["LICENSE"]
urls.homepage = "https://aka.ms/agent-framework"
urls.source = "https://github.com/microsoft/agent-framework/tree/main/python"
@@ -23,7 +23,7 @@ classifiers = [
"Typing :: Typed",
]
dependencies = [
"agent-framework-core>=1.0.0,<2",
"agent-framework-core>=1.0.0rc6",
"anthropic>=0.80.0,<0.80.1",
]
@@ -172,7 +172,7 @@ def test_anthropic_client_service_url(mock_anthropic_client: MagicMock) -> None:
def test_prepare_message_for_anthropic_text(mock_anthropic_client: MagicMock) -> None:
"""Test converting text message to Anthropic format."""
client = create_test_anthropic_client(mock_anthropic_client)
message = Message(role="user", contents=["Hello, world!"])
message = Message(role="user", text="Hello, world!")
result = client._prepare_message_for_anthropic(message)
@@ -491,8 +491,8 @@ def test_prepare_messages_for_anthropic_with_system(
"""Test converting messages list with system message."""
client = create_test_anthropic_client(mock_anthropic_client)
messages = [
Message(role="system", contents=["You are a helpful assistant."]),
Message(role="user", contents=["Hello!"]),
Message(role="system", text="You are a helpful assistant."),
Message(role="user", text="Hello!"),
]
result = client._prepare_messages_for_anthropic(messages)
@@ -509,8 +509,8 @@ def test_prepare_messages_for_anthropic_without_system(
"""Test converting messages list without system message."""
client = create_test_anthropic_client(mock_anthropic_client)
messages = [
Message(role="user", contents=["Hello!"]),
Message(role="assistant", contents=["Hi there!"]),
Message(role="user", text="Hello!"),
Message(role="assistant", text="Hi there!"),
]
result = client._prepare_messages_for_anthropic(messages)
@@ -735,7 +735,7 @@ async def test_prepare_options_basic(mock_anthropic_client: MagicMock) -> None:
"""Test _prepare_options with basic ChatOptions."""
client = create_test_anthropic_client(mock_anthropic_client)
messages = [Message(role="user", contents=["Hello"])]
messages = [Message(role="user", text="Hello")]
chat_options = ChatOptions(max_tokens=100, temperature=0.7)
run_options = client._prepare_options(messages, chat_options)
@@ -753,8 +753,8 @@ async def test_prepare_options_with_system_message(
client = create_test_anthropic_client(mock_anthropic_client)
messages = [
Message(role="system", contents=["You are helpful."]),
Message(role="user", contents=["Hello"]),
Message(role="system", text="You are helpful."),
Message(role="user", text="Hello"),
]
chat_options = ChatOptions()
@@ -807,7 +807,7 @@ async def test_anthropic_shell_tool_is_invoked_in_function_loop(
]
await client.get_response(
messages=[Message(role="user", contents=["Run pwd"])],
messages=[Message(role="user", text="Run pwd")],
options={"tools": [shell_tool_instance], "max_tokens": 64},
)
@@ -833,7 +833,7 @@ async def test_prepare_options_with_tool_choice_auto(
"""Test _prepare_options with auto tool choice."""
client = create_test_anthropic_client(mock_anthropic_client)
messages = [Message(role="user", contents=["Hello"])]
messages = [Message(role="user", text="Hello")]
chat_options = ChatOptions(tool_choice="auto", allow_multiple_tool_calls=False)
run_options = client._prepare_options(messages, chat_options)
@@ -849,7 +849,7 @@ async def test_prepare_options_with_tool_choice_required(
"""Test _prepare_options with required tool choice."""
client = create_test_anthropic_client(mock_anthropic_client)
messages = [Message(role="user", contents=["Hello"])]
messages = [Message(role="user", text="Hello")]
# For required with specific function, need to pass as dict
chat_options = ChatOptions(tool_choice={"mode": "required", "required_function_name": "get_weather"})
@@ -865,7 +865,7 @@ async def test_prepare_options_with_tool_choice_none(
"""Test _prepare_options with none tool choice."""
client = create_test_anthropic_client(mock_anthropic_client)
messages = [Message(role="user", contents=["Hello"])]
messages = [Message(role="user", text="Hello")]
chat_options = ChatOptions(tool_choice="none")
run_options = client._prepare_options(messages, chat_options)
@@ -882,7 +882,7 @@ async def test_prepare_options_with_tools(mock_anthropic_client: MagicMock) -> N
"""Get weather for a location."""
return f"Weather for {location}"
messages = [Message(role="user", contents=["Hello"])]
messages = [Message(role="user", text="Hello")]
chat_options = ChatOptions(tools=[get_weather])
run_options = client._prepare_options(messages, chat_options)
@@ -897,7 +897,7 @@ async def test_prepare_options_with_stop_sequences(
"""Test _prepare_options with stop sequences."""
client = create_test_anthropic_client(mock_anthropic_client)
messages = [Message(role="user", contents=["Hello"])]
messages = [Message(role="user", text="Hello")]
chat_options = ChatOptions(stop=["STOP", "END"])
run_options = client._prepare_options(messages, chat_options)
@@ -909,7 +909,7 @@ async def test_prepare_options_with_top_p(mock_anthropic_client: MagicMock) -> N
"""Test _prepare_options with top_p."""
client = create_test_anthropic_client(mock_anthropic_client)
messages = [Message(role="user", contents=["Hello"])]
messages = [Message(role="user", text="Hello")]
chat_options = ChatOptions(top_p=0.9)
run_options = client._prepare_options(messages, chat_options)
@@ -923,7 +923,7 @@ async def test_prepare_options_excludes_stream_option(
"""Test _prepare_options excludes stream when stream is provided in options."""
client = create_test_anthropic_client(mock_anthropic_client)
messages = [Message(role="user", contents=["Hello"])]
messages = [Message(role="user", text="Hello")]
chat_options: dict[str, Any] = {"stream": True, "max_tokens": 100}
run_options = client._prepare_options(messages, chat_options)
@@ -941,7 +941,7 @@ async def test_prepare_options_filters_internal_kwargs(
"""
client = create_test_anthropic_client(mock_anthropic_client)
messages = [Message(role="user", contents=["Hello"])]
messages = [Message(role="user", text="Hello")]
chat_options: ChatOptions = {}
# Simulate internal kwargs that get passed through the middleware pipeline
@@ -1174,7 +1174,10 @@ def test_parse_contents_server_tool_use_input_json_delta_ignored(
delta_content.partial_json = '{"query": "latest news"}'
result = client._parse_contents_from_anthropic([delta_content])
assert result == [], "input_json_delta after server_tool_use should produce no content, but got: %r" % result
assert result == [], (
"input_json_delta after server_tool_use should produce no content, "
"but got: %r" % result
)
# A second delta must also be ignored
delta_content_2 = MagicMock()
@@ -1183,7 +1186,8 @@ def test_parse_contents_server_tool_use_input_json_delta_ignored(
result = client._parse_contents_from_anthropic([delta_content_2])
assert result == [], (
"subsequent input_json_delta after server_tool_use should also be ignored, but got: %r" % result
"subsequent input_json_delta after server_tool_use should also be ignored, "
"but got: %r" % result
)
@@ -1218,7 +1222,7 @@ async def test_inner_get_response(mock_anthropic_client: MagicMock) -> None:
mock_anthropic_client.beta.messages.create.return_value = mock_message
messages = [Message(role="user", contents=["Hi"])]
messages = [Message(role="user", text="Hi")]
chat_options = ChatOptions(max_tokens=10)
response = await client._inner_get_response( # type: ignore[attr-defined]
@@ -1244,7 +1248,7 @@ async def test_inner_get_response_ignores_options_stream_non_streaming(
mock_message.stop_reason = "end_turn"
mock_anthropic_client.beta.messages.create.return_value = mock_message
messages = [Message(role="user", contents=["Hi"])]
messages = [Message(role="user", text="Hi")]
options: dict[str, Any] = {"max_tokens": 10, "stream": True}
await client._inner_get_response( # type: ignore[attr-defined]
@@ -1268,7 +1272,7 @@ async def test_inner_get_response_streaming(mock_anthropic_client: MagicMock) ->
mock_anthropic_client.beta.messages.create.return_value = mock_stream()
messages = [Message(role="user", contents=["Hi"])]
messages = [Message(role="user", text="Hi")]
chat_options = ChatOptions(max_tokens=10)
chunks: list[ChatResponseUpdate] = []
@@ -1295,7 +1299,7 @@ async def test_inner_get_response_ignores_options_stream_streaming(
mock_anthropic_client.beta.messages.create.return_value = mock_stream()
messages = [Message(role="user", contents=["Hi"])]
messages = [Message(role="user", text="Hi")]
options: dict[str, Any] = {"max_tokens": 10, "stream": False}
async for _ in client._inner_get_response( # type: ignore[attr-defined]
@@ -1449,7 +1453,7 @@ async def test_anthropic_client_integration_basic_chat() -> None:
"""Integration test for basic chat completion."""
client = AnthropicClient()
messages = [Message(role="user", contents=["Say 'Hello, World!' and nothing else."])]
messages = [Message(role="user", text="Say 'Hello, World!' and nothing else.")]
response = await client.get_response(messages=messages, options={"max_tokens": 50})
@@ -1467,7 +1471,7 @@ async def test_anthropic_client_integration_streaming_chat() -> None:
"""Integration test for streaming chat completion."""
client = AnthropicClient()
messages = [Message(role="user", contents=["Count from 1 to 5."])]
messages = [Message(role="user", text="Count from 1 to 5.")]
chunks = []
async for chunk in client.get_response(messages=messages, stream=True, options={"max_tokens": 50}):
@@ -1484,7 +1488,7 @@ async def test_anthropic_client_integration_function_calling() -> None:
"""Integration test for function calling."""
client = AnthropicClient()
messages = [Message(role="user", contents=["What's the weather in San Francisco?"])]
messages = [Message(role="user", text="What's the weather in San Francisco?")]
tools = [get_weather]
response = await client.get_response(
@@ -1505,7 +1509,7 @@ async def test_anthropic_client_integration_hosted_tools() -> None:
"""Integration test for hosted tools."""
client = AnthropicClient()
messages = [Message(role="user", contents=["What tools do you have available?"])]
messages = [Message(role="user", text="What tools do you have available?")]
tools = [
AnthropicClient.get_web_search_tool(),
AnthropicClient.get_code_interpreter_tool(),
@@ -1532,8 +1536,8 @@ async def test_anthropic_client_integration_with_system_message() -> None:
client = AnthropicClient()
messages = [
Message(role="system", contents=["You are a pirate. Always respond like a pirate."]),
Message(role="user", contents=["Hello!"]),
Message(role="system", text="You are a pirate. Always respond like a pirate."),
Message(role="user", text="Hello!"),
]
response = await client.get_response(messages=messages, options={"max_tokens": 50})
@@ -1549,7 +1553,7 @@ async def test_anthropic_client_integration_temperature_control() -> None:
"""Integration test with temperature control."""
client = AnthropicClient()
messages = [Message(role="user", contents=["Say hello."])]
messages = [Message(role="user", text="Say hello.")]
response = await client.get_response(
messages=messages,
@@ -1568,11 +1572,11 @@ async def test_anthropic_client_integration_ordering() -> None:
client = AnthropicClient()
messages = [
Message(role="user", contents=["Say hello."]),
Message(role="user", contents=["Then say goodbye."]),
Message(role="assistant", contents=["Thank you for chatting!"]),
Message(role="assistant", contents=["Let me know if I can help."]),
Message(role="user", contents=["Just testing things."]),
Message(role="user", text="Say hello."),
Message(role="user", text="Then say goodbye."),
Message(role="assistant", text="Thank you for chatting!"),
Message(role="assistant", text="Let me know if I can help."),
Message(role="user", text="Just testing things."),
]
response = await client.get_response(messages=messages)
@@ -2681,7 +2685,7 @@ async def test_anthropic_client_integration_tool_rich_content_image() -> None:
client = AnthropicClient()
client.function_invocation_configuration["max_iterations"] = 2
messages = [Message(role="user", contents=["Call the get_test_image tool and describe what you see."])]
messages = [Message(role="user", text="Call the get_test_image tool and describe what you see.")]
response = await client.get_response(
messages=messages,
@@ -604,9 +604,7 @@ class AzureAISearchContextProvider(ContextProvider):
if not result_messages:
return
context.extend_messages(
self.source_id, [Message(role="user", contents=[self.context_prompt]), *result_messages]
)
context.extend_messages(self.source_id, [Message(role="user", text=self.context_prompt), *result_messages])
def _find_vector_fields(self, index: Any) -> list[str]:
"""Find all fields that can store vectors."""
@@ -721,7 +719,7 @@ class AzureAISearchContextProvider(ContextProvider):
doc_id = doc.get("id") or doc.get("@search.id") # type: ignore[reportUnknownVariableType]
doc_text: str = self._extract_document_text(doc, doc_id=doc_id) # type: ignore[reportUnknownArgumentType]
if doc_text:
result_messages.append(Message(role="user", contents=[doc_text])) # type: ignore[reportUnknownArgumentType]
result_messages.append(Message(role="user", text=doc_text)) # type: ignore[reportUnknownArgumentType]
return result_messages
async def _ensure_knowledge_base(self) -> None:
@@ -953,7 +951,7 @@ class AzureAISearchContextProvider(ContextProvider):
List of Messages, or a single default Message if no results found.
"""
if not retrieval_result.response:
return [Message(role="assistant", contents=["No results found from Knowledge Base."])]
return [Message(role="assistant", text="No results found from Knowledge Base.")]
annotations = AzureAISearchContextProvider._parse_references_to_annotations(retrieval_result.references)
@@ -974,7 +972,7 @@ class AzureAISearchContextProvider(ContextProvider):
result_messages.append(Message(role=kb_msg.role or "assistant", contents=contents))
if not result_messages:
return [Message(role="assistant", contents=["No results found from Knowledge Base."])]
return [Message(role="assistant", text="No results found from Knowledge Base.")]
return result_messages
def _extract_document_text(self, doc: dict[str, Any], doc_id: str | None = None) -> str:
@@ -4,7 +4,7 @@ description = "Azure AI Search integration for Microsoft Agent Framework."
authors = [{ name = "Microsoft", email = "af-support@microsoft.com"}]
readme = "README.md"
requires-python = ">=3.10"
version = "1.0.0b260402"
version = "1.0.0b260330"
license-files = ["LICENSE"]
urls.homepage = "https://aka.ms/agent-framework"
urls.source = "https://github.com/microsoft/agent-framework/tree/main/python"
@@ -23,7 +23,7 @@ classifiers = [
"Typing :: Typed",
]
dependencies = [
"agent-framework-core>=1.0.0,<2",
"agent-framework-core>=1.0.0rc6",
"azure-search-documents>=11.7.0b2,<11.7.0b3",
]
@@ -1370,7 +1370,7 @@ class TestPrepareMessagesForKbSearch:
assert len(result) == 0
def test_fallback_to_msg_text_when_no_contents(self) -> None:
msg = Message(role="user", contents=["fallback text"])
msg = Message(role="user", text="fallback text")
result = AzureAISearchContextProvider._prepare_messages_for_kb_search([msg])
assert len(result) == 1
assert result[0].content[0].text == "fallback text"
+2 -2
View File
@@ -4,7 +4,7 @@ description = "Azure Cosmos DB history provider integration for Microsoft Agent
authors = [{ name = "Microsoft", email = "af-support@microsoft.com"}]
readme = "README.md"
requires-python = ">=3.10"
version = "1.0.0b260402"
version = "1.0.0b260330"
license-files = ["LICENSE"]
urls.homepage = "https://aka.ms/agent-framework"
urls.source = "https://github.com/microsoft/agent-framework/tree/main/python"
@@ -23,7 +23,7 @@ classifiers = [
"Typing :: Typed",
]
dependencies = [
"agent-framework-core>=1.0.0,<2",
"agent-framework-core>=1.0.0rc6",
"azure-cosmos>=4.3.0,<5",
]
@@ -240,11 +240,11 @@ def build_agent_executor_response(
Returns:
AgentExecutorResponse with reconstructed conversation
"""
final_text: str = response_text or ""
final_text = response_text
if structured_response:
final_text = json.dumps(structured_response)
assistant_message = Message(role="assistant", contents=[final_text])
assistant_message = Message(role="assistant", text=final_text)
agent_response = AgentResponse(
messages=[assistant_message],
@@ -255,7 +255,7 @@ def build_agent_executor_response(
if isinstance(previous_message, AgentExecutorResponse) and previous_message.full_conversation:
full_conversation.extend(previous_message.full_conversation)
elif isinstance(previous_message, str):
full_conversation.append(Message(role="user", contents=[previous_message]))
full_conversation.append(Message(role="user", text=previous_message))
full_conversation.append(assistant_message)
@@ -4,7 +4,7 @@ description = "Azure Functions integration for Microsoft Agent Framework."
authors = [{ name = "Microsoft", email = "af-support@microsoft.com"}]
readme = "README.md"
requires-python = ">=3.10"
version = "1.0.0b260402"
version = "1.0.0b260330"
license-files = ["LICENSE"]
urls.homepage = "https://aka.ms/agent-framework"
urls.source = "https://github.com/microsoft/agent-framework/tree/main/python"
@@ -22,7 +22,7 @@ classifiers = [
"Typing :: Typed",
]
dependencies = [
"agent-framework-core>=1.0.0,<2",
"agent-framework-core>=1.0.0rc6",
"agent-framework-durabletask",
"azure-functions>=1.24.0,<2",
"azure-functions-durable>=1.3.1,<2",
@@ -357,7 +357,7 @@ class TestAgentEntityOperations:
"""Test that entity can run agent operation."""
mock_agent = Mock()
mock_agent.run = AsyncMock(
return_value=AgentResponse(messages=[Message(role="assistant", contents=["Test response"])])
return_value=AgentResponse(messages=[Message(role="assistant", text="Test response")])
)
entity = AgentEntity(mock_agent, state_provider=_InMemoryStateProvider(thread_id="test-conv-123"))
@@ -374,9 +374,7 @@ class TestAgentEntityOperations:
async def test_entity_stores_conversation_history(self) -> None:
"""Test that the entity stores conversation history."""
mock_agent = Mock()
mock_agent.run = AsyncMock(
return_value=AgentResponse(messages=[Message(role="assistant", contents=["Response 1"])])
)
mock_agent.run = AsyncMock(return_value=AgentResponse(messages=[Message(role="assistant", text="Response 1")]))
entity = AgentEntity(mock_agent, state_provider=_InMemoryStateProvider(thread_id="conv-1"))
@@ -408,9 +406,7 @@ class TestAgentEntityOperations:
async def test_entity_increments_message_count(self) -> None:
"""Test that the entity increments the message count."""
mock_agent = Mock()
mock_agent.run = AsyncMock(
return_value=AgentResponse(messages=[Message(role="assistant", contents=["Response"])])
)
mock_agent.run = AsyncMock(return_value=AgentResponse(messages=[Message(role="assistant", text="Response")]))
entity = AgentEntity(mock_agent, state_provider=_InMemoryStateProvider(thread_id="conv-1"))
@@ -449,9 +445,7 @@ class TestAgentEntityFactory:
def test_entity_function_handles_run_operation(self) -> None:
"""Test that the entity function handles the run operation."""
mock_agent = Mock()
mock_agent.run = AsyncMock(
return_value=AgentResponse(messages=[Message(role="assistant", contents=["Response"])])
)
mock_agent.run = AsyncMock(return_value=AgentResponse(messages=[Message(role="assistant", text="Response")]))
entity_function = create_agent_entity(mock_agent)
@@ -476,9 +470,7 @@ class TestAgentEntityFactory:
def test_entity_function_handles_run_agent_operation(self) -> None:
"""Test that the entity function handles the deprecated run_agent operation for backward compatibility."""
mock_agent = Mock()
mock_agent.run = AsyncMock(
return_value=AgentResponse(messages=[Message(role="assistant", contents=["Response"])])
)
mock_agent.run = AsyncMock(return_value=AgentResponse(messages=[Message(role="assistant", text="Response")]))
entity_function = create_agent_entity(mock_agent)
@@ -19,9 +19,7 @@ FuncT = TypeVar("FuncT", bound=Callable[..., Any])
def _agent_response(text: str | None) -> AgentResponse:
"""Create an AgentResponse with a single assistant message."""
message = (
Message(role="assistant", contents=[text]) if text is not None else Message(role="assistant", contents=[""])
)
message = Message(role="assistant", text=text) if text is not None else Message(role="assistant", text="")
return AgentResponse(messages=[message])
@@ -206,7 +206,7 @@ class TestSerializationRoundtrip:
def test_roundtrip_chat_message(self) -> None:
"""Test Message survives encode → decode roundtrip."""
original = Message(role="user", contents=["Hello"])
original = Message(role="user", text="Hello")
encoded = serialize_value(original)
decoded = deserialize_value(encoded)
@@ -216,7 +216,7 @@ class TestSerializationRoundtrip:
def test_roundtrip_agent_executor_request(self) -> None:
"""Test AgentExecutorRequest with nested Messages roundtrips."""
original = AgentExecutorRequest(
messages=[Message(role="user", contents=["Hi"])],
messages=[Message(role="user", text="Hi")],
should_respond=True,
)
encoded = serialize_value(original)
@@ -231,8 +231,8 @@ class TestSerializationRoundtrip:
"""Test AgentExecutorResponse with nested AgentResponse roundtrips."""
original = AgentExecutorResponse(
executor_id="test_exec",
agent_response=AgentResponse(messages=[Message(role="assistant", contents=["Reply"])]),
full_conversation=[Message(role="assistant", contents=["Reply"])],
agent_response=AgentResponse(messages=[Message(role="assistant", text="Reply")]),
full_conversation=[Message(role="assistant", text="Reply")],
)
encoded = serialize_value(original)
decoded = deserialize_value(encoded)
@@ -272,8 +272,8 @@ class TestSerializationRoundtrip:
def test_roundtrip_list_of_objects(self) -> None:
"""Test list of typed objects roundtrips."""
original = [
Message(role="user", contents=["Q"]),
Message(role="assistant", contents=["A"]),
Message(role="user", text="Q"),
Message(role="assistant", text="A"),
]
encoded = serialize_value(original)
decoded = deserialize_value(encoded)
@@ -284,7 +284,7 @@ class TestSerializationRoundtrip:
def test_roundtrip_dict_of_objects(self) -> None:
"""Test dict with typed values roundtrips (used for shared state)."""
original = {"count": 42, "msg": Message(role="user", contents=["Hi"])}
original = {"count": 42, "msg": Message(role="user", text="Hi")}
encoded = serialize_value(original)
decoded = deserialize_value(encoded)
@@ -155,7 +155,7 @@ class TestAgentResponseHelpers:
# Simulate successful entity task completion
entity_task.state = TaskState.SUCCEEDED
entity_task.result = AgentResponse(messages=[Message(role="assistant", contents=["Test response"])]).to_dict()
entity_task.result = AgentResponse(messages=[Message(role="assistant", text="Test response")]).to_dict()
# Clear pending_tasks to simulate that parent has processed the child
task.pending_tasks.clear()
@@ -197,9 +197,7 @@ class TestAgentResponseHelpers:
# Simulate successful entity task with JSON response
entity_task.state = TaskState.SUCCEEDED
entity_task.result = AgentResponse(
messages=[Message(role="assistant", contents=['{"answer": "42"}'])]
).to_dict()
entity_task.result = AgentResponse(messages=[Message(role="assistant", text='{"answer": "42"}')]).to_dict()
# Clear pending_tasks to simulate that parent has processed the child
task.pending_tasks.clear()
@@ -177,10 +177,10 @@ class TestBuildAgentExecutorResponse:
# Create a previous response with conversation history
previous = AgentExecutorResponse(
executor_id="prev",
agent_response=AgentResponse(messages=[Message(role="assistant", contents=["Previous"])]),
agent_response=AgentResponse(messages=[Message(role="assistant", text="Previous")]),
full_conversation=[
Message(role="user", contents=["First"]),
Message(role="assistant", contents=["Previous"]),
Message(role="user", text="First"),
Message(role="assistant", text="Previous"),
],
)
@@ -211,8 +211,8 @@ class TestExtractMessageContent:
"""Test extracting from AgentExecutorResponse with text."""
response = AgentExecutorResponse(
executor_id="exec",
agent_response=AgentResponse(messages=[Message(role="assistant", contents=["Response text"])]),
full_conversation=[Message(role="assistant", contents=["Response text"])],
agent_response=AgentResponse(messages=[Message(role="assistant", text="Response text")]),
full_conversation=[Message(role="assistant", text="Response text")],
)
result = _extract_message_content(response)
@@ -225,13 +225,13 @@ class TestExtractMessageContent:
executor_id="exec",
agent_response=AgentResponse(
messages=[
Message(role="user", contents=["First"]),
Message(role="assistant", contents=["Last message"]),
Message(role="user", text="First"),
Message(role="assistant", text="Last message"),
]
),
full_conversation=[
Message(role="user", contents=["First"]),
Message(role="assistant", contents=["Last message"]),
Message(role="user", text="First"),
Message(role="assistant", text="Last message"),
],
)
@@ -244,8 +244,8 @@ class TestExtractMessageContent:
"""Test extracting from AgentExecutorRequest."""
request = AgentExecutorRequest(
messages=[
Message(role="user", contents=["First"]),
Message(role="user", contents=["Last request"]),
Message(role="user", text="First"),
Message(role="user", text="Last request"),
]
)
+2 -2
View File
@@ -4,7 +4,7 @@ description = "Amazon Bedrock integration for Microsoft Agent Framework."
authors = [{ name = "Microsoft", email = "af-support@microsoft.com"}]
readme = "README.md"
requires-python = ">=3.10"
version = "1.0.0b260402"
version = "1.0.0b260330"
license-files = ["LICENSE"]
urls.homepage = "https://aka.ms/agent-framework"
urls.source = "https://github.com/microsoft/agent-framework/tree/main/python"
@@ -23,7 +23,7 @@ classifiers = [
"Typing :: Typed",
]
dependencies = [
"agent-framework-core>=1.0.0,<2",
"agent-framework-core>=1.0.0rc6",
"boto3>=1.35.0,<2.0.0",
"botocore>=1.35.0,<2.0.0",
]
@@ -102,7 +102,7 @@ class ThreadItemConverter:
# If only text and no attachments, use text parameter for simplicity
if text_content.strip() and not data_contents:
user_message = Message(role="user", contents=[text_content.strip()])
user_message = Message(role="user", text=text_content.strip())
else:
# Build contents list with both text and attachments
contents: list[Content] = []
@@ -116,7 +116,7 @@ class ThreadItemConverter:
if item.quoted_text and is_last_message:
quoted_context = Message(
role="user",
contents=[f"The user is referring to this in particular:\n{item.quoted_text}"],
text=f"The user is referring to this in particular:\n{item.quoted_text}",
)
# Prepend quoted context before the main message
messages.insert(0, quoted_context)
@@ -211,9 +211,9 @@ class ThreadItemConverter:
content="User's email: user@example.com",
)
message = converter.hidden_context_to_input(hidden_item)
# Returns: Message(role=SYSTEM, contents=["<HIDDEN_CONTEXT>User's email: ...</HIDDEN_CONTEXT>"])
# Returns: Message(role=SYSTEM, text="<HIDDEN_CONTEXT>User's email: ...</HIDDEN_CONTEXT>")
"""
return Message(role="system", contents=[f"<HIDDEN_CONTEXT>{item.content}</HIDDEN_CONTEXT>"])
return Message(role="system", text=f"<HIDDEN_CONTEXT>{item.content}</HIDDEN_CONTEXT>")
def tag_to_message_content(self, tag: UserMessageTagContent) -> Content:
"""Convert a ChatKit tag (@-mention) to Agent Framework content.
@@ -292,7 +292,7 @@ class ThreadItemConverter:
f"A message was displayed to the user that the following task was performed:\n<Task>\n{task_text}\n</Task>"
)
return Message(role="user", contents=[text])
return Message(role="user", text=text)
def workflow_to_input(self, item: WorkflowItem) -> Message | list[Message] | None:
"""Convert a ChatKit WorkflowItem to Agent Framework Message(s).
@@ -347,7 +347,7 @@ class ThreadItemConverter:
f"<Task>\n{task_text}\n</Task>"
)
messages.append(Message(role="user", contents=[text]))
messages.append(Message(role="user", text=text))
return messages if messages else None
@@ -389,7 +389,7 @@ class ThreadItemConverter:
try:
widget_json = item.widget.model_dump_json(exclude_unset=True, exclude_none=True)
text = f"The following graphical UI widget (id: {item.id}) was displayed to the user:{widget_json}"
return Message(role="user", contents=[text])
return Message(role="user", text=text)
except Exception:
# If JSON serialization fails, skip the widget
return None
@@ -415,7 +415,7 @@ class ThreadItemConverter:
if not text_parts:
return None
return Message(role="assistant", contents=["".join(text_parts)])
return Message(role="assistant", text="".join(text_parts))
async def client_tool_call_to_input(self, item: ClientToolCallItem) -> Message | list[Message] | None:
"""Convert a ChatKit ClientToolCallItem to Agent Framework Message(s).
+2 -2
View File
@@ -4,7 +4,7 @@ description = "OpenAI ChatKit integration for Microsoft Agent Framework."
authors = [{ name = "Microsoft", email = "af-support@microsoft.com"}]
readme = "README.md"
requires-python = ">=3.10"
version = "1.0.0b260402"
version = "1.0.0b260330"
license-files = ["LICENSE"]
urls.homepage = "https://aka.ms/agent-framework"
urls.source = "https://github.com/microsoft/agent-framework/tree/main/python"
@@ -22,7 +22,7 @@ classifiers = [
"Typing :: Typed",
]
dependencies = [
"agent-framework-core>=1.0.0,<2",
"agent-framework-core>=1.0.0rc6",
"openai-chatkit>=1.4.1,<2.0.0",
]
+2 -2
View File
@@ -4,7 +4,7 @@ description = "Claude Agent SDK integration for Microsoft Agent Framework."
authors = [{ name = "Microsoft", email = "af-support@microsoft.com"}]
readme = "README.md"
requires-python = ">=3.10"
version = "1.0.0b260402"
version = "1.0.0b260330"
license-files = ["LICENSE"]
urls.homepage = "https://aka.ms/agent-framework"
urls.source = "https://github.com/microsoft/agent-framework/tree/main/python"
@@ -23,7 +23,7 @@ classifiers = [
"Typing :: Typed",
]
dependencies = [
"agent-framework-core>=1.0.0,<2",
"agent-framework-core>=1.0.0rc6",
"claude-agent-sdk>=0.1.36,<0.1.49",
]
+2 -2
View File
@@ -4,7 +4,7 @@ description = "Copilot Studio integration for Microsoft Agent Framework."
authors = [{ name = "Microsoft", email = "af-support@microsoft.com"}]
readme = "README.md"
requires-python = ">=3.10"
version = "1.0.0b260402"
version = "1.0.0b260330"
license-files = ["LICENSE"]
urls.homepage = "https://aka.ms/agent-framework"
urls.source = "https://github.com/microsoft/agent-framework/tree/main/python"
@@ -23,7 +23,7 @@ classifiers = [
"Typing :: Typed",
]
dependencies = [
"agent-framework-core>=1.0.0,<2",
"agent-framework-core>=1.0.0rc6",
"microsoft-agents-copilotstudio-client>=0.3.1,<0.3.2",
]
+1 -1
View File
@@ -136,7 +136,7 @@ from agent_framework import BaseChatClient, ChatResponse, Message
class MyClient(BaseChatClient):
async def _inner_get_response(self, *, messages, options, **kwargs) -> ChatResponse:
# Call your LLM here
return ChatResponse(messages=[Message(role="assistant", contents=["Hi!"])])
return ChatResponse(messages=[Message(role="assistant", text="Hi!")])
async def _inner_get_streaming_response(self, *, messages, options, **kwargs):
yield ChatResponseUpdate(...)
+3 -3
View File
@@ -13,11 +13,11 @@ Highlights
## Quick Install
```bash
pip install agent-framework-core
pip install agent-framework-core --pre
# Optional: Add Azure AI Foundry integration
pip install agent-framework-foundry
pip install agent-framework-foundry --pre
# Optional: Add OpenAI integration
pip install agent-framework-openai
pip install agent-framework-openai --pre
```
Supported Platforms:
@@ -102,6 +102,8 @@ from ._middleware import (
)
from ._sessions import (
AgentSession,
BaseContextProvider, # type: ignore[reportDeprecated]
BaseHistoryProvider, # type: ignore[reportDeprecated]
ContextProvider,
HistoryProvider,
InMemoryHistoryProvider,
@@ -278,7 +280,9 @@ __all__ = [
"Annotation",
"BaseAgent",
"BaseChatClient",
"BaseContextProvider",
"BaseEmbeddingClient",
"BaseHistoryProvider",
"Case",
"CharacterEstimatorTokenizer",
"ChatAndFunctionMiddlewareTypes",
@@ -253,8 +253,7 @@ class BaseChatClient(SerializationMixin, ABC, Generic[OptionsCoT]):
else:
# Non-streaming implementation
return ChatResponse(
messages=[Message(role="assistant", contents=["Hello!"])],
response_id="custom-response",
messages=[Message(role="assistant", text="Hello!")], response_id="custom-response"
)
@@ -262,9 +261,9 @@ class BaseChatClient(SerializationMixin, ABC, Generic[OptionsCoT]):
client = CustomChatClient()
# Use the client to get responses
response = await client.get_response([Message(role="user", contents=["Hello, how are you?"])])
response = await client.get_response([Message(role="user", text="Hello, how are you?")])
# Or stream responses
async for update in client.get_response([Message(role="user", contents=["Hello!"])], stream=True):
async for update in client.get_response([Message(role="user", text="Hello!")], stream=True):
print(update)
"""
@@ -877,7 +877,7 @@ class ToolResultCompactionStrategy:
insertion_index = starts.get(group_id, 0)
summary_message = Message(
role="assistant",
contents=[summary_text],
text=summary_text,
message_id=summary_id,
additional_properties={
GROUP_ANNOTATION_KEY: summary_annotation,
@@ -1015,10 +1015,10 @@ class SummarizationStrategy:
try:
summary_response: ChatResponse[None] = await self.client.get_response(
[
Message(role="system", contents=[self.prompt]),
Message(role="system", text=self.prompt),
Message(
role="user",
contents=[_format_messages_for_summary(messages_to_summarize)],
text=_format_messages_for_summary(messages_to_summarize),
),
],
stream=False,
@@ -1044,7 +1044,7 @@ class SummarizationStrategy:
summary_message = Message(
role="assistant",
contents=[summary_text],
text=summary_text,
message_id=summary_id,
additional_properties={
GROUP_ANNOTATION_KEY: summary_annotation,

Some files were not shown because too many files have changed in this diff Show More