From f74131ef61751f088d7e69f3f53ee5c13d177242 Mon Sep 17 00:00:00 2001
From: Mark Wallace <127216156+markwallace-microsoft@users.noreply.github.com>
Date: Wed, 12 Nov 2025 14:15:15 +0000
Subject: [PATCH] .NET: Add FoundryAgentFactory which uses AgentClient (#2123)
* Add FoundryAgentFactory which uses AgentClient
* Update dotnet/src/Microsoft.Agents.AI.Declarative.AzureAI/FoundryAgentFactory.cs
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* Update dotnet/src/Microsoft.Agents.AI.Declarative.AzureAI/FoundryAgentFactory.cs
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* Update dotnet/src/Microsoft.Agents.AI.Declarative.AzureAI/FoundryAgentFactory.cs
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* Update dotnet/src/Microsoft.Agents.AI.Declarative.AzureAI/FoundryAgentFactory.cs
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* Update dotnet/src/Microsoft.Agents.AI.Declarative.AzureAI/Extensions/FunctionToolExtensions.cs
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* Update dotnet/src/Microsoft.Agents.AI.Declarative.AzureAI/Extensions/FileSearchToolExtensions.cs
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* Update dotnet/src/Microsoft.Agents.AI.Declarative.AzureAI/Extensions/CodeInterpreterToolExtensions.cs
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* Update dotnet/src/Microsoft.Agents.AI.Declarative.AzureAI/Extensions/PromptAgentExtensions.cs
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* Code review feedback
* Add sample showing use of FoundryAgentFactory
* Add sample showing use of FoundryAgentFactory
* Update dotnet/src/Microsoft.Agents.AI.Declarative.AzureAI/Extensions/CodeInterpreterToolExtensions.cs
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* Update dotnet/src/Microsoft.Agents.AI.Declarative.AzureAI/Extensions/FunctionToolExtensions.cs
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* Update dotnet/src/Microsoft.Agents.AI.Declarative.AzureAI/Extensions/McpServerToolExtensions.cs
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* Update dotnet/src/Microsoft.Agents.AI.Declarative.AzureAI/Extensions/CodeInterpreterToolExtensions.cs
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* Update dotnet/src/Microsoft.Agents.AI.Declarative.AzureAI/Extensions/FileSearchToolExtensions.cs
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* Update dotnet/src/Microsoft.Agents.AI.Declarative.AzureAI/Extensions/WebSearchToolExtensions.cs
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* Update dotnet/src/Microsoft.Agents.AI.Declarative.AzureAI/Extensions/PromptAgentExtensions.cs
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* Fix issue to get FoundryAgentFactory sample working
* Run dotnet format
* Undo changes made by dotnet format
* Update dotnet/src/Microsoft.Agents.AI.Declarative.AzureAI/FoundryAgentFactory.cs
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
---
...PersistentAgent.yaml => FoundryAgent.yaml} | 0
dotnet/agent-framework-dotnet.slnx | 1 +
.../Agent_With_AzureAIAgent/Program.cs | 8 +-
.../DeclarativeAgents/Foundry/Program.cs | 19 +--
.../Foundry/Properties/launchSettings.json | 8 +-
.../DeclarativeFoundryPersistentAgents.csproj | 24 ++++
.../FoundryPersistent/Program.cs | 61 ++++++++++
.../Properties/launchSettings.json | 8 ++
.../CodeInterpreterToolExtensions.cs | 15 +++
.../Extensions/FileSearchToolExtensions.cs | 12 ++
.../Extensions/FunctionToolExtensions.cs | 22 ++++
.../Extensions/McpServerToolExtensions.cs | 23 +++-
.../Extensions/PromptAgentExtensions.cs | 38 +++++-
.../Extensions/WebSearchToolExtensions.cs | 12 ++
.../FoundryAgentFactory.cs | 111 ++++++++++++++++++
.../FoundryPersistentAgentFactory.cs | 8 +-
...osoft.Agents.AI.Declarative.AzureAI.csproj | 4 +-
.../AgentBotElementYamlTests.cs | 1 -
.../ChatClient/ChatClientAgentFactoryTests.cs | 8 +-
19 files changed, 343 insertions(+), 40 deletions(-)
rename agent-samples/foundry/{PersistentAgent.yaml => FoundryAgent.yaml} (100%)
create mode 100644 dotnet/samples/GettingStarted/DeclarativeAgents/FoundryPersistent/DeclarativeFoundryPersistentAgents.csproj
create mode 100644 dotnet/samples/GettingStarted/DeclarativeAgents/FoundryPersistent/Program.cs
create mode 100644 dotnet/samples/GettingStarted/DeclarativeAgents/FoundryPersistent/Properties/launchSettings.json
create mode 100644 dotnet/src/Microsoft.Agents.AI.Declarative.AzureAI/FoundryAgentFactory.cs
diff --git a/agent-samples/foundry/PersistentAgent.yaml b/agent-samples/foundry/FoundryAgent.yaml
similarity index 100%
rename from agent-samples/foundry/PersistentAgent.yaml
rename to agent-samples/foundry/FoundryAgent.yaml
diff --git a/dotnet/agent-framework-dotnet.slnx b/dotnet/agent-framework-dotnet.slnx
index 9835bfe830..884add6094 100644
--- a/dotnet/agent-framework-dotnet.slnx
+++ b/dotnet/agent-framework-dotnet.slnx
@@ -86,6 +86,7 @@
+
diff --git a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Program.cs b/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Program.cs
index 1c2628d8f9..a28355c520 100644
--- a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Program.cs
+++ b/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Program.cs
@@ -16,7 +16,7 @@ const string JokerName = "JokerAgent";
var agentsClient = new AgentClient(new Uri(endpoint), new AzureCliCredential());
// Define the agent you want to create. (Prompt Agent in this case)
-var agentVersionCreationOptions = new AgentVersionCreationOptions(new PromptAgentDefinition(model: deploymentName) { Instructions = JokerInstructions });
+var agentVersionCreationOptions = new AgentVersionCreationOptions(new PromptAgentDefinition(model: deploymentName) { Instructions = JokerInstructions, Temperature = 0.9f });
// Azure.AI.Agents SDK creates and manages agent by name and versions.
// You can create a server side agent version with the Azure.AI.Agents SDK client below.
var agentVersion = agentsClient.CreateAgentVersion(agentName: JokerName, options: agentVersionCreationOptions);
@@ -39,12 +39,14 @@ var latestVersion = jokerAgentLatest.GetService()!;
// The AIAgent version can be accessed via the GetService method.
Console.WriteLine($"Latest agent version id: {latestVersion.Id}");
+var options = new ChatClientAgentRunOptions();
+
// Once you have the AIAgent, you can invoke it like any other AIAgent.
AgentThread thread = jokerAgentLatest.GetNewThread();
-Console.WriteLine(await jokerAgentLatest.RunAsync("Tell me a joke about a pirate.", thread));
+Console.WriteLine(await jokerAgentLatest.RunAsync("Tell me a joke about a pirate.", thread, options));
// This will use the same thread to continue the conversation.
-Console.WriteLine(await jokerAgentLatest.RunAsync("Now tell me a joke about a cat and a dog using last joke as the anchor.", thread));
+Console.WriteLine(await jokerAgentLatest.RunAsync("Now tell me a joke about a cat and a dog using last joke as the anchor.", thread, options));
// Cleanup by agent name removes both agent versions created (jokerAgentV1 + jokerAgentV2).
agentsClient.DeleteAgent(jokerAgentV1.Name);
diff --git a/dotnet/samples/GettingStarted/DeclarativeAgents/Foundry/Program.cs b/dotnet/samples/GettingStarted/DeclarativeAgents/Foundry/Program.cs
index 3a65bdad4b..8e508e7a0f 100644
--- a/dotnet/samples/GettingStarted/DeclarativeAgents/Foundry/Program.cs
+++ b/dotnet/samples/GettingStarted/DeclarativeAgents/Foundry/Program.cs
@@ -2,10 +2,8 @@
// This sample shows how to load an AI agent from a YAML file and process a prompt using Foundry Agents as the backend.
-using System.ComponentModel;
using Azure.Identity;
using Microsoft.Agents.AI;
-using Microsoft.Extensions.AI;
using Microsoft.Extensions.Configuration;
var endpoint = Environment.GetEnvironmentVariable("AZURE_FOUNDRY_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_FOUNDRY_PROJECT_ENDPOINT is not set.");
@@ -40,22 +38,9 @@ IConfiguration configuration = new ConfigurationBuilder()
})
.Build();
-// Example function tool that can be used by the agent.
-[Description("Get the weather for a given location.")]
-static string GetWeather(
- [Description("The city and state, e.g. San Francisco, CA")] string location,
- [Description("The unit of temperature. Possible values are 'celsius' and 'fahrenheit'.")] string unit)
- => $"The weather in {location} is cloudy with a high of {(unit.Equals("celsius", StringComparison.Ordinal) ? "15°C" : "59°F")}.";
-
// Create the agent from the YAML definition.
-var agentFactory = new FoundryPersistentAgentFactory(new AzureCliCredential(), configuration);
+var agentFactory = new FoundryAgentFactory(new AzureCliCredential(), configuration);
var agent = await agentFactory.CreateFromYamlAsync(text);
-// Create agent run options
-var options = new ChatClientAgentRunOptions(new()
-{
- Tools = [AIFunctionFactory.Create(GetWeather, name: nameof(GetWeather))]
-});
-
// Invoke the agent and output the text result.
-Console.WriteLine(await agent!.RunAsync(prompt, options: options));
+Console.WriteLine(await agent!.RunAsync(prompt));
diff --git a/dotnet/samples/GettingStarted/DeclarativeAgents/Foundry/Properties/launchSettings.json b/dotnet/samples/GettingStarted/DeclarativeAgents/Foundry/Properties/launchSettings.json
index ee5dc4fb3d..519c5ef6cc 100644
--- a/dotnet/samples/GettingStarted/DeclarativeAgents/Foundry/Properties/launchSettings.json
+++ b/dotnet/samples/GettingStarted/DeclarativeAgents/Foundry/Properties/launchSettings.json
@@ -1,12 +1,8 @@
{
"profiles": {
- "PersistentAgent": {
+ "FoundryAgent": {
"commandName": "Project",
- "commandLineArgs": "..\\..\\..\\..\\..\\..\\..\\..\\agent-samples\\foundry\\PersistentAgent.yaml \"What is the weather in Cambridge, MA in °C?\""
- },
- "MicrosoftLearnAgent": {
- "commandName": "Project",
- "commandLineArgs": "..\\..\\..\\..\\..\\..\\..\\..\\agent-samples\\foundry\\MicrosoftLearnAgent.yaml \"Tell me a joke about a pirate in Italian.\""
+ "commandLineArgs": "..\\..\\..\\..\\..\\..\\..\\..\\agent-samples\\foundry\\FoundryAgent.yaml \"Tell me a recipe for chocolate chip cookies?\""
}
}
}
\ No newline at end of file
diff --git a/dotnet/samples/GettingStarted/DeclarativeAgents/FoundryPersistent/DeclarativeFoundryPersistentAgents.csproj b/dotnet/samples/GettingStarted/DeclarativeAgents/FoundryPersistent/DeclarativeFoundryPersistentAgents.csproj
new file mode 100644
index 0000000000..e607b92fb7
--- /dev/null
+++ b/dotnet/samples/GettingStarted/DeclarativeAgents/FoundryPersistent/DeclarativeFoundryPersistentAgents.csproj
@@ -0,0 +1,24 @@
+
+
+
+ Exe
+ net9.0
+
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dotnet/samples/GettingStarted/DeclarativeAgents/FoundryPersistent/Program.cs b/dotnet/samples/GettingStarted/DeclarativeAgents/FoundryPersistent/Program.cs
new file mode 100644
index 0000000000..3a65bdad4b
--- /dev/null
+++ b/dotnet/samples/GettingStarted/DeclarativeAgents/FoundryPersistent/Program.cs
@@ -0,0 +1,61 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+// This sample shows how to load an AI agent from a YAML file and process a prompt using Foundry Agents as the backend.
+
+using System.ComponentModel;
+using Azure.Identity;
+using Microsoft.Agents.AI;
+using Microsoft.Extensions.AI;
+using Microsoft.Extensions.Configuration;
+
+var endpoint = Environment.GetEnvironmentVariable("AZURE_FOUNDRY_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_FOUNDRY_PROJECT_ENDPOINT is not set.");
+
+// Read command-line arguments
+if (args.Length < 2)
+{
+ Console.WriteLine("Usage: DeclarativeAgents ");
+ Console.WriteLine(" : The path to the YAML file containing the agent definition");
+ Console.WriteLine(" : The prompt to send to the agent");
+ return;
+}
+
+var yamlFilePath = args[0];
+var prompt = args[1];
+
+// Verify the YAML file exists
+if (!File.Exists(yamlFilePath))
+{
+ Console.WriteLine($"Error: File not found: {yamlFilePath}");
+ return;
+}
+
+// Read the YAML content from the file
+var text = await File.ReadAllTextAsync(yamlFilePath);
+
+// Set up configuration with the Azure Foundry project endpoint
+IConfiguration configuration = new ConfigurationBuilder()
+ .AddInMemoryCollection(new Dictionary
+ {
+ ["AZURE_FOUNDRY_PROJECT_ENDPOINT"] = endpoint
+ })
+ .Build();
+
+// Example function tool that can be used by the agent.
+[Description("Get the weather for a given location.")]
+static string GetWeather(
+ [Description("The city and state, e.g. San Francisco, CA")] string location,
+ [Description("The unit of temperature. Possible values are 'celsius' and 'fahrenheit'.")] string unit)
+ => $"The weather in {location} is cloudy with a high of {(unit.Equals("celsius", StringComparison.Ordinal) ? "15°C" : "59°F")}.";
+
+// Create the agent from the YAML definition.
+var agentFactory = new FoundryPersistentAgentFactory(new AzureCliCredential(), configuration);
+var agent = await agentFactory.CreateFromYamlAsync(text);
+
+// Create agent run options
+var options = new ChatClientAgentRunOptions(new()
+{
+ Tools = [AIFunctionFactory.Create(GetWeather, name: nameof(GetWeather))]
+});
+
+// Invoke the agent and output the text result.
+Console.WriteLine(await agent!.RunAsync(prompt, options: options));
diff --git a/dotnet/samples/GettingStarted/DeclarativeAgents/FoundryPersistent/Properties/launchSettings.json b/dotnet/samples/GettingStarted/DeclarativeAgents/FoundryPersistent/Properties/launchSettings.json
new file mode 100644
index 0000000000..5180e58e7d
--- /dev/null
+++ b/dotnet/samples/GettingStarted/DeclarativeAgents/FoundryPersistent/Properties/launchSettings.json
@@ -0,0 +1,8 @@
+{
+ "profiles": {
+ "FoundryAgent": {
+ "commandName": "Project",
+ "commandLineArgs": "..\\..\\..\\..\\..\\..\\..\\..\\agent-samples\\foundry\\FoundryAgent.yaml \"What is the weather in Cambridge, MA in °C?\""
+ }
+ }
+}
\ No newline at end of file
diff --git a/dotnet/src/Microsoft.Agents.AI.Declarative.AzureAI/Extensions/CodeInterpreterToolExtensions.cs b/dotnet/src/Microsoft.Agents.AI.Declarative.AzureAI/Extensions/CodeInterpreterToolExtensions.cs
index 45308f6bae..e45fe4c5e5 100644
--- a/dotnet/src/Microsoft.Agents.AI.Declarative.AzureAI/Extensions/CodeInterpreterToolExtensions.cs
+++ b/dotnet/src/Microsoft.Agents.AI.Declarative.AzureAI/Extensions/CodeInterpreterToolExtensions.cs
@@ -23,6 +23,21 @@ internal static class CodeInterpreterToolExtensions
return new CodeInterpreterToolDefinition();
}
+ ///
+ /// Converts a to an .
+ ///
+ /// Instance of
+ /// A new instance configured with the container ID from the tool's extension data.
+ internal static OpenAI.Responses.CodeInterpreterTool CreateCodeInterpreterTool(this CodeInterpreterTool tool)
+ {
+ Throw.IfNull(tool);
+
+ var containerId = tool.ExtensionData?.GetPropertyOrNull(InitializablePropertyPath.Create("containerId"))?.Value;
+ Throw.IfNull(containerId, "The 'containerId' property must be specified in the CodeInterpreterTool's extension data to create a code interpreter tool.");
+
+ return new OpenAI.Responses.CodeInterpreterTool(new OpenAI.Responses.CodeInterpreterToolContainer(containerId));
+ }
+
///
/// Collects the file IDs from the extension data of a .
///
diff --git a/dotnet/src/Microsoft.Agents.AI.Declarative.AzureAI/Extensions/FileSearchToolExtensions.cs b/dotnet/src/Microsoft.Agents.AI.Declarative.AzureAI/Extensions/FileSearchToolExtensions.cs
index 52d1e69c04..e582672f93 100644
--- a/dotnet/src/Microsoft.Agents.AI.Declarative.AzureAI/Extensions/FileSearchToolExtensions.cs
+++ b/dotnet/src/Microsoft.Agents.AI.Declarative.AzureAI/Extensions/FileSearchToolExtensions.cs
@@ -25,6 +25,18 @@ internal static class FileSearchToolExtensions
return new FileSearchToolDefinition();
}
+ ///
+ /// Creates an from a .
+ ///
+ /// Instance of
+ /// A new instance configured with the vector store IDs.
+ internal static OpenAI.Responses.FileSearchTool CreateFileSearchTool(this FileSearchTool tool)
+ {
+ Throw.IfNull(tool);
+
+ return new OpenAI.Responses.FileSearchTool(tool.GetVectorStoreIds());
+ }
+
///
/// Get the vector store IDs for the specified .
///
diff --git a/dotnet/src/Microsoft.Agents.AI.Declarative.AzureAI/Extensions/FunctionToolExtensions.cs b/dotnet/src/Microsoft.Agents.AI.Declarative.AzureAI/Extensions/FunctionToolExtensions.cs
index 97281b79b7..73bd9b41b2 100644
--- a/dotnet/src/Microsoft.Agents.AI.Declarative.AzureAI/Extensions/FunctionToolExtensions.cs
+++ b/dotnet/src/Microsoft.Agents.AI.Declarative.AzureAI/Extensions/FunctionToolExtensions.cs
@@ -3,6 +3,7 @@
using System;
using Azure.AI.Agents.Persistent;
using Microsoft.Shared.Diagnostics;
+using OpenAI.Responses;
namespace Microsoft.Bot.ObjectModel;
@@ -28,6 +29,27 @@ public static class FunctionToolExtensions
parameters: parameters);
}
+ ///
+ /// Creates a from a .
+ ///
+ /// Instance of
+ /// A new instance configured with the function name, parameters, and description.
+ internal static FunctionTool CreateFunctionTool(this InvokeClientTaskAction tool)
+ {
+ Throw.IfNull(tool);
+ Throw.IfNull(tool.Name);
+
+ BinaryData parameters = tool.GetParameters();
+
+ return new FunctionTool(
+ functionName: tool.Name,
+ functionParameters: parameters,
+ strictModeEnabled: null)
+ {
+ FunctionDescription = tool.Description
+ };
+ }
+
///
/// Creates the parameters schema for a .
///
diff --git a/dotnet/src/Microsoft.Agents.AI.Declarative.AzureAI/Extensions/McpServerToolExtensions.cs b/dotnet/src/Microsoft.Agents.AI.Declarative.AzureAI/Extensions/McpServerToolExtensions.cs
index 1e0668f846..0e74f53e9a 100644
--- a/dotnet/src/Microsoft.Agents.AI.Declarative.AzureAI/Extensions/McpServerToolExtensions.cs
+++ b/dotnet/src/Microsoft.Agents.AI.Declarative.AzureAI/Extensions/McpServerToolExtensions.cs
@@ -3,6 +3,7 @@
using System;
using Azure.AI.Agents.Persistent;
using Microsoft.Shared.Diagnostics;
+using OpenAI.Responses;
namespace Microsoft.Bot.ObjectModel;
@@ -23,10 +24,30 @@ internal static class McpServerToolExtensions
// TODO: Add support for additional properties
- var connection = tool.Connection as AnonymousConnection ?? throw new ArgumentException("Only AnonymousConnection is supported for MCP Server Tool connections.", nameof(tool));
+ var connection = tool.Connection as AnonymousConnection ?? throw new ArgumentException($"Only AnonymousConnection is supported for MCP Server Tool connections. Actual connection type: {tool.Connection.GetType().Name}", nameof(tool));
var serverUrl = connection.Endpoint?.LiteralValue;
Throw.IfNullOrEmpty(serverUrl, nameof(connection.Endpoint));
return new MCPToolDefinition(tool.ServerName?.LiteralValue, serverUrl);
}
+
+ ///
+ /// Creates a from a .
+ ///
+ /// Instance of
+ /// A new instance configured with the server name and URL.
+ internal static McpTool CreateMcpTool(this McpServerTool tool)
+ {
+ Throw.IfNull(tool);
+ Throw.IfNull(tool.ServerName?.LiteralValue);
+ Throw.IfNull(tool.Connection);
+
+ // TODO: Add support for headers
+
+ var connection = tool.Connection as AnonymousConnection ?? throw new ArgumentException("Only AnonymousConnection is supported for MCP Server Tool connections.", nameof(tool));
+ var serverUrl = connection.Endpoint?.LiteralValue;
+ Throw.IfNullOrEmpty(serverUrl, nameof(connection.Endpoint));
+
+ return new McpTool(tool.ServerName?.LiteralValue, serverUrl);
+ }
}
diff --git a/dotnet/src/Microsoft.Agents.AI.Declarative.AzureAI/Extensions/PromptAgentExtensions.cs b/dotnet/src/Microsoft.Agents.AI.Declarative.AzureAI/Extensions/PromptAgentExtensions.cs
index 899fb26c5b..3fb7c64c1b 100644
--- a/dotnet/src/Microsoft.Agents.AI.Declarative.AzureAI/Extensions/PromptAgentExtensions.cs
+++ b/dotnet/src/Microsoft.Agents.AI.Declarative.AzureAI/Extensions/PromptAgentExtensions.cs
@@ -4,6 +4,7 @@ using System.Collections.Generic;
using System.Linq;
using Azure.AI.Agents.Persistent;
using Microsoft.Shared.Diagnostics;
+using OpenAI.Responses;
namespace Microsoft.Bot.ObjectModel;
@@ -24,11 +25,11 @@ internal static class PromptAgentExtensions
{
return tool switch
{
- CodeInterpreterTool => ((CodeInterpreterTool)tool).CreateCodeInterpreterToolDefinition(),
- InvokeClientTaskAction => ((InvokeClientTaskAction)tool).CreateFunctionToolDefinition(),
- FileSearchTool => ((FileSearchTool)tool).CreateFileSearchToolDefinition(),
- WebSearchTool => ((WebSearchTool)tool).CreateBingGroundingToolDefinition(),
- McpServerTool => ((McpServerTool)tool).CreateMcpToolDefinition(),
+ CodeInterpreterTool codeInterpreterTool => codeInterpreterTool.CreateCodeInterpreterToolDefinition(),
+ InvokeClientTaskAction functionTool => functionTool.CreateFunctionToolDefinition(),
+ FileSearchTool fileSearchTool => fileSearchTool.CreateFileSearchToolDefinition(),
+ WebSearchTool webSearchTool => webSearchTool.CreateBingGroundingToolDefinition(),
+ McpServerTool mcpServerTool => mcpServerTool.CreateMcpToolDefinition(),
// TODO: Add other tool types as custom tools
// AzureAISearch
// AzureFunction
@@ -65,6 +66,33 @@ internal static class PromptAgentExtensions
return toolResources;
}
+ ///
+ /// Returns the Foundry response tools which correspond with the provided .
+ ///
+ /// Instance of .
+ /// A collection of instances corresponding to the tools defined in the agent.
+ internal static IEnumerable GetResponseTools(this GptComponentMetadata promptAgent)
+ {
+ Throw.IfNull(promptAgent);
+
+ return promptAgent.Tools.Select(tool =>
+ {
+ return tool switch
+ {
+ CodeInterpreterTool codeInterpreterTool => codeInterpreterTool.CreateCodeInterpreterTool(),
+ InvokeClientTaskAction functionTool => functionTool.CreateFunctionTool(),
+ FileSearchTool fileSearchTool => fileSearchTool.CreateFileSearchTool(),
+ WebSearchTool webSearchTool => webSearchTool.CreateWebSearchTool(),
+ McpServerTool mcpServerTool => mcpServerTool.CreateMcpTool(),
+ // TODO: Add other tool types as custom tools
+ // AzureAISearch
+ // AzureFunction
+ // OpenApi
+ _ => throw new NotSupportedException($"Unable to create response tool because of unsupported tool type: {tool.Kind}"),
+ };
+ }).ToList();
+ }
+
#region private
private static CodeInterpreterToolResource? GetCodeInterpreterToolResource(this GptComponentMetadata promptAgent)
{
diff --git a/dotnet/src/Microsoft.Agents.AI.Declarative.AzureAI/Extensions/WebSearchToolExtensions.cs b/dotnet/src/Microsoft.Agents.AI.Declarative.AzureAI/Extensions/WebSearchToolExtensions.cs
index f4643f2ca1..b0f65442af 100644
--- a/dotnet/src/Microsoft.Agents.AI.Declarative.AzureAI/Extensions/WebSearchToolExtensions.cs
+++ b/dotnet/src/Microsoft.Agents.AI.Declarative.AzureAI/Extensions/WebSearchToolExtensions.cs
@@ -23,4 +23,16 @@ internal static class WebSearchToolExtensions
return new BingGroundingToolDefinition(parameters);
}
+
+ ///
+ /// Creates a from a .
+ ///
+ /// Instance of
+ /// A new instance.
+ internal static OpenAI.Responses.WebSearchTool CreateWebSearchTool(this WebSearchTool tool)
+ {
+ Throw.IfNull(tool);
+
+ return new OpenAI.Responses.WebSearchTool();
+ }
}
diff --git a/dotnet/src/Microsoft.Agents.AI.Declarative.AzureAI/FoundryAgentFactory.cs b/dotnet/src/Microsoft.Agents.AI.Declarative.AzureAI/FoundryAgentFactory.cs
new file mode 100644
index 0000000000..5d18c7a25c
--- /dev/null
+++ b/dotnet/src/Microsoft.Agents.AI.Declarative.AzureAI/FoundryAgentFactory.cs
@@ -0,0 +1,111 @@
+// Copyright (c) Microsoft. All rights reserved.
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+using Azure.AI.Agents;
+using Azure.AI.Agents.Persistent;
+using Azure.Core;
+using Microsoft.Bot.ObjectModel;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Shared.Diagnostics;
+
+namespace Microsoft.Agents.AI;
+
+///
+/// Provides an which creates instances of using a .
+///
+public sealed class FoundryAgentFactory : AgentFactory
+{
+ private readonly AgentClient? _agentClient;
+ private readonly TokenCredential? _tokenCredential;
+
+ ///
+ /// Creates a new instance of the class with an associated .
+ ///
+ /// The instance to use for creating agents.
+ /// The instance to use for configuration.
+ public FoundryAgentFactory(AgentClient agentClient, IConfiguration? configuration = null) : base(configuration)
+ {
+ Throw.IfNull(agentClient);
+
+ this._agentClient = agentClient;
+ }
+
+ ///
+ /// Creates a new instance of the class with an associated .
+ ///
+ /// The to use for authenticating requests.
+ /// The instance to use for configuration.
+ public FoundryAgentFactory(TokenCredential tokenCredential, IConfiguration? configuration = null) : base(configuration)
+ {
+ Throw.IfNull(tokenCredential);
+
+ this._tokenCredential = tokenCredential;
+ }
+
+ ///
+ public override async Task TryCreateAsync(GptComponentMetadata promptAgent, CancellationToken cancellationToken = default)
+ {
+ Throw.IfNull(promptAgent);
+
+ var agentClient = this._agentClient ?? this.CreateAgentClient(promptAgent);
+
+ var modelId = promptAgent.Model?.ModelNameHint;
+ if (string.IsNullOrEmpty(modelId))
+ {
+ throw new InvalidOperationException("The model id must be specified in the agent definition model to create a foundry agent.");
+ }
+
+ var modelOptions = promptAgent.Model?.Options;
+
+ var promptAgentDefinition = new PromptAgentDefinition(model: modelId)
+ {
+ Instructions = promptAgent.Instructions?.ToTemplateString(),
+ Temperature = (float?)modelOptions?.Temperature?.LiteralValue,
+ TopP = (float?)modelOptions?.TopP?.LiteralValue,
+ };
+
+ foreach (var tool in promptAgent.GetResponseTools())
+ {
+ promptAgentDefinition.Tools.Add(tool);
+ }
+
+ var agentVersionCreationOptions = new AgentVersionCreationOptions(promptAgentDefinition);
+
+ var metadata = promptAgent.Metadata?.ToDictionary();
+ if (metadata is not null)
+ {
+ foreach (var kvp in metadata)
+ {
+ agentVersionCreationOptions.Metadata.Add(kvp.Key, kvp.Value);
+ }
+ }
+
+ var agentVersion = await agentClient.CreateAgentVersionAsync(agentName: promptAgent.Name, options: agentVersionCreationOptions, cancellationToken: cancellationToken).ConfigureAwait(false);
+
+ return agentClient.GetAIAgent(agentVersion, cancellationToken: cancellationToken);
+ }
+
+ private AgentClient CreateAgentClient(GptComponentMetadata promptAgent)
+ {
+ var externalModel = promptAgent.Model as CurrentModels;
+ var connection = externalModel?.Connection as RemoteConnection;
+ if (connection is not null)
+ {
+ var endpoint = connection.Endpoint?.Eval(this.Engine);
+ if (string.IsNullOrEmpty(endpoint))
+ {
+ throw new InvalidOperationException("The endpoint must be specified in the agent definition model connection to create an AgentClient.");
+ }
+
+ if (this._tokenCredential is null)
+ {
+ throw new InvalidOperationException("A TokenCredential must be registered in the service provider to create an AgentClient.");
+ }
+
+ return new AgentClient(new Uri(endpoint), this._tokenCredential);
+ }
+
+ throw new InvalidOperationException("An AgentClient must be registered in the service provider or a RemoteConnection must be specified in the agent definition model connection to create an AgentClient.");
+ }
+}
diff --git a/dotnet/src/Microsoft.Agents.AI.Declarative.AzureAI/FoundryPersistentAgentFactory.cs b/dotnet/src/Microsoft.Agents.AI.Declarative.AzureAI/FoundryPersistentAgentFactory.cs
index 505c627263..ae9bc7756e 100644
--- a/dotnet/src/Microsoft.Agents.AI.Declarative.AzureAI/FoundryPersistentAgentFactory.cs
+++ b/dotnet/src/Microsoft.Agents.AI.Declarative.AzureAI/FoundryPersistentAgentFactory.cs
@@ -19,8 +19,10 @@ public sealed class FoundryPersistentAgentFactory : AgentFactory
private readonly TokenCredential? _tokenCredential;
///
- /// Creates a new instance of the class.
+ /// Creates a new instance of the class with an associated .
///
+ /// The instance to use for creating agents.
+ /// The instance to use for configuration.
public FoundryPersistentAgentFactory(PersistentAgentsClient agentClient, IConfiguration? configuration = null) : base(configuration)
{
Throw.IfNull(agentClient);
@@ -29,8 +31,10 @@ public sealed class FoundryPersistentAgentFactory : AgentFactory
}
///
- /// Creates a new instance of the class.
+ /// Creates a new instance of the class with an associated .
///
+ /// The to use for authenticating requests.
+ /// The instance to use for configuration.
public FoundryPersistentAgentFactory(TokenCredential tokenCredential, IConfiguration? configuration = null) : base(configuration)
{
Throw.IfNull(tokenCredential);
diff --git a/dotnet/src/Microsoft.Agents.AI.Declarative.AzureAI/Microsoft.Agents.AI.Declarative.AzureAI.csproj b/dotnet/src/Microsoft.Agents.AI.Declarative.AzureAI/Microsoft.Agents.AI.Declarative.AzureAI.csproj
index 4f8874ef07..05705a5675 100644
--- a/dotnet/src/Microsoft.Agents.AI.Declarative.AzureAI/Microsoft.Agents.AI.Declarative.AzureAI.csproj
+++ b/dotnet/src/Microsoft.Agents.AI.Declarative.AzureAI/Microsoft.Agents.AI.Declarative.AzureAI.csproj
@@ -24,6 +24,7 @@
+
@@ -31,7 +32,7 @@
-
+
@@ -39,6 +40,7 @@
+
diff --git a/dotnet/tests/Microsoft.Agents.AI.Declarative.UnitTests/AgentBotElementYamlTests.cs b/dotnet/tests/Microsoft.Agents.AI.Declarative.UnitTests/AgentBotElementYamlTests.cs
index d54304897c..c11cfa26fd 100644
--- a/dotnet/tests/Microsoft.Agents.AI.Declarative.UnitTests/AgentBotElementYamlTests.cs
+++ b/dotnet/tests/Microsoft.Agents.AI.Declarative.UnitTests/AgentBotElementYamlTests.cs
@@ -7,7 +7,6 @@ using Microsoft.Bot.ObjectModel;
using Microsoft.Extensions.AI;
using Microsoft.Extensions.Configuration;
using Microsoft.PowerFx;
-using Microsoft.PowerFx.Types;
namespace Microsoft.Agents.AI.Declarative.UnitTests;
diff --git a/dotnet/tests/Microsoft.Agents.AI.Declarative.UnitTests/ChatClient/ChatClientAgentFactoryTests.cs b/dotnet/tests/Microsoft.Agents.AI.Declarative.UnitTests/ChatClient/ChatClientAgentFactoryTests.cs
index 97647ea3a9..b9078ee0b0 100644
--- a/dotnet/tests/Microsoft.Agents.AI.Declarative.UnitTests/ChatClient/ChatClientAgentFactoryTests.cs
+++ b/dotnet/tests/Microsoft.Agents.AI.Declarative.UnitTests/ChatClient/ChatClientAgentFactoryTests.cs
@@ -19,7 +19,7 @@ public sealed class ChatClientAgentFactoryTests
}
[Fact]
- public async Task TryCreateAsync_WithChatClientInConstructor_CreatesAgent()
+ public async Task TryCreateAsync_WithChatClientInConstructor_CreatesAgentAsync()
{
// Arrange
var promptAgent = PromptAgents.CreateTestPromptAgent();
@@ -36,7 +36,7 @@ public sealed class ChatClientAgentFactoryTests
}
[Fact]
- public async Task TryCreateAsync_Creates_ChatClientAgent()
+ public async Task TryCreateAsync_Creates_ChatClientAgentAsync()
{
// Arrange
var promptAgent = PromptAgents.CreateTestPromptAgent();
@@ -56,7 +56,7 @@ public sealed class ChatClientAgentFactoryTests
}
[Fact]
- public async Task TryCreateAsync_Creates_ChatOptions()
+ public async Task TryCreateAsync_Creates_ChatOptionsAsync()
{
// Arrange
var promptAgent = PromptAgents.CreateTestPromptAgent();
@@ -87,7 +87,7 @@ public sealed class ChatClientAgentFactoryTests
}
[Fact]
- public async Task TryCreateAsync_Creates_Tools()
+ public async Task TryCreateAsync_Creates_ToolsAsync()
{
// Arrange
var promptAgent = PromptAgents.CreateTestPromptAgent();