Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ fetch("http://localhost:5555/api/run", {
});
```

Configure the `server/pages/api/api2ai.config.ts` file to add your own APIs. Follow the existing template in this file. You may add as many files as you want.
Configure the [server/pages/api/api2ai.config.ts] file to add your own APIs. Follow the existing template in this file. You may add as many files as you want.

## OpenAPI Spec

Expand Down
2 changes: 1 addition & 1 deletion core/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ fetch("http://localhost:5555/api/run", {
});
```

Configure the `server/pages/api/api2ai.config.ts` file to add your own APIs. Follow the existing template in this file. You may add as many files as you want.
Configure the [server/pages/api/api2ai.config.ts] file to add your own APIs. Follow the existing template in this file. You may add as many files as you want.

## OpenAPI Spec

Expand Down
197 changes: 197 additions & 0 deletions core/fixtures/postman/openai.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
{
"info": {
"_postman_id": "90abb798-cb85-43cb-ba3a-ae7941e968da",
"name": "OpenAI",
"description": "[The OpenAI API](https://beta.openai.com/docs/introduction/overview) can be applied to virtually any task that involves understanding or generating natural language or code. We offer a spectrum of [models](https://beta.openai.com/docs/models) with different levels of power suitable for different tasks, as well as the ability to [fine-tune](https://beta.openai.com/docs/guides/fine-tuning) your own custom models. These models can be used for everything from content generation to semantic search and classification.",
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json",
"_exporter_id": "28827358",
"_collection_link": "https://www.postman.com/devrel/workspace/openai/collection/13183464-90abb798-cb85-43cb-ba3a-ae7941e968da?action=share&creator=28827358&source=collection_link"
},
"item": [
{
"name": "Chat",
"item": [
{
"name": "Create chat message completion",
"event": [
{
"listen": "test",
"script": {
"exec": [
"var jsonData = null;",
"",
"try {",
" jsonData = pm.response.json();",
" pm.test(\"Check if response is valid JSON\", function() { ",
" pm.expect(jsonData).to.be.an('object');",
" pm.visualizer.set(`<textarea style=\"width: 100vw; height: 100vh; font-family: monospace;\">${pm.response.json().choices[0].message.content}</textarea>`);",
"});",
"} catch (a) {",
" console.log(\"Dealing with an SSE stream ... \")",
"}"
],
"type": "text/javascript"
}
}
],
"request": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json"
},
{
"key": "Accept",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"model\": \"gpt-3.5-turbo\",\n \"messages\": [\n {\n \"role\": \"user\",\n \"content\": \"Who won the world series in 2020?\"\n },\n {\n \"role\": \"assistant\",\n \"content\": \"The Los Angeles Dodgers won the World Series in 2020.\"\n },\n {\n \"role\": \"user\",\n \"content\": \"Where was it played?\"\n }\n ],\n \"temperature\": 1,\n \"top_p\": 1,\n \"n\": 1,\n \"stream\": false,\n \"max_tokens\": 250,\n \"presence_penalty\": 0,\n \"frequency_penalty\": 0\n}",
"options": {
"raw": {
"language": "json"
}
}
},
"url": {
"raw": "{{baseUrl}}/chat/completions",
"host": [
"{{baseUrl}}"
],
"path": [
"chat",
"completions"
]
}
},
"response": [
{
"name": "Create chat message completion",
"originalRequest": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json"
},
{
"key": "Accept",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"model\": \"gpt-3.5-turbo\",\n \"messages\": [\n {\n \"role\": \"user\",\n \"content\": \"Who won the world series in 2020?\"\n },\n {\n \"role\": \"assistant\",\n \"content\": \"The Los Angeles Dodgers won the World Series in 2020.\"\n },\n {\n \"role\": \"user\",\n \"content\": \"Where was it played?\"\n }\n ],\n \"temperature\": 1,\n \"top_p\": 1,\n \"n\": 1,\n \"stream\": false,\n \"max_tokens\": 250,\n \"presence_penalty\": 0,\n \"frequency_penalty\": 0\n}",
"options": {
"raw": {
"language": "json"
}
}
},
"url": {
"raw": "{{baseUrl}}/chat/completions",
"host": [
"{{baseUrl}}"
],
"path": [
"chat",
"completions"
]
}
},
"status": "OK",
"code": 200,
"_postman_previewlanguage": "json",
"header": [
{
"key": "Date",
"value": "Mon, 20 Mar 2023 23:50:57 GMT"
},
{
"key": "Content-Type",
"value": "application/json"
},
{
"key": "Content-Length",
"value": "360"
},
{
"key": "Connection",
"value": "keep-alive"
},
{
"key": "Access-Control-Allow-Origin",
"value": "*"
},
{
"key": "Cache-Control",
"value": "no-cache, must-revalidate"
},
{
"key": "Openai-Model",
"value": "gpt-3.5-turbo-0301"
},
{
"key": "Openai-Organization",
"value": "user-ydfxhof885ycvb52zjw5ly51"
},
{
"key": "Openai-Processing-Ms",
"value": "1800"
},
{
"key": "Openai-Version",
"value": "2020-10-01"
},
{
"key": "Strict-Transport-Security",
"value": "max-age=15724800; includeSubDomains"
},
{
"key": "X-Ratelimit-Limit-Requests",
"value": "3500"
},
{
"key": "X-Ratelimit-Remaining-Requests",
"value": "3499"
},
{
"key": "X-Ratelimit-Reset-Requests",
"value": "17ms"
},
{
"key": "X-Request-Id",
"value": "8506a913c39e706fede784458b254ca7"
}
],
"cookie": [],
"body": "{\n \"id\": \"chatcmpl-6wJZ9Ebt8puAO4zW3zUpgFwS8BfJB\",\n \"object\": \"chat.completion\",\n \"created\": 1679356255,\n \"model\": \"gpt-3.5-turbo-0301\",\n \"usage\": {\n \"prompt_tokens\": 45,\n \"completion_tokens\": 19,\n \"total_tokens\": 64\n },\n \"choices\": [\n {\n \"message\": {\n \"role\": \"assistant\",\n \"content\": \"The 2020 World Series was played in Arlington, Texas, at Globe Life Field.\"\n },\n \"finish_reason\": \"stop\",\n \"index\": 0\n }\n ]\n}"
}
]
}
]
}
],
"auth": {
"type": "bearer",
"bearer": [
{
"key": "token",
"value": "{{token}}",
"type": "string"
}
]
},
"variable": [
{
"key": "baseUrl",
"value": "https://api.openai.com/v1"
},
{
"key": "token",
"value": ""
}
]
}
1 change: 1 addition & 0 deletions core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
},
"dependencies": {
"openai": "^3.3.0",
"postman-to-openapi": "3.0.1",
"swagger-parser": "^10.0.3"
}
}
32 changes: 28 additions & 4 deletions core/src/api/__tests__/oas-loader.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@ import path from "path";
import { parse } from "../oas-loader";

describe("#parse", () => {
const filename: string = path.join(
__dirname,
"../../../fixtures/oases/petstore.yaml"
);
let filename: string;

beforeEach(() => {
filename = path.join(__dirname, "../../../fixtures/oases/petstore.yaml");
});

test("parsing open api spec into operations", async () => {
const operations = await parse({ filename });
Expand Down Expand Up @@ -82,4 +83,27 @@ describe("#parse", () => {
});
});
});

describe("when file is a postman collection", () => {
beforeEach(() => {
filename = path.join(__dirname, "../../../fixtures/postman/openai.json");
});

test("converts and parses file as OAS", async () => {
const auth = { token: "sk-secret" };

const operations = await parse({ filename, auth });

expect(operations.map((o) => o.summary())).toEqual([
"Create chat message completion",
]);

expect(operations[0].operationId()).toEqual(
"createChatMessageCompletion"
);
expect(operations[0].securities.length).toEqual(1);
expect(operations[0].securities[0].type).toEqual("http");
expect(operations[0].securities[0].scheme).toEqual("bearer");
});
});
});
40 changes: 39 additions & 1 deletion core/src/api/oas-loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ import SwaggerParser from "@apidevtools/swagger-parser";
import Operation from "./operation";
import Security from "./security";

import postmanToOpenApi from "postman-to-openapi";

import { promises as fs } from "fs";

const parseSecurities = (api: any) => {
if (!api.components?.securitySchemes) {
console.warn("No `securitySchemes` found in this API spec.");
Expand Down Expand Up @@ -42,14 +46,48 @@ const selectSecurities = ({
});
};

const parseJSON = async (filename: string) => {
const rawText: string = await fs.readFile(filename, "utf8");
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Currently only supports local file, need remote file support to be consistent with SwaggerParser


try {
return JSON.parse(rawText);
} catch {
return null;
}
};

const isPostmanCollection = async (filename: string) => {
// Detect and parse postman collection into OAS or default to SwaggerParser.
const json = await parseJSON(filename);

return !!json?.info?._postman_id;
};

const parseAPI = async (filename: string) => {
const isPostman = await isPostmanCollection(filename);

if (isPostman) {
const oas = await postmanToOpenApi(filename, undefined, {
replaceVars: true,
outputFormat: "json",
operationId: "auto",
defaultTag: "General",
});

return JSON.parse(oas);
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Types inference can be done here, so we're not totally blocked by postman-to-openapi. However, there's still no way to infer required from the collection.

} else {
return await SwaggerParser.dereference(filename);
}
};

export const parse = async ({
filename,
auth,
}: {
filename: string;
auth?: object;
}) => {
const api = await SwaggerParser.dereference(filename);
const api = await parseAPI(filename);
const securities = parseSecurities(api);

const operations: Operation[] = [];
Expand Down
File renamed without changes.
Loading