-
Notifications
You must be signed in to change notification settings - Fork 863
Expand file tree
/
Copy pathMicrosoftExtensionsAIResponsesExtensions.cs
More file actions
169 lines (149 loc) · 10.4 KB
/
MicrosoftExtensionsAIResponsesExtensions.cs
File metadata and controls
169 lines (149 loc) · 10.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Threading;
using Microsoft.Extensions.AI;
using Microsoft.Shared.DiagnosticIds;
using Microsoft.Shared.Diagnostics;
#pragma warning disable S3254 // Default parameter values should not be passed as arguments
namespace OpenAI.Responses;
/// <summary>Provides extension methods for working with content associated with OpenAI.Responses.</summary>
[Experimental(DiagnosticIds.Experiments.AIOpenAIResponses)]
public static class MicrosoftExtensionsAIResponsesExtensions
{
/// <summary>Creates an OpenAI <see cref="ResponseTool"/> from an <see cref="AIFunctionDeclaration"/>.</summary>
/// <param name="function">The function to convert.</param>
/// <returns>An OpenAI <see cref="ResponseTool"/> representing <paramref name="function"/>.</returns>
/// <exception cref="ArgumentNullException"><paramref name="function"/> is <see langword="null"/>.</exception>
public static FunctionTool AsOpenAIResponseTool(this AIFunctionDeclaration function) =>
OpenAIResponsesChatClient.ToResponseTool(Throw.IfNull(function));
/// <summary>Creates an OpenAI <see cref="ResponseTool"/> from an <see cref="AITool"/>.</summary>
/// <param name="tool">The tool to convert.</param>
/// <param name="options">
/// The <see cref="ChatOptions"/> that will be sent alongside this tool. When a <see cref="HostedToolSearchTool"/>
/// is present in <see cref="ChatOptions.Tools"/>, function tools may have <c>defer_loading</c> patched based on the
/// <see cref="HostedToolSearchTool.DeferredTools"/> configuration.
/// </param>
/// <returns>An OpenAI <see cref="ResponseTool"/> representing <paramref name="tool"/> or <see langword="null"/> if there is no mapping.</returns>
/// <exception cref="ArgumentNullException"><paramref name="tool"/> is <see langword="null"/>.</exception>
/// <remarks>
/// This method is only able to create <see cref="ResponseTool"/>s for <see cref="AITool"/> types
/// it's aware of, namely all of those available from the Microsoft.Extensions.AI.Abstractions library.
/// </remarks>
public static ResponseTool? AsOpenAIResponseTool(this AITool tool, ChatOptions? options = null) =>
OpenAIResponsesChatClient.ToResponseTool(Throw.IfNull(tool), options);
/// <summary>
/// Creates an OpenAI <see cref="ResponseTextFormat"/> from a <see cref="ChatResponseFormat"/>.
/// </summary>
/// <param name="format">The format.</param>
/// <param name="options">The options to use when interpreting the format.</param>
/// <returns>The converted OpenAI <see cref="ResponseTextFormat"/>.</returns>
public static ResponseTextFormat? AsOpenAIResponseTextFormat(this ChatResponseFormat? format, ChatOptions? options = null) =>
OpenAIResponsesChatClient.ToOpenAIResponseTextFormat(format, options);
/// <summary>Creates a sequence of OpenAI <see cref="ResponseItem"/> instances from the specified input messages.</summary>
/// <param name="messages">The input messages to convert.</param>
/// <param name="options">The options employed while processing <paramref name="messages"/>.</param>
/// <returns>A sequence of OpenAI response items.</returns>
/// <exception cref="ArgumentNullException"><paramref name="messages"/> is <see langword="null"/>.</exception>
public static IEnumerable<ResponseItem> AsOpenAIResponseItems(this IEnumerable<ChatMessage> messages, ChatOptions? options = null) =>
OpenAIResponsesChatClient.ToOpenAIResponseItems(Throw.IfNull(messages), options);
/// <summary>Creates a sequence of <see cref="ChatMessage"/> instances from the specified input items.</summary>
/// <param name="items">The input messages to convert.</param>
/// <returns>A sequence of <see cref="ChatMessage"/> instances.</returns>
/// <exception cref="ArgumentNullException"><paramref name="items"/> is <see langword="null"/>.</exception>
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="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 ResponseResult response, CreateResponseOptions? options = null) =>
OpenAIResponsesChatClient.FromOpenAIResponse(Throw.IfNull(response), options, conversationId: null);
/// <summary>
/// Creates a sequence of Microsoft.Extensions.AI <see cref="ChatResponseUpdate"/> instances from the specified
/// sequence of OpenAI <see cref="StreamingResponseUpdate"/> instances.
/// </summary>
/// <param name="responseUpdates">The update instances.</param>
/// <param name="options">The options employed in the creation of the response.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
/// <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, CreateResponseOptions? options = null, CancellationToken cancellationToken = default) =>
OpenAIResponsesChatClient.FromOpenAIStreamingResponseUpdatesAsync(Throw.IfNull(responseUpdates), options, conversationId: null, cancellationToken: cancellationToken);
/// <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="ResponseResult"/>.</returns>
public static ResponseResult AsOpenAIResponseResult(this ChatResponse response, ChatOptions? options = null)
{
_ = Throw.IfNull(response);
if (response.RawRepresentation is ResponseResult openAIResponse)
{
return openAIResponse;
}
ResponseResult result = new()
{
ConversationOptions = OpenAIClientExtensions.IsConversationId(response.ConversationId) ? new(response.ConversationId) : null,
CreatedAt = response.CreatedAt ?? default,
Id = response.ResponseId,
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),
};
if (options?.Instructions is { Length: > 0 })
{
result.Instructions.Add(ResponseItem.CreateDeveloperMessageItem(options.Instructions));
}
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>
/// <param name="tools">The list of <see cref="AITool"/>s to which the provided tool should be added.</param>
/// <param name="tool">The <see cref="ResponseTool"/> to add.</param>
/// <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(ResponsesClient, string)"/> 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)
{
_ = Throw.IfNull(tools);
tools.Add(AsAITool(tool));
}
/// <summary>Creates an <see cref="AITool"/> to represent a raw <see cref="ResponseTool"/>.</summary>
/// <param name="tool">The tool to wrap as an <see cref="AITool"/>.</param>
/// <returns>The <paramref name="tool"/> wrapped as an <see cref="AITool"/>.</returns>
/// <remarks>
/// <para>
/// The returned tool is only suitable for use with the <see cref="IChatClient"/> returned by
/// <see cref="OpenAIClientExtensions.AsIChatClient(ResponsesClient, string)"/> (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>
/// When a tool has a corresponding <see cref="AITool"/>-derived type already defined in Microsoft.Extensions.AI,
/// such as <see cref="AIFunction"/>, <see cref="HostedWebSearchTool"/>, <see cref="HostedMcpServerTool"/>, or
/// <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(ResponsesClient, string)"/> will
/// be able to unwrap the <see cref="ResponseTool"/> when it processes the list of tools.
/// </para>
/// </remarks>
public static AITool AsAITool(this ResponseTool tool)
{
_ = Throw.IfNull(tool);
return new OpenAIResponsesChatClient.ResponseToolAITool(tool);
}
}