Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public static bool TryGetFunctionAndArguments(
arguments = [];
foreach (var parameter in functionToolCall.Arguments)
{
arguments[parameter.Key] = parameter.Value?.ToString();
arguments[parameter.Key] = parameter.Value;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,11 @@ internal static JsonElement TransformToOpenApi3Schema(JsonElement jsonElement)

static void TransformOpenApi3Object(JsonObject obj)
{
if (obj.TryGetPropertyValue("additionalProperties", out _))
{
obj.Remove("additionalProperties");
}

if (obj.TryGetPropertyValue("properties", out JsonNode? propsNode) && propsNode is JsonObject properties)
{
foreach (var property in properties)
Expand Down Expand Up @@ -386,7 +391,18 @@ static void TransformOpenApi3Object(JsonObject obj)
{
if (propertyObj.TryGetPropertyValue("items", out JsonNode? itemsNode) && itemsNode is JsonObject itemsObj)
{
TransformOpenApi3Object(itemsObj);
// Ensure AnyOf array is considered
if (itemsObj.TryGetPropertyValue("anyOf", out JsonNode? anyOfNode) && anyOfNode is JsonArray anyOfArray)
{
foreach (var anyOfObj in anyOfArray.OfType<JsonObject>())
{
TransformOpenApi3Object(anyOfObj);
}
}
else
{
TransformOpenApi3Object(itemsObj);
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,17 @@ namespace SemanticKernel.IntegrationTests.Connectors.Google.Gemini;

public sealed class GeminiFunctionCallingTests(ITestOutputHelper output) : TestsBase(output)
{
[RetryTheory]
[InlineData(ServiceType.GoogleAI, Skip = "This test is for manual verification.")]
[InlineData(ServiceType.VertexAI, Skip = "This test is for manual verification.")]
public async Task ChatGenerationEnabledFunctionsShouldReturnFunctionToCallAsync(ServiceType serviceType)
private const string SkipMessage = "This test is for manual verification.";

[RetryTheory(Skip = SkipMessage)]
[InlineData(ServiceType.GoogleAI, true)]
[InlineData(ServiceType.VertexAI, false)]
public async Task ChatGenerationEnabledFunctionsShouldReturnFunctionToCallAsync(ServiceType serviceType, bool isBeta)
{
// Arrange
var kernel = new Kernel();
kernel.ImportPluginFromType<CustomerPlugin>(nameof(CustomerPlugin));
var sut = this.GetChatService(serviceType);
var sut = this.GetChatService(serviceType, isBeta);
var chatHistory = new ChatHistory();
chatHistory.AddUserMessage("Hello, could you show me list of customers?");
var executionSettings = new GeminiPromptExecutionSettings()
Expand All @@ -44,15 +46,15 @@ public async Task ChatGenerationEnabledFunctionsShouldReturnFunctionToCallAsync(
item.FullyQualifiedName == $"{nameof(CustomerPlugin)}{GeminiFunction.NameSeparator}{nameof(CustomerPlugin.GetCustomers)}");
}

[RetryTheory]
[InlineData(ServiceType.GoogleAI, Skip = "This test is for manual verification.")]
[InlineData(ServiceType.VertexAI, Skip = "This test is for manual verification.")]
public async Task ChatStreamingEnabledFunctionsShouldReturnFunctionToCallAsync(ServiceType serviceType)
[RetryTheory(Skip = SkipMessage)]
[InlineData(ServiceType.GoogleAI, true)]
[InlineData(ServiceType.VertexAI, false)]
public async Task ChatStreamingEnabledFunctionsShouldReturnFunctionToCallAsync(ServiceType serviceType, bool isBeta)
{
// Arrange
var kernel = new Kernel();
kernel.ImportPluginFromType<CustomerPlugin>(nameof(CustomerPlugin));
var sut = this.GetChatService(serviceType);
var sut = this.GetChatService(serviceType, isBeta);
var chatHistory = new ChatHistory();
chatHistory.AddUserMessage("Hello, could you show me list of customers?");
var executionSettings = new GeminiPromptExecutionSettings()
Expand All @@ -74,15 +76,15 @@ public async Task ChatStreamingEnabledFunctionsShouldReturnFunctionToCallAsync(S
item.FullyQualifiedName == $"{nameof(CustomerPlugin)}{GeminiFunction.NameSeparator}{nameof(CustomerPlugin.GetCustomers)}");
}

[RetryTheory]
[InlineData(ServiceType.GoogleAI, Skip = "This test is for manual verification.")]
[InlineData(ServiceType.VertexAI, Skip = "This test is for manual verification.")]
public async Task ChatGenerationAutoInvokeShouldCallOneFunctionAndReturnResponseAsync(ServiceType serviceType)
[RetryTheory(Skip = SkipMessage)]
[InlineData(ServiceType.GoogleAI, true)]
[InlineData(ServiceType.VertexAI, false)]
public async Task ChatGenerationAutoInvokeShouldCallOneFunctionAndReturnResponseAsync(ServiceType serviceType, bool isBeta)
{
// Arrange
var kernel = new Kernel();
kernel.ImportPluginFromType<CustomerPlugin>("CustomerPlugin");
var sut = this.GetChatService(serviceType);
var sut = this.GetChatService(serviceType, isBeta);
var chatHistory = new ChatHistory();
chatHistory.AddUserMessage("Hello, could you show me list of customers?");
var executionSettings = new GeminiPromptExecutionSettings()
Expand All @@ -101,15 +103,15 @@ public async Task ChatGenerationAutoInvokeShouldCallOneFunctionAndReturnResponse
Assert.Contains("Steve Smith", response.Content, StringComparison.OrdinalIgnoreCase);
}

[RetryTheory]
[InlineData(ServiceType.GoogleAI, Skip = "This test is for manual verification.")]
[InlineData(ServiceType.VertexAI, Skip = "This test is for manual verification.")]
public async Task ChatStreamingAutoInvokeShouldCallOneFunctionAndReturnResponseAsync(ServiceType serviceType)
[RetryTheory(Skip = SkipMessage)]
[InlineData(ServiceType.GoogleAI, true)]
[InlineData(ServiceType.VertexAI, false)]
public async Task ChatStreamingAutoInvokeShouldCallOneFunctionAndReturnResponseAsync(ServiceType serviceType, bool isBeta)
{
// Arrange
var kernel = new Kernel();
kernel.ImportPluginFromType<CustomerPlugin>("CustomerPlugin");
var sut = this.GetChatService(serviceType);
var sut = this.GetChatService(serviceType, isBeta);
var chatHistory = new ChatHistory();
chatHistory.AddUserMessage("Hello, could you show me list of customers?");
var executionSettings = new GeminiPromptExecutionSettings()
Expand All @@ -130,15 +132,15 @@ public async Task ChatStreamingAutoInvokeShouldCallOneFunctionAndReturnResponseA
Assert.Contains("Steve Smith", content, StringComparison.OrdinalIgnoreCase);
}

[RetryTheory]
[InlineData(ServiceType.GoogleAI, Skip = "This test is for manual verification.")]
[InlineData(ServiceType.VertexAI, Skip = "This test is for manual verification.")]
public async Task ChatGenerationAutoInvokeShouldCallTwoFunctionsAndReturnResponseAsync(ServiceType serviceType)
[RetryTheory(Skip = SkipMessage)]
[InlineData(ServiceType.GoogleAI, true)]
[InlineData(ServiceType.VertexAI, false)]
public async Task ChatGenerationAutoInvokeShouldCallTwoFunctionsAndReturnResponseAsync(ServiceType serviceType, bool isBeta)
{
// Arrange
var kernel = new Kernel();
kernel.ImportPluginFromType<CustomerPlugin>("CustomerPlugin");
var sut = this.GetChatService(serviceType);
var sut = this.GetChatService(serviceType, isBeta);
var chatHistory = new ChatHistory();
chatHistory.AddUserMessage("Hello, could you show me list of customers first and next return age of Anna customer?");
var executionSettings = new GeminiPromptExecutionSettings()
Expand All @@ -155,15 +157,15 @@ public async Task ChatGenerationAutoInvokeShouldCallTwoFunctionsAndReturnRespons
Assert.Contains("28", response.Content, StringComparison.OrdinalIgnoreCase);
}

[RetryTheory]
[InlineData(ServiceType.GoogleAI, Skip = "This test is for manual verification.")]
[InlineData(ServiceType.VertexAI, Skip = "This test is for manual verification.")]
public async Task ChatStreamingAutoInvokeShouldCallTwoFunctionsAndReturnResponseAsync(ServiceType serviceType)
[RetryTheory(Skip = SkipMessage)]
[InlineData(ServiceType.GoogleAI, true)]
[InlineData(ServiceType.VertexAI, false)]
public async Task ChatStreamingAutoInvokeShouldCallTwoFunctionsAndReturnResponseAsync(ServiceType serviceType, bool isBeta)
{
// Arrange
var kernel = new Kernel();
kernel.ImportPluginFromType<CustomerPlugin>("CustomerPlugin");
var sut = this.GetChatService(serviceType);
var sut = this.GetChatService(serviceType, isBeta);
var chatHistory = new ChatHistory();
chatHistory.AddUserMessage("Hello, could you show me list of customers first and next return age of Anna customer?");
var executionSettings = new GeminiPromptExecutionSettings()
Expand All @@ -182,16 +184,16 @@ public async Task ChatStreamingAutoInvokeShouldCallTwoFunctionsAndReturnResponse
Assert.Contains("28", content, StringComparison.OrdinalIgnoreCase);
}

[RetryTheory]
[InlineData(ServiceType.GoogleAI, Skip = "This test is for manual verification.")]
[InlineData(ServiceType.VertexAI, Skip = "This test is for manual verification.")]
public async Task ChatGenerationAutoInvokeShouldCallFunctionsMultipleTimesAndReturnResponseAsync(ServiceType serviceType)
[RetryTheory(Skip = SkipMessage)]
[InlineData(ServiceType.GoogleAI, true)]
[InlineData(ServiceType.VertexAI, false)]
public async Task ChatGenerationAutoInvokeShouldCallFunctionsMultipleTimesAndReturnResponseAsync(ServiceType serviceType, bool isBeta)
{
// Arrange
var kernel = new Kernel();
kernel.ImportPluginFromType<CustomerPlugin>("CustomerPlugin");
kernel.ImportPluginFromType<MathPlugin>("MathPlugin");
var sut = this.GetChatService(serviceType);
var sut = this.GetChatService(serviceType, isBeta);
var chatHistory = new ChatHistory();
chatHistory.AddUserMessage(
"Get list of customers and next get customers ages and at the end calculate the sum of ages of all customers.");
Expand All @@ -209,16 +211,16 @@ public async Task ChatGenerationAutoInvokeShouldCallFunctionsMultipleTimesAndRet
Assert.Contains("105", response.Content, StringComparison.OrdinalIgnoreCase);
}

[RetryTheory]
[InlineData(ServiceType.GoogleAI, Skip = "This test is for manual verification.")]
[InlineData(ServiceType.VertexAI, Skip = "This test is for manual verification.")]
public async Task ChatStreamingAutoInvokeShouldCallFunctionsMultipleTimesAndReturnResponseAsync(ServiceType serviceType)
[RetryTheory(Skip = SkipMessage)]
[InlineData(ServiceType.GoogleAI, true)]
[InlineData(ServiceType.VertexAI, false)]
public async Task ChatStreamingAutoInvokeShouldCallFunctionsMultipleTimesAndReturnResponseAsync(ServiceType serviceType, bool isBeta)
{
// Arrange
var kernel = new Kernel();
kernel.ImportPluginFromType<CustomerPlugin>("CustomerPlugin");
kernel.ImportPluginFromType<MathPlugin>("MathPlugin");
var sut = this.GetChatService(serviceType);
var sut = this.GetChatService(serviceType, isBeta);
var chatHistory = new ChatHistory();
chatHistory.AddUserMessage(
"Get list of customers and next get customers ages and at the end calculate the sum of ages of all customers.");
Expand All @@ -238,14 +240,14 @@ public async Task ChatStreamingAutoInvokeShouldCallFunctionsMultipleTimesAndRetu
Assert.Contains("105", content, StringComparison.OrdinalIgnoreCase);
}

[RetryTheory]
[InlineData(ServiceType.GoogleAI, Skip = "This test is for manual verification.")]
[InlineData(ServiceType.VertexAI, Skip = "This test is for manual verification.")]
public async Task ChatGenerationAutoInvokeNullablePropertiesWorksAsync(ServiceType serviceType)
[RetryTheory(Skip = SkipMessage)]
[InlineData(ServiceType.GoogleAI, true)]
[InlineData(ServiceType.VertexAI, false)]
public async Task ChatGenerationAutoInvokeNullablePropertiesWorksAsync(ServiceType serviceType, bool isBeta)
{
var kernel = new Kernel();
kernel.ImportPluginFromType<NullableTestPlugin>();
var sut = this.GetChatService(serviceType);
var sut = this.GetChatService(serviceType, isBeta);

var executionSettings = new GeminiPromptExecutionSettings()
{
Expand All @@ -260,16 +262,16 @@ public async Task ChatGenerationAutoInvokeNullablePropertiesWorksAsync(ServiceTy
Assert.NotNull(response);
}

[RetryTheory]
[InlineData(ServiceType.GoogleAI, Skip = "This test is for manual verification.")]
[InlineData(ServiceType.VertexAI, Skip = "This test is for manual verification.")]
public async Task ChatGenerationAutoInvokeTwoPluginsShouldGetDateAndReturnTasksByDateParamAndReturnResponseAsync(ServiceType serviceType)
[RetryTheory(Skip = SkipMessage)]
[InlineData(ServiceType.GoogleAI, true)]
[InlineData(ServiceType.VertexAI, false)]
public async Task ChatGenerationAutoInvokeTwoPluginsShouldGetDateAndReturnTasksByDateParamAndReturnResponseAsync(ServiceType serviceType, bool isBeta)
{
// Arrange
var kernel = new Kernel();
kernel.ImportPluginFromType<TaskPlugin>(nameof(TaskPlugin));
kernel.ImportPluginFromType<DatePlugin>(nameof(DatePlugin));
var sut = this.GetChatService(serviceType);
var sut = this.GetChatService(serviceType, isBeta);
var chatHistory = new ChatHistory();
chatHistory.AddUserMessage("How many tasks I have to do today? Show me count of tasks for today and date.");
var executionSettings = new GeminiPromptExecutionSettings()
Expand All @@ -286,16 +288,16 @@ public async Task ChatGenerationAutoInvokeTwoPluginsShouldGetDateAndReturnTasksB
Assert.Contains("5", response.Content, StringComparison.OrdinalIgnoreCase);
}

[RetryTheory]
[InlineData(ServiceType.GoogleAI, Skip = "This test is for manual verification.")]
[InlineData(ServiceType.VertexAI, Skip = "This test is for manual verification.")]
public async Task ChatStreamingAutoInvokeTwoPluginsShouldGetDateAndReturnTasksByDateParamAndReturnResponseAsync(ServiceType serviceType)
[RetryTheory(Skip = SkipMessage)]
[InlineData(ServiceType.GoogleAI, true)]
[InlineData(ServiceType.VertexAI, false)]
public async Task ChatStreamingAutoInvokeTwoPluginsShouldGetDateAndReturnTasksByDateParamAndReturnResponseAsync(ServiceType serviceType, bool isBeta)
{
// Arrange
var kernel = new Kernel();
kernel.ImportPluginFromType<TaskPlugin>(nameof(TaskPlugin));
kernel.ImportPluginFromType<DatePlugin>(nameof(DatePlugin));
var sut = this.GetChatService(serviceType);
var sut = this.GetChatService(serviceType, isBeta);
var chatHistory = new ChatHistory();
chatHistory.AddUserMessage("How many tasks I have to do today? Show me count of tasks for today and date.");
var executionSettings = new GeminiPromptExecutionSettings()
Expand All @@ -314,18 +316,18 @@ public async Task ChatStreamingAutoInvokeTwoPluginsShouldGetDateAndReturnTasksBy
Assert.Contains("5", content, StringComparison.OrdinalIgnoreCase);
}

[RetryTheory]
[InlineData(ServiceType.GoogleAI, Skip = "This test is for manual verification.")]
[InlineData(ServiceType.VertexAI, Skip = "This test is for manual verification.")]
public async Task ChatGenerationAutoInvokeShouldCallFunctionWithEnumParameterAndReturnResponseAsync(ServiceType serviceType)
[RetryTheory(Skip = SkipMessage)]
[InlineData(ServiceType.GoogleAI, true)]
[InlineData(ServiceType.VertexAI, false)]
public async Task ChatGenerationAutoInvokeShouldCallFunctionWithEnumParameterAndReturnResponseAsync(ServiceType serviceType, bool isBeta)
{
// Arrange
var kernel = new Kernel();
var timeProvider = new FakeTimeProvider();
timeProvider.SetUtcNow(new DateTimeOffset(new DateTime(2024, 4, 24))); // Wednesday
var timePlugin = new TimePlugin(timeProvider);
kernel.ImportPluginFromObject(timePlugin, nameof(TimePlugin));
var sut = this.GetChatService(serviceType);
var sut = this.GetChatService(serviceType, isBeta);
var chatHistory = new ChatHistory();
chatHistory.AddUserMessage("When was last friday? Show the date in format DD.MM.YYYY for example: 15.07.2019");
var executionSettings = new GeminiPromptExecutionSettings()
Expand All @@ -342,18 +344,18 @@ public async Task ChatGenerationAutoInvokeShouldCallFunctionWithEnumParameterAnd
Assert.Contains("19.04.2024", response.Content, StringComparison.OrdinalIgnoreCase);
}

[RetryTheory]
[InlineData(ServiceType.GoogleAI, Skip = "This test is for manual verification.")]
[InlineData(ServiceType.VertexAI, Skip = "This test is for manual verification.")]
public async Task ChatStreamingAutoInvokeShouldCallFunctionWithEnumParameterAndReturnResponseAsync(ServiceType serviceType)
[RetryTheory(Skip = SkipMessage)]
[InlineData(ServiceType.GoogleAI, true)]
[InlineData(ServiceType.VertexAI, false)]
public async Task ChatStreamingAutoInvokeShouldCallFunctionWithEnumParameterAndReturnResponseAsync(ServiceType serviceType, bool isBeta)
{
// Arrange
var kernel = new Kernel();
var timeProvider = new FakeTimeProvider();
timeProvider.SetUtcNow(new DateTimeOffset(new DateTime(2024, 4, 24))); // Wednesday
var timePlugin = new TimePlugin(timeProvider);
kernel.ImportPluginFromObject(timePlugin, nameof(TimePlugin));
var sut = this.GetChatService(serviceType);
var sut = this.GetChatService(serviceType, isBeta);
var chatHistory = new ChatHistory();
chatHistory.AddUserMessage("When was last friday? Show the date in format DD.MM.YYYY for example: 15.07.2019");
var executionSettings = new GeminiPromptExecutionSettings()
Expand Down
Loading