diff --git a/dotnet/samples/GettingStarted/Custom/Custom_OpenAIChatClientAgent.cs b/dotnet/samples/GettingStarted/Custom/Custom_OpenAIChatClientAgent.cs
new file mode 100644
index 000000000..2363b06cb
--- /dev/null
+++ b/dotnet/samples/GettingStarted/Custom/Custom_OpenAIChatClientAgent.cs
@@ -0,0 +1,41 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+using Microsoft.Extensions.Logging;
+using Microsoft.Shared.Samples;
+using OpenAI;
+using OpenAI.Chat;
+
+namespace Custom;
+
+///
+/// End-to-end sample showing how to use a custom .
+///
+public sealed class Custom_OpenAIChatClientAgent(ITestOutputHelper output) : AgentSample(output)
+{
+ ///
+ /// This will create an instance of and run it.
+ ///
+ [Fact]
+ public async Task RunCustomChatClientAgent()
+ {
+ var chatClient = new OpenAIClient(TestConfiguration.OpenAI.ApiKey).GetChatClient(TestConfiguration.OpenAI.ChatModelId);
+
+ var agent = new MyOpenAIChatClientAgent(chatClient);
+
+ var chatMessage = new UserChatMessage("Tell me a joke about a pirate.");
+ var chatCompletion = await agent.RunAsync(chatMessage);
+
+ Console.WriteLine(chatCompletion.Content.Last().Text);
+ }
+}
+
+public class MyOpenAIChatClientAgent : OpenAIChatClientAgent
+{
+ private const string JokerName = "Joker";
+ private const string JokerInstructions = "You are good at telling jokes.";
+
+ public MyOpenAIChatClientAgent(ChatClient client, ILoggerFactory? loggerFactory = null) :
+ base(client, instructions: JokerInstructions, name: JokerName, loggerFactory: loggerFactory)
+ {
+ }
+}
diff --git a/dotnet/samples/GettingStarted/GettingStarted.csproj b/dotnet/samples/GettingStarted/GettingStarted.csproj
index c3175cc5e..0707da1ad 100644
--- a/dotnet/samples/GettingStarted/GettingStarted.csproj
+++ b/dotnet/samples/GettingStarted/GettingStarted.csproj
@@ -4,7 +4,7 @@
GettingStarted
Library
5ee045b0-aea3-4f08-8d31-32d1a6f8fed0
- $(NoWarn);CA1707;CA1716;IDE0009;IDE1006;OPENAI001;
+ $(NoWarn);CA1707;CA1716;IDE0009;IDE1006; OPENAI001;
enable
true
true
diff --git a/dotnet/samples/GettingStarted/Orchestration/SequentialOrchestration_Foundry_Agents.cs b/dotnet/samples/GettingStarted/Orchestration/SequentialOrchestration_Foundry_Agents.cs
new file mode 100644
index 000000000..a9ff1ee20
--- /dev/null
+++ b/dotnet/samples/GettingStarted/Orchestration/SequentialOrchestration_Foundry_Agents.cs
@@ -0,0 +1,87 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+using Azure.AI.Agents.Persistent;
+using Azure.Identity;
+using Microsoft.Agents.Orchestration;
+using Microsoft.Extensions.AI.Agents;
+using Microsoft.Shared.Samples;
+
+namespace Orchestration;
+
+///
+/// Demonstrates how to use the for
+/// executing multiple Foundry agents in sequence.
+///
+public class SequentialOrchestration_Foundry_Agents(ITestOutputHelper output) : OrchestrationSample(output)
+{
+ [Theory]
+ [InlineData(false)]
+ [InlineData(true)]
+ public async Task RunOrchestrationAsync(bool streamedResponse)
+ {
+ // Get a client to create server side agents with.
+ var persistentAgentsClient = new PersistentAgentsClient(TestConfiguration.AzureAI.Endpoint, new AzureCliCredential());
+ var model = TestConfiguration.OpenAI.ChatModelId;
+
+ // Define the agents
+ AIAgent analystAgent =
+ await persistentAgentsClient.CreateAIAgentAsync(
+ model,
+ name: "Analyst",
+ instructions:
+ """
+ You are a marketing analyst. Given a product description, identify:
+ - Key features
+ - Target audience
+ - Unique selling points
+ """,
+ description: "A agent that extracts key concepts from a product description.");
+ AIAgent writerAgent =
+ await persistentAgentsClient.CreateAIAgentAsync(
+ model,
+ name: "copywriter",
+ instructions:
+ """
+ You are a marketing copywriter. Given a block of text describing features, audience, and USPs,
+ compose a compelling marketing copy (like a newsletter section) that highlights these points.
+ Output should be short (around 150 words), output just the copy as a single text block.
+ """,
+ description: "An agent that writes a marketing copy based on the extracted concepts.");
+ AIAgent editorAgent =
+ await persistentAgentsClient.CreateAIAgentAsync(
+ model,
+ name: "editor",
+ instructions:
+ """
+ You are an editor. Given the draft copy, correct grammar, improve clarity, ensure consistent tone,
+ give format and make it polished. Output the final improved copy as a single text block.
+ """,
+ description: "An agent that formats and proofreads the marketing copy.");
+
+ // Create a monitor to capturing agent responses (via ResponseCallback)
+ // to display at the end of this sample. (optional)
+ // NOTE: Create your own callback to capture responses in your application or service.
+ OrchestrationMonitor monitor = new();
+ // Define the orchestration
+ SequentialOrchestration orchestration =
+ new(analystAgent, writerAgent, editorAgent)
+ {
+ LoggerFactory = this.LoggerFactory,
+ ResponseCallback = monitor.ResponseCallback,
+ StreamingResponseCallback = streamedResponse ? monitor.StreamingResultCallback : null,
+ };
+
+ // Run the orchestration
+ string input = "An eco-friendly stainless steel water bottle that keeps drinks cold for 24 hours";
+ Console.WriteLine($"\n# INPUT: {input}\n");
+ AgentRunResponse result = await orchestration.RunAsync(input);
+ Console.WriteLine($"\n# RESULT: {result}");
+
+ this.DisplayHistory(monitor.History);
+
+ // Cleanup
+ await persistentAgentsClient.Administration.DeleteAgentAsync(editorAgent.Id);
+ await persistentAgentsClient.Administration.DeleteAgentAsync(writerAgent.Id);
+ await persistentAgentsClient.Administration.DeleteAgentAsync(analystAgent.Id);
+ }
+}
diff --git a/dotnet/samples/GettingStarted/Orchestration/SequentialOrchestration_Multi_Agent.cs b/dotnet/samples/GettingStarted/Orchestration/SequentialOrchestration_Multi_Agent.cs
new file mode 100644
index 000000000..f79b6ad61
--- /dev/null
+++ b/dotnet/samples/GettingStarted/Orchestration/SequentialOrchestration_Multi_Agent.cs
@@ -0,0 +1,83 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+using Microsoft.Agents.Orchestration;
+using Microsoft.Extensions.AI.Agents;
+using Microsoft.Shared.Samples;
+using OpenAI;
+
+namespace Orchestration;
+
+///
+/// Demonstrates how to use the for
+/// executing multiple heterogeneous agents in sequence.
+///
+public class SequentialOrchestration_Multi_Agent(ITestOutputHelper output) : OrchestrationSample(output)
+{
+ [Theory]
+ [InlineData(false)]
+ [InlineData(true)]
+ public async Task RunOrchestrationAsync(bool streamedResponse)
+ {
+ var openAIClient = new OpenAIClient(TestConfiguration.OpenAI.ApiKey);
+ var model = TestConfiguration.OpenAI.ChatModelId;
+
+ // Define the agents
+ AIAgent analystAgent =
+ openAIClient.GetChatClient(model).CreateAIAgent(
+ name: "Analyst",
+ instructions:
+ """
+ You are a marketing analyst. Given a product description, identify:
+ - Key features
+ - Target audience
+ - Unique selling points
+ """,
+ description: "A agent that extracts key concepts from a product description.");
+ AIAgent writerAgent =
+ openAIClient.GetOpenAIResponseClient(model).CreateAIAgent(
+ name: "copywriter",
+ instructions:
+ """
+ You are a marketing copywriter. Given a block of text describing features, audience, and USPs,
+ compose a compelling marketing copy (like a newsletter section) that highlights these points.
+ Output should be short (around 150 words), output just the copy as a single text block.
+ """,
+ description: "An agent that writes a marketing copy based on the extracted concepts.");
+ AIAgent editorAgent =
+ openAIClient.GetAssistantClient().CreateAIAgent(
+ model,
+ name: "editor",
+ instructions:
+ """
+ You are an editor. Given the draft copy, correct grammar, improve clarity, ensure consistent tone,
+ give format and make it polished. Output the final improved copy as a single text block.
+ """,
+ description: "An agent that formats and proofreads the marketing copy.");
+
+ // Create a monitor to capturing agent responses (via ResponseCallback)
+ // to display at the end of this sample. (optional)
+ // NOTE: Create your own callback to capture responses in your application or service.
+ OrchestrationMonitor monitor = new();
+ // Define the orchestration
+ SequentialOrchestration orchestration =
+ new(analystAgent, writerAgent, editorAgent)
+ {
+ LoggerFactory = this.LoggerFactory,
+ ResponseCallback = monitor.ResponseCallback,
+ StreamingResponseCallback = streamedResponse ? monitor.StreamingResultCallback : null,
+ };
+
+ // Run the orchestration
+ string input = "An eco-friendly stainless steel water bottle that keeps drinks cold for 24 hours";
+ Console.WriteLine($"\n# INPUT: {input}\n");
+ AgentRunResponse result = await orchestration.RunAsync(input);
+ Console.WriteLine($"\n# RESULT: {result}");
+
+ this.DisplayHistory(monitor.History);
+
+ // Cleanup
+ var assistantClient = openAIClient.GetAssistantClient();
+ await assistantClient.DeleteAssistantAsync(editorAgent.Id);
+ // Need to know how to get the assistant thread ID to delete the thread (issue #260)
+ }
+}
diff --git a/dotnet/samples/GettingStarted/Providers/AIAgent_With_AzureAIAgentsPersistent.cs b/dotnet/samples/GettingStarted/Providers/AIAgent_With_AzureAIAgentsPersistent.cs
new file mode 100644
index 000000000..89f4d704d
--- /dev/null
+++ b/dotnet/samples/GettingStarted/Providers/AIAgent_With_AzureAIAgentsPersistent.cs
@@ -0,0 +1,91 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+using Azure.AI.Agents.Persistent;
+using Azure.Identity;
+using Microsoft.Extensions.AI.Agents;
+using Microsoft.Shared.Samples;
+
+namespace Providers;
+
+///
+/// Shows how to use with Azure AI Persistent Agents.
+///
+///
+/// Running "az login" command in terminal is required for authentication with Azure AI service.
+///
+public sealed class AIAgent_With_AzureAIAgentsPersistent(ITestOutputHelper output) : AgentSample(output)
+{
+ private const string JokerName = "Joker";
+ private const string JokerInstructions = "You are good at telling jokes.";
+
+ [Fact]
+ public async Task GetWithAzureAIAgentsPersistent()
+ {
+ // Get a client to create server side agents with.
+ var persistentAgentsClient = new PersistentAgentsClient(TestConfiguration.AzureAI.Endpoint, new AzureCliCredential());
+
+ // Create a service side persistent agent.
+ var persistentAgent = await persistentAgentsClient.Administration.CreateAgentAsync(
+ model: TestConfiguration.AzureAI.DeploymentName!,
+ name: JokerName,
+ instructions: JokerInstructions);
+
+ // Get a server side agent.
+ AIAgent agent = await persistentAgentsClient.GetAIAgentAsync(persistentAgent.Value.Id);
+
+ // Start a new thread for the agent conversation.
+ AgentThread thread = agent.GetNewThread();
+
+ // Respond to user input
+ await RunAgentAsync("Tell me a joke about a pirate.");
+ await RunAgentAsync("Now add some emojis to the joke.");
+
+ // Local function to run agent and display the conversation messages for the thread.
+ async Task RunAgentAsync(string input)
+ {
+ Console.WriteLine(input);
+
+ var response = await agent.RunAsync(input, thread);
+
+ Console.WriteLine(response);
+ }
+
+ // Cleanup
+ await persistentAgentsClient.Threads.DeleteThreadAsync(thread.Id);
+ await persistentAgentsClient.Administration.DeleteAgentAsync(agent.Id);
+ }
+
+ [Fact]
+ public async Task CreateWithAzureAIAgentsPersistent()
+ {
+ // Get a client to create server side agents with.
+ var persistentAgentsClient = new PersistentAgentsClient(TestConfiguration.AzureAI.Endpoint, new AzureCliCredential());
+
+ // Create a server side persistent agent.
+ AIAgent agent = await persistentAgentsClient.CreateAIAgentAsync(
+ model: TestConfiguration.AzureAI.DeploymentName!,
+ name: JokerName,
+ instructions: JokerInstructions);
+
+ // Start a new thread for the agent conversation.
+ AgentThread thread = agent.GetNewThread();
+
+ // Respond to user input
+ await RunAgentAsync("Tell me a joke about a pirate.");
+ await RunAgentAsync("Now add some emojis to the joke.");
+
+ // Local function to run agent and display the conversation messages for the thread.
+ async Task RunAgentAsync(string input)
+ {
+ Console.WriteLine(input);
+
+ var response = await agent.RunAsync(input, thread);
+
+ Console.WriteLine(response);
+ }
+
+ // Cleanup
+ await persistentAgentsClient.Threads.DeleteThreadAsync(thread.Id);
+ await persistentAgentsClient.Administration.DeleteAgentAsync(agent.Id);
+ }
+}
diff --git a/dotnet/samples/GettingStarted/Providers/ChatClientAgent_With_AzureOpenAIChatCompletion.cs b/dotnet/samples/GettingStarted/Providers/AIAgent_With_AzureOpenAIChatCompletion.cs
similarity index 74%
rename from dotnet/samples/GettingStarted/Providers/ChatClientAgent_With_AzureOpenAIChatCompletion.cs
rename to dotnet/samples/GettingStarted/Providers/AIAgent_With_AzureOpenAIChatCompletion.cs
index e52c7a5d3..86b33c35f 100644
--- a/dotnet/samples/GettingStarted/Providers/ChatClientAgent_With_AzureOpenAIChatCompletion.cs
+++ b/dotnet/samples/GettingStarted/Providers/AIAgent_With_AzureOpenAIChatCompletion.cs
@@ -3,16 +3,16 @@
using System.ClientModel;
using Azure.AI.OpenAI;
using Azure.Identity;
-using Microsoft.Extensions.AI;
using Microsoft.Extensions.AI.Agents;
using Microsoft.Shared.Samples;
+using OpenAI;
namespace Providers;
///
/// End-to-end sample showing how to use with Azure OpenAI Chat Completion.
///
-public sealed class ChatClientAgent_With_AzureOpenAIChatCompletion(ITestOutputHelper output) : AgentSample(output)
+public sealed class AIAgent_With_AzureOpenAIChatCompletion(ITestOutputHelper output) : AgentSample(output)
{
private const string JokerName = "Joker";
private const string JokerInstructions = "You are good at telling jokes.";
@@ -20,16 +20,14 @@ public sealed class ChatClientAgent_With_AzureOpenAIChatCompletion(ITestOutputHe
[Fact]
public async Task RunWithChatCompletion()
{
- // Get the chat client to use for the agent.
- using var chatClient = ((TestConfiguration.AzureOpenAI.ApiKey is null)
+ // Get the OpenAI client to use for the agent.
+ var openAIClient = (TestConfiguration.AzureOpenAI.ApiKey is null)
// Use Azure CLI credentials if API key is not provided.
? new AzureOpenAIClient(TestConfiguration.AzureOpenAI.Endpoint, new AzureCliCredential())
- : new AzureOpenAIClient(TestConfiguration.AzureOpenAI.Endpoint, new ApiKeyCredential(TestConfiguration.AzureOpenAI.ApiKey)))
- .GetChatClient(TestConfiguration.AzureOpenAI.DeploymentName)
- .AsIChatClient();
+ : new AzureOpenAIClient(TestConfiguration.AzureOpenAI.Endpoint, new ApiKeyCredential(TestConfiguration.AzureOpenAI.ApiKey));
- // Define the agent
- ChatClientAgent agent = new(chatClient, JokerInstructions, JokerName);
+ // Create the agent
+ AIAgent agent = openAIClient.GetChatClient(TestConfiguration.AzureOpenAI.DeploymentName).CreateAIAgent(JokerInstructions, JokerName);
// Start a new thread for the agent conversation.
AgentThread thread = agent.GetNewThread();
diff --git a/dotnet/samples/GettingStarted/Providers/ChatClientAgent_With_OpenAIAssistant.cs b/dotnet/samples/GettingStarted/Providers/AIAgent_With_OpenAIAssistant.cs
similarity index 62%
rename from dotnet/samples/GettingStarted/Providers/ChatClientAgent_With_OpenAIAssistant.cs
rename to dotnet/samples/GettingStarted/Providers/AIAgent_With_OpenAIAssistant.cs
index f3c12a3ff..df9772922 100644
--- a/dotnet/samples/GettingStarted/Providers/ChatClientAgent_With_OpenAIAssistant.cs
+++ b/dotnet/samples/GettingStarted/Providers/AIAgent_With_OpenAIAssistant.cs
@@ -1,6 +1,5 @@
// Copyright (c) Microsoft. All rights reserved.
-using Microsoft.Extensions.AI;
using Microsoft.Extensions.AI.Agents;
using Microsoft.Shared.Samples;
using OpenAI;
@@ -10,36 +9,29 @@
namespace Providers;
///
-/// End-to-end sample showing how to use with OpenAI Assistants.
+/// End-to-end sample showing how to use with OpenAI Assistants.
///
-public sealed class ChatClientAgent_With_OpenAIAssistant(ITestOutputHelper output) : AgentSample(output)
+public sealed class AIAgent_With_OpenAIAssistant(ITestOutputHelper output) : AgentSample(output)
{
private const string JokerName = "Joker";
private const string JokerInstructions = "You are good at telling jokes.";
[Fact]
- public async Task RunWithOpenAIAssistant()
+ public async Task RunWithAssistant()
{
// Get a client to create server side agents with.
var openAIClient = new OpenAIClient(TestConfiguration.OpenAI.ApiKey);
- var assistantClient = openAIClient.GetAssistantClient();
-
- // Create a server side agent to work with.
- var assistantCreateResult = await assistantClient.CreateAssistantAsync(
- TestConfiguration.OpenAI.ChatModelId,
- new()
- {
- Name = JokerName,
- Instructions = JokerInstructions
- });
-
- var assistantId = assistantCreateResult.Value.Id;
- // Get the chat client to use for the agent.
- using var chatClient = assistantClient.AsIChatClient(assistantId);
-
- // Define the agent.
- ChatClientAgent agent = new(chatClient);
+ // Get the agent directly from OpenAIClient.
+ AIAgent agent = openAIClient
+ .GetAssistantClient()
+ .CreateAIAgent(
+ TestConfiguration.OpenAI.ChatModelId,
+ options: new()
+ {
+ Name = JokerName,
+ Instructions = JokerInstructions,
+ });
// Start a new thread for the agent conversation.
AgentThread thread = agent.GetNewThread();
@@ -59,7 +51,8 @@ async Task RunAgentAsync(string input)
}
// Cleanup
+ var assistantClient = openAIClient.GetAssistantClient();
await assistantClient.DeleteThreadAsync(thread.Id);
- await assistantClient.DeleteAssistantAsync(assistantId);
+ await assistantClient.DeleteAssistantAsync(agent.Id);
}
}
diff --git a/dotnet/samples/GettingStarted/Providers/AIAgent_With_OpenAIClient.cs b/dotnet/samples/GettingStarted/Providers/AIAgent_With_OpenAIClient.cs
new file mode 100644
index 000000000..2c85b93bf
--- /dev/null
+++ b/dotnet/samples/GettingStarted/Providers/AIAgent_With_OpenAIClient.cs
@@ -0,0 +1,98 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+using Microsoft.Extensions.AI.Agents;
+using Microsoft.Shared.Samples;
+using OpenAI;
+using OpenAI.Chat;
+
+namespace Providers;
+
+///
+/// End-to-end sample showing how to use with OpenAI Chat Completion and Responses.
+///
+public sealed class AIAgent_With_OpenAIClient(ITestOutputHelper output) : AgentSample(output)
+{
+ private const string JokerName = "Joker";
+ private const string JokerInstructions = "You are good at telling jokes.";
+
+ [Fact]
+ public async Task RunWithChatCompletion()
+ {
+ // Get the agent directly from OpenAIClient.
+ AIAgent agent = new OpenAIClient(TestConfiguration.OpenAI.ApiKey)
+ .GetChatClient(TestConfiguration.OpenAI.ChatModelId)
+ .CreateAIAgent(JokerInstructions, JokerName);
+
+ // Start a new thread for the agent conversation.
+ AgentThread thread = agent.GetNewThread();
+
+ // Respond to user input.
+ await RunAgentAsync("Tell me a joke about a pirate.");
+ await RunAgentAsync("Now add some emojis to the joke.");
+
+ // Local function to invoke agent and display the conversation messages for the thread.
+ async Task RunAgentAsync(string input)
+ {
+ Console.WriteLine(input);
+
+ var response = await agent.RunAsync(input, thread);
+
+ Console.WriteLine(response.Messages.Last().Text);
+ }
+ }
+
+ [Fact]
+ public async Task RunWithChatCompletionReturnChatCompletion()
+ {
+ // Get the agent directly from OpenAIClient.
+ var agent = new OpenAIClient(TestConfiguration.OpenAI.ApiKey)
+ .GetChatClient(TestConfiguration.OpenAI.ChatModelId)
+ .CreateAIAgent(JokerInstructions, JokerName);
+
+ // Start a new thread for the agent conversation.
+ AgentThread thread = agent.GetNewThread();
+
+ // Respond to user input.
+ await RunAgentAsync("Tell me a joke about a pirate.");
+ await RunAgentAsync("Now add some emojis to the joke.");
+
+ // Local function to invoke agent and display the conversation messages for the thread.
+ async Task RunAgentAsync(string input)
+ {
+ Console.WriteLine(input);
+
+ var response = await agent.RunAsync(input, thread);
+ var chatCompletion = response.AsChatCompletion();
+
+ Console.WriteLine(chatCompletion.Content.Last().Text);
+ }
+ }
+
+ [Fact]
+ public async Task RunWithChatCompletionWithOpenAIChatMessage()
+ {
+ // Get the agent directly from OpenAIClient.
+ var agent = new OpenAIClient(TestConfiguration.OpenAI.ApiKey)
+ .GetChatClient(TestConfiguration.OpenAI.ChatModelId)
+ .CreateAIAgent(JokerInstructions, JokerName);
+
+ // Start a new thread for the agent conversation.
+ AgentThread thread = agent.GetNewThread();
+
+ // Respond to user input.
+ await RunAgentAsync("Tell me a joke about a pirate.");
+ await RunAgentAsync("Now add some emojis to the joke.");
+
+ // Local function to invoke agent and display the conversation messages for the thread.
+ async Task RunAgentAsync(string input)
+ {
+ Console.WriteLine(input);
+
+ // Use the OpenAI.Chat message types directly
+ var chatMessage = new UserChatMessage(input);
+ var chatCompletion = await agent.RunAsync(chatMessage, thread);
+
+ Console.WriteLine(chatCompletion.Content.Last().Text);
+ }
+ }
+}
diff --git a/dotnet/samples/GettingStarted/Providers/ChatClientAgent_With_OpenAIResponseChatCompletion.cs b/dotnet/samples/GettingStarted/Providers/AIAgent_With_OpenAIResponseClient.cs
similarity index 77%
rename from dotnet/samples/GettingStarted/Providers/ChatClientAgent_With_OpenAIResponseChatCompletion.cs
rename to dotnet/samples/GettingStarted/Providers/AIAgent_With_OpenAIResponseClient.cs
index 3e3489da4..314a49c76 100644
--- a/dotnet/samples/GettingStarted/Providers/ChatClientAgent_With_OpenAIResponseChatCompletion.cs
+++ b/dotnet/samples/GettingStarted/Providers/AIAgent_With_OpenAIResponseClient.cs
@@ -11,7 +11,7 @@ namespace Providers;
///
/// End-to-end sample showing how to use with OpenAI Chat Completion.
///
-public sealed class ChatClientAgent_With_OpenAIResponsesChatCompletion(ITestOutputHelper output) : AgentSample(output)
+public sealed class AIAgent_With_OpenAIResponseClient(ITestOutputHelper output) : AgentSample(output)
{
private const string JokerName = "Joker";
private const string JokerInstructions = "You are good at telling jokes.";
@@ -20,15 +20,12 @@ public sealed class ChatClientAgent_With_OpenAIResponsesChatCompletion(ITestOutp
/// This will use the conversation id to reference the thread state on the server side.
///
[Fact]
- public async Task RunWithChatCompletionServiceManagedThread()
+ public async Task RunWithResponses()
{
- // Get the chat client to use for the agent.
- using var chatClient = new OpenAIClient(TestConfiguration.OpenAI.ApiKey)
+ // Get the agent directly from OpenAIClient.
+ AIAgent agent = new OpenAIClient(TestConfiguration.OpenAI.ApiKey)
.GetOpenAIResponseClient(TestConfiguration.OpenAI.ChatModelId)
- .AsIChatClient();
-
- // Define the agent
- ChatClientAgent agent = new(chatClient, JokerInstructions, JokerName);
+ .CreateAIAgent(JokerInstructions, JokerName);
// Start a new thread for the agent conversation based on the type.
AgentThread thread = agent.GetNewThread();
@@ -52,16 +49,12 @@ async Task RunAgentAsync(string input)
/// This will use in-memory messages to store the thread state.
///
[Fact]
- public async Task RunWithChatCompletionInMemoryThread()
+ public async Task RunWithResponsesAndStoreOutputDisabled()
{
- // Get the chat client to use for the agent.
- using var chatClient = new OpenAIClient(TestConfiguration.OpenAI.ApiKey)
+ // Get the agent directly from OpenAIClient.
+ AIAgent agent = new OpenAIClient(TestConfiguration.OpenAI.ApiKey)
.GetOpenAIResponseClient(TestConfiguration.OpenAI.ChatModelId)
- .AsIChatClient();
-
- // Define the agent
- ChatClientAgent agent =
- new(chatClient, options: new()
+ .CreateAIAgent(options: new()
{
Name = JokerName,
Instructions = JokerInstructions,
diff --git a/dotnet/samples/GettingStarted/Providers/ChatClientAgent_With_AzureAIAgentsPersistent.cs b/dotnet/samples/GettingStarted/Providers/ChatClientAgent_With_AzureAIAgentsPersistent.cs
deleted file mode 100644
index 4bbb3df0d..000000000
--- a/dotnet/samples/GettingStarted/Providers/ChatClientAgent_With_AzureAIAgentsPersistent.cs
+++ /dev/null
@@ -1,57 +0,0 @@
-// Copyright (c) Microsoft. All rights reserved.
-
-using Azure.AI.Agents.Persistent;
-using Azure.Identity;
-using Microsoft.Extensions.AI.Agents;
-using Microsoft.Shared.Samples;
-
-namespace Providers;
-
-///
-/// Shows how to use with Azure AI Persistent Agents.
-///
-///
-/// Running "az login" command in terminal is required for authentication with Azure AI service.
-///
-public sealed class ChatClientAgent_With_AzureAIAgentsPersistent(ITestOutputHelper output) : AgentSample(output)
-{
- private const string JokerName = "Joker";
- private const string JokerInstructions = "You are good at telling jokes.";
-
- [Fact]
- public async Task RunWithAzureAIAgentsPersistent()
- {
- // Get a client to create server side agents with.
- var persistentAgentsClient = new PersistentAgentsClient(TestConfiguration.AzureAI.Endpoint, new AzureCliCredential());
-
- // Create a server side persistent agent.
- var createPersistentAgentResponse = await persistentAgentsClient.Administration.CreateAgentAsync(
- model: TestConfiguration.AzureAI.DeploymentName,
- name: JokerName,
- instructions: JokerInstructions);
-
- // Get a local proxy for the agent to work with.
- AIAgent agent = await persistentAgentsClient.GetRunnableAgentAsync(createPersistentAgentResponse.Value.Id);
-
- // Start a new thread for the agent conversation.
- AgentThread thread = agent.GetNewThread();
-
- // Respond to user input
- await RunAgentAsync("Tell me a joke about a pirate.");
- await RunAgentAsync("Now add some emojis to the joke.");
-
- // Local function to run agent and display the conversation messages for the thread.
- async Task RunAgentAsync(string input)
- {
- this.WriteUserMessage(input);
-
- var response = await agent.RunAsync(input, thread);
-
- this.WriteResponseOutput(response);
- }
-
- // Cleanup
- await persistentAgentsClient.Threads.DeleteThreadAsync(thread.Id);
- await persistentAgentsClient.Administration.DeleteAgentAsync(createPersistentAgentResponse.Value.Id);
- }
-}
diff --git a/dotnet/samples/GettingStarted/Providers/ChatClientAgent_With_OpenAIChatCompletion.cs b/dotnet/samples/GettingStarted/Providers/ChatClientAgent_With_OpenAIChatCompletion.cs
deleted file mode 100644
index e21cf5e3a..000000000
--- a/dotnet/samples/GettingStarted/Providers/ChatClientAgent_With_OpenAIChatCompletion.cs
+++ /dev/null
@@ -1,46 +0,0 @@
-// Copyright (c) Microsoft. All rights reserved.
-
-using Microsoft.Extensions.AI;
-using Microsoft.Extensions.AI.Agents;
-using Microsoft.Shared.Samples;
-using OpenAI;
-
-namespace Providers;
-
-///
-/// End-to-end sample showing how to use with OpenAI Chat Completion.
-///
-public sealed class ChatClientAgent_With_OpenAIChatCompletion(ITestOutputHelper output) : AgentSample(output)
-{
- private const string JokerName = "Joker";
- private const string JokerInstructions = "You are good at telling jokes.";
-
- [Fact]
- public async Task RunWithChatCompletion()
- {
- // Get the chat client to use for the agent.
- using var chatClient = new OpenAIClient(TestConfiguration.OpenAI.ApiKey)
- .GetChatClient(TestConfiguration.OpenAI.ChatModelId)
- .AsIChatClient();
-
- // Define the agent
- ChatClientAgent agent = new(chatClient, JokerInstructions, JokerName);
-
- // Start a new thread for the agent conversation.
- AgentThread thread = agent.GetNewThread();
-
- // Respond to user input.
- await RunAgentAsync("Tell me a joke about a pirate.");
- await RunAgentAsync("Now add some emojis to the joke.");
-
- // Local function to invoke agent and display the conversation messages for the thread.
- async Task RunAgentAsync(string input)
- {
- this.WriteUserMessage(input);
-
- var response = await agent.RunAsync(input, thread);
-
- this.WriteResponseOutput(response);
- }
- }
-}
diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents.AzureAI/Microsoft.Extensions.AI.Agents.AzureAI.csproj b/dotnet/src/Microsoft.Extensions.AI.Agents.AzureAI/Microsoft.Extensions.AI.Agents.AzureAI.csproj
index 58008d7b3..a9e91d1e7 100644
--- a/dotnet/src/Microsoft.Extensions.AI.Agents.AzureAI/Microsoft.Extensions.AI.Agents.AzureAI.csproj
+++ b/dotnet/src/Microsoft.Extensions.AI.Agents.AzureAI/Microsoft.Extensions.AI.Agents.AzureAI.csproj
@@ -1,11 +1,11 @@
-
+
$(ProjectsTargetFrameworks)
$(ProjectsDebugTargetFrameworks)
alpha
- $(NoWarn);IDE0009;
enable
+ $(NoWarn);IDE0009;
diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents.AzureAI/ResponseExtensions.cs b/dotnet/src/Microsoft.Extensions.AI.Agents.AzureAI/PersistentAgentResponseExtensions.cs
similarity index 85%
rename from dotnet/src/Microsoft.Extensions.AI.Agents.AzureAI/ResponseExtensions.cs
rename to dotnet/src/Microsoft.Extensions.AI.Agents.AzureAI/PersistentAgentResponseExtensions.cs
index 517350da1..d63cfb915 100644
--- a/dotnet/src/Microsoft.Extensions.AI.Agents.AzureAI/ResponseExtensions.cs
+++ b/dotnet/src/Microsoft.Extensions.AI.Agents.AzureAI/PersistentAgentResponseExtensions.cs
@@ -17,14 +17,14 @@ internal static class PersistentAgentResponseExtensions
/// The client used to interact with persistent agents. Cannot be .
/// The default to use when interacting with the agent.
/// A instance that can be used to perform operations on the persistent agent.
- public static ChatClientAgent AsRunnableAgent(this Response persistentAgentResponse, PersistentAgentsClient persistentAgentsClient, ChatOptions? chatOptions = null)
+ public static ChatClientAgent AsAIAgent(this Response persistentAgentResponse, PersistentAgentsClient persistentAgentsClient, ChatOptions? chatOptions = null)
{
if (persistentAgentResponse is null)
{
throw new ArgumentNullException(nameof(persistentAgentResponse));
}
- return AsRunnableAgent(persistentAgentResponse.Value, persistentAgentsClient, chatOptions);
+ return AsAIAgent(persistentAgentResponse.Value, persistentAgentsClient, chatOptions);
}
///
@@ -34,7 +34,7 @@ public static ChatClientAgent AsRunnableAgent(this Response per
/// The client used to interact with persistent agents. Cannot be .
/// The default to use when interacting with the agent.
/// A instance that can be used to perform operations on the persistent agent.
- public static ChatClientAgent AsRunnableAgent(this PersistentAgent persistentAgentMetadata, PersistentAgentsClient persistentAgentsClient, ChatOptions? chatOptions = null)
+ public static ChatClientAgent AsAIAgent(this PersistentAgent persistentAgentMetadata, PersistentAgentsClient persistentAgentsClient, ChatOptions? chatOptions = null)
{
if (persistentAgentMetadata is null)
{
diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents.AzureAI/PersistentAgentsClientExtensions.cs b/dotnet/src/Microsoft.Extensions.AI.Agents.AzureAI/PersistentAgentsClientExtensions.cs
index 1f9e217c5..acfa0a11e 100644
--- a/dotnet/src/Microsoft.Extensions.AI.Agents.AzureAI/PersistentAgentsClientExtensions.cs
+++ b/dotnet/src/Microsoft.Extensions.AI.Agents.AzureAI/PersistentAgentsClientExtensions.cs
@@ -19,7 +19,7 @@ public static class PersistentAgentsClientExtensions
/// Options that should apply to all runs of the agent.
/// The to monitor for cancellation requests. The default is .
/// A instance that can be used to perform operations on the persistent agent.
- public static async Task GetRunnableAgentAsync(
+ public static async Task GetAIAgentAsync(
this PersistentAgentsClient persistentAgentsClient,
string agentId,
ChatOptions? chatOptions = null,
@@ -36,6 +36,57 @@ public static async Task GetRunnableAgentAsync(
}
var persistentAgentResponse = await persistentAgentsClient.Administration.GetAgentAsync(agentId, cancellationToken).ConfigureAwait(false);
- return persistentAgentResponse.AsRunnableAgent(persistentAgentsClient, chatOptions);
+ return persistentAgentResponse.AsAIAgent(persistentAgentsClient, chatOptions);
+ }
+
+ ///
+ /// Creates a new server side agent using the provided .
+ ///
+ /// The to create the agent with.
+ /// The model to be used by the agent.
+ /// The name of the agent.
+ /// The description of the agent.
+ /// The instructions for the agent.
+ /// The tools to be used by the agent.
+ /// The resources for the tools.
+ /// The temperature setting for the agent.
+ /// The top-p setting for the agent.
+ /// The response format for the agent.
+ /// The metadata for the agent.
+ /// The to monitor for cancellation requests. The default is .
+ /// A instance that can be used to perform operations on the newly created agent.
+ public static async Task CreateAIAgentAsync(
+ this PersistentAgentsClient persistentAgentsClient,
+ string model,
+ string? name = null,
+ string? description = null,
+ string? instructions = null,
+ IEnumerable? tools = null,
+ ToolResources? toolResources = null,
+ float? temperature = null,
+ float? topP = null,
+ BinaryData? responseFormat = null,
+ IReadOnlyDictionary? metadata = null,
+ CancellationToken cancellationToken = default)
+ {
+ if (persistentAgentsClient is null)
+ {
+ throw new ArgumentNullException(nameof(persistentAgentsClient));
+ }
+
+ var createPersistentAgentResponse = await persistentAgentsClient.Administration.CreateAgentAsync(
+ model,
+ name,
+ instructions,
+ tools: tools,
+ toolResources: toolResources,
+ temperature: temperature,
+ topP: topP,
+ responseFormat: responseFormat,
+ metadata: metadata,
+ cancellationToken: cancellationToken).ConfigureAwait(false);
+
+ // Get a local proxy for the agent to work with.
+ return await persistentAgentsClient.GetAIAgentAsync(createPersistentAgentResponse.Value.Id, cancellationToken: cancellationToken).ConfigureAwait(false);
}
}
diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents.OpenAI/AIAgentWithOpenAIExtensions.cs b/dotnet/src/Microsoft.Extensions.AI.Agents.OpenAI/AIAgentWithOpenAIExtensions.cs
new file mode 100644
index 000000000..dc23ad1df
--- /dev/null
+++ b/dotnet/src/Microsoft.Extensions.AI.Agents.OpenAI/AIAgentWithOpenAIExtensions.cs
@@ -0,0 +1,356 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+using System.Text;
+using Microsoft.Extensions.AI;
+using Microsoft.Extensions.AI.Agents;
+using Microsoft.Shared.Diagnostics;
+using OpenAI.Chat;
+
+namespace OpenAI;
+
+///
+/// Provides extension methods for to simplify interaction with OpenAI chat messages
+/// and return native OpenAI responses.
+///
+///
+/// These extensions bridge the gap between the Microsoft Extensions AI framework and the OpenAI SDK,
+/// allowing developers to work with native OpenAI types while leveraging the AI Agent framework.
+/// The methods handle the conversion between OpenAI chat message types and Microsoft Extensions AI types,
+/// and return OpenAI objects directly from the agent's .
+///
+public static class AIAgentWithOpenAIExtensions
+{
+ ///
+ /// Runs the AI agent with a single OpenAI chat message and returns the response as a native OpenAI .
+ ///
+ /// The AI agent to run.
+ /// The OpenAI chat message to send to the agent.
+ /// The conversation thread to continue with this invocation. If not provided, creates a new thread. The thread will be mutated with the provided message and agent response.
+ /// Optional parameters for agent invocation.
+ /// The to monitor for cancellation requests. The default is .
+ /// A representing the asynchronous operation that returns a native OpenAI response.
+ /// Thrown when or is .
+ /// Thrown when the agent's response cannot be converted to a , typically when the underlying representation is not an OpenAI response.
+ /// Thrown when the type is not supported by the message conversion method.
+ ///
+ /// This method converts the OpenAI chat message to the Microsoft Extensions AI format using the appropriate conversion method,
+ /// runs the agent, and then extracts the native OpenAI from the response using .
+ ///
+ public static async Task RunAsync(this AIAgent agent, OpenAI.Chat.ChatMessage message, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default)
+ {
+ Throw.IfNull(agent);
+ Throw.IfNull(message);
+
+ var response = await agent.RunAsync(message.AsChatMessage(), thread, options, cancellationToken).ConfigureAwait(false);
+
+ var chatCompletion = response.AsChatCompletion();
+ return chatCompletion;
+ }
+
+ ///
+ /// Runs the AI agent with a collection of OpenAI chat messages and returns the response as a native OpenAI .
+ ///
+ /// The AI agent to run.
+ /// The collection of OpenAI chat messages to send to the agent.
+ /// The conversation thread to continue with this invocation. If not provided, creates a new thread. The thread will be mutated with the provided messages and agent response.
+ /// Optional parameters for agent invocation.
+ /// The to monitor for cancellation requests. The default is .
+ /// A representing the asynchronous operation that returns a native OpenAI response.
+ /// Thrown when or is .
+ /// Thrown when the agent's response cannot be converted to a , typically when the underlying representation is not an OpenAI response.
+ /// Thrown when any message in has a type that is not supported by the message conversion method.
+ ///
+ /// This method converts each OpenAI chat message to the Microsoft Extensions AI format using ,
+ /// runs the agent with the converted message collection, and then extracts the native OpenAI from the response using .
+ ///
+ public static async Task RunAsync(this AIAgent agent, IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default)
+ {
+ Throw.IfNull(agent);
+ Throw.IfNull(messages);
+
+ var response = await agent.RunAsync([.. messages.AsChatMessages()], thread, options, cancellationToken).ConfigureAwait(false);
+
+ var chatCompletion = response.AsChatCompletion();
+ return chatCompletion;
+ }
+
+ ///
+ /// Creates a sequence of instances from the specified OpenAI input messages.
+ ///
+ /// The OpenAI input messages to convert.
+ /// A sequence of Microsoft Extensions AI chat messages converted from the OpenAI messages.
+ /// Thrown when is .
+ /// Thrown when a message type is encountered that cannot be converted.
+ ///
+ /// This method supports conversion of the following OpenAI message types:
+ ///
+ ///
+ ///
+ /// - (obsolete)
+ ///
+ ///
+ ///
+ ///
+ ///
+ internal static IEnumerable AsChatMessages(this IEnumerable messages)
+ {
+ Throw.IfNull(messages);
+
+ foreach (OpenAI.Chat.ChatMessage message in messages)
+ {
+ switch (message)
+ {
+ case OpenAI.Chat.AssistantChatMessage assistantMessage:
+ yield return assistantMessage.AsChatMessage();
+ break;
+ case OpenAI.Chat.DeveloperChatMessage developerMessage:
+ yield return developerMessage.AsChatMessage();
+ break;
+#pragma warning disable CS0618 // Type or member is obsolete
+ case OpenAI.Chat.FunctionChatMessage functionMessage:
+ yield return functionMessage.AsChatMessage();
+ break;
+#pragma warning restore CS0618 // Type or member is obsolete
+ case OpenAI.Chat.SystemChatMessage systemMessage:
+ yield return systemMessage.AsChatMessage();
+ break;
+ case OpenAI.Chat.ToolChatMessage toolMessage:
+ yield return toolMessage.AsChatMessage();
+ break;
+ case OpenAI.Chat.UserChatMessage userMessage:
+ yield return userMessage.AsChatMessage();
+ break;
+ }
+ }
+ }
+
+ ///
+ /// Converts an OpenAI chat message to a Microsoft Extensions AI .
+ ///
+ /// The OpenAI chat message to convert.
+ /// A equivalent of the input OpenAI message.
+ /// Thrown when is .
+ /// Thrown when the type is not supported for conversion.
+ ///
+ /// This method provides a bridge between OpenAI SDK message types and Microsoft Extensions AI message types.
+ /// It handles the conversion by switching on the concrete type of the OpenAI message and calling the appropriate
+ /// specialized conversion method.
+ ///
+ internal static Microsoft.Extensions.AI.ChatMessage AsChatMessage(this OpenAI.Chat.ChatMessage chatMessage)
+ {
+ Throw.IfNull(chatMessage);
+
+ return chatMessage switch
+ {
+ AssistantChatMessage assistantMessage => assistantMessage.AsChatMessage(),
+ DeveloperChatMessage developerMessage => developerMessage.AsChatMessage(),
+ SystemChatMessage systemMessage => systemMessage.AsChatMessage(),
+ ToolChatMessage toolMessage => toolMessage.AsChatMessage(),
+ UserChatMessage userMessage => userMessage.AsChatMessage(),
+ _ => throw new NotSupportedException($"Message type {chatMessage.GetType().Name} is not supported for conversion.")
+ };
+ }
+
+ ///
+ /// Converts OpenAI chat message content to Microsoft Extensions AI content items.
+ ///
+ /// The OpenAI chat message content to convert.
+ /// A sequence of items converted from the OpenAI content.
+ ///
+ /// This method supports conversion of the following OpenAI content part types:
+ ///
+ /// - Text content (converted to )
+ /// - Refusal content (converted to )
+ /// - Image content (converted to or )
+ /// - Input audio content (converted to )
+ /// - File content (converted to )
+ ///
+ ///
+ private static IEnumerable AsAIContent(this OpenAI.Chat.ChatMessageContent content)
+ {
+ Throw.IfNull(content);
+
+ foreach (OpenAI.Chat.ChatMessageContentPart part in content)
+ {
+ switch (part.Kind)
+ {
+ case OpenAI.Chat.ChatMessageContentPartKind.Text:
+ yield return new TextContent(part.Text)
+ {
+ RawRepresentation = content
+ };
+ break;
+ case OpenAI.Chat.ChatMessageContentPartKind.Refusal:
+ yield return new TextContent(part.Refusal)
+ {
+ RawRepresentation = content
+ };
+ break;
+ case OpenAI.Chat.ChatMessageContentPartKind.Image:
+ if (part.ImageBytes is not null)
+ {
+ yield return new DataContent(part.ImageBytes, part.ImageBytesMediaType)
+ {
+ RawRepresentation = content
+ };
+ }
+ else
+ {
+ yield return new UriContent(part.ImageUri, "image/*")
+ {
+ RawRepresentation = content
+ };
+ }
+ break;
+ case OpenAI.Chat.ChatMessageContentPartKind.InputAudio:
+ yield return new DataContent(part.InputAudioBytes, "audio/*")
+ {
+ RawRepresentation = content
+ };
+ break;
+ case OpenAI.Chat.ChatMessageContentPartKind.File:
+ yield return new DataContent(part.FileBytes, part.FileBytesMediaType)
+ {
+ RawRepresentation = content
+ };
+ break;
+ default:
+ throw new NotSupportedException($"Content part kind '{part.Kind}' is not supported for conversion to AIContent.");
+ }
+ }
+ }
+
+ ///
+ /// Converts OpenAI chat message content to text.
+ ///
+ /// The OpenAI chat message content to convert.
+ /// A string created from the text and refusal parts of the OpenAI content.
+ ///
+ /// Using when converting OpenAI For tool messages, the contents can only be of type text.
+ ///
+ private static string AsText(this OpenAI.Chat.ChatMessageContent content)
+ {
+ Throw.IfNull(content);
+
+ StringBuilder text = new();
+ foreach (OpenAI.Chat.ChatMessageContentPart part in content)
+ {
+ switch (part.Kind)
+ {
+ case OpenAI.Chat.ChatMessageContentPartKind.Text:
+ text.Append(part.Text);
+ break;
+ case OpenAI.Chat.ChatMessageContentPartKind.Refusal:
+ text.Append(part.Refusal);
+ break;
+ default:
+ throw new NotSupportedException($"Content part kind '{part.Kind}' is not supported for conversion to text.");
+ }
+ }
+ return text.ToString();
+ }
+
+ ///
+ /// Converts an OpenAI to a Microsoft Extensions AI .
+ ///
+ /// The OpenAI assistant message to convert.
+ /// A Microsoft Extensions AI chat message with assistant role.
+ ///
+ /// This method converts the assistant message content using and preserves
+ /// the participant name as the author name in the resulting message.
+ ///
+ private static Microsoft.Extensions.AI.ChatMessage AsChatMessage(this AssistantChatMessage assistantMessage)
+ {
+ Throw.IfNull(assistantMessage);
+
+ return new Microsoft.Extensions.AI.ChatMessage(Microsoft.Extensions.AI.ChatRole.Assistant, [.. assistantMessage.Content.AsAIContent()])
+ {
+ AuthorName = assistantMessage.ParticipantName,
+ RawRepresentation = assistantMessage
+ };
+ }
+
+ ///
+ /// Converts an OpenAI to a Microsoft Extensions AI .
+ ///
+ /// The OpenAI developer message to convert.
+ /// A Microsoft Extensions AI chat message with system role.
+ ///
+ /// Developer messages are treated as system messages in the Microsoft Extensions AI framework.
+ /// The participant name is preserved as the author name.
+ ///
+ private static Microsoft.Extensions.AI.ChatMessage AsChatMessage(this DeveloperChatMessage developerMessage)
+ {
+ Throw.IfNull(developerMessage);
+
+ return new Microsoft.Extensions.AI.ChatMessage(Microsoft.Extensions.AI.ChatRole.System, [.. developerMessage.Content.AsAIContent()])
+ {
+ AuthorName = developerMessage.ParticipantName,
+ RawRepresentation = developerMessage
+ };
+ }
+
+ ///
+ /// Converts an OpenAI to a Microsoft Extensions AI .
+ ///
+ /// The OpenAI system message to convert.
+ /// A Microsoft Extensions AI chat message with system role.
+ ///
+ /// This method converts the system message content using and preserves
+ /// the participant name as the author name in the resulting message.
+ ///
+ private static Microsoft.Extensions.AI.ChatMessage AsChatMessage(this SystemChatMessage systemMessage)
+ {
+ Throw.IfNull(systemMessage);
+
+ return new Microsoft.Extensions.AI.ChatMessage(Microsoft.Extensions.AI.ChatRole.System, [.. systemMessage.Content.AsAIContent()])
+ {
+ AuthorName = systemMessage.ParticipantName,
+ RawRepresentation = systemMessage
+ };
+ }
+
+ ///
+ /// Converts an OpenAI to a Microsoft Extensions AI .
+ ///
+ /// The OpenAI tool message to convert.
+ /// A Microsoft Extensions AI chat message with tool role.
+ ///
+ /// This method converts tool message content using and includes the tool call ID
+ /// in the resulting message's additional properties for traceability.
+ ///
+ private static Microsoft.Extensions.AI.ChatMessage AsChatMessage(this ToolChatMessage toolMessage)
+ {
+ Throw.IfNull(toolMessage);
+
+ var content = new FunctionResultContent(toolMessage.ToolCallId, toolMessage.Content.AsText())
+ {
+ RawRepresentation = toolMessage
+ };
+ return new Microsoft.Extensions.AI.ChatMessage(Microsoft.Extensions.AI.ChatRole.Tool, [content])
+ {
+ RawRepresentation = toolMessage,
+ AdditionalProperties = new() { ["tool_call_id"] = toolMessage.ToolCallId }
+ };
+ }
+
+ ///
+ /// Converts an OpenAI to a Microsoft Extensions AI .
+ ///
+ /// The OpenAI user message to convert.
+ /// A Microsoft Extensions AI chat message with user role.
+ ///
+ /// This method converts the user message content using and preserves
+ /// the participant name as the author name in the resulting message.
+ ///
+ private static Microsoft.Extensions.AI.ChatMessage AsChatMessage(this UserChatMessage userMessage)
+ {
+ Throw.IfNull(userMessage);
+
+ return new Microsoft.Extensions.AI.ChatMessage(Microsoft.Extensions.AI.ChatRole.User, [.. userMessage.Content.AsAIContent()])
+ {
+ AuthorName = userMessage.ParticipantName,
+ RawRepresentation = userMessage
+ };
+ }
+}
diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents.OpenAI/AgentRunResponseExtensions.cs b/dotnet/src/Microsoft.Extensions.AI.Agents.OpenAI/AgentRunResponseExtensions.cs
new file mode 100644
index 000000000..b3e5c5c93
--- /dev/null
+++ b/dotnet/src/Microsoft.Extensions.AI.Agents.OpenAI/AgentRunResponseExtensions.cs
@@ -0,0 +1,52 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+using Microsoft.Extensions.AI;
+using Microsoft.Extensions.AI.Agents;
+using Microsoft.Shared.Diagnostics;
+using OpenAI.Chat;
+
+namespace OpenAI;
+
+///
+/// Provides extension methods for to extract native OpenAI response objects
+/// from the Microsoft Extensions AI Agent framework responses.
+///
+///
+/// These extensions enable developers to access the underlying OpenAI SDK objects when working with
+/// AI agents that are backed by OpenAI services. The methods extract strongly-typed OpenAI responses
+/// from the property, providing a bridge between
+/// the Microsoft Extensions AI framework and the native OpenAI SDK types.
+///
+public static class AgentRunResponseExtensions
+{
+ ///
+ /// Extracts a native OpenAI object from an .
+ ///
+ /// The agent response containing the raw OpenAI representation.
+ /// The native OpenAI object.
+ /// Thrown when is .
+ ///
+ /// Thrown when the is not a object.
+ /// This typically occurs when the agent response was not generated by an OpenAI chat completion service
+ /// or when the underlying representation has been modified or corrupted.
+ ///
+ ///
+ ///
+ /// This method provides access to the native OpenAI object that was used
+ /// to generate the agent response. This is useful when you need to access OpenAI-specific properties
+ /// or metadata that are not exposed through the Microsoft Extensions AI abstractions.
+ ///
+ ///
+ public static ChatCompletion AsChatCompletion(this AgentRunResponse agentResponse)
+ {
+ Throw.IfNull(agentResponse);
+
+ if (agentResponse.RawRepresentation is ChatResponse chatResponse)
+ {
+ return chatResponse.RawRepresentation is ChatCompletion chatCompletion
+ ? chatCompletion
+ : throw new ArgumentException("ChatResponse.RawRepresentation must be a ChatCompletion");
+ }
+ throw new ArgumentException("AgentRunResponse.RawRepresentation must be a ChatResponse");
+ }
+}
diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents.OpenAI/Microsoft.Extensions.AI.Agents.OpenAI.csproj b/dotnet/src/Microsoft.Extensions.AI.Agents.OpenAI/Microsoft.Extensions.AI.Agents.OpenAI.csproj
index 826eead02..eb4ad0143 100644
--- a/dotnet/src/Microsoft.Extensions.AI.Agents.OpenAI/Microsoft.Extensions.AI.Agents.OpenAI.csproj
+++ b/dotnet/src/Microsoft.Extensions.AI.Agents.OpenAI/Microsoft.Extensions.AI.Agents.OpenAI.csproj
@@ -4,7 +4,7 @@
$(ProjectsTargetFrameworks)
$(ProjectsDebugTargetFrameworks)
alpha
- $(NoWarn);IDE0009;
+ $(NoWarn);IDE0009;OPENAI001;
enable
true
diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents.OpenAI/NewOpenAIAssistantChatClient.cs b/dotnet/src/Microsoft.Extensions.AI.Agents.OpenAI/NewOpenAIAssistantChatClient.cs
index 7cf1a7d0f..f6a4ef951 100644
--- a/dotnet/src/Microsoft.Extensions.AI.Agents.OpenAI/NewOpenAIAssistantChatClient.cs
+++ b/dotnet/src/Microsoft.Extensions.AI.Agents.OpenAI/NewOpenAIAssistantChatClient.cs
@@ -49,7 +49,7 @@ public sealed class NewOpenAIAssistantChatClient : IChatClient
private IReadOnlyList? _assistantTools;
/// Initializes a new instance of the class for the specified .
- public NewOpenAIAssistantChatClient(AssistantClient assistantClient, string assistantId, string? defaultThreadId)
+ public NewOpenAIAssistantChatClient(AssistantClient assistantClient, string assistantId, string? defaultThreadId = null)
{
_client = Throw.IfNull(assistantClient);
_assistantId = Throw.IfNullOrWhitespace(assistantId);
diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents.OpenAI/OpenAIAssistantClientExtensions.cs b/dotnet/src/Microsoft.Extensions.AI.Agents.OpenAI/OpenAIAssistantClientExtensions.cs
new file mode 100644
index 000000000..71277db74
--- /dev/null
+++ b/dotnet/src/Microsoft.Extensions.AI.Agents.OpenAI/OpenAIAssistantClientExtensions.cs
@@ -0,0 +1,206 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+using Microsoft.Extensions.AI;
+using Microsoft.Extensions.AI.Agents;
+using Microsoft.Extensions.Logging;
+using Microsoft.Shared.Diagnostics;
+using OpenAI.Assistants;
+
+namespace OpenAI;
+
+///
+/// Provides extension methods for OpenAI
+/// to simplify the creation of AI agents that work with OpenAI services.
+///
+///
+/// These extensions bridge the gap between OpenAI SDK client objects and the Microsoft Extensions AI Agent framework,
+/// allowing developers to easily create AI agents that leverage OpenAI's chat completion and response services.
+/// The methods handle the conversion from OpenAI clients to instances and then wrap them
+/// in objects that implement the interface.
+///
+public static class OpenAIAssistantClientExtensions
+{
+ ///
+ /// Creates an AI agent from an using the OpenAI Assistant API.
+ ///
+ /// The OpenAI to use for the agent.
+ /// The model identifier to use (e.g., "gpt-4").
+ /// Optional system instructions that define the agent's behavior and personality.
+ /// Optional name for the agent for identification purposes.
+ /// Optional description of the agent's capabilities and purpose.
+ /// Optional collection of AI tools that the agent can use during conversations.
+ /// Optional logger factory for enabling logging within the agent.
+ /// An instance backed by the OpenAI Assistant service.
+ /// Thrown when or is .
+ /// Thrown when is empty or whitespace.
+ public static AIAgent CreateAIAgent(this AssistantClient client, string model, string? instructions = null, string? name = null, string? description = null, IList? tools = null, ILoggerFactory? loggerFactory = null)
+ {
+ return client.CreateAIAgent(
+ model,
+ new ChatClientAgentOptions()
+ {
+ Name = name,
+ Description = description,
+ Instructions = instructions,
+ ChatOptions = tools is null ? null : new ChatOptions()
+ {
+ Tools = tools,
+ }
+ },
+ loggerFactory);
+ }
+
+ ///
+ /// Creates an AI agent from an using the OpenAI Assistant API.
+ ///
+ /// The OpenAI to use for the agent.
+ /// The model identifier to use (e.g., "gpt-4").
+ /// Full set of options to configure the agent.
+ /// Optional logger factory for enabling logging within the agent.
+ /// An instance backed by the OpenAI Assistant service.
+ /// Thrown when or or is .
+ /// Thrown when is empty or whitespace.
+ public static AIAgent CreateAIAgent(this AssistantClient client, string model, ChatClientAgentOptions options, ILoggerFactory? loggerFactory = null)
+ {
+ Throw.IfNull(client);
+ Throw.IfNullOrEmpty(model);
+ Throw.IfNull(options);
+
+ var assistantOptions = new AssistantCreationOptions()
+ {
+ Name = options.Name,
+ Description = options.Description,
+ Instructions = options.Instructions,
+ };
+
+ if (options.ChatOptions?.Tools is not null)
+ {
+ foreach (AITool tool in options.ChatOptions.Tools)
+ {
+ switch (tool)
+ {
+ case AIFunction aiFunction:
+ assistantOptions.Tools.Add(NewOpenAIAssistantChatClient.ToOpenAIAssistantsFunctionToolDefinition(aiFunction));
+ break;
+
+ case HostedCodeInterpreterTool:
+ var codeInterpreterToolDefinition = new CodeInterpreterToolDefinition();
+ assistantOptions.Tools.Add(codeInterpreterToolDefinition);
+ break;
+ }
+ }
+ }
+
+ var assistantCreateResult = client.CreateAssistant(model, assistantOptions);
+ var assistantId = assistantCreateResult.Value.Id;
+
+ var agentOptions = new ChatClientAgentOptions()
+ {
+ Id = assistantId,
+ Name = options.Name,
+ Description = options.Description,
+ Instructions = options.Instructions,
+ ChatOptions = options.ChatOptions?.Tools is null ? null : new ChatOptions()
+ {
+ Tools = options.ChatOptions.Tools,
+ }
+ };
+
+#pragma warning disable CA2000 // Dispose objects before losing scope
+ var chatClient = new NewOpenAIAssistantChatClient(client, assistantId);
+#pragma warning restore CA2000 // Dispose objects before losing scope
+ return new ChatClientAgent(chatClient, agentOptions, loggerFactory);
+ }
+
+ ///
+ /// Creates an AI agent from an using the OpenAI Assistant API.
+ ///
+ /// The OpenAI to use for the agent.
+ /// The model identifier to use (e.g., "gpt-4").
+ /// Optional system instructions that define the agent's behavior and personality.
+ /// Optional name for the agent for identification purposes.
+ /// Optional description of the agent's capabilities and purpose.
+ /// Optional collection of AI tools that the agent can use during conversations.
+ /// Optional logger factory for enabling logging within the agent.
+ /// An instance backed by the OpenAI Assistant service.
+ /// Thrown when or is .
+ /// Thrown when is empty or whitespace.
+ public static async Task CreateAIAgentAsync(this AssistantClient client, string model, string? instructions = null, string? name = null, string? description = null, IList? tools = null, ILoggerFactory? loggerFactory = null)
+ {
+ return await client.CreateAIAgentAsync(
+ model,
+ new ChatClientAgentOptions()
+ {
+ Name = name,
+ Description = description,
+ Instructions = instructions,
+ ChatOptions = tools is null ? null : new ChatOptions()
+ {
+ Tools = tools,
+ }
+ },
+ loggerFactory).ConfigureAwait(false);
+ }
+
+ ///
+ /// Creates an AI agent from an using the OpenAI Assistant API.
+ ///
+ /// The OpenAI to use for the agent.
+ /// The model identifier to use (e.g., "gpt-4").
+ /// Full set of options to configure the agent.
+ /// Optional logger factory for enabling logging within the agent.
+ /// An instance backed by the OpenAI Assistant service.
+ /// Thrown when or is .
+ /// Thrown when is empty or whitespace.
+ public static async Task CreateAIAgentAsync(this AssistantClient client, string model, ChatClientAgentOptions options, ILoggerFactory? loggerFactory = null)
+ {
+ Throw.IfNull(client);
+ Throw.IfNull(model);
+ Throw.IfNull(options);
+
+ var assistantOptions = new AssistantCreationOptions()
+ {
+ Name = options.Name,
+ Description = options.Description,
+ Instructions = options.Instructions,
+ };
+
+ if (options.ChatOptions?.Tools is not null)
+ {
+ foreach (AITool tool in options.ChatOptions.Tools)
+ {
+ switch (tool)
+ {
+ case AIFunction aiFunction:
+ assistantOptions.Tools.Add(NewOpenAIAssistantChatClient.ToOpenAIAssistantsFunctionToolDefinition(aiFunction));
+ break;
+
+ case HostedCodeInterpreterTool:
+ var codeInterpreterToolDefinition = new CodeInterpreterToolDefinition();
+ assistantOptions.Tools.Add(codeInterpreterToolDefinition);
+ break;
+ }
+ }
+ }
+
+ var assistantCreateResult = await client.CreateAssistantAsync(model, assistantOptions).ConfigureAwait(false);
+ var assistantId = assistantCreateResult.Value.Id;
+
+ var agentOptions = new ChatClientAgentOptions()
+ {
+ Id = assistantId,
+ Name = options.Name,
+ Description = options.Description,
+ Instructions = options.Instructions,
+ ChatOptions = options.ChatOptions?.Tools is null ? null : new ChatOptions()
+ {
+ Tools = options.ChatOptions.Tools,
+ }
+ };
+
+#pragma warning disable CA2000 // Dispose objects before losing scope
+ var chatClient = new NewOpenAIAssistantChatClient(client, assistantId);
+#pragma warning restore CA2000 // Dispose objects before losing scope
+ return new ChatClientAgent(chatClient, agentOptions, loggerFactory);
+ }
+}
diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents.OpenAI/OpenAIChatClientAgent.cs b/dotnet/src/Microsoft.Extensions.AI.Agents.OpenAI/OpenAIChatClientAgent.cs
new file mode 100644
index 000000000..61c3b12fa
--- /dev/null
+++ b/dotnet/src/Microsoft.Extensions.AI.Agents.OpenAI/OpenAIChatClientAgent.cs
@@ -0,0 +1,90 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+using Microsoft.Extensions.AI;
+using Microsoft.Extensions.AI.Agents;
+using Microsoft.Extensions.Logging;
+using Microsoft.Shared.Diagnostics;
+using OpenAI.Chat;
+using ChatMessage = OpenAI.Chat.ChatMessage;
+
+namespace OpenAI;
+
+///
+/// OpenAI chat completion based implementation of .
+///
+public class OpenAIChatClientAgent : AIAgent
+{
+ private readonly ChatClientAgent _chatClientAgent;
+
+ ///
+ /// Initialize an instance of
+ ///
+ /// Instance of
+ /// Optional instructions for the agent.
+ /// Optional name for the agent.
+ /// Optional description for the agent.
+ /// Optional instance of
+ public OpenAIChatClientAgent(ChatClient client, string? instructions = null, string? name = null, string? description = null, ILoggerFactory? loggerFactory = null)
+ {
+ Throw.IfNull(client);
+
+ var chatClient = client.AsIChatClient();
+ this._chatClientAgent = new(
+ chatClient,
+ new ChatClientAgentOptions()
+ {
+ Name = name,
+ Description = description,
+ Instructions = instructions,
+ },
+ loggerFactory);
+ }
+
+ ///
+ /// Initialize an instance of
+ ///
+ /// Instance of
+ /// Options to create the agent.
+ /// Optional instance of
+ public OpenAIChatClientAgent(ChatClient client, ChatClientAgentOptions options, ILoggerFactory? loggerFactory = null)
+ {
+ Throw.IfNull(client);
+
+ var chatClient = client.AsIChatClient();
+ this._chatClientAgent = new(chatClient, options, loggerFactory);
+ }
+
+ ///
+ /// Run the agent with the provided message and arguments.
+ ///
+ /// The messages to pass to the agent.
+ /// The conversation thread to continue with this invocation. If not provided, creates a new thread. The thread will be mutated with the provided messages and agent response.
+ /// Optional parameters for agent invocation.
+ /// The to monitor for cancellation requests. The default is .
+ /// A containing the list of items.
+ public virtual async Task RunAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default)
+ {
+ var response = await this.RunAsync([.. messages.AsChatMessages()], thread, options, cancellationToken).ConfigureAwait(false);
+
+ var chatCompletion = response.AsChatCompletion();
+ return chatCompletion;
+ }
+
+ ///
+ public sealed override AgentThread GetNewThread()
+ {
+ return this._chatClientAgent.GetNewThread();
+ }
+
+ ///
+ public sealed override Task RunAsync(IReadOnlyCollection messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default)
+ {
+ return this._chatClientAgent.RunAsync(messages, thread, options, cancellationToken);
+ }
+
+ ///
+ public sealed override IAsyncEnumerable RunStreamingAsync(IReadOnlyCollection messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default)
+ {
+ return this._chatClientAgent.RunStreamingAsync(messages, thread, options, cancellationToken);
+ }
+}
diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents.OpenAI/OpenAIChatClientExtensions.cs b/dotnet/src/Microsoft.Extensions.AI.Agents.OpenAI/OpenAIChatClientExtensions.cs
new file mode 100644
index 000000000..90e279eab
--- /dev/null
+++ b/dotnet/src/Microsoft.Extensions.AI.Agents.OpenAI/OpenAIChatClientExtensions.cs
@@ -0,0 +1,67 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+using Microsoft.Extensions.AI;
+using Microsoft.Extensions.AI.Agents;
+using Microsoft.Extensions.Logging;
+using Microsoft.Shared.Diagnostics;
+using OpenAI.Chat;
+
+namespace OpenAI;
+
+///
+/// Provides extension methods for
+/// to simplify the creation of AI agents that work with OpenAI services.
+///
+///
+/// These extensions bridge the gap between OpenAI SDK client objects and the Microsoft Extensions AI Agent framework,
+/// allowing developers to easily create AI agents that leverage OpenAI's chat completion and response services.
+/// The methods handle the conversion from OpenAI clients to instances and then wrap them
+/// in objects that implement the interface.
+///
+public static class OpenAIChatClientExtensions
+{
+ ///
+ /// Creates an AI agent from an using the OpenAI Chat Completion API.
+ ///
+ /// The OpenAI to use for the agent.
+ /// Optional system instructions that define the agent's behavior and personality.
+ /// Optional name for the agent for identification purposes.
+ /// Optional description of the agent's capabilities and purpose.
+ /// Optional collection of AI tools that the agent can use during conversations.
+ /// Optional logger factory for enabling logging within the agent.
+ /// An instance backed by the OpenAI Chat Completion service.
+ /// Thrown when is .
+ public static AIAgent CreateAIAgent(this ChatClient client, string? instructions = null, string? name = null, string? description = null, IList? tools = null, ILoggerFactory? loggerFactory = null)
+ {
+ return client.CreateAIAgent(
+ new ChatClientAgentOptions()
+ {
+ Name = name,
+ Description = description,
+ Instructions = instructions,
+ ChatOptions = tools is null ? null : new ChatOptions()
+ {
+ Tools = tools,
+ }
+ },
+ loggerFactory);
+ }
+
+ ///
+ /// Creates an AI agent from an using the OpenAI Chat Completion API.
+ ///
+ /// The OpenAI to use for the agent.
+ /// Full set of options to configure the agent.
+ /// Optional logger factory for enabling logging within the agent.
+ /// An instance backed by the OpenAI Chat Completion service.
+ /// Thrown when or is .
+ public static AIAgent CreateAIAgent(this ChatClient client, ChatClientAgentOptions options, ILoggerFactory? loggerFactory = null)
+ {
+ Throw.IfNull(client);
+ Throw.IfNull(options);
+
+ var chatClient = client.AsIChatClient();
+ ChatClientAgent agent = new(chatClient, options, loggerFactory);
+ return agent;
+ }
+}
diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents.OpenAI/OpenAIResponseClientExtensions.cs b/dotnet/src/Microsoft.Extensions.AI.Agents.OpenAI/OpenAIResponseClientExtensions.cs
new file mode 100644
index 000000000..37f27f304
--- /dev/null
+++ b/dotnet/src/Microsoft.Extensions.AI.Agents.OpenAI/OpenAIResponseClientExtensions.cs
@@ -0,0 +1,67 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+using Microsoft.Extensions.AI;
+using Microsoft.Extensions.AI.Agents;
+using Microsoft.Extensions.Logging;
+using Microsoft.Shared.Diagnostics;
+using OpenAI.Responses;
+
+namespace OpenAI;
+
+///
+/// Provides extension methods for
+/// to simplify the creation of AI agents that work with OpenAI services.
+///
+///
+/// These extensions bridge the gap between OpenAI SDK client objects and the Microsoft Extensions AI Agent framework,
+/// allowing developers to easily create AI agents that leverage OpenAI's chat completion and response services.
+/// The methods handle the conversion from OpenAI clients to instances and then wrap them
+/// in objects that implement the interface.
+///
+public static class OpenAIResponseClientExtensions
+{
+ ///
+ /// Creates an AI agent from an using the OpenAI Response API.
+ ///
+ /// The to use for the agent.
+ /// Optional system instructions that define the agent's behavior and personality.
+ /// Optional name for the agent for identification purposes.
+ /// Optional description of the agent's capabilities and purpose.
+ /// Optional collection of AI tools that the agent can use during conversations.
+ /// Optional logger factory for enabling logging within the agent.
+ /// An instance backed by the OpenAI Response service.
+ /// Thrown when is .
+ public static AIAgent CreateAIAgent(this OpenAIResponseClient client, string? instructions = null, string? name = null, string? description = null, IList? tools = null, ILoggerFactory? loggerFactory = null)
+ {
+ return client.CreateAIAgent(
+ new ChatClientAgentOptions()
+ {
+ Name = name,
+ Description = description,
+ Instructions = instructions,
+ ChatOptions = tools is null ? null : new ChatOptions()
+ {
+ Tools = tools,
+ }
+ },
+ loggerFactory);
+ }
+
+ ///
+ /// Creates an AI agent from an using the OpenAI Response API.
+ ///
+ /// The to use for the agent.
+ /// Full set of options to configure the agent.
+ /// Optional logger factory for enabling logging within the agent.
+ /// An instance backed by the OpenAI Response service.
+ /// Thrown when or is .
+ public static AIAgent CreateAIAgent(this OpenAIResponseClient client, ChatClientAgentOptions options, ILoggerFactory? loggerFactory = null)
+ {
+ Throw.IfNull(client);
+ Throw.IfNull(options);
+
+ var chatClient = client.AsIChatClient();
+ ChatClientAgent agent = new(chatClient, options, loggerFactory);
+ return agent;
+ }
+}