Skip to content

Commit d74b40c

Browse files
committed
Fix duplication between OpenAI Assistants pre-configured tools
Assistants lets you configure an agent with function signatures it's implicitly aware of, rather than them needing to be provided per request. That, however, creates complication for callers, as if they provide that function in ChatTools.Options, it leads to the function's signature being sent as part of the request, and the duplication of it with the pre-configured function signature results in an error. It's possible to work around this, by simply not including that function in the request, but it's a natural thing for a developer to do, especially if they want the function to be automatically invoked when the model requests it. You can achieve that by putting the function into the FunctionInvocationChatClient's AdditionalTools, which exists for this kind of purpose, but that's harder to discover. We're already downloading a list of the agent's functions. This commit simply uses that list to avoid sending tools from ChatOptions.Tools if they have the same name as one of those functions. We don't know whether the duplication was in error or on purpose, so we just assume it was on purpose.
1 parent 22cbbb7 commit d74b40c

File tree

1 file changed

+19
-3
lines changed

1 file changed

+19
-3
lines changed

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

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
using System;
55
using System.Collections.Generic;
6+
using System.Diagnostics.CodeAnalysis;
67
using System.Linq;
78
using System.Runtime.CompilerServices;
89
using System.Text;
@@ -38,6 +39,9 @@ internal sealed class OpenAIAssistantsChatClient : IChatClient
3839
/// <summary>List of tools associated with the assistant.</summary>
3940
private IReadOnlyList<ToolDefinition>? _assistantTools;
4041

42+
/// <summary>Set of the names of the functions in <see cref="_assistantTools"/>.</summary>
43+
private HashSet<string>? _assistantFunctionNames;
44+
4145
/// <summary>Initializes a new instance of the <see cref="OpenAIAssistantsChatClient"/> class for the specified <see cref="AssistantClient"/>.</summary>
4246
public OpenAIAssistantsChatClient(AssistantClient assistantClient, string assistantId, string? defaultThreadId)
4347
{
@@ -52,7 +56,7 @@ public OpenAIAssistantsChatClient(AssistantClient assistantClient, string assist
5256
public OpenAIAssistantsChatClient(AssistantClient assistantClient, Assistant assistant, string? defaultThreadId)
5357
: this(assistantClient, Throw.IfNull(assistant).Id, defaultThreadId)
5458
{
55-
_assistantTools = assistant.Tools;
59+
InitializeAssistantTools(assistant.Tools);
5660
}
5761

5862
/// <inheritdoc />
@@ -315,7 +319,7 @@ internal static FunctionToolDefinition ToOpenAIAssistantsFunctionToolDefinition(
315319
if (_assistantTools is null)
316320
{
317321
var assistant = await _client.GetAssistantAsync(_assistantId, cancellationToken).ConfigureAwait(false);
318-
_assistantTools = assistant.Value.Tools;
322+
InitializeAssistantTools(assistant.Value.Tools);
319323
}
320324

321325
foreach (var tool in _assistantTools)
@@ -330,7 +334,11 @@ internal static FunctionToolDefinition ToOpenAIAssistantsFunctionToolDefinition(
330334
switch (tool)
331335
{
332336
case AIFunctionDeclaration aiFunction:
333-
runOptions.ToolsOverride.Add(ToOpenAIAssistantsFunctionToolDefinition(aiFunction, options));
337+
if (_assistantFunctionNames?.Contains(aiFunction.Name) is not true)
338+
{
339+
runOptions.ToolsOverride.Add(ToOpenAIAssistantsFunctionToolDefinition(aiFunction, options));
340+
}
341+
334342
break;
335343

336344
case HostedCodeInterpreterTool codeInterpreterTool:
@@ -543,4 +551,12 @@ void AppendSystemInstructions(string? toAppend)
543551

544552
return runId;
545553
}
554+
555+
[MemberNotNull(nameof(_assistantTools))]
556+
[MemberNotNull(nameof(_assistantFunctionNames))]
557+
private void InitializeAssistantTools(IReadOnlyList<ToolDefinition> assistantTools)
558+
{
559+
_ = Interlocked.CompareExchange(ref _assistantTools, assistantTools, null);
560+
_ = Interlocked.CompareExchange(ref _assistantFunctionNames, [.. _assistantTools.OfType<FunctionToolDefinition>().Select(t => t.FunctionName)], null);
561+
}
546562
}

0 commit comments

Comments
 (0)