@@ -44,6 +44,9 @@ internal sealed partial class OpenAIAssistantChatClient : IChatClient
44
44
/// <summary>The thread ID to use if none is supplied in <see cref="ChatOptions.ConversationId"/>.</summary>
45
45
private readonly string ? _defaultThreadId ;
46
46
47
+ /// <summary>List of tools associated with the assistant.</summary>
48
+ private IReadOnlyList < ToolDefinition > ? _assistantTools ;
49
+
47
50
/// <summary>Initializes a new instance of the <see cref="OpenAIAssistantChatClient"/> class for the specified <see cref="AssistantClient"/>.</summary>
48
51
public OpenAIAssistantChatClient ( AssistantClient assistantClient , string assistantId , string ? defaultThreadId )
49
52
{
@@ -83,7 +86,7 @@ public async IAsyncEnumerable<ChatResponseUpdate> GetStreamingResponseAsync(
83
86
_ = Throw . IfNull ( messages ) ;
84
87
85
88
// 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 ) ;
87
90
88
91
// Get the thread ID.
89
92
string ? threadId = options ? . ConversationId ?? _defaultThreadId ;
@@ -238,8 +241,8 @@ void IDisposable.Dispose()
238
241
/// Creates the <see cref="RunCreationOptions"/> to use for the request and extracts any function result contents
239
242
/// that need to be submitted as tool results.
240
243
/// </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 )
243
246
{
244
247
// Create the options instance to populate, either a fresh or using one the caller provides.
245
248
RunCreationOptions runOptions =
@@ -257,6 +260,24 @@ void IDisposable.Dispose()
257
260
258
261
if ( options . Tools is { Count : > 0 } tools )
259
262
{
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
+
260
281
// The caller can provide tools in the supplied ThreadAndRunOptions. Augment it with any supplied via ChatOptions.Tools.
261
282
foreach ( AITool tool in tools )
262
283
{
@@ -290,7 +311,6 @@ void IDisposable.Dispose()
290
311
runOptions . ToolConstraint = ToolConstraint . None ;
291
312
break ;
292
313
293
- case null :
294
314
case AutoChatToolMode :
295
315
runOptions . ToolConstraint = ToolConstraint . Auto ;
296
316
break ;
0 commit comments