chatgpt automation block
This commit is contained in:
parent
3e78989e74
commit
84f52683b2
|
@ -99,6 +99,7 @@
|
||||||
"mysql2": "2.3.3",
|
"mysql2": "2.3.3",
|
||||||
"node-fetch": "2.6.7",
|
"node-fetch": "2.6.7",
|
||||||
"open": "8.4.0",
|
"open": "8.4.0",
|
||||||
|
"openai": "^3.2.1",
|
||||||
"pg": "8.5.1",
|
"pg": "8.5.1",
|
||||||
"posthog-node": "1.3.0",
|
"posthog-node": "1.3.0",
|
||||||
"pouchdb": "7.3.0",
|
"pouchdb": "7.3.0",
|
||||||
|
|
|
@ -14,6 +14,7 @@ import * as filter from "./steps/filter"
|
||||||
import * as delay from "./steps/delay"
|
import * as delay from "./steps/delay"
|
||||||
import * as queryRow from "./steps/queryRows"
|
import * as queryRow from "./steps/queryRows"
|
||||||
import * as loop from "./steps/loop"
|
import * as loop from "./steps/loop"
|
||||||
|
import * as openai from "./steps/openai"
|
||||||
import env from "../environment"
|
import env from "../environment"
|
||||||
import {
|
import {
|
||||||
AutomationStepSchema,
|
AutomationStepSchema,
|
||||||
|
@ -39,6 +40,7 @@ const ACTION_IMPLS: Record<
|
||||||
DELAY: delay.run,
|
DELAY: delay.run,
|
||||||
FILTER: filter.run,
|
FILTER: filter.run,
|
||||||
QUERY_ROWS: queryRow.run,
|
QUERY_ROWS: queryRow.run,
|
||||||
|
OPEN_AI: openai.run,
|
||||||
// these used to be lowercase step IDs, maintain for backwards compat
|
// these used to be lowercase step IDs, maintain for backwards compat
|
||||||
discord: discord.run,
|
discord: discord.run,
|
||||||
slack: slack.run,
|
slack: slack.run,
|
||||||
|
@ -59,6 +61,7 @@ export const BUILTIN_ACTION_DEFINITIONS: Record<string, AutomationStepSchema> =
|
||||||
FILTER: filter.definition,
|
FILTER: filter.definition,
|
||||||
QUERY_ROWS: queryRow.definition,
|
QUERY_ROWS: queryRow.definition,
|
||||||
LOOP: loop.definition,
|
LOOP: loop.definition,
|
||||||
|
OPEN_AI: openai.definition,
|
||||||
// these used to be lowercase step IDs, maintain for backwards compat
|
// these used to be lowercase step IDs, maintain for backwards compat
|
||||||
discord: discord.definition,
|
discord: discord.definition,
|
||||||
slack: slack.definition,
|
slack: slack.definition,
|
||||||
|
|
|
@ -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),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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()
|
||||||
|
});
|
||||||
|
})
|
|
@ -72,6 +72,7 @@ const environment = {
|
||||||
BB_ADMIN_USER_EMAIL: process.env.BB_ADMIN_USER_EMAIL,
|
BB_ADMIN_USER_EMAIL: process.env.BB_ADMIN_USER_EMAIL,
|
||||||
BB_ADMIN_USER_PASSWORD: process.env.BB_ADMIN_USER_PASSWORD,
|
BB_ADMIN_USER_PASSWORD: process.env.BB_ADMIN_USER_PASSWORD,
|
||||||
PLUGINS_DIR: process.env.PLUGINS_DIR || "/plugins",
|
PLUGINS_DIR: process.env.PLUGINS_DIR || "/plugins",
|
||||||
|
OPENAI_API_KEY: process.env.OPENAI_API_KEY,
|
||||||
// flags
|
// flags
|
||||||
ALLOW_DEV_AUTOMATIONS: process.env.ALLOW_DEV_AUTOMATIONS,
|
ALLOW_DEV_AUTOMATIONS: process.env.ALLOW_DEV_AUTOMATIONS,
|
||||||
DISABLE_THREADING: process.env.DISABLE_THREADING,
|
DISABLE_THREADING: process.env.DISABLE_THREADING,
|
||||||
|
|
|
@ -56,6 +56,7 @@ export enum AutomationActionStepId {
|
||||||
FILTER = "FILTER",
|
FILTER = "FILTER",
|
||||||
QUERY_ROWS = "QUERY_ROWS",
|
QUERY_ROWS = "QUERY_ROWS",
|
||||||
LOOP = "LOOP",
|
LOOP = "LOOP",
|
||||||
|
OPEN_AI = "OPEN_AI",
|
||||||
// these used to be lowercase step IDs, maintain for backwards compat
|
// these used to be lowercase step IDs, maintain for backwards compat
|
||||||
discord = "discord",
|
discord = "discord",
|
||||||
slack = "slack",
|
slack = "slack",
|
||||||
|
|
20
yarn.lock
20
yarn.lock
|
@ -1486,15 +1486,15 @@
|
||||||
pouchdb-promise "^6.0.4"
|
pouchdb-promise "^6.0.4"
|
||||||
through2 "^2.0.0"
|
through2 "^2.0.0"
|
||||||
|
|
||||||
"@budibase/pro@2.5.6-alpha.29":
|
"@budibase/pro@2.5.6-alpha.30":
|
||||||
version "2.5.6-alpha.29"
|
version "2.5.6-alpha.30"
|
||||||
resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-2.5.6-alpha.29.tgz#71414f68a296535ef53ffb0453352ea137c4aeab"
|
resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-2.5.6-alpha.30.tgz#9b8089a983fd61a062f31a8e5757d7bb5b56fb8c"
|
||||||
integrity sha512-tQuzMOo2WFxKvsUgYAfUEcLabRpmAD7hPlhBhCFzYasaXNbJiPhcwv4i52US0i0Wr2IXMb2X0d7fwa8tnbKzIA==
|
integrity sha512-YTyjMHK/wsSOFJkON7a5WRJSgAr8Gh/cflRzifm6Jw1Gb8S8B8Z6uTWW/S7+psVBRGeUfV1s8biYNr71tXz2Ng==
|
||||||
dependencies:
|
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/shared-core" "2.4.44-alpha.1"
|
||||||
"@budibase/string-templates" "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"
|
"@koa/router" "8.0.8"
|
||||||
bull "4.10.1"
|
bull "4.10.1"
|
||||||
joi "17.6.0"
|
joi "17.6.0"
|
||||||
|
@ -18358,6 +18358,14 @@ open@^8.4.0:
|
||||||
is-docker "^2.1.1"
|
is-docker "^2.1.1"
|
||||||
is-wsl "^2.2.0"
|
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:
|
openapi-response-validator@^9.2.0:
|
||||||
version "9.3.1"
|
version "9.3.1"
|
||||||
resolved "https://registry.yarnpkg.com/openapi-response-validator/-/openapi-response-validator-9.3.1.tgz#54284d8be608ef53283cbe7448accce8106b1c56"
|
resolved "https://registry.yarnpkg.com/openapi-response-validator/-/openapi-response-validator-9.3.1.tgz#54284d8be608ef53283cbe7448accce8106b1c56"
|
||||||
|
|
Loading…
Reference in New Issue