Skip to content

Commit

Permalink
Finish integration tests
Browse files Browse the repository at this point in the history
  • Loading branch information
TaoChenOSU committed Feb 20, 2025
1 parent 34e830b commit c54b5d2
Show file tree
Hide file tree
Showing 3 changed files with 146 additions and 9 deletions.
6 changes: 3 additions & 3 deletions dotnet/Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
</PropertyGroup>
<ItemGroup>
<PackageVersion Include="AWSSDK.BedrockAgent" Version="3.7.416.4" />
<PackageVersion Include="AWSSDK.BedrockAgentRuntime" Version="3.7.418.3" />
<PackageVersion Include="AWSSDK.BedrockAgent" Version="4.0.0-preview.5" />
<PackageVersion Include="AWSSDK.BedrockAgentRuntime" Version="4.0.0-preview.5" />
<PackageVersion Include="AWSSDK.BedrockRuntime" Version="4.0.0-preview.5" />
<PackageVersion Include="AWSSDK.Core" Version="4.0.0-preview.5" />
<PackageVersion Include="AWSSDK.Extensions.NETCore.Setup" Version="3.7.301" />
<PackageVersion Include="AWSSDK.Extensions.NETCore.Setup" Version="4.0.0-preview.5" />
<PackageVersion Include="Azure.AI.ContentSafety" Version="1.0.0" />
<PackageVersion Include="Azure.AI.Inference" Version="1.0.0-beta.2" />
<PackageVersion Include="Azure.AI.OpenAI" Version="[2.2.0-beta.1]" />
Expand Down
4 changes: 2 additions & 2 deletions dotnet/src/Agents/Bedrock/BedrockAgent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ public IAsyncEnumerable<ChatMessageContent> InvokeAsync(
KernelArguments? arguments,
CancellationToken cancellationToken = default)
{
return invokeAgentRequest.StreamingConfigurations != null && invokeAgentRequest.StreamingConfigurations.StreamFinalResponse
return invokeAgentRequest.StreamingConfigurations != null && (invokeAgentRequest.StreamingConfigurations.StreamFinalResponse ?? false)
? throw new ArgumentException("The streaming configuration must be null for non-streaming responses.")
: ActivityExtensions.RunWithActivityAsync(
() => ModelDiagnostics.StartAgentInvocationActivity(this.Id, this.GetDisplayName(), this.Description),
Expand Down Expand Up @@ -202,7 +202,7 @@ public IAsyncEnumerable<StreamingChatMessageContent> InvokeStreamingAsync(
StreamFinalResponse = true,
};
}
else if (!invokeAgentRequest.StreamingConfigurations.StreamFinalResponse)
else if (!(invokeAgentRequest.StreamingConfigurations.StreamFinalResponse ?? false))
{
throw new ArgumentException("The streaming configuration must have StreamFinalResponse set to true.");
}
Expand Down
145 changes: 141 additions & 4 deletions dotnet/src/IntegrationTests/Agents/BedrockAgentTests.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
// Copyright (c) Microsoft. All rights reserved.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Threading.Tasks;
using Amazon.BedrockAgent;
using Amazon.BedrockAgent.Model;
using Microsoft.Extensions.Configuration;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Agents.Bedrock;
using Microsoft.SemanticKernel.Agents.Bedrock.Extensions;
using SemanticKernel.IntegrationTests.TestSettings;
Expand All @@ -26,10 +30,9 @@ public sealed class BedrockAgentTests : IDisposable
private readonly AmazonBedrockAgentClient _client = new();

/// <summary>
/// Integration test for creating a <see cref="BedrockAgent"/>.
/// Integration test for invoking a <see cref="BedrockAgent"/>.
/// </summary>
// [Theory(Skip = "This test is for manual verification.")]
[Theory()]
[Theory(Skip = "This test is for manual verification.")]
[InlineData("Why is the sky blue in one sentence?")]
public async Task InvokeTestAsync(string input)
{
Expand All @@ -46,6 +49,88 @@ public async Task InvokeTestAsync(string input)
}
}

/// <summary>
/// Integration test for invoking a <see cref="BedrockAgent"/> with streaming.
/// </summary>
[Theory(Skip = "This test is for manual verification.")]
[InlineData("Why is the sky blue in one sentence?")]
public async Task InvokeStreamingTestAsync(string input)
{
var agentModel = await this._client.CreateAndPrepareAgentAsync(this.GetCreateAgentRequest());
var bedrockAgent = new BedrockAgent(agentModel, this._client);

try
{
await this.ExecuteAgentStreamingAsync(bedrockAgent, input);
}
finally
{
await this._client.DeleteAgentAsync(new() { AgentId = bedrockAgent.Id });
}
}

/// <summary>
/// Integration test for invoking a <see cref="BedrockAgent"/> with code interpreter.
/// </summary>
[Theory(Skip = "This test is for manual verification.")]
[InlineData(@"Create a bar chart for the following data:
Panda 5
Tiger 8
Lion 3
Monkey 6
Dolphin 2")]
public async Task InvokeWithCodeInterpreterTestAsync(string input)
{
var agentModel = await this._client.CreateAndPrepareAgentAsync(this.GetCreateAgentRequest());
var bedrockAgent = new BedrockAgent(agentModel, this._client);
await bedrockAgent.CreateCodeInterpreterActionGroupAsync();

try
{
var responses = await this.ExecuteAgentAsync(bedrockAgent, input);
BinaryContent? binaryContent = null;
foreach (var response in responses)
{
if (binaryContent == null && response.Items.Count > 0)
{
binaryContent = response.Items.OfType<BinaryContent>().FirstOrDefault();
}
}
Assert.NotNull(binaryContent);
}
finally
{
await this._client.DeleteAgentAsync(new() { AgentId = bedrockAgent.Id });
}
}

/// <summary>
/// Integration test for invoking a <see cref="BedrockAgent"/> with Kernel functions.
/// </summary>
[Theory(Skip = "This test is for manual verification.")]
[InlineData("What is the current weather in Seattle and what is the weather forecast in Seattle?", "weather")]
public async Task InvokeWithKernelFunctionTestAsync(string input, string expected)
{
Kernel kernel = new();
kernel.Plugins.Add(KernelPluginFactory.CreateFromType<WeatherPlugin>());

var agentModel = await this._client.CreateAndPrepareAgentAsync(this.GetCreateAgentRequest());
var bedrockAgent = new BedrockAgent(agentModel, this._client)
{
Kernel = kernel,
};
await bedrockAgent.CreateKernelFunctionActionGroupAsync();

try
{
await this.ExecuteAgentAsync(bedrockAgent, input, expected);
}
finally
{
await this._client.DeleteAgentAsync(new() { AgentId = bedrockAgent.Id });
}
}

/// <summary>
/// Executes a <see cref="BedrockAgent"/> with the specified input and expected output.
/// The output of the agent will be verified against the expected output.
Expand All @@ -54,14 +139,17 @@ public async Task InvokeTestAsync(string input)
/// <param name="agent">The agent to execute.</param>
/// <param name="input">The input to provide to the agent.</param>
/// <param name="expected">The expected output from the agent.</param>
private async Task ExecuteAgentAsync(BedrockAgent agent, string input, string? expected = null)
/// <returns>The chat messages returned by the agent for additional verification.</returns>
private async Task<List<ChatMessageContent>> ExecuteAgentAsync(BedrockAgent agent, string input, string? expected = null)
{
var responses = agent.InvokeAsync(BedrockAgent.CreateSessionId(), input, null, default);
string responseContent = string.Empty;
List<ChatMessageContent> chatMessages = new();
await foreach (var response in responses)
{
// Non-streaming invoke will only return one response.
responseContent = response.Content ?? string.Empty;
chatMessages.Add(response);
}

if (expected != null)
Expand All @@ -72,6 +160,40 @@ private async Task ExecuteAgentAsync(BedrockAgent agent, string input, string? e
{
Assert.False(string.IsNullOrEmpty(responseContent));
}

return chatMessages;
}

/// <summary>
/// Executes a <see cref="BedrockAgent"/> with the specified input and expected output using streaming.
/// The output of the agent will be verified against the expected output.
/// If the expected output is not provided, the verification will pass as long as the output is not null or empty.
/// </summary>
/// <param name="agent">The agent to execute.</param>
/// <param name="input">The input to provide to the agent.</param>
/// <param name="expected">The expected output from the agent.</param>
/// <returns>The chat messages returned by the agent for additional verification.</returns>
private async Task<List<StreamingChatMessageContent>> ExecuteAgentStreamingAsync(BedrockAgent agent, string input, string? expected = null)
{
var responses = agent.InvokeStreamingAsync(BedrockAgent.CreateSessionId(), input, null, default);
string responseContent = string.Empty;
List<StreamingChatMessageContent> chatMessages = new();
await foreach (var response in responses)
{
responseContent = response.Content ?? string.Empty;
chatMessages.Add(response);
}

if (expected != null)
{
Assert.Contains(expected, responseContent);
}
else
{
Assert.False(string.IsNullOrEmpty(responseContent));
}

return chatMessages;
}

private const string AgentName = "SKIntegrationTestAgent";
Expand All @@ -96,4 +218,19 @@ public void Dispose()
{
this._client.Dispose();
}

private sealed class WeatherPlugin

Check failure on line 222 in dotnet/src/IntegrationTests/Agents/BedrockAgentTests.cs

View workflow job for this annotation

GitHub Actions / dotnet-build-and-test (8.0, ubuntu-latest, Release, true, integration)

'BedrockAgentTests.WeatherPlugin' is an internal class that is apparently never instantiated. If so, remove the code from the assembly. If this class is intended to contain only static members, make it 'static' (Module in Visual Basic). (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1812)

Check failure on line 222 in dotnet/src/IntegrationTests/Agents/BedrockAgentTests.cs

View workflow job for this annotation

GitHub Actions / dotnet-build-and-test (8.0, ubuntu-latest, Release, true, integration)

'BedrockAgentTests.WeatherPlugin' is an internal class that is apparently never instantiated. If so, remove the code from the assembly. If this class is intended to contain only static members, make it 'static' (Module in Visual Basic). (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1812)

Check failure on line 222 in dotnet/src/IntegrationTests/Agents/BedrockAgentTests.cs

View workflow job for this annotation

GitHub Actions / dotnet-build-and-test (8.0, windows-latest, Debug)

'BedrockAgentTests.WeatherPlugin' is an internal class that is apparently never instantiated. If so, remove the code from the assembly. If this class is intended to contain only static members, make it 'static' (Module in Visual Basic). (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1812)

Check failure on line 222 in dotnet/src/IntegrationTests/Agents/BedrockAgentTests.cs

View workflow job for this annotation

GitHub Actions / dotnet-build-and-test (8.0, windows-latest, Debug)

'BedrockAgentTests.WeatherPlugin' is an internal class that is apparently never instantiated. If so, remove the code from the assembly. If this class is intended to contain only static members, make it 'static' (Module in Visual Basic). (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1812)

Check failure on line 222 in dotnet/src/IntegrationTests/Agents/BedrockAgentTests.cs

View workflow job for this annotation

GitHub Actions / dotnet-build-and-test (8.0, windows-latest, Release)

'BedrockAgentTests.WeatherPlugin' is an internal class that is apparently never instantiated. If so, remove the code from the assembly. If this class is intended to contain only static members, make it 'static' (Module in Visual Basic). (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1812)

Check failure on line 222 in dotnet/src/IntegrationTests/Agents/BedrockAgentTests.cs

View workflow job for this annotation

GitHub Actions / dotnet-build-and-test (8.0, windows-latest, Release)

'BedrockAgentTests.WeatherPlugin' is an internal class that is apparently never instantiated. If so, remove the code from the assembly. If this class is intended to contain only static members, make it 'static' (Module in Visual Basic). (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1812)
{
[KernelFunction, Description("Provides realtime weather information.")]
public string Current([Description("The location to get the weather for.")] string location)
{
return $"The current weather in {location} is 72 degrees.";
}

[KernelFunction, Description("Forecast weather information.")]
public string Forecast([Description("The location to get the weather for.")] string location)
{
return $"The forecast for {location} is 75 degrees tomorrow.";
}
}
}

0 comments on commit c54b5d2

Please sign in to comment.