Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion eng/packages/General.props
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
<PackageVersion Include="ModelContextProtocol.Core" Version="0.4.0-preview.3" />
<PackageVersion Include="Newtonsoft.Json" Version="13.0.3" />
<PackageVersion Include="OllamaSharp" Version="5.1.9" />
<PackageVersion Include="OpenAI" Version="2.7.0" />
<PackageVersion Include="OpenAI" Version="2.8.0" />
<PackageVersion Include="Polly" Version="8.4.2" />
<PackageVersion Include="Polly.Core" Version="8.4.2" />
<PackageVersion Include="Polly.Extensions" Version="8.4.2" />
Expand Down
5 changes: 5 additions & 0 deletions src/Libraries/Microsoft.Extensions.AI.OpenAI/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# Microsoft.Extensions.AI.OpenAI Release History

## 10.1.1-preview.1.?

- Updated to depend on OpenAI 2.8.0.
- Updated public API signatures in `OpenAIClientExtensions` and `MicrosoftExtensionsAIResponsesExtensions` to match the corresponding breaking changes in OpenAI's Responses APIs.

## 10.1.0-preview.1.25608.1

- Fixed package references for net10.0 asset.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,12 +56,12 @@ public static IEnumerable<ResponseItem> AsOpenAIResponseItems(this IEnumerable<C
public static IEnumerable<ChatMessage> AsChatMessages(this IEnumerable<ResponseItem> items) =>
OpenAIResponsesChatClient.ToChatMessages(Throw.IfNull(items));

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

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

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

if (response.RawRepresentation is OpenAIResponse openAIResponse)
if (response.RawRepresentation is ResponseResult openAIResponse)
{
return openAIResponse;
}

return OpenAIResponsesModelFactory.OpenAIResponse(
response.ResponseId,
response.CreatedAt ?? default,
ResponseStatus.Completed,
usage: null, // No way to construct a ResponseTokenUsage right now from external to the OpenAI library
maxOutputTokenCount: options?.MaxOutputTokens,
outputItems: OpenAIResponsesChatClient.ToOpenAIResponseItems(response.Messages, options),
parallelToolCallsEnabled: options?.AllowMultipleToolCalls ?? false,
model: response.ModelId ?? options?.ModelId,
temperature: options?.Temperature,
topP: options?.TopP,
previousResponseId: options?.ConversationId,
instructions: options?.Instructions);
ResponseResult result = new()
{
ConversationOptions = OpenAIClientExtensions.IsConversationId(response.ConversationId) ? new(response.ConversationId) : null,
CreatedAt = response.CreatedAt ?? default,
Id = response.ResponseId,
Instructions = options?.Instructions,
MaxOutputTokenCount = options?.MaxOutputTokens,
Model = response.ModelId ?? options?.ModelId,
ParallelToolCallsEnabled = options?.AllowMultipleToolCalls ?? true,
Status = ResponseStatus.Completed,
Temperature = options?.Temperature,
TopP = options?.TopP,
Usage = OpenAIResponsesChatClient.ToResponseTokenUsage(response.Usage),
};

foreach (var responseItem in OpenAIResponsesChatClient.ToOpenAIResponseItems(response.Messages, options))
{
result.OutputItems.Add(responseItem);
}

return result;
}

/// <summary>Adds the <see cref="ResponseTool"/> to the list of <see cref="AITool"/>s.</summary>
Expand All @@ -111,7 +119,7 @@ public static OpenAIResponse AsOpenAIResponse(this ChatResponse response, ChatOp
/// <remarks>
/// <see cref="ResponseTool"/> does not derive from <see cref="AITool"/>, so it cannot be added directly to a list of <see cref="AITool"/>s.
/// Instead, this method wraps the provided <see cref="ResponseTool"/> in an <see cref="AITool"/> and adds that to the list.
/// The <see cref="IChatClient"/> returned by <see cref="OpenAIClientExtensions.AsIChatClient(OpenAIResponseClient)"/> will
/// The <see cref="IChatClient"/> returned by <see cref="OpenAIClientExtensions.AsIChatClient(ResponsesClient)"/> will
/// be able to unwrap the <see cref="ResponseTool"/> when it processes the list of tools and use the provided <paramref name="tool"/> as-is.
/// </remarks>
public static void Add(this IList<AITool> tools, ResponseTool tool)
Expand All @@ -127,7 +135,7 @@ public static void Add(this IList<AITool> tools, ResponseTool tool)
/// <remarks>
/// <para>
/// The returned tool is only suitable for use with the <see cref="IChatClient"/> returned by
/// <see cref="OpenAIClientExtensions.AsIChatClient(OpenAIResponseClient)"/> (or <see cref="IChatClient"/>s that delegate
/// <see cref="OpenAIClientExtensions.AsIChatClient(ResponsesClient)"/> (or <see cref="IChatClient"/>s that delegate
/// to such an instance). It is likely to be ignored by any other <see cref="IChatClient"/> implementation.
/// </para>
/// <para>
Expand All @@ -136,7 +144,7 @@ public static void Add(this IList<AITool> tools, ResponseTool tool)
/// <see cref="HostedFileSearchTool"/>, those types should be preferred instead of this method, as they are more portable,
/// capable of being respected by any <see cref="IChatClient"/> implementation. This method does not attempt to
/// map the supplied <see cref="ResponseTool"/> to any of those types, it simply wraps it as-is:
/// the <see cref="IChatClient"/> returned by <see cref="OpenAIClientExtensions.AsIChatClient(OpenAIResponseClient)"/> will
/// the <see cref="IChatClient"/> returned by <see cref="OpenAIClientExtensions.AsIChatClient(ResponsesClient)"/> will
/// be able to unwrap the <see cref="ResponseTool"/> when it processes the list of tools.
/// </para>
/// </remarks>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ namespace Microsoft.Extensions.AI;
public static class OpenAIClientExtensions
{
/// <summary>Key into AdditionalProperties used to store a strict option.</summary>
private const string StrictKey = "strictJsonSchema";
private const string StrictKey = "strict";

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

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

/// <summary>Gets an <see cref="IChatClient"/> for use with this <see cref="AssistantClient"/>.</summary>
Expand Down Expand Up @@ -242,6 +242,14 @@ internal static void PatchModelIfNotSet(ref JsonPatch patch, string? modelId)
}
}

/// <summary>Gets whether an ID is an OpenAI conversation ID.</summary>
/// <remarks>
/// Technically, OpenAI's IDs are opaque. However, by convention conversation IDs start with "conv_" and
/// we can use that to disambiguate whether we're looking at a conversation ID or something else, like a response ID.
/// </remarks>
internal static bool IsConversationId(string? id) =>
id?.StartsWith("conv_", StringComparison.OrdinalIgnoreCase) is true;

/// <summary>Used to create the JSON payload for an OpenAI tool description.</summary>
internal sealed class ToolJson
{
Expand Down
Loading
Loading