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

.Net: API Manifest Plugins #4662

Closed
wants to merge 39 commits into from

Conversation

zengin
Copy link
Contributor

@zengin zengin commented Jan 17, 2024

Motivation and Context

This PR adds a prototype implementation of API Manifest based plugins.
APIManifest spec is located here. API Manifest is a format that allows defining an AI plugin from an existing OpenAPI, by specifying which subset of request paths to use in addition to other requirements such as auth. This PR contains a basic prototype covering a subset of the spec. The rest of the evolving spec will be implemented with follow up PRs and they will be shaped by the feedback we get.

Description

This PR exposes two public kernel extensions ImportPluginFromApiManifestAsync and CreatePluginFromApiManifestAsync which are similar in signature as their OpenAPI counterparts. They take APIManifest file location, read contents, slice referenced OpenAPI document based on what is described in the requests section. They then use underlying SemanticKernel OpenAPI function generation logic to generate functions and return a plugin instance.

These features are marked as experimental and give a compiler warning when used. We used SKEXP0099 as the diagnostic ID to avoid collision. We would like to hear what the right way is to increment and assign these numbers.

We also extended the function name generation logic to not rely on operationId as it is optional in OpenAPI spec. We should probably change the main Functions.OpenAPI implementation as well, but current implementation only proposes an override at the moment, where the name is generated from the path and method outside.

We also fixed a minor serialization bug where the depth was not enough to represent Graph API shape.

Lastly, we add Microsoft Graph samples in the KernelSyntaxExamples project. Same argument holds with the naming of `Example99``, i.e. to avoid collision with examples coming from other PRs.

Running the sample

In order to run the samples, you need an appsettings.Development.json file in KernelSyntaxExamples folder in this shape:

{
  "AzureOpenAI": {
    "ChatModelId": "gpt-4",
    "ServiceId": "gpt-4",
    "ChatDeploymentName": "gpt-4",
    "Endpoint": "<endpoint>",
    "ApiKey": "<endpoint>"
  },
  "MsGraph": {
    "ClientId": "<MSA-client-id>",
    "TenantId": "9188040d-6c67-4c5b-b112-36a304b66dad", // MSA/Consumer/Personal tenant,  https://learn.microsoft.com/azure/active-directory/develop/accounts-overview
    "Scopes": [
      "User.Read",
      "Files.ReadWrite",
      "Tasks.ReadWrite",
      "Mail.Send",
      "Contacts.Read",
      "Calendars.Read"
    ],
    "RedirectUri": "http://localhost"
  }
}
  • Create an MSAL app registration in the Azure portal, and add the client id to the appsettings.Development.json file.

  • Run the following command and login with your test MSA account. The sample expects at least one email message in inbox, a test.txt file in the root OneDrive folder, and at least one event in the calendar.

dotnet test --filter Example99_ApiManifest --logger "console;verbosity=detailed"

Expected output

Running Example99_ApiManifest... 
 
======== [ApiManifest Plugins] Sample 1 - Create and Execute "show the subject of my first message" Plan ======== 
======== Plugins: MessagesPlugin ======== 
======== Expected Output: latest email message subject is shown ======== 
 
>> MessagesPlugin is created. 
-------------------- 
 
Result: 
<REDACTED: email title> 
 
-------------------- 
 
======== [ApiManifest Plugins] Sample 2 - Create and Execute "get contents of file with id=test.txt" Plan ======== 
======== Plugins: DriveItemPlugin MessagesPlugin ======== 
======== Expected Output: test.txt file is fetched and the content is shown ======== 
 
>> DriveItemPlugin is created. 
>> MessagesPlugin is created. 
-------------------- 
 
Result: 
<REDACTED: contents of test.txt in the root OneDrive folder> 
 
-------------------- 
 
======== [ApiManifest Plugins] Sample 3 - Create and Execute "get contents of file with id=test.txt" Plan ======== 
======== Plugins: MessagesPlugin ======== 
======== Expected Output: test.txt file is not fetched because DriveItemPlugin is not loaded ======== 
 
>> MessagesPlugin is created. 
-------------------- 
 
Result: 
The provided functions do not include a method for retrieving the contents of a file. The goal of getting the contents of a file with the ID 'test.txt' cannot be achieved with the current set of functions. 
 
-------------------- 
 
======== [ApiManifest Plugins] Sample 4 - Create and Execute "tell me how many contacts I have" Plan ======== 
======== Plugins: MessagesPlugin ContactsPlugin ======== 
======== Expected Output: number of contacts is shown ======== 
 
>> MessagesPlugin is created. 
>> ContactsPlugin is created. 
-------------------- 
 
Result: 
You have 0 contacts. 
 
-------------------- 
 
======== [ApiManifest Plugins] Sample 5 - Create and Execute "tell me title of first event in my calendar" Plan ======== 
======== Plugins: MessagesPlugin CalendarPlugin ======== 
======== Expected Output: title of first event from calendar is shown if exists ======== 
 
>> MessagesPlugin is created. 
>> CalendarPlugin is created. 
-------------------- 
 
Result: 
API Manifest Event 
 
-------------------- 
 
== DONE == 

Contribution Checklist

@shawncal shawncal added .NET Issue or Pull requests regarding .NET code kernel.core labels Jan 17, 2024
@github-actions github-actions bot changed the title API Manifest Plugins .Net: API Manifest Plugins Jan 17, 2024
@zengin zengin marked this pull request as ready for review January 17, 2024 22:47
@zengin zengin requested a review from a team as a code owner January 17, 2024 22:47
dotnet/Directory.Packages.props Outdated Show resolved Hide resolved
kernel.LoggerFactory.CreateLogger(typeof(OpenApiKernelExtensions)) ?? NullLogger.Instance,
httpClient,
authCallback: null,
HttpHeaderValues.UserAgent,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should be possible to override the user agent if needed. See the example here -

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The type name OpenApiFunctionExecutionParameters suggests that these are parameters to be used while calling the plugin functions (i.e. executing them). For example, we can't use the authCallback override from execution parameters in the line above because it is intended for the REST API call, not for fetching the OpenAPI document. Wouldn't the parameters be overloaded if used at the time of import? If the intention is to use for both, please let me know.

@SergeyMenshykh
Copy link
Member

SergeyMenshykh commented Jan 23, 2024

Motivation and Context

This PR adds a prototype implementation of API Manifest based plugins. APIManifest spec is located here. API Manifest is a format that allows defining an AI plugin from an existing OpenAPI, by specifying which subset of request paths to use in addition to other requirements such as auth. This PR contains a basic prototype covering a subset of the spec. The rest of the evolving spec will be implemented with follow up PRs and they will be shaped by the feedback we get.

@zengin Could you please describe the scenarios in which this API Manifest functionality will be used?

@zengin
Copy link
Contributor Author

zengin commented Jan 24, 2024

@zengin Could you please describe the scenarios in which this API Manifest functionality will be used?

Sure.

The biggest advantage of using API Manifest over an entire OpenAPI document currently is when an API contains too many operations such as Microsoft Graph (thousands of operations), where you only need a slice of it as a plugin.

Another developer's API may not be as big, but they may want to limit operations of the plugin to read-only endpoints (or any other subset criterion that makes sense).

API Manifest also allows adding more than one OpenAPI reference, which a scenario specific plugin may require (e.g. read GitHub issues and add them as a task into the todo list).

Given that API Manifest is under development and is more flexible in terms of the format, we will be able to add features required to describe a plugin, which may not necessarily be part of the OpenAPI specification. Additional decorations it contains are similar but not limited to OpenAI's plugin manifest or Microsoft Copilot's plugin manifest.

Eventually OpenAPI spec may support all these additional decorations to power AI scenarios (as pointed out here), in that case, plugin manifests may be considered an interim solution, but at least in the short term, they are going to be around to complement what's missing in the OpenAPI spec to guide LLMs on how to consume the APIs.

zengin and others added 2 commits January 24, 2024 10:16
Co-authored-by: Peter Ombwa <peter.ombwa@microsoft.com>
@markwallace-microsoft
Copy link
Member

@zengin We are going to close this PR and move forward with a separate package for this functionality.

github-merge-queue bot pushed a commit that referenced this pull request Feb 9, 2024
### Motivation and Context
- fixes #4851

### Description

Adds an empty project to host OpenAPI plugin extensions e.g. API
Manifest plugins (related PR: #4662)

### Contribution Checklist

<!-- Before submitting this PR, please make sure: -->

- [x] The code builds clean without any errors or warnings
- [x] The PR follows the [SK Contribution
Guidelines](https://github.com/microsoft/semantic-kernel/blob/main/CONTRIBUTING.md)
and the [pre-submission formatting
script](https://github.com/microsoft/semantic-kernel/blob/main/CONTRIBUTING.md#development-scripts)
raises no violations
- [x] All unit tests pass, and I have added new tests where possible
- [x] I didn't break anyone 😄

---------

Co-authored-by: SergeyMenshykh <68852919+SergeyMenshykh@users.noreply.github.com>
Co-authored-by: Mark Wallace <127216156+markwallace-microsoft@users.noreply.github.com>
@zengin zengin mentioned this pull request Feb 12, 2024
4 tasks
eavanvalkenburg pushed a commit to eavanvalkenburg/semantic-kernel that referenced this pull request Feb 12, 2024
### Motivation and Context
- fixes microsoft#4851

### Description

Adds an empty project to host OpenAPI plugin extensions e.g. API
Manifest plugins (related PR: microsoft#4662)

### Contribution Checklist

<!-- Before submitting this PR, please make sure: -->

- [x] The code builds clean without any errors or warnings
- [x] The PR follows the [SK Contribution
Guidelines](https://github.com/microsoft/semantic-kernel/blob/main/CONTRIBUTING.md)
and the [pre-submission formatting
script](https://github.com/microsoft/semantic-kernel/blob/main/CONTRIBUTING.md#development-scripts)
raises no violations
- [x] All unit tests pass, and I have added new tests where possible
- [x] I didn't break anyone 😄

---------

Co-authored-by: SergeyMenshykh <68852919+SergeyMenshykh@users.noreply.github.com>
Co-authored-by: Mark Wallace <127216156+markwallace-microsoft@users.noreply.github.com>
github-merge-queue bot pushed a commit that referenced this pull request Feb 15, 2024
### Motivation and Context

This PR contains main implementation part of #4662 and adds it into
`Functions.OpenApi.Extensions` project. There were also couple of bug
fixes in the mentioned PR, those will be handled in subsequent PRs.

API Manifest can describe capabilities of an AI plugin for a chat-based
system among other things. For a full description of API Manifest, see
the [API Manifest
Specification](https://darrelmiller.github.io/api-manifest/draft-miller-api-manifest.html).

API Manifest refers to one or more OpenAPI documents as API dependencies
and it selects a subset of operations from those documents to describe
the capabilities of an AI plugin.

### Description

Semantic Kernel has a way of creating plugins from OpenAPI operations,
but it has a couple of limitations that can be addressed with API
Manifest:
1. It can only create functions from OpenAPI operations that are
described in a single OpenAPI document.
2. It cannot reference multiple OpenAPI documents in a single plugin.

Not being able to slice OpenAPI operations is a major limitation,
especially for large APIs such as Microsoft Graph, which has over 1000
operations.

Today, we can create a Semantic Kernel plugin from an OpenAPI file and
one OpenAPI file corresponds to one plugin. We don't have a way of
selecting a subset of operations.

```mermaid
graph TB

subgraph OpenAPI1
operation11
operation12
operation13
end

subgraph OpenAPI2
operation21
operation22
operation23
end

subgraph OpenAPIPlugin1
function11
function12
function13
end

subgraph OpenAPIPlugin2
function21
function22
function23
end

operation11 -.- function11
operation12 -.- function12
operation13 -.- function13

operation21 -.- function21
operation22 -.- function22
operation23 -.- function23

style OpenAPIPlugin1 fill:blue,color:white
style OpenAPIPlugin2 fill:blue,color:white

```

### Proposed Solution
After Semantic Kernel APIManifest support, we will have an APIManifest
file, which will be used as a filter on OpenAPI files and it can
reference more than one OpenAPI file. The filter can be used as a guide
to select only certain operations to be converted into functions.

Assume that we want to create a single plugin with `function11` from
`OpenAPI1` and `function21` and `function22` from `OpenAPI2`. We want to
preserve the one-to-one mapping between the APIManifest and the plugin,
e.g. between `ApiManifestX` and `PluginX`:

```mermaid
graph TB

subgraph OpenAPI1
operation11
operation12
operation13
end

subgraph OpenAPI2
operation21
operation22
operation23
end

subgraph APIManifestX
reference11
reference21
reference22
end

subgraph PluginX
function11
function21
function22
end

reference11 -.- operation11
reference21 -.- operation21
reference22 -.- operation22

function11 -.- reference11
function21 -.- reference21
function22 -.- reference22

style APIManifestX fill:green,color:white
style PluginX fill:blue,color:white

```

If we use existing OpenAPI to Plugin public methods after on-the-fly
slicing, we can only achieve the following because there is an
assumption that a single OpenAPI is converted into a single plugin,
which breaks APIManifest to Plugin one-to-one mapping. A single
`APIManifestX` file corresponds to two plugins, namely, `PluginX1` and
`PluginX2`:

```mermaid
graph TB

subgraph OpenAPI1
operation11
operation12
operation13
end

subgraph OpenAPI2
operation21
operation22
operation23
end

subgraph APIManifestX
reference11
reference21
reference22
end

subgraph OpenAPI1_Sliced
operation11_sliced[operation11]
end

subgraph OpenAPI2_Sliced
operation21_sliced[operation21]
operation22_sliced[operation22]
end

subgraph PluginX1
function11
end

subgraph PluginX2
function21
function22
end

reference11 -.- operation11
reference21 -.- operation21
reference22 -.- operation22

operation11 -.- operation11_sliced -.- function11
operation21 -.- operation21_sliced -.- function21
operation22 -.- operation22_sliced -.- function22

style APIManifestX fill:green,color:white
style PluginX1 fill:blue,color:white
style PluginX2 fill:blue,color:white

```

To avoid this, the implementation of regular OpenAPI plugin generation
and API Manifest plugin generation meets at the function generation
level, so reuse starts at `CreateRestApiFunction`.

The PR also adds a KernelSyntaxExample for `/me/messages` endpoint of
Microsoft Graph.

### Contribution Checklist

<!-- Before submitting this PR, please make sure: -->

- [x] The code builds clean without any errors or warnings
- [x] The PR follows the [SK Contribution
Guidelines](https://github.com/microsoft/semantic-kernel/blob/main/CONTRIBUTING.md)
and the [pre-submission formatting
script](https://github.com/microsoft/semantic-kernel/blob/main/CONTRIBUTING.md#development-scripts)
raises no violations
- [x] All unit tests pass, and I have added new tests where possible
- [x] I didn't break anyone 😄

---------

Co-authored-by: SergeyMenshykh <68852919+SergeyMenshykh@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
kernel.core .NET Issue or Pull requests regarding .NET code
Projects
Archived in project
Development

Successfully merging this pull request may close these issues.

None yet

6 participants