Skip to content

Commit

Permalink
feat(OpenAI Node): Add a node to work with OpenAI (#4932)
Browse files Browse the repository at this point in the history
* feat(OpenAI Node): Add a node to work with OpenAI

* Added codex file for OpenAi node

* Minor tweaks to Operation Image.

* Minor tweaks to Resource Text.

* Minor copy modification to Image:Create.

* Removed "a Text" in Text operations names.

* ⚡ Connect Response Format parameter and other improvements

* ✨ Add "filter" postReceiveAction

* ⚡ Rename operations and add spelling mistake again to example

* ⚡ Rename another operation

Co-authored-by: Jonathan Bennetts <jonathan.bennetts@gmail.com>
Co-authored-by: Giulio Andreini <andreini@netseven.it>
  • Loading branch information
3 people authored Dec 16, 2022
1 parent 3028ad3 commit 7a984bb
Show file tree
Hide file tree
Showing 9 changed files with 821 additions and 2 deletions.
50 changes: 50 additions & 0 deletions packages/nodes-base/credentials/OpenAiApi.credentials.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import {
IAuthenticateGeneric,
ICredentialTestRequest,
ICredentialType,
INodeProperties,
} from 'n8n-workflow';

export class OpenAiApi implements ICredentialType {
name = 'openAiApi';

displayName = 'OpenAi';

documentationUrl = 'openAiApi';

properties: INodeProperties[] = [
{
displayName: 'API Key',
name: 'apiKey',
type: 'string',
typeOptions: { password: true },
required: true,
default: '',
},
{
displayName: 'Organization ID',
name: 'organizationId',
type: 'string',
default: '',
description:
"For users who belong to multiple organizations, you can set which organization is used for an API request. Usage from these API requests will count against the specified organization's subscription quota.",
},
];

authenticate: IAuthenticateGeneric = {
type: 'generic',
properties: {
headers: {
Authorization: '=Bearer {{$credentials.apiKey}}',
'OpenAI-Organization': '={{$credentials.organizationId}}',
},
},
};

test: ICredentialTestRequest = {
request: {
baseURL: 'https://api.openai.com',
url: '/v1/models',
},
};
}
186 changes: 186 additions & 0 deletions packages/nodes-base/nodes/OpenAi/ImageDescription.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
import { INodeExecutionData, INodeProperties } from 'n8n-workflow';

export const imageOperations: INodeProperties[] = [
{
displayName: 'Operation',
name: 'operation',
type: 'options',
noDataExpression: true,
displayOptions: {
show: {
resource: ['image'],
},
},
options: [
{
name: 'Create',
value: 'create',
action: 'Create an Image',
description: 'Create an image for a given text',
routing: {
request: {
method: 'POST',
url: '/v1/images/generations',
},
},
},
],
routing: {
output: {
postReceive: [
{
type: 'rootProperty',
properties: {
property: 'data',
},
},
],
},
},
default: 'create',
},
];

const createOperations: INodeProperties[] = [
{
displayName: 'Prompt',
name: 'prompt',
type: 'string',
placeholder: 'e.g. A cute cat eating a dinosaur',
description:
'A text description of the desired image(s). The maximum length is 1000 characters.',
displayOptions: {
show: {
resource: ['image'],
operation: ['create'],
},
},
default: '',
routing: {
send: {
type: 'body',
property: 'prompt',
},
},
},
{
displayName: 'Response Format',
name: 'responseFormat',
type: 'options',
default: 'binaryData',
description: 'The format in which to return the image(s)',
displayOptions: {
show: {
resource: ['image'],
operation: ['create'],
},
},
options: [
{
name: 'Binary Data',
value: 'binaryData',
},
{
name: 'Image Url',
value: 'imageUrl',
},
],
routing: {
send: {
type: 'body',
property: 'response_format',
value: '={{ $value === "imageUrl" ? "url" : "b64_json" }}',
},
output: {
postReceive: [
async function (items: INodeExecutionData[]): Promise<INodeExecutionData[]> {
if (this.getNode().parameters.responseFormat === 'imageUrl') {
return items;
}

const result: INodeExecutionData[] = [];
for (let i = 0; i < items.length; i++) {
result.push({
json: {},
binary: {
data: await this.helpers.prepareBinaryData(
Buffer.from(items[i].json.b64_json as string, 'base64'),
'data',
),
},
} as INodeExecutionData);
}

return result;
},
],
},
},
},
{
displayName: 'Options',
name: 'options',
placeholder: 'Add Option',
description: 'Additional options to add',
type: 'collection',
default: {},
displayOptions: {
show: {
resource: ['image'],
operation: ['create'],
},
},
options: [
{
displayName: 'Number of Images',
name: 'n',
default: 1,
description: 'Number of images to generate',
type: 'number',
typeOptions: {
minValue: 1,
maxValue: 10,
},
routing: {
send: {
type: 'body',
property: 'n',
},
},
},
{
displayName: 'Resolution',
name: 'size',
type: 'options',
options: [
{
name: '256x256',
value: '256x256',
},
{
name: '512x512',
value: '512x512',
},
{
name: '1024x1024',
value: '1024x1024',
},
],
routing: {
send: {
type: 'body',
property: 'size',
},
},
default: '1024x1024',
},
],
},
];

export const imageFields: INodeProperties[] = [
/* -------------------------------------------------------------------------- */
/* image:create */
/* -------------------------------------------------------------------------- */
...createOperations,
];
19 changes: 19 additions & 0 deletions packages/nodes-base/nodes/OpenAi/OpenAi.node.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"node": "n8n-nodes-base.openAi",
"nodeVersion": "1.0",
"codexVersion": "1.0",
"categories": ["Utility"],
"resources": {
"credentialDocumentation": [
{
"url": "https://docs.n8n.io/credentials/openAiApi"
}
],
"primaryDocumentation": [
{
"url": "https://docs.n8n.io/integrations/builtin/app-nodes/n8n-nodes-base.openai/"
}
]
},
"alias": ["ChatGPT", "DallE"]
}
54 changes: 54 additions & 0 deletions packages/nodes-base/nodes/OpenAi/OpenAi.node.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { INodeType, INodeTypeDescription } from 'n8n-workflow';
import { imageFields, imageOperations } from './ImageDescription';
import { textFields, textOperations } from './TextDescription';

export class OpenAi implements INodeType {
description: INodeTypeDescription = {
displayName: 'OpenAI',
name: 'openAi',
icon: 'file:openAi.svg',
group: ['transform'],
version: 1,
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
description: 'Consume Open AI',
defaults: {
name: 'OpenAI',
},
inputs: ['main'],
outputs: ['main'],
credentials: [
{
name: 'openAiApi',
required: true,
},
],
requestDefaults: {
baseURL: 'https://api.openai.com',
},
properties: [
{
displayName: 'Resource',
name: 'resource',
type: 'options',
noDataExpression: true,
options: [
{
name: 'Image',
value: 'image',
},
{
name: 'Text',
value: 'text',
},
],
default: 'text',
},

...imageOperations,
...imageFields,

...textOperations,
...textFields,
],
};
}
Loading

0 comments on commit 7a984bb

Please sign in to comment.