Skip to content

Commit

Permalink
Merge from main to feature-openapi (microsoft#264)
Browse files Browse the repository at this point in the history
  • Loading branch information
adrianwyatt authored Mar 31, 2023
1 parent 7b7fe96 commit e0e1540
Show file tree
Hide file tree
Showing 108 changed files with 11,108 additions and 33 deletions.
10 changes: 10 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -410,6 +410,7 @@ FodyWeavers.xsd
*.key

.env
certs/
launchSettings.json
appsettings.development.json
testsettings.development.json
Expand Down Expand Up @@ -446,3 +447,12 @@ _site
# Yarn
.yarn
.yarnrc.yml

# Python Environments
.env
.venv
.myenv
env/
venv/
myvenv/
ENV/
1 change: 0 additions & 1 deletion dotnet/SK-dotnet.sln
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.4.33213.308
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,13 @@
<ProjectReference Include="..\..\SemanticKernel\SemanticKernel.csproj" />
</ItemGroup>

<ItemGroup>
<None Update="HuggingFace\TestData\completion_test_response.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="HuggingFace\TestData\embeddings_test_response.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// Copyright (c) Microsoft. All rights reserved.

using System.IO;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using Moq;
using Moq.Protected;

namespace SemanticKernel.Connectors.UnitTests.HuggingFace;

/// <summary>
/// Helper for HuggingFace test purposes.
/// </summary>
internal static class HuggingFaceTestHelper
{
/// <summary>
/// Reads test response from file for mocking purposes.
/// </summary>
/// <param name="fileName">Name of the file with test response.</param>
internal static string GetTestResponse(string fileName)
{
return File.ReadAllText($"./HuggingFace/TestData/{fileName}");
}

/// <summary>
/// Returns mocked instance of <see cref="HttpClientHandler"/>.
/// </summary>
/// <param name="httpResponseMessage">Message to return for mocked <see cref="HttpClientHandler"/>.</param>
internal static HttpClientHandler GetHttpClientHandlerMock(HttpResponseMessage httpResponseMessage)
{
var httpClientHandler = new Mock<HttpClientHandler>();

httpClientHandler
.Protected()
.Setup<Task<HttpResponseMessage>>(
"SendAsync",
ItExpr.IsAny<HttpRequestMessage>(),
ItExpr.IsAny<CancellationToken>())
.ReturnsAsync(httpResponseMessage);

return httpClientHandler.Object;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[
{
"generated_text": "This is test completion response"
}
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"data": [
{
"embedding": [
-0.08541165292263031,
0.08639130741357803,
-0.12805694341659546,
-0.2877824902534485,
0.2114177942276001,
-0.29374566674232483,
-0.10496602207422256,
0.009402364492416382
],
"index": 0,
"object": "embedding"
}
],
"object": "list",
"usage": {
"prompt_tokens": 15,
"total_tokens": 15
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
// Copyright (c) Microsoft. All rights reserved.

using System;
using System.Net;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.SemanticKernel.AI.TextCompletion;
using Microsoft.SemanticKernel.Connectors.HuggingFace.TextCompletion;
using Xunit;

namespace SemanticKernel.Connectors.UnitTests.HuggingFace.TextCompletion;

/// <summary>
/// Unit tests for <see cref="HuggingFaceTextCompletion"/> class.
/// </summary>
public class HuggingFaceTextCompletionTests : IDisposable
{
private const string Endpoint = "http://localhost:5000/completions";
private const string Model = "gpt2";

private readonly HttpResponseMessage _response = new()
{
StatusCode = HttpStatusCode.OK,
};

/// <summary>
/// Verifies that <see cref="HuggingFaceTextCompletion.CompleteAsync(string, CompleteRequestSettings, CancellationToken)"/>
/// returns expected completed text without errors.
/// </summary>
[Fact]
public async Task ItReturnsCompletionCorrectlyAsync()
{
// Arrange
const string prompt = "This is test";
CompleteRequestSettings requestSettings = new();

using var service = this.CreateService(HuggingFaceTestHelper.GetTestResponse("completion_test_response.json"));

// Act
var completion = await service.CompleteAsync(prompt, requestSettings);

// Assert
Assert.Equal("This is test completion response", completion);
}

/// <summary>
/// Initializes <see cref="HuggingFaceTextCompletion"/> with mocked <see cref="HttpClientHandler"/>.
/// </summary>
/// <param name="testResponse">Test response for <see cref="HttpClientHandler"/> to return.</param>
private HuggingFaceTextCompletion CreateService(string testResponse)
{
this._response.Content = new StringContent(testResponse);

var httpClientHandler = HuggingFaceTestHelper.GetHttpClientHandlerMock(this._response);

return new HuggingFaceTextCompletion(new Uri(Endpoint), Model, httpClientHandler);
}

public void Dispose()
{
this.Dispose(true);
GC.SuppressFinalize(this);
}

protected virtual void Dispose(bool disposing)
{
if (disposing)
{
this._response.Dispose();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
// Copyright (c) Microsoft. All rights reserved.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.SemanticKernel.Connectors.HuggingFace.TextEmbedding;
using Xunit;

namespace SemanticKernel.Connectors.UnitTests.HuggingFace.TextEmbedding;

/// <summary>
/// Unit tests for <see cref="HuggingFaceTextEmbeddingGeneration"/> class.
/// </summary>
public class HuggingFaceEmbeddingGenerationTests : IDisposable
{
private const string Endpoint = "http://localhost:5000/embeddings";
private const string Model = "gpt2";

private readonly HttpResponseMessage _response = new()
{
StatusCode = HttpStatusCode.OK,
};

/// <summary>
/// Verifies that <see cref="HuggingFaceTextEmbeddingGeneration.GenerateEmbeddingsAsync(IList{string})"/>
/// returns expected list of generated embeddings without errors.
/// </summary>
[Fact]
public async Task ItReturnsEmbeddingsCorrectlyAsync()
{
// Arrange
const int expectedEmbeddingCount = 1;
const int expectedVectorCount = 8;
List<string> data = new() { "test_string_1", "test_string_2", "test_string_3" };

using var service = this.CreateService(HuggingFaceTestHelper.GetTestResponse("embeddings_test_response.json"));

// Act
var embeddings = await service.GenerateEmbeddingsAsync(data);

// Assert
Assert.NotNull(embeddings);
Assert.Equal(expectedEmbeddingCount, embeddings.Count);
Assert.Equal(expectedVectorCount, embeddings.First().Count);
}

/// <summary>
/// Initializes <see cref="HuggingFaceTextEmbeddingGeneration"/> with mocked <see cref="HttpClientHandler"/>.
/// </summary>
/// <param name="testResponse">Test response for <see cref="HttpClientHandler"/> to return.</param>
private HuggingFaceTextEmbeddingGeneration CreateService(string testResponse)
{
this._response.Content = new StringContent(testResponse);

var httpClientHandler = HuggingFaceTestHelper.GetHttpClientHandlerMock(this._response);

return new HuggingFaceTextEmbeddingGeneration(new Uri(Endpoint), Model, httpClientHandler);
}

public void Dispose()
{
this.Dispose(true);
GC.SuppressFinalize(this);
}

protected virtual void Dispose(bool disposing)
{
if (disposing)
{
this._response.Dispose();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// Copyright (c) Microsoft. All rights reserved.

using System;
using System.Threading.Tasks;
using Microsoft.Extensions.Configuration;
using Microsoft.SemanticKernel.AI.TextCompletion;
using Microsoft.SemanticKernel.Connectors.HuggingFace.TextCompletion;
using Xunit;

namespace SemanticKernel.IntegrationTests.Connectors.HuggingFace.TextCompletion;

/// <summary>
/// Integration tests for <see cref="HuggingFaceTextCompletion"/>.
/// </summary>
public sealed class HuggingFaceTextCompletionTests
{
private const string Endpoint = "http://localhost:5000/completions";
private const string Model = "gpt2";

private readonly IConfigurationRoot _configuration;

public HuggingFaceTextCompletionTests()
{
// Load configuration
this._configuration = new ConfigurationBuilder()
.AddJsonFile(path: "testsettings.json", optional: false, reloadOnChange: true)
.AddJsonFile(path: "testsettings.development.json", optional: true, reloadOnChange: true)
.AddEnvironmentVariables()
.Build();
}

[Fact(Skip = "This test is for manual verification.")]
public async Task HuggingFaceLocalAndRemoteTextCompletionAsync()
{
// Arrange
const string input = "This is test";

using var huggingFaceLocal = new HuggingFaceTextCompletion(new Uri(Endpoint), Model);
using var huggingFaceRemote = new HuggingFaceTextCompletion(this.GetApiKey(), Model);

// Act
var localResponse = await huggingFaceLocal.CompleteAsync(input, new CompleteRequestSettings()).ConfigureAwait(false);
var remoteResponse = await huggingFaceRemote.CompleteAsync(input, new CompleteRequestSettings()).ConfigureAwait(false);

// Assert
Assert.NotNull(localResponse);
Assert.NotNull(remoteResponse);

Assert.StartsWith(input, localResponse, StringComparison.InvariantCulture);
Assert.StartsWith(input, remoteResponse, StringComparison.InvariantCulture);
}

private string GetApiKey()
{
return this._configuration.GetSection("HuggingFace:ApiKey").Get<string>()!;
}
}
8 changes: 7 additions & 1 deletion dotnet/src/SemanticKernel.IntegrationTests/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
1. **Azure OpenAI**: go to the [Azure OpenAI Quickstart](https://learn.microsoft.com/en-us/azure/cognitive-services/openai/quickstart)
and deploy an instance of Azure OpenAI, deploy a model like "text-davinci-003" find your Endpoint and API key.
2. **OpenAI**: go to [OpenAI](https://openai.com/api/) to register and procure your API key.
3. **Azure Bing Web Search API**: go to [Bing Web Seach API](https://www.microsoft.com/en-us/bing/apis/bing-web-search-api)
3. **HuggingFace API key**: see https://huggingface.co/docs/huggingface_hub/guides/inference for details.
4. **Azure Bing Web Search API**: go to [Bing Web Seach API](https://www.microsoft.com/en-us/bing/apis/bing-web-search-api)
and select `Try Now` to get started.

## Setup
Expand Down Expand Up @@ -42,6 +43,9 @@ For example:
"Endpoint": "https://contoso.openai.azure.com/",
"ApiKey": "...."
},
"HuggingFace": {
"ApiKey": ""
},
"Bing": {
"ApiKey": "...."
}
Expand All @@ -58,6 +62,7 @@ For example:
export AzureOpenAI__DeploymentName="azure-text-davinci-003"
export AzureOpenAIEmbeddings__DeploymentName="azure-text-embedding-ada-002"
export AzureOpenAI__Endpoint="https://contoso.openai.azure.com/"
export HuggingFace__ApiKey="...."
export Bing__ApiKey="...."
```

Expand All @@ -69,5 +74,6 @@ For example:
$env:AzureOpenAI__DeploymentName = "azure-text-davinci-003"
$env:AzureOpenAIEmbeddings__DeploymentName = "azure-text-embedding-ada-002"
$env:AzureOpenAI__Endpoint = "https://contoso.openai.azure.com/"
$env:HuggingFace__ApiKey = "...."
$env:Bing__ApiKey = "...."
```
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,8 @@
</Content>
</ItemGroup>

<ItemGroup>
<Folder Include="Connectors\HuggingFace" />
</ItemGroup>

</Project>
3 changes: 3 additions & 0 deletions dotnet/src/SemanticKernel.IntegrationTests/testsettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@
"Endpoint": "",
"ApiKey": ""
},
"HuggingFace": {
"ApiKey": ""
},
"Bing": {
"ApiKey": ""
}
Expand Down
Loading

0 comments on commit e0e1540

Please sign in to comment.