mirror of
https://github.com/microsoft/semantic-kernel.git
synced 2026-10-12 02:19:06 +00:00
Update OpenAI to 2.9.1, Azure.AI.OpenAI to 2.9.0-beta.1, Azure.AI.Projects to 2.0.0-beta.2, Microsoft.Extensions.AI* to 10.4.0 (#13668)
### Motivation and Context Bumps core AI SDK dependencies to their latest versions to pick up new features and fixes. ### Description Updates the following packages in `Directory.Packages.props`: - **OpenAI** 2.8.0 → 2.9.1 - **Azure.AI.OpenAI** 2.8.0-beta.1 → 2.9.0-beta.1 - **Azure.AI.Projects** 1.1.0 → 2.0.0-beta.2 - **Azure.Identity** 1.17.1 → 1.19.0 (cascading dependency from Azure.AI.Projects 2.0.0-beta.2) - **Microsoft.Extensions.AI\*** 10.3.0 → 10.4.0 - **System.Text.Json, System.Numerics.Tensors, Microsoft.Extensions.Logging.Abstractions, Microsoft.Bcl.AsyncInterfaces, System.Threading.Channels** → 10.0.4 Addresses the following breaking changes introduced by OpenAI 2.9.1: - `CreateInputImagePart` BinaryData overload removed (replaced with data URI approach) - Realtime API type renames (`ConversationTool` → `RealtimeTool`, `ConversationSessionOptions` → `RealtimeConversationSessionOptions`, etc.) - `ResponsesClient` constructor no longer accepts a model parameter; model is now specified on `CreateResponseOptions.Model` - `ResponseResult.Instructions` is now read-only Code changes to adapt: - Added `ModelId` property to `OpenAIResponseAgent` and propagated it through `ResponseCreationOptionsFactory` - Updated unit tests (`BaseOpenAIResponseClientTest`, `OpenAIResponseAgentExtensionsTests`, `OpenAIResponseExtensionsTests`) - Updated integration tests (`OpenAIResponseAgentTests`, `OpenAIResponseAgentFixture`) - Updated `BaseResponsesAgentTest` and `GettingStartedWithAgents` samples - Updated `AgentFrameworkMigration` samples (`GetResponsesClient`, `AsIChatClient`, `CreateAIAgent`, modelId propagation) - Rewrote `OpenAIRealtime` demo for the restructured Realtime API event model ### Contribution Checklist - [x] The code builds clean without any errors or warnings - [x] The PR follows the [SK Contribution Guidelines](https://github.com/microsoft/semantic-kernel/blob/main/CONTRIBUTING.md) and the [pre-submission formatting script](https://github.com/microsoft/semantic-kernel/blob/main/CONTRIBUTING.md#development-scripts) raises no violations - [x] All unit tests pass, and I have added new tests where possible - [ ] I didn't break anyone 😄 <!-- START COPILOT CODING AGENT TIPS --> --- 📍 Connect Copilot coding agent with [Jira](https://gh.io/cca-jira-docs), [Azure Boards](https://gh.io/cca-azure-boards-docs) or [Linear](https://gh.io/cca-linear-docs) to delegate work to Copilot in one click without leaving your project management tool. --------- Co-authored-by: copilot-swe-agent[bot] <[email protected]> Co-authored-by: stephentoub <[email protected]> Co-authored-by: Copilot <[email protected]> Co-authored-by: Roger Barreto <[email protected]>
This commit is contained in:
co-authored by
stephentoub
Copilot
Roger Barreto
parent
3c1b688dcf
commit
30aec16700
@@ -22,9 +22,9 @@
|
||||
<PackageVersion Include="AWSSDK.SecurityToken" Version="4.0.5.6" />
|
||||
<PackageVersion Include="Azure.AI.Agents.Persistent" Version="1.1.0" />
|
||||
<PackageVersion Include="Azure.AI.ContentSafety" Version="1.0.0" />
|
||||
<PackageVersion Include="Azure.AI.OpenAI" Version="2.8.0-beta.1" />
|
||||
<PackageVersion Include="Azure.AI.Projects" Version="1.1.0" />
|
||||
<PackageVersion Include="Azure.Identity" Version="1.17.1" />
|
||||
<PackageVersion Include="Azure.AI.OpenAI" Version="2.9.0-beta.1" />
|
||||
<PackageVersion Include="Azure.AI.Projects" Version="2.0.0-beta.2" />
|
||||
<PackageVersion Include="Azure.Identity" Version="1.19.0" />
|
||||
<PackageVersion Include="Azure.Monitor.OpenTelemetry.Exporter" Version="1.5.0" />
|
||||
<PackageVersion Include="Azure.Search.Documents" Version="11.7.0" />
|
||||
<PackageVersion Include="Community.OData.Linq" Version="2.1.0" />
|
||||
@@ -92,7 +92,7 @@
|
||||
<PackageVersion Include="Npgsql" Version="8.0.7" />
|
||||
<PackageVersion Include="OData2Linq" Version="2.2.0" />
|
||||
<PackageVersion Include="OllamaSharp" Version="5.4.12" />
|
||||
<PackageVersion Include="OpenAI" Version="2.8.0" />
|
||||
<PackageVersion Include="OpenAI" Version="2.9.1" />
|
||||
<PackageVersion Include="OpenTelemetry.Exporter.Console" Version="1.14.0" />
|
||||
<PackageVersion Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.14.0" />
|
||||
<PackageVersion Include="OpenTelemetry.Extensions.Hosting" Version="1.14.0" />
|
||||
@@ -109,8 +109,8 @@
|
||||
<PackageVersion Include="System.Linq.AsyncEnumerable" Version="10.0.4" />
|
||||
<PackageVersion Include="System.Memory.Data" Version="10.0.2" />
|
||||
<PackageVersion Include="System.Net.Http" Version="4.3.4" />
|
||||
<PackageVersion Include="System.Numerics.Tensors" Version="10.0.3" />
|
||||
<PackageVersion Include="System.Text.Json" Version="10.0.3" />
|
||||
<PackageVersion Include="System.Numerics.Tensors" Version="10.0.4" />
|
||||
<PackageVersion Include="System.Text.Json" Version="10.0.4" />
|
||||
<PackageVersion Include="System.ValueTuple" Version="4.6.1" />
|
||||
<PackageVersion Include="System.Threading.Tasks.Extensions" Version="4.6.3" />
|
||||
<PackageVersion Include="A2A" Version="0.3.1-preview" />
|
||||
@@ -119,10 +119,10 @@
|
||||
<!-- Tokenizers -->
|
||||
<PackageVersion Include="Microsoft.ML.Tokenizers" Version="2.0.0" />
|
||||
<!-- Microsoft.Extensions.* -->
|
||||
<PackageVersion Include="Microsoft.Extensions.AI" Version="10.3.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.AI.Abstractions" Version="10.3.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.AI" Version="10.4.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.AI.Abstractions" Version="10.4.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.AI.AzureAIInference" Version="10.0.0-preview.1.25559.3" />
|
||||
<PackageVersion Include="Microsoft.Extensions.AI.OpenAI" Version="10.3.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.AI.OpenAI" Version="10.4.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Configuration" Version="10.0.2" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Configuration.Abstractions" Version="10.0.2" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Configuration.Binder" Version="10.0.2" />
|
||||
@@ -131,14 +131,14 @@
|
||||
<PackageVersion Include="Microsoft.Extensions.Configuration.Json" Version="10.0.2" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Configuration.UserSecrets" Version="10.0.2" />
|
||||
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="10.0.2" />
|
||||
<PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="10.0.3" />
|
||||
<PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="10.0.4" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Diagnostics.Testing" Version="10.0.2" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Hosting" Version="10.0.2" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Http" Version="10.0.2" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Http.Resilience" Version="10.2.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.ServiceDiscovery" Version="10.2.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Logging" Version="10.0.2" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="10.0.3" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="10.0.4" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Logging.Console" Version="10.0.2" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Logging.Debug" Version="10.0.2" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Options.DataAnnotations" Version="10.0.2" />
|
||||
@@ -151,7 +151,7 @@
|
||||
<PackageVersion Include="Moq" Version="[4.18.4]" />
|
||||
<PackageVersion Include="FluentAssertions" Version="8.2.0" />
|
||||
<PackageVersion Include="System.Text.RegularExpressions" Version="4.3.1" />
|
||||
<PackageVersion Include="System.Threading.Channels" Version="10.0.3" />
|
||||
<PackageVersion Include="System.Threading.Channels" Version="10.0.4" />
|
||||
<PackageVersion Include="System.Threading.Tasks.Dataflow" Version="10.0.2" />
|
||||
<PackageVersion Include="xunit" Version="2.9.3" />
|
||||
<PackageVersion Include="xunit.abstractions" Version="2.0.3" />
|
||||
|
||||
+6
-5
@@ -3,6 +3,7 @@
|
||||
using Azure.AI.OpenAI;
|
||||
using Azure.Identity;
|
||||
using Microsoft.Agents.AI;
|
||||
using Microsoft.Extensions.AI;
|
||||
using Microsoft.SemanticKernel.Agents.OpenAI;
|
||||
using OpenAI;
|
||||
|
||||
@@ -24,8 +25,8 @@ async Task SKAgentAsync()
|
||||
Console.WriteLine("\n=== SK Agent ===\n");
|
||||
|
||||
var responseClient = new AzureOpenAIClient(new Uri(endpoint), new AzureCliCredential())
|
||||
.GetResponsesClient(deploymentName);
|
||||
OpenAIResponseAgent agent = new(responseClient)
|
||||
.GetResponsesClient();
|
||||
OpenAIResponseAgent agent = new(responseClient, deploymentName)
|
||||
{
|
||||
Name = "Joker",
|
||||
Instructions = "You are good at telling jokes.",
|
||||
@@ -54,9 +55,9 @@ async Task SKAgent_As_AFAgentAsync()
|
||||
Console.WriteLine("\n=== SK Agent Converted as an AF Agent ===\n");
|
||||
|
||||
var responseClient = new AzureOpenAIClient(new Uri(endpoint), new AzureCliCredential())
|
||||
.GetResponsesClient(deploymentName);
|
||||
.GetResponsesClient();
|
||||
|
||||
OpenAIResponseAgent skAgent = new(responseClient)
|
||||
OpenAIResponseAgent skAgent = new(responseClient, deploymentName)
|
||||
{
|
||||
Name = "Joker",
|
||||
Instructions = "You are good at telling jokes.",
|
||||
@@ -83,7 +84,7 @@ async Task AFAgentAsync()
|
||||
Console.WriteLine("\n=== AF Agent ===\n");
|
||||
|
||||
var agent = new AzureOpenAIClient(new Uri(endpoint), new AzureCliCredential())
|
||||
.GetResponsesClient(deploymentName)
|
||||
.GetResponsesClient().AsIChatClient(deploymentName)
|
||||
.CreateAIAgent(name: "Joker", instructions: "You are good at telling jokes.");
|
||||
|
||||
var thread = agent.GetNewThread();
|
||||
|
||||
+5
-5
@@ -51,8 +51,8 @@ async Task SKAgentAsync()
|
||||
Console.WriteLine("\n=== SK Agent ===\n");
|
||||
|
||||
var responseClient = new AzureOpenAIClient(new Uri(endpoint), new AzureCliCredential())
|
||||
.GetResponsesClient(deploymentName);
|
||||
OpenAIResponseAgent agent = new(responseClient)
|
||||
.GetResponsesClient();
|
||||
OpenAIResponseAgent agent = new(responseClient, deploymentName)
|
||||
{
|
||||
Name = "Thinker",
|
||||
Instructions = "You are good at thinking hard before answering.",
|
||||
@@ -119,11 +119,11 @@ async Task SKAgent_As_AFAgentAsync()
|
||||
Console.WriteLine("\n=== SK Agent Converted as an AF Agent ===\n");
|
||||
|
||||
var responseClient = new AzureOpenAIClient(new Uri(endpoint), new AzureCliCredential())
|
||||
.GetResponsesClient(deploymentName);
|
||||
.GetResponsesClient();
|
||||
|
||||
#pragma warning disable SKEXP0110 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
|
||||
|
||||
OpenAIResponseAgent skAgent = new(responseClient)
|
||||
OpenAIResponseAgent skAgent = new(responseClient, deploymentName)
|
||||
{
|
||||
Name = "Thinker",
|
||||
Instructions = "You are good at thinking hard before answering.",
|
||||
@@ -180,7 +180,7 @@ async Task AFAgentAsync()
|
||||
Console.WriteLine("\n=== AF Agent ===\n");
|
||||
|
||||
var agent = new AzureOpenAIClient(new Uri(endpoint), new AzureCliCredential())
|
||||
.GetResponsesClient(deploymentName)
|
||||
.GetResponsesClient().AsIChatClient(deploymentName)
|
||||
.CreateAIAgent(name: "Thinker", instructions: "You are good at thinking hard before answering.");
|
||||
|
||||
var thread = agent.GetNewThread();
|
||||
|
||||
+3
-3
@@ -29,7 +29,7 @@ await AFAgentAsync();
|
||||
async Task SKAgentAsync()
|
||||
{
|
||||
OpenAIResponseAgent agent = new(new AzureOpenAIClient(new Uri(endpoint), new AzureCliCredential())
|
||||
.GetResponsesClient(deploymentName))
|
||||
.GetResponsesClient(), deploymentName)
|
||||
{ StoreEnabled = true };
|
||||
|
||||
// Initialize plugin and add to the agent's Kernel (same as direct Kernel usage).
|
||||
@@ -49,7 +49,7 @@ async Task SKAgentAsync()
|
||||
async Task SKAgent_As_AFAgentAsync()
|
||||
{
|
||||
OpenAIResponseAgent skAgent = new(new AzureOpenAIClient(new Uri(endpoint), new AzureCliCredential())
|
||||
.GetResponsesClient(deploymentName))
|
||||
.GetResponsesClient(), deploymentName)
|
||||
{ StoreEnabled = true };
|
||||
|
||||
// Initialize plugin and add to the agent's Kernel (same as direct Kernel usage).
|
||||
@@ -66,7 +66,7 @@ async Task SKAgent_As_AFAgentAsync()
|
||||
async Task AFAgentAsync()
|
||||
{
|
||||
var agent = new AzureOpenAIClient(new Uri(endpoint), new AzureCliCredential())
|
||||
.GetResponsesClient(deploymentName)
|
||||
.GetResponsesClient().AsIChatClient(deploymentName)
|
||||
.CreateAIAgent(instructions: "You are a helpful assistant", tools: [AIFunctionFactory.Create(GetWeather)]);
|
||||
|
||||
Console.WriteLine("\n=== AF Agent Response ===\n");
|
||||
|
||||
+3
-3
@@ -28,7 +28,7 @@ async Task SKAgentAsync()
|
||||
var serviceCollection = new ServiceCollection();
|
||||
serviceCollection.AddTransient<Microsoft.SemanticKernel.Agents.Agent>((sp)
|
||||
=> new OpenAIResponseAgent(new AzureOpenAIClient(new Uri(endpoint), new AzureCliCredential())
|
||||
.GetResponsesClient(deploymentName))
|
||||
.GetResponsesClient(), deploymentName)
|
||||
{
|
||||
Name = "Joker",
|
||||
Instructions = "You are good at telling jokes.",
|
||||
@@ -49,7 +49,7 @@ async Task SKAgent_As_AFAgentAsync()
|
||||
var serviceCollection = new ServiceCollection();
|
||||
serviceCollection.AddTransient<Microsoft.SemanticKernel.Agents.Agent>((sp)
|
||||
=> new OpenAIResponseAgent(new AzureOpenAIClient(new Uri(endpoint), new AzureCliCredential())
|
||||
.GetResponsesClient(deploymentName))
|
||||
.GetResponsesClient(), deploymentName)
|
||||
{
|
||||
Name = "Joker",
|
||||
Instructions = "You are good at telling jokes.",
|
||||
@@ -71,7 +71,7 @@ async Task AFAgentAsync()
|
||||
|
||||
var serviceCollection = new ServiceCollection();
|
||||
serviceCollection.AddTransient<AIAgent>((sp) => new AzureOpenAIClient(new Uri(endpoint), new AzureCliCredential())
|
||||
.GetResponsesClient(deploymentName)
|
||||
.GetResponsesClient().AsIChatClient(deploymentName)
|
||||
.CreateAIAgent(name: "Joker", instructions: "You are good at telling jokes."));
|
||||
|
||||
await using ServiceProvider serviceProvider = serviceCollection.BuildServiceProvider();
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using Microsoft.Agents.AI;
|
||||
using Microsoft.Extensions.AI;
|
||||
using Microsoft.SemanticKernel.Agents.OpenAI;
|
||||
using OpenAI;
|
||||
|
||||
@@ -21,7 +22,7 @@ async Task SKAgentAsync()
|
||||
{
|
||||
Console.WriteLine("\n=== SK Agent ===\n");
|
||||
|
||||
var responseClient = new OpenAIClient(apiKey).GetResponsesClient(model);
|
||||
var responseClient = new OpenAIClient(apiKey).GetResponsesClient();
|
||||
OpenAIResponseAgent agent = new(responseClient)
|
||||
{
|
||||
Name = "Joker",
|
||||
@@ -51,7 +52,7 @@ async Task SKAgent_As_AFAgentAsync()
|
||||
{
|
||||
Console.WriteLine("\n=== SK Agent Converted as an AF Agent ===\n");
|
||||
|
||||
var responseClient = new OpenAIClient(apiKey).GetResponsesClient(model);
|
||||
var responseClient = new OpenAIClient(apiKey).GetResponsesClient();
|
||||
|
||||
OpenAIResponseAgent skAgent = new(responseClient)
|
||||
{
|
||||
@@ -79,7 +80,7 @@ async Task AFAgentAsync()
|
||||
{
|
||||
Console.WriteLine("\n=== AF Agent ===\n");
|
||||
|
||||
var agent = new OpenAIClient(apiKey).GetResponsesClient(model)
|
||||
var agent = new OpenAIClient(apiKey).GetResponsesClient().AsIChatClient(model)
|
||||
.CreateAIAgent(name: "Joker", instructions: "You are good at telling jokes.");
|
||||
|
||||
var thread = agent.GetNewThread();
|
||||
|
||||
+3
-3
@@ -48,7 +48,7 @@ async Task SKAgentAsync()
|
||||
{
|
||||
Console.WriteLine("\n=== SK Agent ===\n");
|
||||
|
||||
var responseClient = new OpenAIClient(apiKey).GetResponsesClient(model);
|
||||
var responseClient = new OpenAIClient(apiKey).GetResponsesClient();
|
||||
OpenAIResponseAgent agent = new(responseClient)
|
||||
{
|
||||
Name = "Thinker",
|
||||
@@ -116,7 +116,7 @@ async Task SKAgent_As_AFAgentAsync()
|
||||
{
|
||||
Console.WriteLine("\n=== SK Agent Converted as an AF Agent ===\n");
|
||||
|
||||
var responseClient = new OpenAIClient(apiKey).GetResponsesClient(model);
|
||||
var responseClient = new OpenAIClient(apiKey).GetResponsesClient();
|
||||
|
||||
#pragma warning disable SKEXP0110 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
|
||||
|
||||
@@ -176,7 +176,7 @@ async Task AFAgentAsync()
|
||||
{
|
||||
Console.WriteLine("\n=== AF Agent ===\n");
|
||||
|
||||
var agent = new OpenAIClient(apiKey).GetResponsesClient(model)
|
||||
var agent = new OpenAIClient(apiKey).GetResponsesClient().AsIChatClient(model)
|
||||
.CreateAIAgent(name: "Thinker", instructions: "You are at thinking hard before answering.");
|
||||
|
||||
var thread = agent.GetNewThread();
|
||||
|
||||
@@ -28,7 +28,7 @@ async Task SKAgentAsync()
|
||||
{
|
||||
var builder = Kernel.CreateBuilder().AddOpenAIChatClient(model, apiKey);
|
||||
|
||||
OpenAIResponseAgent agent = new(new OpenAIClient(apiKey).GetResponsesClient(model)) { StoreEnabled = true };
|
||||
OpenAIResponseAgent agent = new(new OpenAIClient(apiKey).GetResponsesClient(), model) { StoreEnabled = true };
|
||||
|
||||
// Initialize plugin and add to the agent's Kernel (same as direct Kernel usage).
|
||||
agent.Kernel.Plugins.Add(KernelPluginFactory.CreateFromFunctions("KernelPluginName", [KernelFunctionFactory.CreateFromMethod(GetWeather)]));
|
||||
@@ -48,7 +48,7 @@ async Task SKAgent_As_AFAgentAsync()
|
||||
{
|
||||
var builder = Kernel.CreateBuilder().AddOpenAIChatClient(model, apiKey);
|
||||
|
||||
OpenAIResponseAgent skAgent = new(new OpenAIClient(apiKey).GetResponsesClient(model)) { StoreEnabled = true };
|
||||
OpenAIResponseAgent skAgent = new(new OpenAIClient(apiKey).GetResponsesClient(), model) { StoreEnabled = true };
|
||||
|
||||
// Initialize plugin and add to the agent's Kernel (same as direct Kernel usage).
|
||||
skAgent.Kernel.Plugins.Add(KernelPluginFactory.CreateFromFunctions("KernelPluginName", [KernelFunctionFactory.CreateFromMethod(GetWeather)]));
|
||||
@@ -63,7 +63,7 @@ async Task SKAgent_As_AFAgentAsync()
|
||||
|
||||
async Task AFAgentAsync()
|
||||
{
|
||||
var agent = new OpenAIClient(apiKey).GetResponsesClient(model).CreateAIAgent(
|
||||
var agent = new OpenAIClient(apiKey).GetResponsesClient().AsIChatClient(model).CreateAIAgent(
|
||||
instructions: "You are a helpful assistant",
|
||||
tools: [AIFunctionFactory.Create(GetWeather)]);
|
||||
|
||||
|
||||
+3
-3
@@ -25,7 +25,7 @@ async Task SKAgentAsync()
|
||||
|
||||
var serviceCollection = new ServiceCollection();
|
||||
serviceCollection.AddTransient<Microsoft.SemanticKernel.Agents.Agent>((sp)
|
||||
=> new OpenAIResponseAgent(new OpenAIClient(apiKey).GetResponsesClient(model))
|
||||
=> new OpenAIResponseAgent(new OpenAIClient(apiKey).GetResponsesClient(), model)
|
||||
{
|
||||
Name = "Joker",
|
||||
Instructions = "You are good at telling jokes."
|
||||
@@ -44,7 +44,7 @@ async Task SKAgent_As_AFAgentAsync()
|
||||
|
||||
var serviceCollection = new ServiceCollection();
|
||||
serviceCollection.AddTransient<Microsoft.SemanticKernel.Agents.Agent>((sp)
|
||||
=> new OpenAIResponseAgent(new OpenAIClient(apiKey).GetResponsesClient(model))
|
||||
=> new OpenAIResponseAgent(new OpenAIClient(apiKey).GetResponsesClient(), model)
|
||||
{
|
||||
Name = "Joker",
|
||||
Instructions = "You are good at telling jokes."
|
||||
@@ -65,7 +65,7 @@ async Task AFAgentAsync()
|
||||
|
||||
var serviceCollection = new ServiceCollection();
|
||||
serviceCollection.AddTransient((sp) => new OpenAIClient(apiKey)
|
||||
.GetResponsesClient(model)
|
||||
.GetResponsesClient().AsIChatClient(model)
|
||||
.CreateAIAgent(name: "Joker", instructions: "You are good at telling jokes."));
|
||||
|
||||
await using ServiceProvider serviceProvider = serviceCollection.BuildServiceProvider();
|
||||
|
||||
@@ -33,19 +33,22 @@ internal sealed class Program
|
||||
kernel.ImportPluginFromType<WeatherPlugin>();
|
||||
|
||||
// Start a new conversation session.
|
||||
using RealtimeSession session = await realtimeConversationClient.StartConversationSessionAsync("gpt-4o-realtime-preview");
|
||||
using RealtimeSessionClient session = await realtimeConversationClient.StartConversationSessionAsync("gpt-4o-realtime-preview");
|
||||
|
||||
// Initialize session options.
|
||||
// Session options control connection-wide behavior shared across all conversations,
|
||||
// including audio input format and voice activity detection settings.
|
||||
ConversationSessionOptions sessionOptions = new()
|
||||
RealtimeConversationSessionOptions sessionOptions = new()
|
||||
{
|
||||
Voice = ConversationVoice.Alloy,
|
||||
InputAudioFormat = RealtimeAudioFormat.Pcm16,
|
||||
OutputAudioFormat = RealtimeAudioFormat.Pcm16,
|
||||
InputTranscriptionOptions = new()
|
||||
AudioOptions = new()
|
||||
{
|
||||
Model = "whisper-1",
|
||||
InputAudioOptions = new()
|
||||
{
|
||||
AudioTranscriptionOptions = new()
|
||||
{
|
||||
Model = "whisper-1",
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -58,7 +61,7 @@ internal sealed class Program
|
||||
// If any tools are available, set tool choice to "auto".
|
||||
if (sessionOptions.Tools.Count > 0)
|
||||
{
|
||||
sessionOptions.ToolChoice = ConversationToolChoice.CreateAutoToolChoice();
|
||||
sessionOptions.ToolChoice = RealtimeDefaultToolChoice.Auto;
|
||||
}
|
||||
|
||||
// Configure session with defined options.
|
||||
@@ -66,8 +69,8 @@ internal sealed class Program
|
||||
|
||||
// Items such as user, assistant, or system messages, as well as input audio, can be sent to the session.
|
||||
// An example of sending user message to the session.
|
||||
// ConversationItem can be constructed from Microsoft.SemanticKernel.ChatMessageContent if needed by mapping the relevant fields.
|
||||
await session.AddItemAsync(RealtimeItem.CreateUserMessage(["I'm trying to decide what to wear on my trip."]));
|
||||
// RealtimeItem can be constructed from Microsoft.SemanticKernel.ChatMessageContent if needed by mapping the relevant fields.
|
||||
await session.AddItemAsync(RealtimeItem.CreateUserMessageItem("I'm trying to decide what to wear on my trip."));
|
||||
|
||||
// Use audio file that contains a recorded question: "What's the weather like in San Francisco, California?"
|
||||
string inputAudioPath = FindFile("Assets\\realtime_whats_the_weather_pcm16_24khz_mono.wav");
|
||||
@@ -81,115 +84,132 @@ internal sealed class Program
|
||||
Dictionary<string, StringBuilder> functionArgumentBuildersById = [];
|
||||
|
||||
// Define a loop to receive conversation updates in the session.
|
||||
await foreach (RealtimeUpdate update in session.ReceiveUpdatesAsync())
|
||||
await foreach (RealtimeServerUpdate update in session.ReceiveUpdatesAsync())
|
||||
{
|
||||
// Notification indicating the start of the conversation session.
|
||||
if (update is ConversationSessionStartedUpdate sessionStartedUpdate)
|
||||
if (update is RealtimeServerUpdateSessionCreated sessionStartedUpdate)
|
||||
{
|
||||
Console.WriteLine($"<<< Session started. ID: {sessionStartedUpdate.SessionId}");
|
||||
Console.WriteLine($"<<< Session started. ID: {sessionStartedUpdate.EventId}");
|
||||
Console.WriteLine();
|
||||
}
|
||||
|
||||
// Notification indicating the start of detected voice activity.
|
||||
if (update is InputAudioSpeechStartedUpdate speechStartedUpdate)
|
||||
if (update is RealtimeServerUpdateInputAudioBufferSpeechStarted speechStartedUpdate)
|
||||
{
|
||||
Console.WriteLine(
|
||||
$" -- Voice activity detection started at {speechStartedUpdate.AudioStartTime}");
|
||||
}
|
||||
|
||||
// Notification indicating the end of detected voice activity.
|
||||
if (update is InputAudioSpeechFinishedUpdate speechFinishedUpdate)
|
||||
if (update is RealtimeServerUpdateInputAudioBufferSpeechStopped speechFinishedUpdate)
|
||||
{
|
||||
Console.WriteLine(
|
||||
$" -- Voice activity detection ended at {speechFinishedUpdate.AudioEndTime}");
|
||||
}
|
||||
|
||||
// Notification indicating the start of item streaming, such as a function call or response message.
|
||||
if (update is OutputStreamingStartedUpdate itemStreamingStartedUpdate)
|
||||
if (update is RealtimeServerUpdateResponseOutputItemAdded itemStreamingStartedUpdate)
|
||||
{
|
||||
Console.WriteLine(" -- Begin streaming of new item");
|
||||
if (!string.IsNullOrEmpty(itemStreamingStartedUpdate.FunctionName))
|
||||
if (itemStreamingStartedUpdate.Item is RealtimeFunctionCallItem funcItem)
|
||||
{
|
||||
Console.Write($" {itemStreamingStartedUpdate.FunctionName}: ");
|
||||
Console.Write($" {funcItem.FunctionName}: ");
|
||||
}
|
||||
}
|
||||
|
||||
// Notification about item streaming delta, which may include audio transcript, audio bytes, or function arguments.
|
||||
if (update is OutputDeltaUpdate deltaUpdate)
|
||||
// Notification about audio transcript delta.
|
||||
if (update is RealtimeServerUpdateResponseOutputAudioTranscriptDelta audioTranscriptDelta)
|
||||
{
|
||||
Console.Write(deltaUpdate.AudioTranscript);
|
||||
Console.Write(deltaUpdate.Text);
|
||||
Console.Write(deltaUpdate.FunctionArguments);
|
||||
Console.Write(audioTranscriptDelta.Delta);
|
||||
}
|
||||
|
||||
// Handle audio bytes.
|
||||
if (deltaUpdate.AudioBytes is not null)
|
||||
// Notification about text delta.
|
||||
if (update is RealtimeServerUpdateResponseOutputTextDelta textDelta)
|
||||
{
|
||||
Console.Write(textDelta.Delta);
|
||||
}
|
||||
|
||||
// Notification about audio bytes delta.
|
||||
if (update is RealtimeServerUpdateResponseOutputAudioDelta audioDelta)
|
||||
{
|
||||
if (audioDelta.Delta is not null)
|
||||
{
|
||||
if (!outputAudioStreamsById.TryGetValue(deltaUpdate.ItemId, out MemoryStream? value))
|
||||
if (!outputAudioStreamsById.TryGetValue(audioDelta.ItemId, out MemoryStream? value))
|
||||
{
|
||||
value = new MemoryStream();
|
||||
outputAudioStreamsById[deltaUpdate.ItemId] = value;
|
||||
outputAudioStreamsById[audioDelta.ItemId] = value;
|
||||
}
|
||||
|
||||
value.Write(deltaUpdate.AudioBytes);
|
||||
value.Write(audioDelta.Delta.ToArray());
|
||||
}
|
||||
}
|
||||
|
||||
// Notification about function call arguments delta.
|
||||
if (update is RealtimeServerUpdateResponseFunctionCallArgumentsDelta funcArgsDelta)
|
||||
{
|
||||
if (!functionArgumentBuildersById.TryGetValue(funcArgsDelta.ItemId, out StringBuilder? arguments))
|
||||
{
|
||||
functionArgumentBuildersById[funcArgsDelta.ItemId] = arguments = new();
|
||||
}
|
||||
|
||||
// Handle function arguments.
|
||||
if (!functionArgumentBuildersById.TryGetValue(deltaUpdate.ItemId, out StringBuilder? arguments))
|
||||
if (funcArgsDelta.Delta is not null)
|
||||
{
|
||||
functionArgumentBuildersById[deltaUpdate.ItemId] = arguments = new();
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(deltaUpdate.FunctionArguments))
|
||||
{
|
||||
arguments.Append(deltaUpdate.FunctionArguments);
|
||||
arguments.Append(funcArgsDelta.Delta.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
// Notification indicating the end of item streaming, such as a function call or response message.
|
||||
// At this point, audio transcript can be displayed on console, or a function can be called with aggregated arguments.
|
||||
if (update is OutputStreamingFinishedUpdate itemStreamingFinishedUpdate)
|
||||
if (update is RealtimeServerUpdateResponseOutputItemDone itemStreamingFinishedUpdate)
|
||||
{
|
||||
Console.WriteLine();
|
||||
Console.WriteLine($" -- Item streaming finished, item_id={itemStreamingFinishedUpdate.ItemId}");
|
||||
Console.WriteLine($" -- Item streaming finished, response_id={itemStreamingFinishedUpdate.ResponseId}");
|
||||
|
||||
// If an item is a function call, invoke a function with provided arguments.
|
||||
if (itemStreamingFinishedUpdate.FunctionCallId is not null)
|
||||
if (itemStreamingFinishedUpdate.Item is RealtimeFunctionCallItem functionCallItem)
|
||||
{
|
||||
Console.WriteLine($" + Responding to tool invoked by item: {itemStreamingFinishedUpdate.FunctionName}");
|
||||
Console.WriteLine($" + Responding to tool invoked by item: {functionCallItem.FunctionName}");
|
||||
|
||||
// Parse function name.
|
||||
var (functionName, pluginName) = ParseFunctionName(itemStreamingFinishedUpdate.FunctionName);
|
||||
var (functionName, pluginName) = ParseFunctionName(functionCallItem.FunctionName);
|
||||
|
||||
// Deserialize arguments.
|
||||
var argumentsString = functionArgumentBuildersById[itemStreamingFinishedUpdate.ItemId].ToString();
|
||||
var argumentsString = functionArgumentBuildersById.TryGetValue(functionCallItem.Id, out var sb) ? sb.ToString() : "{}";
|
||||
var arguments = DeserializeArguments(argumentsString);
|
||||
|
||||
// Create a function call content based on received data.
|
||||
var functionCallContent = new FunctionCallContent(
|
||||
functionName: functionName,
|
||||
pluginName: pluginName,
|
||||
id: itemStreamingFinishedUpdate.FunctionCallId,
|
||||
id: functionCallItem.CallId,
|
||||
arguments: arguments);
|
||||
|
||||
// Invoke a function.
|
||||
var resultContent = await functionCallContent.InvokeAsync(kernel);
|
||||
|
||||
// Create a function call output conversation item with function call result.
|
||||
RealtimeItem functionOutputItem = RealtimeItem.CreateFunctionCallOutput(
|
||||
callId: itemStreamingFinishedUpdate.FunctionCallId,
|
||||
output: ProcessFunctionResult(resultContent.Result));
|
||||
RealtimeItem functionOutputItem = RealtimeItem.CreateFunctionCallOutputItem(
|
||||
callId: functionCallItem.CallId,
|
||||
functionOutput: ProcessFunctionResult(resultContent.Result));
|
||||
|
||||
// Send function call output conversation item to the session, so the model can use it for further processing.
|
||||
await session.AddItemAsync(functionOutputItem);
|
||||
}
|
||||
// If an item is a response message, output it to the console.
|
||||
else if (itemStreamingFinishedUpdate.MessageContentParts?.Count > 0)
|
||||
else if (itemStreamingFinishedUpdate.Item is RealtimeMessageItem messageItem && messageItem.Content?.Count > 0)
|
||||
{
|
||||
Console.Write($" + [{itemStreamingFinishedUpdate.MessageRole}]: ");
|
||||
Console.Write($" + [{messageItem.Role}]: ");
|
||||
|
||||
foreach (ConversationContentPart contentPart in itemStreamingFinishedUpdate.MessageContentParts)
|
||||
foreach (RealtimeMessageContentPart contentPart in messageItem.Content)
|
||||
{
|
||||
Console.Write(contentPart.AudioTranscript);
|
||||
if (contentPart is RealtimeOutputAudioMessageContentPart audioContentPart)
|
||||
{
|
||||
Console.Write(audioContentPart.Transcript);
|
||||
}
|
||||
else if (contentPart is RealtimeOutputTextMessageContentPart textContentPart)
|
||||
{
|
||||
Console.Write(textContentPart.Text);
|
||||
}
|
||||
}
|
||||
|
||||
Console.WriteLine();
|
||||
@@ -197,7 +217,7 @@ internal sealed class Program
|
||||
}
|
||||
|
||||
// Notification indicating the completion of transcription from input audio.
|
||||
if (update is InputAudioTranscriptionFinishedUpdate transcriptionCompletedUpdate)
|
||||
if (update is RealtimeServerUpdateConversationItemInputAudioTranscriptionCompleted transcriptionCompletedUpdate)
|
||||
{
|
||||
Console.WriteLine();
|
||||
Console.WriteLine($" -- User audio transcript: {transcriptionCompletedUpdate.Transcript}");
|
||||
@@ -205,13 +225,13 @@ internal sealed class Program
|
||||
}
|
||||
|
||||
// Notification about completed model response turn.
|
||||
if (update is ResponseFinishedUpdate turnFinishedUpdate)
|
||||
if (update is RealtimeServerUpdateResponseDone turnFinishedUpdate)
|
||||
{
|
||||
Console.WriteLine($" -- Model turn generation finished. Status: {turnFinishedUpdate.Status}");
|
||||
Console.WriteLine($" -- Model turn generation finished. Status: {turnFinishedUpdate.Response?.Status}");
|
||||
|
||||
// If the created session items contain a function name, it indicates a function call result has been provided,
|
||||
// If the output items contain a function call, it indicates a function call result has been provided,
|
||||
// and response updates can begin.
|
||||
if (turnFinishedUpdate.CreatedItems.Any(item => item.FunctionName?.Length > 0))
|
||||
if (turnFinishedUpdate.Response?.OutputItems?.Any(item => item is RealtimeFunctionCallItem) == true)
|
||||
{
|
||||
Console.WriteLine(" -- Ending client turn for pending tool responses");
|
||||
|
||||
@@ -225,10 +245,10 @@ internal sealed class Program
|
||||
}
|
||||
|
||||
// Notification about error in conversation session.
|
||||
if (update is RealtimeErrorUpdate errorUpdate)
|
||||
if (update is RealtimeServerUpdateError errorUpdate)
|
||||
{
|
||||
Console.WriteLine();
|
||||
Console.WriteLine($"ERROR: {errorUpdate.Message}");
|
||||
Console.WriteLine($"ERROR: {errorUpdate.Error?.Message}");
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -337,7 +357,7 @@ internal sealed class Program
|
||||
}
|
||||
|
||||
/// <summary>Helper method to convert Kernel plugins/function to realtime session conversation tools.</summary>
|
||||
private static IEnumerable<ConversationTool> ConvertFunctions(Kernel kernel)
|
||||
private static IEnumerable<RealtimeTool> ConvertFunctions(Kernel kernel)
|
||||
{
|
||||
foreach (var plugin in kernel.Plugins)
|
||||
{
|
||||
@@ -347,10 +367,10 @@ internal sealed class Program
|
||||
{
|
||||
var toolDefinition = metadata.ToOpenAIFunction().ToFunctionDefinition(false);
|
||||
|
||||
yield return new ConversationFunctionTool(name: toolDefinition.FunctionName)
|
||||
yield return new RealtimeFunctionTool(functionName: toolDefinition.FunctionName)
|
||||
{
|
||||
Description = toolDefinition.FunctionDescription,
|
||||
Parameters = toolDefinition.FunctionParameters
|
||||
FunctionDescription = toolDefinition.FunctionDescription,
|
||||
FunctionParameters = toolDefinition.FunctionParameters
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
+5
-5
@@ -15,7 +15,7 @@ public class Step01_OpenAIResponseAgent(ITestOutputHelper output) : BaseResponse
|
||||
public async Task UseOpenAIResponseAgentAsync()
|
||||
{
|
||||
// Define the agent
|
||||
OpenAIResponseAgent agent = new(this.Client)
|
||||
OpenAIResponseAgent agent = new(this.Client, this.ModelId)
|
||||
{
|
||||
Name = "ResponseAgent",
|
||||
Instructions = "Answer all queries in English and French.",
|
||||
@@ -33,7 +33,7 @@ public class Step01_OpenAIResponseAgent(ITestOutputHelper output) : BaseResponse
|
||||
public async Task UseOpenAIResponseAgentStreamingAsync()
|
||||
{
|
||||
// Define the agent
|
||||
OpenAIResponseAgent agent = new(this.Client)
|
||||
OpenAIResponseAgent agent = new(this.Client, this.ModelId)
|
||||
{
|
||||
Name = "ResponseAgent",
|
||||
Instructions = "Answer all queries in English and French.",
|
||||
@@ -48,7 +48,7 @@ public class Step01_OpenAIResponseAgent(ITestOutputHelper output) : BaseResponse
|
||||
public async Task UseOpenAIResponseAgentWithThreadedConversationAsync()
|
||||
{
|
||||
// Define the agent
|
||||
OpenAIResponseAgent agent = new(this.Client)
|
||||
OpenAIResponseAgent agent = new(this.Client, this.ModelId)
|
||||
{
|
||||
Name = "ResponseAgent",
|
||||
Instructions = "Answer all queries in the users preferred language.",
|
||||
@@ -84,7 +84,7 @@ public class Step01_OpenAIResponseAgent(ITestOutputHelper output) : BaseResponse
|
||||
public async Task UseOpenAIResponseAgentWithThreadedConversationStreamingAsync()
|
||||
{
|
||||
// Define the agent
|
||||
OpenAIResponseAgent agent = new(this.Client)
|
||||
OpenAIResponseAgent agent = new(this.Client, this.ModelId)
|
||||
{
|
||||
Name = "ResponseAgent",
|
||||
Instructions = "Answer all queries in the users preferred language.",
|
||||
@@ -116,7 +116,7 @@ public class Step01_OpenAIResponseAgent(ITestOutputHelper output) : BaseResponse
|
||||
public async Task UseOpenAIResponseAgentWithImageContentAsync()
|
||||
{
|
||||
// Define the agent
|
||||
OpenAIResponseAgent agent = new(this.Client)
|
||||
OpenAIResponseAgent agent = new(this.Client, this.ModelId)
|
||||
{
|
||||
Name = "ResponseAgent",
|
||||
Instructions = "Provide a detailed description including the weather conditions.",
|
||||
|
||||
+6
-6
@@ -18,7 +18,7 @@ public class Step02_OpenAIResponseAgent_ConversationState(ITestOutputHelper outp
|
||||
public async Task ManuallyConstructPastConversationAsync()
|
||||
{
|
||||
// Define the agent
|
||||
OpenAIResponseAgent agent = new(this.Client)
|
||||
OpenAIResponseAgent agent = new(this.Client, this.ModelId)
|
||||
{
|
||||
StoreEnabled = false,
|
||||
};
|
||||
@@ -46,7 +46,7 @@ public class Step02_OpenAIResponseAgent_ConversationState(ITestOutputHelper outp
|
||||
public async Task ManuallyConstructPastConversationStreamingAsync()
|
||||
{
|
||||
// Define the agent
|
||||
OpenAIResponseAgent agent = new(this.Client)
|
||||
OpenAIResponseAgent agent = new(this.Client, this.ModelId)
|
||||
{
|
||||
StoreEnabled = false,
|
||||
};
|
||||
@@ -75,7 +75,7 @@ public class Step02_OpenAIResponseAgent_ConversationState(ITestOutputHelper outp
|
||||
public async Task ManageConversationStateWithResponseIdAsync()
|
||||
{
|
||||
// Define the agent
|
||||
OpenAIResponseAgent agent = new(this.Client)
|
||||
OpenAIResponseAgent agent = new(this.Client, this.ModelId)
|
||||
{
|
||||
StoreEnabled = false,
|
||||
};
|
||||
@@ -106,7 +106,7 @@ public class Step02_OpenAIResponseAgent_ConversationState(ITestOutputHelper outp
|
||||
public async Task ManageConversationStateWithResponseIdStreamingAsync()
|
||||
{
|
||||
// Define the agent
|
||||
OpenAIResponseAgent agent = new(this.Client)
|
||||
OpenAIResponseAgent agent = new(this.Client, this.ModelId)
|
||||
{
|
||||
StoreEnabled = false,
|
||||
};
|
||||
@@ -138,7 +138,7 @@ public class Step02_OpenAIResponseAgent_ConversationState(ITestOutputHelper outp
|
||||
public async Task StoreConversationStateAsync()
|
||||
{
|
||||
// Define the agent
|
||||
OpenAIResponseAgent agent = new(this.Client)
|
||||
OpenAIResponseAgent agent = new(this.Client, this.ModelId)
|
||||
{
|
||||
StoreEnabled = true,
|
||||
};
|
||||
@@ -186,7 +186,7 @@ public class Step02_OpenAIResponseAgent_ConversationState(ITestOutputHelper outp
|
||||
public async Task StoreConversationStateWithStreamingAsync()
|
||||
{
|
||||
// Define the agent
|
||||
OpenAIResponseAgent agent = new(this.Client)
|
||||
OpenAIResponseAgent agent = new(this.Client, this.ModelId)
|
||||
{
|
||||
StoreEnabled = true,
|
||||
};
|
||||
|
||||
+3
-3
@@ -15,7 +15,7 @@ public class Step03_OpenAIResponseAgent_ReasoningModel(ITestOutputHelper output)
|
||||
public async Task UseOpenAIResponseAgentWithAReasoningModelAsync()
|
||||
{
|
||||
// Define the agent
|
||||
OpenAIResponseAgent agent = new(this.Client)
|
||||
OpenAIResponseAgent agent = new(this.Client, this.ModelId)
|
||||
{
|
||||
Name = "ResponseAgent",
|
||||
Instructions = "Answer all queries with a detailed response.",
|
||||
@@ -33,7 +33,7 @@ public class Step03_OpenAIResponseAgent_ReasoningModel(ITestOutputHelper output)
|
||||
public async Task UseOpenAIResponseAgentWithAReasoningModelAndSummariesAsync()
|
||||
{
|
||||
// Define the agent
|
||||
OpenAIResponseAgent agent = new(this.Client);
|
||||
OpenAIResponseAgent agent = new(this.Client, this.ModelId);
|
||||
|
||||
// ResponseCreationOptions allows you to specify tools for the agent.
|
||||
OpenAIResponseAgentInvokeOptions invokeOptions = new()
|
||||
@@ -86,7 +86,7 @@ public class Step03_OpenAIResponseAgent_ReasoningModel(ITestOutputHelper output)
|
||||
public async Task UseOpenAIResponseAgentWithAReasoningModelAndToolsAsync()
|
||||
{
|
||||
// Define the agent
|
||||
OpenAIResponseAgent agent = new(this.Client)
|
||||
OpenAIResponseAgent agent = new(this.Client, this.ModelId)
|
||||
{
|
||||
Name = "ResponseAgent",
|
||||
Instructions = "Answer all queries with a detailed response.",
|
||||
|
||||
+4
-4
@@ -22,7 +22,7 @@ public class Step04_OpenAIResponseAgent_Tools(ITestOutputHelper output) : BaseRe
|
||||
public async Task InvokeAgentWithFunctionToolsAsync()
|
||||
{
|
||||
// Define the agent
|
||||
OpenAIResponseAgent agent = new(this.Client)
|
||||
OpenAIResponseAgent agent = new(this.Client, this.ModelId)
|
||||
{
|
||||
StoreEnabled = false,
|
||||
};
|
||||
@@ -53,7 +53,7 @@ public class Step04_OpenAIResponseAgent_Tools(ITestOutputHelper output) : BaseRe
|
||||
public async Task InvokeAgentWithWebSearchAsync()
|
||||
{
|
||||
// Define the agent
|
||||
OpenAIResponseAgent agent = new(this.Client)
|
||||
OpenAIResponseAgent agent = new(this.Client, this.ModelId)
|
||||
{
|
||||
StoreEnabled = false,
|
||||
};
|
||||
@@ -89,7 +89,7 @@ public class Step04_OpenAIResponseAgent_Tools(ITestOutputHelper output) : BaseRe
|
||||
});
|
||||
|
||||
// Define the agent
|
||||
OpenAIResponseAgent agent = new(this.Client)
|
||||
OpenAIResponseAgent agent = new(this.Client, this.ModelId)
|
||||
{
|
||||
StoreEnabled = false,
|
||||
};
|
||||
@@ -131,7 +131,7 @@ public class Step04_OpenAIResponseAgent_Tools(ITestOutputHelper output) : BaseRe
|
||||
public async Task InvokeAgentWithMultipleToolsAsync()
|
||||
{
|
||||
// Define the agent
|
||||
OpenAIResponseAgent agent = new(this.Client)
|
||||
OpenAIResponseAgent agent = new(this.Client, this.ModelId)
|
||||
{
|
||||
StoreEnabled = false,
|
||||
};
|
||||
|
||||
@@ -29,11 +29,18 @@ internal static class KernelContentExtensions
|
||||
|
||||
internal static ResponseContentPart ToResponseContentPart(this ImageContent content)
|
||||
{
|
||||
return content.Uri is not null
|
||||
? ResponseContentPart.CreateInputImagePart(content.Uri)
|
||||
: content.Data is not null
|
||||
? ResponseContentPart.CreateInputImagePart(new BinaryData(content.Data), content.MimeType)
|
||||
: throw new NotSupportedException("ImageContent cannot be converted to ResponseContentPart. Only ImageContent with a uri or binary data is supported.");
|
||||
if (content.Uri is not null)
|
||||
{
|
||||
return ResponseContentPart.CreateInputImagePart(content.Uri);
|
||||
}
|
||||
|
||||
if (content.Data is not null)
|
||||
{
|
||||
var dataUri = new Uri($"data:{content.MimeType};base64,{Convert.ToBase64String(content.Data.Value.ToArray())}");
|
||||
return ResponseContentPart.CreateInputImagePart(dataUri);
|
||||
}
|
||||
|
||||
throw new NotSupportedException("ImageContent cannot be converted to ResponseContentPart. Only ImageContent with a uri or binary data is supported.");
|
||||
}
|
||||
|
||||
internal static ResponseContentPart ToResponseContentPart(this BinaryContent content)
|
||||
|
||||
@@ -23,6 +23,7 @@ internal static class ResponseCreationOptionsFactory
|
||||
{
|
||||
creationOptions = new CreateResponseOptions
|
||||
{
|
||||
Model = responseAgentInvokeOptions.ResponseCreationOptions.Model ?? agent.ModelId,
|
||||
EndUserId = responseAgentInvokeOptions.ResponseCreationOptions.EndUserId ?? agent.GetDisplayName(),
|
||||
Instructions = responseAgentInvokeOptions.ResponseCreationOptions.Instructions ?? instructions,
|
||||
StoredOutputEnabled = responseAgentInvokeOptions.ResponseCreationOptions.StoredOutputEnabled ?? agent.StoreEnabled,
|
||||
@@ -44,6 +45,7 @@ internal static class ResponseCreationOptionsFactory
|
||||
{
|
||||
creationOptions = new CreateResponseOptions
|
||||
{
|
||||
Model = agent.ModelId,
|
||||
EndUserId = agent.GetDisplayName(),
|
||||
Instructions = instructions,
|
||||
StoredOutputEnabled = agent.StoreEnabled,
|
||||
|
||||
@@ -22,11 +22,13 @@ public sealed class OpenAIResponseAgent : Agent
|
||||
/// Initializes a new instance of the <see cref="OpenAIResponseAgent"/> class.
|
||||
/// </summary>
|
||||
/// <param name="client">The OpenAI provider for accessing the Responses API service.</param>
|
||||
public OpenAIResponseAgent(ResponsesClient client)
|
||||
/// <param name="modelId">The model to use for generating responses.</param>
|
||||
public OpenAIResponseAgent(ResponsesClient client, string? modelId = null)
|
||||
{
|
||||
Verify.NotNull(client);
|
||||
|
||||
this.Client = client;
|
||||
this.ModelId = modelId;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -34,6 +36,11 @@ public sealed class OpenAIResponseAgent : Agent
|
||||
/// </summary>
|
||||
public ResponsesClient Client { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The model to use for generating responses.
|
||||
/// </summary>
|
||||
public string? ModelId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Storing of messages is enabled.
|
||||
/// </summary>
|
||||
|
||||
@@ -27,7 +27,7 @@ public class BaseOpenAIResponseClientTest : IDisposable
|
||||
{
|
||||
Transport = new HttpClientPipelineTransport(this.HttpClient)
|
||||
};
|
||||
this.Client = new ResponsesClient("model", new ApiKeyCredential("apiKey"), clientOptions);
|
||||
this.Client = new ResponsesClient(new ApiKeyCredential("apiKey"), clientOptions);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -238,14 +238,13 @@ public class OpenAIResponseExtensionsTests
|
||||
return result;
|
||||
}
|
||||
|
||||
private ResponseResult CreateMockOpenAIResponse(string id, DateTimeOffset createdAt, ResponseError error, string instructions, string model, string previousResponseId, float temperature, IEnumerable<ResponseTool> tools, float topP, IDictionary<string, string> metadata, ResponseIncompleteStatusDetails incompleteStatusDetails, IEnumerable<ResponseItem> outputItems, bool parallelToolCallsEnabled, ResponseToolChoice toolChoice)
|
||||
private ResponseResult CreateMockOpenAIResponse(string id, DateTimeOffset createdAt, ResponseError error, string model, string previousResponseId, float temperature, IEnumerable<ResponseTool> tools, float topP, IDictionary<string, string> metadata, ResponseIncompleteStatusDetails incompleteStatusDetails, IEnumerable<ResponseItem> outputItems, bool parallelToolCallsEnabled, ResponseToolChoice toolChoice)
|
||||
{
|
||||
var result = new ResponseResult
|
||||
{
|
||||
Id = id,
|
||||
CreatedAt = createdAt,
|
||||
Error = error,
|
||||
Instructions = instructions,
|
||||
Model = model,
|
||||
PreviousResponseId = previousResponseId,
|
||||
Temperature = temperature,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.ClientModel;
|
||||
using System.Text.Json;
|
||||
using Microsoft.SemanticKernel.Agents;
|
||||
using Microsoft.SemanticKernel.Agents.OpenAI;
|
||||
@@ -16,7 +17,7 @@ public sealed class OpenAIResponseAgentExtensionsTests
|
||||
public void AsAIAgent_WithValidOpenAIResponseAgent_ReturnsSemanticKernelAIAgent()
|
||||
{
|
||||
// Arrange
|
||||
var responseClient = new ResponsesClient("model", "apikey");
|
||||
var responseClient = new ResponsesClient(new ApiKeyCredential("apikey"));
|
||||
var responseAgent = new OpenAIResponseAgent(responseClient);
|
||||
|
||||
// Act
|
||||
@@ -41,7 +42,7 @@ public sealed class OpenAIResponseAgentExtensionsTests
|
||||
public void AsAIAgent_CreatesWorkingThreadFactoryStoreTrue()
|
||||
{
|
||||
// Arrange
|
||||
var responseClient = new ResponsesClient("model", "apikey");
|
||||
var responseClient = new ResponsesClient(new ApiKeyCredential("apikey"));
|
||||
var responseAgent = new OpenAIResponseAgent(responseClient)
|
||||
{
|
||||
StoreEnabled = true
|
||||
@@ -62,7 +63,7 @@ public sealed class OpenAIResponseAgentExtensionsTests
|
||||
public void AsAIAgent_CreatesWorkingThreadFactoryStoreFalse()
|
||||
{
|
||||
// Arrange
|
||||
var responseClient = new ResponsesClient("model", "apikey");
|
||||
var responseClient = new ResponsesClient(new ApiKeyCredential("apikey"));
|
||||
var responseAgent = new OpenAIResponseAgent(responseClient)
|
||||
{
|
||||
StoreEnabled = false
|
||||
@@ -83,7 +84,7 @@ public sealed class OpenAIResponseAgentExtensionsTests
|
||||
public void AsAIAgent_ThreadDeserializationFactory_WithNullAgentId_CreatesNewThread()
|
||||
{
|
||||
// Arrange
|
||||
var responseClient = new ResponsesClient("model", "apikey");
|
||||
var responseClient = new ResponsesClient(new ApiKeyCredential("apikey"));
|
||||
var responseAgent = new OpenAIResponseAgent(responseClient)
|
||||
{
|
||||
StoreEnabled = true
|
||||
@@ -105,7 +106,7 @@ public sealed class OpenAIResponseAgentExtensionsTests
|
||||
public void AsAIAgent_ThreadDeserializationFactory_WithValidAgentId_CreatesThreadWithId()
|
||||
{
|
||||
// Arrange
|
||||
var responseClient = new ResponsesClient("model", "apikey");
|
||||
var responseClient = new ResponsesClient(new ApiKeyCredential("apikey"));
|
||||
var responseAgent = new OpenAIResponseAgent(responseClient)
|
||||
{
|
||||
StoreEnabled = true
|
||||
@@ -129,7 +130,7 @@ public sealed class OpenAIResponseAgentExtensionsTests
|
||||
public void AsAIAgent_ThreadSerializer_SerializesThreadId()
|
||||
{
|
||||
// Arrange
|
||||
var responseClient = new ResponsesClient("model", "apikey");
|
||||
var responseClient = new ResponsesClient(new ApiKeyCredential("apikey"));
|
||||
var responseAgent = new OpenAIResponseAgent(responseClient)
|
||||
{
|
||||
StoreEnabled = true
|
||||
@@ -152,7 +153,7 @@ public sealed class OpenAIResponseAgentExtensionsTests
|
||||
[Fact]
|
||||
public void AsAIAgent_ThreadDeserializationFactory_WithNullJson_CreatesThreadWithEmptyChatHistory()
|
||||
{
|
||||
var responseClient = new ResponsesClient("model", "apikey");
|
||||
var responseClient = new ResponsesClient(new ApiKeyCredential("apikey"));
|
||||
var responseAgent = new OpenAIResponseAgent(responseClient);
|
||||
var jsonElement = JsonSerializer.SerializeToElement((string?)null);
|
||||
|
||||
@@ -171,7 +172,7 @@ public sealed class OpenAIResponseAgentExtensionsTests
|
||||
[Fact]
|
||||
public void AsAIAgent_ThreadDeserializationFactory_WithChatHistory_CreatesThreadWithChatHistory()
|
||||
{
|
||||
var responseClient = new ResponsesClient("model", "apikey");
|
||||
var responseClient = new ResponsesClient(new ApiKeyCredential("apikey"));
|
||||
var responseAgent = new OpenAIResponseAgent(responseClient);
|
||||
var expectedChatHistory = new ChatHistory("mock message", AuthorRole.User);
|
||||
var jsonElement = JsonSerializer.SerializeToElement(expectedChatHistory);
|
||||
@@ -195,7 +196,7 @@ public sealed class OpenAIResponseAgentExtensionsTests
|
||||
public void AsAIAgent_ThreadSerializer_SerializesChatHistory()
|
||||
{
|
||||
// Arrange
|
||||
var responseClient = new ResponsesClient("model", "apikey");
|
||||
var responseClient = new ResponsesClient(new ApiKeyCredential("apikey"));
|
||||
var responseAgent = new OpenAIResponseAgent(responseClient);
|
||||
var expectedChatHistory = new ChatHistory("mock message", AuthorRole.User);
|
||||
var jsonElement = JsonSerializer.SerializeToElement(expectedChatHistory);
|
||||
|
||||
@@ -122,8 +122,8 @@ internal partial class ClientCore
|
||||
{
|
||||
"STANDARD" => GeneratedImageQuality.Standard,
|
||||
"HIGH" or "HD" => GeneratedImageQuality.High,
|
||||
"MEDIUM" => GeneratedImageQuality.Medium,
|
||||
"LOW" => GeneratedImageQuality.Low,
|
||||
"MEDIUM" => GeneratedImageQuality.MediumQuality,
|
||||
"LOW" => GeneratedImageQuality.LowQuality,
|
||||
"AUTO" => GeneratedImageQuality.Auto,
|
||||
_ => throw new NotSupportedException($"The provided quality '{quality}' is not supported.")
|
||||
};
|
||||
|
||||
+4
-4
@@ -95,12 +95,12 @@ public class OpenAIResponseAgentFixture : AgentFixture
|
||||
{
|
||||
OpenAIConfiguration configuration = this._configuration.GetSection("OpenAI").Get<OpenAIConfiguration>()!;
|
||||
var options = new OpenAIClientOptions();
|
||||
this._responseClient = new(model: configuration.ChatModelId, credential: new ApiKeyCredential(configuration.ApiKey), options: options);
|
||||
this._responseClient = new(credential: new ApiKeyCredential(configuration.ApiKey), options: options);
|
||||
|
||||
var kernelBuilder = Kernel.CreateBuilder();
|
||||
Kernel kernel = kernelBuilder.Build();
|
||||
|
||||
this._agent = new OpenAIResponseAgent(this._responseClient)
|
||||
this._agent = new OpenAIResponseAgent(this._responseClient, configuration.ChatModelId)
|
||||
{
|
||||
Name = "HelpfulAssistant",
|
||||
Instructions = "You are a helpful assistant.",
|
||||
@@ -109,10 +109,10 @@ public class OpenAIResponseAgentFixture : AgentFixture
|
||||
};
|
||||
this._thread = new OpenAIResponseAgentThread(this._responseClient);
|
||||
|
||||
var response = await this._responseClient.CreateResponseAsync([ResponseItem.CreateUserMessageItem("Hello")]);
|
||||
var response = await this._responseClient.CreateResponseAsync(new CreateResponseOptions(configuration.ChatModelId, [ResponseItem.CreateUserMessageItem("Hello")]));
|
||||
this._createdThread = new OpenAIResponseAgentThread(this._responseClient, response.Value.Id);
|
||||
|
||||
var serviceFailingClient = new ResponsesClient(configuration.ModelId, credential: new ApiKeyCredential("FakeApiKey"), options: options);
|
||||
var serviceFailingClient = new ResponsesClient(credential: new ApiKeyCredential("FakeApiKey"), options: options);
|
||||
this._serviceFailingAgentThread = new OpenAIResponseAgentThread(serviceFailingClient);
|
||||
|
||||
this._createdServiceFailingAgentThread = new OpenAIResponseAgentThread(this._responseClient, "FakeId");
|
||||
|
||||
@@ -43,10 +43,11 @@ public sealed class OpenAIResponseAgentTests(ITestOutputHelper output)
|
||||
[InlineData("What is the capital of France?", "Paris", false, false)]
|
||||
public async Task OpenAIResponseAgentInvokeAsync(string input, string expectedAnswerContains, bool isOpenAI, bool storeEnabled)
|
||||
{
|
||||
ResponsesClient client = this.CreateClient(isOpenAI);
|
||||
var (client, modelId) = this.CreateClient(isOpenAI);
|
||||
|
||||
await this.ExecuteAgentAsync(
|
||||
client,
|
||||
modelId,
|
||||
storeEnabled,
|
||||
input,
|
||||
expectedAnswerContains);
|
||||
@@ -63,8 +64,8 @@ public sealed class OpenAIResponseAgentTests(ITestOutputHelper output)
|
||||
public async Task OpenAIResponseAgentInvokeWithThreadAsync(string input, string expectedAnswerContains, bool isOpenAI, bool storeEnabled)
|
||||
{
|
||||
// Arrange
|
||||
ResponsesClient client = this.CreateClient(isOpenAI);
|
||||
OpenAIResponseAgent agent = new(client)
|
||||
var (client, modelId) = this.CreateClient(isOpenAI);
|
||||
OpenAIResponseAgent agent = new(client, modelId)
|
||||
{
|
||||
StoreEnabled = storeEnabled,
|
||||
Instructions = "Answer all queries in English and French."
|
||||
@@ -112,9 +113,9 @@ public sealed class OpenAIResponseAgentTests(ITestOutputHelper output)
|
||||
public async Task OpenAIResponseAgentInvokeWithFunctionCallingAsync(string input, string expectedAnswerContains, bool isOpenAI, bool storeEnabled)
|
||||
{
|
||||
// Arrange
|
||||
ResponsesClient client = this.CreateClient(isOpenAI);
|
||||
var (client, modelId) = this.CreateClient(isOpenAI);
|
||||
KernelPlugin plugin = KernelPluginFactory.CreateFromType<MenuPlugin>();
|
||||
OpenAIResponseAgent agent = new(client)
|
||||
OpenAIResponseAgent agent = new(client, modelId)
|
||||
{
|
||||
StoreEnabled = storeEnabled,
|
||||
Instructions = "Answer questions about the menu."
|
||||
@@ -161,10 +162,11 @@ public sealed class OpenAIResponseAgentTests(ITestOutputHelper output)
|
||||
[InlineData("What is the capital of France?", "Paris", false, false)]
|
||||
public async Task OpenAIResponseAgentInvokeStreamingAsync(string input, string expectedAnswerContains, bool isOpenAI, bool storeEnabled)
|
||||
{
|
||||
ResponsesClient client = this.CreateClient(isOpenAI);
|
||||
var (client, modelId) = this.CreateClient(isOpenAI);
|
||||
|
||||
await this.ExecuteStreamingAgentAsync(
|
||||
client,
|
||||
modelId,
|
||||
storeEnabled,
|
||||
input,
|
||||
expectedAnswerContains);
|
||||
@@ -181,8 +183,8 @@ public sealed class OpenAIResponseAgentTests(ITestOutputHelper output)
|
||||
public async Task OpenAIResponseAgentInvokeStreamingWithThreadAsync(bool isOpenAI, bool storeEnabled)
|
||||
{
|
||||
// Arrange
|
||||
ResponsesClient client = this.CreateClient(isOpenAI);
|
||||
OpenAIResponseAgent agent = new(client)
|
||||
var (client, modelId) = this.CreateClient(isOpenAI);
|
||||
OpenAIResponseAgent agent = new(client, modelId)
|
||||
{
|
||||
StoreEnabled = storeEnabled,
|
||||
Instructions = "Answer all queries in English and French."
|
||||
@@ -229,9 +231,9 @@ public sealed class OpenAIResponseAgentTests(ITestOutputHelper output)
|
||||
public async Task OpenAIResponseAgentInvokeStreamingWithFunctionCallingAsync(string input, string expectedAnswerContains, bool isOpenAI, bool storeEnabled)
|
||||
{
|
||||
// Arrange
|
||||
ResponsesClient client = this.CreateClient(isOpenAI);
|
||||
var (client, modelId) = this.CreateClient(isOpenAI);
|
||||
KernelPlugin plugin = KernelPluginFactory.CreateFromType<MenuPlugin>();
|
||||
OpenAIResponseAgent agent = new(client)
|
||||
OpenAIResponseAgent agent = new(client, modelId)
|
||||
{
|
||||
StoreEnabled = storeEnabled,
|
||||
Instructions = "Answer questions about the menu."
|
||||
@@ -274,12 +276,13 @@ public sealed class OpenAIResponseAgentTests(ITestOutputHelper output)
|
||||
|
||||
private async Task ExecuteAgentAsync(
|
||||
ResponsesClient client,
|
||||
string modelId,
|
||||
bool storeEnabled,
|
||||
string input,
|
||||
string expected)
|
||||
{
|
||||
// Arrange
|
||||
OpenAIResponseAgent agent = new(client)
|
||||
OpenAIResponseAgent agent = new(client, modelId)
|
||||
{
|
||||
StoreEnabled = storeEnabled,
|
||||
Instructions = "Answer all queries in English and French."
|
||||
@@ -306,12 +309,13 @@ public sealed class OpenAIResponseAgentTests(ITestOutputHelper output)
|
||||
|
||||
private async Task ExecuteStreamingAgentAsync(
|
||||
ResponsesClient client,
|
||||
string modelId,
|
||||
bool storeEnabled,
|
||||
string input,
|
||||
string expected)
|
||||
{
|
||||
// Arrange
|
||||
OpenAIResponseAgent agent = new(client)
|
||||
OpenAIResponseAgent agent = new(client, modelId)
|
||||
{
|
||||
StoreEnabled = storeEnabled,
|
||||
Instructions = "Answer all queries in English and French."
|
||||
@@ -355,22 +359,17 @@ public sealed class OpenAIResponseAgentTests(ITestOutputHelper output)
|
||||
return configuration;
|
||||
}
|
||||
|
||||
private ResponsesClient CreateClient(bool isOpenAI)
|
||||
private (ResponsesClient Client, string ModelId) CreateClient(bool isOpenAI)
|
||||
{
|
||||
ResponsesClient client;
|
||||
if (isOpenAI)
|
||||
{
|
||||
client = this.CreateClient(this._configuration.GetSection("OpenAI").Get<OpenAIConfiguration>());
|
||||
}
|
||||
else
|
||||
{
|
||||
client = this.CreateClient(this._configuration.GetSection("AzureOpenAI").Get<AzureOpenAIConfiguration>());
|
||||
return this.CreateClient(this._configuration.GetSection("OpenAI").Get<OpenAIConfiguration>());
|
||||
}
|
||||
|
||||
return client;
|
||||
return this.CreateClient(this._configuration.GetSection("AzureOpenAI").Get<AzureOpenAIConfiguration>());
|
||||
}
|
||||
|
||||
private ResponsesClient CreateClient(OpenAIConfiguration? configuration)
|
||||
private (ResponsesClient, string) CreateClient(OpenAIConfiguration? configuration)
|
||||
{
|
||||
Assert.NotNull(configuration);
|
||||
|
||||
@@ -387,10 +386,10 @@ public sealed class OpenAIResponseAgentTests(ITestOutputHelper output)
|
||||
};
|
||||
}
|
||||
|
||||
return new ResponsesClient(configuration.ChatModelId, new ApiKeyCredential(configuration.ApiKey), options);
|
||||
return (new ResponsesClient(new ApiKeyCredential(configuration.ApiKey), options), configuration.ChatModelId!);
|
||||
}
|
||||
|
||||
private ResponsesClient CreateClient(AzureOpenAIConfiguration? configuration)
|
||||
private (ResponsesClient, string) CreateClient(AzureOpenAIConfiguration? configuration)
|
||||
{
|
||||
Assert.NotNull(configuration);
|
||||
|
||||
@@ -408,7 +407,7 @@ public sealed class OpenAIResponseAgentTests(ITestOutputHelper output)
|
||||
}
|
||||
|
||||
var azureClient = new AzureOpenAIClient(new Uri(configuration.Endpoint), new AzureCliCredential(), options);
|
||||
return azureClient.GetResponsesClient(configuration.ChatDeploymentName);
|
||||
return (azureClient.GetResponsesClient(), configuration.ChatDeploymentName!);
|
||||
}
|
||||
|
||||
public sealed class MenuPlugin
|
||||
|
||||
@@ -27,11 +27,14 @@ public abstract class BaseResponsesAgentTest : BaseAgentsTest<ResponsesClient>
|
||||
});
|
||||
}
|
||||
|
||||
this.Client = new(model: model ?? TestConfiguration.OpenAI.ChatModelId, credential: new ApiKeyCredential(TestConfiguration.OpenAI.ApiKey), options: options);
|
||||
this.ModelId = model ?? TestConfiguration.OpenAI.ChatModelId;
|
||||
this.Client = new(credential: new ApiKeyCredential(TestConfiguration.OpenAI.ApiKey), options: options);
|
||||
this.FileClient = new OpenAIFileClient(TestConfiguration.OpenAI.ApiKey);
|
||||
this.VectorStoreClient = new VectorStoreClient(TestConfiguration.OpenAI.ApiKey);
|
||||
}
|
||||
|
||||
protected string ModelId { get; set; }
|
||||
|
||||
protected OpenAIFileClient FileClient { get; set; }
|
||||
|
||||
protected VectorStoreClient VectorStoreClient { get; set; }
|
||||
|
||||
Reference in New Issue
Block a user