Skip to content

Commit

Permalink
Merge branch 'main' into bump-py-ver-20241121
Browse files Browse the repository at this point in the history
  • Loading branch information
TaoChenOSU authored Nov 21, 2024
2 parents 9df6022 + d8acb75 commit d900270
Show file tree
Hide file tree
Showing 7 changed files with 74 additions and 28 deletions.
57 changes: 38 additions & 19 deletions dotnet/samples/Concepts/Filtering/TelemetryWithFilters.cs
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,17 @@ public async Task OnFunctionInvocationAsync(FunctionInvocationContext context, F

logger.LogInformation("Function {FunctionName} succeeded.", context.Function.Name);

await this.LogFunctionResultAsync(context);
if (context.IsStreaming)
{
// Overriding the result in a streaming scenario enables the filter to stream chunks
// back to the operation's origin without interrupting the data flow.
var enumerable = context.Result.GetValue<IAsyncEnumerable<StreamingChatMessageContent>>();
context.Result = new FunctionResult(context.Result, ProcessFunctionResultStreamingAsync(enumerable!));
}
else
{
ProcessFunctionResult(context.Result);
}
}
catch (Exception exception)
{
Expand All @@ -167,34 +177,43 @@ public async Task OnFunctionInvocationAsync(FunctionInvocationContext context, F
}
}

private async Task LogFunctionResultAsync(FunctionInvocationContext context)
private void ProcessFunctionResult(FunctionResult functionResult)
{
string? result = functionResult.GetValue<string>();
object? usage = functionResult.Metadata?["Usage"];

if (!string.IsNullOrWhiteSpace(result))
{
logger.LogTrace("Function result: {Result}", result);
}

if (logger.IsEnabled(LogLevel.Information) && usage is not null)
{
logger.LogInformation("Usage: {Usage}", JsonSerializer.Serialize(usage));
}
}

private async IAsyncEnumerable<StreamingChatMessageContent> ProcessFunctionResultStreamingAsync(IAsyncEnumerable<StreamingChatMessageContent> data)
{
string? result = null;
object? usage = null;

if (context.IsStreaming)
var stringBuilder = new StringBuilder();

await foreach (var item in data)
{
var stringBuilder = new StringBuilder();
yield return item;

await foreach (var item in context.Result.GetValue<IAsyncEnumerable<StreamingChatMessageContent>>()!)
if (item.Content is not null)
{
if (item.Content is not null)
{
stringBuilder.Append(item.Content);
}

usage = item.Metadata?["Usage"];
stringBuilder.Append(item.Content);
}

result = stringBuilder.ToString();
}
else
{
result = context.Result.GetValue<string>();
usage = context.Result.Metadata?["Usage"];
usage = item.Metadata?["Usage"];
}

if (result is not null)
var result = stringBuilder.ToString();

if (!string.IsNullOrWhiteSpace(result))
{
logger.LogTrace("Function result: {Result}", result);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,41 @@ public async Task FiltersAreExecutedCorrectlyAsync()
Assert.Equal("Test chat response", result.ToString());
}

[Fact]
public async Task FunctionSequenceIndexIsCorrectForConcurrentCallsAsync()
{
// Arrange
List<int> functionSequenceNumbers = [];
List<int> expectedFunctionSequenceNumbers = [0, 1, 0, 1];

var function1 = KernelFunctionFactory.CreateFromMethod((string parameter) => { return parameter; }, "Function1");
var function2 = KernelFunctionFactory.CreateFromMethod((string parameter) => { return parameter; }, "Function2");

var plugin = KernelPluginFactory.CreateFromFunctions("MyPlugin", [function1, function2]);

var kernel = this.GetKernelWithFilter(plugin, async (context, next) =>
{
functionSequenceNumbers.Add(context.FunctionSequenceIndex);
await next(context);
});

this._messageHandlerStub.ResponsesToReturn = GetFunctionCallingResponses();

// Act
var result = await kernel.InvokePromptAsync("Test prompt", new(new OpenAIPromptExecutionSettings
{
FunctionChoiceBehavior = FunctionChoiceBehavior.Auto(options: new()
{
AllowParallelCalls = true,
AllowConcurrentInvocation = true
})
}));

// Assert
Assert.Equal(expectedFunctionSequenceNumbers, functionSequenceNumbers);
}

[Fact]
public async Task FiltersAreExecutedCorrectlyOnStreamingAsync()
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
// Copyright (c) Microsoft. All rights reserved.

using System.Diagnostics.CodeAnalysis;
using System.Threading;
using Microsoft.SemanticKernel.ChatCompletion;

Expand All @@ -9,7 +8,6 @@ namespace Microsoft.SemanticKernel;
/// <summary>
/// Class with data related to automatic function invocation.
/// </summary>
[Experimental("SKEXP0001")]
public class AutoFunctionInvocationContext
{
/// <summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
// Copyright (c) Microsoft. All rights reserved.

using System;
using System.Diagnostics.CodeAnalysis;
using System.Threading.Tasks;

namespace Microsoft.SemanticKernel;
Expand All @@ -11,7 +10,6 @@ namespace Microsoft.SemanticKernel;
/// <summary>
/// Interface for filtering actions during automatic function invocation.
/// </summary>
[Experimental("SKEXP0001")]
public interface IAutoFunctionInvocationFilter
{
/// <summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
// Copyright (c) Microsoft. All rights reserved.

using System.Diagnostics.CodeAnalysis;
using System.Threading;

namespace Microsoft.SemanticKernel;
Expand Down Expand Up @@ -38,7 +37,6 @@ internal FunctionInvocationContext(Kernel kernel, KernelFunction function, Kerne
/// <summary>
/// Boolean flag which indicates whether a filter is invoked within streaming or non-streaming mode.
/// </summary>
[Experimental("SKEXP0001")]
public bool IsStreaming { get; init; }

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright (c) Microsoft. All rights reserved.

using System.Diagnostics.CodeAnalysis;
using System.Threading;

namespace Microsoft.SemanticKernel;

/// <summary>
Expand Down Expand Up @@ -37,7 +37,6 @@ internal PromptRenderContext(Kernel kernel, KernelFunction function, KernelArgum
/// <summary>
/// Boolean flag which indicates whether a filter is invoked within streaming or non-streaming mode.
/// </summary>
[Experimental("SKEXP0001")]
public bool IsStreaming { get; init; }

/// <summary>
Expand Down
1 change: 0 additions & 1 deletion dotnet/src/SemanticKernel.Abstractions/Kernel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,6 @@ public Kernel Clone() =>
/// <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) ??
Expand Down

0 comments on commit d900270

Please sign in to comment.