Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mitigate support for OpenAPI 3.1 #354

Merged
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Next Next commit
Workaround to handle OpenAPI document version 3.1 by downgrading them…
… to 3.0.1 by rewriting the document version.
  • Loading branch information
SergeyMenshykh committed Apr 6, 2023
commit 8eb725e2b8265aff7327cd6d0e4ff88545794db8
Original file line number Diff line number Diff line change
Expand Up @@ -412,8 +412,8 @@ public async Task<bool> DoesCollectionExistAsync(string collectionName, Cancella
}
else
{
return false;
this._log.LogError("Collection fetch failed: {0}, {1}", response.StatusCode, responseContent);
return false;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ public async Task CollectionsCanBeDeletedAsync()
// Arrange
SqliteMemoryStore db = await SqliteMemoryStore.ConnectAsync(DatabaseFile);
var collections = await db.GetCollectionsAsync().ToListAsync();
int numCollections = collections.Count();
int numCollections = collections.Count;
Assert.True(numCollections > 0);

// Act
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using System.Net.Http;
using System.Resources;
using System.Text.Json.Nodes;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Microsoft.SemanticKernel.Diagnostics;
Expand All @@ -28,9 +29,10 @@ public static class KernelChatGptPluginExtensions
/// <param name="url">Url to in which to retrieve the ChatGPT plugin.</param>
/// <param name="httpClient">Optional HttpClient to use for the request.</param>
/// <param name="authCallback">Optional callback for adding auth data to the API requests.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>A list of all the semantic functions representing the skill.</returns>
public static async Task<IDictionary<string, ISKFunction>> ImportChatGptPluginSkillFromUrlAsync(
this IKernel kernel, string skillName, Uri url, HttpClient? httpClient = null, AuthenticateRequestAsyncCallback? authCallback = null)
this IKernel kernel, string skillName, Uri url, HttpClient? httpClient = null, AuthenticateRequestAsyncCallback? authCallback = null, CancellationToken cancellationToken = default)
{
Verify.ValidSkillName(skillName);

Expand All @@ -47,18 +49,18 @@ public static async Task<IDictionary<string, ISKFunction>> ImportChatGptPluginSk
//using HttpClient client = new HttpClient(retryHandler, false);
using HttpClient client = new HttpClient();

response = await client.GetAsync(url);
response = await client.GetAsync(url, cancellationToken);
}
else
{
response = await httpClient.GetAsync(url);
response = await httpClient.GetAsync(url, cancellationToken);
}
response.EnsureSuccessStatusCode();

string gptPluginJson = await response.Content.ReadAsStringAsync();
string? openApiUrl = ParseOpenApiUrl(gptPluginJson);

return await kernel.ImportOpenApiSkillFromUrlAsync(skillName, new Uri(openApiUrl), httpClient, authCallback);
return await kernel.ImportOpenApiSkillFromUrlAsync(skillName, new Uri(openApiUrl), httpClient, authCallback, cancellationToken);
}
finally
{
Expand All @@ -76,9 +78,13 @@ public static async Task<IDictionary<string, ISKFunction>> ImportChatGptPluginSk
/// <param name="skillName">Skill name.</param>
/// <param name="httpClient">Optional HttpClient to use for the request.</param>
/// <param name="authCallback">Optional callback for adding auth data to the API requests.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>A list of all the semantic functions representing the skill.</returns>
public static async Task<IDictionary<string, ISKFunction>> ImportChatGptPluginSkillFromResourceAsync(
this IKernel kernel, string skillName, HttpClient? httpClient = null, AuthenticateRequestAsyncCallback? authCallback = null)
this IKernel kernel,
string skillName, HttpClient? httpClient = null,
AuthenticateRequestAsyncCallback? authCallback = null,
CancellationToken cancellationToken = default)
{
Verify.ValidSkillName(skillName);

Expand All @@ -97,7 +103,7 @@ public static async Task<IDictionary<string, ISKFunction>> ImportChatGptPluginSk

string? openApiUrl = ParseOpenApiUrl(gptPluginJson);

return await kernel.ImportOpenApiSkillFromUrlAsync(skillName, new Uri(openApiUrl), httpClient, authCallback);
return await kernel.ImportOpenApiSkillFromUrlAsync(skillName, new Uri(openApiUrl), httpClient, authCallback, cancellationToken);
}

/// <summary>
Expand All @@ -108,9 +114,15 @@ public static async Task<IDictionary<string, ISKFunction>> ImportChatGptPluginSk
/// <param name="skillDirectoryName">Name of the directory containing the selected skill.</param>
/// <param name="httpClient">Optional HttpClient to use for the request.</param>
/// <param name="authCallback">Optional callback for adding auth data to the API requests.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>A list of all the semantic functions representing the skill.</returns>
public static IDictionary<string, ISKFunction> ImportChatGptPluginSkillSkillFromDirectory(
this IKernel kernel, string parentDirectory, string skillDirectoryName, HttpClient? httpClient = null, AuthenticateRequestAsyncCallback? authCallback = null)
public static async Task<IDictionary<string, ISKFunction>> ImportChatGptPluginSkillSkillFromDirectoryAsync(
this IKernel kernel,
string parentDirectory,
string skillDirectoryName,
HttpClient? httpClient = null,
AuthenticateRequestAsyncCallback? authCallback = null,
CancellationToken cancellationToken = default)
{
const string CHATGPT_PLUGIN_FILE = "ai-plugin.json";

Expand All @@ -130,7 +142,7 @@ public static IDictionary<string, ISKFunction> ImportChatGptPluginSkillSkillFrom

using var stream = File.OpenRead(chatGptPluginPath);

return kernel.RegisterOpenApiSkill(stream, skillDirectoryName, authCallback);
return await kernel.RegisterOpenApiSkillAsync(stream, skillDirectoryName, authCallback, cancellationToken);
}

/// <summary>
Expand All @@ -141,9 +153,15 @@ public static IDictionary<string, ISKFunction> ImportChatGptPluginSkillSkillFrom
/// <param name="filePath">File path to the ChatGPT plugin definition.</param>
/// <param name="httpClient">Optional HttpClient to use for the request.</param>
/// <param name="authCallback">Optional callback for adding auth data to the API requests.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>A list of all the semantic functions representing the skill.</returns>
public static IDictionary<string, ISKFunction> ImportChatGptPluginSkillSkillFromFile(
this IKernel kernel, string skillName, string filePath, HttpClient? httpClient = null, AuthenticateRequestAsyncCallback? authCallback = null)
public static async Task<IDictionary<string, ISKFunction>> ImportChatGptPluginSkillSkillFromFileAsync(
this IKernel kernel,
string skillName,
string filePath,
HttpClient? httpClient = null,
AuthenticateRequestAsyncCallback? authCallback = null,
CancellationToken cancellationToken = default)
{
if (!File.Exists(filePath))
{
Expand All @@ -155,7 +173,7 @@ public static IDictionary<string, ISKFunction> ImportChatGptPluginSkillSkillFrom

using var stream = File.OpenRead(filePath);

return kernel.RegisterOpenApiSkill(stream, skillName, authCallback);
return await kernel.RegisterOpenApiSkillAsync(stream, skillName, authCallback, cancellationToken);
}

private static string ParseOpenApiUrl(string gptPluginJson)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using System.Linq;
using System.Net.Http;
using System.Resources;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Microsoft.SemanticKernel.Diagnostics;
Expand All @@ -32,9 +33,15 @@ public static class KernelOpenApiExtensions
/// <param name="url">Url to in which to retrieve the OpenAPI definition.</param>
/// <param name="httpClient">Optional HttpClient to use for the request.</param>
/// <param name="authCallback">Optional callback for adding auth data to the API requests.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>A list of all the semantic functions representing the skill.</returns>
public static async Task<IDictionary<string, ISKFunction>> ImportOpenApiSkillFromUrlAsync(
this IKernel kernel, string skillName, Uri url, HttpClient? httpClient = null, AuthenticateRequestAsyncCallback? authCallback = null)
this IKernel kernel,
string skillName,
Uri url,
HttpClient? httpClient = null,
AuthenticateRequestAsyncCallback? authCallback = null,
CancellationToken cancellationToken = default)
{
Verify.ValidSkillName(skillName);

Expand All @@ -51,11 +58,11 @@ public static async Task<IDictionary<string, ISKFunction>> ImportOpenApiSkillFro
//using HttpClient client = new HttpClient(retryHandler, false);
using HttpClient client = new HttpClient();

response = await client.GetAsync(url);
response = await client.GetAsync(url, cancellationToken);
}
else
{
response = await httpClient.GetAsync(url);
response = await httpClient.GetAsync(url, cancellationToken);
}
response.EnsureSuccessStatusCode();

Expand All @@ -65,7 +72,7 @@ public static async Task<IDictionary<string, ISKFunction>> ImportOpenApiSkillFro
throw new MissingManifestResourceException($"Unable to load OpenApi skill from url '{url}'.");
}

return kernel.RegisterOpenApiSkill(stream, skillName, authCallback);
return await kernel.RegisterOpenApiSkillAsync(stream, skillName, authCallback, cancellationToken);
lemillermicrosoft marked this conversation as resolved.
Show resolved Hide resolved
}
finally
{
Expand All @@ -82,8 +89,9 @@ public static async Task<IDictionary<string, ISKFunction>> ImportOpenApiSkillFro
/// <param name="kernel">Semantic Kernel instance.</param>
/// <param name="skillName">Skill name.</param>
/// <param name="authCallback">Optional callback for adding auth data to the API requests.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>A list of all the semantic functions representing the skill.</returns>
public static IDictionary<string, ISKFunction> ImportOpenApiSkillFromResource(this IKernel kernel, string skillName, AuthenticateRequestAsyncCallback? authCallback = null)
public static async Task<IDictionary<string, ISKFunction>> ImportOpenApiSkillFromResourceAsync(this IKernel kernel, string skillName, AuthenticateRequestAsyncCallback? authCallback = null, CancellationToken cancellationToken = default)
{
Verify.ValidSkillName(skillName);

Expand All @@ -97,7 +105,7 @@ public static IDictionary<string, ISKFunction> ImportOpenApiSkillFromResource(th
throw new MissingManifestResourceException($"Unable to load OpenApi skill from assembly resource '{resourceName}'.");
}

return kernel.RegisterOpenApiSkill(stream, skillName, authCallback);
return await kernel.RegisterOpenApiSkillAsync(stream, skillName, authCallback, cancellationToken);
}

/// <summary>
Expand All @@ -107,9 +115,14 @@ public static IDictionary<string, ISKFunction> ImportOpenApiSkillFromResource(th
/// <param name="parentDirectory">Directory containing the skill directory.</param>
/// <param name="skillDirectoryName">Name of the directory containing the selected skill.</param>
/// <param name="authCallback">Optional callback for adding auth data to the API requests.</param>
/// <param name="cancellationToken"></param>
/// <returns>A list of all the semantic functions representing the skill.</returns>
public static IDictionary<string, ISKFunction> ImportOpenApiSkillFromDirectory(
this IKernel kernel, string parentDirectory, string skillDirectoryName, AuthenticateRequestAsyncCallback? authCallback = null)
public static async Task<IDictionary<string, ISKFunction>> ImportOpenApiSkillFromDirectoryAsync(
this IKernel kernel,
string parentDirectory,
string skillDirectoryName,
AuthenticateRequestAsyncCallback? authCallback = null,
CancellationToken cancellationToken = default)
{
const string OPENAPI_FILE = "openapi.json";

Expand All @@ -129,7 +142,7 @@ public static IDictionary<string, ISKFunction> ImportOpenApiSkillFromDirectory(

using var stream = File.OpenRead(openApiDocumentPath);

return kernel.RegisterOpenApiSkill(stream, skillDirectoryName, authCallback);
return await kernel.RegisterOpenApiSkillAsync(stream, skillDirectoryName, authCallback, cancellationToken);
}

/// <summary>
Expand All @@ -139,8 +152,9 @@ public static IDictionary<string, ISKFunction> ImportOpenApiSkillFromDirectory(
/// <param name="skillName">Name of the skill to register.</param>
/// <param name="filePath">File path to the OpenAPI document.</param>
/// <param name="authCallback">Optional callback for adding auth data to the API requests.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>A list of all the semantic functions representing the skill.</returns>
public static IDictionary<string, ISKFunction> ImportOpenApiSkillFromFile(this IKernel kernel, string skillName, string filePath, AuthenticateRequestAsyncCallback? authCallback = null)
public static async Task<IDictionary<string, ISKFunction>> ImportOpenApiSkillFromFileAsync(this IKernel kernel, string skillName, string filePath, AuthenticateRequestAsyncCallback? authCallback = null, CancellationToken cancellationToken = default)
{
if (!File.Exists(filePath))
{
Expand All @@ -152,7 +166,7 @@ public static IDictionary<string, ISKFunction> ImportOpenApiSkillFromFile(this I

using var stream = File.OpenRead(filePath);

return kernel.RegisterOpenApiSkill(stream, skillName, authCallback);
return await kernel.RegisterOpenApiSkillAsync(stream, skillName, authCallback, cancellationToken);
}

/// <summary>
Expand All @@ -162,16 +176,17 @@ public static IDictionary<string, ISKFunction> ImportOpenApiSkillFromFile(this I
/// <param name="documentStream">OpenApi document stream.</param>
/// <param name="skillName">Skill name.</param>
/// <param name="authCallback">Optional callback for adding auth data to the API requests.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>A list of all the semantic functions representing the skill.</returns>
public static IDictionary<string, ISKFunction> RegisterOpenApiSkill(this IKernel kernel, Stream documentStream, string skillName, AuthenticateRequestAsyncCallback? authCallback = null)
public static async Task<IDictionary<string, ISKFunction>> RegisterOpenApiSkillAsync(this IKernel kernel, Stream documentStream, string skillName, AuthenticateRequestAsyncCallback? authCallback = null, CancellationToken cancellationToken = default)
{
Verify.NotNull(kernel, nameof(kernel));
Verify.ValidSkillName(skillName);

//Parse
var parser = new OpenApiDocumentParser();

var operations = parser.Parse(documentStream);
var operations = await parser.ParseAsync(documentStream, cancellationToken);

var skill = new Dictionary<string, ISKFunction>();

Expand All @@ -180,7 +195,7 @@ public static IDictionary<string, ISKFunction> RegisterOpenApiSkill(this IKernel
try
{
kernel.Log.LogTrace("Registering Rest function {0}.{1}.", skillName, operation.Id);
var function = kernel.RegisterRestApiFunction(skillName, operation, authCallback);
var function = kernel.RegisterRestApiFunction(skillName, operation, authCallback, cancellationToken);
skill[function.Name] = function;
}
catch (Exception ex) when (!ex.IsCriticalException())
Expand All @@ -202,8 +217,9 @@ public static IDictionary<string, ISKFunction> RegisterOpenApiSkill(this IKernel
/// <param name="skillName">Skill name.</param>
/// <param name="operation">The REST API operation.</param>
/// <param name="authCallback">Optional callback for adding auth data to the API requests.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>An instance of <see cref="SKFunction"/> class.</returns>
private static ISKFunction RegisterRestApiFunction(this IKernel kernel, string skillName, RestApiOperation operation, AuthenticateRequestAsyncCallback? authCallback = null)
private static ISKFunction RegisterRestApiFunction(this IKernel kernel, string skillName, RestApiOperation operation, AuthenticateRequestAsyncCallback? authCallback = null, CancellationToken cancellationToken = default)
{
var restOperationParameters = operation.GetParameters();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

using System.Collections.Generic;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.SemanticKernel.Skills.OpenAPI.Model;

namespace Microsoft.SemanticKernel.Skills.OpenAPI.OpenApi;
Expand All @@ -15,6 +17,7 @@ internal interface IOpenApiDocumentParser
/// Parses OpenAPI document.
/// </summary>
/// <param name="stream">Stream containing OpenAPI document to parse.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>List of rest operations.</returns>
IList<RestApiOperation> Parse(Stream stream);
Task<IList<RestApiOperation>> ParseAsync(Stream stream, CancellationToken cancellationToken);
}
Loading