-
Notifications
You must be signed in to change notification settings - Fork 3.3k
/
Kernel.cs
693 lines (612 loc) · 35.3 KB
/
Kernel.cs
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
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
// Copyright (c) Microsoft. All rights reserved.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.SemanticKernel.Services;
namespace Microsoft.SemanticKernel;
/// <summary>
/// Provides state for use throughout a Semantic Kernel workload.
/// </summary>
/// <remarks>
/// An instance of <see cref="Kernel"/> is passed through to every function invocation and service call
/// throughout the system, providing to each the ability to access shared state and services.
/// </remarks>
public sealed class Kernel
{
/// <summary>Key used by <see cref="IKernelBuilder"/> to store type information into the service provider.</summary>
internal const string KernelServiceTypeToKeyMappings = nameof(KernelServiceTypeToKeyMappings);
/// <summary>Dictionary containing ambient data stored in the kernel, lazily-initialized on first access.</summary>
private Dictionary<string, object?>? _data;
/// <summary><see cref="CultureInfo"/> to be used by any operations that need access to the culture, a format provider, etc.</summary>
private CultureInfo _culture = CultureInfo.InvariantCulture;
/// <summary>The collection of plugins, initialized via the constructor or lazily-initialized on first access via <see cref="Plugins"/>.</summary>
private KernelPluginCollection? _plugins;
/// <summary>The collection of function filters, initialized via the constructor or lazily-initialized on first access via <see cref="Plugins"/>.</summary>
private NonNullCollection<IFunctionInvocationFilter>? _functionInvocationFilters;
/// <summary>The collection of prompt filters, initialized via the constructor or lazily-initialized on first access via <see cref="Plugins"/>.</summary>
private NonNullCollection<IPromptRenderFilter>? _promptRenderFilters;
/// <summary>The collection of automatic function invocation filters, initialized via the constructor or lazily-initialized on first access via <see cref="Plugins"/>.</summary>
private NonNullCollection<IAutoFunctionInvocationFilter>? _autoFunctionInvocationFilters;
/// <summary>
/// Initializes a new instance of <see cref="Kernel"/>.
/// </summary>
/// <param name="services">The <see cref="IServiceProvider"/> used to query for services available through the kernel.</param>
/// <param name="plugins">
/// The collection of plugins available through the kernel. If null, an empty collection will be used.
/// If non-null, the supplied collection instance is used, not a copy; if it's desired for the <see cref="Kernel"/>
/// to have a copy, the caller is responsible for supplying it.
/// </param>
/// <remarks>
/// The KernelBuilder class provides a fluent API for constructing a <see cref="Kernel"/> instance.
/// </remarks>
public Kernel(
IServiceProvider? services = null,
KernelPluginCollection? plugins = null)
{
// Store the provided services, or an empty singleton if there aren't any.
this.Services = services ?? EmptyServiceProvider.Instance;
// Store the provided plugins. If there weren't any, look in DI to see if there's a plugin collection.
this._plugins = plugins ?? this.Services.GetService<KernelPluginCollection>();
if (this._plugins is null)
{
// Otherwise, enumerate any plugins that may have been registered directly.
IEnumerable<KernelPlugin> registeredPlugins = this.Services.GetServices<KernelPlugin>();
// It'll be common not to have any plugins directly registered as a service.
// If we can efficiently tell there aren't any, avoid proactively allocating
// the plugins collection.
if (IsNotEmpty(registeredPlugins))
{
this._plugins = new(registeredPlugins);
}
}
this.AddFilters();
}
/// <summary>Creates a builder for constructing <see cref="Kernel"/> instances.</summary>
/// <returns>A new <see cref="IKernelBuilder"/> instance.</returns>
public static IKernelBuilder CreateBuilder() => new KernelBuilder();
/// <summary>
/// Clone the <see cref="Kernel"/> object to create a new instance that may be mutated without affecting the current instance.
/// </summary>
/// <remarks>
/// The current instance is unmodified by this operation. The new <see cref="Kernel"/> will be initialized with:
/// <list type="bullet">
/// <item>
/// The same <see cref="IServiceProvider"/> reference as is returned by the current instance's <see cref="Kernel.Services"/>.
/// </item>
/// <item>
/// A new <see cref="KernelPluginCollection"/> instance initialized with the same <see cref="KernelPlugin"/> instances as are stored by the current instance's <see cref="Kernel.Plugins"/> collection.
/// Changes to the new instance's plugin collection will not affect the current instance's plugin collection, and vice versa.
/// </item>
/// <item>
/// All of the delegates registered with each event. Delegates are immutable (every time an additional delegate is added or removed, a new one is created),
/// so changes to the new instance's event delegates will not affect the current instance's event delegates, and vice versa.
/// </item>
/// <item>
/// A new <see cref="IDictionary{TKey, TValue}"/> containing all of the key/value pairs from the current instance's <see cref="Kernel.Data"/> dictionary.
/// Any changes made to the new instance's dictionary will not affect the current instance's dictionary, and vice versa.
/// </item>
/// <item>The same <see cref="CultureInfo"/> reference as is returned by the current instance's <see cref="Kernel.Culture"/>.</item>
/// </list>
/// </remarks>
public Kernel Clone() =>
new(this.Services, this._plugins is { Count: > 0 } ? new KernelPluginCollection(this._plugins) : null)
{
FunctionInvoking = this.FunctionInvoking,
FunctionInvoked = this.FunctionInvoked,
PromptRendering = this.PromptRendering,
PromptRendered = this.PromptRendered,
_functionInvocationFilters = this._functionInvocationFilters is { Count: > 0 } ? new NonNullCollection<IFunctionInvocationFilter>(this._functionInvocationFilters) : null,
_promptRenderFilters = this._promptRenderFilters is { Count: > 0 } ? new NonNullCollection<IPromptRenderFilter>(this._promptRenderFilters) : null,
_autoFunctionInvocationFilters = this._autoFunctionInvocationFilters is { Count: > 0 } ? new NonNullCollection<IAutoFunctionInvocationFilter>(this._autoFunctionInvocationFilters) : null,
_data = this._data is { Count: > 0 } ? new Dictionary<string, object?>(this._data) : null,
_culture = this._culture,
};
/// <summary>
/// Gets the collection of plugins available through the kernel.
/// </summary>
public KernelPluginCollection Plugins =>
this._plugins ??
Interlocked.CompareExchange(ref this._plugins, [], null) ??
this._plugins;
/// <summary>
/// Gets the collection of function filters available through the kernel.
/// </summary>
public IList<IFunctionInvocationFilter> FunctionInvocationFilters =>
this._functionInvocationFilters ??
Interlocked.CompareExchange(ref this._functionInvocationFilters, [], null) ??
this._functionInvocationFilters;
/// <summary>
/// Gets the collection of function filters available through the kernel.
/// </summary>
public IList<IPromptRenderFilter> PromptRenderFilters =>
this._promptRenderFilters ??
Interlocked.CompareExchange(ref this._promptRenderFilters, [], null) ??
this._promptRenderFilters;
/// <summary>
/// Gets the collection of auto function invocation filters available through the kernel.
/// </summary>
[Experimental("SKEXP0001")]
public IList<IAutoFunctionInvocationFilter> AutoFunctionInvocationFilters =>
this._autoFunctionInvocationFilters ??
Interlocked.CompareExchange(ref this._autoFunctionInvocationFilters, [], null) ??
this._autoFunctionInvocationFilters;
/// <summary>
/// Gets the service provider used to query for services available through the kernel.
/// </summary>
public IServiceProvider Services { get; }
/// <summary>
/// Gets the culture currently associated with this <see cref="Kernel"/>.
/// </summary>
/// <remarks>
/// The culture defaults to <see cref="CultureInfo.InvariantCulture"/> if not explicitly set.
/// It may be set to another culture, such as <see cref="CultureInfo.CurrentCulture"/>,
/// and any functions invoked within the context can consult this property for use in
/// operations like formatting and parsing.
/// </remarks>
[AllowNull]
public CultureInfo Culture
{
get => this._culture;
set => this._culture = value ?? CultureInfo.InvariantCulture;
}
/// <summary>
/// Gets the <see cref="ILoggerFactory"/> associated with this <see cref="Kernel"/>.
/// </summary>
/// <remarks>
/// This returns any <see cref="ILoggerFactory"/> in <see cref="Services"/>. If there is
/// none, it returns an <see cref="ILoggerFactory"/> that won't perform any logging.
/// </remarks>
public ILoggerFactory LoggerFactory =>
this.Services.GetService<ILoggerFactory>() ??
NullLoggerFactory.Instance;
/// <summary>
/// Gets the <see cref="IAIServiceSelector"/> associated with this <see cref="Kernel"/>.
/// </summary>
public IAIServiceSelector ServiceSelector =>
this.Services.GetService<IAIServiceSelector>() ??
OrderedAIServiceSelector.Instance;
/// <summary>
/// Gets a dictionary for ambient data associated with the kernel.
/// </summary>
/// <remarks>
/// This may be used to flow arbitrary data in and out of operations performed with this kernel instance.
/// </remarks>
public IDictionary<string, object?> Data =>
this._data ??
Interlocked.CompareExchange(ref this._data, [], null) ??
this._data;
#region GetServices
/// <summary>Gets a required service from the <see cref="Services"/> provider.</summary>
/// <typeparam name="T">Specifies the type of the service to get.</typeparam>
/// <param name="serviceKey">An object that specifies the key of the service to get.</param>
/// <returns>The found service instance.</returns>
/// <exception cref="KernelException">A service of the specified type and name could not be found.</exception>
public T GetRequiredService<T>(object? serviceKey = null) where T : class
{
T? service = null;
if (serviceKey is not null)
{
if (this.Services is IKeyedServiceProvider)
{
// We were given a service ID, so we need to use the keyed service lookup.
service = this.Services.GetKeyedService<T>(serviceKey);
}
}
else
{
// No ID was given. We first want to use non-keyed lookup, in order to match against
// a service registered without an ID. If we can't find one, then we try to match with
// a service registered with an ID. In both cases, if there were multiple, this will match
// with whichever was registered last.
service = this.Services.GetService<T>();
if (service is null && this.Services is IKeyedServiceProvider)
{
service = this.GetAllServices<T>().LastOrDefault();
}
}
// If we couldn't find the service, throw an exception.
if (service is null)
{
string message =
serviceKey is null ? $"Service of type '{typeof(T)}' not registered." :
this.Services is not IKeyedServiceProvider ? $"Key '{serviceKey}' specified but service provider '{this.Services}' is not a {nameof(IKeyedServiceProvider)}." :
$"Service of type '{typeof(T)}' and key '{serviceKey}' not registered.";
throw new KernelException(message);
}
// Return the found service.
return service;
}
/// <summary>Gets all services of the specified type.</summary>
/// <typeparam name="T">Specifies the type of the services to retrieve.</typeparam>
/// <returns>An enumerable of all instances of the specified service that are registered.</returns>
/// <remarks>There is no guaranteed ordering on the results.</remarks>
public IEnumerable<T> GetAllServices<T>() where T : class
{
if (this.Services is IKeyedServiceProvider)
{
// M.E.DI doesn't support querying for a service without a key, and it also doesn't
// support AnyKey currently: https://github.com/dotnet/runtime/issues/91466
// As a workaround, KernelBuilder injects a service containing the type-to-all-keys
// mapping. We can query for that service and then use it to try to get a service.
if (this.Services.GetKeyedService<Dictionary<Type, HashSet<object?>>>(KernelServiceTypeToKeyMappings) is { } typeToKeyMappings)
{
if (typeToKeyMappings.TryGetValue(typeof(T), out HashSet<object?>? keys))
{
return keys.SelectMany(this.Services.GetKeyedServices<T>);
}
return [];
}
}
return this.Services.GetServices<T>();
}
#endregion
#region Filters
private void AddFilters()
{
// Enumerate any function filters that may have been registered.
IEnumerable<IFunctionInvocationFilter> functionInvocationFilters = this.Services.GetServices<IFunctionInvocationFilter>();
if (IsNotEmpty(functionInvocationFilters))
{
this._functionInvocationFilters = new(functionInvocationFilters);
}
// Enumerate any prompt filters that may have been registered.
IEnumerable<IPromptRenderFilter> promptRenderFilters = this.Services.GetServices<IPromptRenderFilter>();
if (IsNotEmpty(promptRenderFilters))
{
this._promptRenderFilters = new(promptRenderFilters);
}
// Enumerate any automatic function invocation filters that may have been registered.
IEnumerable<IAutoFunctionInvocationFilter> autoFunctionInvocationFilters = this.Services.GetServices<IAutoFunctionInvocationFilter>();
if (IsNotEmpty(autoFunctionInvocationFilters))
{
this._autoFunctionInvocationFilters = new(autoFunctionInvocationFilters);
}
}
internal async Task<FunctionInvocationContext> OnFunctionInvocationAsync(
KernelFunction function,
KernelArguments arguments,
FunctionResult functionResult,
bool isStreaming,
Func<FunctionInvocationContext, Task> functionCallback,
CancellationToken cancellationToken)
{
FunctionInvocationContext context = new(this, function, arguments, functionResult)
{
CancellationToken = cancellationToken,
IsStreaming = isStreaming
};
await InvokeFilterOrFunctionAsync(this._functionInvocationFilters, functionCallback, context).ConfigureAwait(false);
return context;
}
/// <summary>
/// This method will execute filters and kernel function recursively.
/// If there are no registered filters, just kernel function will be executed.
/// If there are registered filters, filter on <paramref name="index"/> position will be executed.
/// Second parameter of filter is callback. It can be either filter on <paramref name="index"/> + 1 position or kernel function if there are no remaining filters to execute.
/// Kernel function will be always executed as last step after all filters.
/// </summary>
private static async Task InvokeFilterOrFunctionAsync(
NonNullCollection<IFunctionInvocationFilter>? functionFilters,
Func<FunctionInvocationContext, Task> functionCallback,
FunctionInvocationContext context,
int index = 0)
{
if (functionFilters is { Count: > 0 } && index < functionFilters.Count)
{
await functionFilters[index].OnFunctionInvocationAsync(context,
(context) => InvokeFilterOrFunctionAsync(functionFilters, functionCallback, context, index + 1)).ConfigureAwait(false);
}
else
{
await functionCallback(context).ConfigureAwait(false);
}
}
internal async Task<PromptRenderContext> OnPromptRenderAsync(
KernelFunction function,
KernelArguments arguments,
bool isStreaming,
Func<PromptRenderContext, Task> renderCallback,
CancellationToken cancellationToken)
{
PromptRenderContext context = new(this, function, arguments)
{
CancellationToken = cancellationToken,
IsStreaming = isStreaming
};
await InvokeFilterOrPromptRenderAsync(this._promptRenderFilters, renderCallback, context).ConfigureAwait(false);
return context;
}
/// <summary>
/// This method will execute prompt filters and prompt rendering recursively.
/// If there are no registered filters, just prompt rendering will be executed.
/// If there are registered filters, filter on <paramref name="index"/> position will be executed.
/// Second parameter of filter is callback. It can be either filter on <paramref name="index"/> + 1 position or prompt rendering if there are no remaining filters to execute.
/// Prompt rendering will be always executed as last step after all filters.
/// </summary>
private static async Task InvokeFilterOrPromptRenderAsync(
NonNullCollection<IPromptRenderFilter>? promptFilters,
Func<PromptRenderContext, Task> renderCallback,
PromptRenderContext context,
int index = 0)
{
if (promptFilters is { Count: > 0 } && index < promptFilters.Count)
{
await promptFilters[index].OnPromptRenderAsync(context,
(context) => InvokeFilterOrPromptRenderAsync(promptFilters, renderCallback, context, index + 1)).ConfigureAwait(false);
}
else
{
await renderCallback(context).ConfigureAwait(false);
}
}
#endregion
#region InvokeAsync
/// <summary>
/// Invokes the <see cref="KernelFunction"/>.
/// </summary>
/// <param name="function">The <see cref="KernelFunction"/> to invoke.</param>
/// <param name="arguments">The arguments to pass to the function's invocation, including any <see cref="PromptExecutionSettings"/>.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
/// <returns>The result of the function's execution.</returns>
/// <exception cref="ArgumentNullException"><paramref name="function"/> is null.</exception>
/// <exception cref="KernelFunctionCanceledException">The <see cref="KernelFunction"/>'s invocation was canceled.</exception>
/// <remarks>
/// This behaves identically to invoking the specified <paramref name="function"/> with this <see cref="Kernel"/> as its <see cref="Kernel"/> argument.
/// </remarks>
public Task<FunctionResult> InvokeAsync(
KernelFunction function,
KernelArguments? arguments = null,
CancellationToken cancellationToken = default)
{
Verify.NotNull(function);
return function.InvokeAsync(this, arguments, cancellationToken);
}
/// <summary>
/// Invokes a function from <see cref="Kernel.Plugins"/> using the specified arguments.
/// </summary>
/// <param name="pluginName">The name of the plugin containing the function to invoke. If null, all plugins will be searched for the first function of the specified name.</param>
/// <param name="functionName">The name of the function to invoke.</param>
/// <param name="arguments">The arguments to pass to the function's invocation, including any <see cref="PromptExecutionSettings"/>.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
/// <returns>The result of the function's execution.</returns>
/// <exception cref="ArgumentNullException"><paramref name="functionName"/> is null.</exception>
/// <exception cref="ArgumentException"><paramref name="functionName"/> is composed entirely of whitespace.</exception>
/// <exception cref="KernelFunctionCanceledException">The <see cref="KernelFunction"/>'s invocation was canceled.</exception>
/// <remarks>
/// This behaves identically to using <see cref="KernelPluginExtensions.GetFunction"/> to find the desired <see cref="KernelFunction"/> and then
/// invoking it with this <see cref="Kernel"/> as its <see cref="Kernel"/> argument.
/// </remarks>
public Task<FunctionResult> InvokeAsync(
string? pluginName,
string functionName,
KernelArguments? arguments = null,
CancellationToken cancellationToken = default)
{
Verify.NotNullOrWhiteSpace(functionName);
var function = this.Plugins.GetFunction(pluginName, functionName);
return function.InvokeAsync(this, arguments, cancellationToken);
}
/// <summary>
/// Invokes the <see cref="KernelFunction"/>.
/// </summary>
/// <typeparam name="TResult">Specifies the type of the result value of the function.</typeparam>
/// <param name="function">The <see cref="KernelFunction"/> to invoke.</param>
/// <param name="arguments">The arguments to pass to the function's invocation, including any <see cref="PromptExecutionSettings"/>.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
/// <returns>The result of the function's execution, cast to <typeparamref name="TResult"/>.</returns>
/// <exception cref="ArgumentNullException"><paramref name="function"/> is null.</exception>
/// <exception cref="KernelFunctionCanceledException">The <see cref="KernelFunction"/>'s invocation was canceled.</exception>
/// <exception cref="InvalidCastException">The function's result could not be cast to <typeparamref name="TResult"/>.</exception>
/// <remarks>
/// This behaves identically to invoking the specified <paramref name="function"/> with this <see cref="Kernel"/> as its <see cref="Kernel"/> argument.
/// </remarks>
public async Task<TResult?> InvokeAsync<TResult>(
KernelFunction function,
KernelArguments? arguments = null,
CancellationToken cancellationToken = default)
{
FunctionResult result = await this.InvokeAsync(function, arguments, cancellationToken).ConfigureAwait(false);
return result.GetValue<TResult>();
}
/// <summary>
/// Invokes a function from <see cref="Plugins"/> using the specified arguments.
/// </summary>
/// <typeparam name="TResult">Specifies the type of the result value of the function.</typeparam>
/// <param name="pluginName">The name of the plugin containing the function to invoke. If null, all plugins will be searched for the first function of the specified name.</param>
/// <param name="functionName">The name of the function to invoke.</param>
/// <param name="arguments">The arguments to pass to the function's invocation, including any <see cref="PromptExecutionSettings"/>.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
/// <returns>The result of the function's execution, cast to <typeparamref name="TResult"/>.</returns>
/// <exception cref="ArgumentNullException"><paramref name="functionName"/> is null.</exception>
/// <exception cref="ArgumentException"><paramref name="functionName"/> is composed entirely of whitespace.</exception>
/// <exception cref="KernelFunctionCanceledException">The <see cref="KernelFunction"/>'s invocation was canceled.</exception>
/// <exception cref="InvalidCastException">The function's result could not be cast to <typeparamref name="TResult"/>.</exception>
/// <remarks>
/// This behaves identically to using <see cref="KernelPluginExtensions.GetFunction"/> to find the desired <see cref="KernelFunction"/> and then
/// invoking it with this <see cref="Kernel"/> as its <see cref="Kernel"/> argument.
/// </remarks>
public async Task<TResult?> InvokeAsync<TResult>(
string? pluginName,
string functionName,
KernelArguments? arguments = null,
CancellationToken cancellationToken = default)
{
FunctionResult result = await this.InvokeAsync(pluginName, functionName, arguments, cancellationToken).ConfigureAwait(false);
return result.GetValue<TResult>();
}
#endregion
#region InvokeStreamingAsync
/// <summary>
/// Invokes the <see cref="KernelFunction"/> and streams its results.
/// </summary>
/// <param name="function">The <see cref="KernelFunction"/> to invoke.</param>
/// <param name="arguments">The arguments to pass to the function's invocation, including any <see cref="PromptExecutionSettings"/>.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
/// <returns>An <see cref="IAsyncEnumerable{T}"/> for streaming the results of the function's invocation.</returns>
/// <exception cref="ArgumentNullException"><paramref name="function"/> is null.</exception>
/// <remarks>
/// The function will not be invoked until an enumerator is retrieved from the returned <see cref="IAsyncEnumerable{T}"/>
/// and its iteration initiated via an initial call to <see cref="IAsyncEnumerator{T}.MoveNextAsync"/>.
/// </remarks>
public IAsyncEnumerable<StreamingKernelContent> InvokeStreamingAsync(
KernelFunction function,
KernelArguments? arguments = null,
CancellationToken cancellationToken = default)
{
Verify.NotNull(function);
return function.InvokeStreamingAsync<StreamingKernelContent>(this, arguments, cancellationToken);
}
/// <summary>
/// Invokes the <see cref="KernelFunction"/> and streams its results.
/// </summary>
/// <param name="pluginName">The name of the plugin containing the function to invoke. If null, all plugins will be searched for the first function of the specified name.</param>
/// <param name="functionName">The name of the function to invoke.</param>
/// <param name="arguments">The arguments to pass to the function's invocation, including any <see cref="PromptExecutionSettings"/>.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
/// <returns>An <see cref="IAsyncEnumerable{T}"/> for streaming the results of the function's invocation.</returns>
/// <exception cref="ArgumentNullException"><paramref name="functionName"/> is null.</exception>
/// <exception cref="ArgumentException"><paramref name="functionName"/> is composed entirely of whitespace.</exception>
/// <exception cref="KernelFunctionCanceledException">The <see cref="KernelFunction"/>'s invocation was canceled.</exception>
/// <remarks>
/// The function will not be invoked until an enumerator is retrieved from the returned <see cref="IAsyncEnumerable{T}"/>
/// and its iteration initiated via an initial call to <see cref="IAsyncEnumerator{T}.MoveNextAsync"/>.
/// </remarks>
public IAsyncEnumerable<StreamingKernelContent> InvokeStreamingAsync(
string? pluginName,
string functionName,
KernelArguments? arguments = null,
CancellationToken cancellationToken = default)
{
Verify.NotNullOrWhiteSpace(functionName);
var function = this.Plugins.GetFunction(pluginName, functionName);
return function.InvokeStreamingAsync<StreamingKernelContent>(this, arguments, cancellationToken);
}
/// <summary>
/// Invokes the <see cref="KernelFunction"/> and streams its results.
/// </summary>
/// <param name="function">The <see cref="KernelFunction"/> to invoke.</param>
/// <param name="arguments">The arguments to pass to the function's invocation, including any <see cref="PromptExecutionSettings"/>.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
/// <returns>An <see cref="IAsyncEnumerable{T}"/> for streaming the results of the function's invocation.</returns>
/// <exception cref="ArgumentNullException"><paramref name="function"/> is null.</exception>
/// <remarks>
/// The function will not be invoked until an enumerator is retrieved from the returned <see cref="IAsyncEnumerable{T}"/>
/// and its iteration initiated via an initial call to <see cref="IAsyncEnumerator{T}.MoveNextAsync"/>.
/// </remarks>
public IAsyncEnumerable<T> InvokeStreamingAsync<T>(
KernelFunction function,
KernelArguments? arguments = null,
CancellationToken cancellationToken = default)
{
Verify.NotNull(function);
return function.InvokeStreamingAsync<T>(this, arguments, cancellationToken);
}
/// <summary>
/// Invokes the <see cref="KernelFunction"/> and streams its results.
/// </summary>
/// <param name="pluginName">The name of the plugin containing the function to invoke. If null, all plugins will be searched for the first function of the specified name.</param>
/// <param name="functionName">The name of the function to invoke.</param>
/// <param name="arguments">The arguments to pass to the function's invocation, including any <see cref="PromptExecutionSettings"/>.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
/// <returns>An <see cref="IAsyncEnumerable{T}"/> for streaming the results of the function's invocation.</returns>
/// <exception cref="ArgumentNullException"><paramref name="functionName"/> is null.</exception>
/// <exception cref="ArgumentException"><paramref name="functionName"/> is composed entirely of whitespace.</exception>
/// <exception cref="KernelFunctionCanceledException">The <see cref="KernelFunction"/>'s invocation was canceled.</exception>
/// <remarks>
/// The function will not be invoked until an enumerator is retrieved from the returned <see cref="IAsyncEnumerable{T}"/>
/// and its iteration initiated via an initial call to <see cref="IAsyncEnumerator{T}.MoveNextAsync"/>.
/// </remarks>
public IAsyncEnumerable<T> InvokeStreamingAsync<T>(
string? pluginName,
string functionName,
KernelArguments? arguments = null,
CancellationToken cancellationToken = default)
{
Verify.NotNullOrWhiteSpace(functionName);
var function = this.Plugins.GetFunction(pluginName, functionName);
return function.InvokeStreamingAsync<T>(this, arguments, cancellationToken);
}
#endregion
#region Private
private static bool IsNotEmpty<T>(IEnumerable<T> enumerable) =>
enumerable is not ICollection<T> collection || collection.Count != 0;
#endregion
#region Obsolete
/// <summary>
/// Provides an event that's raised prior to a function's invocation.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
[Obsolete("Events are deprecated in favor of filters. Example in dotnet/samples/GettingStarted/Step7_Observability.cs of Semantic Kernel repository.")]
public event EventHandler<FunctionInvokingEventArgs>? FunctionInvoking;
/// <summary>
/// Provides an event that's raised after a function's invocation.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
[Obsolete("Events are deprecated in favor of filters. Example in dotnet/samples/GettingStarted/Step7_Observability.cs of Semantic Kernel repository.")]
public event EventHandler<FunctionInvokedEventArgs>? FunctionInvoked;
/// <summary>
/// Provides an event that's raised prior to a prompt being rendered.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
[Obsolete("Events are deprecated in favor of filters. Example in dotnet/samples/GettingStarted/Step7_Observability.cs of Semantic Kernel repository.")]
public event EventHandler<PromptRenderingEventArgs>? PromptRendering;
/// <summary>
/// Provides an event that's raised after a prompt is rendered.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
[Obsolete("Events are deprecated in favor of filters. Example in dotnet/samples/GettingStarted/Step7_Observability.cs of Semantic Kernel repository.")]
public event EventHandler<PromptRenderedEventArgs>? PromptRendered;
[Obsolete("Events are deprecated in favor of filters. Example in dotnet/samples/GettingStarted/Step7_Observability.cs of Semantic Kernel repository.")]
internal FunctionInvokingEventArgs? OnFunctionInvoking(KernelFunction function, KernelArguments arguments)
{
FunctionInvokingEventArgs? eventArgs = null;
if (this.FunctionInvoking is { } functionInvoking)
{
eventArgs = new(function, arguments);
functionInvoking.Invoke(this, eventArgs);
}
return eventArgs;
}
[Obsolete("Events are deprecated in favor of filters. Example in dotnet/samples/GettingStarted/Step7_Observability.cs of Semantic Kernel repository.")]
internal FunctionInvokedEventArgs? OnFunctionInvoked(KernelFunction function, KernelArguments arguments, FunctionResult result)
{
FunctionInvokedEventArgs? eventArgs = null;
if (this.FunctionInvoked is { } functionInvoked)
{
eventArgs = new(function, arguments, result);
functionInvoked.Invoke(this, eventArgs);
}
return eventArgs;
}
[Obsolete("Events are deprecated in favor of filters. Example in dotnet/samples/GettingStarted/Step7_Observability.cs of Semantic Kernel repository.")]
internal PromptRenderingEventArgs? OnPromptRendering(KernelFunction function, KernelArguments arguments)
{
PromptRenderingEventArgs? eventArgs = null;
if (this.PromptRendering is { } promptRendering)
{
eventArgs = new(function, arguments);
promptRendering.Invoke(this, eventArgs);
}
return eventArgs;
}
[Obsolete("Events are deprecated in favor of filters. Example in dotnet/samples/GettingStarted/Step7_Observability.cs of Semantic Kernel repository.")]
internal PromptRenderedEventArgs? OnPromptRendered(KernelFunction function, KernelArguments arguments, string renderedPrompt)
{
PromptRenderedEventArgs? eventArgs = null;
if (this.PromptRendered is { } promptRendered)
{
eventArgs = new(function, arguments, renderedPrompt);
promptRendered.Invoke(this, eventArgs);
}
return eventArgs;
}
#endregion
}