Skip to content

Commit 4bea223

Browse files
authored
Workaround OpenAI assistant's RunCreationOption's tools override (#6512)
In OpenAIAssistantChatClient, as part of providing ChatOptions.Tools to the service, the only API available to do so is ToolsOverride, which ends up replacing rather than augmenting any tools defined at the assistant level. To work around that, this changes the implementation to retrieve the assistant's tools and send them as part of the override, effectively implementing a merge rather than override.
1 parent 6eb7ad8 commit 4bea223

File tree

1 file changed

+24
-4
lines changed

1 file changed

+24
-4
lines changed

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

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,9 @@ internal sealed partial class OpenAIAssistantChatClient : IChatClient
4444
/// <summary>The thread ID to use if none is supplied in <see cref="ChatOptions.ConversationId"/>.</summary>
4545
private readonly string? _defaultThreadId;
4646

47+
/// <summary>List of tools associated with the assistant.</summary>
48+
private IReadOnlyList<ToolDefinition>? _assistantTools;
49+
4750
/// <summary>Initializes a new instance of the <see cref="OpenAIAssistantChatClient"/> class for the specified <see cref="AssistantClient"/>.</summary>
4851
public OpenAIAssistantChatClient(AssistantClient assistantClient, string assistantId, string? defaultThreadId)
4952
{
@@ -83,7 +86,7 @@ public async IAsyncEnumerable<ChatResponseUpdate> GetStreamingResponseAsync(
8386
_ = Throw.IfNull(messages);
8487

8588
// Extract necessary state from messages and options.
86-
(RunCreationOptions runOptions, List<FunctionResultContent>? toolResults) = CreateRunOptions(messages, options);
89+
(RunCreationOptions runOptions, List<FunctionResultContent>? toolResults) = await CreateRunOptionsAsync(messages, options, cancellationToken).ConfigureAwait(false);
8790

8891
// Get the thread ID.
8992
string? threadId = options?.ConversationId ?? _defaultThreadId;
@@ -238,8 +241,8 @@ void IDisposable.Dispose()
238241
/// Creates the <see cref="RunCreationOptions"/> to use for the request and extracts any function result contents
239242
/// that need to be submitted as tool results.
240243
/// </summary>
241-
private (RunCreationOptions RunOptions, List<FunctionResultContent>? ToolResults) CreateRunOptions(
242-
IEnumerable<ChatMessage> messages, ChatOptions? options)
244+
private async ValueTask<(RunCreationOptions RunOptions, List<FunctionResultContent>? ToolResults)> CreateRunOptionsAsync(
245+
IEnumerable<ChatMessage> messages, ChatOptions? options, CancellationToken cancellationToken)
243246
{
244247
// Create the options instance to populate, either a fresh or using one the caller provides.
245248
RunCreationOptions runOptions =
@@ -257,6 +260,24 @@ void IDisposable.Dispose()
257260

258261
if (options.Tools is { Count: > 0 } tools)
259262
{
263+
// If the caller has provided any tool overrides, we'll assume they don't want to use the assistant's tools.
264+
// But if they haven't, the only way we can provide our tools is via an override, whereas we'd really like to
265+
// just add them. To handle that, we'll get all of the assistant's tools and add them to the override list
266+
// along with our tools.
267+
if (runOptions.ToolsOverride.Count == 0)
268+
{
269+
if (_assistantTools is null)
270+
{
271+
var assistant = await _client.GetAssistantAsync(_assistantId, cancellationToken).ConfigureAwait(false);
272+
_assistantTools = assistant.Value.Tools;
273+
}
274+
275+
foreach (var tool in _assistantTools)
276+
{
277+
runOptions.ToolsOverride.Add(tool);
278+
}
279+
}
280+
260281
// The caller can provide tools in the supplied ThreadAndRunOptions. Augment it with any supplied via ChatOptions.Tools.
261282
foreach (AITool tool in tools)
262283
{
@@ -290,7 +311,6 @@ void IDisposable.Dispose()
290311
runOptions.ToolConstraint = ToolConstraint.None;
291312
break;
292313

293-
case null:
294314
case AutoChatToolMode:
295315
runOptions.ToolConstraint = ToolConstraint.Auto;
296316
break;

0 commit comments

Comments
 (0)