mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
Add some OpenAI and Foundry extension methods (#225)
* Add some OpenAI specific extensions * Update samples and extension methods * Apply suggestion from @Copilot Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Apply suggestion from @Copilot Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Add extension methods for creating agents using the Assistant API * Add orchestration sample * Add orchestration sample * Sample for the Foundry alignment document * Address code review feedback * Rename provider samples * Sample showing how to get an AI agent for Foundry SDK * Add OpenAI chat completion based implementation of AIAgent * Split OpenAI client extension methods by client type * Remove OpenAIClient extension methods * Rename AsRunnableAgent * Fix XML comments --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
Unverified
parent
42c7a59640
commit
624709e5d1
@@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// End-to-end sample showing how to use a custom <see cref="OpenAIChatClientAgent"/>.
|
||||
/// </summary>
|
||||
public sealed class Custom_OpenAIChatClientAgent(ITestOutputHelper output) : AgentSample(output)
|
||||
{
|
||||
/// <summary>
|
||||
/// This will create an instance of <see cref="MyOpenAIChatClientAgent"/> and run it.
|
||||
/// </summary>
|
||||
[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)
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@
|
||||
<RootNamespace>GettingStarted</RootNamespace>
|
||||
<OutputType>Library</OutputType>
|
||||
<UserSecretsId>5ee045b0-aea3-4f08-8d31-32d1a6f8fed0</UserSecretsId>
|
||||
<NoWarn>$(NoWarn);CA1707;CA1716;IDE0009;IDE1006;OPENAI001;</NoWarn>
|
||||
<NoWarn>$(NoWarn);CA1707;CA1716;IDE0009;IDE1006; OPENAI001;</NoWarn>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<InjectSharedSamples>true</InjectSharedSamples>
|
||||
<InjectSharedThrow>true</InjectSharedThrow>
|
||||
|
||||
@@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// Demonstrates how to use the <see cref="SequentialOrchestration"/> for
|
||||
/// executing multiple Foundry agents in sequence.
|
||||
/// </summary>
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// Demonstrates how to use the <see cref="SequentialOrchestration"/> for
|
||||
/// executing multiple heterogeneous agents in sequence.
|
||||
/// </summary>
|
||||
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)
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// Shows how to use <see cref="AIAgent"/> with Azure AI Persistent Agents.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Running "az login" command in terminal is required for authentication with Azure AI service.
|
||||
/// </remarks>
|
||||
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);
|
||||
}
|
||||
}
|
||||
+7
-9
@@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// End-to-end sample showing how to use <see cref="ChatClientAgent"/> with Azure OpenAI Chat Completion.
|
||||
/// </summary>
|
||||
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();
|
||||
+15
-22
@@ -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 @@ using OpenAI;
|
||||
namespace Providers;
|
||||
|
||||
/// <summary>
|
||||
/// End-to-end sample showing how to use <see cref="ChatClientAgent"/> with OpenAI Assistants.
|
||||
/// End-to-end sample showing how to use <see cref="AIAgent"/> with OpenAI Assistants.
|
||||
/// </summary>
|
||||
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 @@ public sealed class ChatClientAgent_With_OpenAIAssistant(ITestOutputHelper outpu
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
var assistantClient = openAIClient.GetAssistantClient();
|
||||
await assistantClient.DeleteThreadAsync(thread.Id);
|
||||
await assistantClient.DeleteAssistantAsync(assistantId);
|
||||
await assistantClient.DeleteAssistantAsync(agent.Id);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// End-to-end sample showing how to use <see cref="AIAgent"/> with OpenAI Chat Completion and Responses.
|
||||
/// </summary>
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
+9
-16
@@ -11,7 +11,7 @@ namespace Providers;
|
||||
/// <summary>
|
||||
/// End-to-end sample showing how to use <see cref="ChatClientAgent"/> with OpenAI Chat Completion.
|
||||
/// </summary>
|
||||
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.
|
||||
/// </summary>
|
||||
[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 @@ public sealed class ChatClientAgent_With_OpenAIResponsesChatCompletion(ITestOutp
|
||||
/// This will use in-memory messages to store the thread state.
|
||||
/// </summary>
|
||||
[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,
|
||||
-57
@@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// Shows how to use <see cref="ChatClientAgent"/> with Azure AI Persistent Agents.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Running "az login" command in terminal is required for authentication with Azure AI service.
|
||||
/// </remarks>
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// End-to-end sample showing how to use <see cref="ChatClientAgent"/> with OpenAI Chat Completion.
|
||||
/// </summary>
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -1,11 +1,11 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>$(ProjectsTargetFrameworks)</TargetFrameworks>
|
||||
<TargetFrameworks Condition="'$(Configuration)' == 'Debug'">$(ProjectsDebugTargetFrameworks)</TargetFrameworks>
|
||||
<VersionSuffix>alpha</VersionSuffix>
|
||||
<NoWarn>$(NoWarn);IDE0009;</NoWarn>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<NoWarn>$(NoWarn);IDE0009;</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
<Import Project="$(RepoRoot)/dotnet/nuget/nuget-package.props" />
|
||||
|
||||
+3
-3
@@ -17,14 +17,14 @@ internal static class PersistentAgentResponseExtensions
|
||||
/// <param name="persistentAgentsClient">The client used to interact with persistent agents. Cannot be <see langword="null"/>.</param>
|
||||
/// <param name="chatOptions">The default <see cref="ChatOptions"/> to use when interacting with the agent.</param>
|
||||
/// <returns>A <see cref="ChatClientAgent"/> instance that can be used to perform operations on the persistent agent.</returns>
|
||||
public static ChatClientAgent AsRunnableAgent(this Response<PersistentAgent> persistentAgentResponse, PersistentAgentsClient persistentAgentsClient, ChatOptions? chatOptions = null)
|
||||
public static ChatClientAgent AsAIAgent(this Response<PersistentAgent> 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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -34,7 +34,7 @@ internal static class PersistentAgentResponseExtensions
|
||||
/// <param name="persistentAgentsClient">The client used to interact with persistent agents. Cannot be <see langword="null"/>.</param>
|
||||
/// <param name="chatOptions">The default <see cref="ChatOptions"/> to use when interacting with the agent.</param>
|
||||
/// <returns>A <see cref="ChatClientAgent"/> instance that can be used to perform operations on the persistent agent.</returns>
|
||||
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)
|
||||
{
|
||||
+53
-2
@@ -19,7 +19,7 @@ public static class PersistentAgentsClientExtensions
|
||||
/// <param name="chatOptions">Options that should apply to all runs of the agent.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
|
||||
/// <returns>A <see cref="ChatClientAgent"/> instance that can be used to perform operations on the persistent agent.</returns>
|
||||
public static async Task<ChatClientAgent> GetRunnableAgentAsync(
|
||||
public static async Task<ChatClientAgent> GetAIAgentAsync(
|
||||
this PersistentAgentsClient persistentAgentsClient,
|
||||
string agentId,
|
||||
ChatOptions? chatOptions = null,
|
||||
@@ -36,6 +36,57 @@ public static class PersistentAgentsClientExtensions
|
||||
}
|
||||
|
||||
var persistentAgentResponse = await persistentAgentsClient.Administration.GetAgentAsync(agentId, cancellationToken).ConfigureAwait(false);
|
||||
return persistentAgentResponse.AsRunnableAgent(persistentAgentsClient, chatOptions);
|
||||
return persistentAgentResponse.AsAIAgent(persistentAgentsClient, chatOptions);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new server side agent using the provided <see cref="PersistentAgentsClient"/>.
|
||||
/// </summary>
|
||||
/// <param name="persistentAgentsClient">The <see cref="PersistentAgentsClient"/> to create the agent with.</param>
|
||||
/// <param name="model">The model to be used by the agent.</param>
|
||||
/// <param name="name">The name of the agent.</param>
|
||||
/// <param name="description">The description of the agent.</param>
|
||||
/// <param name="instructions">The instructions for the agent.</param>
|
||||
/// <param name="tools">The tools to be used by the agent.</param>
|
||||
/// <param name="toolResources">The resources for the tools.</param>
|
||||
/// <param name="temperature">The temperature setting for the agent.</param>
|
||||
/// <param name="topP">The top-p setting for the agent.</param>
|
||||
/// <param name="responseFormat">The response format for the agent.</param>
|
||||
/// <param name="metadata">The metadata for the agent.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
|
||||
/// <returns>A <see cref="ChatClientAgent"/> instance that can be used to perform operations on the newly created agent.</returns>
|
||||
public static async Task<ChatClientAgent> CreateAIAgentAsync(
|
||||
this PersistentAgentsClient persistentAgentsClient,
|
||||
string model,
|
||||
string? name = null,
|
||||
string? description = null,
|
||||
string? instructions = null,
|
||||
IEnumerable<ToolDefinition>? tools = null,
|
||||
ToolResources? toolResources = null,
|
||||
float? temperature = null,
|
||||
float? topP = null,
|
||||
BinaryData? responseFormat = null,
|
||||
IReadOnlyDictionary<string, string>? 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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// Provides extension methods for <see cref="AIAgent"/> to simplify interaction with OpenAI chat messages
|
||||
/// and return native OpenAI <see cref="ChatCompletion"/> responses.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 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 <see cref="ChatCompletion"/> objects directly from the agent's <see cref="AgentRunResponse"/>.
|
||||
/// </remarks>
|
||||
public static class AIAgentWithOpenAIExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Runs the AI agent with a single OpenAI chat message and returns the response as a native OpenAI <see cref="ChatCompletion"/>.
|
||||
/// </summary>
|
||||
/// <param name="agent">The AI agent to run.</param>
|
||||
/// <param name="message">The OpenAI chat message to send to the agent.</param>
|
||||
/// <param name="thread">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.</param>
|
||||
/// <param name="options">Optional parameters for agent invocation.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
|
||||
/// <returns>A <see cref="Task{ChatCompletion}"/> representing the asynchronous operation that returns a native OpenAI <see cref="ChatCompletion"/> response.</returns>
|
||||
/// <exception cref="ArgumentNullException">Thrown when <paramref name="agent"/> or <paramref name="message"/> is <see langword="null"/>.</exception>
|
||||
/// <exception cref="ArgumentException">Thrown when the agent's response cannot be converted to a <see cref="ChatCompletion"/>, typically when the underlying representation is not an OpenAI response.</exception>
|
||||
/// <exception cref="NotSupportedException">Thrown when the <paramref name="message"/> type is not supported by the message conversion method.</exception>
|
||||
/// <remarks>
|
||||
/// 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 <see cref="ChatCompletion"/> from the response using <see cref="AgentRunResponseExtensions.AsChatCompletion"/>.
|
||||
/// </remarks>
|
||||
public static async Task<ChatCompletion> 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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Runs the AI agent with a collection of OpenAI chat messages and returns the response as a native OpenAI <see cref="ChatCompletion"/>.
|
||||
/// </summary>
|
||||
/// <param name="agent">The AI agent to run.</param>
|
||||
/// <param name="messages">The collection of OpenAI chat messages to send to the agent.</param>
|
||||
/// <param name="thread">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.</param>
|
||||
/// <param name="options">Optional parameters for agent invocation.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
|
||||
/// <returns>A <see cref="Task{ChatCompletion}"/> representing the asynchronous operation that returns a native OpenAI <see cref="ChatCompletion"/> response.</returns>
|
||||
/// <exception cref="ArgumentNullException">Thrown when <paramref name="agent"/> or <paramref name="messages"/> is <see langword="null"/>.</exception>
|
||||
/// <exception cref="ArgumentException">Thrown when the agent's response cannot be converted to a <see cref="ChatCompletion"/>, typically when the underlying representation is not an OpenAI response.</exception>
|
||||
/// <exception cref="NotSupportedException">Thrown when any message in <paramref name="messages"/> has a type that is not supported by the message conversion method.</exception>
|
||||
/// <remarks>
|
||||
/// This method converts each OpenAI chat message to the Microsoft Extensions AI format using <see cref="AsChatMessages"/>,
|
||||
/// runs the agent with the converted message collection, and then extracts the native OpenAI <see cref="ChatCompletion"/> from the response using <see cref="AgentRunResponseExtensions.AsChatCompletion"/>.
|
||||
/// </remarks>
|
||||
public static async Task<ChatCompletion> RunAsync(this AIAgent agent, IEnumerable<OpenAI.Chat.ChatMessage> 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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a sequence of <see cref="Microsoft.Extensions.AI.ChatMessage"/> instances from the specified OpenAI input messages.
|
||||
/// </summary>
|
||||
/// <param name="messages">The OpenAI input messages to convert.</param>
|
||||
/// <returns>A sequence of Microsoft Extensions AI chat messages converted from the OpenAI messages.</returns>
|
||||
/// <exception cref="ArgumentNullException">Thrown when <paramref name="messages"/> is <see langword="null"/>.</exception>
|
||||
/// <exception cref="NotSupportedException">Thrown when a message type is encountered that cannot be converted.</exception>
|
||||
/// <remarks>
|
||||
/// This method supports conversion of the following OpenAI message types:
|
||||
/// <list type="bullet">
|
||||
/// <item><description><see cref="AssistantChatMessage"/></description></item>
|
||||
/// <item><description><see cref="DeveloperChatMessage"/></description></item>
|
||||
/// <item><description><see cref="FunctionChatMessage"/> (obsolete)</description></item>
|
||||
/// <item><description><see cref="SystemChatMessage"/></description></item>
|
||||
/// <item><description><see cref="ToolChatMessage"/></description></item>
|
||||
/// <item><description><see cref="UserChatMessage"/></description></item>
|
||||
/// </list>
|
||||
/// </remarks>
|
||||
internal static IEnumerable<Microsoft.Extensions.AI.ChatMessage> AsChatMessages(this IEnumerable<OpenAI.Chat.ChatMessage> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts an OpenAI chat message to a Microsoft Extensions AI <see cref="Microsoft.Extensions.AI.ChatMessage"/>.
|
||||
/// </summary>
|
||||
/// <param name="chatMessage">The OpenAI chat message to convert.</param>
|
||||
/// <returns>A <see cref="Microsoft.Extensions.AI.ChatMessage"/> equivalent of the input OpenAI message.</returns>
|
||||
/// <exception cref="ArgumentNullException">Thrown when <paramref name="chatMessage"/> is <see langword="null"/>.</exception>
|
||||
/// <exception cref="NotSupportedException">Thrown when the <paramref name="chatMessage"/> type is not supported for conversion.</exception>
|
||||
/// <remarks>
|
||||
/// 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.
|
||||
/// </remarks>
|
||||
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.")
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts OpenAI chat message content to Microsoft Extensions AI content items.
|
||||
/// </summary>
|
||||
/// <param name="content">The OpenAI chat message content to convert.</param>
|
||||
/// <returns>A sequence of <see cref="AIContent"/> items converted from the OpenAI content.</returns>
|
||||
/// <remarks>
|
||||
/// This method supports conversion of the following OpenAI content part types:
|
||||
/// <list type="bullet">
|
||||
/// <item><description>Text content (converted to <see cref="TextContent"/>)</description></item>
|
||||
/// <item><description>Refusal content (converted to <see cref="TextContent"/>)</description></item>
|
||||
/// <item><description>Image content (converted to <see cref="DataContent"/> or <see cref="UriContent"/>)</description></item>
|
||||
/// <item><description>Input audio content (converted to <see cref="DataContent"/>)</description></item>
|
||||
/// <item><description>File content (converted to <see cref="DataContent"/>)</description></item>
|
||||
/// </list>
|
||||
/// </remarks>
|
||||
private static IEnumerable<AIContent> 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.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts OpenAI chat message content to text.
|
||||
/// </summary>
|
||||
/// <param name="content">The OpenAI chat message content to convert.</param>
|
||||
/// <returns>A string created from the text and refusal parts of the OpenAI content.</returns>
|
||||
/// <remarks>
|
||||
/// Using when converting OpenAI For <c>tool</c> messages, the contents can only be of type <c>text</c>.
|
||||
/// </remarks>
|
||||
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();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts an OpenAI <see cref="AssistantChatMessage"/> to a Microsoft Extensions AI <see cref="Microsoft.Extensions.AI.ChatMessage"/>.
|
||||
/// </summary>
|
||||
/// <param name="assistantMessage">The OpenAI assistant message to convert.</param>
|
||||
/// <returns>A Microsoft Extensions AI chat message with assistant role.</returns>
|
||||
/// <remarks>
|
||||
/// This method converts the assistant message content using <see cref="AsAIContent"/> and preserves
|
||||
/// the participant name as the author name in the resulting message.
|
||||
/// </remarks>
|
||||
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
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts an OpenAI <see cref="DeveloperChatMessage"/> to a Microsoft Extensions AI <see cref="Microsoft.Extensions.AI.ChatMessage"/>.
|
||||
/// </summary>
|
||||
/// <param name="developerMessage">The OpenAI developer message to convert.</param>
|
||||
/// <returns>A Microsoft Extensions AI chat message with system role.</returns>
|
||||
/// <remarks>
|
||||
/// Developer messages are treated as system messages in the Microsoft Extensions AI framework.
|
||||
/// The participant name is preserved as the author name.
|
||||
/// </remarks>
|
||||
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
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts an OpenAI <see cref="SystemChatMessage"/> to a Microsoft Extensions AI <see cref="Microsoft.Extensions.AI.ChatMessage"/>.
|
||||
/// </summary>
|
||||
/// <param name="systemMessage">The OpenAI system message to convert.</param>
|
||||
/// <returns>A Microsoft Extensions AI chat message with system role.</returns>
|
||||
/// <remarks>
|
||||
/// This method converts the system message content using <see cref="AsAIContent"/> and preserves
|
||||
/// the participant name as the author name in the resulting message.
|
||||
/// </remarks>
|
||||
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
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts an OpenAI <see cref="ToolChatMessage"/> to a Microsoft Extensions AI <see cref="Microsoft.Extensions.AI.ChatMessage"/>.
|
||||
/// </summary>
|
||||
/// <param name="toolMessage">The OpenAI tool message to convert.</param>
|
||||
/// <returns>A Microsoft Extensions AI chat message with tool role.</returns>
|
||||
/// <remarks>
|
||||
/// This method converts tool message content using <see cref="AsAIContent"/> and includes the tool call ID
|
||||
/// in the resulting message's additional properties for traceability.
|
||||
/// </remarks>
|
||||
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 }
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts an OpenAI <see cref="UserChatMessage"/> to a Microsoft Extensions AI <see cref="Microsoft.Extensions.AI.ChatMessage"/>.
|
||||
/// </summary>
|
||||
/// <param name="userMessage">The OpenAI user message to convert.</param>
|
||||
/// <returns>A Microsoft Extensions AI chat message with user role.</returns>
|
||||
/// <remarks>
|
||||
/// This method converts the user message content using <see cref="AsAIContent"/> and preserves
|
||||
/// the participant name as the author name in the resulting message.
|
||||
/// </remarks>
|
||||
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
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// Provides extension methods for <see cref="AgentRunResponse"/> to extract native OpenAI response objects
|
||||
/// from the Microsoft Extensions AI Agent framework responses.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 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 <see cref="AgentRunResponse.RawRepresentation"/> property, providing a bridge between
|
||||
/// the Microsoft Extensions AI framework and the native OpenAI SDK types.
|
||||
/// </remarks>
|
||||
public static class AgentRunResponseExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Extracts a native OpenAI <see cref="ChatCompletion"/> object from an <see cref="AgentRunResponse"/>.
|
||||
/// </summary>
|
||||
/// <param name="agentResponse">The agent response containing the raw OpenAI representation.</param>
|
||||
/// <returns>The native OpenAI <see cref="ChatCompletion"/> object.</returns>
|
||||
/// <exception cref="ArgumentNullException">Thrown when <paramref name="agentResponse"/> is <see langword="null"/>.</exception>
|
||||
/// <exception cref="ArgumentException">
|
||||
/// Thrown when the <see cref="AgentRunResponse.RawRepresentation"/> is not a <see cref="ChatCompletion"/> 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.
|
||||
/// </exception>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// This method provides access to the native OpenAI <see cref="ChatCompletion"/> 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.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
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");
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -4,7 +4,7 @@
|
||||
<TargetFrameworks>$(ProjectsTargetFrameworks)</TargetFrameworks>
|
||||
<TargetFrameworks Condition="'$(Configuration)' == 'Debug'">$(ProjectsDebugTargetFrameworks)</TargetFrameworks>
|
||||
<VersionSuffix>alpha</VersionSuffix>
|
||||
<NoWarn>$(NoWarn);IDE0009;</NoWarn>
|
||||
<NoWarn>$(NoWarn);IDE0009;OPENAI001;</NoWarn>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<InjectSharedThrow>true</InjectSharedThrow>
|
||||
</PropertyGroup>
|
||||
|
||||
@@ -49,7 +49,7 @@ public sealed class NewOpenAIAssistantChatClient : IChatClient
|
||||
private IReadOnlyList<ToolDefinition>? _assistantTools;
|
||||
|
||||
/// <summary>Initializes a new instance of the <see cref="OpenAIAssistantChatClient"/> class for the specified <see cref="AssistantClient"/>.</summary>
|
||||
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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// Provides extension methods for OpenAI <see cref="AssistantClient"/>
|
||||
/// to simplify the creation of AI agents that work with OpenAI services.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// These extensions bridge the gap between OpenAI SDK client objects and the Microsoft 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 <see cref="IChatClient"/> instances and then wrap them
|
||||
/// in <see cref="ChatClientAgent"/> objects that implement the <see cref="AIAgent"/> interface.
|
||||
/// </remarks>
|
||||
public static class OpenAIAssistantClientExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates an AI agent from an <see cref="AssistantClient"/> using the OpenAI Assistant API.
|
||||
/// </summary>
|
||||
/// <param name="client">The OpenAI <see cref="AssistantClient" /> to use for the agent.</param>
|
||||
/// <param name="model">The model identifier to use (e.g., "gpt-4").</param>
|
||||
/// <param name="instructions">Optional system instructions that define the agent's behavior and personality.</param>
|
||||
/// <param name="name">Optional name for the agent for identification purposes.</param>
|
||||
/// <param name="description">Optional description of the agent's capabilities and purpose.</param>
|
||||
/// <param name="tools">Optional collection of AI tools that the agent can use during conversations.</param>
|
||||
/// <param name="loggerFactory">Optional logger factory for enabling logging within the agent.</param>
|
||||
/// <returns>An <see cref="AIAgent"/> instance backed by the OpenAI Assistant service.</returns>
|
||||
/// <exception cref="ArgumentNullException">Thrown when <paramref name="client"/> or <paramref name="model"/> is <see langword="null"/>.</exception>
|
||||
/// <exception cref="ArgumentException">Thrown when <paramref name="model"/> is empty or whitespace.</exception>
|
||||
public static AIAgent CreateAIAgent(this AssistantClient client, string model, string? instructions = null, string? name = null, string? description = null, IList<AITool>? 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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates an AI agent from an <see cref="AssistantClient"/> using the OpenAI Assistant API.
|
||||
/// </summary>
|
||||
/// <param name="client">The OpenAI <see cref="AssistantClient" /> to use for the agent.</param>
|
||||
/// <param name="model">The model identifier to use (e.g., "gpt-4").</param>
|
||||
/// <param name="options">Full set of options to configure the agent.</param>
|
||||
/// <param name="loggerFactory">Optional logger factory for enabling logging within the agent.</param>
|
||||
/// <returns>An <see cref="AIAgent"/> instance backed by the OpenAI Assistant service.</returns>
|
||||
/// <exception cref="ArgumentNullException">Thrown when <paramref name="client"/> or <paramref name="model"/> or <paramref name="options"/> is <see langword="null"/>.</exception>
|
||||
/// <exception cref="ArgumentException">Thrown when <paramref name="model"/> is empty or whitespace.</exception>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates an AI agent from an <see cref="AssistantClient"/> using the OpenAI Assistant API.
|
||||
/// </summary>
|
||||
/// <param name="client">The OpenAI <see cref="AssistantClient" /> to use for the agent.</param>
|
||||
/// <param name="model">The model identifier to use (e.g., "gpt-4").</param>
|
||||
/// <param name="instructions">Optional system instructions that define the agent's behavior and personality.</param>
|
||||
/// <param name="name">Optional name for the agent for identification purposes.</param>
|
||||
/// <param name="description">Optional description of the agent's capabilities and purpose.</param>
|
||||
/// <param name="tools">Optional collection of AI tools that the agent can use during conversations.</param>
|
||||
/// <param name="loggerFactory">Optional logger factory for enabling logging within the agent.</param>
|
||||
/// <returns>An <see cref="AIAgent"/> instance backed by the OpenAI Assistant service.</returns>
|
||||
/// <exception cref="ArgumentNullException">Thrown when <paramref name="client"/> or <paramref name="model"/> is <see langword="null"/>.</exception>
|
||||
/// <exception cref="ArgumentException">Thrown when <paramref name="model"/> is empty or whitespace.</exception>
|
||||
public static async Task<AIAgent> CreateAIAgentAsync(this AssistantClient client, string model, string? instructions = null, string? name = null, string? description = null, IList<AITool>? 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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates an AI agent from an <see cref="AssistantClient"/> using the OpenAI Assistant API.
|
||||
/// </summary>
|
||||
/// <param name="client">The OpenAI <see cref="AssistantClient" /> to use for the agent.</param>
|
||||
/// <param name="model">The model identifier to use (e.g., "gpt-4").</param>
|
||||
/// <param name="options">Full set of options to configure the agent.</param>
|
||||
/// <param name="loggerFactory">Optional logger factory for enabling logging within the agent.</param>
|
||||
/// <returns>An <see cref="AIAgent"/> instance backed by the OpenAI Assistant service.</returns>
|
||||
/// <exception cref="ArgumentNullException">Thrown when <paramref name="client"/> or <paramref name="model"/> is <see langword="null"/>.</exception>
|
||||
/// <exception cref="ArgumentException">Thrown when <paramref name="model"/> is empty or whitespace.</exception>
|
||||
public static async Task<AIAgent> 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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// OpenAI chat completion based implementation of <see cref="AIAgent"/>.
|
||||
/// </summary>
|
||||
public class OpenAIChatClientAgent : AIAgent
|
||||
{
|
||||
private readonly ChatClientAgent _chatClientAgent;
|
||||
|
||||
/// <summary>
|
||||
/// Initialize an instance of <see cref="OpenAIChatClientAgent"/>
|
||||
/// </summary>
|
||||
/// <param name="client">Instance of <see cref="ChatClient"/></param>
|
||||
/// <param name="instructions">Optional instructions for the agent.</param>
|
||||
/// <param name="name">Optional name for the agent.</param>
|
||||
/// <param name="description">Optional description for the agent.</param>
|
||||
/// <param name="loggerFactory">Optional instance of <see cref="ILoggerFactory"/></param>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initialize an instance of <see cref="OpenAIChatClientAgent"/>
|
||||
/// </summary>
|
||||
/// <param name="client">Instance of <see cref="ChatClient"/></param>
|
||||
/// <param name="options">Options to create the agent.</param>
|
||||
/// <param name="loggerFactory">Optional instance of <see cref="ILoggerFactory"/></param>
|
||||
public OpenAIChatClientAgent(ChatClient client, ChatClientAgentOptions options, ILoggerFactory? loggerFactory = null)
|
||||
{
|
||||
Throw.IfNull(client);
|
||||
|
||||
var chatClient = client.AsIChatClient();
|
||||
this._chatClientAgent = new(chatClient, options, loggerFactory);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Run the agent with the provided message and arguments.
|
||||
/// </summary>
|
||||
/// <param name="messages">The messages to pass to the agent.</param>
|
||||
/// <param name="thread">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.</param>
|
||||
/// <param name="options">Optional parameters for agent invocation.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
|
||||
/// <returns>A <see cref="ChatCompletion"/> containing the list of <see cref="ChatMessage"/> items.</returns>
|
||||
public virtual async Task<ChatCompletion> RunAsync(IEnumerable<ChatMessage> 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;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public sealed override AgentThread GetNewThread()
|
||||
{
|
||||
return this._chatClientAgent.GetNewThread();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public sealed override Task<AgentRunResponse> RunAsync(IReadOnlyCollection<Microsoft.Extensions.AI.ChatMessage> messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return this._chatClientAgent.RunAsync(messages, thread, options, cancellationToken);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public sealed override IAsyncEnumerable<AgentRunResponseUpdate> RunStreamingAsync(IReadOnlyCollection<Microsoft.Extensions.AI.ChatMessage> messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return this._chatClientAgent.RunStreamingAsync(messages, thread, options, cancellationToken);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// Provides extension methods for <see cref="ChatClient"/>
|
||||
/// to simplify the creation of AI agents that work with OpenAI services.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// These extensions bridge the gap between OpenAI SDK client objects and the Microsoft 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 <see cref="IChatClient"/> instances and then wrap them
|
||||
/// in <see cref="ChatClientAgent"/> objects that implement the <see cref="AIAgent"/> interface.
|
||||
/// </remarks>
|
||||
public static class OpenAIChatClientExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates an AI agent from an <see cref="ChatClient"/> using the OpenAI Chat Completion API.
|
||||
/// </summary>
|
||||
/// <param name="client">The OpenAI <see cref="ChatClient"/> to use for the agent.</param>
|
||||
/// <param name="instructions">Optional system instructions that define the agent's behavior and personality.</param>
|
||||
/// <param name="name">Optional name for the agent for identification purposes.</param>
|
||||
/// <param name="description">Optional description of the agent's capabilities and purpose.</param>
|
||||
/// <param name="tools">Optional collection of AI tools that the agent can use during conversations.</param>
|
||||
/// <param name="loggerFactory">Optional logger factory for enabling logging within the agent.</param>
|
||||
/// <returns>An <see cref="AIAgent"/> instance backed by the OpenAI Chat Completion service.</returns>
|
||||
/// <exception cref="ArgumentNullException">Thrown when <paramref name="client"/> is <see langword="null"/>.</exception>
|
||||
public static AIAgent CreateAIAgent(this ChatClient client, string? instructions = null, string? name = null, string? description = null, IList<AITool>? 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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates an AI agent from an <see cref="ChatClient"/> using the OpenAI Chat Completion API.
|
||||
/// </summary>
|
||||
/// <param name="client">The OpenAI <see cref="ChatClient"/> to use for the agent.</param>
|
||||
/// <param name="options">Full set of options to configure the agent.</param>
|
||||
/// <param name="loggerFactory">Optional logger factory for enabling logging within the agent.</param>
|
||||
/// <returns>An <see cref="AIAgent"/> instance backed by the OpenAI Chat Completion service.</returns>
|
||||
/// <exception cref="ArgumentNullException">Thrown when <paramref name="client"/> or <paramref name="options"/> is <see langword="null"/>.</exception>
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// Provides extension methods for <see cref="OpenAIResponseClient"/>
|
||||
/// to simplify the creation of AI agents that work with OpenAI services.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// These extensions bridge the gap between OpenAI SDK client objects and the Microsoft 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 <see cref="IChatClient"/> instances and then wrap them
|
||||
/// in <see cref="ChatClientAgent"/> objects that implement the <see cref="AIAgent"/> interface.
|
||||
/// </remarks>
|
||||
public static class OpenAIResponseClientExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates an AI agent from an <see cref="OpenAIResponseClient"/> using the OpenAI Response API.
|
||||
/// </summary>
|
||||
/// <param name="client">The <see cref="OpenAIResponseClient" /> to use for the agent.</param>
|
||||
/// <param name="instructions">Optional system instructions that define the agent's behavior and personality.</param>
|
||||
/// <param name="name">Optional name for the agent for identification purposes.</param>
|
||||
/// <param name="description">Optional description of the agent's capabilities and purpose.</param>
|
||||
/// <param name="tools">Optional collection of AI tools that the agent can use during conversations.</param>
|
||||
/// <param name="loggerFactory">Optional logger factory for enabling logging within the agent.</param>
|
||||
/// <returns>An <see cref="AIAgent"/> instance backed by the OpenAI Response service.</returns>
|
||||
/// <exception cref="ArgumentNullException">Thrown when <paramref name="client"/> is <see langword="null"/>.</exception>
|
||||
public static AIAgent CreateAIAgent(this OpenAIResponseClient client, string? instructions = null, string? name = null, string? description = null, IList<AITool>? 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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates an AI agent from an <see cref="OpenAIResponseClient"/> using the OpenAI Response API.
|
||||
/// </summary>
|
||||
/// <param name="client">The <see cref="OpenAIResponseClient" /> to use for the agent.</param>
|
||||
/// <param name="options">Full set of options to configure the agent.</param>
|
||||
/// <param name="loggerFactory">Optional logger factory for enabling logging within the agent.</param>
|
||||
/// <returns>An <see cref="AIAgent"/> instance backed by the OpenAI Response service.</returns>
|
||||
/// <exception cref="ArgumentNullException">Thrown when <paramref name="client"/> or <paramref name="options"/> is <see langword="null"/>.</exception>
|
||||
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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user