1
0
Fork 0
mirror of synced 2024-07-03 21:40:55 +12:00

chatgpt automation block

This commit is contained in:
Martin McKeaveney 2023-04-26 15:55:44 +01:00
parent 3e78989e74
commit 84f52683b2
7 changed files with 209 additions and 6 deletions

View file

@ -99,6 +99,7 @@
"mysql2": "2.3.3",
"node-fetch": "2.6.7",
"open": "8.4.0",
"openai": "^3.2.1",
"pg": "8.5.1",
"posthog-node": "1.3.0",
"pouchdb": "7.3.0",

View file

@ -14,6 +14,7 @@ import * as filter from "./steps/filter"
import * as delay from "./steps/delay"
import * as queryRow from "./steps/queryRows"
import * as loop from "./steps/loop"
import * as openai from "./steps/openai"
import env from "../environment"
import {
AutomationStepSchema,
@ -39,6 +40,7 @@ const ACTION_IMPLS: Record<
DELAY: delay.run,
FILTER: filter.run,
QUERY_ROWS: queryRow.run,
OPEN_AI: openai.run,
// these used to be lowercase step IDs, maintain for backwards compat
discord: discord.run,
slack: slack.run,
@ -59,6 +61,7 @@ export const BUILTIN_ACTION_DEFINITIONS: Record<string, AutomationStepSchema> =
FILTER: filter.definition,
QUERY_ROWS: queryRow.definition,
LOOP: loop.definition,
OPEN_AI: openai.definition,
// these used to be lowercase step IDs, maintain for backwards compat
discord: discord.definition,
slack: slack.definition,

View file

@ -0,0 +1,104 @@
import { Configuration, OpenAIApi } from "openai";
import {
AutomationActionStepId,
AutomationStepSchema,
AutomationStepInput,
AutomationStepType,
AutomationIOType,
} from "@budibase/types"
import * as automationUtils from "../automationUtils"
import environment from "../../environment";
enum Model {
GPT_35_TURBO = "gpt-3.5-turbo",
// will only work with api keys that have access to the GPT4 API
// GPT_4 = "gpt-4",
}
export const definition: AutomationStepSchema = {
name: "OpenAI",
tagline: "Send prompts to ChatGPT",
icon: "Algorithm",
description: "Interact with the OpenAI ChatGPT API.",
type: AutomationStepType.ACTION,
internal: true,
stepId: AutomationActionStepId.OPEN_AI,
inputs: {
prompt: "",
},
schema: {
inputs: {
properties: {
prompt: {
type: AutomationIOType.STRING,
title: "Prompt",
},
model: {
type: AutomationIOType.STRING,
title: "Model",
enum: Object.values(Model),
},
},
required: ["prompt", "model"],
},
outputs: {
properties: {
success: {
type: AutomationIOType.BOOLEAN,
description: "Whether the action was successful",
},
response: {
type: AutomationIOType.STRING,
description: "What was output",
},
},
required: ["success", "response"],
},
},
}
export async function run({ inputs, context }: AutomationStepInput) {
if (!environment.OPENAI_API_KEY) {
return {
success: false,
response: "OpenAI API Key not configured - please add the OPENAI_API_KEY environment variable.",
}
}
if (inputs.prompt == null) {
return {
success: false,
response: "Budibase OpenAI Automation Failed: No prompt supplied",
}
}
try {
const configuration = new Configuration({
apiKey: environment.OPENAI_API_KEY,
});
const openai = new OpenAIApi(configuration);
const completion = await openai.createChatCompletion({
model: inputs.model,
messages: [
{
role: "user",
content: inputs.prompt
}
],
});
let response = completion?.data?.choices[0]?.message?.content
return {
response,
success: true,
}
} catch (err) {
return {
success: false,
response: automationUtils.getError(err),
}
}
}

View file

@ -0,0 +1,85 @@
const setup = require("./utilities")
import environment from "../../environment";
import openai from "openai"
jest.mock("openai", jest.fn(() => ({
Configuration: jest.fn(),
OpenAIApi: jest.fn(() => ({
createChatCompletion: jest.fn(() => ({
data: {
choices: [
{
message: {
content: "This is a test"
},
}
]
}
}))
}))
})))
const OPENAI_PROMPT = "What is the meaning of life?"
describe("test the openai action", () => {
let config = setup.getConfig()
beforeAll(async () => {
await config.init()
})
beforeEach(() => {
environment.OPENAI_API_KEY = "abc123"
})
afterAll(setup.afterAll)
it("should present the correct error message when the OPENAI_API_KEY variable isn't set", async () => {
delete environment.OPENAI_API_KEY
let res = await setup.runStep("OPEN_AI",
{
prompt: OPENAI_PROMPT
}
)
expect(res.response).toEqual("OpenAI API Key not configured - please add the OPENAI_API_KEY environment variable.")
expect(res.success).toBeFalsy()
})
it("should be able to receive a response from ChatGPT given a prompt", async () => {
const res = await setup.runStep("OPEN_AI",
{
prompt: OPENAI_PROMPT
}
)
expect(res.response).toEqual("This is a test")
expect(res.success).toBeTruthy()
})
it("should present the correct error message when a prompt is not provided", async () => {
const res = await setup.runStep("OPEN_AI",
{
prompt: null
}
)
expect(res.response).toEqual("Budibase OpenAI Automation Failed: No prompt supplied")
expect(res.success).toBeFalsy()
})
it("should present the correct error message when an error is thrown from the createChatCompletion call", async () => {
openai.OpenAIApi.mockImplementation(() => ({
createChatCompletion: jest.fn(() => {
throw new Error("An error occurred while calling createChatCompletion");
}),
}));
const res = await setup.runStep("OPEN_AI", {
prompt: OPENAI_PROMPT,
});
expect(res.response).toEqual("Error: An error occurred while calling createChatCompletion")
expect(res.success).toBeFalsy()
});
})

View file

@ -72,6 +72,7 @@ const environment = {
BB_ADMIN_USER_EMAIL: process.env.BB_ADMIN_USER_EMAIL,
BB_ADMIN_USER_PASSWORD: process.env.BB_ADMIN_USER_PASSWORD,
PLUGINS_DIR: process.env.PLUGINS_DIR || "/plugins",
OPENAI_API_KEY: process.env.OPENAI_API_KEY,
// flags
ALLOW_DEV_AUTOMATIONS: process.env.ALLOW_DEV_AUTOMATIONS,
DISABLE_THREADING: process.env.DISABLE_THREADING,

View file

@ -56,6 +56,7 @@ export enum AutomationActionStepId {
FILTER = "FILTER",
QUERY_ROWS = "QUERY_ROWS",
LOOP = "LOOP",
OPEN_AI = "OPEN_AI",
// these used to be lowercase step IDs, maintain for backwards compat
discord = "discord",
slack = "slack",

View file

@ -1486,15 +1486,15 @@
pouchdb-promise "^6.0.4"
through2 "^2.0.0"
"@budibase/pro@2.5.6-alpha.29":
version "2.5.6-alpha.29"
resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-2.5.6-alpha.29.tgz#71414f68a296535ef53ffb0453352ea137c4aeab"
integrity sha512-tQuzMOo2WFxKvsUgYAfUEcLabRpmAD7hPlhBhCFzYasaXNbJiPhcwv4i52US0i0Wr2IXMb2X0d7fwa8tnbKzIA==
"@budibase/pro@2.5.6-alpha.30":
version "2.5.6-alpha.30"
resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-2.5.6-alpha.30.tgz#9b8089a983fd61a062f31a8e5757d7bb5b56fb8c"
integrity sha512-YTyjMHK/wsSOFJkON7a5WRJSgAr8Gh/cflRzifm6Jw1Gb8S8B8Z6uTWW/S7+psVBRGeUfV1s8biYNr71tXz2Ng==
dependencies:
"@budibase/backend-core" "2.5.6-alpha.29"
"@budibase/backend-core" "2.5.6-alpha.30"
"@budibase/shared-core" "2.4.44-alpha.1"
"@budibase/string-templates" "2.4.44-alpha.1"
"@budibase/types" "2.5.6-alpha.29"
"@budibase/types" "2.5.6-alpha.30"
"@koa/router" "8.0.8"
bull "4.10.1"
joi "17.6.0"
@ -18358,6 +18358,14 @@ open@^8.4.0:
is-docker "^2.1.1"
is-wsl "^2.2.0"
openai@^3.2.1:
version "3.2.1"
resolved "https://registry.yarnpkg.com/openai/-/openai-3.2.1.tgz#1fa35bdf979cbde8453b43f2dd3a7d401ee40866"
integrity sha512-762C9BNlJPbjjlWZi4WYK9iM2tAVAv0uUp1UmI34vb0CN5T2mjB/qM6RYBmNKMh/dN9fC+bxqPwWJZUTWW052A==
dependencies:
axios "^0.26.0"
form-data "^4.0.0"
openapi-response-validator@^9.2.0:
version "9.3.1"
resolved "https://registry.yarnpkg.com/openapi-response-validator/-/openapi-response-validator-9.3.1.tgz#54284d8be608ef53283cbe7448accce8106b1c56"