πŸ’¬ #ChatGPT Plugin in NET – Map route handler for manifest file: ai-plugin.json

El Bruno - Jul 6 '23 - - Dev Community

Hi!

The ChatGPT Plugin in NET is a fun side pet project. And hey, my goal is to share this with a fully functional Codespace, so everyone can test this without a local dev environment.

Note: If you want to learn more about Codespaces, check the official documentation here.

Plugin File Manifest

Let’s review the official documentation about plugin file manifest:

Every plugin requires a ai-plugin.json file, which needs to be hosted on the API’s domain. For example, a company called example.com would make the plugin JSON file accessible via an https://example.com domain since that is where their API is hosted. When you install the plugin via the ChatGPT UI, on the backend we look for a file located at /.well-known/ai-plugin.json. The /.well-known folder is required and must exist on your domain in order for ChatGPT to connect with your plugin. If there is no file found, the plugin cannot be installed. For local development, you can use HTTP but if you are pointing to a remote server, HTTPS is required.

Super easy, however, we have a challenge with a single ai-plugin.json file that serves both; dev on local and dev on Codespaces.

Dynamic Plugin File Manifest for Local and Codespaces dev environments

There are several ways to solve this problem. I like this one:

use route handlers for the ai-plugin.json request

Reading the Minimal API doc, the Route Handlers in Minimal API apps section, I decided to give a try to a route handler that handles the ai-plugin.json request. In this request

We will get the current host for the API. Can be local dev, Codespaces dev or Production.

We will read the ai-plugin.json file and replace the $host variable with the current host value.

The handler will return the ai-plugin.json with the correct values.

This is the final code for this:


// publish the plugin manifest information, update the host with the current one
app.MapGet("/.well-known/ai-plugin.json", (HttpRequest request) =>
{
    // get current url from request headers (codespaces dev) or app Urls (local dev)
    var userAgent = request.Headers.UserAgent;
    var customHeader = request. Headers["x-custom-header"];
    var currentUrl = request. Headers["x-forwarded-proto"] + "://" + request.Headers["x-forwarded-host"];
    if (currentUrl == "://")
        currentUrl = app.Urls.First();

    // update current host in manifest
    string aiPlugInManifest = File.ReadAllText("pluginInfo/ai-plugin.json");
    aiPlugInManifest = aiPlugInManifest.Replace("$host", currentUrl);
    return Results.Json(aiPlugInManifest);
})
.WithName(".well-known/ai-plugin.json")
.WithOpenApi(generatedOperation =>
{
    generatedOperation.Description = "Gets the plugin manifest information.";
    return generatedOperation;
});

Enter fullscreen mode Exit fullscreen mode

And this is a sample of the ai-plugin.json file:


{
  "schema_version": "v1",
  "name_for_human": "El Bruno Pet Search",
  "name_for_model": "elbrunopets",
  "description_for_human": "Search through El Bruno's wide range of pets and pets' owners.",
  "description_for_model": "Plugin for searching through El Bruno's pets. Use it whenever a user asks about pets or pets'owners.",
  "auth": {
    "type": "none"
  },
  "api": {
    "type": "openapi",
    "url": "$host/swagger/v1/swagger.yaml",
    "is_user_authenticated": false
  },
  "logo_url": "$host/pluginInfo/logo.png",
  "contact_email": "support@example.com",
  "legal_info_url": "http://www.example.com/legal"
}

Enter fullscreen mode Exit fullscreen mode

Now, once the api is running, we can see the Swagger definition including this GET action.

And this is a sample of the expected output in localhost mode:

And in codespaces mode

This is super cool. I’ll continue updating the repo with the demo instructions, and a live session will be scheduled soon!

Happy coding!

Greetings

El Bruno

More posts in my blog ElBruno.com.

More info in https://beacons.ai/elbruno


. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Terabox Video Player