test
Some checks failed
CodeQL / Analyze (csharp) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
dotnet-build-and-test / paths-filter (push) Has been cancelled
dotnet-build-and-test / dotnet-build-and-test (Debug, windows-latest, net9.0) (push) Has been cancelled
dotnet-build-and-test / dotnet-build-and-test (Release, integration, true, ubuntu-latest, net10.0) (push) Has been cancelled
dotnet-build-and-test / dotnet-build-and-test (Release, integration, true, windows-latest, net472) (push) Has been cancelled
dotnet-build-and-test / dotnet-build-and-test (Release, ubuntu-latest, net8.0) (push) Has been cancelled
dotnet-build-and-test / dotnet-build-and-test-check (push) Has been cancelled
Python - Merge - Tests / paths-filter (push) Has been cancelled
Python - Merge - Tests / Python Tests - Core (integration, ubuntu-latest, 3.10) (push) Has been cancelled
Python - Merge - Tests / Python Tests - Azure AI (integration, ubuntu-latest, 3.10) (push) Has been cancelled
Python - Merge - Tests / python-integration-tests-check (push) Has been cancelled
Python - Lab Tests / paths-filter (push) Has been cancelled
Python - Lab Tests / Python Lab Tests (ubuntu-latest, 3.10) (push) Has been cancelled
Python - Lab Tests / Python Lab Tests (ubuntu-latest, 3.11) (push) Has been cancelled
Python - Lab Tests / Python Lab Tests (ubuntu-latest, 3.12) (push) Has been cancelled
Python - Lab Tests / Python Lab Tests (ubuntu-latest, 3.13) (push) Has been cancelled
Python - Lab Tests / Python Lab Tests (ubuntu-latest, 3.14) (push) Has been cancelled
Python - Lab Tests / Python Lab Tests (windows-latest, 3.10) (push) Has been cancelled
Python - Lab Tests / Python Lab Tests (windows-latest, 3.11) (push) Has been cancelled
Python - Lab Tests / Python Lab Tests (windows-latest, 3.12) (push) Has been cancelled
Python - Lab Tests / Python Lab Tests (windows-latest, 3.13) (push) Has been cancelled
Python - Lab Tests / Python Lab Tests (windows-latest, 3.14) (push) Has been cancelled
Check .md links / markdown-link-check (push) Has been cancelled

This commit is contained in:
2026-01-24 03:05:12 +11:00
parent f78f2388b3
commit 539852f81c
2584 changed files with 287471 additions and 0 deletions

View File

@@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFrameworks>net10.0</TargetFrameworks>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\..\src\Microsoft.Agents.AI.OpenAI\Microsoft.Agents.AI.OpenAI.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,31 @@
// Copyright (c) Microsoft. All rights reserved.
// This sample shows how to create and use a simple AI agent with OpenAI as the backend.
using System.ClientModel;
using Microsoft.Agents.AI;
using OpenAI;
using OpenAI.Chat;
var apiKey = Environment.GetEnvironmentVariable("OPENAI_API_KEY") ?? throw new InvalidOperationException("OPENAI_API_KEY is not set.");
var model = Environment.GetEnvironmentVariable("OPENAI_MODEL") ?? "gpt-4o-mini";
AIAgent agent = new OpenAIClient(apiKey)
.GetChatClient(model)
.AsAIAgent(instructions: "You are good at telling jokes.", name: "Joker");
UserChatMessage chatMessage = new("Tell me a joke about a pirate.");
// Invoke the agent and output the text result.
ChatCompletion chatCompletion = await agent.RunAsync([chatMessage]);
Console.WriteLine(chatCompletion.Content.Last().Text);
// Invoke the agent with streaming support.
AsyncCollectionResult<StreamingChatCompletionUpdate> completionUpdates = agent.RunStreamingAsync([chatMessage]);
await foreach (StreamingChatCompletionUpdate completionUpdate in completionUpdates)
{
if (completionUpdate.ContentUpdate.Count > 0)
{
Console.WriteLine(completionUpdate.ContentUpdate[0].Text);
}
}

View File

@@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFrameworks>net10.0</TargetFrameworks>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\..\src\Microsoft.Agents.AI.OpenAI\Microsoft.Agents.AI.OpenAI.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,54 @@
// Copyright (c) Microsoft. All rights reserved.
// This sample shows how to create and use an AI agent with reasoning capabilities.
using Microsoft.Agents.AI;
using Microsoft.Extensions.AI;
using OpenAI;
using OpenAI.Responses;
var apiKey = Environment.GetEnvironmentVariable("OPENAI_API_KEY") ?? throw new InvalidOperationException("OPENAI_API_KEY is not set.");
var model = Environment.GetEnvironmentVariable("OPENAI_MODEL") ?? "gpt-5";
var client = new OpenAIClient(apiKey)
.GetResponsesClient(model)
.AsIChatClient().AsBuilder()
.ConfigureOptions(o =>
{
o.RawRepresentationFactory = _ => new CreateResponseOptions()
{
ReasoningOptions = new()
{
ReasoningEffortLevel = ResponseReasoningEffortLevel.Medium,
// Verbosity requires OpenAI verified Organization
ReasoningSummaryVerbosity = ResponseReasoningSummaryVerbosity.Detailed
}
};
}).Build();
AIAgent agent = new ChatClientAgent(client);
Console.WriteLine("1. Non-streaming:");
var response = await agent.RunAsync("Solve this problem step by step: If a train travels 60 miles per hour and needs to cover 180 miles, how long will the journey take? Show your reasoning.");
Console.WriteLine(response.Text);
Console.WriteLine("Token usage:");
Console.WriteLine($"Input: {response.Usage?.InputTokenCount}, Output: {response.Usage?.OutputTokenCount}, {string.Join(", ", response.Usage?.AdditionalCounts ?? [])}");
Console.WriteLine();
Console.WriteLine("2. Streaming");
await foreach (var update in agent.RunStreamingAsync("Explain the theory of relativity in simple terms."))
{
foreach (var item in update.Contents)
{
if (item is TextReasoningContent reasoningContent)
{
Console.Write($"\e[97m{reasoningContent.Text}\e[0m");
}
else if (item is TextContent textContent)
{
Console.Write(textContent.Text);
}
}
}

View File

@@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFrameworks>net10.0</TargetFrameworks>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\..\src\Microsoft.Agents.AI.OpenAI\Microsoft.Agents.AI.OpenAI.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,96 @@
// Copyright (c) Microsoft. All rights reserved.
using Microsoft.Agents.AI;
using Microsoft.Extensions.AI;
using Microsoft.Extensions.Logging;
using OpenAI.Chat;
using ChatMessage = OpenAI.Chat.ChatMessage;
namespace OpenAIChatClientSample;
/// <summary>
/// Provides an <see cref="AIAgent"/> backed by an OpenAI chat completion implementation.
/// </summary>
public class OpenAIChatClientAgent : DelegatingAIAgent
{
/// <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) :
this(client, new()
{
Name = name,
Description = description,
ChatOptions = new ChatOptions() { 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) :
base(new ChatClientAgent((client ?? throw new ArgumentNullException(nameof(client))).AsIChatClient(), 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);
return response.AsOpenAIChatCompletion();
}
/// <summary>
/// Run the agent streaming 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 IAsyncEnumerable<StreamingChatCompletionUpdate> RunStreamingAsync(
IEnumerable<ChatMessage> messages,
AgentThread? thread = null,
AgentRunOptions? options = null,
CancellationToken cancellationToken = default)
{
var response = this.RunStreamingAsync(messages.AsChatMessages(), thread, options, cancellationToken);
return response.AsChatResponseUpdatesAsync().AsOpenAIStreamingChatCompletionUpdatesAsync(cancellationToken);
}
/// <inheritdoc/>
protected sealed override Task<AgentResponse> RunCoreAsync(IEnumerable<Microsoft.Extensions.AI.ChatMessage> messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) =>
base.RunCoreAsync(messages, thread, options, cancellationToken);
/// <inheritdoc/>
protected override IAsyncEnumerable<AgentResponseUpdate> RunCoreStreamingAsync(IEnumerable<Microsoft.Extensions.AI.ChatMessage> messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) =>
base.RunCoreStreamingAsync(messages, thread, options, cancellationToken);
}

View File

@@ -0,0 +1,32 @@
// Copyright (c) Microsoft. All rights reserved.
// This sample demonstrates how to create an AI agent directly from an OpenAI.Chat.ChatClient instance using OpenAIChatClientAgent.
using OpenAI;
using OpenAI.Chat;
using OpenAIChatClientSample;
string apiKey = Environment.GetEnvironmentVariable("OPENAI_API_KEY") ?? throw new InvalidOperationException("OPENAI_API_KEY is not set.");
string model = Environment.GetEnvironmentVariable("OPENAI_MODEL") ?? "gpt-4o-mini";
// Create a ChatClient directly from OpenAIClient
ChatClient chatClient = new OpenAIClient(apiKey).GetChatClient(model);
// Create an agent directly from the ChatClient using OpenAIChatClientAgent
OpenAIChatClientAgent agent = new(chatClient, instructions: "You are good at telling jokes.", name: "Joker");
UserChatMessage chatMessage = new("Tell me a joke about a pirate.");
// Invoke the agent and output the text result.
ChatCompletion chatCompletion = await agent.RunAsync([chatMessage]);
Console.WriteLine(chatCompletion.Content.Last().Text);
// Invoke the agent with streaming support.
IAsyncEnumerable<StreamingChatCompletionUpdate> completionUpdates = agent.RunStreamingAsync([chatMessage]);
await foreach (StreamingChatCompletionUpdate completionUpdate in completionUpdates)
{
if (completionUpdate.ContentUpdate.Count > 0)
{
Console.WriteLine(completionUpdate.ContentUpdate[0].Text);
}
}

View File

@@ -0,0 +1,22 @@
# Creating an Agent from a ChatClient
This sample demonstrates how to create an AI agent directly from an `OpenAI.Chat.ChatClient` instance using the `OpenAIChatClientAgent` class.
## What This Sample Shows
- **Direct ChatClient Creation**: Shows how to create an `OpenAI.Chat.ChatClient` from `OpenAI.OpenAIClient` and then use it to instantiate an agent
- **OpenAIChatClientAgent**: Demonstrates using the OpenAI SDK primitives instead of the ones from Microsoft.Extensions.AI and Microsoft.Agents.AI abstractions
- **Full Agent Capabilities**: Shows both regular and streaming invocation of the agent
## Running the Sample
1. Set the required environment variables:
```bash
set OPENAI_API_KEY=your_api_key_here
set OPENAI_MODEL=gpt-4o-mini
```
2. Run the sample:
```bash
dotnet run
```

View File

@@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFrameworks>net10.0</TargetFrameworks>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\..\src\Microsoft.Agents.AI.OpenAI\Microsoft.Agents.AI.OpenAI.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,114 @@
// Copyright (c) Microsoft. All rights reserved.
using System.Runtime.CompilerServices;
using Microsoft.Agents.AI;
using Microsoft.Extensions.AI;
using Microsoft.Extensions.Logging;
using OpenAI.Responses;
namespace OpenAIResponseClientSample;
/// <summary>
/// Provides an <see cref="AIAgent"/> backed by an OpenAI Responses implementation.
/// </summary>
public class OpenAIResponseClientAgent : DelegatingAIAgent
{
/// <summary>
/// Initialize an instance of <see cref="OpenAIResponseClientAgent"/>.
/// </summary>
/// <param name="client">Instance of <see cref="ResponsesClient"/></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 OpenAIResponseClientAgent(
ResponsesClient client,
string? instructions = null,
string? name = null,
string? description = null,
ILoggerFactory? loggerFactory = null) :
this(client, new()
{
Name = name,
Description = description,
ChatOptions = new ChatOptions() { Instructions = instructions },
}, loggerFactory)
{
}
/// <summary>
/// Initialize an instance of <see cref="OpenAIResponseClientAgent"/>.
/// </summary>
/// <param name="client">Instance of <see cref="ResponsesClient"/></param>
/// <param name="options">Options to create the agent.</param>
/// <param name="loggerFactory">Optional instance of <see cref="ILoggerFactory"/></param>
public OpenAIResponseClientAgent(
ResponsesClient client, ChatClientAgentOptions options, ILoggerFactory? loggerFactory = null) :
base(new ChatClientAgent((client ?? throw new ArgumentNullException(nameof(client))).AsIChatClient(), 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="ResponseResult"/> containing the list of <see cref="ChatMessage"/> items.</returns>
public virtual async Task<ResponseResult> RunAsync(
IEnumerable<ResponseItem> messages,
AgentThread? thread = null,
AgentRunOptions? options = null,
CancellationToken cancellationToken = default)
{
var response = await this.RunAsync(messages.AsChatMessages(), thread, options, cancellationToken).ConfigureAwait(false);
return response.AsOpenAIResponse();
}
/// <summary>
/// Run the agent streaming 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="ResponseResult"/> containing the list of <see cref="ChatMessage"/> items.</returns>
public virtual async IAsyncEnumerable<StreamingResponseUpdate> RunStreamingAsync(
IEnumerable<ResponseItem> messages,
AgentThread? thread = null,
AgentRunOptions? options = null,
[EnumeratorCancellation] CancellationToken cancellationToken = default)
{
var response = this.RunStreamingAsync(messages.AsChatMessages(), thread, options, cancellationToken);
await foreach (var update in response.ConfigureAwait(false))
{
switch (update.RawRepresentation)
{
case StreamingResponseUpdate rawUpdate:
yield return rawUpdate;
break;
case ChatResponseUpdate { RawRepresentation: StreamingResponseUpdate rawUpdate }:
yield return rawUpdate;
break;
default:
// TODO: The OpenAI library does not currently expose model factory methods for creating
// StreamingResponseUpdates. We are thus unable to manufacture such instances when there isn't
// already one in the update and instead skip them.
break;
}
}
}
/// <inheritdoc/>
protected sealed override Task<AgentResponse> RunCoreAsync(IEnumerable<ChatMessage> messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) =>
base.RunCoreAsync(messages, thread, options, cancellationToken);
/// <inheritdoc/>
protected sealed override IAsyncEnumerable<AgentResponseUpdate> RunCoreStreamingAsync(IEnumerable<ChatMessage> messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) =>
base.RunCoreStreamingAsync(messages, thread, options, cancellationToken);
}

View File

@@ -0,0 +1,32 @@
// Copyright (c) Microsoft. All rights reserved.
// This sample demonstrates how to create OpenAIResponseClientAgent directly from an ResponsesClient instance.
using OpenAI;
using OpenAI.Responses;
using OpenAIResponseClientSample;
var apiKey = Environment.GetEnvironmentVariable("OPENAI_API_KEY") ?? throw new InvalidOperationException("OPENAI_API_KEY is not set.");
var model = Environment.GetEnvironmentVariable("OPENAI_MODEL") ?? "gpt-4o-mini";
// Create a ResponsesClient directly from OpenAIClient
ResponsesClient responseClient = new OpenAIClient(apiKey).GetResponsesClient(model);
// Create an agent directly from the ResponsesClient using OpenAIResponseClientAgent
OpenAIResponseClientAgent agent = new(responseClient, instructions: "You are good at telling jokes.", name: "Joker");
ResponseItem userMessage = ResponseItem.CreateUserMessageItem("Tell me a joke about a pirate.");
// Invoke the agent and output the text result.
ResponseResult response = await agent.RunAsync([userMessage]);
Console.WriteLine(response.GetOutputText());
// Invoke the agent with streaming support.
IAsyncEnumerable<StreamingResponseUpdate> responseUpdates = agent.RunStreamingAsync([userMessage]);
await foreach (StreamingResponseUpdate responseUpdate in responseUpdates)
{
if (responseUpdate is StreamingResponseOutputTextDeltaUpdate textUpdate)
{
Console.WriteLine(textUpdate.Delta);
}
}

View File

@@ -0,0 +1,22 @@
# Creating an Agent from an OpenAIResponseClient
This sample demonstrates how to create an AI agent directly from an `OpenAI.Responses.OpenAIResponseClient` instance using the `OpenAIResponseClientAgent` class.
## What This Sample Shows
- **Direct OpenAIResponseClient Creation**: Shows how to create an `OpenAI.Responses.OpenAIResponseClient` from `OpenAI.OpenAIClient` and then use it to instantiate an agent
- **OpenAIResponseClientAgent**: Demonstrates using the OpenAI SDK primitives instead of the ones from Microsoft.Extensions.AI and Microsoft.Agents.AI abstractions
- **Full Agent Capabilities**: Shows both regular and streaming invocation of the agent
## Running the Sample
1. Set the required environment variables:
```bash
set OPENAI_API_KEY=your_api_key_here
set OPENAI_MODEL=gpt-4o-mini
```
2. Run the sample:
```bash
dotnet run
```

View File

@@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFrameworks>net10.0</TargetFrameworks>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\..\src\Microsoft.Agents.AI.OpenAI\Microsoft.Agents.AI.OpenAI.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,98 @@
// Copyright (c) Microsoft. All rights reserved.
// This sample demonstrates how to maintain conversation state using the OpenAIResponseClientAgent
// and AgentThread. By passing the same thread to multiple agent invocations, the agent
// automatically maintains the conversation history, allowing the AI model to understand
// context from previous exchanges.
using System.ClientModel;
using System.ClientModel.Primitives;
using System.Text.Json;
using Microsoft.Agents.AI;
using Microsoft.Extensions.AI;
using OpenAI;
using OpenAI.Chat;
using OpenAI.Conversations;
string apiKey = Environment.GetEnvironmentVariable("OPENAI_API_KEY") ?? throw new InvalidOperationException("OPENAI_API_KEY is not set.");
string model = Environment.GetEnvironmentVariable("OPENAI_MODEL") ?? "gpt-4o-mini";
// Create a ConversationClient directly from OpenAIClient
OpenAIClient openAIClient = new(apiKey);
ConversationClient conversationClient = openAIClient.GetConversationClient();
// Create an agent directly from the ResponsesClient using OpenAIResponseClientAgent
ChatClientAgent agent = new(openAIClient.GetResponsesClient(model).AsIChatClient(), instructions: "You are a helpful assistant.", name: "ConversationAgent");
ClientResult createConversationResult = await conversationClient.CreateConversationAsync(BinaryContent.Create(BinaryData.FromString("{}")));
using JsonDocument createConversationResultAsJson = JsonDocument.Parse(createConversationResult.GetRawResponse().Content.ToString());
string conversationId = createConversationResultAsJson.RootElement.GetProperty("id"u8)!.GetString()!;
// Create a thread for the conversation - this enables conversation state management for subsequent turns
AgentThread thread = await agent.GetNewThreadAsync(conversationId);
Console.WriteLine("=== Multi-turn Conversation Demo ===\n");
// First turn: Ask about a topic
Console.WriteLine("User: What is the capital of France?");
UserChatMessage firstMessage = new("What is the capital of France?");
// After this call, the conversation state associated in the options is stored in 'thread' and used in subsequent calls
ChatCompletion firstResponse = await agent.RunAsync([firstMessage], thread);
Console.WriteLine($"Assistant: {firstResponse.Content.Last().Text}\n");
// Second turn: Follow-up question that relies on conversation context
Console.WriteLine("User: What famous landmarks are located there?");
UserChatMessage secondMessage = new("What famous landmarks are located there?");
ChatCompletion secondResponse = await agent.RunAsync([secondMessage], thread);
Console.WriteLine($"Assistant: {secondResponse.Content.Last().Text}\n");
// Third turn: Another follow-up that demonstrates context continuity
Console.WriteLine("User: How tall is the most famous one?");
UserChatMessage thirdMessage = new("How tall is the most famous one?");
ChatCompletion thirdResponse = await agent.RunAsync([thirdMessage], thread);
Console.WriteLine($"Assistant: {thirdResponse.Content.Last().Text}\n");
Console.WriteLine("=== End of Conversation ===");
// Show full conversation history
Console.WriteLine("Full Conversation History:");
ClientResult getConversationResult = await conversationClient.GetConversationAsync(conversationId);
Console.WriteLine("Conversation created.");
Console.WriteLine($" Conversation ID: {conversationId}");
Console.WriteLine();
CollectionResult getConversationItemsResults = conversationClient.GetConversationItems(conversationId);
foreach (ClientResult result in getConversationItemsResults.GetRawPages())
{
Console.WriteLine("Message contents retrieved. Order is most recent first by default.");
using JsonDocument getConversationItemsResultAsJson = JsonDocument.Parse(result.GetRawResponse().Content.ToString());
foreach (JsonElement element in getConversationItemsResultAsJson.RootElement.GetProperty("data").EnumerateArray())
{
string messageId = element.GetProperty("id"u8).ToString();
string messageRole = element.GetProperty("role"u8).ToString();
Console.WriteLine($" Message ID: {messageId}");
Console.WriteLine($" Message Role: {messageRole}");
foreach (var content in element.GetProperty("content").EnumerateArray())
{
string messageContentText = content.GetProperty("text"u8).ToString();
Console.WriteLine($" Message Text: {messageContentText}");
}
Console.WriteLine();
}
}
ClientResult deleteConversationResult = conversationClient.DeleteConversation(conversationId);
using JsonDocument deleteConversationResultAsJson = JsonDocument.Parse(deleteConversationResult.GetRawResponse().Content.ToString());
bool deleted = deleteConversationResultAsJson.RootElement
.GetProperty("deleted"u8)
.GetBoolean();
Console.WriteLine("Conversation deleted.");
Console.WriteLine($" Deleted: {deleted}");
Console.WriteLine();

View File

@@ -0,0 +1,90 @@
# Managing Conversation State with OpenAI
This sample demonstrates how to maintain conversation state across multiple turns using the Agent Framework with OpenAI's Conversation API.
## What This Sample Shows
- **Conversation State Management**: Shows how to use `ConversationClient` and `AgentThread` to maintain conversation context across multiple agent invocations
- **Multi-turn Conversations**: Demonstrates follow-up questions that rely on context from previous messages in the conversation
- **Server-Side Storage**: Uses OpenAI's Conversation API to manage conversation history server-side, allowing the model to access previous messages without resending them
- **Conversation Lifecycle**: Demonstrates creating, retrieving, and deleting conversations
## Key Concepts
### ConversationClient for Server-Side Storage
The `ConversationClient` manages conversations on OpenAI's servers:
```csharp
// Create a ConversationClient from OpenAIClient
OpenAIClient openAIClient = new(apiKey);
ConversationClient conversationClient = openAIClient.GetConversationClient();
// Create a new conversation
ClientResult createConversationResult = await conversationClient.CreateConversationAsync(BinaryContent.Create(BinaryData.FromString("{}")));
```
### AgentThread for Conversation State
The `AgentThread` works with `ChatClientAgentRunOptions` to link the agent to a server-side conversation:
```csharp
// Set up agent run options with the conversation ID
ChatClientAgentRunOptions agentRunOptions = new() { ChatOptions = new ChatOptions() { ConversationId = conversationId } };
// Create a thread for the conversation
AgentThread thread = await agent.GetNewThreadAsync();
// First call links the thread to the conversation
ChatCompletion firstResponse = await agent.RunAsync([firstMessage], thread, agentRunOptions);
// Subsequent calls use the thread without needing to pass options again
ChatCompletion secondResponse = await agent.RunAsync([secondMessage], thread);
```
### Retrieving Conversation History
You can retrieve the full conversation history from the server:
```csharp
CollectionResult getConversationItemsResults = conversationClient.GetConversationItems(conversationId);
foreach (ClientResult result in getConversationItemsResults.GetRawPages())
{
// Process conversation items
}
```
### How It Works
1. **Create an OpenAI Client**: Initialize an `OpenAIClient` with your API key
2. **Create a Conversation**: Use `ConversationClient` to create a server-side conversation
3. **Create an Agent**: Initialize an `OpenAIResponseClientAgent` with the desired model and instructions
4. **Create a Thread**: Call `agent.GetNewThreadAsync()` to create a new conversation thread
5. **Link Thread to Conversation**: Pass `ChatClientAgentRunOptions` with the `ConversationId` on the first call
6. **Send Messages**: Subsequent calls to `agent.RunAsync()` only need the thread - context is maintained
7. **Cleanup**: Delete the conversation when done using `conversationClient.DeleteConversation()`
## Running the Sample
1. Set the required environment variables:
```powershell
$env:OPENAI_API_KEY = "your_api_key_here"
$env:OPENAI_MODEL = "gpt-4o-mini"
```
2. Run the sample:
```powershell
dotnet run
```
## Expected Output
The sample demonstrates a three-turn conversation where each follow-up question relies on context from previous messages:
1. First question asks about the capital of France
2. Second question asks about landmarks "there" - requiring understanding of the previous answer
3. Third question asks about "the most famous one" - requiring context from both previous turns
After the conversation, the sample retrieves and displays the full conversation history from the server, then cleans up by deleting the conversation.
This demonstrates that the conversation state is properly maintained across multiple agent invocations using OpenAI's server-side conversation storage.

View File

@@ -0,0 +1,17 @@
# Agent Framework with OpenAI
These samples show how to use the Agent Framework with the OpenAI exchange types.
By default, the .Net version of Agent Framework uses the [Microsoft.Extensions.AI.Abstractions](https://www.nuget.org/packages/Microsoft.Extensions.AI.Abstractions/) exchange types.
For developers who are using the [OpenAI SDK](https://www.nuget.org/packages/OpenAI) this can be problematic because there are conflicting exchange types which can cause confusion.
Agent Framework provides additional support to allow OpenAI developers to use the OpenAI exchange types.
|Sample|Description|
|---|---|
|[Creating an AIAgent](./Agent_OpenAI_Step01_Running/)|This sample demonstrates how to create and run a basic agent with native OpenAI SDK types. Shows both regular and streaming invocation of the agent.|
|[Using Reasoning Capabilities](./Agent_OpenAI_Step02_Reasoning/)|This sample demonstrates how to create an AI agent with reasoning capabilities using OpenAI's reasoning models and response types.|
|[Creating an Agent from a ChatClient](./Agent_OpenAI_Step03_CreateFromChatClient/)|This sample demonstrates how to create an AI agent directly from an OpenAI.Chat.ChatClient instance using OpenAIChatClientAgent.|
|[Creating an Agent from an OpenAIResponseClient](./Agent_OpenAI_Step04_CreateFromOpenAIResponseClient/)|This sample demonstrates how to create an AI agent directly from an OpenAI.Responses.OpenAIResponseClient instance using OpenAIResponseClientAgent.|
|[Managing Conversation State](./Agent_OpenAI_Step05_Conversation/)|This sample demonstrates how to maintain conversation state across multiple turns using the AgentThread for context continuity.|