Skip to content

.Net: Feature openai responses agent #11550

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 7 commits into from
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
// Copyright (c) Microsoft. All rights reserved.
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Agents;
using Microsoft.SemanticKernel.Agents.OpenAI;
using Microsoft.SemanticKernel.ChatCompletion;

namespace GettingStarted.OpenAIResponseAgents;

/// <summary>
/// This example demonstrates using <see cref="OpenAIResponseAgent"/>.
/// </summary>
public class Step01_OpenAIResponseAgent(ITestOutputHelper output) : BaseResponsesAgentTest(output)
{
[Fact]
public async Task UseOpenAIResponseAgentAsync()
{
// Define the agent
OpenAIResponseAgent agent = new(this.Client)
{
Name = "ResponseAgent",
Instructions = "Answer all queries in English and French.",
};

// Invoke the agent and output the response
var responseItems = agent.InvokeAsync("What is the capital of France?");
await foreach (ChatMessageContent responseItem in responseItems)
{
WriteAgentChatMessage(responseItem);
}
}

[Fact]
public async Task UseOpenAIResponseAgentWithMessagesAsync()
{
// Define the agent
OpenAIResponseAgent agent = new(this.Client)
{
Name = "ResponseAgent",
Instructions = "Answer all queries in English and French."
};

ICollection<ChatMessageContent> messages =
[
new ChatMessageContent(AuthorRole.User, "What is the capital of France?"),
new ChatMessageContent(AuthorRole.User, "What is the capital of Ireland?")
];

// Invoke the agent and output the response
var responseItems = agent.InvokeAsync(messages);
await foreach (ChatMessageContent responseItem in responseItems)
{
WriteAgentChatMessage(responseItem);
}
}

[Fact]
public async Task UseOpenAIResponseAgentWithThreadedConversationAsync()
{
// Define the agent
OpenAIResponseAgent agent = new(this.Client)
{
Name = "ResponseAgent",
Instructions = "Answer all queries in the users preferred language.",
};

string[] messages =
[
"My name is Bob and my preferred language is French.",
"What is the capital of France?",
"What is the capital of Spain?",
"What is the capital of Italy?"
];

// Initial thread can be null as it will be automatically created
AgentThread? agentThread = null;

// Invoke the agent and output the response
foreach (string message in messages)
{
var responseItems = agent.InvokeAsync(new ChatMessageContent(AuthorRole.User, message), agentThread);
await foreach (AgentResponseItem<ChatMessageContent> responseItem in responseItems)
{
// Update the thread so the previous response id is used
agentThread = responseItem.Thread;

WriteAgentChatMessage(responseItem.Message);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
// Copyright (c) Microsoft. All rights reserved.

using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Agents;
using Microsoft.SemanticKernel.Agents.OpenAI;
using Microsoft.SemanticKernel.ChatCompletion;

namespace GettingStarted.OpenAIResponseAgents;

/// <summary>
/// This example demonstrates how to manage conversation state during a model interaction using <see cref="OpenAIResponseAgent"/>.
/// OpenAI provides a few ways to manage conversation state, which is important for preserving information across multiple messages or turns in a conversation.
/// </summary>
public class Step02_ConversationState(ITestOutputHelper output) : BaseResponsesAgentTest(output)
{
[Fact]
public async Task ManuallyConstructPastConversationAsync()
{
// Define the agent
OpenAIResponseAgent agent = new(this.Client)
{
StoreEnabled = false,
};

ICollection<ChatMessageContent> messages =
[
new ChatMessageContent(AuthorRole.User, "knock knock."),
new ChatMessageContent(AuthorRole.Assistant, "Who's there?"),
new ChatMessageContent(AuthorRole.User, "Orange.")
];
foreach (ChatMessageContent message in messages)
{
WriteAgentChatMessage(message);
}

// Invoke the agent and output the response
var responseItems = agent.InvokeAsync(messages);
await foreach (ChatMessageContent responseItem in responseItems)
{
WriteAgentChatMessage(responseItem);
}
}

[Fact]
public async Task ManuallyManageConversationStateWithResponsesChatCompletionApiAsync()
{
// Define the agent
OpenAIResponseAgent agent = new(this.Client)
{
StoreEnabled = false,
};

string[] messages =
[
"Tell me a joke?",
"Tell me another?",
];

// Invoke the agent and output the response
AgentThread? agentThread = null;
foreach (string message in messages)
{
var userMessage = new ChatMessageContent(AuthorRole.User, message);
WriteAgentChatMessage(userMessage);

var responseItems = agent.InvokeAsync(userMessage, agentThread);
await foreach (AgentResponseItem<ChatMessageContent> responseItem in responseItems)
{
agentThread = responseItem.Thread;
WriteAgentChatMessage(responseItem.Message);
}
}
}

[Fact]
public async Task ManageConversationStateWithResponseApiAsync()
{
// Define the agent
OpenAIResponseAgent agent = new(this.Client)
{
StoreEnabled = true,
};

string[] messages =
[
"Tell me a joke?",
"Explain why this is funny.",
];

// Invoke the agent and output the response
AgentThread? agentThread = null;
foreach (string message in messages)
{
var userMessage = new ChatMessageContent(AuthorRole.User, message);
WriteAgentChatMessage(userMessage);

var responseItems = agent.InvokeAsync(userMessage, agentThread);
await foreach (AgentResponseItem<ChatMessageContent> responseItem in responseItems)
{
agentThread = responseItem.Thread;
WriteAgentChatMessage(responseItem.Message);
}
}

// Display the contents in the latest thread
if (agentThread is not null)
{
this.Output.WriteLine("\n\nResponse Thread Messages\n");
var responseAgentThread = agentThread as OpenAIResponseAgentThread;
var threadMessages = responseAgentThread?.GetMessagesAsync();
if (threadMessages is not null)
{
await foreach (var threadMessage in threadMessages)
{
WriteAgentChatMessage(threadMessage);
}
}

await agentThread.DeleteAsync();
}
}
}
1 change: 1 addition & 0 deletions dotnet/src/Agents/OpenAI/Agents.OpenAI.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
<ItemGroup>
<ProjectReference Include="..\..\Connectors\Connectors.OpenAI\Connectors.OpenAI.csproj" />
<ProjectReference Include="..\Abstractions\Agents.Abstractions.csproj" />
<ProjectReference Include="..\Core\Agents.Core.csproj" />
</ItemGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
// Copyright (c) Microsoft. All rights reserved.
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.SemanticKernel.Agents.OpenAI.Internal;
using OpenAI.Assistants;
using OpenAI.Responses;

namespace Microsoft.SemanticKernel.Agents.OpenAI;

Expand Down Expand Up @@ -33,4 +35,21 @@ public static IEnumerable<ThreadInitializationMessage> ToThreadInitializationMes
{
return messages.Select(message => message.ToThreadInitializationMessage());
}

/// <summary>
/// Converts a <see cref="ChatMessageContent"/> instance to a <see cref="ResponseItem"/>.
/// </summary>
/// <param name="message">The chat message content to convert.</param>
/// <returns>A <see cref="ResponseItem"/> instance.</returns>
public static ResponseItem ToResponseItem(this ChatMessageContent message)
{
return message.Role.Label switch
{
"system" => ResponseItem.CreateSystemMessageItem(message.Content),
"user" => ResponseItem.CreateUserMessageItem(message.Content),
"developer" => ResponseItem.CreateDeveloperMessageItem(message.Content),
"assistant" => ResponseItem.CreateAssistantMessageItem(message.Content),
_ => throw new NotSupportedException($"Unsupported role {message.Role.Label}. Only system, user, developer or assistant roles are allowed."),
};
}
}
69 changes: 69 additions & 0 deletions dotnet/src/Agents/OpenAI/Extensions/ResponseItemExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// Copyright (c) Microsoft. All rights reserved.

using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using Microsoft.SemanticKernel.ChatCompletion;
using OpenAI.Responses;

namespace Microsoft.SemanticKernel.Agents.OpenAI;

[ExcludeFromCodeCoverage]
internal static class ResponseItemExtensions
{
/// <summary>
/// Converts a <see cref="ResponseItem"/> instance to a <see cref="ChatMessageContent"/>.
/// </summary>
/// <param name="item">The response item to convert.</param>
/// <returns>A <see cref="ChatMessageContent"/> instance.</returns>
public static ChatMessageContent ToChatMessageContent(this ResponseItem item)
{
if (item is MessageResponseItem messageResponseItem)
{
var role = messageResponseItem.Role.ToAuthorRole();
var collection = messageResponseItem.Content.ToChatMessageContentItemCollection();

return new ChatMessageContent(role, collection, innerContent: messageResponseItem);
}
throw new InvalidOperationException();
}

#region private
private static ChatMessageContentItemCollection ToChatMessageContentItemCollection(this IList<ResponseContentPart> content)
{
var collection = new ChatMessageContentItemCollection();
foreach (var part in content)
{
if (part.Kind == ResponseContentPartKind.OutputText || part.Kind == ResponseContentPartKind.InputText)
{
collection.Add(new TextContent(part.Text, innerContent: part));
}
else if (part.Kind == ResponseContentPartKind.InputImage)
{
collection.Add(new FileReferenceContent(part.InputImageFileId, innerContent: part));
}
else if (part.Kind == ResponseContentPartKind.InputFile)
{
collection.Add(new FileReferenceContent(part.InputFileId, innerContent: part));
}
else if (part.Kind == ResponseContentPartKind.Refusal)
{
collection.Add(new TextContent(part.Refusal, innerContent: part));
}
}
return collection;
}

private static AuthorRole ToAuthorRole(this MessageRole messageRole)
{
return messageRole switch
{
MessageRole.Assistant => AuthorRole.Assistant,
MessageRole.Developer => AuthorRole.Developer,
MessageRole.System => AuthorRole.System,
MessageRole.User => AuthorRole.User,
_ => new AuthorRole("unknown"),
};
}
#endregion
}
Loading
Loading