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
19 changes: 19 additions & 0 deletions dotnet/src/SemanticKernel.IntegrationTests/AI/AIServiceType.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Copyright (c) Microsoft. All rights reserved.

namespace SemanticKernel.IntegrationTests.AI;

/// <summary>
/// Enumeration to run integration tests for different AI services
/// </summary>
public enum AIServiceType
{
/// <summary>
/// Open AI service
/// </summary>
OpenAI = 0,

/// <summary>
/// Azure Open AI service
/// </summary>
AzureOpenAI = 1
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ public OpenAICompletionTests(ITestOutputHelper output)
.AddEnvironmentVariables()
.AddUserSecrets<OpenAICompletionTests>()
.Build();

this._serviceConfiguration.Add(AIServiceType.OpenAI, this.ConfigureOpenAI);
this._serviceConfiguration.Add(AIServiceType.AzureOpenAI, this.ConfigureAzureOpenAI);
}

[Theory(Skip = "OpenAI will often throttle requests. This test is for manual verification.")]
Expand All @@ -40,15 +43,7 @@ public async Task OpenAITestAsync(string prompt, string expectedAnswerContains)
// Arrange
IKernel target = Kernel.Builder.WithLogger(this._logger).Build();

OpenAIConfiguration? openAIConfiguration = this._configuration.GetSection("OpenAI").Get<OpenAIConfiguration>();
Assert.NotNull(openAIConfiguration);

target.Config.AddOpenAITextCompletion(
serviceId: openAIConfiguration.Label,
modelId: openAIConfiguration.ModelId,
apiKey: openAIConfiguration.ApiKey);

target.Config.SetDefaultTextCompletionService(openAIConfiguration.Label);
this.ConfigureOpenAI(target);

IDictionary<string, ISKFunction> skill = TestHelpers.GetSkill("ChatSkill", target);

Expand All @@ -66,18 +61,7 @@ public async Task AzureOpenAITestAsync(string prompt, string expectedAnswerConta
// Arrange
IKernel target = Kernel.Builder.WithLogger(this._logger).Build();

// OpenAIConfiguration? openAIConfiguration = this._configuration.GetSection("OpenAI").Get<OpenAIConfiguration>();
// Assert.NotNull(openAIConfiguration);
AzureOpenAIConfiguration? azureOpenAIConfiguration = this._configuration.GetSection("AzureOpenAI").Get<AzureOpenAIConfiguration>();
Assert.NotNull(azureOpenAIConfiguration);

target.Config.AddAzureOpenAITextCompletion(
serviceId: azureOpenAIConfiguration.Label,
deploymentName: azureOpenAIConfiguration.DeploymentName,
endpoint: azureOpenAIConfiguration.Endpoint,
apiKey: azureOpenAIConfiguration.ApiKey);

target.Config.SetDefaultTextCompletionService(azureOpenAIConfiguration.Label);
this.ConfigureAzureOpenAI(target);

IDictionary<string, ISKFunction> skill = TestHelpers.GetSkill("ChatSkill", target);

Expand Down Expand Up @@ -118,11 +102,41 @@ public async Task OpenAIHttpRetryPolicyTestAsync(string prompt, string expectedO
Assert.Contains(expectedOutput, this._testOutputHelper.GetLogs(), StringComparison.InvariantCultureIgnoreCase);
}

[Theory(Skip = "This test is for manual verification.")]
[InlineData("\n", AIServiceType.OpenAI)]
[InlineData("\r\n", AIServiceType.OpenAI)]
[InlineData("\n", AIServiceType.AzureOpenAI)]
[InlineData("\r\n", AIServiceType.AzureOpenAI)]
public async Task CompletionWithDifferentLineEndingsAsync(string lineEnding, AIServiceType service)
{
// Arrange
var prompt =
$"Given a json input and a request. Apply the request on the json input and return the result. " +
$"Put the result in between <result></result> tags{lineEnding}" +
$"Input:{lineEnding}{{\"name\": \"John\", \"age\": 30}}{lineEnding}{lineEnding}Request:{lineEnding}name";

const string expectedAnswerContains = "<result>John</result>";

IKernel target = Kernel.Builder.WithLogger(this._logger).Build();

this._serviceConfiguration[service](target);

IDictionary<string, ISKFunction> skill = TestHelpers.GetSkill("ChatSkill", target);

// Act
SKContext actual = await target.RunAsync(prompt, skill["Chat"]);

// Assert
Assert.Contains(expectedAnswerContains, actual.Result, StringComparison.InvariantCultureIgnoreCase);
}

#region internals

private readonly XunitLogger<Kernel> _logger;
private readonly RedirectOutput _testOutputHelper;

private readonly Dictionary<AIServiceType, Action<IKernel>> _serviceConfiguration = new();

public void Dispose()
{
this.Dispose(true);
Expand All @@ -143,5 +157,34 @@ private void Dispose(bool disposing)
}
}

private void ConfigureOpenAI(IKernel kernel)
{
var openAIConfiguration = this._configuration.GetSection("OpenAI").Get<OpenAIConfiguration>();

Assert.NotNull(openAIConfiguration);

kernel.Config.AddOpenAITextCompletion(
serviceId: openAIConfiguration.Label,
modelId: openAIConfiguration.ModelId,
apiKey: openAIConfiguration.ApiKey);

kernel.Config.SetDefaultTextCompletionService(openAIConfiguration.Label);
}

private void ConfigureAzureOpenAI(IKernel kernel)
{
var azureOpenAIConfiguration = this._configuration.GetSection("AzureOpenAI").Get<AzureOpenAIConfiguration>();

Assert.NotNull(azureOpenAIConfiguration);

kernel.Config.AddAzureOpenAITextCompletion(
serviceId: azureOpenAIConfiguration.Label,
deploymentName: azureOpenAIConfiguration.DeploymentName,
endpoint: azureOpenAIConfiguration.Endpoint,
apiKey: azureOpenAIConfiguration.ApiKey);

kernel.Config.SetDefaultTextCompletionService(azureOpenAIConfiguration.Label);
}

#endregion
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Copyright (c) Microsoft. All rights reserved.

using System;
using Microsoft.SemanticKernel.Text;
using Xunit;

namespace SemanticKernel.UnitTests.Text;

/// <summary>
/// Unit tests for <see cref="StringExtensions"/>
/// </summary>
public class StringExtensionsTests
{
[Theory]
[InlineData("\r\n", "\n")]
[InlineData("Test string\r\n", "Test string\n")]
[InlineData("\r\nTest string", "\nTest string")]
[InlineData("\r\nTest string\r\n", "\nTest string\n")]
public void ItNormalizesLineEndingsCorrectly(string input, string expectedString)
{
// Act
input = input.NormalizeLineEndings();

// Assert
Assert.Equal(expectedString, input);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ public async Task<string> CompleteAsync(string text, CompleteRequestSettings req

var requestBody = Json.Serialize(new AzureTextCompletionRequest
{
Prompt = text,
Prompt = text.NormalizeLineEndings(),
Temperature = requestSettings.Temperature,
TopP = requestSettings.TopP,
PresencePenalty = requestSettings.PresencePenalty,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ public Task<string> CompleteAsync(string text, CompleteRequestSettings requestSe
var requestBody = Json.Serialize(new OpenAITextCompletionRequest
{
Model = this._modelId,
Prompt = text,
Prompt = text.NormalizeLineEndings(),
Temperature = requestSettings.Temperature,
TopP = requestSettings.TopP,
PresencePenalty = requestSettings.PresencePenalty,
Expand Down
5 changes: 5 additions & 0 deletions dotnet/src/SemanticKernel/Text/StringExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,9 @@ internal static bool EqualsIgnoreCase(this string src, string value)
{
return src.Equals(value, StringComparison.InvariantCultureIgnoreCase);
}

internal static string NormalizeLineEndings(this string src)
{
return src.Replace("\r\n", "\n", StringComparison.InvariantCultureIgnoreCase);
}
}