diff --git a/dotnet/.github/skills/project-structure/SKILL.md b/dotnet/.github/skills/project-structure/SKILL.md
index 01dcafabf8..6ec9476039 100644
--- a/dotnet/.github/skills/project-structure/SKILL.md
+++ b/dotnet/.github/skills/project-structure/SKILL.md
@@ -12,8 +12,8 @@ dotnet/
│ ├── Microsoft.Agents.AI.Abstractions/ # Core AI agent abstractions
│ ├── Microsoft.Agents.AI.A2A/ # Agent-to-Agent (A2A) provider
│ ├── Microsoft.Agents.AI.OpenAI/ # OpenAI provider
-│ ├── Microsoft.Agents.AI.AzureAI/ # Azure AI Foundry Agents (v2) provider
-│ ├── Microsoft.Agents.AI.AzureAI.Persistent/ # Legacy Azure AI Foundry Agents (v1) provider
+│ ├── Microsoft.Agents.AI.Foundry/ # Microsoft Foundry Agents (v2) provider
+│ ├── Microsoft.Agents.AI.AzureAI.Persistent/ # Legacy Microsoft Foundry Agents (v1) provider
│ ├── Microsoft.Agents.AI.Anthropic/ # Anthropic provider
│ ├── Microsoft.Agents.AI.Workflows/ # Workflow orchestration
│ └── ... # Other packages
diff --git a/dotnet/agent-framework-dotnet.slnx b/dotnet/agent-framework-dotnet.slnx
index f16a580519..748bed8017 100644
--- a/dotnet/agent-framework-dotnet.slnx
+++ b/dotnet/agent-framework-dotnet.slnx
@@ -478,13 +478,13 @@
-
+
-
+
@@ -495,7 +495,7 @@
-
+
@@ -506,11 +506,11 @@
-
+
-
+
@@ -526,12 +526,12 @@
-
+
-
+
diff --git a/dotnet/agent-framework-release.slnf b/dotnet/agent-framework-release.slnf
index 1c8f477b16..98f9dcf887 100644
--- a/dotnet/agent-framework-release.slnf
+++ b/dotnet/agent-framework-release.slnf
@@ -8,13 +8,13 @@
"src\\Microsoft.Agents.AI.Anthropic\\Microsoft.Agents.AI.Anthropic.csproj",
"src\\Microsoft.Agents.AI.GitHub.Copilot\\Microsoft.Agents.AI.GitHub.Copilot.csproj",
"src\\Microsoft.Agents.AI.AzureAI.Persistent\\Microsoft.Agents.AI.AzureAI.Persistent.csproj",
- "src\\Microsoft.Agents.AI.AzureAI\\Microsoft.Agents.AI.AzureAI.csproj",
+ "src\\Microsoft.Agents.AI.Foundry\\Microsoft.Agents.AI.Foundry.csproj",
"src\\Microsoft.Agents.AI.CopilotStudio\\Microsoft.Agents.AI.CopilotStudio.csproj",
"src\\Microsoft.Agents.AI.CosmosNoSql\\Microsoft.Agents.AI.CosmosNoSql.csproj",
"src\\Microsoft.Agents.AI.Declarative\\Microsoft.Agents.AI.Declarative.csproj",
"src\\Microsoft.Agents.AI.DevUI\\Microsoft.Agents.AI.DevUI.csproj",
"src\\Microsoft.Agents.AI.DurableTask\\Microsoft.Agents.AI.DurableTask.csproj",
- "src\\Microsoft.Agents.AI.FoundryMemory\\Microsoft.Agents.AI.FoundryMemory.csproj",
+
"src\\Microsoft.Agents.AI.Hosting.A2A.AspNetCore\\Microsoft.Agents.AI.Hosting.A2A.AspNetCore.csproj",
"src\\Microsoft.Agents.AI.Hosting.A2A\\Microsoft.Agents.AI.Hosting.A2A.csproj",
"src\\Microsoft.Agents.AI.Hosting.AGUI.AspNetCore\\Microsoft.Agents.AI.Hosting.AGUI.AspNetCore.csproj",
@@ -24,7 +24,7 @@
"src\\Microsoft.Agents.AI.Mem0\\Microsoft.Agents.AI.Mem0.csproj",
"src\\Microsoft.Agents.AI.OpenAI\\Microsoft.Agents.AI.OpenAI.csproj",
"src\\Microsoft.Agents.AI.Purview\\Microsoft.Agents.AI.Purview.csproj",
- "src\\Microsoft.Agents.AI.Workflows.Declarative.AzureAI\\Microsoft.Agents.AI.Workflows.Declarative.AzureAI.csproj",
+ "src\\Microsoft.Agents.AI.Workflows.Declarative.Foundry\\Microsoft.Agents.AI.Workflows.Declarative.Foundry.csproj",
"src\\Microsoft.Agents.AI.Workflows.Declarative\\Microsoft.Agents.AI.Workflows.Declarative.csproj",
"src\\Microsoft.Agents.AI.Workflows.Generators\\Microsoft.Agents.AI.Workflows.Generators.csproj",
"src\\Microsoft.Agents.AI.Workflows\\Microsoft.Agents.AI.Workflows.csproj",
diff --git a/dotnet/samples/02-agents/AgentProviders/Agent_With_AzureAIProject/Agent_With_AzureAIProject.csproj b/dotnet/samples/02-agents/AgentProviders/Agent_With_AzureAIProject/Agent_With_AzureAIProject.csproj
index a8deaa57b5..562ce0c37e 100644
--- a/dotnet/samples/02-agents/AgentProviders/Agent_With_AzureAIProject/Agent_With_AzureAIProject.csproj
+++ b/dotnet/samples/02-agents/AgentProviders/Agent_With_AzureAIProject/Agent_With_AzureAIProject.csproj
@@ -15,7 +15,7 @@
-
+
diff --git a/dotnet/samples/02-agents/AgentProviders/Agent_With_AzureAIProject/Program.cs b/dotnet/samples/02-agents/AgentProviders/Agent_With_AzureAIProject/Program.cs
index 8265623904..355f4e1380 100644
--- a/dotnet/samples/02-agents/AgentProviders/Agent_With_AzureAIProject/Program.cs
+++ b/dotnet/samples/02-agents/AgentProviders/Agent_With_AzureAIProject/Program.cs
@@ -6,7 +6,7 @@ using Azure.AI.Projects;
using Azure.AI.Projects.Agents;
using Azure.Identity;
using Microsoft.Agents.AI;
-using Microsoft.Agents.AI.AzureAI;
+using Microsoft.Agents.AI.Foundry;
var endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set.");
var deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-4o-mini";
diff --git a/dotnet/samples/02-agents/AgentWithMemory/AgentWithMemory_Step04_MemoryUsingFoundry/AgentWithMemory_Step04_MemoryUsingFoundry.csproj b/dotnet/samples/02-agents/AgentWithMemory/AgentWithMemory_Step04_MemoryUsingFoundry/AgentWithMemory_Step04_MemoryUsingFoundry.csproj
index 0b6c06a5a8..4c83380f90 100644
--- a/dotnet/samples/02-agents/AgentWithMemory/AgentWithMemory_Step04_MemoryUsingFoundry/AgentWithMemory_Step04_MemoryUsingFoundry.csproj
+++ b/dotnet/samples/02-agents/AgentWithMemory/AgentWithMemory_Step04_MemoryUsingFoundry/AgentWithMemory_Step04_MemoryUsingFoundry.csproj
@@ -1,4 +1,4 @@
-
+
Exe
@@ -14,8 +14,7 @@
-
-
+
diff --git a/dotnet/samples/02-agents/AgentWithMemory/AgentWithMemory_Step04_MemoryUsingFoundry/Program.cs b/dotnet/samples/02-agents/AgentWithMemory/AgentWithMemory_Step04_MemoryUsingFoundry/Program.cs
index 9a8c643bb9..1132e9ef1d 100644
--- a/dotnet/samples/02-agents/AgentWithMemory/AgentWithMemory_Step04_MemoryUsingFoundry/Program.cs
+++ b/dotnet/samples/02-agents/AgentWithMemory/AgentWithMemory_Step04_MemoryUsingFoundry/Program.cs
@@ -11,8 +11,7 @@ using System.Text.Json;
using Azure.AI.Projects;
using Azure.Identity;
using Microsoft.Agents.AI;
-using Microsoft.Agents.AI.AzureAI;
-using Microsoft.Agents.AI.FoundryMemory;
+using Microsoft.Agents.AI.Foundry;
string foundryEndpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set.");
string memoryStoreName = Environment.GetEnvironmentVariable("AZURE_AI_MEMORY_STORE_ID") ?? "memory-store-sample";
@@ -37,7 +36,7 @@ FoundryMemoryProvider memoryProvider = new(
memoryStoreName,
stateInitializer: _ => new(new FoundryMemoryProviderScope("sample-user-123")));
-FoundryAgent agent = projectClient.AsAIAgent(
+ChatClientAgent agent = projectClient.AsAIAgent(
new ChatClientAgentOptions()
{
Name = "TravelAssistantWithFoundryMemory",
diff --git a/dotnet/samples/02-agents/AgentWithRAG/AgentWithRAG_Step04_FoundryServiceRAG/AgentWithRAG_Step04_FoundryServiceRAG.csproj b/dotnet/samples/02-agents/AgentWithRAG/AgentWithRAG_Step04_FoundryServiceRAG/AgentWithRAG_Step04_FoundryServiceRAG.csproj
index d90e1c394b..6a2bd7618c 100644
--- a/dotnet/samples/02-agents/AgentWithRAG/AgentWithRAG_Step04_FoundryServiceRAG/AgentWithRAG_Step04_FoundryServiceRAG.csproj
+++ b/dotnet/samples/02-agents/AgentWithRAG/AgentWithRAG_Step04_FoundryServiceRAG/AgentWithRAG_Step04_FoundryServiceRAG.csproj
@@ -14,7 +14,7 @@
-
+
diff --git a/dotnet/samples/02-agents/AgentWithRAG/AgentWithRAG_Step04_FoundryServiceRAG/Program.cs b/dotnet/samples/02-agents/AgentWithRAG/AgentWithRAG_Step04_FoundryServiceRAG/Program.cs
index e049618a72..7eb5bd5a39 100644
--- a/dotnet/samples/02-agents/AgentWithRAG/AgentWithRAG_Step04_FoundryServiceRAG/Program.cs
+++ b/dotnet/samples/02-agents/AgentWithRAG/AgentWithRAG_Step04_FoundryServiceRAG/Program.cs
@@ -7,7 +7,7 @@ using Azure.AI.Projects;
using Azure.AI.Projects.Agents;
using Azure.Identity;
using Microsoft.Agents.AI;
-using Microsoft.Agents.AI.AzureAI;
+using Microsoft.Agents.AI.Foundry;
using OpenAI;
using OpenAI.Files;
using OpenAI.Responses;
diff --git a/dotnet/samples/02-agents/Agents/Agent_Step07_AsMcpTool/Agent_Step07_AsMcpTool.csproj b/dotnet/samples/02-agents/Agents/Agent_Step07_AsMcpTool/Agent_Step07_AsMcpTool.csproj
index 5239225499..a7df53251d 100644
--- a/dotnet/samples/02-agents/Agents/Agent_Step07_AsMcpTool/Agent_Step07_AsMcpTool.csproj
+++ b/dotnet/samples/02-agents/Agents/Agent_Step07_AsMcpTool/Agent_Step07_AsMcpTool.csproj
@@ -17,7 +17,7 @@
-
+
diff --git a/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step00_FoundryAgentLifecycle/Agent_Step00_FoundryAgentLifecycle.csproj b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step00_FoundryAgentLifecycle/Agent_Step00_FoundryAgentLifecycle.csproj
index d861331d9f..4c83380f90 100644
--- a/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step00_FoundryAgentLifecycle/Agent_Step00_FoundryAgentLifecycle.csproj
+++ b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step00_FoundryAgentLifecycle/Agent_Step00_FoundryAgentLifecycle.csproj
@@ -14,7 +14,7 @@
-
+
diff --git a/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step00_FoundryAgentLifecycle/Program.cs b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step00_FoundryAgentLifecycle/Program.cs
index 691af1fc16..16c219e144 100644
--- a/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step00_FoundryAgentLifecycle/Program.cs
+++ b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step00_FoundryAgentLifecycle/Program.cs
@@ -7,7 +7,7 @@
using Azure.AI.Projects;
using Azure.AI.Projects.Agents;
using Azure.Identity;
-using Microsoft.Agents.AI.AzureAI;
+using Microsoft.Agents.AI.Foundry;
string endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set.");
string deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-4o-mini";
diff --git a/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step01_Basics/Agent_Step01_Basics.csproj b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step01_Basics/Agent_Step01_Basics.csproj
index 7367c1d2f8..6b4cb8f43e 100644
--- a/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step01_Basics/Agent_Step01_Basics.csproj
+++ b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step01_Basics/Agent_Step01_Basics.csproj
@@ -1,4 +1,4 @@
-
+
Exe
@@ -9,7 +9,7 @@
-
+
diff --git a/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step02.1_MultiturnConversation/Agent_Step02.1_MultiturnConversation.csproj b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step02.1_MultiturnConversation/Agent_Step02.1_MultiturnConversation.csproj
index 5e73fd236a..6b4cb8f43e 100644
--- a/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step02.1_MultiturnConversation/Agent_Step02.1_MultiturnConversation.csproj
+++ b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step02.1_MultiturnConversation/Agent_Step02.1_MultiturnConversation.csproj
@@ -9,7 +9,7 @@
-
+
diff --git a/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step02.2_MultiturnWithServerConversations/Agent_Step02.2_MultiturnWithServerConversations.csproj b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step02.2_MultiturnWithServerConversations/Agent_Step02.2_MultiturnWithServerConversations.csproj
index 7367c1d2f8..6b4cb8f43e 100644
--- a/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step02.2_MultiturnWithServerConversations/Agent_Step02.2_MultiturnWithServerConversations.csproj
+++ b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step02.2_MultiturnWithServerConversations/Agent_Step02.2_MultiturnWithServerConversations.csproj
@@ -1,4 +1,4 @@
-
+
Exe
@@ -9,7 +9,7 @@
-
+
diff --git a/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step02.2_MultiturnWithServerConversations/Program.cs b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step02.2_MultiturnWithServerConversations/Program.cs
index 7a66555c18..6057d71895 100644
--- a/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step02.2_MultiturnWithServerConversations/Program.cs
+++ b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step02.2_MultiturnWithServerConversations/Program.cs
@@ -4,10 +4,10 @@
// Server-side conversations persist on the Foundry service and are visible in the Foundry Project UI.
// Use this when you need conversation history to be stored and accessible server-side.
+using Azure.AI.Extensions.OpenAI;
using Azure.AI.Projects;
using Azure.Identity;
using Microsoft.Agents.AI;
-using Microsoft.Agents.AI.AzureAI;
string endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set.");
string deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-4o-mini";
@@ -15,12 +15,20 @@ string deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLO
// WARNING: DefaultAzureCredential is convenient for development but requires careful consideration in production.
// In production, consider using a specific credential (e.g., ManagedIdentityCredential) to avoid
// latency issues, unintended credential probing, and potential security risks from fallback mechanisms.
-FoundryAgent agent = new AIProjectClient(new Uri(endpoint), new DefaultAzureCredential())
+AIProjectClient aiProjectClient = new(new Uri(endpoint), new DefaultAzureCredential());
+
+ChatClientAgent agent = aiProjectClient
.AsAIAgent(deploymentName, instructions: "You are good at telling jokes.", name: "JokerAgent");
+ProjectConversationsClient conversationsClient = aiProjectClient
+ .GetProjectOpenAIClient()
+ .GetProjectConversationsClient();
+
+ProjectConversation conversation = (await conversationsClient.CreateProjectConversationAsync().ConfigureAwait(false)).Value;
+
// CreateConversationSessionAsync creates a server-side ProjectConversation
// that persists on the Foundry service and is visible in the Foundry Project UI.
-AgentSession session = await agent.CreateConversationSessionAsync();
+AgentSession session = await agent.CreateSessionAsync(conversation.Id);
Console.WriteLine(await agent.RunAsync("Tell me a joke about a pirate.", session));
Console.WriteLine(await agent.RunAsync("Now add some emojis to the joke and tell it in the voice of a pirate's parrot.", session));
diff --git a/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step03_UsingFunctionTools/Agent_Step03_UsingFunctionTools.csproj b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step03_UsingFunctionTools/Agent_Step03_UsingFunctionTools.csproj
index 7367c1d2f8..6b4cb8f43e 100644
--- a/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step03_UsingFunctionTools/Agent_Step03_UsingFunctionTools.csproj
+++ b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step03_UsingFunctionTools/Agent_Step03_UsingFunctionTools.csproj
@@ -1,4 +1,4 @@
-
+
Exe
@@ -9,7 +9,7 @@
-
+
diff --git a/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step04_UsingFunctionToolsWithApprovals/Agent_Step04_UsingFunctionToolsWithApprovals.csproj b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step04_UsingFunctionToolsWithApprovals/Agent_Step04_UsingFunctionToolsWithApprovals.csproj
index 7367c1d2f8..6b4cb8f43e 100644
--- a/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step04_UsingFunctionToolsWithApprovals/Agent_Step04_UsingFunctionToolsWithApprovals.csproj
+++ b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step04_UsingFunctionToolsWithApprovals/Agent_Step04_UsingFunctionToolsWithApprovals.csproj
@@ -1,4 +1,4 @@
-
+
Exe
@@ -9,7 +9,7 @@
-
+
diff --git a/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step05_StructuredOutput/Agent_Step05_StructuredOutput.csproj b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step05_StructuredOutput/Agent_Step05_StructuredOutput.csproj
index 7367c1d2f8..6b4cb8f43e 100644
--- a/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step05_StructuredOutput/Agent_Step05_StructuredOutput.csproj
+++ b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step05_StructuredOutput/Agent_Step05_StructuredOutput.csproj
@@ -1,4 +1,4 @@
-
+
Exe
@@ -9,7 +9,7 @@
-
+
diff --git a/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step06_PersistedConversations/Agent_Step06_PersistedConversations.csproj b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step06_PersistedConversations/Agent_Step06_PersistedConversations.csproj
index 7367c1d2f8..6b4cb8f43e 100644
--- a/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step06_PersistedConversations/Agent_Step06_PersistedConversations.csproj
+++ b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step06_PersistedConversations/Agent_Step06_PersistedConversations.csproj
@@ -1,4 +1,4 @@
-
+
Exe
@@ -9,7 +9,7 @@
-
+
diff --git a/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step07_Observability/Agent_Step07_Observability.csproj b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step07_Observability/Agent_Step07_Observability.csproj
index 1189939bc0..60190545a1 100644
--- a/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step07_Observability/Agent_Step07_Observability.csproj
+++ b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step07_Observability/Agent_Step07_Observability.csproj
@@ -1,4 +1,4 @@
-
+
Exe
@@ -15,7 +15,7 @@
-
+
diff --git a/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step08_DependencyInjection/Agent_Step08_DependencyInjection.csproj b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step08_DependencyInjection/Agent_Step08_DependencyInjection.csproj
index 72af634725..fd54882035 100644
--- a/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step08_DependencyInjection/Agent_Step08_DependencyInjection.csproj
+++ b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step08_DependencyInjection/Agent_Step08_DependencyInjection.csproj
@@ -1,4 +1,4 @@
-
+
Exe
@@ -15,7 +15,7 @@
-
+
diff --git a/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step09_UsingMcpClientAsTools/Agent_Step09_UsingMcpClientAsTools.csproj b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step09_UsingMcpClientAsTools/Agent_Step09_UsingMcpClientAsTools.csproj
index 96cdf948fe..1f596a94a1 100644
--- a/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step09_UsingMcpClientAsTools/Agent_Step09_UsingMcpClientAsTools.csproj
+++ b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step09_UsingMcpClientAsTools/Agent_Step09_UsingMcpClientAsTools.csproj
@@ -1,4 +1,4 @@
-
+
Exe
@@ -15,7 +15,7 @@
-
+
diff --git a/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step10_UsingImages/Agent_Step10_UsingImages.csproj b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step10_UsingImages/Agent_Step10_UsingImages.csproj
index 6064cf9334..c2bb03bffd 100644
--- a/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step10_UsingImages/Agent_Step10_UsingImages.csproj
+++ b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step10_UsingImages/Agent_Step10_UsingImages.csproj
@@ -1,4 +1,4 @@
-
+
Exe
@@ -9,7 +9,7 @@
-
+
diff --git a/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step11_AsFunctionTool/Agent_Step11_AsFunctionTool.csproj b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step11_AsFunctionTool/Agent_Step11_AsFunctionTool.csproj
index 7367c1d2f8..6b4cb8f43e 100644
--- a/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step11_AsFunctionTool/Agent_Step11_AsFunctionTool.csproj
+++ b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step11_AsFunctionTool/Agent_Step11_AsFunctionTool.csproj
@@ -1,4 +1,4 @@
-
+
Exe
@@ -9,7 +9,7 @@
-
+
diff --git a/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step12_Middleware/Agent_Step12_Middleware.csproj b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step12_Middleware/Agent_Step12_Middleware.csproj
index b30baccd54..d1cfe0da4a 100644
--- a/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step12_Middleware/Agent_Step12_Middleware.csproj
+++ b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step12_Middleware/Agent_Step12_Middleware.csproj
@@ -1,4 +1,4 @@
-
+
Exe
@@ -13,7 +13,7 @@
-
+
diff --git a/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step13_Plugins/Agent_Step13_Plugins.csproj b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step13_Plugins/Agent_Step13_Plugins.csproj
index 1f5e37c1a3..5dfa730c8a 100644
--- a/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step13_Plugins/Agent_Step13_Plugins.csproj
+++ b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step13_Plugins/Agent_Step13_Plugins.csproj
@@ -1,4 +1,4 @@
-
+
Exe
@@ -15,7 +15,7 @@
-
+
diff --git a/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step14_CodeInterpreter/Agent_Step14_CodeInterpreter.csproj b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step14_CodeInterpreter/Agent_Step14_CodeInterpreter.csproj
index e11688b6ba..7d91cedca5 100644
--- a/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step14_CodeInterpreter/Agent_Step14_CodeInterpreter.csproj
+++ b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step14_CodeInterpreter/Agent_Step14_CodeInterpreter.csproj
@@ -1,4 +1,4 @@
-
+
Exe
@@ -13,7 +13,7 @@
-
+
diff --git a/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step15_ComputerUse/Agent_Step15_ComputerUse.csproj b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step15_ComputerUse/Agent_Step15_ComputerUse.csproj
index f739f56123..7bd94bd716 100644
--- a/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step15_ComputerUse/Agent_Step15_ComputerUse.csproj
+++ b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step15_ComputerUse/Agent_Step15_ComputerUse.csproj
@@ -1,4 +1,4 @@
-
+
Exe
@@ -15,7 +15,7 @@
-
+
diff --git a/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step15_ComputerUse/Program.cs b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step15_ComputerUse/Program.cs
index 22f03e27b3..d79c1122b7 100644
--- a/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step15_ComputerUse/Program.cs
+++ b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step15_ComputerUse/Program.cs
@@ -5,7 +5,7 @@
using Azure.AI.Projects;
using Azure.Identity;
using Microsoft.Agents.AI;
-using Microsoft.Agents.AI.AzureAI;
+using Microsoft.Agents.AI.Foundry;
using Microsoft.Extensions.AI;
using OpenAI.Responses;
diff --git a/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step16_FileSearch/Agent_Step16_FileSearch.csproj b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step16_FileSearch/Agent_Step16_FileSearch.csproj
index e11688b6ba..7d91cedca5 100644
--- a/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step16_FileSearch/Agent_Step16_FileSearch.csproj
+++ b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step16_FileSearch/Agent_Step16_FileSearch.csproj
@@ -1,4 +1,4 @@
-
+
Exe
@@ -13,7 +13,7 @@
-
+
diff --git a/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step17_OpenAPITools/Agent_Step17_OpenAPITools.csproj b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step17_OpenAPITools/Agent_Step17_OpenAPITools.csproj
index 4602e9c9e0..8671b3027c 100644
--- a/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step17_OpenAPITools/Agent_Step17_OpenAPITools.csproj
+++ b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step17_OpenAPITools/Agent_Step17_OpenAPITools.csproj
@@ -1,4 +1,4 @@
-
+
Exe
@@ -14,7 +14,7 @@
-
+
diff --git a/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step17_OpenAPITools/Program.cs b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step17_OpenAPITools/Program.cs
index 5cc2720dd9..1968b89ca1 100644
--- a/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step17_OpenAPITools/Program.cs
+++ b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step17_OpenAPITools/Program.cs
@@ -6,7 +6,7 @@ using Azure.AI.Projects;
using Azure.AI.Projects.Agents;
using Azure.Identity;
using Microsoft.Agents.AI;
-using Microsoft.Agents.AI.AzureAI;
+using Microsoft.Agents.AI.Foundry;
using Microsoft.Extensions.AI;
string endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set.");
diff --git a/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step18_BingCustomSearch/Agent_Step18_BingCustomSearch.csproj b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step18_BingCustomSearch/Agent_Step18_BingCustomSearch.csproj
index e11688b6ba..7d91cedca5 100644
--- a/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step18_BingCustomSearch/Agent_Step18_BingCustomSearch.csproj
+++ b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step18_BingCustomSearch/Agent_Step18_BingCustomSearch.csproj
@@ -1,4 +1,4 @@
-
+
Exe
@@ -13,7 +13,7 @@
-
+
diff --git a/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step18_BingCustomSearch/Program.cs b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step18_BingCustomSearch/Program.cs
index 4ab548403d..e244bb3cb0 100644
--- a/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step18_BingCustomSearch/Program.cs
+++ b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step18_BingCustomSearch/Program.cs
@@ -6,7 +6,7 @@ using Azure.AI.Projects;
using Azure.AI.Projects.Agents;
using Azure.Identity;
using Microsoft.Agents.AI;
-using Microsoft.Agents.AI.AzureAI;
+using Microsoft.Agents.AI.Foundry;
string connectionId = Environment.GetEnvironmentVariable("AZURE_AI_CUSTOM_SEARCH_CONNECTION_ID") ?? throw new InvalidOperationException("AZURE_AI_CUSTOM_SEARCH_CONNECTION_ID is not set.");
string instanceName = Environment.GetEnvironmentVariable("AZURE_AI_CUSTOM_SEARCH_INSTANCE_NAME") ?? throw new InvalidOperationException("AZURE_AI_CUSTOM_SEARCH_INSTANCE_NAME is not set.");
diff --git a/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step19_SharePoint/Agent_Step19_SharePoint.csproj b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step19_SharePoint/Agent_Step19_SharePoint.csproj
index e11688b6ba..7d91cedca5 100644
--- a/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step19_SharePoint/Agent_Step19_SharePoint.csproj
+++ b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step19_SharePoint/Agent_Step19_SharePoint.csproj
@@ -1,4 +1,4 @@
-
+
Exe
@@ -13,7 +13,7 @@
-
+
diff --git a/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step19_SharePoint/Program.cs b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step19_SharePoint/Program.cs
index aafca6b8bd..a5f8bf845a 100644
--- a/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step19_SharePoint/Program.cs
+++ b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step19_SharePoint/Program.cs
@@ -6,7 +6,7 @@ using Azure.AI.Projects;
using Azure.AI.Projects.Agents;
using Azure.Identity;
using Microsoft.Agents.AI;
-using Microsoft.Agents.AI.AzureAI;
+using Microsoft.Agents.AI.Foundry;
string sharepointConnectionId = Environment.GetEnvironmentVariable("SHAREPOINT_PROJECT_CONNECTION_ID") ?? throw new InvalidOperationException("SHAREPOINT_PROJECT_CONNECTION_ID is not set.");
diff --git a/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step20_MicrosoftFabric/Agent_Step20_MicrosoftFabric.csproj b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step20_MicrosoftFabric/Agent_Step20_MicrosoftFabric.csproj
index e11688b6ba..7d91cedca5 100644
--- a/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step20_MicrosoftFabric/Agent_Step20_MicrosoftFabric.csproj
+++ b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step20_MicrosoftFabric/Agent_Step20_MicrosoftFabric.csproj
@@ -1,4 +1,4 @@
-
+
Exe
@@ -13,7 +13,7 @@
-
+
diff --git a/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step20_MicrosoftFabric/Program.cs b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step20_MicrosoftFabric/Program.cs
index 7c49afa7ee..69954b9483 100644
--- a/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step20_MicrosoftFabric/Program.cs
+++ b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step20_MicrosoftFabric/Program.cs
@@ -6,7 +6,7 @@ using Azure.AI.Projects;
using Azure.AI.Projects.Agents;
using Azure.Identity;
using Microsoft.Agents.AI;
-using Microsoft.Agents.AI.AzureAI;
+using Microsoft.Agents.AI.Foundry;
string fabricConnectionId = Environment.GetEnvironmentVariable("FABRIC_PROJECT_CONNECTION_ID") ?? throw new InvalidOperationException("FABRIC_PROJECT_CONNECTION_ID is not set.");
diff --git a/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step21_WebSearch/Agent_Step21_WebSearch.csproj b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step21_WebSearch/Agent_Step21_WebSearch.csproj
index e11688b6ba..7d91cedca5 100644
--- a/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step21_WebSearch/Agent_Step21_WebSearch.csproj
+++ b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step21_WebSearch/Agent_Step21_WebSearch.csproj
@@ -1,4 +1,4 @@
-
+
Exe
@@ -13,7 +13,7 @@
-
+
diff --git a/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step22_MemorySearch/Agent_Step22_MemorySearch.csproj b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step22_MemorySearch/Agent_Step22_MemorySearch.csproj
index 4602e9c9e0..8671b3027c 100644
--- a/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step22_MemorySearch/Agent_Step22_MemorySearch.csproj
+++ b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step22_MemorySearch/Agent_Step22_MemorySearch.csproj
@@ -1,4 +1,4 @@
-
+
Exe
@@ -14,7 +14,7 @@
-
+
diff --git a/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step22_MemorySearch/Program.cs b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step22_MemorySearch/Program.cs
index 9e1a902f29..f2b447e52e 100644
--- a/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step22_MemorySearch/Program.cs
+++ b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step22_MemorySearch/Program.cs
@@ -9,7 +9,7 @@ using Azure.AI.Projects;
using Azure.AI.Projects.Agents;
using Azure.Identity;
using Microsoft.Agents.AI;
-using Microsoft.Agents.AI.AzureAI;
+using Microsoft.Agents.AI.Foundry;
using Microsoft.Extensions.AI;
using OpenAI.Responses;
diff --git a/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step23_LocalMCP/Agent_Step23_LocalMCP.csproj b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step23_LocalMCP/Agent_Step23_LocalMCP.csproj
index e51f57c439..53b8a3af34 100644
--- a/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step23_LocalMCP/Agent_Step23_LocalMCP.csproj
+++ b/dotnet/samples/02-agents/AgentsWithFoundry/Agent_Step23_LocalMCP/Agent_Step23_LocalMCP.csproj
@@ -1,4 +1,4 @@
-
+
Exe
@@ -14,7 +14,7 @@
-
+
diff --git a/dotnet/samples/02-agents/ModelContextProtocol/FoundryAgent_Hosted_MCP/FoundryAgent_Hosted_MCP.csproj b/dotnet/samples/02-agents/ModelContextProtocol/FoundryAgent_Hosted_MCP/FoundryAgent_Hosted_MCP.csproj
index d861331d9f..4c83380f90 100644
--- a/dotnet/samples/02-agents/ModelContextProtocol/FoundryAgent_Hosted_MCP/FoundryAgent_Hosted_MCP.csproj
+++ b/dotnet/samples/02-agents/ModelContextProtocol/FoundryAgent_Hosted_MCP/FoundryAgent_Hosted_MCP.csproj
@@ -14,7 +14,7 @@
-
+
diff --git a/dotnet/samples/03-workflows/Agents/FoundryAgent/FoundryAgent.csproj b/dotnet/samples/03-workflows/Agents/FoundryAgent/FoundryAgent.csproj
index a7648b7a10..dd6854fbfe 100644
--- a/dotnet/samples/03-workflows/Agents/FoundryAgent/FoundryAgent.csproj
+++ b/dotnet/samples/03-workflows/Agents/FoundryAgent/FoundryAgent.csproj
@@ -15,7 +15,7 @@
-
+
diff --git a/dotnet/samples/03-workflows/Agents/FoundryAgent/Program.cs b/dotnet/samples/03-workflows/Agents/FoundryAgent/Program.cs
index e4fb1378b1..1ecbbae08e 100644
--- a/dotnet/samples/03-workflows/Agents/FoundryAgent/Program.cs
+++ b/dotnet/samples/03-workflows/Agents/FoundryAgent/Program.cs
@@ -4,7 +4,7 @@ using Azure.AI.Projects;
using Azure.AI.Projects.Agents;
using Azure.Identity;
using Microsoft.Agents.AI;
-using Microsoft.Agents.AI.AzureAI;
+using Microsoft.Agents.AI.Foundry;
using Microsoft.Agents.AI.Workflows;
using Microsoft.Extensions.AI;
diff --git a/dotnet/samples/03-workflows/Declarative/ConfirmInput/ConfirmInput.csproj b/dotnet/samples/03-workflows/Declarative/ConfirmInput/ConfirmInput.csproj
index dac2f49921..e7a4c3a774 100644
--- a/dotnet/samples/03-workflows/Declarative/ConfirmInput/ConfirmInput.csproj
+++ b/dotnet/samples/03-workflows/Declarative/ConfirmInput/ConfirmInput.csproj
@@ -26,7 +26,7 @@
-
+
diff --git a/dotnet/samples/03-workflows/Declarative/CustomerSupport/CustomerSupport.csproj b/dotnet/samples/03-workflows/Declarative/CustomerSupport/CustomerSupport.csproj
index 0bc83997d0..ec6ca6093c 100644
--- a/dotnet/samples/03-workflows/Declarative/CustomerSupport/CustomerSupport.csproj
+++ b/dotnet/samples/03-workflows/Declarative/CustomerSupport/CustomerSupport.csproj
@@ -26,7 +26,7 @@
-
+
diff --git a/dotnet/samples/03-workflows/Declarative/DeepResearch/DeepResearch.csproj b/dotnet/samples/03-workflows/Declarative/DeepResearch/DeepResearch.csproj
index cd533a0707..60928aaa52 100644
--- a/dotnet/samples/03-workflows/Declarative/DeepResearch/DeepResearch.csproj
+++ b/dotnet/samples/03-workflows/Declarative/DeepResearch/DeepResearch.csproj
@@ -26,7 +26,7 @@
-
+
diff --git a/dotnet/samples/03-workflows/Declarative/ExecuteCode/ExecuteCode.csproj b/dotnet/samples/03-workflows/Declarative/ExecuteCode/ExecuteCode.csproj
index 6a9c4957c2..c92a9ffbf1 100644
--- a/dotnet/samples/03-workflows/Declarative/ExecuteCode/ExecuteCode.csproj
+++ b/dotnet/samples/03-workflows/Declarative/ExecuteCode/ExecuteCode.csproj
@@ -27,7 +27,7 @@
-
+
diff --git a/dotnet/samples/03-workflows/Declarative/ExecuteWorkflow/ExecuteWorkflow.csproj b/dotnet/samples/03-workflows/Declarative/ExecuteWorkflow/ExecuteWorkflow.csproj
index fce40b64d4..243d6d3d52 100644
--- a/dotnet/samples/03-workflows/Declarative/ExecuteWorkflow/ExecuteWorkflow.csproj
+++ b/dotnet/samples/03-workflows/Declarative/ExecuteWorkflow/ExecuteWorkflow.csproj
@@ -26,7 +26,7 @@
-
+
diff --git a/dotnet/samples/03-workflows/Declarative/FunctionTools/FunctionTools.csproj b/dotnet/samples/03-workflows/Declarative/FunctionTools/FunctionTools.csproj
index f890fb30a8..5ed2187e2f 100644
--- a/dotnet/samples/03-workflows/Declarative/FunctionTools/FunctionTools.csproj
+++ b/dotnet/samples/03-workflows/Declarative/FunctionTools/FunctionTools.csproj
@@ -26,7 +26,7 @@
-
+
diff --git a/dotnet/samples/03-workflows/Declarative/HostedWorkflow/HostedWorkflow.csproj b/dotnet/samples/03-workflows/Declarative/HostedWorkflow/HostedWorkflow.csproj
index f9379f38a3..31e88d54e7 100644
--- a/dotnet/samples/03-workflows/Declarative/HostedWorkflow/HostedWorkflow.csproj
+++ b/dotnet/samples/03-workflows/Declarative/HostedWorkflow/HostedWorkflow.csproj
@@ -27,7 +27,7 @@
-
+
diff --git a/dotnet/samples/03-workflows/Declarative/HostedWorkflow/Program.cs b/dotnet/samples/03-workflows/Declarative/HostedWorkflow/Program.cs
index 7852a8730f..1d688531df 100644
--- a/dotnet/samples/03-workflows/Declarative/HostedWorkflow/Program.cs
+++ b/dotnet/samples/03-workflows/Declarative/HostedWorkflow/Program.cs
@@ -8,7 +8,7 @@ using Azure.AI.Projects;
using Azure.AI.Projects.Agents;
using Azure.Identity;
using Microsoft.Agents.AI;
-using Microsoft.Agents.AI.AzureAI;
+using Microsoft.Agents.AI.Foundry;
using Microsoft.Extensions.AI;
using Microsoft.Extensions.Configuration;
using Shared.Foundry;
diff --git a/dotnet/samples/03-workflows/Declarative/InputArguments/InputArguments.csproj b/dotnet/samples/03-workflows/Declarative/InputArguments/InputArguments.csproj
index 45bc44eaf3..ae6a0046ef 100644
--- a/dotnet/samples/03-workflows/Declarative/InputArguments/InputArguments.csproj
+++ b/dotnet/samples/03-workflows/Declarative/InputArguments/InputArguments.csproj
@@ -26,7 +26,7 @@
-
+
diff --git a/dotnet/samples/03-workflows/Declarative/InvokeFunctionTool/InvokeFunctionTool.csproj b/dotnet/samples/03-workflows/Declarative/InvokeFunctionTool/InvokeFunctionTool.csproj
index 67229da4b8..f2d23835a1 100644
--- a/dotnet/samples/03-workflows/Declarative/InvokeFunctionTool/InvokeFunctionTool.csproj
+++ b/dotnet/samples/03-workflows/Declarative/InvokeFunctionTool/InvokeFunctionTool.csproj
@@ -26,7 +26,7 @@
-
+
diff --git a/dotnet/samples/03-workflows/Declarative/InvokeMcpTool/InvokeMcpTool.csproj b/dotnet/samples/03-workflows/Declarative/InvokeMcpTool/InvokeMcpTool.csproj
index 317d93c4e9..ea122bd971 100644
--- a/dotnet/samples/03-workflows/Declarative/InvokeMcpTool/InvokeMcpTool.csproj
+++ b/dotnet/samples/03-workflows/Declarative/InvokeMcpTool/InvokeMcpTool.csproj
@@ -26,7 +26,7 @@
-
+
diff --git a/dotnet/samples/03-workflows/Declarative/Marketing/Marketing.csproj b/dotnet/samples/03-workflows/Declarative/Marketing/Marketing.csproj
index 20e5843554..23d1224a5e 100644
--- a/dotnet/samples/03-workflows/Declarative/Marketing/Marketing.csproj
+++ b/dotnet/samples/03-workflows/Declarative/Marketing/Marketing.csproj
@@ -26,7 +26,7 @@
-
+
diff --git a/dotnet/samples/03-workflows/Declarative/StudentTeacher/StudentTeacher.csproj b/dotnet/samples/03-workflows/Declarative/StudentTeacher/StudentTeacher.csproj
index 8136706b8d..2785c5930b 100644
--- a/dotnet/samples/03-workflows/Declarative/StudentTeacher/StudentTeacher.csproj
+++ b/dotnet/samples/03-workflows/Declarative/StudentTeacher/StudentTeacher.csproj
@@ -26,7 +26,7 @@
-
+
diff --git a/dotnet/samples/03-workflows/Declarative/ToolApproval/ToolApproval.csproj b/dotnet/samples/03-workflows/Declarative/ToolApproval/ToolApproval.csproj
index a44e140f1f..e5cf939e15 100644
--- a/dotnet/samples/03-workflows/Declarative/ToolApproval/ToolApproval.csproj
+++ b/dotnet/samples/03-workflows/Declarative/ToolApproval/ToolApproval.csproj
@@ -26,7 +26,7 @@
-
+
diff --git a/dotnet/samples/05-end-to-end/A2AClientServer/A2AServer/A2AServer.csproj b/dotnet/samples/05-end-to-end/A2AClientServer/A2AServer/A2AServer.csproj
index 5a7ef20208..98b7b293c8 100644
--- a/dotnet/samples/05-end-to-end/A2AClientServer/A2AServer/A2AServer.csproj
+++ b/dotnet/samples/05-end-to-end/A2AClientServer/A2AServer/A2AServer.csproj
@@ -1,4 +1,4 @@
-
+
Exe
@@ -23,7 +23,7 @@
-
+
diff --git a/dotnet/samples/05-end-to-end/HostedAgents/FoundrySingleAgent/Program.cs b/dotnet/samples/05-end-to-end/HostedAgents/FoundrySingleAgent/Program.cs
index 5173578ee8..185afd186f 100644
--- a/dotnet/samples/05-end-to-end/HostedAgents/FoundrySingleAgent/Program.cs
+++ b/dotnet/samples/05-end-to-end/HostedAgents/FoundrySingleAgent/Program.cs
@@ -70,17 +70,19 @@ string GetAvailableHotels(
// Build response
var result = new StringBuilder();
- result.AppendLine($"Available hotels in Seattle from {checkInDate} to {checkOutDate} ({nights} nights):");
- result.AppendLine();
+ result
+ .AppendLine($"Available hotels in Seattle from {checkInDate} to {checkOutDate} ({nights} nights):")
+ .AppendLine();
foreach (var hotel in availableHotels)
{
var totalCost = hotel.PricePerNight * nights;
- result.AppendLine($"**{hotel.Name}**");
- result.AppendLine($" Location: {hotel.Location}");
- result.AppendLine($" Rating: {hotel.Rating}/5");
- result.AppendLine($" ${hotel.PricePerNight}/night (Total: ${totalCost})");
- result.AppendLine();
+ result
+ .AppendLine($"**{hotel.Name}**")
+ .AppendLine($" Location: {hotel.Location}")
+ .AppendLine($" Rating: {hotel.Rating}/5")
+ .AppendLine($" ${hotel.PricePerNight}/night (Total: ${totalCost})")
+ .AppendLine();
}
return result.ToString();
diff --git a/dotnet/src/Microsoft.Agents.AI.AzureAI/AzureAIProjectChatClientExtensions.cs b/dotnet/src/Microsoft.Agents.AI.AzureAI/AzureAIProjectChatClientExtensions.cs
deleted file mode 100644
index 479ab894ae..0000000000
--- a/dotnet/src/Microsoft.Agents.AI.AzureAI/AzureAIProjectChatClientExtensions.cs
+++ /dev/null
@@ -1,935 +0,0 @@
-// Copyright (c) Microsoft. All rights reserved.
-
-using System.ClientModel;
-using System.ClientModel.Primitives;
-using System.Diagnostics.CodeAnalysis;
-using System.Runtime.CompilerServices;
-using System.Text;
-using System.Text.Json;
-using System.Text.Json.Nodes;
-using System.Text.Json.Serialization;
-using System.Text.RegularExpressions;
-using Azure.AI.Extensions.OpenAI;
-using Azure.AI.Projects.Agents;
-using Microsoft.Agents.AI;
-using Microsoft.Agents.AI.AzureAI;
-using Microsoft.Extensions.AI;
-using Microsoft.Extensions.Logging;
-using Microsoft.Shared.DiagnosticIds;
-using Microsoft.Shared.Diagnostics;
-using OpenAI;
-using OpenAI.Responses;
-
-namespace Azure.AI.Projects;
-
-///
-/// Provides extension methods for .
-///
-[Experimental(DiagnosticIds.Experiments.AIOpenAIResponses)]
-public static partial class AzureAIProjectChatClientExtensions
-{
- ///
- /// Uses an existing server side agent, wrapped as a using the provided and .
- ///
- /// The to create the with. Cannot be .
- /// The representing the name and version of the server side agent to create a for. Cannot be .
- /// The tools to use when interacting with the agent. This is required when using prompt agent definitions with tools.
- /// Provides a way to customize the creation of the underlying used by the agent.
- /// An optional to use for resolving services required by the instances being invoked.
- /// A instance that can be used to perform operations based on the latest version of the named Azure AI Agent.
- /// Thrown when or is .
- /// The agent with the specified name was not found.
- ///
- /// When instantiating a by using an , minimal information will be available about the agent in the instance level, and any logic that relies
- /// on to retrieve information about the agent like will receive as the result.
- ///
- public static FoundryAgent AsAIAgent(
- this AIProjectClient aiProjectClient,
- AgentReference agentReference,
- IList? tools = null,
- Func? clientFactory = null,
- IServiceProvider? services = null)
- {
- Throw.IfNull(aiProjectClient);
- Throw.IfNull(agentReference);
- ThrowIfInvalidAgentName(agentReference.Name);
-
- var innerAgent = AsChatClientAgent(
- aiProjectClient,
- agentReference,
- new ChatClientAgentOptions()
- {
- Id = $"{agentReference.Name}:{agentReference.Version}",
- Name = agentReference.Name,
- ChatOptions = new() { Tools = tools },
- },
- clientFactory,
- services);
-
- return new FoundryAgent(aiProjectClient, innerAgent);
- }
-
- ///
- /// Asynchronously retrieves an existing server side agent, wrapped as a using the provided .
- ///
- /// The to create the with. Cannot be .
- /// The name of the server side agent to create a for. Cannot be or whitespace.
- /// The tools to use when interacting with the agent. This is required when using prompt agent definitions with tools.
- /// Provides a way to customize the creation of the underlying used by the agent.
- /// An optional to use for resolving services required by the instances being invoked.
- /// The to monitor for cancellation requests. The default is .
- /// A instance that can be used to perform operations based on the latest version of the named Azure AI Agent.
- /// Thrown when or is .
- /// Thrown when is empty or whitespace, or when the agent with the specified name was not found.
- /// The agent with the specified name was not found.
- [Obsolete("Use native AIProjectClient agent APIs and AsAIAgent(AgentRecord/AgentVersion) instead.")]
- public static async Task GetAIAgentAsync(
- this AIProjectClient aiProjectClient,
- string name,
- IList? tools = null,
- Func? clientFactory = null,
- IServiceProvider? services = null,
- CancellationToken cancellationToken = default)
- {
- Throw.IfNull(aiProjectClient);
- ThrowIfInvalidAgentName(name);
-
- AgentRecord agentRecord = await GetAgentRecordByNameAsync(aiProjectClient, name, cancellationToken).ConfigureAwait(false);
-
- return AsAIAgent(
- aiProjectClient,
- agentRecord,
- tools,
- clientFactory,
- services);
- }
-
- ///
- /// Uses an existing server side agent, wrapped as a using the provided and .
- ///
- /// The client used to interact with Azure AI Agents. Cannot be .
- /// The agent record to be converted. The latest version will be used. Cannot be .
- /// The tools to use when interacting with the agent. This is required when using prompt agent definitions with tools.
- /// Provides a way to customize the creation of the underlying used by the agent.
- /// An optional to use for resolving services required by the instances being invoked.
- /// A instance that can be used to perform operations based on the latest version of the Azure AI Agent.
- /// Thrown when or is .
- public static FoundryAgent AsAIAgent(
- this AIProjectClient aiProjectClient,
- AgentRecord agentRecord,
- IList? tools = null,
- Func? clientFactory = null,
- IServiceProvider? services = null)
- {
- Throw.IfNull(aiProjectClient);
- Throw.IfNull(agentRecord);
-
- var allowDeclarativeMode = tools is not { Count: > 0 };
-
- var innerAgent = AsChatClientAgent(
- aiProjectClient,
- agentRecord,
- tools,
- clientFactory,
- !allowDeclarativeMode,
- services);
-
- return new FoundryAgent(aiProjectClient, innerAgent);
- }
-
- ///
- /// Uses an existing server side agent, wrapped as a using the provided and .
- ///
- /// The client used to interact with Azure AI Agents. Cannot be .
- /// The agent version to be converted. Cannot be .
- /// In-process invocable tools to be provided. If no tools are provided manual handling will be necessary to invoke in-process tools.
- /// Provides a way to customize the creation of the underlying used by the agent.
- /// An optional to use for resolving services required by the instances being invoked.
- /// A instance that can be used to perform operations based on the provided version of the Azure AI Agent.
- /// Thrown when or is .
- public static FoundryAgent AsAIAgent(
- this AIProjectClient aiProjectClient,
- AgentVersion agentVersion,
- IList? tools = null,
- Func? clientFactory = null,
- IServiceProvider? services = null)
- {
- Throw.IfNull(aiProjectClient);
- Throw.IfNull(agentVersion);
-
- var allowDeclarativeMode = tools is not { Count: > 0 };
-
- var innerAgent = AsChatClientAgent(
- aiProjectClient,
- agentVersion,
- tools,
- clientFactory,
- !allowDeclarativeMode,
- services);
-
- return new FoundryAgent(aiProjectClient, innerAgent);
- }
-
- ///
- /// Asynchronously retrieves an existing server side agent, wrapped as a using the provided .
- ///
- /// The client used to manage and interact with AI agents. Cannot be .
- /// The options for creating the agent. Cannot be .
- /// A factory function to customize the creation of the chat client used by the agent.
- /// An optional to use for resolving services required by the instances being invoked.
- /// A to cancel the operation if needed.
- /// A instance that can be used to perform operations on the newly created agent.
- /// Thrown when or is .
- [Obsolete("Use native AIProjectClient agent APIs and AsAIAgent(AgentRecord/AgentVersion) instead.")]
- public static async Task GetAIAgentAsync(
- this AIProjectClient aiProjectClient,
- ChatClientAgentOptions options,
- Func? clientFactory = null,
- IServiceProvider? services = null,
- CancellationToken cancellationToken = default)
- {
- Throw.IfNull(aiProjectClient);
- Throw.IfNull(options);
-
- if (string.IsNullOrWhiteSpace(options.Name))
- {
- throw new ArgumentException("Agent name must be provided in the options.Name property", nameof(options));
- }
-
- ThrowIfInvalidAgentName(options.Name);
-
- AgentRecord agentRecord = await GetAgentRecordByNameAsync(aiProjectClient, options.Name, cancellationToken).ConfigureAwait(false);
- var agentVersion = agentRecord.GetLatestVersion();
-
- var agentOptions = CreateChatClientAgentOptions(agentVersion, options, requireInvocableTools: !options.UseProvidedChatClientAsIs);
-
- return new FoundryAgent(
- aiProjectClient,
- AsChatClientAgent(aiProjectClient, agentVersion, agentOptions, clientFactory, services));
- }
-
- ///
- /// Creates a new Prompt AI agent in the Foundry service using the specified configuration parameters, and exposes it as a .
- ///
- /// The client used to manage and interact with AI agents. Cannot be .
- /// The name for the agent.
- /// The name of the model to use for the agent. Cannot be or whitespace.
- /// The instructions that guide the agent's behavior. Cannot be or whitespace.
- /// The description for the agent.
- /// The tools to use when interacting with the agent, this is required when using prompt agent definitions with tools.
- /// A factory function to customize the creation of the chat client used by the agent.
- /// An optional to use for resolving services required by the instances being invoked.
- /// A token to monitor for cancellation requests.
- /// A instance that can be used to perform operations on the newly created agent.
- /// Thrown when , , or is .
- /// Thrown when or is empty or whitespace.
- /// When using prompt agent definitions with tools the parameter needs to be provided.
- [Obsolete("Use native AIProjectClient.Agents APIs instead.")]
- public static Task CreateAIAgentAsync(
- this AIProjectClient aiProjectClient,
- string name,
- string model,
- string instructions,
- string? description = null,
- IList? tools = null,
- Func? clientFactory = null,
- IServiceProvider? services = null,
- CancellationToken cancellationToken = default)
- {
- Throw.IfNull(aiProjectClient);
- ThrowIfInvalidAgentName(name);
- Throw.IfNullOrWhitespace(model);
- Throw.IfNullOrWhitespace(instructions);
-
- return CreateAIAgentAsync(
- aiProjectClient,
- name,
- tools,
- new AgentVersionCreationOptions(new PromptAgentDefinition(model) { Instructions = instructions }) { Description = description },
- clientFactory,
- services,
- cancellationToken);
- }
-
- ///
- /// Creates a new Prompt AI agent in the Foundry service using the specified configuration parameters, and exposes it as a .
- ///
- /// The client used to manage and interact with AI agents. Cannot be .
- /// The name of the model to use for the agent. Cannot be or whitespace.
- /// The options for creating the agent. Cannot be .
- /// A factory function to customize the creation of the chat client used by the agent.
- /// An optional to use for resolving services required by the instances being invoked.
- /// A to cancel the operation if needed.
- /// A instance that can be used to perform operations on the newly created agent.
- /// Thrown when or is .
- /// Thrown when is empty or whitespace, or when the agent name is not provided in the options.
- [Obsolete("Use native AIProjectClient.Agents APIs instead.")]
- public static async Task CreateAIAgentAsync(
- this AIProjectClient aiProjectClient,
- string model,
- ChatClientAgentOptions options,
- Func? clientFactory = null,
- IServiceProvider? services = null,
- CancellationToken cancellationToken = default)
- {
- Throw.IfNull(aiProjectClient);
- Throw.IfNull(options);
- Throw.IfNullOrWhitespace(model);
-
- if (string.IsNullOrWhiteSpace(options.Name))
- {
- throw new ArgumentException("Agent name must be provided in the options.Name property", nameof(options));
- }
-
- ThrowIfInvalidAgentName(options.Name);
-
- AgentVersion agentVersion = await CreateAgentVersionFromOptionsAsync(aiProjectClient, model, options, cancellationToken).ConfigureAwait(false);
-
- var agentOptions = CreateChatClientAgentOptions(agentVersion, options, requireInvocableTools: true);
-
- return new FoundryAgent(
- aiProjectClient,
- AsChatClientAgent(aiProjectClient, agentVersion, agentOptions, clientFactory, services));
- }
-
- ///
- /// Creates a new Prompt AI agent in the Foundry service using the specified configuration parameters, and exposes it as a .
- /// parameters.
- ///
- /// The client used to manage and interact with AI agents. Cannot be .
- /// The name for the agent.
- /// Settings that control the creation of the agent.
- /// A factory function to customize the creation of the chat client used by the agent.
- /// A token to monitor for cancellation requests.
- /// A instance that can be used to perform operations on the newly created agent.
- /// Thrown when or is .
- ///
- /// When using this extension method with a the tools are only declarative and not invocable.
- /// Invocation of any in-process tools will need to be handled manually.
- ///
- [Obsolete("Use native AIProjectClient.Agents APIs instead.")]
- public static Task CreateAIAgentAsync(
- this AIProjectClient aiProjectClient,
- string name,
- AgentVersionCreationOptions creationOptions,
- Func? clientFactory = null,
- CancellationToken cancellationToken = default)
- {
- Throw.IfNull(aiProjectClient);
- ThrowIfInvalidAgentName(name);
- Throw.IfNull(creationOptions);
-
- return CreateAIAgentAsync(
- aiProjectClient,
- name,
- tools: null,
- creationOptions,
- clientFactory,
- services: null,
- cancellationToken);
- }
-
- ///
- /// Creates a non-versioned backed by the project's Responses API using the specified model and instructions.
- ///
- /// The to use for Responses API calls. Cannot be .
- /// The model deployment name to use for the agent. Cannot be or whitespace.
- /// The instructions that guide the agent's behavior. Cannot be or whitespace.
- /// Optional name for the agent.
- /// Optional human-readable description for the agent.
- /// Optional collection of tools that the agent can invoke during conversations.
- /// Provides a way to customize the creation of the underlying used by the agent.
- /// Optional logger factory for creating loggers used by the agent.
- /// An optional to use for resolving services required by the instances being invoked.
- /// A backed by the project's Responses API.
- /// Thrown when is .
- /// Thrown when or is empty or whitespace.
- public static FoundryAgent AsAIAgent(
- this AIProjectClient aiProjectClient,
- string model,
- string instructions,
- string? name = null,
- string? description = null,
- IList? tools = null,
- Func? clientFactory = null,
- ILoggerFactory? loggerFactory = null,
- IServiceProvider? services = null)
- {
- Throw.IfNull(aiProjectClient);
- Throw.IfNullOrWhitespace(model);
- Throw.IfNullOrWhitespace(instructions);
-
- ChatClientAgentOptions options = new()
- {
- Name = name,
- Description = description,
- ChatOptions = new ChatOptions
- {
- ModelId = model,
- Instructions = instructions,
- Tools = tools,
- },
- };
-
- return new FoundryAgent(aiProjectClient, CreateResponsesChatClientAgent(aiProjectClient, options, clientFactory, loggerFactory, services));
- }
-
- ///
- /// Creates a non-versioned backed by the project's Responses API using the specified options.
- ///
- /// The to use for Responses API calls. Cannot be .
- /// Configuration options that control the agent's behavior. is required.
- /// Provides a way to customize the creation of the underlying used by the agent.
- /// Optional logger factory for creating loggers used by the agent.
- /// An optional to use for resolving services required by the instances being invoked.
- /// A backed by the project's Responses API.
- /// Thrown when or is .
- /// Thrown when does not specify .
- public static FoundryAgent AsAIAgent(
- this AIProjectClient aiProjectClient,
- ChatClientAgentOptions options,
- Func? clientFactory = null,
- ILoggerFactory? loggerFactory = null,
- IServiceProvider? services = null)
- {
- Throw.IfNull(aiProjectClient);
- Throw.IfNull(options);
-
- return new FoundryAgent(aiProjectClient, CreateResponsesChatClientAgent(aiProjectClient, options, clientFactory, loggerFactory, services));
- }
-
- #region Private
-
- private static readonly ModelReaderWriterOptions s_modelWriterOptionsWire = new("W");
-
- ///
- /// Asynchronously retrieves an agent record by name using the protocol method to inject user-agent headers.
- ///
- internal static async Task GetAgentRecordByNameAsync(AIProjectClient aiProjectClient, string agentName, CancellationToken cancellationToken)
- {
- ClientResult protocolResponse = await aiProjectClient.Agents.GetAgentAsync(agentName, cancellationToken.ToRequestOptions(false)).ConfigureAwait(false);
- var rawResponse = protocolResponse.GetRawResponse();
- AgentRecord? result = ModelReaderWriter.Read(rawResponse.Content, s_modelWriterOptionsWire, AzureAIProjectsAgentsContext.Default);
- return result ?? throw new InvalidOperationException($"Agent with name '{agentName}' not found.");
- }
-
- ///
- /// Asynchronously creates an agent version using the protocol method to inject user-agent headers.
- ///
- internal static async Task CreateAgentVersionWithProtocolAsync(AIProjectClient aiProjectClient, string agentName, AgentVersionCreationOptions creationOptions, CancellationToken cancellationToken)
- {
- BinaryData serializedOptions = ModelReaderWriter.Write(creationOptions, s_modelWriterOptionsWire, AzureAIProjectsAgentsContext.Default);
- BinaryContent content = BinaryContent.Create(serializedOptions);
- ClientResult protocolResponse = await aiProjectClient.Agents.CreateAgentVersionAsync(agentName, content, foundryFeatures: null, cancellationToken.ToRequestOptions(false)).ConfigureAwait(false);
- var rawResponse = protocolResponse.GetRawResponse();
- AgentVersion? result = ModelReaderWriter.Read(rawResponse.Content, s_modelWriterOptionsWire, AzureAIProjectsAgentsContext.Default);
- return result ?? throw new InvalidOperationException($"Failed to create agent version for agent '{agentName}'.");
- }
-
- private static async Task CreateAIAgentAsync(
- this AIProjectClient aiProjectClient,
- string name,
- IList? tools,
- AgentVersionCreationOptions creationOptions,
- Func? clientFactory,
- IServiceProvider? services,
- CancellationToken cancellationToken)
- {
- var allowDeclarativeMode = tools is not { Count: > 0 };
-
- if (!allowDeclarativeMode)
- {
- ApplyToolsToAgentDefinition(creationOptions.Definition, tools);
- }
-
- AgentVersion agentVersion = await CreateAgentVersionWithProtocolAsync(aiProjectClient, name, creationOptions, cancellationToken).ConfigureAwait(false);
-
- return new FoundryAgent(aiProjectClient, AsChatClientAgent(aiProjectClient, agentVersion, tools, clientFactory, !allowDeclarativeMode, services));
- }
-
- ///
- /// Creates an agent version with optional tool application, using the protocol method to inject user-agent headers.
- ///
- internal static async Task CreateAgentVersionWithProtocolAsync(AIProjectClient aiProjectClient, string agentName, AgentVersionCreationOptions creationOptions, IList? tools, CancellationToken cancellationToken)
- {
- if (tools is { Count: > 0 })
- {
- ApplyToolsToAgentDefinition(creationOptions.Definition, tools);
- }
-
- return await CreateAgentVersionWithProtocolAsync(aiProjectClient, agentName, creationOptions, cancellationToken).ConfigureAwait(false);
- }
-
- ///
- /// Creates an agent version from , mapping options to a .
- ///
- internal static async Task CreateAgentVersionFromOptionsAsync(
- AIProjectClient aiProjectClient,
- string model,
- ChatClientAgentOptions options,
- CancellationToken cancellationToken)
- {
- PromptAgentDefinition agentDefinition = new(model)
- {
- Instructions = options.ChatOptions?.Instructions,
- Temperature = options.ChatOptions?.Temperature,
- TopP = options.ChatOptions?.TopP,
- TextOptions = new() { TextFormat = ToOpenAIResponseTextFormat(options.ChatOptions?.ResponseFormat, options.ChatOptions) }
- };
-
- if (options.ChatOptions?.Reasoning is { } reasoning)
- {
- agentDefinition.ReasoningOptions = ToResponseReasoningOptions(reasoning);
- }
- else if (options.ChatOptions?.RawRepresentationFactory?.Invoke(new NoOpChatClient()) is CreateResponseOptions respCreationOptions)
- {
- agentDefinition.ReasoningOptions = respCreationOptions.ReasoningOptions;
- }
-
- ApplyToolsToAgentDefinition(agentDefinition, options.ChatOptions?.Tools);
-
- AgentVersionCreationOptions creationOptions = new(agentDefinition);
- if (!string.IsNullOrWhiteSpace(options.Description))
- {
- creationOptions.Description = options.Description;
- }
-
- return await CreateAgentVersionWithProtocolAsync(aiProjectClient, options.Name!, creationOptions, cancellationToken).ConfigureAwait(false);
- }
-
- /// Creates a with the specified options.
- internal static ChatClientAgent CreateChatClientAgent(
- AIProjectClient aiProjectClient,
- AgentVersion agentVersion,
- ChatClientAgentOptions agentOptions,
- Func? clientFactory,
- IServiceProvider? services)
- {
- IChatClient chatClient = new AzureAIProjectChatClient(aiProjectClient, agentVersion, agentOptions.ChatOptions);
-
- if (clientFactory is not null)
- {
- chatClient = clientFactory(chatClient);
- }
-
- return new ChatClientAgent(chatClient, agentOptions, services: services);
- }
-
- internal static ChatClientAgent CreateResponsesChatClientAgent(
- AIProjectClient aiProjectClient,
- ChatClientAgentOptions agentOptions,
- Func? clientFactory,
- ILoggerFactory? loggerFactory,
- IServiceProvider? services)
- {
- Throw.IfNull(aiProjectClient);
- Throw.IfNull(agentOptions);
- Throw.IfNull(agentOptions.ChatOptions);
- Throw.IfNullOrWhitespace(agentOptions.ChatOptions.ModelId);
-
- IChatClient chatClient = new AzureAIProjectResponsesChatClient(aiProjectClient, agentOptions.ChatOptions.ModelId);
-
- if (clientFactory is not null)
- {
- chatClient = clientFactory(chatClient);
- }
-
- return new ChatClientAgent(chatClient, agentOptions, loggerFactory, services);
- }
-
- /// This method creates an with the specified ChatClientAgentOptions.
- private static ChatClientAgent AsChatClientAgent(
- AIProjectClient aiProjectClient,
- AgentVersion agentVersion,
- ChatClientAgentOptions agentOptions,
- Func? clientFactory,
- IServiceProvider? services)
- => CreateChatClientAgent(aiProjectClient, agentVersion, agentOptions, clientFactory, services);
-
- /// This method creates an with the specified ChatClientAgentOptions.
- private static ChatClientAgent AsChatClientAgent(
- AIProjectClient aiProjectClient,
- AgentRecord agentRecord,
- ChatClientAgentOptions agentOptions,
- Func? clientFactory,
- IServiceProvider? services)
- {
- IChatClient chatClient = new AzureAIProjectChatClient(aiProjectClient, agentRecord, agentOptions.ChatOptions);
-
- if (clientFactory is not null)
- {
- chatClient = clientFactory(chatClient);
- }
-
- return new ChatClientAgent(chatClient, agentOptions, services: services);
- }
-
- /// This method creates an with the specified ChatClientAgentOptions.
- private static ChatClientAgent AsChatClientAgent(
- AIProjectClient aiProjectClient,
- AgentReference agentReference,
- ChatClientAgentOptions agentOptions,
- Func? clientFactory,
- IServiceProvider? services)
- {
- IChatClient chatClient = new AzureAIProjectChatClient(aiProjectClient, agentReference, defaultModelId: null, agentOptions.ChatOptions);
-
- if (clientFactory is not null)
- {
- chatClient = clientFactory(chatClient);
- }
-
- return new ChatClientAgent(chatClient, agentOptions, services: services);
- }
-
- /// This method creates an with a auto-generated ChatClientAgentOptions from the specified configuration parameters.
- private static ChatClientAgent AsChatClientAgent(
- AIProjectClient AIProjectClient,
- AgentVersion agentVersion,
- IList? tools,
- Func? clientFactory,
- bool requireInvocableTools,
- IServiceProvider? services)
- => AsChatClientAgent(
- AIProjectClient,
- agentVersion,
- CreateChatClientAgentOptions(agentVersion, new ChatOptions() { Tools = tools }, requireInvocableTools),
- clientFactory,
- services);
-
- /// This method creates an with a auto-generated ChatClientAgentOptions from the specified configuration parameters.
- private static ChatClientAgent AsChatClientAgent(
- AIProjectClient AIProjectClient,
- AgentRecord agentRecord,
- IList? tools,
- Func? clientFactory,
- bool requireInvocableTools,
- IServiceProvider? services)
- => AsChatClientAgent(
- AIProjectClient,
- agentRecord,
- CreateChatClientAgentOptions(agentRecord.GetLatestVersion(), new ChatOptions() { Tools = tools }, requireInvocableTools),
- clientFactory,
- services);
-
- ///
- /// This method creates for the specified and the provided tools.
- ///
- /// The agent version.
- /// The to use when interacting with the agent.
- /// Indicates whether to enforce the presence of invocable tools when the AIAgent is created with an agent definition that uses them.
- /// The created .
- /// Thrown when the agent definition requires in-process tools but none were provided.
- /// Thrown when the agent definition required tools were not provided.
- ///
- /// 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.
- ///
- internal static ChatClientAgentOptions CreateChatClientAgentOptions(AgentVersion agentVersion, ChatOptions? chatOptions, bool requireInvocableTools)
- {
- var agentDefinition = agentVersion.Definition;
-
- List? agentTools = null;
- 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))
- {
- throw new ArgumentException("The agent definition in-process tools must be provided in the extension method tools parameter.");
- }
-
- // Agregate all missing tools for a single error message.
- List? missingTools = null;
-
- // Check function tools
- foreach (ResponseTool responseTool in definitionTools)
- {
- if (responseTool is FunctionTool functionTool)
- {
- // Check if a tool with the same type and name exists in the provided tools.
- // Always prefer matching AIFunction when available, regardless of requireInvocableTools.
- var matchingTool = chatOptions?.Tools?.FirstOrDefault(t => t is AIFunction tf && functionTool.FunctionName == tf.Name);
-
- if (matchingTool is not null)
- {
- (agentTools ??= []).Add(matchingTool!);
- continue;
- }
-
- if (requireInvocableTools)
- {
- (missingTools ??= []).Add($"Function tool: {functionTool.FunctionName}");
- continue;
- }
- }
-
- (agentTools ??= []).Add(responseTool.AsAITool());
- }
-
- if (requireInvocableTools && missingTools is { Count: > 0 })
- {
- throw new InvalidOperationException($"The following prompt agent definition required tools were not provided: {string.Join(", ", missingTools)}");
- }
- }
-
- // Use the agent version's ID if available, otherwise generate one from name and version.
- // This handles cases where hosted agents (like MCP agents) may not have an ID assigned.
- var version = string.IsNullOrWhiteSpace(agentVersion.Version) ? "latest" : agentVersion.Version;
- var agentId = string.IsNullOrWhiteSpace(agentVersion.Id)
- ? $"{agentVersion.Name}:{version}"
- : agentVersion.Id;
-
- var agentOptions = new ChatClientAgentOptions()
- {
- Id = agentId,
- Name = agentVersion.Name,
- Description = agentVersion.Description,
- };
-
- if (agentDefinition is PromptAgentDefinition promptAgentDefinition)
- {
- agentOptions.ChatOptions ??= chatOptions?.Clone() ?? new();
- agentOptions.ChatOptions.Instructions = promptAgentDefinition.Instructions;
- agentOptions.ChatOptions.Temperature = promptAgentDefinition.Temperature;
- agentOptions.ChatOptions.TopP = promptAgentDefinition.TopP;
- }
-
- if (agentTools is { Count: > 0 })
- {
- agentOptions.ChatOptions ??= chatOptions?.Clone() ?? new();
- agentOptions.ChatOptions.Tools = agentTools;
- }
-
- return agentOptions;
- }
-
- ///
- /// Creates a new instance of configured for the specified agent version and
- /// optional base options.
- ///
- /// The agent version to use when configuring the chat client agent options.
- /// An optional instance whose relevant properties will be copied to the
- /// returned options. If , only default values are used.
- /// Specifies whether the returned options must include invocable tools. Set to to require
- /// invocable tools; otherwise, .
- /// A instance configured according to the specified parameters.
- internal static ChatClientAgentOptions CreateChatClientAgentOptions(AgentVersion agentVersion, ChatClientAgentOptions? options, bool requireInvocableTools)
- {
- var agentOptions = CreateChatClientAgentOptions(agentVersion, options?.ChatOptions, requireInvocableTools);
- if (options is not null)
- {
- agentOptions.AIContextProviders = options.AIContextProviders;
- agentOptions.ChatHistoryProvider = options.ChatHistoryProvider;
- agentOptions.UseProvidedChatClientAsIs = options.UseProvidedChatClientAsIs;
- }
-
- return agentOptions;
- }
-
- ///
- /// Adds the specified AI tools to a prompt agent definition, while also ensuring that all invocable tools are provided.
- ///
- /// The agent definition to which the tools will be applied. Must be a PromptAgentDefinition to support tools.
- /// A list of AI tools to add to the agent definition. If null or empty, no tools are added.
- /// Thrown if tools were provided but is not a .
- /// When providing functions, they need to be invokable AIFunctions.
- private static void ApplyToolsToAgentDefinition(AgentDefinition agentDefinition, IList? tools)
- {
- if (tools is { Count: > 0 })
- {
- if (agentDefinition is not PromptAgentDefinition promptAgentDefinition)
- {
- throw new ArgumentException("Only prompt agent definitions support tools.", nameof(agentDefinition));
- }
-
- // When tools are provided, those should represent the complete set of tools for the agent definition.
- // This is particularly important for existing agents so no duplication happens for what was already defined.
- promptAgentDefinition.Tools.Clear();
-
- foreach (var tool in tools)
- {
- // Ensure that any AIFunctions provided are In-Proc, not just the declarations.
- if (tool is not AIFunction && (
- tool.GetService() is not null // Declarative FunctionTool converted as AsAITool()
- || tool is AIFunctionDeclaration)) // AIFunctionDeclaration type
- {
- throw new InvalidOperationException("When providing functions, they need to be invokable AIFunctions. AIFunctions can be created correctly using AIFunctionFactory.Create");
- }
-
- promptAgentDefinition.Tools.Add(
- // If this is a converted ResponseTool as AITool, we can directly retrieve the ResponseTool instance from GetService.
- tool.GetService()
- // Otherwise we should be able to convert existing MEAI Tool abstractions into OpenAI ResponseTools
- ?? tool.AsOpenAIResponseTool()
- ?? throw new InvalidOperationException("The provided AITool could not be converted to a ResponseTool, ensure that the AITool was created using responseTool.AsAITool() extension."));
- }
- }
- }
-
- private static ResponseTextFormat? ToOpenAIResponseTextFormat(ChatResponseFormat? format, ChatOptions? options = null) =>
- format switch
- {
- ChatResponseFormatText => ResponseTextFormat.CreateTextFormat(),
-
- ChatResponseFormatJson jsonFormat when StrictSchemaTransformCache.GetOrCreateTransformedSchema(jsonFormat) is { } jsonSchema =>
- ResponseTextFormat.CreateJsonSchemaFormat(
- jsonFormat.SchemaName ?? "json_schema",
- BinaryData.FromBytes(JsonSerializer.SerializeToUtf8Bytes(jsonSchema, AgentClientJsonContext.Default.JsonElement)),
- jsonFormat.SchemaDescription,
- HasStrict(options?.AdditionalProperties)),
-
- ChatResponseFormatJson => ResponseTextFormat.CreateJsonObjectFormat(),
-
- _ => null,
- };
-
- /// Key into AdditionalProperties used to store a strict option.
- private const string StrictKey = "strictJsonSchema";
-
- /// Gets whether the properties specify that strict schema handling is desired.
- private static bool? HasStrict(IReadOnlyDictionary? additionalProperties) =>
- additionalProperties?.TryGetValue(StrictKey, out object? strictObj) is true &&
- strictObj is bool strictValue ?
- strictValue : null;
-
- ///
- /// Gets the JSON schema transformer cache conforming to OpenAI strict / structured output restrictions per
- /// https://platform.openai.com/docs/guides/structured-outputs?api-mode=responses#supported-schemas.
- ///
- private static AIJsonSchemaTransformCache StrictSchemaTransformCache { get; } = new(new()
- {
- DisallowAdditionalProperties = true,
- ConvertBooleanSchemas = true,
- MoveDefaultKeywordToDescription = true,
- RequireAllProperties = true,
- TransformSchemaNode = (ctx, node) =>
- {
- // Move content from common but unsupported properties to description. In particular, we focus on properties that
- // the AIJsonUtilities schema generator might produce and/or that are explicitly mentioned in the OpenAI documentation.
-
- if (node is JsonObject schemaObj)
- {
- StringBuilder? additionalDescription = null;
-
- ReadOnlySpan unsupportedProperties =
- [
- // Produced by AIJsonUtilities but not in allow list at https://platform.openai.com/docs/guides/structured-outputs#supported-properties:
- "contentEncoding", "contentMediaType", "not",
-
- // Explicitly mentioned at https://platform.openai.com/docs/guides/structured-outputs?api-mode=responses#key-ordering as being unsupported with some models:
- "minLength", "maxLength", "pattern", "format",
- "minimum", "maximum", "multipleOf",
- "patternProperties",
- "minItems", "maxItems",
-
- // Explicitly mentioned at https://learn.microsoft.com/azure/ai-services/openai/how-to/structured-outputs?pivots=programming-language-csharp&tabs=python-secure%2Cdotnet-entra-id#unsupported-type-specific-keywords
- // as being unsupported with Azure OpenAI:
- "unevaluatedProperties", "propertyNames", "minProperties", "maxProperties",
- "unevaluatedItems", "contains", "minContains", "maxContains", "uniqueItems",
- ];
-
- foreach (string propName in unsupportedProperties)
- {
- if (schemaObj[propName] is { } propNode)
- {
- _ = schemaObj.Remove(propName);
- AppendLine(ref additionalDescription, propName, propNode);
- }
- }
-
- if (additionalDescription is not null)
- {
- schemaObj["description"] = schemaObj["description"] is { } descriptionNode && descriptionNode.GetValueKind() == JsonValueKind.String ?
- $"{descriptionNode.GetValue()}{Environment.NewLine}{additionalDescription}" :
- additionalDescription.ToString();
- }
-
- return node;
-
- static void AppendLine(ref StringBuilder? sb, string propName, JsonNode propNode)
- {
- sb ??= new();
-
- if (sb.Length > 0)
- {
- _ = sb.AppendLine();
- }
-
- _ = sb.Append(propName).Append(": ").Append(propNode);
- }
- }
-
- return node;
- },
- });
-
- ///
- /// This class is a no-op implementation of to be used to honor the argument passed
- /// while triggering avoiding any unexpected exception on the caller implementation.
- ///
- private sealed class NoOpChatClient : IChatClient
- {
- public void Dispose() { }
-
- public Task GetResponseAsync(IEnumerable messages, ChatOptions? options = null, CancellationToken cancellationToken = default)
- => Task.FromResult(new ChatResponse());
-
- public object? GetService(Type serviceType, object? serviceKey = null) => null;
-
- public async IAsyncEnumerable GetStreamingResponseAsync(IEnumerable messages, ChatOptions? options = null, [EnumeratorCancellation] CancellationToken cancellationToken = default)
- {
- yield return new ChatResponseUpdate();
- }
- }
- #endregion
-
-#if NET
- [GeneratedRegex("^[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?$")]
- private static partial Regex AgentNameValidationRegex();
-#else
- private static Regex AgentNameValidationRegex() => new("^[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?$");
-#endif
-
- internal static string ThrowIfInvalidAgentName(string? name)
- {
- Throw.IfNullOrWhitespace(name);
- if (!AgentNameValidationRegex().IsMatch(name))
- {
- throw new ArgumentException("Agent name must be 1-63 characters long, start and end with an alphanumeric character, and can only contain alphanumeric characters or hyphens.", nameof(name));
- }
- return name;
- }
-
- private static ResponseReasoningOptions? ToResponseReasoningOptions(ReasoningOptions reasoning)
- {
- ResponseReasoningEffortLevel? effortLevel = reasoning.Effort switch
- {
- ReasoningEffort.Low => ResponseReasoningEffortLevel.Low,
- ReasoningEffort.Medium => ResponseReasoningEffortLevel.Medium,
- ReasoningEffort.High => ResponseReasoningEffortLevel.High,
- ReasoningEffort.ExtraHigh => ResponseReasoningEffortLevel.High,
- _ => null,
- };
-
- ResponseReasoningSummaryVerbosity? summary = reasoning.Output switch
- {
- ReasoningOutput.Summary => ResponseReasoningSummaryVerbosity.Concise,
- ReasoningOutput.Full => ResponseReasoningSummaryVerbosity.Detailed,
- _ => null,
- };
-
- if (effortLevel is null && summary is null)
- {
- return null;
- }
-
- return new ResponseReasoningOptions
- {
- ReasoningEffortLevel = effortLevel,
- ReasoningSummaryVerbosity = summary,
- };
- }
-}
-
-[JsonSerializable(typeof(JsonElement))]
-internal sealed partial class AgentClientJsonContext : JsonSerializerContext;
diff --git a/dotnet/src/Microsoft.Agents.AI.AzureAI/AzureAIProjectChatClient.cs b/dotnet/src/Microsoft.Agents.AI.Foundry/AzureAIProjectChatClient.cs
similarity index 98%
rename from dotnet/src/Microsoft.Agents.AI.AzureAI/AzureAIProjectChatClient.cs
rename to dotnet/src/Microsoft.Agents.AI.Foundry/AzureAIProjectChatClient.cs
index ec788233ed..b33af228b2 100644
--- a/dotnet/src/Microsoft.Agents.AI.AzureAI/AzureAIProjectChatClient.cs
+++ b/dotnet/src/Microsoft.Agents.AI.Foundry/AzureAIProjectChatClient.cs
@@ -1,7 +1,11 @@
// Copyright (c) Microsoft. All rights reserved.
+using System;
+using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
+using System.Threading;
+using System.Threading.Tasks;
using Azure.AI.Extensions.OpenAI;
using Azure.AI.Projects;
using Azure.AI.Projects.Agents;
@@ -10,7 +14,7 @@ using Microsoft.Shared.DiagnosticIds;
using Microsoft.Shared.Diagnostics;
using OpenAI.Responses;
-namespace Microsoft.Agents.AI.AzureAI;
+namespace Microsoft.Agents.AI.Foundry;
///
/// Provides a chat client implementation that integrates with Azure AI Agents, enabling chat interactions using
diff --git a/dotnet/src/Microsoft.Agents.AI.Foundry/AzureAIProjectChatClientExtensions.cs b/dotnet/src/Microsoft.Agents.AI.Foundry/AzureAIProjectChatClientExtensions.cs
new file mode 100644
index 0000000000..d83299de90
--- /dev/null
+++ b/dotnet/src/Microsoft.Agents.AI.Foundry/AzureAIProjectChatClientExtensions.cs
@@ -0,0 +1,436 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
+using System.Linq;
+using System.Text.Json;
+using System.Text.Json.Serialization;
+using System.Text.RegularExpressions;
+using Azure.AI.Extensions.OpenAI;
+using Azure.AI.Projects.Agents;
+using Microsoft.Agents.AI;
+using Microsoft.Agents.AI.Foundry;
+using Microsoft.Extensions.AI;
+using Microsoft.Extensions.Logging;
+using Microsoft.Shared.DiagnosticIds;
+using Microsoft.Shared.Diagnostics;
+using OpenAI.Responses;
+
+namespace Azure.AI.Projects;
+
+///
+/// Provides extension methods for .
+///
+[Experimental(DiagnosticIds.Experiments.AIOpenAIResponses)]
+public static partial class AzureAIProjectChatClientExtensions
+{
+ ///
+ /// Uses an existing server side agent, wrapped as a using the provided and .
+ ///
+ /// The to create the with. Cannot be .
+ /// The representing the name and version of the server side agent to create a for. Cannot be .
+ /// The tools to use when interacting with the agent. This is required when using prompt agent definitions with tools.
+ /// Provides a way to customize the creation of the underlying used by the agent.
+ /// An optional to use for resolving services required by the instances being invoked.
+ /// A instance that can be used to perform operations based on the latest version of the named Azure AI Agent.
+ /// Thrown when or is .
+ /// The agent with the specified name was not found.
+ ///
+ /// When instantiating a by using an , minimal information will be available about the agent in the instance level, and any logic that relies
+ /// on to retrieve information about the agent like will receive as the result.
+ ///
+ public static FoundryAgent AsAIAgent(
+ this AIProjectClient aiProjectClient,
+ AgentReference agentReference,
+ IList? tools = null,
+ Func? clientFactory = null,
+ IServiceProvider? services = null)
+ {
+ Throw.IfNull(aiProjectClient);
+ Throw.IfNull(agentReference);
+ ThrowIfInvalidAgentName(agentReference.Name);
+
+ var innerAgent = AsChatClientAgent(
+ aiProjectClient,
+ agentReference,
+ new ChatClientAgentOptions()
+ {
+ Id = $"{agentReference.Name}:{agentReference.Version}",
+ Name = agentReference.Name,
+ ChatOptions = new() { Tools = tools },
+ },
+ clientFactory,
+ services);
+
+ return new FoundryAgent(aiProjectClient, innerAgent);
+ }
+
+ ///
+ /// Uses an existing server side agent, wrapped as a using the provided and .
+ ///
+ /// The client used to interact with Azure AI Agents. Cannot be .
+ /// The agent record to be converted. The latest version will be used. Cannot be .
+ /// The tools to use when interacting with the agent. This is required when using prompt agent definitions with tools.
+ /// Provides a way to customize the creation of the underlying used by the agent.
+ /// An optional to use for resolving services required by the instances being invoked.
+ /// A instance that can be used to perform operations based on the latest version of the Azure AI Agent.
+ /// Thrown when or is .
+ public static FoundryAgent AsAIAgent(
+ this AIProjectClient aiProjectClient,
+ AgentRecord agentRecord,
+ IList? tools = null,
+ Func? clientFactory = null,
+ IServiceProvider? services = null)
+ {
+ Throw.IfNull(aiProjectClient);
+ Throw.IfNull(agentRecord);
+
+ var allowDeclarativeMode = tools is not { Count: > 0 };
+
+ var innerAgent = AsChatClientAgent(
+ aiProjectClient,
+ agentRecord,
+ tools,
+ clientFactory,
+ !allowDeclarativeMode,
+ services);
+
+ return new FoundryAgent(aiProjectClient, innerAgent);
+ }
+
+ ///
+ /// Uses an existing server side agent, wrapped as a using the provided and .
+ ///
+ /// The client used to interact with Azure AI Agents. Cannot be .
+ /// The agent version to be converted. Cannot be .
+ /// In-process invocable tools to be provided. If no tools are provided manual handling will be necessary to invoke in-process tools.
+ /// Provides a way to customize the creation of the underlying used by the agent.
+ /// An optional to use for resolving services required by the instances being invoked.
+ /// A instance that can be used to perform operations based on the provided version of the Azure AI Agent.
+ /// Thrown when or is .
+ public static FoundryAgent AsAIAgent(
+ this AIProjectClient aiProjectClient,
+ AgentVersion agentVersion,
+ IList? tools = null,
+ Func? clientFactory = null,
+ IServiceProvider? services = null)
+ {
+ Throw.IfNull(aiProjectClient);
+ Throw.IfNull(agentVersion);
+
+ var allowDeclarativeMode = tools is not { Count: > 0 };
+
+ var innerAgent = AsChatClientAgent(
+ aiProjectClient,
+ agentVersion,
+ tools,
+ clientFactory,
+ !allowDeclarativeMode,
+ services);
+
+ return new FoundryAgent(aiProjectClient, innerAgent);
+ }
+
+ ///
+ /// Creates a non-versioned backed by the project's Responses API using the specified model and instructions.
+ ///
+ /// The to use for Responses API calls. Cannot be .
+ /// The model deployment name to use for the agent. Cannot be or whitespace.
+ /// The instructions that guide the agent's behavior. Cannot be or whitespace.
+ /// Optional name for the agent.
+ /// Optional human-readable description for the agent.
+ /// Optional collection of tools that the agent can invoke during conversations.
+ /// Provides a way to customize the creation of the underlying used by the agent.
+ /// Optional logger factory for creating loggers used by the agent.
+ /// An optional to use for resolving services required by the instances being invoked.
+ /// A backed by the project's Responses API.
+ /// Thrown when is .
+ /// Thrown when or is empty or whitespace.
+ public static ChatClientAgent AsAIAgent(
+ this AIProjectClient aiProjectClient,
+ string model,
+ string instructions,
+ string? name = null,
+ string? description = null,
+ IList? tools = null,
+ Func? clientFactory = null,
+ ILoggerFactory? loggerFactory = null,
+ IServiceProvider? services = null)
+ {
+ Throw.IfNull(aiProjectClient);
+ Throw.IfNullOrWhitespace(model);
+ Throw.IfNullOrWhitespace(instructions);
+
+ ChatClientAgentOptions options = new()
+ {
+ Name = name,
+ Description = description,
+ ChatOptions = new ChatOptions
+ {
+ ModelId = model,
+ Instructions = instructions,
+ Tools = tools,
+ },
+ };
+
+ return CreateResponsesChatClientAgent(aiProjectClient, options, clientFactory, loggerFactory, services);
+ }
+
+ ///
+ /// Creates a non-versioned backed by the project's Responses API using the specified options.
+ ///
+ /// The to use for Responses API calls. Cannot be .
+ /// Configuration options that control the agent's behavior. is required.
+ /// Provides a way to customize the creation of the underlying used by the agent.
+ /// Optional logger factory for creating loggers used by the agent.
+ /// An optional to use for resolving services required by the instances being invoked.
+ /// A backed by the project's Responses API.
+ /// Thrown when or is .
+ /// Thrown when does not specify .
+ public static ChatClientAgent AsAIAgent(
+ this AIProjectClient aiProjectClient,
+ ChatClientAgentOptions options,
+ Func? clientFactory = null,
+ ILoggerFactory? loggerFactory = null,
+ IServiceProvider? services = null)
+ {
+ Throw.IfNull(aiProjectClient);
+ Throw.IfNull(options);
+
+ return CreateResponsesChatClientAgent(aiProjectClient, options, clientFactory, loggerFactory, services);
+ }
+
+ #region Private
+
+ /// Creates a with the specified options.
+ private static ChatClientAgent CreateChatClientAgent(
+ AIProjectClient aiProjectClient,
+ AgentVersion agentVersion,
+ ChatClientAgentOptions agentOptions,
+ Func? clientFactory,
+ IServiceProvider? services)
+ {
+ IChatClient chatClient = new AzureAIProjectChatClient(aiProjectClient, agentVersion, agentOptions.ChatOptions);
+
+ if (clientFactory is not null)
+ {
+ chatClient = clientFactory(chatClient);
+ }
+
+ return new ChatClientAgent(chatClient, agentOptions, services: services);
+ }
+
+ private static ChatClientAgent CreateResponsesChatClientAgent(
+ AIProjectClient aiProjectClient,
+ ChatClientAgentOptions agentOptions,
+ Func? clientFactory,
+ ILoggerFactory? loggerFactory,
+ IServiceProvider? services)
+ {
+ Throw.IfNull(aiProjectClient);
+ Throw.IfNull(agentOptions);
+ Throw.IfNull(agentOptions.ChatOptions);
+ Throw.IfNullOrWhitespace(agentOptions.ChatOptions.ModelId);
+
+ IChatClient chatClient = aiProjectClient
+ .GetProjectOpenAIClient()
+ .GetResponsesClient()
+ .AsIChatClient(agentOptions.ChatOptions.ModelId);
+
+ if (clientFactory is not null)
+ {
+ chatClient = clientFactory(chatClient);
+ }
+
+ return new ChatClientAgent(chatClient, agentOptions, loggerFactory, services);
+ }
+
+ /// This method creates an with the specified ChatClientAgentOptions.
+ private static ChatClientAgent AsChatClientAgent(
+ AIProjectClient aiProjectClient,
+ AgentVersion agentVersion,
+ ChatClientAgentOptions agentOptions,
+ Func? clientFactory,
+ IServiceProvider? services)
+ => CreateChatClientAgent(aiProjectClient, agentVersion, agentOptions, clientFactory, services);
+
+ /// This method creates an with the specified ChatClientAgentOptions.
+ private static ChatClientAgent AsChatClientAgent(
+ AIProjectClient aiProjectClient,
+ AgentRecord agentRecord,
+ ChatClientAgentOptions agentOptions,
+ Func? clientFactory,
+ IServiceProvider? services)
+ {
+ IChatClient chatClient = new AzureAIProjectChatClient(aiProjectClient, agentRecord, agentOptions.ChatOptions);
+
+ if (clientFactory is not null)
+ {
+ chatClient = clientFactory(chatClient);
+ }
+
+ return new ChatClientAgent(chatClient, agentOptions, services: services);
+ }
+
+ /// This method creates an with the specified ChatClientAgentOptions.
+ private static ChatClientAgent AsChatClientAgent(
+ AIProjectClient aiProjectClient,
+ AgentReference agentReference,
+ ChatClientAgentOptions agentOptions,
+ Func? clientFactory,
+ IServiceProvider? services)
+ {
+ IChatClient chatClient = new AzureAIProjectChatClient(aiProjectClient, agentReference, defaultModelId: null, agentOptions.ChatOptions);
+
+ if (clientFactory is not null)
+ {
+ chatClient = clientFactory(chatClient);
+ }
+
+ return new ChatClientAgent(chatClient, agentOptions, services: services);
+ }
+
+ /// This method creates an with a auto-generated ChatClientAgentOptions from the specified configuration parameters.
+ private static ChatClientAgent AsChatClientAgent(
+ AIProjectClient AIProjectClient,
+ AgentVersion agentVersion,
+ IList? tools,
+ Func? clientFactory,
+ bool requireInvocableTools,
+ IServiceProvider? services)
+ => AsChatClientAgent(
+ AIProjectClient,
+ agentVersion,
+ CreateChatClientAgentOptions(agentVersion, new ChatOptions() { Tools = tools }, requireInvocableTools),
+ clientFactory,
+ services);
+
+ /// This method creates an with a auto-generated ChatClientAgentOptions from the specified configuration parameters.
+ private static ChatClientAgent AsChatClientAgent(
+ AIProjectClient AIProjectClient,
+ AgentRecord agentRecord,
+ IList? tools,
+ Func? clientFactory,
+ bool requireInvocableTools,
+ IServiceProvider? services)
+ => AsChatClientAgent(
+ AIProjectClient,
+ agentRecord,
+ CreateChatClientAgentOptions(agentRecord.GetLatestVersion(), new ChatOptions() { Tools = tools }, requireInvocableTools),
+ clientFactory,
+ services);
+
+ ///
+ /// This method creates for the specified and the provided tools.
+ ///
+ /// The agent version.
+ /// The to use when interacting with the agent.
+ /// Indicates whether to enforce the presence of invocable tools when the AIAgent is created with an agent definition that uses them.
+ /// The created .
+ /// Thrown when the agent definition requires in-process tools but none were provided.
+ /// Thrown when the agent definition required tools were not provided.
+ ///
+ /// 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.
+ ///
+ private static ChatClientAgentOptions CreateChatClientAgentOptions(AgentVersion agentVersion, ChatOptions? chatOptions, bool requireInvocableTools)
+ {
+ var agentDefinition = agentVersion.Definition;
+
+ List? agentTools = null;
+ 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))
+ {
+ throw new ArgumentException("The agent definition in-process tools must be provided in the extension method tools parameter.");
+ }
+
+ // Agregate all missing tools for a single error message.
+ List? missingTools = null;
+
+ // Check function tools
+ foreach (ResponseTool responseTool in definitionTools)
+ {
+ if (responseTool is FunctionTool functionTool)
+ {
+ // Check if a tool with the same type and name exists in the provided tools.
+ // Always prefer matching AIFunction when available, regardless of requireInvocableTools.
+ var matchingTool = chatOptions?.Tools?.FirstOrDefault(t => t is AIFunction tf && functionTool.FunctionName == tf.Name);
+
+ if (matchingTool is not null)
+ {
+ (agentTools ??= []).Add(matchingTool!);
+ continue;
+ }
+
+ if (requireInvocableTools)
+ {
+ (missingTools ??= []).Add($"Function tool: {functionTool.FunctionName}");
+ continue;
+ }
+ }
+
+ (agentTools ??= []).Add(responseTool.AsAITool());
+ }
+
+ if (requireInvocableTools && missingTools is { Count: > 0 })
+ {
+ throw new InvalidOperationException($"The following prompt agent definition required tools were not provided: {string.Join(", ", missingTools)}");
+ }
+ }
+
+ // Use the agent version's ID if available, otherwise generate one from name and version.
+ // This handles cases where hosted agents (like MCP agents) may not have an ID assigned.
+ var version = string.IsNullOrWhiteSpace(agentVersion.Version) ? "latest" : agentVersion.Version;
+ var agentId = string.IsNullOrWhiteSpace(agentVersion.Id)
+ ? $"{agentVersion.Name}:{version}"
+ : agentVersion.Id;
+
+ var agentOptions = new ChatClientAgentOptions()
+ {
+ Id = agentId,
+ Name = agentVersion.Name,
+ Description = agentVersion.Description,
+ };
+
+ if (agentDefinition is PromptAgentDefinition promptAgentDefinition)
+ {
+ agentOptions.ChatOptions ??= chatOptions?.Clone() ?? new();
+ agentOptions.ChatOptions.Instructions = promptAgentDefinition.Instructions;
+ agentOptions.ChatOptions.Temperature = promptAgentDefinition.Temperature;
+ agentOptions.ChatOptions.TopP = promptAgentDefinition.TopP;
+ }
+
+ if (agentTools is { Count: > 0 })
+ {
+ agentOptions.ChatOptions ??= chatOptions?.Clone() ?? new();
+ agentOptions.ChatOptions.Tools = agentTools;
+ }
+
+ return agentOptions;
+ }
+
+#if NET
+ [GeneratedRegex("^[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?$")]
+ private static partial Regex AgentNameValidationRegex();
+#else
+ private static Regex AgentNameValidationRegex() => new("^[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?$");
+#endif
+
+ internal static string ThrowIfInvalidAgentName(string? name)
+ {
+ Throw.IfNullOrWhitespace(name);
+ if (!AgentNameValidationRegex().IsMatch(name))
+ {
+ throw new ArgumentException("Agent name must be 1-63 characters long, start and end with an alphanumeric character, and can only contain alphanumeric characters or hyphens.", nameof(name));
+ }
+ return name;
+ }
+}
+
+[JsonSerializable(typeof(JsonElement))]
+internal sealed partial class AgentClientJsonContext : JsonSerializerContext;
+
+#endregion
diff --git a/dotnet/src/Microsoft.Agents.AI.AzureAI/AzureAIProjectResponsesChatClient.cs b/dotnet/src/Microsoft.Agents.AI.Foundry/AzureAIProjectResponsesChatClient.cs
similarity index 95%
rename from dotnet/src/Microsoft.Agents.AI.AzureAI/AzureAIProjectResponsesChatClient.cs
rename to dotnet/src/Microsoft.Agents.AI.Foundry/AzureAIProjectResponsesChatClient.cs
index 48bb20d766..a768d102f8 100644
--- a/dotnet/src/Microsoft.Agents.AI.AzureAI/AzureAIProjectResponsesChatClient.cs
+++ b/dotnet/src/Microsoft.Agents.AI.Foundry/AzureAIProjectResponsesChatClient.cs
@@ -1,10 +1,11 @@
// Copyright (c) Microsoft. All rights reserved.
+using System;
using Azure.AI.Projects;
using Microsoft.Extensions.AI;
using Microsoft.Shared.Diagnostics;
-namespace Microsoft.Agents.AI.AzureAI;
+namespace Microsoft.Agents.AI.Foundry;
#pragma warning disable OPENAI001
internal sealed class AzureAIProjectResponsesChatClient : DelegatingChatClient
diff --git a/dotnet/src/Microsoft.Agents.AI.AzureAI/CompatibilitySuppressions.xml b/dotnet/src/Microsoft.Agents.AI.Foundry/CompatibilitySuppressions.xml
similarity index 100%
rename from dotnet/src/Microsoft.Agents.AI.AzureAI/CompatibilitySuppressions.xml
rename to dotnet/src/Microsoft.Agents.AI.Foundry/CompatibilitySuppressions.xml
diff --git a/dotnet/src/Microsoft.Agents.AI.AzureAI/FoundryAITool.cs b/dotnet/src/Microsoft.Agents.AI.Foundry/FoundryAITool.cs
similarity index 99%
rename from dotnet/src/Microsoft.Agents.AI.AzureAI/FoundryAITool.cs
rename to dotnet/src/Microsoft.Agents.AI.Foundry/FoundryAITool.cs
index 80ed48e1df..79e221f02f 100644
--- a/dotnet/src/Microsoft.Agents.AI.AzureAI/FoundryAITool.cs
+++ b/dotnet/src/Microsoft.Agents.AI.Foundry/FoundryAITool.cs
@@ -1,5 +1,7 @@
// Copyright (c) Microsoft. All rights reserved.
+using System;
+using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using Azure.AI.Projects.Agents;
using Microsoft.Extensions.AI;
@@ -8,7 +10,7 @@ using OpenAI.Responses;
#pragma warning disable OPENAI001
-namespace Microsoft.Agents.AI.AzureAI;
+namespace Microsoft.Agents.AI.Foundry;
///
/// Provides factory methods for creating instances from Microsoft Foundry and OpenAI response tools.
diff --git a/dotnet/src/Microsoft.Agents.AI.AzureAI/FoundryAgent.cs b/dotnet/src/Microsoft.Agents.AI.Foundry/FoundryAgent.cs
similarity index 89%
rename from dotnet/src/Microsoft.Agents.AI.AzureAI/FoundryAgent.cs
rename to dotnet/src/Microsoft.Agents.AI.Foundry/FoundryAgent.cs
index 66fa93e39f..b1d97a5c9c 100644
--- a/dotnet/src/Microsoft.Agents.AI.AzureAI/FoundryAgent.cs
+++ b/dotnet/src/Microsoft.Agents.AI.Foundry/FoundryAgent.cs
@@ -1,7 +1,11 @@
// Copyright (c) Microsoft. All rights reserved.
+using System;
using System.ClientModel;
+using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
+using System.Threading;
+using System.Threading.Tasks;
using Azure.AI.Extensions.OpenAI;
using Azure.AI.Projects;
using Microsoft.Extensions.AI;
@@ -9,7 +13,7 @@ using Microsoft.Extensions.Logging;
using Microsoft.Shared.DiagnosticIds;
using Microsoft.Shared.Diagnostics;
-namespace Microsoft.Agents.AI.AzureAI;
+namespace Microsoft.Agents.AI.Foundry;
///
/// Provides an that uses Microsoft Foundry for AI agent capabilities.
@@ -163,7 +167,29 @@ public sealed class FoundryAgent : DelegatingAIAgent
},
};
- return AzureAIProjectChatClientExtensions.CreateResponsesChatClientAgent(aiProjectClient, options, clientFactory, loggerFactory, services);
+ return CreateResponsesChatClientAgent(aiProjectClient, options, clientFactory, loggerFactory, services);
+ }
+
+ private static ChatClientAgent CreateResponsesChatClientAgent(
+ AIProjectClient aiProjectClient,
+ ChatClientAgentOptions agentOptions,
+ Func? clientFactory,
+ ILoggerFactory? loggerFactory,
+ IServiceProvider? services)
+ {
+ Throw.IfNull(aiProjectClient);
+ Throw.IfNull(agentOptions);
+ Throw.IfNull(agentOptions.ChatOptions);
+ Throw.IfNullOrWhitespace(agentOptions.ChatOptions.ModelId);
+
+ IChatClient chatClient = new AzureAIProjectResponsesChatClient(aiProjectClient, agentOptions.ChatOptions.ModelId);
+
+ if (clientFactory is not null)
+ {
+ chatClient = clientFactory(chatClient);
+ }
+
+ return new ChatClientAgent(chatClient, agentOptions, loggerFactory, services);
}
private static ChatClientAgent CreateInnerAgentFromEndpoint(
diff --git a/dotnet/src/Microsoft.Agents.AI.FoundryMemory/FoundryMemoryJsonUtilities.cs b/dotnet/src/Microsoft.Agents.AI.Foundry/Memory/FoundryMemoryJsonUtilities.cs
similarity index 84%
rename from dotnet/src/Microsoft.Agents.AI.FoundryMemory/FoundryMemoryJsonUtilities.cs
rename to dotnet/src/Microsoft.Agents.AI.Foundry/Memory/FoundryMemoryJsonUtilities.cs
index 1a0dd4f4e2..1ed4046de0 100644
--- a/dotnet/src/Microsoft.Agents.AI.FoundryMemory/FoundryMemoryJsonUtilities.cs
+++ b/dotnet/src/Microsoft.Agents.AI.Foundry/Memory/FoundryMemoryJsonUtilities.cs
@@ -1,13 +1,16 @@
// Copyright (c) Microsoft. All rights reserved.
+using System.Diagnostics.CodeAnalysis;
using System.Text.Json;
using System.Text.Json.Serialization;
+using Microsoft.Shared.DiagnosticIds;
-namespace Microsoft.Agents.AI.FoundryMemory;
+namespace Microsoft.Agents.AI.Foundry;
///
/// Provides JSON serialization utilities for the Foundry Memory provider.
///
+[Experimental(DiagnosticIds.Experiments.AgentsAIExperiments)]
internal static class FoundryMemoryJsonUtilities
{
///
@@ -33,4 +36,5 @@ internal static class FoundryMemoryJsonUtilities
WriteIndented = false)]
[JsonSerializable(typeof(FoundryMemoryProviderScope))]
[JsonSerializable(typeof(FoundryMemoryProvider.State))]
+[Experimental(DiagnosticIds.Experiments.AgentsAIExperiments)]
internal partial class FoundryMemoryJsonContext : JsonSerializerContext;
diff --git a/dotnet/src/Microsoft.Agents.AI.FoundryMemory/FoundryMemoryProvider.cs b/dotnet/src/Microsoft.Agents.AI.Foundry/Memory/FoundryMemoryProvider.cs
similarity index 99%
rename from dotnet/src/Microsoft.Agents.AI.FoundryMemory/FoundryMemoryProvider.cs
rename to dotnet/src/Microsoft.Agents.AI.Foundry/Memory/FoundryMemoryProvider.cs
index 93b343b2de..cc92c40a41 100644
--- a/dotnet/src/Microsoft.Agents.AI.FoundryMemory/FoundryMemoryProvider.cs
+++ b/dotnet/src/Microsoft.Agents.AI.Foundry/Memory/FoundryMemoryProvider.cs
@@ -16,7 +16,7 @@ using Microsoft.Shared.DiagnosticIds;
using Microsoft.Shared.Diagnostics;
using OpenAI.Responses;
-namespace Microsoft.Agents.AI.FoundryMemory;
+namespace Microsoft.Agents.AI.Foundry;
///
/// Provides a Microsoft Foundry Memory backed that persists conversation messages as memories
@@ -27,7 +27,7 @@ namespace Microsoft.Agents.AI.FoundryMemory;
/// for new invocations using the memory search endpoint. Retrieved memories are injected as user messages
/// to the model, prefixed by a configurable context prompt.
///
-[Experimental(DiagnosticIds.Experiments.AIOpenAIResponses)]
+[Experimental(DiagnosticIds.Experiments.AgentsAIExperiments)]
public sealed class FoundryMemoryProvider : AIContextProvider
{
private const string DefaultContextPrompt = "## Memories\nConsider the following memories when answering user questions:";
diff --git a/dotnet/src/Microsoft.Agents.AI.FoundryMemory/FoundryMemoryProviderOptions.cs b/dotnet/src/Microsoft.Agents.AI.Foundry/Memory/FoundryMemoryProviderOptions.cs
similarity index 95%
rename from dotnet/src/Microsoft.Agents.AI.FoundryMemory/FoundryMemoryProviderOptions.cs
rename to dotnet/src/Microsoft.Agents.AI.Foundry/Memory/FoundryMemoryProviderOptions.cs
index cf4fb5ab15..668db44112 100644
--- a/dotnet/src/Microsoft.Agents.AI.FoundryMemory/FoundryMemoryProviderOptions.cs
+++ b/dotnet/src/Microsoft.Agents.AI.Foundry/Memory/FoundryMemoryProviderOptions.cs
@@ -2,14 +2,17 @@
using System;
using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
using Microsoft.Extensions.AI;
using Microsoft.Extensions.Compliance.Redaction;
+using Microsoft.Shared.DiagnosticIds;
-namespace Microsoft.Agents.AI.FoundryMemory;
+namespace Microsoft.Agents.AI.Foundry;
///
/// Options for configuring the .
///
+[Experimental(DiagnosticIds.Experiments.AgentsAIExperiments)]
public sealed class FoundryMemoryProviderOptions
{
///
diff --git a/dotnet/src/Microsoft.Agents.AI.FoundryMemory/FoundryMemoryProviderScope.cs b/dotnet/src/Microsoft.Agents.AI.Foundry/Memory/FoundryMemoryProviderScope.cs
similarity index 89%
rename from dotnet/src/Microsoft.Agents.AI.FoundryMemory/FoundryMemoryProviderScope.cs
rename to dotnet/src/Microsoft.Agents.AI.Foundry/Memory/FoundryMemoryProviderScope.cs
index 6646c482a3..769aff7370 100644
--- a/dotnet/src/Microsoft.Agents.AI.FoundryMemory/FoundryMemoryProviderScope.cs
+++ b/dotnet/src/Microsoft.Agents.AI.Foundry/Memory/FoundryMemoryProviderScope.cs
@@ -1,9 +1,11 @@
// Copyright (c) Microsoft. All rights reserved.
using System;
+using System.Diagnostics.CodeAnalysis;
+using Microsoft.Shared.DiagnosticIds;
using Microsoft.Shared.Diagnostics;
-namespace Microsoft.Agents.AI.FoundryMemory;
+namespace Microsoft.Agents.AI.Foundry;
///
/// Allows scoping of memories for the .
@@ -13,6 +15,7 @@ namespace Microsoft.Agents.AI.FoundryMemory;
/// Common patterns include using a user ID, team ID, or other unique identifier
/// to partition memories across different contexts.
///
+[Experimental(DiagnosticIds.Experiments.AgentsAIExperiments)]
public sealed class FoundryMemoryProviderScope
{
///
diff --git a/dotnet/src/Microsoft.Agents.AI.FoundryMemory/AIProjectClientExtensions.cs b/dotnet/src/Microsoft.Agents.AI.Foundry/Memory/MemoryStoreExtensions.cs
similarity index 93%
rename from dotnet/src/Microsoft.Agents.AI.FoundryMemory/AIProjectClientExtensions.cs
rename to dotnet/src/Microsoft.Agents.AI.Foundry/Memory/MemoryStoreExtensions.cs
index 9e24703d92..a696a33e5a 100644
--- a/dotnet/src/Microsoft.Agents.AI.FoundryMemory/AIProjectClientExtensions.cs
+++ b/dotnet/src/Microsoft.Agents.AI.Foundry/Memory/MemoryStoreExtensions.cs
@@ -5,12 +5,12 @@ using System.Threading;
using System.Threading.Tasks;
using Azure.AI.Projects;
-namespace Microsoft.Agents.AI.FoundryMemory;
+namespace Microsoft.Agents.AI.Foundry;
///
/// Internal extension methods for to provide MemoryStores helper operations.
///
-internal static class AIProjectClientExtensions
+internal static class MemoryStoreExtensions
{
///
/// Creates a memory store if it doesn't already exist.
diff --git a/dotnet/src/Microsoft.Agents.AI.AzureAI/Microsoft.Agents.AI.AzureAI.csproj b/dotnet/src/Microsoft.Agents.AI.Foundry/Microsoft.Agents.AI.Foundry.csproj
similarity index 63%
rename from dotnet/src/Microsoft.Agents.AI.AzureAI/Microsoft.Agents.AI.AzureAI.csproj
rename to dotnet/src/Microsoft.Agents.AI.Foundry/Microsoft.Agents.AI.Foundry.csproj
index 0cd8690126..03d84c9d47 100644
--- a/dotnet/src/Microsoft.Agents.AI.AzureAI/Microsoft.Agents.AI.AzureAI.csproj
+++ b/dotnet/src/Microsoft.Agents.AI.Foundry/Microsoft.Agents.AI.Foundry.csproj
@@ -2,21 +2,29 @@
true
- enable
true
+ $(NoWarn);OPENAI001
+
+
+
+ false
+
+
true
true
+ true
+
@@ -30,4 +38,9 @@
Provides Microsoft Agent Framework support for Foundry Agents.
+
+
+
+
+
diff --git a/dotnet/src/Microsoft.Agents.AI.AzureAI/ProjectResponsesClientExtensions.cs b/dotnet/src/Microsoft.Agents.AI.Foundry/ProjectResponsesClientExtensions.cs
similarity index 99%
rename from dotnet/src/Microsoft.Agents.AI.AzureAI/ProjectResponsesClientExtensions.cs
rename to dotnet/src/Microsoft.Agents.AI.Foundry/ProjectResponsesClientExtensions.cs
index 5a899d5076..cd253776a8 100644
--- a/dotnet/src/Microsoft.Agents.AI.AzureAI/ProjectResponsesClientExtensions.cs
+++ b/dotnet/src/Microsoft.Agents.AI.Foundry/ProjectResponsesClientExtensions.cs
@@ -1,5 +1,6 @@
// Copyright (c) Microsoft. All rights reserved.
+using System;
using System.Diagnostics.CodeAnalysis;
using Microsoft.Extensions.AI;
using Microsoft.Shared.DiagnosticIds;
diff --git a/dotnet/src/Microsoft.Agents.AI.AzureAI/RequestOptionsExtensions.cs b/dotnet/src/Microsoft.Agents.AI.Foundry/RequestOptionsExtensions.cs
similarity index 96%
rename from dotnet/src/Microsoft.Agents.AI.AzureAI/RequestOptionsExtensions.cs
rename to dotnet/src/Microsoft.Agents.AI.Foundry/RequestOptionsExtensions.cs
index 2705611b57..03e48e293b 100644
--- a/dotnet/src/Microsoft.Agents.AI.AzureAI/RequestOptionsExtensions.cs
+++ b/dotnet/src/Microsoft.Agents.AI.Foundry/RequestOptionsExtensions.cs
@@ -1,7 +1,10 @@
// Copyright (c) Microsoft. All rights reserved.
using System.ClientModel.Primitives;
+using System.Collections.Generic;
using System.Reflection;
+using System.Threading;
+using System.Threading.Tasks;
namespace Microsoft.Agents.AI;
diff --git a/dotnet/src/Microsoft.Agents.AI.FoundryMemory/Microsoft.Agents.AI.FoundryMemory.csproj b/dotnet/src/Microsoft.Agents.AI.FoundryMemory/Microsoft.Agents.AI.FoundryMemory.csproj
deleted file mode 100644
index 7abc3d0bcc..0000000000
--- a/dotnet/src/Microsoft.Agents.AI.FoundryMemory/Microsoft.Agents.AI.FoundryMemory.csproj
+++ /dev/null
@@ -1,39 +0,0 @@
-
-
-
- preview
- $(NoWarn);OPENAI001
-
-
-
- true
- true
- true
- true
- true
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Microsoft Agent Framework - Azure AI Foundry Memory integration
- Provides Azure AI Foundry Memory integration for Microsoft Agent Framework.
-
-
-
-
-
-
-
-
diff --git a/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative.AzureAI/AzureAgentProvider.cs b/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative.Foundry/AzureAgentProvider.cs
similarity index 100%
rename from dotnet/src/Microsoft.Agents.AI.Workflows.Declarative.AzureAI/AzureAgentProvider.cs
rename to dotnet/src/Microsoft.Agents.AI.Workflows.Declarative.Foundry/AzureAgentProvider.cs
diff --git a/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative.AzureAI/CompatibilitySuppressions.xml b/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative.Foundry/CompatibilitySuppressions.xml
similarity index 80%
rename from dotnet/src/Microsoft.Agents.AI.Workflows.Declarative.AzureAI/CompatibilitySuppressions.xml
rename to dotnet/src/Microsoft.Agents.AI.Workflows.Declarative.Foundry/CompatibilitySuppressions.xml
index 3454984fae..fbf1db84c7 100644
--- a/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative.AzureAI/CompatibilitySuppressions.xml
+++ b/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative.Foundry/CompatibilitySuppressions.xml
@@ -1,39 +1,39 @@
-
+
CP0002
M:Microsoft.Agents.AI.Workflows.Declarative.AzureAgentProvider.get_OpenAIClientOptions
- lib/net10.0/Microsoft.Agents.AI.Workflows.Declarative.AzureAI.dll
- lib/net10.0/Microsoft.Agents.AI.Workflows.Declarative.AzureAI.dll
+ lib/net10.0/Microsoft.Agents.AI.Workflows.Declarative.Foundry.dll
+ lib/net10.0/Microsoft.Agents.AI.Workflows.Declarative.Foundry.dll
true
CP0002
M:Microsoft.Agents.AI.Workflows.Declarative.AzureAgentProvider.get_OpenAIClientOptions
- lib/net472/Microsoft.Agents.AI.Workflows.Declarative.AzureAI.dll
- lib/net472/Microsoft.Agents.AI.Workflows.Declarative.AzureAI.dll
+ lib/net472/Microsoft.Agents.AI.Workflows.Declarative.Foundry.dll
+ lib/net472/Microsoft.Agents.AI.Workflows.Declarative.Foundry.dll
true
CP0002
M:Microsoft.Agents.AI.Workflows.Declarative.AzureAgentProvider.get_OpenAIClientOptions
- lib/net8.0/Microsoft.Agents.AI.Workflows.Declarative.AzureAI.dll
- lib/net8.0/Microsoft.Agents.AI.Workflows.Declarative.AzureAI.dll
+ lib/net8.0/Microsoft.Agents.AI.Workflows.Declarative.Foundry.dll
+ lib/net8.0/Microsoft.Agents.AI.Workflows.Declarative.Foundry.dll
true
CP0002
M:Microsoft.Agents.AI.Workflows.Declarative.AzureAgentProvider.get_OpenAIClientOptions
- lib/net9.0/Microsoft.Agents.AI.Workflows.Declarative.AzureAI.dll
- lib/net9.0/Microsoft.Agents.AI.Workflows.Declarative.AzureAI.dll
+ lib/net9.0/Microsoft.Agents.AI.Workflows.Declarative.Foundry.dll
+ lib/net9.0/Microsoft.Agents.AI.Workflows.Declarative.Foundry.dll
true
CP0002
M:Microsoft.Agents.AI.Workflows.Declarative.AzureAgentProvider.get_OpenAIClientOptions
- lib/netstandard2.0/Microsoft.Agents.AI.Workflows.Declarative.AzureAI.dll
- lib/netstandard2.0/Microsoft.Agents.AI.Workflows.Declarative.AzureAI.dll
+ lib/netstandard2.0/Microsoft.Agents.AI.Workflows.Declarative.Foundry.dll
+ lib/netstandard2.0/Microsoft.Agents.AI.Workflows.Declarative.Foundry.dll
true
\ No newline at end of file
diff --git a/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative.AzureAI/Microsoft.Agents.AI.Workflows.Declarative.AzureAI.csproj b/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative.Foundry/Microsoft.Agents.AI.Workflows.Declarative.Foundry.csproj
similarity index 70%
rename from dotnet/src/Microsoft.Agents.AI.Workflows.Declarative.AzureAI/Microsoft.Agents.AI.Workflows.Declarative.AzureAI.csproj
rename to dotnet/src/Microsoft.Agents.AI.Workflows.Declarative.Foundry/Microsoft.Agents.AI.Workflows.Declarative.Foundry.csproj
index 5bf9f6d29e..407593536e 100644
--- a/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative.AzureAI/Microsoft.Agents.AI.Workflows.Declarative.AzureAI.csproj
+++ b/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative.Foundry/Microsoft.Agents.AI.Workflows.Declarative.Foundry.csproj
@@ -13,10 +13,16 @@
+
+
+
+ false
+
+
- Microsoft Agent Framework Declarative Workflows Azure AI
- Provides Microsoft Agent Framework support for declarative workflows for Azure AI Agents.
+ Microsoft Agent Framework Declarative Workflows Foundry
+ Provides Microsoft Agent Framework support for declarative workflows for Microsoft Foundry Agents.
@@ -24,7 +30,7 @@
-
+
diff --git a/dotnet/src/Shared/IntegrationTests/TestSettings.cs b/dotnet/src/Shared/IntegrationTests/TestSettings.cs
index 880db9d1cd..de5757314c 100644
--- a/dotnet/src/Shared/IntegrationTests/TestSettings.cs
+++ b/dotnet/src/Shared/IntegrationTests/TestSettings.cs
@@ -16,6 +16,7 @@ internal static class TestSettings
// Azure AI (Foundry)
public const string AzureAIBingConnectionId = "AZURE_AI_BING_CONNECTION_ID";
+ public const string AzureAIEmbeddingDeploymentName = "AZURE_AI_EMBEDDING_DEPLOYMENT_NAME";
public const string AzureAIMemoryStoreId = "AZURE_AI_MEMORY_STORE_ID";
public const string AzureAIModelDeploymentName = "AZURE_AI_MODEL_DEPLOYMENT_NAME";
public const string AzureAIProjectEndpoint = "AZURE_AI_PROJECT_ENDPOINT";
diff --git a/dotnet/tests/AzureAI.IntegrationTests/AIProjectClientChatClientAgentRunStreamingTests.cs b/dotnet/tests/AzureAI.IntegrationTests/AIProjectClientChatClientAgentRunStreamingTests.cs
deleted file mode 100644
index 0e507e44c1..0000000000
--- a/dotnet/tests/AzureAI.IntegrationTests/AIProjectClientChatClientAgentRunStreamingTests.cs
+++ /dev/null
@@ -1,18 +0,0 @@
-// Copyright (c) Microsoft. All rights reserved.
-
-using System;
-using System.Threading.Tasks;
-using AgentConformance.IntegrationTests;
-
-namespace AzureAI.IntegrationTests;
-
-#pragma warning disable CS0618 // Tests intentionally exercise obsolete AIProjectClientFixture
-[Obsolete("Use FoundryVersionedAgentRunTests instead. These tests exercise obsolete AIProjectClient extension methods.")]
-public class AIProjectClientChatClientAgentRunStreamingTests() : ChatClientAgentRunStreamingTests(() => new())
-{
- public override Task RunWithInstructionsAndNoMessageReturnsExpectedResultAsync()
- {
- Assert.Skip("No messages is not supported");
- return base.RunWithInstructionsAndNoMessageReturnsExpectedResultAsync();
- }
-}
diff --git a/dotnet/tests/AzureAI.IntegrationTests/AIProjectClientChatClientAgentRunTests.cs b/dotnet/tests/AzureAI.IntegrationTests/AIProjectClientChatClientAgentRunTests.cs
deleted file mode 100644
index a0ee72ebd4..0000000000
--- a/dotnet/tests/AzureAI.IntegrationTests/AIProjectClientChatClientAgentRunTests.cs
+++ /dev/null
@@ -1,18 +0,0 @@
-// Copyright (c) Microsoft. All rights reserved.
-
-using System;
-using System.Threading.Tasks;
-using AgentConformance.IntegrationTests;
-
-namespace AzureAI.IntegrationTests;
-
-#pragma warning disable CS0618 // Tests intentionally exercise obsolete AIProjectClientFixture
-[Obsolete("Use FoundryVersionedAgentRunTests instead. These tests exercise obsolete AIProjectClient extension methods.")]
-public class AIProjectClientChatClientAgentRunTests() : ChatClientAgentRunTests(() => new())
-{
- public override Task RunWithInstructionsAndNoMessageReturnsExpectedResultAsync()
- {
- Assert.Skip("No messages is not supported");
- return base.RunWithInstructionsAndNoMessageReturnsExpectedResultAsync();
- }
-}
diff --git a/dotnet/tests/AzureAI.IntegrationTests/AzureAI.IntegrationTests.csproj b/dotnet/tests/Foundry.IntegrationTests/Foundry.IntegrationTests.csproj
similarity index 83%
rename from dotnet/tests/AzureAI.IntegrationTests/AzureAI.IntegrationTests.csproj
rename to dotnet/tests/Foundry.IntegrationTests/Foundry.IntegrationTests.csproj
index 2703360cb2..dbff50104c 100644
--- a/dotnet/tests/AzureAI.IntegrationTests/AzureAI.IntegrationTests.csproj
+++ b/dotnet/tests/Foundry.IntegrationTests/Foundry.IntegrationTests.csproj
@@ -7,7 +7,7 @@
-
+
diff --git a/dotnet/tests/Foundry.IntegrationTests/FoundryVersionedAgentChatClientRunStreamingTests.cs b/dotnet/tests/Foundry.IntegrationTests/FoundryVersionedAgentChatClientRunStreamingTests.cs
new file mode 100644
index 0000000000..c9128050fc
--- /dev/null
+++ b/dotnet/tests/Foundry.IntegrationTests/FoundryVersionedAgentChatClientRunStreamingTests.cs
@@ -0,0 +1,15 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+using System.Threading.Tasks;
+using AgentConformance.IntegrationTests;
+
+namespace Foundry.IntegrationTests;
+
+public class FoundryVersionedAgentChatClientRunStreamingTests() : ChatClientAgentRunStreamingTests(() => new())
+{
+ public override Task RunWithInstructionsAndNoMessageReturnsExpectedResultAsync()
+ {
+ Assert.Skip("No messages is not supported");
+ return base.RunWithInstructionsAndNoMessageReturnsExpectedResultAsync();
+ }
+}
diff --git a/dotnet/tests/Foundry.IntegrationTests/FoundryVersionedAgentChatClientRunTests.cs b/dotnet/tests/Foundry.IntegrationTests/FoundryVersionedAgentChatClientRunTests.cs
new file mode 100644
index 0000000000..fff2ad529f
--- /dev/null
+++ b/dotnet/tests/Foundry.IntegrationTests/FoundryVersionedAgentChatClientRunTests.cs
@@ -0,0 +1,15 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+using System.Threading.Tasks;
+using AgentConformance.IntegrationTests;
+
+namespace Foundry.IntegrationTests;
+
+public class FoundryVersionedAgentChatClientRunTests() : ChatClientAgentRunTests(() => new())
+{
+ public override Task RunWithInstructionsAndNoMessageReturnsExpectedResultAsync()
+ {
+ Assert.Skip("No messages is not supported");
+ return base.RunWithInstructionsAndNoMessageReturnsExpectedResultAsync();
+ }
+}
diff --git a/dotnet/tests/AzureAI.IntegrationTests/AIProjectClientCreateTests.cs b/dotnet/tests/Foundry.IntegrationTests/FoundryVersionedAgentCreateTests.cs
similarity index 70%
rename from dotnet/tests/AzureAI.IntegrationTests/AIProjectClientCreateTests.cs
rename to dotnet/tests/Foundry.IntegrationTests/FoundryVersionedAgentCreateTests.cs
index bc5f38acf2..160ab697f7 100644
--- a/dotnet/tests/AzureAI.IntegrationTests/AIProjectClientCreateTests.cs
+++ b/dotnet/tests/Foundry.IntegrationTests/FoundryVersionedAgentCreateTests.cs
@@ -1,7 +1,5 @@
// Copyright (c) Microsoft. All rights reserved.
-#pragma warning disable CS0618 // Tests intentionally exercise obsolete extension methods
-
using System;
using System.IO;
using System.Threading.Tasks;
@@ -9,45 +7,43 @@ using AgentConformance.IntegrationTests.Support;
using Azure.AI.Projects;
using Azure.AI.Projects.Agents;
using Microsoft.Agents.AI;
-using Microsoft.Agents.AI.AzureAI;
+using Microsoft.Agents.AI.Foundry;
using Microsoft.Extensions.AI;
using OpenAI.Files;
using OpenAI.Responses;
using Shared.IntegrationTests;
-namespace AzureAI.IntegrationTests;
+namespace Foundry.IntegrationTests;
-[Obsolete("Use FoundryVersionedAgentCreateTests instead. These tests exercise obsolete AIProjectClient extension methods.")]
-public class AIProjectClientCreateTests
+///
+/// Integration tests for versioned creation via
+/// AIProjectClient.Agents.CreateAgentVersionAsync and AIProjectClient.AsAIAgent(AgentVersion).
+///
+public class FoundryVersionedAgentCreateTests
{
private readonly AIProjectClient _client = new(new Uri(TestConfiguration.GetRequiredValue(TestSettings.AzureAIProjectEndpoint)), TestAzureCliCredentials.CreateAzureCliCredential());
- [Theory]
- [InlineData("CreateWithChatClientAgentOptionsAsync")]
- [InlineData("CreateWithFoundryOptionsAsync")]
- public async Task CreateAgent_CreatesAgentWithCorrectMetadataAsync(string createMechanism)
+ [Fact]
+ public async Task CreateAgent_CreatesAgentWithCorrectMetadataAsync()
{
// Arrange.
- string AgentName = AIProjectClientFixture.GenerateUniqueAgentName("IntegrationTestAgent");
+ string AgentName = FoundryVersionedAgentFixture.GenerateUniqueAgentName("IntegrationTestAgent");
const string AgentDescription = "An agent created during integration tests";
const string AgentInstructions = "You are an integration test agent";
// Act.
- var agent = createMechanism switch
- {
- "CreateWithChatClientAgentOptionsAsync" => await this._client.CreateAIAgentAsync(
- model: TestConfiguration.GetRequiredValue(TestSettings.AzureAIModelDeploymentName),
- options: new ChatClientAgentOptions()
+ var agentVersion = await this._client.Agents.CreateAgentVersionAsync(
+ AgentName,
+ new AgentVersionCreationOptions(
+ new PromptAgentDefinition(TestConfiguration.GetRequiredValue(TestSettings.AzureAIModelDeploymentName))
{
- Name = AgentName,
- Description = AgentDescription,
- ChatOptions = new() { Instructions = AgentInstructions }
- }),
- "CreateWithFoundryOptionsAsync" => await this._client.CreateAIAgentAsync(
- name: AgentName,
- creationOptions: new AgentVersionCreationOptions(new PromptAgentDefinition(TestConfiguration.GetRequiredValue(TestSettings.AzureAIModelDeploymentName)) { Instructions = AgentInstructions }) { Description = AgentDescription }),
- _ => throw new InvalidOperationException($"Unknown create mechanism: {createMechanism}")
- };
+ Instructions = AgentInstructions
+ })
+ {
+ Description = AgentDescription
+ });
+
+ var agent = this._client.AsAIAgent(agentVersion);
try
{
@@ -72,12 +68,11 @@ public class AIProjectClientCreateTests
}
[Theory(Skip = "For manual testing only")]
- [InlineData("CreateWithChatClientAgentOptionsAsync")]
- [InlineData("CreateWithFoundryOptionsAsync")]
- public async Task CreateAgent_CreatesAgentWithVectorStoresAsync(string createMechanism)
+ [InlineData("FileSearchTool")]
+ public async Task CreateAgent_CreatesAgentWithVectorStoresAsync(string _)
{
// Arrange.
- string AgentName = AIProjectClientFixture.GenerateUniqueAgentName("VectorStoreAgent");
+ string AgentName = FoundryVersionedAgentFixture.GenerateUniqueAgentName("VectorStoreAgent");
const string AgentInstructions = """
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.
@@ -99,22 +94,19 @@ public class AIProjectClientCreateTests
);
var vectorStoreMetadata = await projectOpenAIClient.GetProjectVectorStoresClient().CreateVectorStoreAsync(options: new() { FileIds = { uploadedAgentFile.Id }, Name = "WordCodeLookup_VectorStore" });
- // Act.
- var agent = createMechanism switch
+ // Act — create agent version with FileSearch tool via native SDK, then wrap with AsAIAgent.
+ var definition = new PromptAgentDefinition(TestConfiguration.GetRequiredValue(TestSettings.AzureAIModelDeploymentName))
{
- "CreateWithChatClientAgentOptionsAsync" => await this._client.CreateAIAgentAsync(
- model: TestConfiguration.GetRequiredValue(TestSettings.AzureAIModelDeploymentName),
- name: AgentName,
- instructions: AgentInstructions,
- tools: [new HostedFileSearchTool() { Inputs = [new HostedVectorStoreContent(vectorStoreMetadata.Value.Id)] }]),
- "CreateWithFoundryOptionsAsync" => await this._client.CreateAIAgentAsync(
- model: TestConfiguration.GetRequiredValue(TestSettings.AzureAIModelDeploymentName),
- name: AgentName,
- instructions: AgentInstructions,
- tools: [ResponseTool.CreateFileSearchTool(vectorStoreIds: [vectorStoreMetadata.Value.Id]).AsAITool()]),
- _ => throw new InvalidOperationException($"Unknown create mechanism: {createMechanism}")
+ Instructions = AgentInstructions,
+ Tools = { ResponseTool.CreateFileSearchTool(vectorStoreIds: [vectorStoreMetadata.Value.Id]) }
};
+ var agentVersion = await this._client.Agents.CreateAgentVersionAsync(
+ AgentName,
+ new AgentVersionCreationOptions(definition));
+
+ var agent = this._client.AsAIAgent(agentVersion);
+
try
{
// Assert.
@@ -132,13 +124,11 @@ public class AIProjectClientCreateTests
}
}
- [Theory]
- [InlineData("CreateWithChatClientAgentOptionsAsync")]
- [InlineData("CreateWithFoundryOptionsAsync")]
- public async Task CreateAgent_CreatesAgentWithCodeInterpreterAsync(string createMechanism)
+ [Fact]
+ public async Task CreateAgent_CreatesAgentWithCodeInterpreterAsync()
{
// Arrange.
- string AgentName = AIProjectClientFixture.GenerateUniqueAgentName("CodeInterpreterAgent");
+ string AgentName = FoundryVersionedAgentFixture.GenerateUniqueAgentName("CodeInterpreterAgent");
const string AgentInstructions = """
You are a helpful coding agent. A Python file is provided. Use the Code Interpreter Tool to run the file
and report the SECRET_NUMBER value it prints. Respond only with the number.
@@ -158,24 +148,19 @@ public class AIProjectClientCreateTests
purpose: FileUploadPurpose.Assistants
);
- // Act.
- var agent = createMechanism switch
+ // Act — create agent version with CodeInterpreter tool via native SDK, then wrap with AsAIAgent.
+ var definition = new PromptAgentDefinition(TestConfiguration.GetRequiredValue(TestSettings.AzureAIModelDeploymentName))
{
- // Hosted tool path (tools supplied via ChatClientAgentOptions)
- "CreateWithChatClientAgentOptionsAsync" => await this._client.CreateAIAgentAsync(
- model: TestConfiguration.GetRequiredValue(TestSettings.AzureAIModelDeploymentName),
- name: AgentName,
- instructions: AgentInstructions,
- tools: [new HostedCodeInterpreterTool() { Inputs = [new HostedFileContent(uploadedCodeFile.Id)] }]),
- // Foundry (definitions + resources provided directly)
- "CreateWithFoundryOptionsAsync" => await this._client.CreateAIAgentAsync(
- model: TestConfiguration.GetRequiredValue(TestSettings.AzureAIModelDeploymentName),
- name: AgentName,
- instructions: AgentInstructions,
- tools: [ResponseTool.CreateCodeInterpreterTool(new CodeInterpreterToolContainer(CodeInterpreterToolContainerConfiguration.CreateAutomaticContainerConfiguration([uploadedCodeFile.Id]))).AsAITool()]),
- _ => throw new InvalidOperationException($"Unknown create mechanism: {createMechanism}")
+ Instructions = AgentInstructions,
+ Tools = { ResponseTool.CreateCodeInterpreterTool(new CodeInterpreterToolContainer(CodeInterpreterToolContainerConfiguration.CreateAutomaticContainerConfiguration([uploadedCodeFile.Id]))) }
};
+ var agentVersion = await this._client.Agents.CreateAgentVersionAsync(
+ AgentName,
+ new AgentVersionCreationOptions(definition));
+
+ var agent = this._client.AsAIAgent(agentVersion);
+
try
{
// Assert.
@@ -202,7 +187,7 @@ public class AIProjectClientCreateTests
public async Task AsAIAgent_WithOpenAPITool_NativeSDKCreation_InvokesServerSideToolAsync()
{
// Arrange — create agent version with OpenAPI tool using native Azure.AI.Projects SDK types.
- string AgentName = AIProjectClientFixture.GenerateUniqueAgentName("OpenAPITestAgent");
+ string AgentName = FoundryVersionedAgentFixture.GenerateUniqueAgentName("OpenAPITestAgent");
const string AgentInstructions = "You are a helpful assistant that can use the countries API to retrieve information about countries by their currency code.";
const string CountriesOpenApiSpec = """
@@ -320,28 +305,29 @@ public class AIProjectClientCreateTests
}
}
- [Theory]
- [InlineData("CreateWithChatClientAgentOptionsAsync")]
- public async Task CreateAgent_CreatesAgentWithAIFunctionToolsAsync(string createMechanism)
+ [Fact]
+ public async Task CreateAgent_CreatesAgentWithAIFunctionToolsAsync()
{
// Arrange.
- string AgentName = AIProjectClientFixture.GenerateUniqueAgentName("WeatherAgent");
+ string AgentName = FoundryVersionedAgentFixture.GenerateUniqueAgentName("WeatherAgent");
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);
- FoundryAgent agent = createMechanism switch
+ // 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 PromptAgentDefinition(TestConfiguration.GetRequiredValue(TestSettings.AzureAIModelDeploymentName))
{
- "CreateWithChatClientAgentOptionsAsync" => await this._client.CreateAIAgentAsync(
- model: TestConfiguration.GetRequiredValue(TestSettings.AzureAIModelDeploymentName),
- options: new ChatClientAgentOptions()
- {
- Name = AgentName,
- ChatOptions = new() { Instructions = AgentInstructions, Tools = [weatherFunction] }
- }),
- _ => throw new InvalidOperationException($"Unknown create mechanism: {createMechanism}")
+ Instructions = AgentInstructions,
};
+ definition.Tools.Add(weatherFunction.AsOpenAIResponseTool());
+
+ var agentVersion = await this._client.Agents.CreateAgentVersionAsync(
+ AgentName,
+ new AgentVersionCreationOptions(definition));
+
+ FoundryAgent agent = this._client.AsAIAgent(agentVersion, tools: [weatherFunction]);
try
{
diff --git a/dotnet/tests/AzureAI.IntegrationTests/AIProjectClientFixture.cs b/dotnet/tests/Foundry.IntegrationTests/FoundryVersionedAgentFixture.cs
similarity index 70%
rename from dotnet/tests/AzureAI.IntegrationTests/AIProjectClientFixture.cs
rename to dotnet/tests/Foundry.IntegrationTests/FoundryVersionedAgentFixture.cs
index 112c76571b..2c06404eb6 100644
--- a/dotnet/tests/AzureAI.IntegrationTests/AIProjectClientFixture.cs
+++ b/dotnet/tests/Foundry.IntegrationTests/FoundryVersionedAgentFixture.cs
@@ -1,7 +1,5 @@
// Copyright (c) Microsoft. All rights reserved.
-#pragma warning disable CS0618 // Tests intentionally exercise obsolete extension methods
-
using System;
using System.Collections.Generic;
using System.Linq;
@@ -10,16 +8,21 @@ using AgentConformance.IntegrationTests;
using AgentConformance.IntegrationTests.Support;
using Azure.AI.Extensions.OpenAI;
using Azure.AI.Projects;
+using Azure.AI.Projects.Agents;
using Microsoft.Agents.AI;
-using Microsoft.Agents.AI.AzureAI;
+using Microsoft.Agents.AI.Foundry;
using Microsoft.Extensions.AI;
using OpenAI.Responses;
using Shared.IntegrationTests;
-namespace AzureAI.IntegrationTests;
+namespace Foundry.IntegrationTests;
-[Obsolete("Use FoundryVersionedAgentFixture instead. These tests exercise obsolete AIProjectClient extension methods.")]
-public class AIProjectClientFixture : IChatClientAgentFixture
+///
+/// Integration test fixture that creates versioned Foundry agents via
+/// AIProjectClient.Agents.CreateAgentVersionAsync and wraps them
+/// with AIProjectClient.AsAIAgent(AgentVersion).
+///
+public class FoundryVersionedAgentFixture : IChatClientAgentFixture
{
private FoundryAgent _agent = null!;
private AIProjectClient _client = null!;
@@ -40,7 +43,6 @@ public class AIProjectClientFixture : IChatClientAgentFixture
if (chatClientSession.ConversationId?.StartsWith("conv_", StringComparison.OrdinalIgnoreCase) == true)
{
- // Conversation sessions do not persist message history.
return await this.GetChatHistoryFromConversationAsync(chatClientSession.ConversationId);
}
@@ -119,14 +121,48 @@ public class AIProjectClientFixture : IChatClientAgentFixture
string instructions = "You are a helpful assistant.",
IList? aiTools = null)
{
- return (await this._client.CreateAIAgentAsync(GenerateUniqueAgentName(name), model: TestConfiguration.GetRequiredValue(TestSettings.AzureAIModelDeploymentName), instructions: instructions, tools: aiTools)).GetService()!;
+ var definition = new PromptAgentDefinition(TestConfiguration.GetRequiredValue(TestSettings.AzureAIModelDeploymentName))
+ {
+ Instructions = instructions
+ };
+
+ // Register AIFunction tool definitions in the server-side agent definition so the model
+ // can invoke them. The local AIFunction implementations are matched by name via AsAIAgent.
+ if (aiTools is not null)
+ {
+ foreach (var tool in aiTools)
+ {
+ if (tool.AsOpenAIResponseTool() is ResponseTool responseTool)
+ {
+ definition.Tools.Add(responseTool);
+ }
+ }
+ }
+
+ var agentVersion = await this._client.Agents.CreateAgentVersionAsync(
+ GenerateUniqueAgentName(name),
+ new AgentVersionCreationOptions(definition));
+
+ return this._client.AsAIAgent(agentVersion, tools: aiTools).GetService()!;
}
public async Task CreateChatClientAgentAsync(ChatClientAgentOptions options)
{
options.Name ??= GenerateUniqueAgentName("HelpfulAssistant");
- return (await this._client.CreateAIAgentAsync(model: TestConfiguration.GetRequiredValue(TestSettings.AzureAIModelDeploymentName), options)).GetService()!;
+ var definition = new PromptAgentDefinition(
+ options.ChatOptions?.ModelId ?? TestConfiguration.GetRequiredValue(TestSettings.AzureAIModelDeploymentName))
+ {
+ Instructions = options.ChatOptions?.Instructions
+ };
+
+ var agentVersion = await this._client.Agents.CreateAgentVersionAsync(
+ options.Name,
+ new AgentVersionCreationOptions(definition) { Description = options.Description });
+
+ var agent = this._client.AsAIAgent(agentVersion, tools: options.ChatOptions?.Tools);
+
+ return agent.GetService()!;
}
public static string GenerateUniqueAgentName(string baseName) =>
@@ -174,13 +210,33 @@ public class AIProjectClientFixture : IChatClientAgentFixture
public virtual async ValueTask InitializeAsync()
{
this._client = new(new Uri(TestConfiguration.GetRequiredValue(TestSettings.AzureAIProjectEndpoint)), TestAzureCliCredentials.CreateAzureCliCredential());
- this._agent = await this._client.CreateAIAgentAsync(GenerateUniqueAgentName("HelpfulAssistant"), model: TestConfiguration.GetRequiredValue(TestSettings.AzureAIModelDeploymentName), instructions: "You are a helpful assistant.");
+
+ var agentVersion = await this._client.Agents.CreateAgentVersionAsync(
+ GenerateUniqueAgentName("HelpfulAssistant"),
+ new AgentVersionCreationOptions(
+ new PromptAgentDefinition(TestConfiguration.GetRequiredValue(TestSettings.AzureAIModelDeploymentName))
+ {
+ Instructions = "You are a helpful assistant."
+ }));
+
+ this._agent = this._client.AsAIAgent(agentVersion);
}
public async Task InitializeAsync(ChatClientAgentOptions options)
{
this._client = new(new Uri(TestConfiguration.GetRequiredValue(TestSettings.AzureAIProjectEndpoint)), TestAzureCliCredentials.CreateAzureCliCredential());
options.Name ??= GenerateUniqueAgentName("HelpfulAssistant");
- this._agent = await this._client.CreateAIAgentAsync(model: TestConfiguration.GetRequiredValue(TestSettings.AzureAIModelDeploymentName), options);
+
+ var definition = new PromptAgentDefinition(
+ options.ChatOptions?.ModelId ?? TestConfiguration.GetRequiredValue(TestSettings.AzureAIModelDeploymentName))
+ {
+ Instructions = options.ChatOptions?.Instructions
+ };
+
+ var agentVersion = await this._client.Agents.CreateAgentVersionAsync(
+ options.Name,
+ new AgentVersionCreationOptions(definition) { Description = options.Description });
+
+ this._agent = this._client.AsAIAgent(agentVersion, tools: options.ChatOptions?.Tools);
}
}
diff --git a/dotnet/tests/AzureAI.IntegrationTests/AIProjectClientAgentRunTests.cs b/dotnet/tests/Foundry.IntegrationTests/FoundryVersionedAgentRunStreamingTests.cs
similarity index 57%
rename from dotnet/tests/AzureAI.IntegrationTests/AIProjectClientAgentRunTests.cs
rename to dotnet/tests/Foundry.IntegrationTests/FoundryVersionedAgentRunStreamingTests.cs
index ad10ec5b7a..f5df9f1f42 100644
--- a/dotnet/tests/AzureAI.IntegrationTests/AIProjectClientAgentRunTests.cs
+++ b/dotnet/tests/Foundry.IntegrationTests/FoundryVersionedAgentRunStreamingTests.cs
@@ -5,11 +5,9 @@ using System.Threading.Tasks;
using AgentConformance.IntegrationTests;
using Microsoft.Agents.AI;
-namespace AzureAI.IntegrationTests;
+namespace Foundry.IntegrationTests;
-#pragma warning disable CS0618 // Tests intentionally exercise obsolete AIProjectClientFixture
-[Obsolete("Use FoundryVersionedAgentRunTests instead. These tests exercise obsolete AIProjectClient extension methods.")]
-public class AIProjectClientAgentRunPreviousResponseTests() : RunTests(() => new())
+public class FoundryVersionedAgentRunStreamingPreviousResponseTests() : RunStreamingTests(() => new())
{
public override Task RunWithNoMessageDoesNotFailAsync()
{
@@ -18,8 +16,7 @@ public class AIProjectClientAgentRunPreviousResponseTests() : RunTests(() => new())
+public class FoundryVersionedAgentRunStreamingConversationTests() : RunStreamingTests(() => new())
{
public override Func> AgentRunOptionsFactory => async () =>
{
diff --git a/dotnet/tests/AzureAI.IntegrationTests/AIProjectClientAgentRunStreamingTests.cs b/dotnet/tests/Foundry.IntegrationTests/FoundryVersionedAgentRunTests.cs
similarity index 56%
rename from dotnet/tests/AzureAI.IntegrationTests/AIProjectClientAgentRunStreamingTests.cs
rename to dotnet/tests/Foundry.IntegrationTests/FoundryVersionedAgentRunTests.cs
index 0f2b123fd9..9cefbd0f46 100644
--- a/dotnet/tests/AzureAI.IntegrationTests/AIProjectClientAgentRunStreamingTests.cs
+++ b/dotnet/tests/Foundry.IntegrationTests/FoundryVersionedAgentRunTests.cs
@@ -5,11 +5,9 @@ using System.Threading.Tasks;
using AgentConformance.IntegrationTests;
using Microsoft.Agents.AI;
-namespace AzureAI.IntegrationTests;
+namespace Foundry.IntegrationTests;
-#pragma warning disable CS0618 // Tests intentionally exercise obsolete AIProjectClientFixture
-[Obsolete("Use FoundryVersionedAgentRunTests instead. These tests exercise obsolete AIProjectClient extension methods.")]
-public class AIProjectClientAgentRunStreamingPreviousResponseTests() : RunStreamingTests(() => new())
+public class FoundryVersionedAgentRunPreviousResponseTests() : RunTests(() => new())
{
public override Task RunWithNoMessageDoesNotFailAsync()
{
@@ -18,8 +16,7 @@ public class AIProjectClientAgentRunStreamingPreviousResponseTests() : RunStream
}
}
-[Obsolete("Use FoundryVersionedAgentRunTests instead. These tests exercise obsolete AIProjectClient extension methods.")]
-public class AIProjectClientAgentRunStreamingConversationTests() : RunStreamingTests(() => new())
+public class FoundryVersionedAgentRunConversationTests() : RunTests(() => new())
{
public override Func> AgentRunOptionsFactory => async () =>
{
diff --git a/dotnet/tests/AzureAI.IntegrationTests/AIProjectClientAgentStructuredOutputRunTests.cs b/dotnet/tests/Foundry.IntegrationTests/FoundryVersionedAgentStructuredOutputRunTests.cs
similarity index 71%
rename from dotnet/tests/AzureAI.IntegrationTests/AIProjectClientAgentStructuredOutputRunTests.cs
rename to dotnet/tests/Foundry.IntegrationTests/FoundryVersionedAgentStructuredOutputRunTests.cs
index b3782a6601..015877df05 100644
--- a/dotnet/tests/AzureAI.IntegrationTests/AIProjectClientAgentStructuredOutputRunTests.cs
+++ b/dotnet/tests/Foundry.IntegrationTests/FoundryVersionedAgentStructuredOutputRunTests.cs
@@ -1,25 +1,23 @@
// Copyright (c) Microsoft. All rights reserved.
-using System;
using System.Threading.Tasks;
using AgentConformance.IntegrationTests;
using AgentConformance.IntegrationTests.Support;
using Microsoft.Agents.AI;
using Microsoft.Extensions.AI;
-namespace AzureAI.IntegrationTests;
+namespace Foundry.IntegrationTests;
-#pragma warning disable CS0618 // Tests intentionally exercise obsolete AIProjectClientFixture
-[Obsolete("Use FoundryVersionedAgentStructuredOutputRunTests instead. These tests exercise obsolete AIProjectClient extension methods.")]
-public class AIProjectClientAgentStructuredOutputRunTests() : StructuredOutputRunTests>(() => new AIProjectClientStructuredOutputFixture())
+public class FoundryVersionedAgentStructuredOutputRunTests() : StructuredOutputRunTests>(() => new FoundryVersionedAgentStructuredOutputFixture())
{
- private const string NotSupported = "AIProjectClient does not support specifying structured output type at invocation time.";
+ private const string NotSupported = "Versioned Foundry agents do not support specifying structured output type at invocation time.";
+ private const string ResponseFormatNotSupported = "AzureAIProjectChatClient clears ResponseFormat for versioned agents; structured output must be defined in the server-side agent definition.";
///
/// Verifies that response format provided at agent initialization is used when invoking RunAsync.
///
///
- [RetryFact(Constants.RetryCount, Constants.RetryDelay)]
+ [RetryFact(Constants.RetryCount, Constants.RetryDelay, Skip = ResponseFormatNotSupported)]
public async Task RunWithResponseFormatAtAgentInitializationReturnsExpectedResultAsync()
{
// Arrange
@@ -39,14 +37,14 @@ public class AIProjectClientAgentStructuredOutputRunTests() : StructuredOutputRu
}
///
- /// Verifies that generic RunAsync works with AIProjectClient when structured output is configured at agent initialization.
+ /// Verifies that generic RunAsync works with versioned Foundry agents when structured output is configured at agent initialization.
///
///
- /// AIProjectClient does not support specifying the structured output type at invocation time yet.
+ /// Versioned Foundry agents do not support specifying the structured output type at invocation time yet.
/// The type T provided to RunAsync<T> is ignored by AzureAIProjectChatClient and is only used
/// for deserializing the agent response by AgentResponse<T>.Result.
///
- [RetryFact(Constants.RetryCount, Constants.RetryDelay)]
+ [RetryFact(Constants.RetryCount, Constants.RetryDelay, Skip = ResponseFormatNotSupported)]
public async Task RunGenericWithResponseFormatAtAgentInitializationReturnsExpectedResultAsync()
{
// Arrange
@@ -88,10 +86,9 @@ public class AIProjectClientAgentStructuredOutputRunTests() : StructuredOutputRu
}
///
-/// Represents a fixture for testing AIProjectClient with structured output of type provided at agent initialization.
+/// Represents a fixture for testing versioned Foundry agents with structured output of type provided at agent initialization.
///
-[Obsolete("Use FoundryVersionedAgentStructuredOutputFixture instead.")]
-public class AIProjectClientStructuredOutputFixture : AIProjectClientFixture
+public class FoundryVersionedAgentStructuredOutputFixture : FoundryVersionedAgentFixture
{
public override async ValueTask InitializeAsync()
{
diff --git a/dotnet/tests/Microsoft.Agents.AI.FoundryMemory.IntegrationTests/FoundryMemoryProviderTests.cs b/dotnet/tests/Foundry.IntegrationTests/Memory/FoundryMemoryProviderTests.cs
similarity index 57%
rename from dotnet/tests/Microsoft.Agents.AI.FoundryMemory.IntegrationTests/FoundryMemoryProviderTests.cs
rename to dotnet/tests/Foundry.IntegrationTests/Memory/FoundryMemoryProviderTests.cs
index d6092f5231..9b9b39cbdf 100644
--- a/dotnet/tests/Microsoft.Agents.AI.FoundryMemory.IntegrationTests/FoundryMemoryProviderTests.cs
+++ b/dotnet/tests/Foundry.IntegrationTests/Memory/FoundryMemoryProviderTests.cs
@@ -1,14 +1,17 @@
// Copyright (c) Microsoft. All rights reserved.
-#pragma warning disable CS0618 // Tests intentionally exercise obsolete extension methods
-
using System;
using System.Threading.Tasks;
using Azure.AI.Projects;
+using Azure.Identity;
+using Microsoft.Agents.AI;
+using Microsoft.Agents.AI.Foundry;
+using Microsoft.Extensions.AI;
using Microsoft.Extensions.Configuration;
+using OpenAI.Responses;
using Shared.IntegrationTests;
-namespace Microsoft.Agents.AI.FoundryMemory.IntegrationTests;
+namespace Foundry.IntegrationTests.Memory;
///
/// Integration tests for against a configured Azure AI Foundry Memory service.
@@ -16,7 +19,6 @@ namespace Microsoft.Agents.AI.FoundryMemory.IntegrationTests;
///
/// These integration tests are skipped by default and require a live Azure AI Foundry Memory service.
/// The tests need to be updated to use the new AIAgent-based API pattern.
-/// Set to null to enable them after configuring the service.
///
public sealed class FoundryMemoryProviderTests : IDisposable
{
@@ -25,6 +27,7 @@ public sealed class FoundryMemoryProviderTests : IDisposable
private readonly AIProjectClient? _client;
private readonly string? _memoryStoreName;
private readonly string? _deploymentName;
+ private readonly string? _embeddingDeploymentName;
private bool _disposed;
public FoundryMemoryProviderTests()
@@ -38,13 +41,15 @@ public sealed class FoundryMemoryProviderTests : IDisposable
var endpoint = configuration[TestSettings.AzureAIProjectEndpoint];
var memoryStoreName = configuration[TestSettings.AzureAIMemoryStoreId];
var deploymentName = configuration[TestSettings.AzureAIModelDeploymentName];
+ var embeddingDeploymentName = configuration[TestSettings.AzureAIEmbeddingDeploymentName];
if (!string.IsNullOrWhiteSpace(endpoint) &&
!string.IsNullOrWhiteSpace(memoryStoreName))
{
- this._client = new AIProjectClient(new Uri(endpoint), TestAzureCliCredentials.CreateAzureCliCredential());
+ this._client = new AIProjectClient(new Uri(endpoint), new DefaultAzureCredential());
this._memoryStoreName = memoryStoreName;
this._deploymentName = deploymentName ?? "gpt-4.1-mini";
+ this._embeddingDeploymentName = embeddingDeploymentName ?? "text-embedding-ada-002";
}
}
@@ -57,8 +62,17 @@ public sealed class FoundryMemoryProviderTests : IDisposable
this._memoryStoreName!,
stateInitializer: _ => new(new FoundryMemoryProviderScope("it-user-1")));
- AIAgent agent = await this._client!.CreateAIAgentAsync(this._deploymentName!,
- options: new ChatClientAgentOptions { AIContextProviders = [memoryProvider] });
+ await memoryProvider.EnsureMemoryStoreCreatedAsync(this._deploymentName!, this._embeddingDeploymentName!);
+
+ AIAgent agent = this._client!.AsAIAgent(new ChatClientAgentOptions
+ {
+ ChatOptions = new ChatOptions
+ {
+ ModelId = this._deploymentName!,
+ Instructions = "You are a helpful assistant. Use known memories about the user when responding, and do not invent details."
+ },
+ AIContextProviders = [memoryProvider]
+ });
AgentSession session = await agent.CreateSessionAsync();
@@ -72,6 +86,15 @@ public sealed class FoundryMemoryProviderTests : IDisposable
await memoryProvider.WhenUpdatesCompletedAsync();
await Task.Delay(2000);
+ // Assert - verify memories were actually created in the store before querying via agent
+ var searchResult = await this._client!.MemoryStores.SearchMemoriesAsync(
+ this._memoryStoreName!,
+ new MemorySearchOptions("it-user-1")
+ {
+ Items = { ResponseItem.CreateUserMessageItem("Caoimhe") }
+ });
+ Assert.NotEmpty(searchResult.Value.Memories);
+
AgentResponse resultAfter = await agent.RunAsync("What is my name?", session);
// Cleanup
@@ -95,10 +118,27 @@ public sealed class FoundryMemoryProviderTests : IDisposable
this._memoryStoreName!,
stateInitializer: _ => new(new FoundryMemoryProviderScope("it-scope-b")));
- AIAgent agent1 = await this._client!.CreateAIAgentAsync(this._deploymentName!,
- options: new ChatClientAgentOptions { AIContextProviders = [memoryProvider1] });
- AIAgent agent2 = await this._client!.CreateAIAgentAsync(this._deploymentName!,
- options: new ChatClientAgentOptions { AIContextProviders = [memoryProvider2] });
+ await memoryProvider1.EnsureMemoryStoreCreatedAsync(this._deploymentName!, this._embeddingDeploymentName!);
+
+ AIAgent agent1 = this._client!.AsAIAgent(new ChatClientAgentOptions
+ {
+ ChatOptions = new ChatOptions
+ {
+ ModelId = this._deploymentName!,
+ Instructions = "You are a helpful assistant. Use known memories about the user when responding, and do not invent details."
+ },
+ AIContextProviders = [memoryProvider1]
+ });
+
+ AIAgent agent2 = this._client!.AsAIAgent(new ChatClientAgentOptions
+ {
+ ChatOptions = new ChatOptions
+ {
+ ModelId = this._deploymentName!,
+ Instructions = "You are a helpful assistant. Use known memories about the user when responding, and do not invent details."
+ },
+ AIContextProviders = [memoryProvider2]
+ });
AgentSession session1 = await agent1.CreateSessionAsync();
AgentSession session2 = await agent2.CreateSessionAsync();
@@ -111,8 +151,25 @@ public sealed class FoundryMemoryProviderTests : IDisposable
await memoryProvider1.WhenUpdatesCompletedAsync();
await Task.Delay(2000);
- AgentResponse result1 = await agent1.RunAsync("What is your name?", session1);
- AgentResponse result2 = await agent2.RunAsync("What is your name?", session2);
+ // Assert - verify memories were created in scope A but not in scope B
+ var searchResultA = await this._client!.MemoryStores.SearchMemoriesAsync(
+ this._memoryStoreName!,
+ new MemorySearchOptions("it-scope-a")
+ {
+ Items = { ResponseItem.CreateUserMessageItem("Caoimhe") }
+ });
+ Assert.NotEmpty(searchResultA.Value.Memories);
+
+ var searchResultB = await this._client.MemoryStores.SearchMemoriesAsync(
+ this._memoryStoreName!,
+ new MemorySearchOptions("it-scope-b")
+ {
+ Items = { ResponseItem.CreateUserMessageItem("Caoimhe") }
+ });
+ Assert.Empty(searchResultB.Value.Memories);
+
+ AgentResponse result1 = await agent1.RunAsync("What is my name?", session1);
+ AgentResponse result2 = await agent2.RunAsync("What is my name?", session2);
// Assert
Assert.Contains("Caoimhe", result1.Text);
diff --git a/dotnet/tests/AzureAI.IntegrationTests/ResponsesAgentChatClientRunStreamingTests.cs b/dotnet/tests/Foundry.IntegrationTests/ResponsesAgentChatClientRunStreamingTests.cs
similarity index 93%
rename from dotnet/tests/AzureAI.IntegrationTests/ResponsesAgentChatClientRunStreamingTests.cs
rename to dotnet/tests/Foundry.IntegrationTests/ResponsesAgentChatClientRunStreamingTests.cs
index 5895ceb8b9..c07509e04e 100644
--- a/dotnet/tests/AzureAI.IntegrationTests/ResponsesAgentChatClientRunStreamingTests.cs
+++ b/dotnet/tests/Foundry.IntegrationTests/ResponsesAgentChatClientRunStreamingTests.cs
@@ -3,7 +3,7 @@
using System.Threading.Tasks;
using AgentConformance.IntegrationTests;
-namespace AzureAI.IntegrationTests;
+namespace Foundry.IntegrationTests;
public class ResponsesAgentChatClientRunStreamingTests() : ChatClientAgentRunStreamingTests(() => new())
{
diff --git a/dotnet/tests/AzureAI.IntegrationTests/ResponsesAgentChatClientRunTests.cs b/dotnet/tests/Foundry.IntegrationTests/ResponsesAgentChatClientRunTests.cs
similarity index 92%
rename from dotnet/tests/AzureAI.IntegrationTests/ResponsesAgentChatClientRunTests.cs
rename to dotnet/tests/Foundry.IntegrationTests/ResponsesAgentChatClientRunTests.cs
index d80b25deb2..100a3c001b 100644
--- a/dotnet/tests/AzureAI.IntegrationTests/ResponsesAgentChatClientRunTests.cs
+++ b/dotnet/tests/Foundry.IntegrationTests/ResponsesAgentChatClientRunTests.cs
@@ -3,7 +3,7 @@
using System.Threading.Tasks;
using AgentConformance.IntegrationTests;
-namespace AzureAI.IntegrationTests;
+namespace Foundry.IntegrationTests;
public class ResponsesAgentChatClientRunTests() : ChatClientAgentRunTests(() => new())
{
diff --git a/dotnet/tests/AzureAI.IntegrationTests/ResponsesAgentExtensionCreateTests.cs b/dotnet/tests/Foundry.IntegrationTests/ResponsesAgentExtensionCreateTests.cs
similarity index 79%
rename from dotnet/tests/AzureAI.IntegrationTests/ResponsesAgentExtensionCreateTests.cs
rename to dotnet/tests/Foundry.IntegrationTests/ResponsesAgentExtensionCreateTests.cs
index cd8dc6cb03..af358a9e55 100644
--- a/dotnet/tests/AzureAI.IntegrationTests/ResponsesAgentExtensionCreateTests.cs
+++ b/dotnet/tests/Foundry.IntegrationTests/ResponsesAgentExtensionCreateTests.cs
@@ -3,13 +3,13 @@
using System;
using System.Threading.Tasks;
using AgentConformance.IntegrationTests.Support;
+using Azure.AI.Extensions.OpenAI;
using Azure.AI.Projects;
using Microsoft.Agents.AI;
-using Microsoft.Agents.AI.AzureAI;
using Microsoft.Extensions.AI;
using Shared.IntegrationTests;
-namespace AzureAI.IntegrationTests;
+namespace Foundry.IntegrationTests;
///
/// Integration tests for non-versioned creation via extension methods.
@@ -30,16 +30,19 @@ public class ResponsesAgentExtensionCreateTests
const string AgentDescription = "Integration test agent created from AIProjectClient.AsAIAgent(model, instructions).";
const string VerificationToken = "integration-extension-ok";
- FoundryAgent agent = this._client.AsAIAgent(
+ ChatClientAgent agent = this._client.AsAIAgent(
model: Model,
instructions: $"You are a helpful assistant. When asked for verification, reply with exactly '{VerificationToken}'.",
name: AgentName,
description: AgentDescription);
- AgentSession session = await agent.CreateSessionAsync();
+ AgentSession? session = null;
try
{
+ var conversation = await CreateConversationAsync(this._client);
+ session = await agent.CreateSessionAsync(conversation.Id);
+
// Act
AgentResponse response = await agent.RunAsync("Return the verification token.", session);
@@ -47,7 +50,6 @@ public class ResponsesAgentExtensionCreateTests
Assert.NotNull(agent);
Assert.Equal(AgentName, agent.Name);
Assert.Equal(AgentDescription, agent.Description);
- Assert.Same(this._client, agent.GetService());
Assert.NotNull(agent.GetService());
Assert.Contains(VerificationToken, response.Text, StringComparison.OrdinalIgnoreCase);
}
@@ -73,19 +75,22 @@ public class ResponsesAgentExtensionCreateTests
},
};
- FoundryAgent agent = this._client.AsAIAgent(options);
- ChatClientAgentSession session = await agent.CreateConversationSessionAsync();
+ ChatClientAgent agent = this._client.AsAIAgent(options);
+
+ ChatClientAgentSession? session = null;
try
{
+ var conversation = await CreateConversationAsync(this._client);
+ session = ((await agent.CreateSessionAsync(conversation.Id)) as ChatClientAgentSession)!;
+
// Act
AgentResponse response = await agent.RunAsync("Return the verification token.", session);
// Assert
- Assert.StartsWith("conv_", session.ConversationId, StringComparison.OrdinalIgnoreCase);
+ Assert.StartsWith("conv_", session!.ConversationId, StringComparison.OrdinalIgnoreCase);
Assert.Equal(options.Name, agent.Name);
Assert.Equal(options.Description, agent.Description);
- Assert.Same(this._client, agent.GetService());
Assert.Contains(VerificationToken, response.Text, StringComparison.OrdinalIgnoreCase);
}
finally
@@ -94,8 +99,13 @@ public class ResponsesAgentExtensionCreateTests
}
}
- private static async Task DeleteSessionAsync(AIProjectClient client, AgentSession session)
+ private static async Task DeleteSessionAsync(AIProjectClient client, AgentSession? session)
{
+ if (session is null)
+ {
+ return;
+ }
+
ChatClientAgentSession typedSession = (ChatClientAgentSession)session;
if (typedSession.ConversationId?.StartsWith("conv_", StringComparison.OrdinalIgnoreCase) == true)
@@ -119,4 +129,10 @@ public class ResponsesAgentExtensionCreateTests
await DeleteResponseChainAsync(client, response.Value.PreviousResponseId);
}
}
+
+ private static async Task CreateConversationAsync(AIProjectClient client)
+ {
+ ProjectConversationsClient conversationsClient = client.GetProjectOpenAIClient().GetProjectConversationsClient();
+ return (await conversationsClient.CreateProjectConversationAsync()).Value!;
+ }
}
diff --git a/dotnet/tests/AzureAI.IntegrationTests/ResponsesAgentFixture.cs b/dotnet/tests/Foundry.IntegrationTests/ResponsesAgentFixture.cs
similarity index 98%
rename from dotnet/tests/AzureAI.IntegrationTests/ResponsesAgentFixture.cs
rename to dotnet/tests/Foundry.IntegrationTests/ResponsesAgentFixture.cs
index ec678a00d9..7bd0da0d95 100644
--- a/dotnet/tests/AzureAI.IntegrationTests/ResponsesAgentFixture.cs
+++ b/dotnet/tests/Foundry.IntegrationTests/ResponsesAgentFixture.cs
@@ -9,19 +9,18 @@ using AgentConformance.IntegrationTests.Support;
using Azure.AI.Extensions.OpenAI;
using Azure.AI.Projects;
using Microsoft.Agents.AI;
-using Microsoft.Agents.AI.AzureAI;
using Microsoft.Extensions.AI;
using OpenAI.Responses;
using Shared.IntegrationTests;
-namespace AzureAI.IntegrationTests;
+namespace Foundry.IntegrationTests;
///
/// Integration test fixture that creates non-versioned Responses agents via the direct AIProjectClient.AsAIAgent(...) path.
///
public class ResponsesAgentFixture : IChatClientAgentFixture
{
- private FoundryAgent _agent = null!;
+ private ChatClientAgent _agent = null!;
private AIProjectClient _client = null!;
public IChatClient ChatClient => this._agent.GetService()!.ChatClient;
diff --git a/dotnet/tests/AzureAI.IntegrationTests/ResponsesAgentRunStreamingTests.cs b/dotnet/tests/Foundry.IntegrationTests/ResponsesAgentRunStreamingTests.cs
similarity index 96%
rename from dotnet/tests/AzureAI.IntegrationTests/ResponsesAgentRunStreamingTests.cs
rename to dotnet/tests/Foundry.IntegrationTests/ResponsesAgentRunStreamingTests.cs
index 5f21c316c4..09f5fe6b2e 100644
--- a/dotnet/tests/AzureAI.IntegrationTests/ResponsesAgentRunStreamingTests.cs
+++ b/dotnet/tests/Foundry.IntegrationTests/ResponsesAgentRunStreamingTests.cs
@@ -5,7 +5,7 @@ using System.Threading.Tasks;
using AgentConformance.IntegrationTests;
using Microsoft.Agents.AI;
-namespace AzureAI.IntegrationTests;
+namespace Foundry.IntegrationTests;
public class ResponsesAgentRunStreamingPreviousResponseTests() : RunStreamingTests(() => new())
{
diff --git a/dotnet/tests/AzureAI.IntegrationTests/ResponsesAgentRunTests.cs b/dotnet/tests/Foundry.IntegrationTests/ResponsesAgentRunTests.cs
similarity index 96%
rename from dotnet/tests/AzureAI.IntegrationTests/ResponsesAgentRunTests.cs
rename to dotnet/tests/Foundry.IntegrationTests/ResponsesAgentRunTests.cs
index 71460f7737..0635b0f4ac 100644
--- a/dotnet/tests/AzureAI.IntegrationTests/ResponsesAgentRunTests.cs
+++ b/dotnet/tests/Foundry.IntegrationTests/ResponsesAgentRunTests.cs
@@ -5,7 +5,7 @@ using System.Threading.Tasks;
using AgentConformance.IntegrationTests;
using Microsoft.Agents.AI;
-namespace AzureAI.IntegrationTests;
+namespace Foundry.IntegrationTests;
public class ResponsesAgentRunPreviousResponseTests() : RunTests(() => new())
{
diff --git a/dotnet/tests/AzureAI.IntegrationTests/ResponsesAgentStructuredOutputRunTests.cs b/dotnet/tests/Foundry.IntegrationTests/ResponsesAgentStructuredOutputRunTests.cs
similarity index 99%
rename from dotnet/tests/AzureAI.IntegrationTests/ResponsesAgentStructuredOutputRunTests.cs
rename to dotnet/tests/Foundry.IntegrationTests/ResponsesAgentStructuredOutputRunTests.cs
index 19ebdb4e28..a561fbae1b 100644
--- a/dotnet/tests/AzureAI.IntegrationTests/ResponsesAgentStructuredOutputRunTests.cs
+++ b/dotnet/tests/Foundry.IntegrationTests/ResponsesAgentStructuredOutputRunTests.cs
@@ -7,7 +7,7 @@ using Microsoft.Agents.AI;
using Microsoft.Extensions.AI;
using Shared.IntegrationTests;
-namespace AzureAI.IntegrationTests;
+namespace Foundry.IntegrationTests;
public class ResponsesAgentStructuredOutputRunTests() : StructuredOutputRunTests>(() => new())
{
diff --git a/dotnet/tests/Microsoft.Agents.AI.AzureAI.UnitTests/AzureAIProjectChatClientExtensionsTests.cs b/dotnet/tests/Microsoft.Agents.AI.AzureAI.UnitTests/AzureAIProjectChatClientExtensionsTests.cs
deleted file mode 100644
index 5c954d30e8..0000000000
--- a/dotnet/tests/Microsoft.Agents.AI.AzureAI.UnitTests/AzureAIProjectChatClientExtensionsTests.cs
+++ /dev/null
@@ -1,3472 +0,0 @@
-// Copyright (c) Microsoft. All rights reserved.
-
-using System;
-using System.ClientModel;
-using System.ClientModel.Primitives;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Net;
-using System.Net.Http;
-using System.Text;
-using System.Text.Json;
-using System.Threading;
-using System.Threading.Tasks;
-using Azure.AI.Extensions.OpenAI;
-using Azure.AI.Projects;
-using Azure.AI.Projects.Agents;
-using Microsoft.Extensions.AI;
-using Moq;
-using OpenAI.Responses;
-
-namespace Microsoft.Agents.AI.AzureAI.UnitTests;
-
-#pragma warning disable CS0618
-///
-/// Unit tests for the class.
-///
-[Obsolete("Includes coverage for obsolete AIProjectClient compatibility extension methods.")]
-public sealed class AzureAIProjectChatClientExtensionsTests
-{
- #region AsAIAgent(AIProjectClient, model, instructions) Tests
-
- ///
- /// Verify that the non-versioned AsAIAgent overload throws ArgumentNullException when AIProjectClient is null.
- ///
- [Fact]
- public void AsAIAgent_WithModelAndInstructions_WithNullClient_ThrowsArgumentNullException()
- {
- // Arrange
- AIProjectClient? client = null;
-
- // Act & Assert
- ArgumentNullException exception = Assert.Throws(() =>
- client!.AsAIAgent("gpt-4o-mini", "You are helpful."));
-
- Assert.Equal("aiProjectClient", exception.ParamName);
- }
-
- ///
- /// Verify that the non-versioned AsAIAgent overload creates a valid ChatClientAgent.
- ///
- [Fact]
- public void AsAIAgent_WithModelAndInstructions_CreatesChatClientAgent()
- {
- // Arrange
- AIProjectClient client = this.CreateTestAgentClient();
- List tools =
- [
- AIFunctionFactory.Create(() => "test", "test_function", "A test function")
- ];
-
- // Act
- FoundryAgent agent = client.AsAIAgent(
- "gpt-4o-mini",
- "You are helpful.",
- name: "test-agent",
- description: "A test agent",
- tools: tools);
-
- // Assert
- Assert.NotNull(agent);
- Assert.Equal("test-agent", agent.Name);
- Assert.Equal("A test agent", agent.Description);
- Assert.Same(client, agent.GetService());
- Assert.NotNull(agent.GetService());
- }
-
- ///
- /// Verify that the non-versioned AsAIAgent overload applies the clientFactory.
- ///
- [Fact]
- public void AsAIAgent_WithModelAndInstructions_WithClientFactory_AppliesFactoryCorrectly()
- {
- // Arrange
- AIProjectClient client = this.CreateTestAgentClient();
- TestChatClient? testChatClient = null;
-
- // Act
- FoundryAgent agent = client.AsAIAgent(
- "gpt-4o-mini",
- "You are helpful.",
- clientFactory: innerClient => testChatClient = new TestChatClient(innerClient));
-
- // Assert
- Assert.NotNull(agent);
- TestChatClient? retrievedTestClient = agent.GetService();
- Assert.NotNull(retrievedTestClient);
- Assert.Same(testChatClient, retrievedTestClient);
- }
-
- ///
- /// Verify that the options-based non-versioned AsAIAgent overload creates a valid ChatClientAgent.
- ///
- [Fact]
- public void AsAIAgent_WithOptions_CreatesChatClientAgent()
- {
- // Arrange
- AIProjectClient client = this.CreateTestAgentClient();
- ChatClientAgentOptions options = new()
- {
- Name = "options-agent",
- Description = "Agent from options",
- ChatOptions = new ChatOptions
- {
- ModelId = "gpt-4o-mini",
- Instructions = "You are helpful.",
- },
- };
-
- // Act
- FoundryAgent agent = client.AsAIAgent(options);
-
- // Assert
- Assert.NotNull(agent);
- Assert.Equal("options-agent", agent.Name);
- Assert.Equal("Agent from options", agent.Description);
- Assert.Same(client, agent.GetService());
- }
-
- ///
- /// Verify that the non-versioned AsAIAgent overload adds the MEAI user-agent header to Responses API requests.
- ///
- [Fact]
- public async Task AsAIAgent_WithModelAndInstructions_UserAgentHeaderAddedToResponsesRequestsAsync()
- {
- // Arrange
- bool userAgentFound = false;
- using HttpHandlerAssert httpHandler = new(request =>
- {
- if (request.Headers.TryGetValues("User-Agent", out IEnumerable? values))
- {
- foreach (string value in values)
- {
- if (value.Contains("MEAI"))
- {
- userAgentFound = true;
- }
- }
- }
-
- if (request.Method == HttpMethod.Post && request.RequestUri!.PathAndQuery.Contains("/responses"))
- {
- return new HttpResponseMessage(HttpStatusCode.OK)
- {
- Content = new StringContent(
- TestDataUtil.GetOpenAIDefaultResponseJson(),
- Encoding.UTF8,
- "application/json")
- };
- }
-
- return new HttpResponseMessage(HttpStatusCode.OK)
- {
- Content = new StringContent("{}", Encoding.UTF8, "application/json")
- };
- });
-
-#pragma warning disable CA5399
- using HttpClient httpClient = new(httpHandler);
-#pragma warning restore CA5399
-
- AIProjectClient aiProjectClient = new(
- new Uri("https://test.openai.azure.com/"),
- new FakeAuthenticationTokenProvider(),
- new() { Transport = new HttpClientPipelineTransport(httpClient) });
-
- FoundryAgent agent = aiProjectClient.AsAIAgent(
- "gpt-4o-mini",
- "You are helpful.");
-
- // Act
- AgentSession session = await agent.CreateSessionAsync();
- await agent.RunAsync("Hello", session);
-
- // Assert
- Assert.True(userAgentFound, "MEAI user-agent header was not found in any request");
- }
-
- #endregion
-
- #region AsAIAgent(AIProjectClient, AgentRecord) Tests
-
- ///
- /// Verify that AsAIAgent throws ArgumentNullException when AIProjectClient is null.
- ///
- [Fact]
- public void AsAIAgent_WithAgentRecord_WithNullClient_ThrowsArgumentNullException()
- {
- // Arrange
- AIProjectClient? client = null;
- AgentRecord agentRecord = this.CreateTestAgentRecord();
-
- // Act & Assert
- var exception = Assert.Throws(() =>
- client!.AsAIAgent(agentRecord));
-
- Assert.Equal("aiProjectClient", exception.ParamName);
- }
-
- ///
- /// Verify that AsAIAgent throws ArgumentNullException when agentRecord is null.
- ///
- [Fact]
- public void AsAIAgent_WithAgentRecord_WithNullAgentRecord_ThrowsArgumentNullException()
- {
- // Arrange
- var mockClient = new Mock();
-
- // Act & Assert
- var exception = Assert.Throws(() =>
- mockClient.Object.AsAIAgent((AgentRecord)null!));
-
- Assert.Equal("agentRecord", exception.ParamName);
- }
-
- ///
- /// Verify that AsAIAgent with AgentRecord creates a valid agent.
- ///
- [Fact]
- public void AsAIAgent_WithAgentRecord_CreatesValidAgent()
- {
- // Arrange
- AIProjectClient client = this.CreateTestAgentClient();
- AgentRecord agentRecord = this.CreateTestAgentRecord();
-
- // Act
- var agent = client.AsAIAgent(agentRecord);
-
- // Assert
- Assert.NotNull(agent);
- Assert.Equal("agent_abc123", agent.Name);
- }
-
- ///
- /// Verify that AsAIAgent with AgentRecord and clientFactory applies the factory.
- ///
- [Fact]
- public void AsAIAgent_WithAgentRecord_WithClientFactory_AppliesFactoryCorrectly()
- {
- // Arrange
- AIProjectClient client = this.CreateTestAgentClient();
- AgentRecord agentRecord = this.CreateTestAgentRecord();
- TestChatClient? testChatClient = null;
-
- // Act
- var agent = client.AsAIAgent(
- agentRecord,
- clientFactory: (innerClient) => testChatClient = new TestChatClient(innerClient));
-
- // Assert
- Assert.NotNull(agent);
- var retrievedTestClient = agent.GetService();
- Assert.NotNull(retrievedTestClient);
- Assert.Same(testChatClient, retrievedTestClient);
- }
-
- #endregion
-
- #region AsAIAgent(AIProjectClient, AgentVersion) Tests
-
- ///
- /// Verify that AsAIAgent throws ArgumentNullException when AIProjectClient is null.
- ///
- [Fact]
- public void AsAIAgent_WithAgentVersion_WithNullClient_ThrowsArgumentNullException()
- {
- // Arrange
- AIProjectClient? client = null;
- AgentVersion agentVersion = this.CreateTestAgentVersion();
-
- // Act & Assert
- var exception = Assert.Throws(() =>
- client!.AsAIAgent(agentVersion));
-
- Assert.Equal("aiProjectClient", exception.ParamName);
- }
-
- ///
- /// Verify that AsAIAgent throws ArgumentNullException when agentVersion is null.
- ///
- [Fact]
- public void AsAIAgent_WithAgentVersion_WithNullAgentVersion_ThrowsArgumentNullException()
- {
- // Arrange
- var mockClient = new Mock();
-
- // Act & Assert
- var exception = Assert.Throws(() =>
- mockClient.Object.AsAIAgent((AgentVersion)null!));
-
- Assert.Equal("agentVersion", exception.ParamName);
- }
-
- ///
- /// Verify that AsAIAgent with AgentVersion creates a valid agent.
- ///
- [Fact]
- public void AsAIAgent_WithAgentVersion_CreatesValidAgent()
- {
- // Arrange
- AIProjectClient client = this.CreateTestAgentClient();
- AgentVersion agentVersion = this.CreateTestAgentVersion();
-
- // Act
- var agent = client.AsAIAgent(agentVersion);
-
- // Assert
- Assert.NotNull(agent);
- Assert.Equal("agent_abc123", agent.Name);
- }
-
- ///
- /// Verify that AsAIAgent with AgentVersion and clientFactory applies the factory.
- ///
- [Fact]
- public void AsAIAgent_WithAgentVersion_WithClientFactory_AppliesFactoryCorrectly()
- {
- // Arrange
- AIProjectClient client = this.CreateTestAgentClient();
- AgentVersion agentVersion = this.CreateTestAgentVersion();
- TestChatClient? testChatClient = null;
-
- // Act
- var agent = client.AsAIAgent(
- agentVersion,
- clientFactory: (innerClient) => testChatClient = new TestChatClient(innerClient));
-
- // Assert
- Assert.NotNull(agent);
- var retrievedTestClient = agent.GetService();
- Assert.NotNull(retrievedTestClient);
- Assert.Same(testChatClient, retrievedTestClient);
- }
-
- ///
- /// Verify that AsAIAgent with requireInvocableTools=true enforces invocable tools.
- ///
- [Fact]
- public void AsAIAgent_WithAgentVersion_WithRequireInvocableToolsTrue_EnforcesInvocableTools()
- {
- // Arrange
- AIProjectClient client = this.CreateTestAgentClient();
- AgentVersion agentVersion = this.CreateTestAgentVersion();
- var tools = new List
- {
- AIFunctionFactory.Create(() => "test", "test_function", "A test function")
- };
-
- // Act
- var agent = client.AsAIAgent(agentVersion, tools: tools);
-
- // Assert
- Assert.NotNull(agent);
- Assert.IsType(agent);
- }
-
- ///
- /// Verify that AsAIAgent with requireInvocableTools=false allows declarative functions.
- ///
- [Fact]
- public void AsAIAgent_WithAgentVersion_WithRequireInvocableToolsFalse_AllowsDeclarativeFunctions()
- {
- // Arrange
- AIProjectClient client = this.CreateTestAgentClient();
- AgentVersion agentVersion = this.CreateTestAgentVersion();
-
- // Act - should not throw even without tools when requireInvocableTools is false
- var agent = client.AsAIAgent(agentVersion);
-
- // Assert
- Assert.NotNull(agent);
- Assert.IsType(agent);
- }
-
- #endregion
-
- #region GetAIAgentAsync(AIProjectClient, ChatClientAgentOptions) Tests
-
- ///
- /// Verify that GetAIAgentAsync with ChatClientAgentOptions throws ArgumentNullException when client is null.
- ///
- [Fact]
- public async Task GetAIAgentAsync_WithOptions_WithNullClient_ThrowsArgumentNullExceptionAsync()
- {
- // Arrange
- AIProjectClient? client = null;
- var options = new ChatClientAgentOptions { Name = "test-agent" };
-
- // Act & Assert
- var exception = await Assert.ThrowsAsync(() =>
- client!.GetAIAgentAsync(options));
-
- Assert.Equal("aiProjectClient", exception.ParamName);
- }
-
- ///
- /// Verify that GetAIAgentAsync with ChatClientAgentOptions throws ArgumentNullException when options is null.
- ///
- [Fact]
- public async Task GetAIAgentAsync_WithOptions_WithNullOptions_ThrowsArgumentNullExceptionAsync()
- {
- // Arrange
- var mockClient = new Mock();
-
- // Act & Assert
- var exception = await Assert.ThrowsAsync(() =>
- mockClient.Object.GetAIAgentAsync((ChatClientAgentOptions)null!));
-
- Assert.Equal("options", exception.ParamName);
- }
-
- ///
- /// Verify that GetAIAgentAsync with ChatClientAgentOptions creates a valid agent.
- ///
- [Fact]
- public async Task GetAIAgentAsync_WithOptions_CreatesValidAgentAsync()
- {
- // Arrange
- AIProjectClient client = this.CreateTestAgentClient(agentName: "test-agent");
- var options = new ChatClientAgentOptions { Name = "test-agent" };
-
- // Act
- var agent = await client.GetAIAgentAsync(options);
-
- // Assert
- Assert.NotNull(agent);
- Assert.Equal("test-agent", agent.Name);
- }
-
- #endregion
-
- #region AsAIAgent(AIProjectClient, string) Tests
-
- ///
- /// Verify that AsAIAgent throws ArgumentNullException when AIProjectClient is null.
- ///
- [Fact]
- public void AsAIAgent_ByName_WithNullClient_ThrowsArgumentNullException()
- {
- // Arrange
- AIProjectClient? client = null;
-
- // Act & Assert
- var exception = Assert.Throws(() =>
- client!.AsAIAgent("test-agent"));
-
- Assert.Equal("aiProjectClient", exception.ParamName);
- }
-
- ///
- /// Verify that AsAIAgent throws ArgumentNullException when name is null.
- ///
- [Fact]
- public void AsAIAgent_ByName_WithNullName_ThrowsArgumentNullException()
- {
- // Arrange
- var mockClient = new Mock();
-
- // Act & Assert
- var exception = Assert.Throws(() =>
- mockClient.Object.AsAIAgent((string)null!));
-
- Assert.Equal("name", exception.ParamName);
- }
-
- ///
- /// Verify that AsAIAgent throws ArgumentException when name is empty.
- ///
- [Fact]
- public void AsAIAgent_ByName_WithEmptyName_ThrowsArgumentException()
- {
- // Arrange
- var mockClient = new Mock();
-
- // Act & Assert
- var exception = Assert.Throws(() =>
- mockClient.Object.AsAIAgent(string.Empty));
-
- Assert.Equal("name", exception.ParamName);
- }
-
- #endregion
-
- #region GetAIAgentAsync(AIProjectClient, string) Tests
-
- ///
- /// Verify that GetAIAgentAsync throws ArgumentNullException when AIProjectClient is null.
- ///
- [Fact]
- public async Task GetAIAgentAsync_ByName_WithNullClient_ThrowsArgumentNullExceptionAsync()
- {
- // Arrange
- AIProjectClient? client = null;
-
- // Act & Assert
- var exception = await Assert.ThrowsAsync(() =>
- client!.GetAIAgentAsync("test-agent"));
-
- Assert.Equal("aiProjectClient", exception.ParamName);
- }
-
- ///
- /// Verify that GetAIAgentAsync throws ArgumentNullException when name is null.
- ///
- [Fact]
- public async Task GetAIAgentAsync_ByName_WithNullName_ThrowsArgumentNullExceptionAsync()
- {
- // Arrange
- var mockClient = new Mock();
-
- // Act & Assert
- var exception = await Assert.ThrowsAsync(() =>
- mockClient.Object.GetAIAgentAsync(name: null!));
-
- Assert.Equal("name", exception.ParamName);
- }
-
- ///
- /// Verify that GetAIAgentAsync throws InvalidOperationException when agent is not found.
- ///
- [Fact]
- public async Task GetAIAgentAsync_ByName_WithNonExistentAgent_ThrowsInvalidOperationExceptionAsync()
- {
- // Arrange
- var mockAgentOperations = new Mock();
- mockAgentOperations
- .Setup(c => c.GetAgentAsync(It.IsAny(), It.IsAny()))
- .ReturnsAsync(ClientResult.FromOptionalValue((AgentRecord)null!, new MockPipelineResponse(200, BinaryData.FromString("null"))));
-
- var mockClient = new Mock();
- mockClient.SetupGet(c => c.Agents).Returns(mockAgentOperations.Object);
- mockClient.Setup(x => x.GetConnection(It.IsAny())).Returns(new ClientConnection("fake-connection-id", "http://localhost", ClientPipeline.Create(), CredentialKind.None));
-
- // Act & Assert
- var exception = await Assert.ThrowsAsync(() =>
- mockClient.Object.GetAIAgentAsync("non-existent-agent"));
-
- Assert.Contains("not found", exception.Message);
- }
-
- #endregion
-
- #region AsAIAgent(AIProjectClient, AgentRecord) with tools Tests
-
- ///
- /// Verify that AsAIAgent with additional tools when the definition has no tools does not throw and results in an agent with no tools.
- ///
- [Fact]
- public void AsAIAgent_WithAgentRecordAndAdditionalTools_WhenDefinitionHasNoTools_ShouldNotThrow()
- {
- // Arrange
- AIProjectClient client = this.CreateTestAgentClient();
- AgentRecord agentRecord = this.CreateTestAgentRecord();
- var tools = new List
- {
- AIFunctionFactory.Create(() => "test", "test_function", "A test function")
- };
-
- // Act
- var agent = client.AsAIAgent(agentRecord, tools: tools);
-
- // Assert
- Assert.NotNull(agent);
- Assert.IsType(agent);
- var chatClient = agent.GetService();
- Assert.NotNull(chatClient);
- var agentVersion = chatClient.GetService();
- Assert.NotNull(agentVersion);
- var definition = Assert.IsType(agentVersion.Definition);
- Assert.Empty(definition.Tools);
- }
-
- ///
- /// Verify that AsAIAgent with null tools works correctly.
- ///
- [Fact]
- public void AsAIAgent_WithAgentRecordAndNullTools_WorksCorrectly()
- {
- // Arrange
- AIProjectClient client = this.CreateTestAgentClient();
- AgentRecord agentRecord = this.CreateTestAgentRecord();
-
- // Act
- var agent = client.AsAIAgent(agentRecord, tools: null);
-
- // Assert
- Assert.NotNull(agent);
- Assert.Equal("agent_abc123", agent.Name);
- }
-
- #endregion
-
- #region GetAIAgentAsync(AIProjectClient, string) with tools Tests
-
- ///
- /// Verify that GetAIAgentAsync with tools parameter creates an agent.
- ///
- [Fact]
- public async Task GetAIAgentAsync_WithNameAndTools_CreatesAgentAsync()
- {
- // Arrange
- AIProjectClient client = this.CreateTestAgentClient();
- var tools = new List
- {
- AIFunctionFactory.Create(() => "test", "test_function", "A test function")
- };
-
- // Act
- var agent = await client.GetAIAgentAsync("test-agent", tools: tools);
-
- // Assert
- Assert.NotNull(agent);
- Assert.IsType(agent);
- }
-
- ///
- /// Verify that CreateAIAgentAsync with model and options creates a valid agent.
- ///
- [Fact]
- public async Task CreateAIAgentAsync_WithModelAndOptions_CreatesValidAgentAsync()
- {
- // Arrange
- using var testClient = CreateTestAgentClientWithHandler(agentName: "test-agent", instructions: "Test instructions");
- var options = new ChatClientAgentOptions
- {
- Name = "test-agent",
- ChatOptions = new() { Instructions = "Test instructions" }
- };
-
- // Act
- var agent = await testClient.Client.CreateAIAgentAsync("test-model", options);
-
- // Assert
- Assert.NotNull(agent);
- Assert.Equal("test-agent", agent.Name);
- Assert.Equal("Test instructions", agent.GetService()!.Instructions);
- }
-
- ///
- /// Verify that CreateAIAgentAsync with model and options and clientFactory applies the factory.
- ///
- [Fact]
- public async Task CreateAIAgentAsync_WithModelAndOptions_WithClientFactory_AppliesFactoryCorrectlyAsync()
- {
- // Arrange
- using var testClient = CreateTestAgentClientWithHandler(agentName: "test-agent", instructions: "Test instructions");
- var options = new ChatClientAgentOptions
- {
- Name = "test-agent",
- ChatOptions = new() { Instructions = "Test instructions" }
- };
- TestChatClient? testChatClient = null;
-
- // Act
- var agent = await testClient.Client.CreateAIAgentAsync(
- "test-model",
- options,
- clientFactory: (innerClient) => testChatClient = new TestChatClient(innerClient));
-
- // Assert
- Assert.NotNull(agent);
- var retrievedTestClient = agent.GetService();
- Assert.NotNull(retrievedTestClient);
- Assert.Same(testChatClient, retrievedTestClient);
- }
-
- #endregion
-
- #region CreateAIAgentAsync(AIProjectClient, string, AgentDefinition) Tests
-
- ///
- /// Verify that CreateAIAgentAsync throws ArgumentNullException when AIProjectClient is null.
- ///
- [Fact]
- public async Task CreateAIAgentAsync_WithAgentDefinition_WithNullClient_ThrowsArgumentNullExceptionAsync()
- {
- // Arrange
- AIProjectClient? client = null;
- var definition = new PromptAgentDefinition("test-model");
- var options = new AgentVersionCreationOptions(definition);
-
- // Act & Assert
- var exception = await Assert.ThrowsAsync(() =>
- client!.CreateAIAgentAsync("agent-name", options));
-
- Assert.Equal("aiProjectClient", exception.ParamName);
- }
-
- ///
- /// Verify that CreateAIAgentAsync throws ArgumentNullException when creationOptions is null.
- ///
- [Fact]
- public async Task CreateAIAgentAsync_WithAgentDefinition_WithNullDefinition_ThrowsArgumentNullExceptionAsync()
- {
- // Arrange
- var mockClient = new Mock();
-
- // Act & Assert
- var exception = await Assert.ThrowsAsync(() =>
- mockClient.Object.CreateAIAgentAsync(name: "agent-name", null!));
-
- Assert.Equal("creationOptions", exception.ParamName);
- }
-
- #endregion
-
- #region Tool Validation Tests
-
- ///
- /// Verify that CreateAIAgent creates an agent successfully.
- ///
- [Fact]
- public async Task CreateAIAgentAsync_WithDefinition_CreatesAgentSuccessfullyAsync()
- {
- // Arrange
- using var testClient = CreateTestAgentClientWithHandler();
- var definition = new PromptAgentDefinition("test-model") { Instructions = "Test" };
- var options = new AgentVersionCreationOptions(definition);
-
- // Act
- var agent = await testClient.Client.CreateAIAgentAsync("test-agent", options);
-
- // Assert
- Assert.NotNull(agent);
- Assert.IsType(agent);
- }
-
- ///
- /// Verify that CreateAIAgent without tools parameter creates an agent successfully.
- ///
- [Fact]
- public async Task CreateAIAgentAsync_WithoutToolsParameter_CreatesAgentSuccessfullyAsync()
- {
- // Arrange
- var definition = new PromptAgentDefinition("test-model") { Instructions = "Test" };
-
- var definitionResponse = GeneratePromptDefinitionResponse(definition, null);
- using var testClient = CreateTestAgentClientWithHandler(agentName: "test-agent", agentDefinitionResponse: definitionResponse);
-
- var options = new AgentVersionCreationOptions(definition);
-
- // Act
- var agent = await testClient.Client.CreateAIAgentAsync("test-agent", options);
-
- // Assert
- Assert.NotNull(agent);
- Assert.IsType(agent);
- }
-
- ///
- /// Verify that CreateAIAgent without tools in definition creates an agent successfully.
- ///
- [Fact]
- public async Task CreateAIAgentAsync_WithoutToolsInDefinition_CreatesAgentSuccessfullyAsync()
- {
- // Arrange
- var definition = new PromptAgentDefinition("test-model") { Instructions = "Test" };
- using var testClient = CreateTestAgentClientWithHandler(agentName: "test-agent", agentDefinitionResponse: definition);
-
- var options = new AgentVersionCreationOptions(definition);
-
- // Act
- var agent = await testClient.Client.CreateAIAgentAsync("test-agent", options);
-
- // Assert
- Assert.NotNull(agent);
- Assert.IsType(agent);
- }
-
- ///
- /// Verify that CreateAIAgent uses tools from the definition when no separate tools parameter is provided.
- ///
- [Fact]
- public async Task CreateAIAgentAsync_WithDefinitionTools_UsesDefinitionToolsAsync()
- {
- // Arrange
- var definition = new PromptAgentDefinition("test-model") { Instructions = "Test" };
-
- // Add a function tool to the definition
- definition.Tools.Add(ResponseTool.CreateFunctionTool("required_tool", BinaryData.FromString("{}"), strictModeEnabled: false));
-
- // Create a response definition with the same tool
- var definitionResponse = GeneratePromptDefinitionResponse(definition, definition.Tools.Select(t => t.AsAITool()).ToList());
- using var testClient = CreateTestAgentClientWithHandler(agentName: "test-agent", agentDefinitionResponse: definitionResponse);
-
- var options = new AgentVersionCreationOptions(definition);
-
- // Act
- var agent = await testClient.Client.CreateAIAgentAsync("test-agent", options);
-
- // Assert
- Assert.NotNull(agent);
- Assert.IsType(agent);
- var agentVersion = agent.GetService();
- Assert.NotNull(agentVersion);
- if (agentVersion.Definition is PromptAgentDefinition promptDef)
- {
- Assert.NotEmpty(promptDef.Tools);
- Assert.Single(promptDef.Tools);
- Assert.Equal("required_tool", (promptDef.Tools.First() as FunctionTool)?.FunctionName);
- }
- }
-
- ///
- /// Verify that CreateAIAgent creates an agent successfully when definition has a mix of custom and hosted tools.
- ///
- [Fact]
- public async Task CreateAIAgentAsync_WithMixedToolsInDefinition_CreatesAgentSuccessfullyAsync()
- {
- // Arrange
- var definition = new PromptAgentDefinition("test-model") { Instructions = "Test instructions" };
- definition.Tools.Add(ResponseTool.CreateFunctionTool("create_tool", BinaryData.FromString("{}"), strictModeEnabled: false));
- definition.Tools.Add(new HostedWebSearchTool().GetService() ?? new HostedWebSearchTool().AsOpenAIResponseTool());
- definition.Tools.Add(new HostedFileSearchTool().GetService() ?? new HostedFileSearchTool().AsOpenAIResponseTool());
-
- // Simulate agent definition response with the tools
- var definitionResponse = new PromptAgentDefinition("test-model") { Instructions = "Test instructions" };
- foreach (var tool in definition.Tools)
- {
- definitionResponse.Tools.Add(tool);
- }
-
- using var testClient = CreateTestAgentClientWithHandler(agentDefinitionResponse: definitionResponse);
-
- var options = new AgentVersionCreationOptions(definition);
-
- // Act
- var agent = await testClient.Client.CreateAIAgentAsync("test-agent", options);
-
- // Assert
- Assert.NotNull(agent);
- Assert.IsType(agent);
- var agentVersion = agent.GetService();
- Assert.NotNull(agentVersion);
- if (agentVersion.Definition is PromptAgentDefinition promptDef)
- {
- Assert.NotEmpty(promptDef.Tools);
- Assert.Equal(3, promptDef.Tools.Count);
- }
- }
-
- ///
- /// Verify that CreateAIAgentAsync when AI Tools are provided, uses them for the definition via http request.
- ///
- [Fact]
- public async Task CreateAIAgentAsync_WithNameAndAITools_SendsToolDefinitionViaHttpAsync()
- {
- // Arrange
- using var httpHandler = new HttpHandlerAssert(async (request) =>
- {
- if (request.Content is not null)
- {
- var requestBody = await request.Content.ReadAsStringAsync().ConfigureAwait(false);
-
- Assert.Contains("required_tool", requestBody);
- }
-
- return new HttpResponseMessage(HttpStatusCode.OK) { Content = new StringContent(TestDataUtil.GetAgentVersionResponseJson(), Encoding.UTF8, "application/json") };
- });
-
-#pragma warning disable CA5399
- using var httpClient = new HttpClient(httpHandler);
-#pragma warning restore CA5399
-
- var client = new AIProjectClient(new Uri("https://test.openai.azure.com/"), new FakeAuthenticationTokenProvider(), new() { Transport = new HttpClientPipelineTransport(httpClient) });
-
- // Act
- var agent = await client.CreateAIAgentAsync(
- name: "test-agent",
- model: "test-model",
- instructions: "Test",
- tools: [AIFunctionFactory.Create(() => true, "required_tool")]);
-
- // Assert
- Assert.NotNull(agent);
- Assert.IsType(agent);
- var agentVersion = agent.GetService();
- Assert.NotNull(agentVersion);
- Assert.IsType(agentVersion.Definition);
- }
-
- ///
- /// Verify that when providing AITools with AsAIAgent, any additional tool that doesn't match the tools in agent definition are ignored.
- ///
- [Fact]
- public void AsAIAgent_AdditionalAITools_WhenNotInTheDefinitionAreIgnored()
- {
- // Arrange
- AIProjectClient client = this.CreateTestAgentClient();
- var agentVersion = this.CreateTestAgentVersion();
-
- // Manually add tools to the definition to simulate inline tools
- if (agentVersion.Definition is PromptAgentDefinition promptDef)
- {
- promptDef.Tools.Add(ResponseTool.CreateFunctionTool("inline_tool", BinaryData.FromString("{}"), strictModeEnabled: false));
- }
-
- var invocableInlineAITool = AIFunctionFactory.Create(() => "test", "inline_tool", "An invocable AIFunction for the inline function");
- var shouldBeIgnoredTool = AIFunctionFactory.Create(() => "test", "additional_tool", "An additional test function that should be ignored");
-
- // Act & Assert
- var agent = client.AsAIAgent(agentVersion, tools: [invocableInlineAITool, shouldBeIgnoredTool]);
- Assert.NotNull(agent);
- var version = agent.GetService();
- Assert.NotNull(version);
- var definition = Assert.IsType(version.Definition);
- Assert.NotEmpty(definition.Tools);
- Assert.NotNull(GetAgentChatOptions(agent));
- Assert.NotNull(GetAgentChatOptions(agent)!.Tools);
- Assert.Single(GetAgentChatOptions(agent)!.Tools!);
- Assert.Equal("inline_tool", (definition.Tools.First() as FunctionTool)?.FunctionName);
- }
-
- #endregion
-
- #region Inline Tools vs Parameter Tools Tests
-
- ///
- /// Verify that tools passed as parameters are accepted by AsAIAgent.
- ///
- [Fact]
- public void AsAIAgent_WithParameterTools_AcceptsTools()
- {
- // Arrange
- AIProjectClient client = this.CreateTestAgentClient();
- AgentRecord agentRecord = this.CreateTestAgentRecord();
- var tools = new List
- {
- AIFunctionFactory.Create(() => "tool1", "param_tool_1", "First parameter tool"),
- AIFunctionFactory.Create(() => "tool2", "param_tool_2", "Second parameter tool")
- };
-
- // Act
- var agent = client.AsAIAgent(agentRecord, tools: tools);
-
- // Assert
- Assert.NotNull(agent);
- Assert.IsType(agent);
- var chatClient = agent.GetService();
- Assert.NotNull(chatClient);
- var agentVersion = chatClient.GetService();
- Assert.NotNull(agentVersion);
- }
-
- ///
- /// Verify that CreateAIAgent with string parameters and tools creates an agent.
- ///
- [Fact]
- public async Task CreateAIAgentAsync_WithStringParamsAndTools_CreatesAgentAsync()
- {
- // Arrange
- var tools = new List
- {
- AIFunctionFactory.Create(() => "weather", "string_param_tool", "Tool from string params")
- };
-
- var definitionResponse = GeneratePromptDefinitionResponse(new PromptAgentDefinition("test-model") { Instructions = "Test instructions" }, tools);
-
- using var testClient = CreateTestAgentClientWithHandler(agentName: "test-agent", agentDefinitionResponse: definitionResponse);
-
- // Act
- var agent = await testClient.Client.CreateAIAgentAsync(
- "test-agent",
- "test-model",
- "Test instructions",
- tools: tools);
-
- // Assert
- Assert.NotNull(agent);
- Assert.IsType(agent);
- var agentVersion = agent.GetService();
- Assert.NotNull(agentVersion);
- if (agentVersion.Definition is PromptAgentDefinition promptDef)
- {
- Assert.NotEmpty(promptDef.Tools);
- Assert.Single(promptDef.Tools);
- }
- }
-
- ///
- /// Verify that CreateAIAgentAsync with tools in definition creates an agent.
- ///
- [Fact]
- public async Task CreateAIAgentAsync_WithDefinitionTools_CreatesAgentAsync()
- {
- // Arrange
- using var testClient = CreateTestAgentClientWithHandler();
- var definition = new PromptAgentDefinition("test-model") { Instructions = "Test instructions" };
- definition.Tools.Add(ResponseTool.CreateFunctionTool("async_tool", BinaryData.FromString("{}"), strictModeEnabled: false));
-
- var options = new AgentVersionCreationOptions(definition);
-
- // Act
- var agent = await testClient.Client.CreateAIAgentAsync("test-agent", options);
-
- // Assert
- Assert.NotNull(agent);
- Assert.IsType(agent);
- }
-
- ///
- /// Verify that GetAIAgentAsync with tools parameter creates an agent.
- ///
- [Fact]
- public async Task GetAIAgentAsync_WithToolsParameter_CreatesAgentAsync()
- {
- // Arrange
- AIProjectClient client = this.CreateTestAgentClient();
- var tools = new List
- {
- AIFunctionFactory.Create(() => "async_get_result", "async_get_tool", "An async get tool")
- };
-
- // Act
- var agent = await client.GetAIAgentAsync("test-agent", tools: tools);
-
- // Assert
- Assert.NotNull(agent);
- Assert.IsType(agent);
- }
-
- #endregion
-
- #region Declarative Function Handling Tests
-
- ///
- /// Verifies that CreateAIAgent uses tools from definition when they are ResponseTool instances, resulting in successful agent creation.
- ///
- [Fact]
- public async Task CreateAIAgentAsync_WithResponseToolsInDefinition_CreatesAgentSuccessfullyAsync()
- {
- // Arrange
- var definition = new PromptAgentDefinition("test-model") { Instructions = "Test instructions" };
-
- var fabricToolOptions = new FabricDataAgentToolOptions();
- fabricToolOptions.ProjectConnections.Add(new ToolProjectConnection("connection-id"));
-
- var sharepointOptions = new SharePointGroundingToolOptions();
- sharepointOptions.ProjectConnections.Add(new ToolProjectConnection("connection-id"));
-
- var structuredOutputs = new StructuredOutputDefinition("name", "description", new Dictionary { ["schema"] = BinaryData.FromString(AIJsonUtilities.CreateJsonSchema(new { id = "test" }.GetType()).ToString()) }, false);
-
- // Add tools to the definition
- definition.Tools.Add(ResponseTool.CreateFunctionTool("create_tool", BinaryData.FromString("{}"), strictModeEnabled: false));
- 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 AgentVersionCreationOptions(definition);
-
- // Act
- var agent = await testClient.Client.CreateAIAgentAsync("test-agent", options);
-
- // Assert
- Assert.NotNull(agent);
- Assert.IsType(agent);
- var agentVersion = agent.GetService();
- Assert.NotNull(agentVersion);
- if (agentVersion.Definition is PromptAgentDefinition promptDef)
- {
- Assert.NotEmpty(promptDef.Tools);
- Assert.Equal(10, promptDef.Tools.Count);
- }
- }
-
- ///
- /// Verify that CreateAIAgentAsync accepts FunctionTools from definition.
- ///
- [Fact]
- public async Task CreateAIAgentAsync_WithFunctionToolsInDefinition_AcceptsDeclarativeFunctionAsync()
- {
- // Arrange
- var functionTool = ResponseTool.CreateFunctionTool(
- functionName: "get_user_name",
- functionParameters: BinaryData.FromString("{}"),
- strictModeEnabled: false,
- functionDescription: "Gets the user's name, as used for friendly address."
- );
-
- var definition = new PromptAgentDefinition("test-model") { Instructions = "Test" };
- definition.Tools.Add(functionTool);
-
- // Generate response with the declarative function
- var definitionResponse = new PromptAgentDefinition("test-model") { Instructions = "Test" };
- definitionResponse.Tools.Add(functionTool);
-
- using var testClient = CreateTestAgentClientWithHandler(agentName: "test-agent", agentDefinitionResponse: definitionResponse);
-
- var options = new AgentVersionCreationOptions(definition);
-
- // Act
- var agent = await testClient.Client.CreateAIAgentAsync("test-agent", options);
-
- // Assert
- Assert.NotNull(agent);
- Assert.IsType(agent);
- }
-
- ///
- /// Verify that CreateAIAgentAsync accepts declarative functions from definition.
- ///
- [Fact]
- public async Task CreateAIAgentAsync_WithDeclarativeFunctionFromDefinition_AcceptsDeclarativeFunctionAsync()
- {
- // Arrange
- using var testClient = CreateTestAgentClientWithHandler();
- var definition = new PromptAgentDefinition("test-model") { Instructions = "Test" };
-
- // Create a declarative function (not invocable) using AIFunctionFactory.CreateDeclaration
- using var doc = JsonDocument.Parse("{}");
- var declarativeFunction = AIFunctionFactory.CreateDeclaration("test_function", "A test function", doc.RootElement);
-
- // Add to definition
- definition.Tools.Add(declarativeFunction.AsOpenAIResponseTool() ?? throw new InvalidOperationException());
-
- var options = new AgentVersionCreationOptions(definition);
-
- // Act
- var agent = await testClient.Client.CreateAIAgentAsync("test-agent", options);
-
- // Assert
- Assert.NotNull(agent);
- Assert.IsType(agent);
- }
-
- ///
- /// Verify that CreateAIAgentAsync accepts declarative functions from definition.
- ///
- [Fact]
- public async Task CreateAIAgentAsync_WithDeclarativeFunctionInDefinition_AcceptsDeclarativeFunctionAsync()
- {
- // Arrange
- var definition = new PromptAgentDefinition("test-model") { Instructions = "Test" };
-
- // Create a declarative function (not invocable) using AIFunctionFactory.CreateDeclaration
- using var doc = JsonDocument.Parse("{}");
- var declarativeFunction = AIFunctionFactory.CreateDeclaration("test_function", "A test function", doc.RootElement);
-
- // Add to definition
- definition.Tools.Add(declarativeFunction.AsOpenAIResponseTool() ?? throw new InvalidOperationException());
-
- // Generate response with the declarative function
- var definitionResponse = new PromptAgentDefinition("test-model") { Instructions = "Test" };
- definitionResponse.Tools.Add(declarativeFunction.AsOpenAIResponseTool() ?? throw new InvalidOperationException());
-
- using var testClient = CreateTestAgentClientWithHandler(agentName: "test-agent", agentDefinitionResponse: definitionResponse);
-
- var options = new AgentVersionCreationOptions(definition);
-
- // Act
- var agent = await testClient.Client.CreateAIAgentAsync("test-agent", options);
-
- // Assert
- Assert.NotNull(agent);
- Assert.IsType(agent);
- }
-
- #endregion
-
- #region Options Generation Validation Tests
-
- ///
- /// Verify that ChatClientAgentOptions are generated correctly without tools.
- ///
- [Fact]
- public async Task CreateAIAgentAsync_GeneratesCorrectChatClientAgentOptionsAsync()
- {
- // Arrange
- var definition = new PromptAgentDefinition("test-model") { Instructions = "Test instructions" };
-
- var definitionResponse = GeneratePromptDefinitionResponse(definition, null);
- using var testClient = CreateTestAgentClientWithHandler(agentName: "test-agent", agentDefinitionResponse: definitionResponse);
-
- var options = new AgentVersionCreationOptions(definition);
-
- // Act
- var agent = await testClient.Client.CreateAIAgentAsync("test-agent", options);
-
- // Assert
- Assert.NotNull(agent);
- var agentVersion = agent.GetService();
- Assert.NotNull(agentVersion);
- Assert.Equal("test-agent", agentVersion.Name);
- Assert.Equal("Test instructions", (agentVersion.Definition as PromptAgentDefinition)?.Instructions);
- }
-
- ///
- /// Verify that GetAIAgentAsync with options preserves custom properties from input options.
- ///
- [Fact]
- public async Task GetAIAgentAsync_WithOptions_PreservesCustomPropertiesAsync()
- {
- // Arrange
- AIProjectClient client = this.CreateTestAgentClient(agentName: "test-agent", instructions: "Custom instructions", description: "Custom description");
- var options = new ChatClientAgentOptions
- {
- Name = "test-agent",
- Description = "Custom description",
- ChatOptions = new ChatOptions { Instructions = "Custom instructions" }
- };
-
- // Act
- var agent = await client.GetAIAgentAsync(options);
-
- // Assert
- Assert.NotNull(agent);
- Assert.Equal("test-agent", agent.Name);
- Assert.Equal("Custom instructions", agent.GetService()!.Instructions);
- Assert.Equal("Custom description", agent.Description);
- }
-
- ///
- /// Verify that CreateAIAgentAsync with options and tools generates correct ChatClientAgentOptions.
- ///
- [Fact]
- public async Task CreateAIAgentAsync_WithOptionsAndTools_GeneratesCorrectOptionsAsync()
- {
- // Arrange
- var tools = new List
- {
- AIFunctionFactory.Create(() => "result", "option_tool", "A tool from options")
- };
-
- var definitionResponse = GeneratePromptDefinitionResponse(
- new PromptAgentDefinition("test-model") { Instructions = "Test" },
- tools);
-
- using var testClient = CreateTestAgentClientWithHandler(agentName: "test-agent", agentDefinitionResponse: definitionResponse);
-
- var options = new ChatClientAgentOptions
- {
- Name = "test-agent",
- ChatOptions = new ChatOptions { Instructions = "Test", Tools = tools }
- };
-
- // Act
- var agent = await testClient.Client.CreateAIAgentAsync("test-model", options);
-
- // Assert
- Assert.NotNull(agent);
- var agentVersion = agent.GetService();
- Assert.NotNull(agentVersion);
- if (agentVersion.Definition is PromptAgentDefinition promptDef)
- {
- Assert.NotEmpty(promptDef.Tools);
- Assert.Single(promptDef.Tools);
- }
- }
-
- #endregion
-
- #region AgentName Validation Tests
-
- ///
- /// Verify that AsAIAgent throws ArgumentException when agent name is invalid.
- ///
- [Theory]
- [MemberData(nameof(InvalidAgentNameTestData.GetInvalidAgentNames), MemberType = typeof(InvalidAgentNameTestData))]
- public void AsAIAgent_ByName_WithInvalidAgentName_ThrowsArgumentException(string invalidName)
- {
- // Arrange
- var mockClient = new Mock();
-
- // Act & Assert
- var exception = Assert.Throws(() =>
- mockClient.Object.AsAIAgent(invalidName));
-
- Assert.Equal("name", exception.ParamName);
- Assert.Contains("Agent name must be 1-63 characters long", exception.Message);
- }
-
- ///
- /// Verify that GetAIAgentAsync throws ArgumentException when agent name is invalid.
- ///
- [Theory]
- [MemberData(nameof(InvalidAgentNameTestData.GetInvalidAgentNames), MemberType = typeof(InvalidAgentNameTestData))]
- public async Task GetAIAgentAsync_ByName_WithInvalidAgentName_ThrowsArgumentExceptionAsync(string invalidName)
- {
- // Arrange
- var mockClient = new Mock();
-
- // Act & Assert
- var exception = await Assert.ThrowsAsync(() =>
- mockClient.Object.GetAIAgentAsync(invalidName));
-
- Assert.Equal("name", exception.ParamName);
- Assert.Contains("Agent name must be 1-63 characters long", exception.Message);
- }
-
- ///
- /// Verify that GetAIAgentAsync with ChatClientAgentOptions throws ArgumentException when agent name is invalid.
- ///
- [Theory]
- [MemberData(nameof(InvalidAgentNameTestData.GetInvalidAgentNames), MemberType = typeof(InvalidAgentNameTestData))]
- public async Task GetAIAgentAsync_WithOptions_WithInvalidAgentName_ThrowsArgumentExceptionAsync(string invalidName)
- {
- // Arrange
- AIProjectClient client = this.CreateTestAgentClient();
- var options = new ChatClientAgentOptions { Name = invalidName };
-
- // Act & Assert
- var exception = await Assert.ThrowsAsync(() =>
- client.GetAIAgentAsync(options));
-
- Assert.Equal("name", exception.ParamName);
- Assert.Contains("Agent name must be 1-63 characters long", exception.Message);
- }
-
- ///
- /// Verify that CreateAIAgentAsync throws ArgumentException when agent name is invalid.
- ///
- [Theory]
- [MemberData(nameof(InvalidAgentNameTestData.GetInvalidAgentNames), MemberType = typeof(InvalidAgentNameTestData))]
- public async Task CreateAIAgentAsync_WithBasicParams_WithInvalidAgentName_ThrowsArgumentExceptionAsync(string invalidName)
- {
- // Arrange
- var mockClient = new Mock();
-
- // Act & Assert
- var exception = await Assert.ThrowsAsync(() =>
- mockClient.Object.CreateAIAgentAsync(invalidName, "model", "instructions"));
-
- Assert.Equal("name", exception.ParamName);
- Assert.Contains("Agent name must be 1-63 characters long", exception.Message);
- }
-
- ///
- /// Verify that CreateAIAgentAsync with AgentVersionCreationOptions throws ArgumentException when agent name is invalid.
- ///
- [Theory]
- [MemberData(nameof(InvalidAgentNameTestData.GetInvalidAgentNames), MemberType = typeof(InvalidAgentNameTestData))]
- public async Task CreateAIAgentAsync_WithAgentDefinition_WithInvalidAgentName_ThrowsArgumentExceptionAsync(string invalidName)
- {
- // Arrange
- var mockClient = new Mock();
- var definition = new PromptAgentDefinition("test-model");
- var options = new AgentVersionCreationOptions(definition);
-
- // Act & Assert
- var exception = await Assert.ThrowsAsync(() =>
- mockClient.Object.CreateAIAgentAsync(invalidName, options));
-
- Assert.Equal("name", exception.ParamName);
- Assert.Contains("Agent name must be 1-63 characters long", exception.Message);
- }
-
- ///
- /// Verify that CreateAIAgentAsync with ChatClientAgentOptions throws ArgumentException when agent name is invalid.
- ///
- [Theory]
- [MemberData(nameof(InvalidAgentNameTestData.GetInvalidAgentNames), MemberType = typeof(InvalidAgentNameTestData))]
- public async Task CreateAIAgentAsync_WithOptions_WithInvalidAgentName_ThrowsArgumentExceptionAsync(string invalidName)
- {
- // Arrange
- AIProjectClient client = this.CreateTestAgentClient();
- var options = new ChatClientAgentOptions { Name = invalidName };
-
- // Act & Assert
- var exception = await Assert.ThrowsAsync(() =>
- client.CreateAIAgentAsync("test-model", options));
-
- Assert.Equal("name", exception.ParamName);
- Assert.Contains("Agent name must be 1-63 characters long", exception.Message);
- }
-
- ///
- /// Verify that AsAIAgent with AgentReference throws ArgumentException when agent name is invalid.
- ///
- [Theory]
- [MemberData(nameof(InvalidAgentNameTestData.GetInvalidAgentNames), MemberType = typeof(InvalidAgentNameTestData))]
- public void AsAIAgent_WithAgentReference_WithInvalidAgentName_ThrowsArgumentException(string invalidName)
- {
- // Arrange
- var mockClient = new Mock();
- var agentReference = new AgentReference(invalidName, "1");
-
- // Act & Assert
- var exception = Assert.Throws(() =>
- mockClient.Object.AsAIAgent(agentReference));
-
- Assert.Equal("name", exception.ParamName);
- Assert.Contains("Agent name must be 1-63 characters long", exception.Message);
- }
-
- #endregion
-
- #region AzureAIChatClient Behavior Tests
-
- ///
- /// Verify that the underlying chat client created by extension methods can be wrapped with clientFactory.
- ///
- [Fact]
- public void AsAIAgent_WithClientFactory_WrapsUnderlyingChatClient()
- {
- // Arrange
- AIProjectClient client = this.CreateTestAgentClient();
- AgentRecord agentRecord = this.CreateTestAgentRecord();
- int factoryCallCount = 0;
-
- // Act
- var agent = client.AsAIAgent(
- agentRecord,
- clientFactory: (innerClient) =>
- {
- factoryCallCount++;
- return new TestChatClient(innerClient);
- });
-
- // Assert
- Assert.NotNull(agent);
- Assert.Equal(1, factoryCallCount);
- var wrappedClient = agent.GetService();
- Assert.NotNull(wrappedClient);
- }
-
- ///
- /// Verify that clientFactory is called with the correct underlying chat client.
- ///
- [Fact]
- public async Task CreateAIAgentAsync_WithClientFactory_ReceivesCorrectUnderlyingClientAsync()
- {
- // Arrange
- using var testClient = CreateTestAgentClientWithHandler();
- var definition = new PromptAgentDefinition("test-model") { Instructions = "Test" };
- IChatClient? receivedClient = null;
-
- var options = new AgentVersionCreationOptions(definition);
-
- // Act
- var agent = await testClient.Client.CreateAIAgentAsync(
- "test-agent",
- options,
- clientFactory: (innerClient) =>
- {
- receivedClient = innerClient;
- return new TestChatClient(innerClient);
- });
-
- // Assert
- Assert.NotNull(agent);
- Assert.NotNull(receivedClient);
- var wrappedClient = agent.GetService();
- Assert.NotNull(wrappedClient);
- }
-
- ///
- /// Verify that multiple clientFactory calls create independent wrapped clients.
- ///
- [Fact]
- public void AsAIAgent_MultipleCallsWithClientFactory_CreatesIndependentClients()
- {
- // Arrange
- AIProjectClient client = this.CreateTestAgentClient();
- AgentRecord agentRecord = this.CreateTestAgentRecord();
-
- // Act
- var agent1 = client.AsAIAgent(
- agentRecord,
- clientFactory: (innerClient) => new TestChatClient(innerClient));
-
- var agent2 = client.AsAIAgent(
- agentRecord,
- clientFactory: (innerClient) => new TestChatClient(innerClient));
-
- // Assert
- Assert.NotNull(agent1);
- Assert.NotNull(agent2);
- var client1 = agent1.GetService();
- var client2 = agent2.GetService();
- Assert.NotNull(client1);
- Assert.NotNull(client2);
- Assert.NotSame(client1, client2);
- }
-
- ///
- /// Verify that agent created with clientFactory maintains agent properties.
- ///
- [Fact]
- public async Task CreateAIAgentAsync_WithClientFactory_PreservesAgentPropertiesAsync()
- {
- // Arrange
- const string AgentName = "test-agent";
- const string Model = "test-model";
- const string Instructions = "Test instructions";
- using var testClient = CreateTestAgentClientWithHandler(AgentName, Instructions);
-
- // Act
- var agent = await testClient.Client.CreateAIAgentAsync(
- AgentName,
- Model,
- Instructions,
- clientFactory: (innerClient) => new TestChatClient(innerClient));
-
- // Assert
- Assert.NotNull(agent);
- Assert.Equal(AgentName, agent.Name);
- Assert.Equal(Instructions, agent.GetService()!.Instructions);
- var wrappedClient = agent.GetService();
- Assert.NotNull(wrappedClient);
- }
-
- ///
- /// Verify that agent created with clientFactory is created successfully.
- ///
- [Fact]
- public async Task CreateAIAgentAsync_WithClientFactory_CreatesAgentSuccessfullyAsync()
- {
- // Arrange
- var definition = new PromptAgentDefinition("test-model") { Instructions = "Test" };
-
- var agentDefinitionResponse = GeneratePromptDefinitionResponse(definition, null);
- using var testClient = CreateTestAgentClientWithHandler(agentName: "test-agent", agentDefinitionResponse: agentDefinitionResponse);
-
- var options = new AgentVersionCreationOptions(definition);
-
- // Act
- var agent = await testClient.Client.CreateAIAgentAsync(
- "test-agent",
- options,
- clientFactory: (innerClient) => new TestChatClient(innerClient));
-
- // Assert
- Assert.NotNull(agent);
- var wrappedClient = agent.GetService();
- Assert.NotNull(wrappedClient);
- var agentVersion = agent.GetService();
- Assert.NotNull(agentVersion);
- }
-
- #endregion
-
- #region User-Agent Header Tests
-
- ///
- /// Verifies that the MEAI user-agent header is added to CreateAIAgentAsync POST requests
- /// via the protocol method's RequestOptions pipeline policy.
- ///
- [Fact]
- public async Task CreateAIAgentAsync_UserAgentHeaderAddedToRequestsAsync()
- {
- using var httpHandler = new HttpHandlerAssert(request =>
- {
- Assert.Equal("POST", request.Method.Method);
-
- // Verify MEAI user-agent header is present on CreateAgentVersion POST request
- Assert.True(request.Headers.TryGetValues("User-Agent", out var userAgentValues));
- Assert.Contains(userAgentValues, v => v.Contains("MEAI"));
-
- return new HttpResponseMessage(HttpStatusCode.OK) { Content = new StringContent(TestDataUtil.GetAgentVersionResponseJson(), Encoding.UTF8, "application/json") };
- });
-
-#pragma warning disable CA5399
- using var httpClient = new HttpClient(httpHandler);
-#pragma warning restore CA5399
-
- // Arrange
- var aiProjectClient = new AIProjectClient(new Uri("https://test.openai.azure.com/"), new FakeAuthenticationTokenProvider(), new() { Transport = new HttpClientPipelineTransport(httpClient) });
-
- var agentOptions = new ChatClientAgentOptions { Name = "test-agent" };
-
- // Act
- var agent = await aiProjectClient.CreateAIAgentAsync("test", agentOptions);
-
- // Assert
- Assert.NotNull(agent);
- }
-
- ///
- /// Verifies that the user-agent header is added to asynchronous GetAIAgentAsync requests.
- ///
- [Fact]
- public async Task GetAIAgent_UserAgentHeaderAddedToRequestsAsync()
- {
- using var httpHandler = new HttpHandlerAssert(request =>
- {
- Assert.Equal("GET", request.Method.Method);
- Assert.Contains("MEAI", request.Headers.UserAgent.ToString());
-
- return new HttpResponseMessage(HttpStatusCode.OK) { Content = new StringContent(TestDataUtil.GetAgentResponseJson(), Encoding.UTF8, "application/json") };
- });
-
-#pragma warning disable CA5399
- using var httpClient = new HttpClient(httpHandler);
-#pragma warning restore CA5399
-
- // Arrange
- var aiProjectClient = new AIProjectClient(new Uri("https://test.openai.azure.com/"), new FakeAuthenticationTokenProvider(), new() { Transport = new HttpClientPipelineTransport(httpClient) });
-
- // Act
- var agent = await aiProjectClient.GetAIAgentAsync("test");
-
- // Assert
- Assert.NotNull(agent);
- }
-
- #endregion
-
- #region GetAIAgent(AIProjectClient, AgentReference) Tests
-
- ///
- /// Verify that AsAIAgent throws ArgumentNullException when AIProjectClient is null.
- ///
- [Fact]
- public void AsAIAgent_WithAgentReference_WithNullClient_ThrowsArgumentNullException()
- {
- // Arrange
- AIProjectClient? client = null;
- var agentReference = new AgentReference("test-name", "1");
-
- // Act & Assert
- var exception = Assert.Throws(() =>
- client!.AsAIAgent(agentReference));
-
- Assert.Equal("aiProjectClient", exception.ParamName);
- }
-
- ///
- /// Verify that AsAIAgent throws ArgumentNullException when agentReference is null.
- ///
- [Fact]
- public void AsAIAgent_WithAgentReference_WithNullAgentReference_ThrowsArgumentNullException()
- {
- // Arrange
- var mockClient = new Mock();
-
- // Act & Assert
- var exception = Assert.Throws(() =>
- mockClient.Object.AsAIAgent((AgentReference)null!));
-
- Assert.Equal("agentReference", exception.ParamName);
- }
-
- ///
- /// Verify that AsAIAgent with AgentReference creates a valid agent.
- ///
- [Fact]
- public void AsAIAgent_WithAgentReference_CreatesValidAgent()
- {
- // Arrange
- AIProjectClient client = this.CreateTestAgentClient();
- var agentReference = new AgentReference("test-name", "1");
-
- // Act
- var agent = client.AsAIAgent(agentReference);
-
- // Assert
- Assert.NotNull(agent);
- Assert.Equal("test-name", agent.Name);
- Assert.Equal("test-name:1", agent.Id);
- }
-
- ///
- /// Verify that AsAIAgent with AgentReference and clientFactory applies the factory.
- ///
- [Fact]
- public void AsAIAgent_WithAgentReference_WithClientFactory_AppliesFactoryCorrectly()
- {
- // Arrange
- AIProjectClient client = this.CreateTestAgentClient();
- var agentReference = new AgentReference("test-name", "1");
- TestChatClient? testChatClient = null;
-
- // Act
- var agent = client.AsAIAgent(
- agentReference,
- clientFactory: (innerClient) => testChatClient = new TestChatClient(innerClient));
-
- // Assert
- Assert.NotNull(agent);
- var retrievedTestClient = agent.GetService();
- Assert.NotNull(retrievedTestClient);
- Assert.Same(testChatClient, retrievedTestClient);
- }
-
- ///
- /// Verify that AsAIAgent with AgentReference sets the agent ID correctly.
- ///
- [Fact]
- public void AsAIAgent_WithAgentReference_SetsAgentIdCorrectly()
- {
- // Arrange
- AIProjectClient client = this.CreateTestAgentClient();
- var agentReference = new AgentReference("test-name", "2");
-
- // Act
- var agent = client.AsAIAgent(agentReference);
-
- // Assert
- Assert.NotNull(agent);
- Assert.Equal("test-name:2", agent.Id);
- }
-
- ///
- /// Verify that AsAIAgent with AgentReference and tools includes the tools in ChatOptions.
- ///
- [Fact]
- public void AsAIAgent_WithAgentReference_WithTools_IncludesToolsInChatOptions()
- {
- // Arrange
- AIProjectClient client = this.CreateTestAgentClient();
- var agentReference = new AgentReference("test-name", "1");
- var tools = new List
- {
- AIFunctionFactory.Create(() => "test", "test_function", "A test function")
- };
-
- // Act
- var agent = client.AsAIAgent(agentReference, tools: tools);
-
- // Assert
- Assert.NotNull(agent);
- var chatOptions = GetAgentChatOptions(agent);
- Assert.NotNull(chatOptions);
- Assert.NotNull(chatOptions.Tools);
- Assert.Single(chatOptions.Tools);
- }
-
- #endregion
-
- #region GetService Tests
-
- ///
- /// Verify that GetService returns AgentRecord for agents created from AgentRecord.
- ///
- [Fact]
- public void GetService_WithAgentRecord_ReturnsAgentRecord()
- {
- // Arrange
- AIProjectClient client = this.CreateTestAgentClient();
- AgentRecord agentRecord = this.CreateTestAgentRecord();
-
- // Act
- var agent = client.AsAIAgent(agentRecord);
- var retrievedRecord = agent.GetService();
-
- // Assert
- Assert.NotNull(retrievedRecord);
- Assert.Equal(agentRecord.Id, retrievedRecord.Id);
- }
-
- ///
- /// Verify that GetService returns null for AgentRecord when agent is created from AgentReference.
- ///
- [Fact]
- public void GetService_WithAgentReference_ReturnsNullForAgentRecord()
- {
- // Arrange
- AIProjectClient client = this.CreateTestAgentClient();
- var agentReference = new AgentReference("test-name", "1");
-
- // Act
- var agent = client.AsAIAgent(agentReference);
- var retrievedRecord = agent.GetService();
-
- // Assert
- Assert.Null(retrievedRecord);
- }
-
- #endregion
-
- #region GetService Tests
-
- ///
- /// Verify that GetService returns AgentVersion for agents created from AgentVersion.
- ///
- [Fact]
- public void GetService_WithAgentVersion_ReturnsAgentVersion()
- {
- // Arrange
- AIProjectClient client = this.CreateTestAgentClient();
- AgentVersion agentVersion = this.CreateTestAgentVersion();
-
- // Act
- var agent = client.AsAIAgent(agentVersion);
- var retrievedVersion = agent.GetService();
-
- // Assert
- Assert.NotNull(retrievedVersion);
- Assert.Equal(agentVersion.Id, retrievedVersion.Id);
- }
-
- ///
- /// Verify that GetService returns null for AgentVersion when agent is created from AgentReference.
- ///
- [Fact]
- public void GetService_WithAgentReference_ReturnsNullForAgentVersion()
- {
- // Arrange
- AIProjectClient client = this.CreateTestAgentClient();
- var agentReference = new AgentReference("test-name", "1");
-
- // Act
- var agent = client.AsAIAgent(agentReference);
- var retrievedVersion = agent.GetService();
-
- // Assert
- Assert.Null(retrievedVersion);
- }
-
- #endregion
-
- #region ChatClientMetadata Tests
-
- ///
- /// Verify that ChatClientMetadata is properly populated for agents created from AgentRecord.
- ///
- [Fact]
- public void ChatClientMetadata_WithAgentRecord_IsPopulatedCorrectly()
- {
- // Arrange
- AIProjectClient client = this.CreateTestAgentClient();
- AgentRecord agentRecord = this.CreateTestAgentRecord();
-
- // Act
- var agent = client.AsAIAgent(agentRecord);
- var metadata = agent.GetService();
-
- // Assert
- Assert.NotNull(metadata);
- Assert.NotNull(metadata.DefaultModelId);
- }
-
- ///
- /// Verify that ChatClientMetadata.DefaultModelId is set from PromptAgentDefinition model property.
- ///
- [Fact]
- public void ChatClientMetadata_WithPromptAgentDefinition_SetsDefaultModelIdFromModel()
- {
- // Arrange
- AIProjectClient client = this.CreateTestAgentClient();
- var definition = new PromptAgentDefinition("gpt-4-turbo")
- {
- Instructions = "Test instructions"
- };
- AgentRecord agentRecord = this.CreateTestAgentRecord(definition);
-
- // Act
- var agent = client.AsAIAgent(agentRecord);
- var metadata = agent.GetService();
-
- // Assert
- Assert.NotNull(metadata);
- // The metadata should contain the model information from the agent definition
- Assert.NotNull(metadata.DefaultModelId);
- Assert.Equal("gpt-4-turbo", metadata.DefaultModelId);
- }
-
- ///
- /// Verify that ChatClientMetadata is properly populated for agents created from AgentVersion.
- ///
- [Fact]
- public void ChatClientMetadata_WithAgentVersion_IsPopulatedCorrectly()
- {
- // Arrange
- AIProjectClient client = this.CreateTestAgentClient();
- AgentVersion agentVersion = this.CreateTestAgentVersion();
-
- // Act
- var agent = client.AsAIAgent(agentVersion);
- var metadata = agent.GetService();
-
- // Assert
- Assert.NotNull(metadata);
- Assert.NotNull(metadata.DefaultModelId);
- Assert.Equal((agentVersion.Definition as PromptAgentDefinition)!.Model, metadata.DefaultModelId);
- }
-
- #endregion
-
- #region AgentReference Availability Tests
-
- ///
- /// Verify that GetService returns AgentReference for agents created from AgentReference.
- ///
- [Fact]
- public void GetService_WithAgentReference_ReturnsAgentReference()
- {
- // Arrange
- AIProjectClient client = this.CreateTestAgentClient();
- var agentReference = new AgentReference("test-agent", "1.0");
-
- // Act
- var agent = client.AsAIAgent(agentReference);
- var retrievedReference = agent.GetService();
-
- // Assert
- Assert.NotNull(retrievedReference);
- Assert.Equal("test-agent", retrievedReference.Name);
- Assert.Equal("1.0", retrievedReference.Version);
- }
-
- ///
- /// Verify that GetService returns null for AgentReference when agent is created from AgentRecord.
- ///
- [Fact]
- public void GetService_WithAgentRecord_ReturnsAlsoAgentReference()
- {
- // Arrange
- AIProjectClient client = this.CreateTestAgentClient();
- AgentRecord agentRecord = this.CreateTestAgentRecord();
-
- // Act
- var agent = client.AsAIAgent(agentRecord);
- var retrievedReference = agent.GetService();
-
- // Assert
- Assert.NotNull(retrievedReference);
- Assert.Equal(agentRecord.Name, retrievedReference.Name);
- }
-
- ///
- /// Verify that GetService returns null for AgentReference when agent is created from AgentVersion.
- ///
- [Fact]
- public void GetService_WithAgentVersion_ReturnsAlsoAgentReference()
- {
- // Arrange
- AIProjectClient client = this.CreateTestAgentClient();
- AgentVersion agentVersion = this.CreateTestAgentVersion();
-
- // Act
- var agent = client.AsAIAgent(agentVersion);
- var retrievedReference = agent.GetService();
-
- // Assert
- Assert.NotNull(retrievedReference);
- Assert.Equal(agentVersion.Name, retrievedReference.Name);
- }
-
- ///
- /// Verify that GetService returns AgentReference with correct version information.
- ///
- [Fact]
- public void GetService_WithAgentReference_ReturnsCorrectVersionInformation()
- {
- // Arrange
- AIProjectClient client = this.CreateTestAgentClient();
- var agentReference = new AgentReference("versioned-agent", "3.5");
-
- // Act
- var agent = client.AsAIAgent(agentReference);
- var retrievedReference = agent.GetService();
-
- // Assert
- Assert.NotNull(retrievedReference);
- Assert.Equal("versioned-agent", retrievedReference.Name);
- Assert.Equal("3.5", retrievedReference.Version);
- }
-
- #endregion
-
- #region GetAIAgentAsync - Empty Name Tests
-
- ///
- /// Verify that GetAIAgentAsync with ChatClientAgentOptions throws ArgumentException when name is null.
- ///
- [Fact]
- public async Task GetAIAgentAsync_WithOptions_WithNullName_ThrowsArgumentExceptionAsync()
- {
- // Arrange
- AIProjectClient client = this.CreateTestAgentClient();
- var options = new ChatClientAgentOptions { Name = null };
-
- // Act & Assert
- ArgumentException exception = await Assert.ThrowsAsync(() =>
- client.GetAIAgentAsync(options));
-
- Assert.Equal("options", exception.ParamName);
- Assert.Contains("Agent name must be provided", exception.Message);
- }
-
- ///
- /// Verify that GetAIAgentAsync with ChatClientAgentOptions throws ArgumentException when name is empty.
- ///
- [Fact]
- public async Task GetAIAgentAsync_WithOptions_WithEmptyName_ThrowsArgumentExceptionAsync()
- {
- // Arrange
- AIProjectClient client = this.CreateTestAgentClient();
- var options = new ChatClientAgentOptions { Name = string.Empty };
-
- // Act & Assert
- ArgumentException exception = await Assert.ThrowsAsync(() =>
- client.GetAIAgentAsync(options));
-
- Assert.Equal("options", exception.ParamName);
- Assert.Contains("Agent name must be provided", exception.Message);
- }
-
- ///
- /// Verify that GetAIAgentAsync with ChatClientAgentOptions throws ArgumentException when name is whitespace.
- ///
- [Fact]
- public async Task GetAIAgentAsync_WithOptions_WithWhitespaceName_ThrowsArgumentExceptionAsync()
- {
- // Arrange
- AIProjectClient client = this.CreateTestAgentClient();
- var options = new ChatClientAgentOptions { Name = " " };
-
- // Act & Assert
- ArgumentException exception = await Assert.ThrowsAsync(() =>
- client.GetAIAgentAsync(options));
-
- Assert.Equal("options", exception.ParamName);
- Assert.Contains("Agent name must be provided", exception.Message);
- }
-
- #endregion
-
- #region CreateAIAgentAsync - Empty Name Tests
-
- ///
- /// Verify that CreateAIAgentAsync with model and options throws ArgumentException when name is null.
- ///
- [Fact]
- public async Task CreateAIAgentAsync_WithModelAndOptions_WithNullName_ThrowsArgumentExceptionAsync()
- {
- // Arrange
- AIProjectClient client = this.CreateTestAgentClient();
- var options = new ChatClientAgentOptions
- {
- Name = null,
- ChatOptions = new ChatOptions { Instructions = "Test" }
- };
-
- // Act & Assert
- ArgumentException exception = await Assert.ThrowsAsync(() =>
- client.CreateAIAgentAsync("test-model", options));
-
- Assert.Equal("options", exception.ParamName);
- Assert.Contains("Agent name must be provided", exception.Message);
- }
-
- ///
- /// Verify that CreateAIAgentAsync with model and options throws ArgumentException when name is empty.
- ///
- [Fact]
- public async Task CreateAIAgentAsync_WithModelAndOptions_WithEmptyName_ThrowsArgumentExceptionAsync()
- {
- // Arrange
- AIProjectClient client = this.CreateTestAgentClient();
- var options = new ChatClientAgentOptions
- {
- Name = string.Empty,
- ChatOptions = new ChatOptions { Instructions = "Test" }
- };
-
- // Act & Assert
- ArgumentException exception = await Assert.ThrowsAsync(() =>
- client.CreateAIAgentAsync("test-model", options));
-
- Assert.Equal("options", exception.ParamName);
- Assert.Contains("Agent name must be provided", exception.Message);
- }
-
- ///
- /// Verify that CreateAIAgentAsync with model and options throws ArgumentException when name is whitespace.
- ///
- [Fact]
- public async Task CreateAIAgentAsync_WithModelAndOptions_WithWhitespaceName_ThrowsArgumentExceptionAsync()
- {
- // Arrange
- AIProjectClient client = this.CreateTestAgentClient();
- var options = new ChatClientAgentOptions
- {
- Name = " ",
- ChatOptions = new ChatOptions { Instructions = "Test" }
- };
-
- // Act & Assert
- ArgumentException exception = await Assert.ThrowsAsync(() =>
- client.CreateAIAgentAsync("test-model", options));
-
- Assert.Equal("options", exception.ParamName);
- Assert.Contains("Agent name must be provided", exception.Message);
- }
-
- #endregion
-
- #region CreateAIAgentAsync - Response Format Tests
-
- ///
- /// Verify that CreateAIAgentAsync with ChatResponseFormatText response format creates agent successfully.
- ///
- [Fact]
- public async Task CreateAIAgentAsync_WithTextResponseFormat_CreatesAgentSuccessfullyAsync()
- {
- // Arrange
- using var testClient = CreateTestAgentClientWithHandler();
- var options = new ChatClientAgentOptions
- {
- Name = "test-agent",
- ChatOptions = new ChatOptions
- {
- Instructions = "Test",
- ResponseFormat = ChatResponseFormat.Text
- }
- };
-
- // Act
- FoundryAgent agent = await testClient.Client.CreateAIAgentAsync("test-model", options);
-
- // Assert
- Assert.NotNull(agent);
- Assert.IsType(agent);
- }
-
- ///
- /// Verify that CreateAIAgentAsync with ChatResponseFormatJson response format without schema creates agent successfully.
- ///
- [Fact]
- public async Task CreateAIAgentAsync_WithJsonResponseFormatWithoutSchema_CreatesAgentSuccessfullyAsync()
- {
- // Arrange
- using var testClient = CreateTestAgentClientWithHandler();
- var options = new ChatClientAgentOptions
- {
- Name = "test-agent",
- ChatOptions = new ChatOptions
- {
- Instructions = "Test",
- ResponseFormat = ChatResponseFormat.Json
- }
- };
-
- // Act
- FoundryAgent agent = await testClient.Client.CreateAIAgentAsync("test-model", options);
-
- // Assert
- Assert.NotNull(agent);
- Assert.IsType(agent);
- }
-
- ///
- /// Verify that CreateAIAgentAsync with ChatResponseFormatJson with schema creates agent successfully.
- ///
- [Fact]
- public async Task CreateAIAgentAsync_WithJsonResponseFormatWithSchema_CreatesAgentSuccessfullyAsync()
- {
- // Arrange
- using var testClient = CreateTestAgentClientWithHandler();
- JsonElement schemaElement = AIJsonUtilities.CreateJsonSchema(typeof(TestSchema));
- var jsonFormat = ChatResponseFormat.ForJsonSchema(schemaElement, "test_schema", "A test schema");
- var options = new ChatClientAgentOptions
- {
- Name = "test-agent",
- ChatOptions = new ChatOptions
- {
- Instructions = "Test",
- ResponseFormat = jsonFormat
- }
- };
-
- // Act
- FoundryAgent agent = await testClient.Client.CreateAIAgentAsync("test-model", options);
-
- // Assert
- Assert.NotNull(agent);
- Assert.IsType(agent);
- }
-
- ///
- /// Verify that CreateAIAgentAsync with ChatResponseFormatJson with schema and strict mode creates agent successfully.
- ///
- [Fact]
- public async Task CreateAIAgentAsync_WithJsonResponseFormatWithSchemaAndStrictMode_CreatesAgentSuccessfullyAsync()
- {
- // Arrange
- using var testClient = CreateTestAgentClientWithHandler();
- JsonElement schemaElement = AIJsonUtilities.CreateJsonSchema(typeof(TestSchema));
- var jsonFormat = ChatResponseFormat.ForJsonSchema(schemaElement, "test_schema", "A test schema");
- var additionalProps = new AdditionalPropertiesDictionary
- {
- ["strictJsonSchema"] = true
- };
- var options = new ChatClientAgentOptions
- {
- Name = "test-agent",
- ChatOptions = new ChatOptions
- {
- Instructions = "Test",
- ResponseFormat = jsonFormat,
- AdditionalProperties = additionalProps
- }
- };
-
- // Act
- FoundryAgent agent = await testClient.Client.CreateAIAgentAsync("test-model", options);
-
- // Assert
- Assert.NotNull(agent);
- Assert.IsType(agent);
- }
-
- ///
- /// Verify that CreateAIAgentAsync with ChatResponseFormatJson with schema and strict mode false creates agent successfully.
- ///
- [Fact]
- public async Task CreateAIAgentAsync_WithJsonResponseFormatWithSchemaAndStrictModeFalse_CreatesAgentSuccessfullyAsync()
- {
- // Arrange
- using var testClient = CreateTestAgentClientWithHandler();
- JsonElement schemaElement = AIJsonUtilities.CreateJsonSchema(typeof(TestSchema));
- var jsonFormat = ChatResponseFormat.ForJsonSchema(schemaElement, "test_schema", "A test schema");
- var additionalProps = new AdditionalPropertiesDictionary
- {
- ["strictJsonSchema"] = false
- };
- var options = new ChatClientAgentOptions
- {
- Name = "test-agent",
- ChatOptions = new ChatOptions
- {
- Instructions = "Test",
- ResponseFormat = jsonFormat,
- AdditionalProperties = additionalProps
- }
- };
-
- // Act
- FoundryAgent agent = await testClient.Client.CreateAIAgentAsync("test-model", options);
-
- // Assert
- Assert.NotNull(agent);
- Assert.IsType(agent);
- }
-
- #endregion
-
- #region CreateAIAgentAsync - RawRepresentationFactory Tests
-
- ///
- /// Verify that CreateAIAgentAsync with RawRepresentationFactory that returns CreateResponseOptions creates agent successfully.
- ///
- [Fact]
- public async Task CreateAIAgentAsync_WithRawRepresentationFactory_CreatesAgentSuccessfullyAsync()
- {
- // Arrange
- using var testClient = CreateTestAgentClientWithHandler();
- var options = new ChatClientAgentOptions
- {
- Name = "test-agent",
- ChatOptions = new ChatOptions
- {
- Instructions = "Test",
- RawRepresentationFactory = _ => new CreateResponseOptions()
- }
- };
-
- // Act
- FoundryAgent agent = await testClient.Client.CreateAIAgentAsync("test-model", options);
-
- // Assert
- Assert.NotNull(agent);
- Assert.IsType(agent);
- }
-
- ///
- /// Verify that CreateAIAgentAsync with RawRepresentationFactory that returns null does not fail.
- ///
- [Fact]
- public async Task CreateAIAgentAsync_WithRawRepresentationFactoryReturningNull_CreatesAgentSuccessfullyAsync()
- {
- // Arrange
- using var testClient = CreateTestAgentClientWithHandler();
- var options = new ChatClientAgentOptions
- {
- Name = "test-agent",
- ChatOptions = new ChatOptions
- {
- Instructions = "Test",
- RawRepresentationFactory = _ => null
- }
- };
-
- // Act
- FoundryAgent agent = await testClient.Client.CreateAIAgentAsync("test-model", options);
-
- // Assert
- Assert.NotNull(agent);
- Assert.IsType(agent);
- }
-
- ///
- /// Verify that CreateAIAgentAsync with RawRepresentationFactory that returns non-CreateResponseOptions does not fail.
- ///
- [Fact]
- public async Task CreateAIAgentAsync_WithRawRepresentationFactoryReturningNonCreateResponseOptions_CreatesAgentSuccessfullyAsync()
- {
- // Arrange
- using var testClient = CreateTestAgentClientWithHandler();
- var options = new ChatClientAgentOptions
- {
- Name = "test-agent",
- ChatOptions = new ChatOptions
- {
- Instructions = "Test",
- RawRepresentationFactory = _ => new object()
- }
- };
-
- // Act
- FoundryAgent agent = await testClient.Client.CreateAIAgentAsync("test-model", options);
-
- // Assert
- Assert.NotNull(agent);
- Assert.IsType(agent);
- }
-
- #endregion
-
- #region CreateAIAgentAsync - Description Tests
-
- ///
- /// Verify that CreateAIAgentAsync with description sets description on the agent.
- ///
- [Fact]
- public async Task CreateAIAgentAsync_WithDescription_SetsDescriptionAsync()
- {
- // Arrange
- using var testClient = CreateTestAgentClientWithHandler(description: "Test description");
- var options = new ChatClientAgentOptions
- {
- Name = "test-agent",
- Description = "Test description",
- ChatOptions = new ChatOptions { Instructions = "Test" }
- };
-
- // Act
- FoundryAgent agent = await testClient.Client.CreateAIAgentAsync("test-model", options);
-
- // Assert
- Assert.NotNull(agent);
- Assert.Equal("Test description", agent.Description);
- }
-
- ///
- /// Verify that CreateAIAgentAsync without description still creates agent successfully.
- ///
- [Fact]
- public async Task CreateAIAgentAsync_WithoutDescription_CreatesAgentSuccessfullyAsync()
- {
- // Arrange
- using var testClient = CreateTestAgentClientWithHandler();
- var options = new ChatClientAgentOptions
- {
- Name = "test-agent",
- ChatOptions = new ChatOptions { Instructions = "Test" }
- };
-
- // Act
- FoundryAgent agent = await testClient.Client.CreateAIAgentAsync("test-model", options);
-
- // Assert
- Assert.NotNull(agent);
- }
-
- #endregion
-
- #region CreateChatClientAgentOptions - Missing Tools Tests
-
- ///
- /// Verify that when invocable tools are required but not provided, an exception is thrown.
- ///
- [Fact]
- public async Task GetAIAgentAsync_WithToolsRequiredButNotProvided_ThrowsArgumentExceptionAsync()
- {
- // Arrange
- PromptAgentDefinition definition = new("test-model") { Instructions = "Test" };
- definition.Tools.Add(ResponseTool.CreateFunctionTool("required_function", BinaryData.FromString("{}"), strictModeEnabled: false));
-
- AIProjectClient client = this.CreateTestAgentClient(agentDefinitionResponse: definition);
- var options = new ChatClientAgentOptions
- {
- Name = "test-agent",
- ChatOptions = new ChatOptions { Instructions = "Test" }
- };
-
- // Act & Assert
- ArgumentException exception = await Assert.ThrowsAsync(() =>
- client.GetAIAgentAsync(options));
-
- Assert.Contains("in-process tools must be provided", exception.Message);
- }
-
- ///
- /// Verify that when specific invocable tools are required but wrong ones are provided, InvalidOperationException is thrown.
- ///
- [Fact]
- public async Task GetAIAgentAsync_WithWrongToolsProvided_ThrowsInvalidOperationExceptionAsync()
- {
- // Arrange
- PromptAgentDefinition definition = new("test-model") { Instructions = "Test" };
- definition.Tools.Add(ResponseTool.CreateFunctionTool("required_function", BinaryData.FromString("{}"), strictModeEnabled: false));
-
- AIProjectClient client = this.CreateTestAgentClient(agentDefinitionResponse: definition);
- var tools = new List
- {
- AIFunctionFactory.Create(() => "test", "wrong_function", "Wrong function")
- };
-
- var options = new ChatClientAgentOptions
- {
- Name = "test-agent",
- ChatOptions = new ChatOptions
- {
- Instructions = "Test",
- Tools = tools
- }
- };
-
- // Act & Assert
- InvalidOperationException exception = await Assert.ThrowsAsync(() =>
- client.GetAIAgentAsync(options));
-
- Assert.Contains("required_function", exception.Message);
- Assert.Contains("were not provided", exception.Message);
- }
-
- ///
- /// Verify that when tools are provided that match the definition, agent is created successfully.
- ///
- [Fact]
- public async Task GetAIAgentAsync_WithMatchingToolsProvided_CreatesAgentSuccessfullyAsync()
- {
- // Arrange
- PromptAgentDefinition definition = new("test-model") { Instructions = "Test" };
- definition.Tools.Add(ResponseTool.CreateFunctionTool("required_function", BinaryData.FromString("{}"), strictModeEnabled: false));
-
- AIProjectClient client = this.CreateTestAgentClient(agentDefinitionResponse: definition);
- var tools = new List
- {
- AIFunctionFactory.Create(() => "test", "required_function", "Required function")
- };
-
- var options = new ChatClientAgentOptions
- {
- Name = "test-agent",
- ChatOptions = new ChatOptions
- {
- Instructions = "Test",
- Tools = tools
- }
- };
-
- // Act
- FoundryAgent agent = await client.GetAIAgentAsync(options);
-
- // Assert
- Assert.NotNull(agent);
- Assert.IsType(agent);
- }
-
- #endregion
-
- #region CreateChatClientAgentOptions - Options Preservation Tests
-
- ///
- /// Verify that CreateChatClientAgentOptions preserves AIContextProviders.
- ///
- [Fact]
- public async Task GetAIAgentAsync_WithAIContextProviders_PreservesProviderAsync()
- {
- // Arrange
- AIProjectClient client = this.CreateTestAgentClient();
- var options = new ChatClientAgentOptions
- {
- Name = "test-agent",
- ChatOptions = new ChatOptions { Instructions = "Test" },
- AIContextProviders = [new TestAIContextProvider()]
- };
-
- // Act
- FoundryAgent agent = await client.GetAIAgentAsync(options);
-
- // Assert
- Assert.NotNull(agent);
- }
-
- ///
- /// Verify that CreateChatClientAgentOptions preserves ChatHistoryProvider.
- ///
- [Fact]
- public async Task GetAIAgentAsync_WithChatHistoryProvider_PreservesProviderAsync()
- {
- // Arrange
- AIProjectClient client = this.CreateTestAgentClient();
- var options = new ChatClientAgentOptions
- {
- Name = "test-agent",
- ChatOptions = new ChatOptions { Instructions = "Test" },
- ChatHistoryProvider = new TestChatHistoryProvider()
- };
-
- // Act
- FoundryAgent agent = await client.GetAIAgentAsync(options);
-
- // Assert
- Assert.NotNull(agent);
- }
-
- ///
- /// Verify that CreateChatClientAgentOptions preserves UseProvidedChatClientAsIs.
- ///
- [Fact]
- public async Task GetAIAgentAsync_WithUseProvidedChatClientAsIs_PreservesSettingAsync()
- {
- // Arrange
- AIProjectClient client = this.CreateTestAgentClient();
- var options = new ChatClientAgentOptions
- {
- Name = "test-agent",
- ChatOptions = new ChatOptions { Instructions = "Test" },
- UseProvidedChatClientAsIs = true
- };
-
- // Act
- FoundryAgent agent = await client.GetAIAgentAsync(options);
-
- // Assert
- Assert.NotNull(agent);
- }
-
- ///
- /// Verify that GetAIAgentAsync with UseProvidedChatClientAsIs=true skips tool validation
- /// and does not throw even when server-side function tools exist without matching invocable tools.
- ///
- [Fact]
- public async Task GetAIAgentAsync_WithUseProvidedChatClientAsIs_SkipsToolValidationAsync()
- {
- // Arrange
- PromptAgentDefinition definition = new("test-model") { Instructions = "Test" };
- definition.Tools.Add(ResponseTool.CreateFunctionTool("required_function", BinaryData.FromString("{}"), strictModeEnabled: false));
-
- AIProjectClient client = this.CreateTestAgentClient(agentDefinitionResponse: definition);
- var options = new ChatClientAgentOptions
- {
- Name = "test-agent",
- ChatOptions = new ChatOptions { Instructions = "Test" },
- UseProvidedChatClientAsIs = true
- };
-
- // Act - should not throw even without tools when UseProvidedChatClientAsIs is true
- FoundryAgent agent = await client.GetAIAgentAsync(options);
-
- // Assert
- Assert.NotNull(agent);
- }
-
- ///
- /// Verify that GetAIAgentAsync with UseProvidedChatClientAsIs=true still matches provided AIFunction tools
- /// to server-side function definitions, instead of falling back to the ResponseToolAITool wrapper.
- ///
- [Fact]
- public async Task GetAIAgentAsync_WithUseProvidedChatClientAsIs_PreservesProvidedToolsAsync()
- {
- // Arrange
- PromptAgentDefinition definition = new("test-model") { Instructions = "Test" };
- definition.Tools.Add(ResponseTool.CreateFunctionTool("my_function", BinaryData.FromString("{}"), strictModeEnabled: false));
-
- AIProjectClient client = this.CreateTestAgentClient(agentDefinitionResponse: definition);
-
- var providedTool = AIFunctionFactory.Create(() => "test", "my_function", "A test function");
- var options = new ChatClientAgentOptions
- {
- Name = "test-agent",
- UseProvidedChatClientAsIs = true,
- ChatOptions = new ChatOptions
- {
- Instructions = "Test",
- Tools = [providedTool]
- },
- };
-
- // Act - UseProvidedChatClientAsIs is true, but provided AIFunctions should still be matched and preserved
- FoundryAgent agent = await client.GetAIAgentAsync(options);
-
- // Assert
- Assert.NotNull(agent);
-
- // Verify the provided AIFunction was matched and preserved in ChatOptions.Tools (not replaced by AsAITool wrapper)
- var chatOptions = agent.GetService();
- Assert.NotNull(chatOptions);
- Assert.NotNull(chatOptions!.Tools);
- Assert.Contains(chatOptions.Tools, t => t is AIFunction af && af.Name == "my_function");
- }
-
- #endregion
-
- #region Empty Version and ID Handling Tests
-
- ///
- /// Verify that GetAIAgentAsync handles an agent with empty version by using "latest" as fallback.
- ///
- [Fact]
- public async Task GetAIAgentAsync_WithEmptyVersion_CreatesAgentSuccessfullyAsync()
- {
- // Arrange
- AIProjectClient client = this.CreateTestAgentClientWithEmptyVersion();
- var options = new ChatClientAgentOptions
- {
- Name = "test-agent",
- ChatOptions = new ChatOptions { Instructions = "Test" }
- };
-
- // Act
- FoundryAgent agent = await client.GetAIAgentAsync(options);
-
- // Assert
- Assert.NotNull(agent);
- Assert.IsType(agent);
- // Verify the agent ID is generated from server-returned name ("agent_abc123") and "latest"
- Assert.Equal("agent_abc123:latest", agent.Id);
- }
-
- ///
- /// Verify that AsAIAgent with AgentRecord handles empty version by using "latest" as fallback.
- ///
- [Fact]
- public void AsAIAgent_WithAgentRecordEmptyVersion_CreatesAgentWithGeneratedId()
- {
- // Arrange
- AIProjectClient client = this.CreateTestAgentClientWithEmptyVersion();
- AgentRecord agentRecord = this.CreateTestAgentRecordWithEmptyVersion();
-
- // Act
- var agent = client.AsAIAgent(agentRecord);
-
- // Assert
- Assert.NotNull(agent);
- // Verify the agent ID is generated from agent record name ("agent_abc123") and "latest"
- Assert.Equal("agent_abc123:latest", agent.Id);
- }
-
- ///
- /// Verify that AsAIAgent with AgentVersion handles empty version by using "latest" as fallback.
- ///
- [Fact]
- public void AsAIAgent_WithAgentVersionEmptyVersion_CreatesAgentWithGeneratedId()
- {
- // Arrange
- AIProjectClient client = this.CreateTestAgentClientWithEmptyVersion();
- AgentVersion agentVersion = this.CreateTestAgentVersionWithEmptyVersion();
-
- // Act
- var agent = client.AsAIAgent(agentVersion);
-
- // Assert
- Assert.NotNull(agent);
- // Verify the agent ID is generated from agent version name ("agent_abc123") and "latest"
- Assert.Equal("agent_abc123:latest", agent.Id);
- }
-
- ///
- /// Verify that GetAIAgentAsync handles an agent with whitespace-only version by using "latest" as fallback.
- ///
- [Fact]
- public async Task GetAIAgentAsync_WithWhitespaceVersion_CreatesAgentSuccessfullyAsync()
- {
- // Arrange
- AIProjectClient client = this.CreateTestAgentClientWithWhitespaceVersion();
- var options = new ChatClientAgentOptions
- {
- Name = "test-agent",
- ChatOptions = new ChatOptions { Instructions = "Test" }
- };
-
- // Act
- FoundryAgent agent = await client.GetAIAgentAsync(options);
-
- // Assert
- Assert.NotNull(agent);
- Assert.IsType(agent);
- // Verify the agent ID is generated from server-returned name ("agent_abc123") and "latest"
- Assert.Equal("agent_abc123:latest", agent.Id);
- }
-
- ///
- /// Verify that AsAIAgent with AgentRecord handles whitespace-only version by using "latest" as fallback.
- ///
- [Fact]
- public void AsAIAgent_WithAgentRecordWhitespaceVersion_CreatesAgentWithGeneratedId()
- {
- // Arrange
- AIProjectClient client = this.CreateTestAgentClientWithWhitespaceVersion();
- AgentRecord agentRecord = this.CreateTestAgentRecordWithWhitespaceVersion();
-
- // Act
- var agent = client.AsAIAgent(agentRecord);
-
- // Assert
- Assert.NotNull(agent);
- // Verify the agent ID is generated from agent record name ("agent_abc123") and "latest"
- Assert.Equal("agent_abc123:latest", agent.Id);
- }
-
- ///
- /// Verify that AsAIAgent with AgentVersion handles whitespace-only version by using "latest" as fallback.
- ///
- [Fact]
- public void AsAIAgent_WithAgentVersionWhitespaceVersion_CreatesAgentWithGeneratedId()
- {
- // Arrange
- AIProjectClient client = this.CreateTestAgentClientWithWhitespaceVersion();
- AgentVersion agentVersion = this.CreateTestAgentVersionWithWhitespaceVersion();
-
- // Act
- var agent = client.AsAIAgent(agentVersion);
-
- // Assert
- Assert.NotNull(agent);
- // Verify the agent ID is generated from agent version name ("agent_abc123") and "latest"
- Assert.Equal("agent_abc123:latest", agent.Id);
- }
-
- #endregion
-
- #region ApplyToolsToAgentDefinition Tests
-
- ///
- /// Verify that CreateAIAgentAsync with non-PromptAgentDefinition and tools throws ArgumentException.
- ///
- [Fact]
- public async Task CreateAIAgentAsync_WithNonPromptAgentDefinitionAndTools_ThrowsArgumentExceptionAsync()
- {
- // Arrange
- var tools = new List
- {
- AIFunctionFactory.Create(() => "test", "test_function", "A test function")
- };
-
- using HttpHandlerAssert httpHandler = new(_ => new HttpResponseMessage(HttpStatusCode.OK)
- {
- Content = new StringContent(TestDataUtil.GetAgentVersionResponseJson(), Encoding.UTF8, "application/json")
- });
-
-#pragma warning disable CA5399
- using HttpClient httpClient = new(httpHandler);
-#pragma warning restore CA5399
-
- AIProjectClient client = new(new Uri("https://test.openai.azure.com/"), new FakeAuthenticationTokenProvider(), new() { Transport = new HttpClientPipelineTransport(httpClient) });
-
- // Create a mock AgentDefinition that is not PromptAgentDefinition
- // Since we can't easily create a non-PromptAgentDefinition in the public API, we test this path via the CreateAIAgentAsync that builds a PromptAgentDefinition
- // The ApplyToolsToAgentDefinition is only called when tools.Count > 0, and we provide tools
- // But PromptAgentDefinition is always created by CreateAIAgentAsync(name, model, instructions, tools)
- // So this path is hard to hit without mocking. Let's test the declarative function rejection instead.
- var declarativeFunction = AIFunctionFactory.CreateDeclaration("test_function", "A test function", JsonDocument.Parse("{}").RootElement);
-
- // Act & Assert
- InvalidOperationException exception = await Assert.ThrowsAsync(() =>
- client.CreateAIAgentAsync(
- name: "test-agent",
- model: "test-model",
- instructions: "Test",
- tools: [declarativeFunction]));
-
- Assert.Contains("invokable AIFunctions", exception.Message);
- }
-
- ///
- /// Verify that CreateAIAgentAsync with AIFunctionDeclaration tools throws InvalidOperationException.
- ///
- [Fact]
- public async Task CreateAIAgentAsync_WithAIFunctionDeclarationTool_ThrowsInvalidOperationExceptionAsync()
- {
- // Arrange
- using var doc = JsonDocument.Parse("{}");
- var declarativeFunction = AIFunctionFactory.CreateDeclaration("test_function", "A test function", doc.RootElement);
-
- using HttpHandlerAssert httpHandler = new(_ => new HttpResponseMessage(HttpStatusCode.OK)
- {
- Content = new StringContent(TestDataUtil.GetAgentVersionResponseJson(), Encoding.UTF8, "application/json")
- });
-
-#pragma warning disable CA5399
- using HttpClient httpClient = new(httpHandler);
-#pragma warning restore CA5399
-
- AIProjectClient client = new(new Uri("https://test.openai.azure.com/"), new FakeAuthenticationTokenProvider(), new() { Transport = new HttpClientPipelineTransport(httpClient) });
-
- // Act & Assert
- InvalidOperationException exception = await Assert.ThrowsAsync(() =>
- client.CreateAIAgentAsync(
- name: "test-agent",
- model: "test-model",
- instructions: "Test",
- tools: [declarativeFunction]));
-
- Assert.Contains("invokable AIFunctions", exception.Message);
- }
-
- ///
- /// Verify that CreateAIAgentAsync with ResponseTool converted via AsAITool works.
- ///
- [Fact]
- public async Task CreateAIAgentAsync_WithResponseToolAsAITool_CreatesAgentSuccessfullyAsync()
- {
- // Arrange
- ResponseTool responseTool = ResponseTool.CreateFunctionTool("response_tool", BinaryData.FromString("{}"), strictModeEnabled: false);
- AITool convertedTool = responseTool.AsAITool();
-
- // Create a definition with the function tool already in it
- PromptAgentDefinition definition = new("test-model") { Instructions = "Test" };
- definition.Tools.Add(responseTool);
-
- AIProjectClient client = this.CreateTestAgentClient(agentDefinitionResponse: definition);
-
- // Matching invokable tool must be provided
- var invokableTool = AIFunctionFactory.Create(() => "test", "response_tool", "Invokable version of the tool");
-
- var options = new ChatClientAgentOptions
- {
- Name = "test-agent",
- ChatOptions = new ChatOptions
- {
- Instructions = "Test",
- Tools = [invokableTool]
- }
- };
-
- // Act
- FoundryAgent agent = await client.GetAIAgentAsync(options);
-
- // Assert
- Assert.NotNull(agent);
- Assert.IsType(agent);
- }
-
- ///
- /// Verify that CreateAIAgentAsync with hosted tool types works correctly.
- ///
- [Fact]
- public async Task CreateAIAgentAsync_WithHostedToolTypes_CreatesAgentSuccessfullyAsync()
- {
- // Arrange
- using var testClient = CreateTestAgentClientWithHandler();
- var webSearchTool = new HostedWebSearchTool();
-
- var options = new ChatClientAgentOptions
- {
- Name = "test-agent",
- ChatOptions = new ChatOptions
- {
- Instructions = "Test",
- Tools = [webSearchTool]
- }
- };
-
- // Act
- FoundryAgent agent = await testClient.Client.CreateAIAgentAsync("test-model", options);
-
- // Assert
- Assert.NotNull(agent);
- Assert.IsType(agent);
- }
-
- ///
- /// Verify that when the server returns tools but matching tools are provided, the agent is created.
- ///
- [Fact]
- public async Task GetAIAgentAsync_WithServerDefinedToolsAndMatchingProvidedTools_CreatesAgentAsync()
- {
- // Arrange
- PromptAgentDefinition definition = new("test-model") { Instructions = "Test" };
- // Add multiple function tools
- definition.Tools.Add(ResponseTool.CreateFunctionTool("tool_one", BinaryData.FromString("{}"), strictModeEnabled: false));
- definition.Tools.Add(ResponseTool.CreateFunctionTool("tool_two", BinaryData.FromString("{}"), strictModeEnabled: false));
-
- AIProjectClient client = this.CreateTestAgentClient(agentDefinitionResponse: definition);
-
- var tools = new List
- {
- AIFunctionFactory.Create(() => "one", "tool_one", "Tool one"),
- AIFunctionFactory.Create(() => "two", "tool_two", "Tool two")
- };
-
- var options = new ChatClientAgentOptions
- {
- Name = "test-agent",
- ChatOptions = new ChatOptions
- {
- Instructions = "Test",
- Tools = tools
- }
- };
-
- // Act
- FoundryAgent agent = await client.GetAIAgentAsync(options);
-
- // Assert
- Assert.NotNull(agent);
- Assert.IsType(agent);
- }
-
- ///
- /// Verify that when the server returns mixed tools (function and hosted), the agent handles them correctly.
- ///
- [Fact]
- public async Task GetAIAgentAsync_WithMixedServerTools_MatchesFunctionToolsOnlyAsync()
- {
- // Arrange
- PromptAgentDefinition definition = new("test-model") { Instructions = "Test" };
- // Add a function tool
- definition.Tools.Add(ResponseTool.CreateFunctionTool("function_tool", BinaryData.FromString("{}"), strictModeEnabled: false));
- // Add a hosted tool
- definition.Tools.Add(new HostedWebSearchTool().GetService() ?? new HostedWebSearchTool().AsOpenAIResponseTool());
-
- AIProjectClient client = this.CreateTestAgentClient(agentDefinitionResponse: definition);
-
- var tools = new List
- {
- AIFunctionFactory.Create(() => "result", "function_tool", "The function tool")
- };
-
- var options = new ChatClientAgentOptions
- {
- Name = "test-agent",
- ChatOptions = new ChatOptions
- {
- Instructions = "Test",
- Tools = tools
- }
- };
-
- // Act
- FoundryAgent agent = await client.GetAIAgentAsync(options);
-
- // Assert
- Assert.NotNull(agent);
- Assert.IsType(agent);
- }
-
- ///
- /// Verify that when partial tools are provided (some missing), InvalidOperationException is thrown listing missing tools.
- ///
- [Fact]
- public async Task GetAIAgentAsync_WithPartialToolsProvided_ThrowsInvalidOperationWithMissingToolNamesAsync()
- {
- // Arrange
- PromptAgentDefinition definition = new("test-model") { Instructions = "Test" };
- definition.Tools.Add(ResponseTool.CreateFunctionTool("provided_tool", BinaryData.FromString("{}"), strictModeEnabled: false));
- definition.Tools.Add(ResponseTool.CreateFunctionTool("missing_tool", BinaryData.FromString("{}"), strictModeEnabled: false));
-
- AIProjectClient client = this.CreateTestAgentClient(agentDefinitionResponse: definition);
-
- var tools = new List
- {
- // Only providing one of two required tools
- AIFunctionFactory.Create(() => "result", "provided_tool", "The provided tool")
- };
-
- var options = new ChatClientAgentOptions
- {
- Name = "test-agent",
- ChatOptions = new ChatOptions
- {
- Instructions = "Test",
- Tools = tools
- }
- };
-
- // Act & Assert
- InvalidOperationException exception = await Assert.ThrowsAsync(() =>
- client.GetAIAgentAsync(options));
-
- Assert.Contains("missing_tool", exception.Message);
- Assert.DoesNotContain("provided_tool", exception.Message);
- }
-
- ///
- /// Verify that when AsAIAgent is called without requireInvocableTools, hosted tools are correctly added.
- ///
- [Fact]
- public void AsAIAgent_WithServerHostedTools_AddsToolsToAgentOptions()
- {
- // Arrange
- PromptAgentDefinition definition = new("test-model") { Instructions = "Test" };
- definition.Tools.Add(new HostedWebSearchTool().GetService() ?? new HostedWebSearchTool().AsOpenAIResponseTool());
-
- AIProjectClient client = this.CreateTestAgentClient();
- AgentVersion agentVersion = ModelReaderWriter.Read(BinaryData.FromString(TestDataUtil.GetAgentVersionResponseJson(agentDefinition: definition)))!;
-
- // Act - no tools provided, but requireInvocableTools is false when no tools param is passed
- FoundryAgent agent = client.AsAIAgent(agentVersion);
-
- // Assert
- Assert.NotNull(agent);
- Assert.IsType(agent);
- }
-
- #endregion
-
- #region Helper Methods
-
- ///
- /// Creates a test AIProjectClient with fake behavior.
- ///
- private FakeAgentClient CreateTestAgentClient(string? agentName = null, string? instructions = null, string? description = null, AgentDefinition? agentDefinitionResponse = null)
- {
- return new FakeAgentClient(agentName, instructions, description, agentDefinitionResponse);
- }
-
- ///
- /// Creates a test AIProjectClient backed by an HTTP handler that returns canned responses.
- /// Used for tests that exercise the protocol-method code path (CreateAgentVersion).
- /// The returned client must be disposed to clean up the underlying HttpClient/handler.
- ///
- private static DisposableTestClient CreateTestAgentClientWithHandler(string? agentName = null, string? instructions = null, string? description = null, AgentDefinition? agentDefinitionResponse = null)
- {
- var responseJson = TestDataUtil.GetAgentVersionResponseJson(agentName, agentDefinitionResponse, instructions, description);
-
- var httpHandler = new HttpHandlerAssert(_ =>
- new HttpResponseMessage(HttpStatusCode.OK) { Content = new StringContent(responseJson, Encoding.UTF8, "application/json") });
-
-#pragma warning disable CA5399
- var httpClient = new HttpClient(httpHandler);
-#pragma warning restore CA5399
-
- var client = new AIProjectClient(
- new Uri("https://test.openai.azure.com/"),
- new FakeAuthenticationTokenProvider(),
- new() { Transport = new HttpClientPipelineTransport(httpClient) });
-
- return new DisposableTestClient(client, httpClient, httpHandler);
- }
-
- ///
- /// Wraps an AIProjectClient and its disposable dependencies for deterministic cleanup.
- ///
- private sealed class DisposableTestClient : IDisposable
- {
- private readonly HttpClient _httpClient;
- private readonly HttpHandlerAssert _httpHandler;
-
- public DisposableTestClient(AIProjectClient client, HttpClient httpClient, HttpHandlerAssert httpHandler)
- {
- this.Client = client;
- this._httpClient = httpClient;
- this._httpHandler = httpHandler;
- }
-
- public AIProjectClient Client { get; }
-
- public void Dispose()
- {
- this._httpClient.Dispose();
- this._httpHandler.Dispose();
- }
- }
-
- ///
- /// Creates a test AgentRecord for testing.
- ///
- private AgentRecord CreateTestAgentRecord(AgentDefinition? agentDefinition = null)
- {
- return ModelReaderWriter.Read(BinaryData.FromString(TestDataUtil.GetAgentResponseJson(agentDefinition: agentDefinition)))!;
- }
-
- ///
- /// Creates a test AIProjectClient with empty version fields for testing hosted MCP agents.
- ///
- private FakeAgentClient CreateTestAgentClientWithEmptyVersion(string? agentName = null, string? instructions = null, string? description = null, AgentDefinition? agentDefinitionResponse = null)
- {
- return new FakeAgentClient(agentName, instructions, description, agentDefinitionResponse, useEmptyVersion: true);
- }
-
- ///
- /// Creates a test AgentRecord with empty version for testing hosted MCP agents.
- ///
- private AgentRecord CreateTestAgentRecordWithEmptyVersion(AgentDefinition? agentDefinition = null)
- {
- return ModelReaderWriter.Read(BinaryData.FromString(TestDataUtil.GetAgentResponseJsonWithEmptyVersion(agentDefinition: agentDefinition)))!;
- }
-
- ///
- /// Creates a test AgentVersion with empty version for testing hosted MCP agents.
- ///
- private AgentVersion CreateTestAgentVersionWithEmptyVersion()
- {
- return ModelReaderWriter.Read(BinaryData.FromString(TestDataUtil.GetAgentVersionResponseJsonWithEmptyVersion()))!;
- }
-
- ///
- /// Creates a test AIProjectClient with whitespace-only version fields for testing hosted MCP agents.
- ///
- 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);
- }
-
- ///
- /// Creates a test AgentRecord with whitespace-only version for testing hosted MCP agents.
- ///
- private AgentRecord CreateTestAgentRecordWithWhitespaceVersion(AgentDefinition? agentDefinition = null)
- {
- return ModelReaderWriter.Read(BinaryData.FromString(TestDataUtil.GetAgentResponseJsonWithWhitespaceVersion(agentDefinition: agentDefinition)))!;
- }
-
- ///
- /// Creates a test AgentVersion with whitespace-only version for testing hosted MCP agents.
- ///
- private AgentVersion CreateTestAgentVersionWithWhitespaceVersion()
- {
- return ModelReaderWriter.Read(BinaryData.FromString(TestDataUtil.GetAgentVersionResponseJsonWithWhitespaceVersion()))!;
- }
-
- private const string OpenAPISpec = """
- {
- "openapi": "3.0.3",
- "info": { "title": "Tiny Test API", "version": "1.0.0" },
- "paths": {
- "/ping": {
- "get": {
- "summary": "Health check",
- "operationId": "getPing",
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "properties": { "message": { "type": "string" } },
- "required": ["message"]
- },
- "example": { "message": "pong" }
- }
- }
- }
- }
- }
- }
- }
- }
- """;
-
- ///
- /// Creates a test AgentVersion for testing.
- ///
- private AgentVersion CreateTestAgentVersion()
- {
- return ModelReaderWriter.Read(BinaryData.FromString(TestDataUtil.GetAgentVersionResponseJson()))!;
- }
-
- ///
- /// Specifies the version mode for test data generation.
- ///
- private enum VersionMode
- {
- Normal,
- Empty,
- Whitespace
- }
-
- ///
- /// Fake AIProjectClient for testing.
- ///
- private sealed class FakeAgentClient : AIProjectClient
- {
- 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.Agents = new FakeAgentsClient(agentName, instructions, description, agentDefinitionResponse, effectiveVersionMode);
- }
-
- public override ClientConnection GetConnection(string connectionId)
- {
- return new ClientConnection("fake-connection-id", "http://localhost", ClientPipeline.Create(), CredentialKind.None);
- }
-
- public override AgentsClient Agents { get; }
-
- private sealed class FakeAgentsClient : AgentsClient
- {
- private readonly string? _agentName;
- private readonly string? _instructions;
- private readonly string? _description;
- private readonly AgentDefinition? _agentDefinition;
- private readonly VersionMode _versionMode;
-
- public FakeAgentsClient(string? agentName = null, string? instructions = null, string? description = null, AgentDefinition? agentDefinitionResponse = null, VersionMode versionMode = VersionMode.Normal)
- {
- this._agentName = agentName;
- this._instructions = instructions;
- this._description = description;
- this._agentDefinition = agentDefinitionResponse;
- this._versionMode = versionMode;
- }
-
- private string GetAgentResponseJson()
- {
- return this._versionMode switch
- {
- VersionMode.Empty => TestDataUtil.GetAgentResponseJsonWithEmptyVersion(this._agentName, this._agentDefinition, this._instructions, this._description),
- VersionMode.Whitespace => TestDataUtil.GetAgentResponseJsonWithWhitespaceVersion(this._agentName, this._agentDefinition, this._instructions, this._description),
- _ => TestDataUtil.GetAgentResponseJson(this._agentName, this._agentDefinition, this._instructions, this._description)
- };
- }
-
- private string GetAgentVersionResponseJson()
- {
- return this._versionMode switch
- {
- VersionMode.Empty => TestDataUtil.GetAgentVersionResponseJsonWithEmptyVersion(this._agentName, this._agentDefinition, this._instructions, this._description),
- VersionMode.Whitespace => TestDataUtil.GetAgentVersionResponseJsonWithWhitespaceVersion(this._agentName, this._agentDefinition, this._instructions, this._description),
- _ => TestDataUtil.GetAgentVersionResponseJson(this._agentName, this._agentDefinition, this._instructions, this._description)
- };
- }
-
- public override ClientResult GetAgent(string agentName, RequestOptions options)
- {
- var responseJson = this.GetAgentResponseJson();
- return ClientResult.FromValue(ModelReaderWriter.Read(BinaryData.FromString(responseJson))!, new MockPipelineResponse(200, BinaryData.FromString(responseJson)));
- }
-
- public override ClientResult GetAgent(string agentName, CancellationToken cancellationToken = default)
- {
- var responseJson = this.GetAgentResponseJson();
- return ClientResult.FromValue(ModelReaderWriter.Read(BinaryData.FromString(responseJson))!, new MockPipelineResponse(200));
- }
-
- public override Task GetAgentAsync(string agentName, RequestOptions options)
- {
- var responseJson = this.GetAgentResponseJson();
- return Task.FromResult(ClientResult.FromValue(ModelReaderWriter.Read(BinaryData.FromString(responseJson))!, new MockPipelineResponse(200, BinaryData.FromString(responseJson))));
- }
-
- public override Task> GetAgentAsync(string agentName, CancellationToken cancellationToken = default)
- {
- var responseJson = this.GetAgentResponseJson();
- return Task.FromResult(ClientResult.FromValue(ModelReaderWriter.Read(BinaryData.FromString(responseJson))!, new MockPipelineResponse(200)));
- }
-
- public override ClientResult CreateAgentVersion(string agentName, AgentVersionCreationOptions? options = null, string? foundryFeatures = null, CancellationToken cancellationToken = default)
- {
- var responseJson = this.GetAgentVersionResponseJson();
- return ClientResult.FromValue(ModelReaderWriter.Read(BinaryData.FromString(responseJson))!, new MockPipelineResponse(200));
- }
-
- public override Task> CreateAgentVersionAsync(string agentName, AgentVersionCreationOptions? options = null, string? foundryFeatures = null, CancellationToken cancellationToken = default)
- {
- var responseJson = this.GetAgentVersionResponseJson();
- return Task.FromResult(ClientResult.FromValue(ModelReaderWriter.Read(BinaryData.FromString(responseJson))!, new MockPipelineResponse(200)));
- }
- }
- }
-
- private static PromptAgentDefinition GeneratePromptDefinitionResponse(PromptAgentDefinition inputDefinition, List? tools)
- {
- var definitionResponse = new PromptAgentDefinition(inputDefinition.Model) { Instructions = inputDefinition.Instructions };
- if (tools is not null)
- {
- foreach (var tool in tools)
- {
- definitionResponse.Tools.Add(tool.GetService() ?? tool.AsOpenAIResponseTool());
- }
- }
-
- return definitionResponse;
- }
-
- ///
- /// Test custom chat client that can be used to verify clientFactory functionality.
- ///
- private sealed class TestChatClient : DelegatingChatClient
- {
- public TestChatClient(IChatClient innerClient) : base(innerClient)
- {
- }
- }
-
- ///
- /// Mock pipeline response for testing ClientResult wrapping.
- ///
- private sealed class MockPipelineResponse : PipelineResponse
- {
- private readonly MockPipelineResponseHeaders _headers;
-
- public MockPipelineResponse(int status, BinaryData? content = null)
- {
- this.Status = status;
- this.Content = content ?? BinaryData.Empty;
- this._headers = new MockPipelineResponseHeaders();
- }
-
- public override int Status { get; }
-
- public override string ReasonPhrase => "OK";
-
- public override Stream? ContentStream
- {
- get => null;
- set { }
- }
-
- public override BinaryData Content { get; }
-
- protected override PipelineResponseHeaders HeadersCore => this._headers;
-
- public override BinaryData BufferContent(CancellationToken cancellationToken = default) =>
- throw new NotSupportedException("Buffering content is not supported for mock responses.");
-
- public override ValueTask BufferContentAsync(CancellationToken cancellationToken = default) =>
- throw new NotSupportedException("Buffering content asynchronously is not supported for mock responses.");
-
- public override void Dispose()
- {
- }
-
- private sealed class MockPipelineResponseHeaders : PipelineResponseHeaders
- {
- private readonly Dictionary _headers = new(StringComparer.OrdinalIgnoreCase)
- {
- { "Content-Type", "application/json" },
- { "x-ms-request-id", "test-request-id" }
- };
-
- public override bool TryGetValue(string name, out string? value)
- {
- return this._headers.TryGetValue(name, out value);
- }
-
- public override bool TryGetValues(string name, out IEnumerable? values)
- {
- if (this._headers.TryGetValue(name, out var value))
- {
- values = [value];
- return true;
- }
-
- values = null;
- return false;
- }
-
- public override IEnumerator> GetEnumerator()
- {
- return this._headers.GetEnumerator();
- }
- }
- }
-
- #endregion
-
- ///
- /// Helper method to access internal ChatOptions property via reflection.
- ///
- private static ChatOptions? GetAgentChatOptions(AIAgent agent)
- {
- ChatClientAgent? chatClientAgent = agent as ChatClientAgent ?? agent.GetService();
- if (chatClientAgent is null)
- {
- return null;
- }
-
- var chatOptionsProperty = typeof(ChatClientAgent).GetProperty(
- "ChatOptions",
- System.Reflection.BindingFlags.Public |
- System.Reflection.BindingFlags.NonPublic |
- System.Reflection.BindingFlags.Instance);
-
- return chatOptionsProperty?.GetValue(chatClientAgent) as ChatOptions;
- }
-
- ///
- /// Test schema for JSON response format tests.
- ///
-#pragma warning disable CA1812 // Avoid uninstantiated internal classes - used via reflection by AIJsonUtilities
- private sealed class TestSchema
- {
- public string? Name { get; set; }
- public int Value { get; set; }
- }
-#pragma warning restore CA1812
-
- ///
- /// Test AIContextProvider for options preservation tests.
- ///
- private sealed class TestAIContextProvider : AIContextProvider
- {
- protected override ValueTask InvokingCoreAsync(InvokingContext context, CancellationToken cancellationToken = default)
- {
- return new ValueTask(context.AIContext);
- }
- }
-
- ///
- /// Test ChatHistoryProvider for options preservation tests.
- ///
- private sealed class TestChatHistoryProvider : ChatHistoryProvider
- {
- protected override ValueTask> InvokingCoreAsync(InvokingContext context, CancellationToken cancellationToken = default)
- {
- return new ValueTask>(context.RequestMessages);
- }
-
- protected override ValueTask InvokedCoreAsync(InvokedContext context, CancellationToken cancellationToken = default)
- {
- return default;
- }
- }
-}
-#pragma warning restore CS0618
-
-///
-/// Provides test data for invalid agent name validation tests.
-///
-internal static class InvalidAgentNameTestData
-{
- ///
- /// Gets a collection of invalid agent names for theory-based testing.
- ///
- /// Collection of invalid agent name test cases.
- public static IEnumerable