Skip to content

Commit cc328a7

Browse files
authored
Update to OpenAI 2.8.0 (#7136)
* Update to OpenAI 2.8.0 * Fix merge and address feedback
1 parent e30c4c0 commit cc328a7

File tree

13 files changed

+336
-325
lines changed

13 files changed

+336
-325
lines changed

eng/packages/General.props

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
<PackageVersion Include="ModelContextProtocol.Core" Version="0.4.0-preview.3" />
2121
<PackageVersion Include="Newtonsoft.Json" Version="13.0.3" />
2222
<PackageVersion Include="OllamaSharp" Version="5.1.9" />
23-
<PackageVersion Include="OpenAI" Version="2.7.0" />
23+
<PackageVersion Include="OpenAI" Version="2.8.0" />
2424
<PackageVersion Include="Polly" Version="8.4.2" />
2525
<PackageVersion Include="Polly.Core" Version="8.4.2" />
2626
<PackageVersion Include="Polly.Extensions" Version="8.4.2" />

src/Libraries/Microsoft.Extensions.AI.OpenAI/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
## 10.1.1-preview.1.? (NOT YET RELEASED)
44

5+
- Updated to depend on OpenAI 2.8.0.
6+
- Updated public API signatures in `OpenAIClientExtensions` and `MicrosoftExtensionsAIResponsesExtensions` to match the corresponding breaking changes in OpenAI's Responses APIs.
57
- Updated to accommodate the additions in `Microsoft.Extensions.AI.Abstractions`.
68
- Updated the OpenAI Responses and Chat Completion `IChatClient`s to populate `UsageDetails`'s `InputCachedTokenCount` and `ReasoningTokenCount`.
79
- Updated handling of `HostedWebSearchTool`, `HostedFileSearchTool`, and `HostedImageGenerationTool` to pull OpenAI-specific

src/Libraries/Microsoft.Extensions.AI.OpenAI/MicrosoftExtensionsAIResponsesExtensions.cs

Lines changed: 32 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -56,12 +56,12 @@ public static IEnumerable<ResponseItem> AsOpenAIResponseItems(this IEnumerable<C
5656
public static IEnumerable<ChatMessage> AsChatMessages(this IEnumerable<ResponseItem> items) =>
5757
OpenAIResponsesChatClient.ToChatMessages(Throw.IfNull(items));
5858

59-
/// <summary>Creates a Microsoft.Extensions.AI <see cref="ChatResponse"/> from an <see cref="OpenAIResponse"/>.</summary>
60-
/// <param name="response">The <see cref="OpenAIResponse"/> to convert to a <see cref="ChatResponse"/>.</param>
59+
/// <summary>Creates a Microsoft.Extensions.AI <see cref="ChatResponse"/> from an <see cref="ResponseResult"/>.</summary>
60+
/// <param name="response">The <see cref="ResponseResult"/> to convert to a <see cref="ChatResponse"/>.</param>
6161
/// <param name="options">The options employed in the creation of the response.</param>
6262
/// <returns>A converted <see cref="ChatResponse"/>.</returns>
6363
/// <exception cref="ArgumentNullException"><paramref name="response"/> is <see langword="null"/>.</exception>
64-
public static ChatResponse AsChatResponse(this OpenAIResponse response, ResponseCreationOptions? options = null) =>
64+
public static ChatResponse AsChatResponse(this ResponseResult response, CreateResponseOptions? options = null) =>
6565
OpenAIResponsesChatClient.FromOpenAIResponse(Throw.IfNull(response), options, conversationId: null);
6666

6767
/// <summary>
@@ -74,35 +74,43 @@ public static ChatResponse AsChatResponse(this OpenAIResponse response, Response
7474
/// <returns>A sequence of converted <see cref="ChatResponseUpdate"/> instances.</returns>
7575
/// <exception cref="ArgumentNullException"><paramref name="responseUpdates"/> is <see langword="null"/>.</exception>
7676
public static IAsyncEnumerable<ChatResponseUpdate> AsChatResponseUpdatesAsync(
77-
this IAsyncEnumerable<StreamingResponseUpdate> responseUpdates, ResponseCreationOptions? options = null, CancellationToken cancellationToken = default) =>
77+
this IAsyncEnumerable<StreamingResponseUpdate> responseUpdates, CreateResponseOptions? options = null, CancellationToken cancellationToken = default) =>
7878
OpenAIResponsesChatClient.FromOpenAIStreamingResponseUpdatesAsync(Throw.IfNull(responseUpdates), options, conversationId: null, cancellationToken: cancellationToken);
7979

80-
/// <summary>Creates an OpenAI <see cref="OpenAIResponse"/> from a <see cref="ChatResponse"/>.</summary>
80+
/// <summary>Creates an OpenAI <see cref="ResponseResult"/> from a <see cref="ChatResponse"/>.</summary>
8181
/// <param name="response">The response to convert.</param>
8282
/// <param name="options">The options employed in the creation of the response.</param>
83-
/// <returns>The created <see cref="OpenAIResponse"/>.</returns>
84-
public static OpenAIResponse AsOpenAIResponse(this ChatResponse response, ChatOptions? options = null)
83+
/// <returns>The created <see cref="ResponseResult"/>.</returns>
84+
public static ResponseResult AsOpenAIResponseResult(this ChatResponse response, ChatOptions? options = null)
8585
{
8686
_ = Throw.IfNull(response);
8787

88-
if (response.RawRepresentation is OpenAIResponse openAIResponse)
88+
if (response.RawRepresentation is ResponseResult openAIResponse)
8989
{
9090
return openAIResponse;
9191
}
9292

93-
return OpenAIResponsesModelFactory.OpenAIResponse(
94-
response.ResponseId,
95-
response.CreatedAt ?? default,
96-
ResponseStatus.Completed,
97-
usage: null, // No way to construct a ResponseTokenUsage right now from external to the OpenAI library
98-
maxOutputTokenCount: options?.MaxOutputTokens,
99-
outputItems: OpenAIResponsesChatClient.ToOpenAIResponseItems(response.Messages, options),
100-
parallelToolCallsEnabled: options?.AllowMultipleToolCalls ?? false,
101-
model: response.ModelId ?? options?.ModelId,
102-
temperature: options?.Temperature,
103-
topP: options?.TopP,
104-
previousResponseId: options?.ConversationId,
105-
instructions: options?.Instructions);
93+
ResponseResult result = new()
94+
{
95+
ConversationOptions = OpenAIClientExtensions.IsConversationId(response.ConversationId) ? new(response.ConversationId) : null,
96+
CreatedAt = response.CreatedAt ?? default,
97+
Id = response.ResponseId,
98+
Instructions = options?.Instructions,
99+
MaxOutputTokenCount = options?.MaxOutputTokens,
100+
Model = response.ModelId ?? options?.ModelId,
101+
ParallelToolCallsEnabled = options?.AllowMultipleToolCalls ?? true,
102+
Status = ResponseStatus.Completed,
103+
Temperature = options?.Temperature,
104+
TopP = options?.TopP,
105+
Usage = OpenAIResponsesChatClient.ToResponseTokenUsage(response.Usage),
106+
};
107+
108+
foreach (var responseItem in OpenAIResponsesChatClient.ToOpenAIResponseItems(response.Messages, options))
109+
{
110+
result.OutputItems.Add(responseItem);
111+
}
112+
113+
return result;
106114
}
107115

108116
/// <summary>Adds the <see cref="ResponseTool"/> to the list of <see cref="AITool"/>s.</summary>
@@ -111,7 +119,7 @@ public static OpenAIResponse AsOpenAIResponse(this ChatResponse response, ChatOp
111119
/// <remarks>
112120
/// <see cref="ResponseTool"/> does not derive from <see cref="AITool"/>, so it cannot be added directly to a list of <see cref="AITool"/>s.
113121
/// Instead, this method wraps the provided <see cref="ResponseTool"/> in an <see cref="AITool"/> and adds that to the list.
114-
/// The <see cref="IChatClient"/> returned by <see cref="OpenAIClientExtensions.AsIChatClient(OpenAIResponseClient)"/> will
122+
/// The <see cref="IChatClient"/> returned by <see cref="OpenAIClientExtensions.AsIChatClient(ResponsesClient)"/> will
115123
/// be able to unwrap the <see cref="ResponseTool"/> when it processes the list of tools and use the provided <paramref name="tool"/> as-is.
116124
/// </remarks>
117125
public static void Add(this IList<AITool> tools, ResponseTool tool)
@@ -127,7 +135,7 @@ public static void Add(this IList<AITool> tools, ResponseTool tool)
127135
/// <remarks>
128136
/// <para>
129137
/// The returned tool is only suitable for use with the <see cref="IChatClient"/> returned by
130-
/// <see cref="OpenAIClientExtensions.AsIChatClient(OpenAIResponseClient)"/> (or <see cref="IChatClient"/>s that delegate
138+
/// <see cref="OpenAIClientExtensions.AsIChatClient(ResponsesClient)"/> (or <see cref="IChatClient"/>s that delegate
131139
/// to such an instance). It is likely to be ignored by any other <see cref="IChatClient"/> implementation.
132140
/// </para>
133141
/// <para>
@@ -136,7 +144,7 @@ public static void Add(this IList<AITool> tools, ResponseTool tool)
136144
/// <see cref="HostedFileSearchTool"/>, those types should be preferred instead of this method, as they are more portable,
137145
/// capable of being respected by any <see cref="IChatClient"/> implementation. This method does not attempt to
138146
/// map the supplied <see cref="ResponseTool"/> to any of those types, it simply wraps it as-is:
139-
/// the <see cref="IChatClient"/> returned by <see cref="OpenAIClientExtensions.AsIChatClient(OpenAIResponseClient)"/> will
147+
/// the <see cref="IChatClient"/> returned by <see cref="OpenAIClientExtensions.AsIChatClient(ResponsesClient)"/> will
140148
/// be able to unwrap the <see cref="ResponseTool"/> when it processes the list of tools.
141149
/// </para>
142150
/// </remarks>

src/Libraries/Microsoft.Extensions.AI.OpenAI/OpenAIClientExtensions.cs

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ namespace Microsoft.Extensions.AI;
2525
public static class OpenAIClientExtensions
2626
{
2727
/// <summary>Key into AdditionalProperties used to store a strict option.</summary>
28-
private const string StrictKey = "strictJsonSchema";
28+
private const string StrictKey = "strict";
2929

3030
/// <summary>Gets the default OpenAI endpoint.</summary>
3131
internal static Uri DefaultOpenAIEndpoint { get; } = new("https://api.openai.com/v1");
@@ -111,11 +111,11 @@ static void AppendLine(ref StringBuilder? sb, string propName, JsonNode propNode
111111
public static IChatClient AsIChatClient(this ChatClient chatClient) =>
112112
new OpenAIChatClient(chatClient);
113113

114-
/// <summary>Gets an <see cref="IChatClient"/> for use with this <see cref="OpenAIResponseClient"/>.</summary>
114+
/// <summary>Gets an <see cref="IChatClient"/> for use with this <see cref="ResponsesClient"/>.</summary>
115115
/// <param name="responseClient">The client.</param>
116-
/// <returns>An <see cref="IChatClient"/> that can be used to converse via the <see cref="OpenAIResponseClient"/>.</returns>
116+
/// <returns>An <see cref="IChatClient"/> that can be used to converse via the <see cref="ResponsesClient"/>.</returns>
117117
/// <exception cref="ArgumentNullException"><paramref name="responseClient"/> is <see langword="null"/>.</exception>
118-
public static IChatClient AsIChatClient(this OpenAIResponseClient responseClient) =>
118+
public static IChatClient AsIChatClient(this ResponsesClient responseClient) =>
119119
new OpenAIResponsesChatClient(responseClient);
120120

121121
/// <summary>Gets an <see cref="IChatClient"/> for use with this <see cref="AssistantClient"/>.</summary>
@@ -246,6 +246,14 @@ internal static void PatchModelIfNotSet(ref JsonPatch patch, string? modelId)
246246
internal static T? GetProperty<T>(this AITool tool, string name) =>
247247
tool.AdditionalProperties?.TryGetValue(name, out object? value) is true && value is T tValue ? tValue : default;
248248

249+
/// <summary>Gets whether an ID is an OpenAI conversation ID.</summary>
250+
/// <remarks>
251+
/// Technically, OpenAI's IDs are opaque. However, by convention conversation IDs start with "conv_" and
252+
/// we can use that to disambiguate whether we're looking at a conversation ID or something else, like a response ID.
253+
/// </remarks>
254+
internal static bool IsConversationId(string? id) =>
255+
id?.StartsWith("conv_", StringComparison.OrdinalIgnoreCase) is true;
256+
249257
/// <summary>Used to create the JSON payload for an OpenAI tool description.</summary>
250258
internal sealed class ToolJson
251259
{

0 commit comments

Comments
 (0)