mirror of
https://github.com/microsoft/semantic-kernel.git
synced 2026-10-12 02:19:06 +00:00
An alternative version of OpenAI authentication functionality
This commit is contained in:
@@ -49,7 +49,7 @@ public static class Example22_OpenApiPlugin_AzureKeyVault
|
||||
var plugin = await kernel.ImportPluginFunctionsAsync(
|
||||
PluginResourceNames.AzureKeyVault,
|
||||
stream!,
|
||||
new OpenApiFunctionExecutionParameters { AuthCallback = authenticationProvider.AuthenticateRequestAsync });
|
||||
new OpenApiFunctionExecutionParameters { AuthenticateCallbackProvider = (manifest) => authenticationProvider.AuthenticateRequestAsync });
|
||||
|
||||
// Add arguments for required parameters, arguments for optional ones can be skipped.
|
||||
var contextVariables = new ContextVariables();
|
||||
@@ -80,7 +80,7 @@ public static class Example22_OpenApiPlugin_AzureKeyVault
|
||||
stream!,
|
||||
new OpenApiFunctionExecutionParameters
|
||||
{
|
||||
AuthCallback = authenticationProvider.AuthenticateRequestAsync,
|
||||
AuthenticateCallbackProvider = _ => authenticationProvider.AuthenticateRequestAsync,
|
||||
EnableDynamicPayload = true
|
||||
});
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ public static class Example23_OpenApiPlugin_GitHub
|
||||
var plugin = await kernel.ImportPluginFunctionsAsync(
|
||||
"GitHubPlugin",
|
||||
"../../../../../../samples/dotnet/OpenApiPluginsExample/GitHubPlugin/openapi.json",
|
||||
new OpenApiFunctionExecutionParameters { AuthCallback = authenticationProvider.AuthenticateRequestAsync });
|
||||
new OpenApiFunctionExecutionParameters { AuthenticateCallbackProvider = _ => authenticationProvider.AuthenticateRequestAsync });
|
||||
|
||||
// Add arguments for required parameters, arguments for optional ones can be skipped.
|
||||
var contextVariables = new ContextVariables();
|
||||
@@ -69,7 +69,7 @@ public static class Example23_OpenApiPlugin_GitHub
|
||||
var plugin = await kernel.ImportPluginFunctionsAsync(
|
||||
"GitHubPlugin",
|
||||
"../../../../../../samples/dotnet/OpenApiPluginsExample/GitHubPlugin/openapi.json",
|
||||
new OpenApiFunctionExecutionParameters { AuthCallback = authenticationProvider.AuthenticateRequestAsync });
|
||||
new OpenApiFunctionExecutionParameters { AuthenticateCallbackProvider = _ => authenticationProvider.AuthenticateRequestAsync });
|
||||
|
||||
// Add arguments for required parameters, arguments for optional ones can be skipped.
|
||||
var contextVariables = new ContextVariables();
|
||||
|
||||
@@ -43,12 +43,12 @@ public static class Example24_OpenApiPlugin_Jira
|
||||
if (useLocalFile)
|
||||
{
|
||||
var apiPluginFile = "./../../../Plugins/JiraPlugin/openapi.json";
|
||||
jiraFunctions = await kernel.ImportPluginFunctionsAsync("jiraPlugin", apiPluginFile, new OpenApiFunctionExecutionParameters(authCallback: tokenProvider.AuthenticateRequestAsync));
|
||||
jiraFunctions = await kernel.ImportPluginFunctionsAsync("jiraPlugin", apiPluginFile, new OpenApiFunctionExecutionParameters(authCallbackProvider: (_) => tokenProvider.AuthenticateRequestAsync));
|
||||
}
|
||||
else
|
||||
{
|
||||
var apiPluginRawFileURL = new Uri("https://raw.githubusercontent.com/microsoft/PowerPlatformConnectors/dev/certified-connectors/JIRA/apiDefinition.swagger.json");
|
||||
jiraFunctions = await kernel.ImportPluginFunctionsAsync("jiraPlugin", apiPluginRawFileURL, new OpenApiFunctionExecutionParameters(httpClient, tokenProvider.AuthenticateRequestAsync));
|
||||
jiraFunctions = await kernel.ImportPluginFunctionsAsync("jiraPlugin", apiPluginRawFileURL, new OpenApiFunctionExecutionParameters(authCallbackProvider: (_) => tokenProvider.AuthenticateRequestAsync));
|
||||
}
|
||||
|
||||
// GetIssue Function
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.SemanticKernel.Functions.OpenAPI.Authentication;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a delegate that provides authentication callback based on plugin manifest auth config.
|
||||
/// </summary>
|
||||
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
|
||||
public delegate AuthenticateRequestAsyncCallback AuthenticateCallbackProvider(OpenAIAuthenticationManifest? manifest = null);
|
||||
@@ -0,0 +1,98 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Microsoft.SemanticKernel.Functions.OpenAPI.Authentication;
|
||||
|
||||
/// <summary>
|
||||
/// Represents the authentication configuration for an OpenAI plugin.
|
||||
/// </summary>
|
||||
public record OpenAIAuthenticationManifest
|
||||
{
|
||||
/// <summary>
|
||||
/// The type of authentication.
|
||||
/// </summary>
|
||||
public OpenAIAuthenticationType Type { get; set; } = OpenAIAuthenticationType.None;
|
||||
|
||||
/// <summary>
|
||||
/// The type of authorization.
|
||||
/// </summary>
|
||||
public OpenAIAuthorizationType? AuthorizationType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The client URL.
|
||||
/// </summary>
|
||||
public Uri? ClientUrl { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The authorization URL.
|
||||
/// </summary>
|
||||
public Uri? AuthorizationUrl { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The authorization content type.
|
||||
/// </summary>
|
||||
public string? AuthorizationContentType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The authorization scope.
|
||||
/// </summary>
|
||||
public string? Scope { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The verification tokens.
|
||||
/// </summary>
|
||||
public Dictionary<string, string>? VerificationTokens { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents the type of authentication for an OpenAI plugin.
|
||||
/// </summary>
|
||||
public enum OpenAIAuthenticationType
|
||||
{
|
||||
/// <summary>
|
||||
/// No authentication.
|
||||
/// </summary>
|
||||
[EnumMember(Value = "none")]
|
||||
None,
|
||||
|
||||
/// <summary>
|
||||
/// User HTTP authentication.
|
||||
/// </summary>
|
||||
[EnumMember(Value = "user_http")]
|
||||
UserHttp,
|
||||
|
||||
/// <summary>
|
||||
/// Service HTTP authentication.
|
||||
/// </summary>
|
||||
[EnumMember(Value = "service_http")]
|
||||
ServiceHttp,
|
||||
|
||||
/// <summary>
|
||||
/// OAuth authentication.
|
||||
/// </summary>
|
||||
[EnumMember(Value = "oauth")]
|
||||
OAuth
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents the type of authorization for an OpenAI plugin.
|
||||
/// </summary>
|
||||
[JsonConverter(typeof(JsonStringEnumConverter))]
|
||||
public enum OpenAIAuthorizationType
|
||||
{
|
||||
/// <summary>
|
||||
/// Basic authorization.
|
||||
/// </summary>
|
||||
[EnumMember(Value = "basic")]
|
||||
Basic,
|
||||
|
||||
/// <summary>
|
||||
/// Bearer authorization.
|
||||
/// </summary>
|
||||
[EnumMember(Value = "bearer")]
|
||||
Bearer
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.SemanticKernel.Functions.OpenAPI.Authentication;
|
||||
|
||||
/// <summary>
|
||||
/// An example of OpenAI auth provider.
|
||||
/// </summary>
|
||||
public class OpenAIAuthenticationProvider
|
||||
{
|
||||
private readonly OpenAIAuthenticationManifest _manifest;
|
||||
|
||||
/// <summary>
|
||||
/// Creates an instance of the <see cref="BearerAuthenticationProvider"/> class.
|
||||
/// </summary>
|
||||
/// <param name="manifest">OpenAI auth manifest</param>
|
||||
public OpenAIAuthenticationProvider(OpenAIAuthenticationManifest manifest)
|
||||
{
|
||||
this._manifest = manifest;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies the token to the provided HTTP request message.
|
||||
/// </summary>
|
||||
/// <param name="request">The HTTP request message.</param>
|
||||
public async Task AuthenticateRequestAsync(HttpRequestMessage request)
|
||||
{
|
||||
var token = "fake-token"; //Implement all the required logic to parse the manifest and issue an auth token according to the manifest.
|
||||
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);
|
||||
}
|
||||
}
|
||||
@@ -15,9 +15,13 @@ using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using Microsoft.SemanticKernel.Diagnostics;
|
||||
using Microsoft.SemanticKernel.Functions.OpenAPI.Authentication;
|
||||
using Microsoft.SemanticKernel.Functions.OpenAPI.Model;
|
||||
using Microsoft.SemanticKernel.Functions.OpenAPI.OpenApi;
|
||||
using Microsoft.SemanticKernel.Orchestration;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Newtonsoft.Json.Serialization;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Microsoft.SemanticKernel.Functions.OpenAPI.Extensions;
|
||||
|
||||
@@ -119,8 +123,9 @@ public static class KernelAIPluginExtensions
|
||||
var pluginContents = await LoadDocumentFromUriAsync(
|
||||
kernel,
|
||||
uri,
|
||||
executionParameters,
|
||||
httpClient,
|
||||
executionParameters?.UserAgent,
|
||||
executionParameters?.AuthenticateCallbackProvider?.Invoke(null),
|
||||
cancellationToken).ConfigureAwait(false);
|
||||
|
||||
return await CompleteImportAsync(
|
||||
@@ -178,15 +183,27 @@ public static class KernelAIPluginExtensions
|
||||
Uri? documentUri = null,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
var document = pluginContents;
|
||||
|
||||
AuthenticateRequestAsyncCallback? authCallback = null;
|
||||
|
||||
if (TryParseAIPluginForUrl(pluginContents, out var openApiUrl))
|
||||
{
|
||||
return await kernel
|
||||
.ImportPluginFunctionsAsync(
|
||||
pluginName,
|
||||
new Uri(openApiUrl),
|
||||
executionParameters,
|
||||
cancellationToken: cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
if (TryParseAIPluginForAuth(document, out var openApiManifestAuth) && openApiManifestAuth!.Type != OpenAIAuthenticationType.None)
|
||||
{
|
||||
if (executionParameters?.AuthenticateCallbackProvider != null)
|
||||
{
|
||||
authCallback = executionParameters.AuthenticateCallbackProvider(openApiManifestAuth);
|
||||
}
|
||||
}
|
||||
|
||||
document = await LoadDocumentFromUriAsync(
|
||||
kernel,
|
||||
new Uri(openApiUrl),
|
||||
httpClient,
|
||||
executionParameters?.UserAgent,
|
||||
authCallback,
|
||||
cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
return await LoadPluginAsync(
|
||||
@@ -194,7 +211,8 @@ public static class KernelAIPluginExtensions
|
||||
pluginName,
|
||||
executionParameters,
|
||||
httpClient,
|
||||
pluginContents,
|
||||
document,
|
||||
authCallback,
|
||||
documentUri,
|
||||
cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
@@ -205,6 +223,7 @@ public static class KernelAIPluginExtensions
|
||||
OpenApiFunctionExecutionParameters? executionParameters,
|
||||
HttpClient httpClient,
|
||||
string pluginJson,
|
||||
AuthenticateRequestAsyncCallback? authCallback,
|
||||
Uri? documentUri = null,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
@@ -216,7 +235,7 @@ public static class KernelAIPluginExtensions
|
||||
|
||||
var runner = new RestApiOperationRunner(
|
||||
httpClient,
|
||||
executionParameters?.AuthCallback,
|
||||
authCallback,
|
||||
executionParameters?.UserAgent,
|
||||
executionParameters?.EnableDynamicPayload ?? false,
|
||||
executionParameters?.EnablePayloadNamespacing ?? false);
|
||||
@@ -247,15 +266,20 @@ public static class KernelAIPluginExtensions
|
||||
private static async Task<string> LoadDocumentFromUriAsync(
|
||||
IKernel kernel,
|
||||
Uri uri,
|
||||
OpenApiFunctionExecutionParameters? executionParameters,
|
||||
HttpClient httpClient,
|
||||
string? userAgent,
|
||||
AuthenticateRequestAsyncCallback? authCallback,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
using var requestMessage = new HttpRequestMessage(HttpMethod.Get, uri.ToString());
|
||||
using var request = new HttpRequestMessage(HttpMethod.Get, uri.ToString());
|
||||
request.Headers.UserAgent.Add(ProductInfoHeaderValue.Parse(userAgent ?? Telemetry.HttpUserAgent));
|
||||
|
||||
requestMessage.Headers.UserAgent.Add(ProductInfoHeaderValue.Parse(executionParameters?.UserAgent ?? Telemetry.HttpUserAgent));
|
||||
if (authCallback != null)
|
||||
{
|
||||
await authCallback(request).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
using var response = await httpClient.SendWithSuccessCheckAsync(requestMessage, cancellationToken).ConfigureAwait(false);
|
||||
using var response = await httpClient.SendWithSuccessCheckAsync(request, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
return await response.Content.ReadAsStringWithExceptionMappingAsync().ConfigureAwait(false);
|
||||
}
|
||||
@@ -322,6 +346,31 @@ public static class KernelAIPluginExtensions
|
||||
}
|
||||
}
|
||||
|
||||
private static bool TryParseAIPluginForAuth(string gptPluginJson, out OpenAIAuthenticationManifest? openApiManifestAuth)
|
||||
{
|
||||
try
|
||||
{
|
||||
var serializerSettings = new JsonSerializerSettings
|
||||
{
|
||||
ContractResolver = new DefaultContractResolver
|
||||
{
|
||||
NamingStrategy = new SnakeCaseNamingStrategy()
|
||||
}
|
||||
};
|
||||
|
||||
JObject? gptPlugin = JsonConvert.DeserializeObject<JObject>(gptPluginJson, serializerSettings);
|
||||
openApiManifestAuth = gptPlugin?["auth"]?.ToObject<OpenAIAuthenticationManifest>(JsonSerializer.Create(serializerSettings));
|
||||
|
||||
return openApiManifestAuth != null;
|
||||
}
|
||||
catch (System.Text.Json.JsonException)
|
||||
{
|
||||
openApiManifestAuth = null;
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers SKFunction for a REST API operation.
|
||||
/// </summary>
|
||||
|
||||
+9
-3
@@ -20,8 +20,14 @@ public class OpenApiFunctionExecutionParameters
|
||||
/// <summary>
|
||||
/// Callback for adding authentication data to HTTP requests.
|
||||
/// </summary>
|
||||
[Obsolete("This property will be removed in one of the next releases. Please use the AuthenticateCallbackProvider property instead.")]
|
||||
public AuthenticateRequestAsyncCallback? AuthCallback { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Provider to get authentication callback.
|
||||
/// </summary>
|
||||
public AuthenticateCallbackProvider? AuthenticateCallbackProvider { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Override for REST API operation server url.
|
||||
/// </summary>
|
||||
@@ -58,8 +64,8 @@ public class OpenApiFunctionExecutionParameters
|
||||
/// Initializes a new instance of the <see cref="OpenApiFunctionExecutionParameters"/> class.
|
||||
/// </summary>
|
||||
/// <param name="httpClient">The HttpClient to use for sending HTTP requests.</param>
|
||||
/// <param name="authCallback">The callback for adding authentication data to HTTP requests.</param>
|
||||
/// <param name="serverUrlOverride">The override for the REST API operation server URL.</param>
|
||||
/// <param name="authCallbackProvider">The provider to return callback for adding authentication data to HTTP requests.</param>
|
||||
/// <param name="userAgent">Optional user agent header value.</param>
|
||||
/// <param name="ignoreNonCompliantErrors">A flag indicating whether to ignore non-compliant errors or not
|
||||
/// If set to true, the operation execution will not throw exceptions for non-compliant documents.
|
||||
@@ -70,15 +76,15 @@ public class OpenApiFunctionExecutionParameters
|
||||
/// Namespaces prevent naming conflicts by adding the parent parameter name as a prefix, separated by dots.</param>
|
||||
public OpenApiFunctionExecutionParameters(
|
||||
HttpClient? httpClient = null,
|
||||
AuthenticateRequestAsyncCallback? authCallback = null,
|
||||
Uri? serverUrlOverride = null,
|
||||
AuthenticateCallbackProvider? authCallbackProvider = null,
|
||||
string userAgent = Telemetry.HttpUserAgent,
|
||||
bool ignoreNonCompliantErrors = false,
|
||||
bool enableDynamicOperationPayload = false,
|
||||
bool enablePayloadNamespacing = false)
|
||||
{
|
||||
this.HttpClient = httpClient;
|
||||
this.AuthCallback = authCallback;
|
||||
this.AuthenticateCallbackProvider = authCallbackProvider;
|
||||
this.ServerUrlOverride = serverUrlOverride;
|
||||
this.UserAgent = userAgent;
|
||||
this.IgnoreNonCompliantErrors = ignoreNonCompliantErrors;
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<None Remove="Grpc\protoV3.proto" />
|
||||
<None Remove="OpenAPI\TestPlugins\ai-plugin.json" />
|
||||
<None Remove="OpenAPI\TestPlugins\documentV3_1.yaml" />
|
||||
<None Remove="OpenAPI\TestPlugins\nonCompliant_documentV3_0.json" />
|
||||
<None Remove="OpenAPI\TestPlugins\openapi.json" />
|
||||
@@ -23,6 +24,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="Grpc\Protobuf\TestPlugins\protoV3.proto" />
|
||||
<EmbeddedResource Include="OpenAPI\TestPlugins\ai-plugin.json" />
|
||||
<EmbeddedResource Include="OpenAPI\TestPlugins\documentV2_0.json" />
|
||||
<EmbeddedResource Include="OpenAPI\TestPlugins\nonCompliant_documentV3_0.json" />
|
||||
<EmbeddedResource Include="OpenAPI\TestPlugins\documentV3_1.yaml" />
|
||||
|
||||
+60
-2
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
@@ -9,10 +10,13 @@ using System.Net.Mime;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.SemanticKernel;
|
||||
using Microsoft.SemanticKernel.Diagnostics;
|
||||
using Microsoft.SemanticKernel.Functions.OpenAPI.Authentication;
|
||||
using Microsoft.SemanticKernel.Functions.OpenAPI.Extensions;
|
||||
using Microsoft.SemanticKernel.Functions.OpenAPI.Model;
|
||||
using Microsoft.SemanticKernel.Functions.OpenAPI.OpenApi;
|
||||
using Microsoft.SemanticKernel.Orchestration;
|
||||
using Moq;
|
||||
using SemanticKernel.Functions.UnitTests.OpenAPI.TestPlugins;
|
||||
using Xunit;
|
||||
|
||||
@@ -47,6 +51,60 @@ public sealed class KernelAIPluginExtensionsTests : IDisposable
|
||||
this._sut = new OpenApiDocumentParser();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ItUsesAuthFromOpenAiPluginManifestWhenFetchingOpenApiSpecAsync()
|
||||
{
|
||||
//Arrange
|
||||
using var messageHandlerStub = new HttpMessageHandlerStub((HttpRequestMessage message) =>
|
||||
{
|
||||
if (message!.RequestUri!.AbsoluteUri == "https://fake-random-test-host/openai_manifest.json")
|
||||
{
|
||||
var response = new HttpResponseMessage();
|
||||
response.Content = new StreamContent(ResourcePluginsProvider.LoadFromResource("ai-plugin.json"));
|
||||
return response;
|
||||
}
|
||||
|
||||
if (message!.RequestUri!.AbsoluteUri == "https://fake-random-test-host/openapi.json")
|
||||
{
|
||||
var response = new HttpResponseMessage();
|
||||
response.Content = new StreamContent(this._openApiDocument);
|
||||
return response;
|
||||
}
|
||||
|
||||
throw new SKException($"Unexpected url - {message!.RequestUri!.AbsoluteUri}");
|
||||
});
|
||||
|
||||
using var httpClient = new HttpClient(messageHandlerStub, false);
|
||||
|
||||
var providerActualArguments = new List<OpenAIAuthenticationManifest?>();
|
||||
|
||||
var authCallbackMock = new Mock<AuthenticateRequestAsyncCallback>();
|
||||
|
||||
var authCallbackProviderMock = new Mock<AuthenticateCallbackProvider>();
|
||||
authCallbackProviderMock
|
||||
.Setup(p => p.Invoke(It.IsAny<OpenAIAuthenticationManifest>()))
|
||||
.Callback<OpenAIAuthenticationManifest>(c => providerActualArguments.Add(c))
|
||||
.Returns(authCallbackMock.Object);
|
||||
|
||||
//Act
|
||||
var plugin = await this._kernel.ImportPluginFunctionsAsync(
|
||||
"fakePlugin",
|
||||
new Uri("https://fake-random-test-host/openai_manifest.json"),
|
||||
new OpenApiFunctionExecutionParameters { HttpClient = httpClient, AuthenticateCallbackProvider = authCallbackProviderMock.Object });
|
||||
|
||||
//Assert
|
||||
var setSecretFunction = plugin["SetSecret"];
|
||||
Assert.NotNull(setSecretFunction);
|
||||
|
||||
//Check provider is called two times: first time with null when loading the OpenAI manifest document and the second time with auth config when loading the OpenAPI document.
|
||||
authCallbackProviderMock.Verify(target => target.Invoke(It.IsAny<OpenAIAuthenticationManifest>()), Times.Exactly(2));
|
||||
Assert.Equal(2, providerActualArguments.Count);
|
||||
Assert.Null(providerActualArguments[0]);
|
||||
Assert.Equal("https://vault.azure.net/.default", providerActualArguments[1]?.Scope);
|
||||
|
||||
authCallbackMock.Verify(target => target.Invoke(It.IsAny<HttpRequestMessage>()), Times.Exactly(2));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ItCanIncludeOpenApiOperationParameterTypesIntoFunctionParametersViewAsync()
|
||||
{
|
||||
@@ -203,8 +261,8 @@ public sealed class KernelAIPluginExtensionsTests : IDisposable
|
||||
public async Task ItShouldConvertPluginComplexResponseToStringToSaveItInContextAsync()
|
||||
{
|
||||
//Arrange
|
||||
using var messageHandlerStub = new HttpMessageHandlerStub();
|
||||
messageHandlerStub.ResponseToReturn.Content = new StringContent("fake-content", Encoding.UTF8, MediaTypeNames.Application.Json);
|
||||
using var content = new StringContent("fake-content", Encoding.UTF8, MediaTypeNames.Application.Json);
|
||||
using var messageHandlerStub = new HttpMessageHandlerStub(content);
|
||||
|
||||
using var httpClient = new HttpClient(messageHandlerStub, false);
|
||||
|
||||
|
||||
@@ -23,24 +23,51 @@ internal sealed class HttpMessageHandlerStub : DelegatingHandler
|
||||
|
||||
public HttpMethod? Method { get; private set; }
|
||||
|
||||
public HttpResponseMessage ResponseToReturn { get; set; }
|
||||
public Func<HttpRequestMessage, HttpResponseMessage> ResponseToReturnProvider { get; set; }
|
||||
|
||||
public HttpMessageHandlerStub()
|
||||
{
|
||||
this.ResponseToReturn = new HttpResponseMessage(System.Net.HttpStatusCode.OK);
|
||||
this.ResponseToReturn.Content = new StringContent("{}", Encoding.UTF8, MediaTypeNames.Application.Json);
|
||||
this.ResponseToReturnProvider = (_) =>
|
||||
{
|
||||
var response = new HttpResponseMessage(System.Net.HttpStatusCode.OK);
|
||||
response.Content = new StringContent("{}", Encoding.UTF8, MediaTypeNames.Application.Json);
|
||||
return response;
|
||||
};
|
||||
}
|
||||
|
||||
public HttpMessageHandlerStub(Stream responseToReturn)
|
||||
{
|
||||
this.ResponseToReturn = new HttpResponseMessage(System.Net.HttpStatusCode.OK);
|
||||
this.ResponseToReturn.Content = new StreamContent(responseToReturn);
|
||||
this.ResponseToReturnProvider = (_) =>
|
||||
{
|
||||
var response = new HttpResponseMessage(System.Net.HttpStatusCode.OK);
|
||||
response.Content = new StreamContent(responseToReturn);
|
||||
return response;
|
||||
};
|
||||
}
|
||||
|
||||
public HttpMessageHandlerStub(HttpContent content)
|
||||
{
|
||||
this.ResponseToReturnProvider = (_) =>
|
||||
{
|
||||
var response = new HttpResponseMessage(System.Net.HttpStatusCode.OK);
|
||||
response.Content = content;
|
||||
return response;
|
||||
};
|
||||
}
|
||||
|
||||
public HttpMessageHandlerStub(Func<HttpRequestMessage, HttpResponseMessage> responseToReturn)
|
||||
{
|
||||
this.ResponseToReturnProvider = responseToReturn;
|
||||
}
|
||||
|
||||
public void ResetResponse()
|
||||
{
|
||||
this.ResponseToReturn = new HttpResponseMessage(System.Net.HttpStatusCode.OK);
|
||||
this.ResponseToReturn.Content = new StringContent("{}", Encoding.UTF8, MediaTypeNames.Application.Json);
|
||||
this.ResponseToReturnProvider = (_) =>
|
||||
{
|
||||
var response = new HttpResponseMessage(System.Net.HttpStatusCode.OK);
|
||||
response.Content = new StringContent("{}", Encoding.UTF8, MediaTypeNames.Application.Json);
|
||||
return response;
|
||||
};
|
||||
}
|
||||
|
||||
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
|
||||
@@ -51,6 +78,6 @@ internal sealed class HttpMessageHandlerStub : DelegatingHandler
|
||||
this.RequestContent = request.Content == null ? null : await request.Content.ReadAsByteArrayAsync(cancellationToken);
|
||||
this.ContentHeaders = request.Content?.Headers;
|
||||
|
||||
return await Task.FromResult(this.ResponseToReturn);
|
||||
return await Task.FromResult(this.ResponseToReturnProvider(request));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"schema_version": "v1",
|
||||
"name_for_human": "AzureKeyVault",
|
||||
"name_for_model": "AzureKeyVault",
|
||||
"description_for_human": "Query and interact with Azure Key Vault",
|
||||
"description_for_model": "Query and interact with Azure Key Vault",
|
||||
"auth": {
|
||||
"type": "oauth",
|
||||
"client_url": "https://login.microsoftonline.com/common/oauth2/v2.0/authorize",
|
||||
"scope": "https://vault.azure.net/.default",
|
||||
"authorization_url": "https://login.microsoftonline.com/common/oauth2/v2.0/token",
|
||||
"authorization_content_type": "application/x-www-form-urlencoded",
|
||||
"verification_tokens": {
|
||||
"openai": "00000000000000000000000000000000"
|
||||
}
|
||||
},
|
||||
"api": {
|
||||
"type": "openapi",
|
||||
"url": "https://fake-random-test-host/openapi.json"
|
||||
},
|
||||
"logo_url": "https://contoso.com/logo.png",
|
||||
"contact_email": "[email protected]",
|
||||
"legal_info_url": "https://privacy.microsoft.com/en-US/privacystatement"
|
||||
}
|
||||
Reference in New Issue
Block a user