Skip to content

Commit

Permalink
Merge pull request #4636 from kishanprmr/openai-extract-data
Browse files Browse the repository at this point in the history
feat(openai): Extract Data from Text Action
  • Loading branch information
abuaboud committed May 8, 2024
2 parents acfc6c4 + 22d0ae1 commit 38ff241
Show file tree
Hide file tree
Showing 3 changed files with 222 additions and 60 deletions.
2 changes: 1 addition & 1 deletion packages/pieces/community/openai/package.json
@@ -1,4 +1,4 @@
{
"name": "@activepieces/piece-openai",
"version": "0.3.22"
"version": "0.3.23"
}
127 changes: 68 additions & 59 deletions packages/pieces/community/openai/src/index.ts
@@ -1,13 +1,10 @@
import {
AuthenticationType,
HttpMethod,
createCustomApiCallAction,
httpClient,
AuthenticationType,
HttpMethod,
createCustomApiCallAction,
httpClient,
} from '@activepieces/pieces-common';
import {
PieceAuth,
createPiece,
} from '@activepieces/pieces-framework';
import { PieceAuth, createPiece } from '@activepieces/pieces-framework';
import { PieceCategory } from '@activepieces/shared';
import { askAssistant } from './lib/actions/ask-assistant';
import { generateImage } from './lib/actions/generate-image';
Expand All @@ -17,6 +14,7 @@ import { transcribeAction } from './lib/actions/transcriptions';
import { translateAction } from './lib/actions/translation';
import { visionPrompt } from './lib/actions/vision-prompt';
import { baseUrl } from './lib/common/common';
import { extractStructuredDataAction } from './lib/actions/extract-structure-data.action';

const markdownDescription = `
Follow these instructions to get your OpenAI API Key:
Expand All @@ -28,58 +26,69 @@ It is strongly recommended that you add your credit card information to your Ope
`;

export const openaiAuth = PieceAuth.SecretText({
description: markdownDescription,
displayName: 'API Key',
required: true,
validate: async (auth) => {
try {
await httpClient.sendRequest<{
data: { id: string }[];
}>({
url: `${baseUrl}/models`,
method: HttpMethod.GET,
authentication: {
type: AuthenticationType.BEARER_TOKEN,
token: auth.auth as string,
},
});
return {
valid: true,
};
} catch (e) {
return {
valid: false,
error: 'Invalid API key',
};
}
},
description: markdownDescription,
displayName: 'API Key',
required: true,
validate: async (auth) => {
try {
await httpClient.sendRequest<{
data: { id: string }[];
}>({
url: `${baseUrl}/models`,
method: HttpMethod.GET,
authentication: {
type: AuthenticationType.BEARER_TOKEN,
token: auth.auth as string,
},
});
return {
valid: true,
};
} catch (e) {
return {
valid: false,
error: 'Invalid API key',
};
}
},
});

export const openai = createPiece({
displayName: 'OpenAI',
description: 'Use the many tools ChatGPT has to offer.',
minimumSupportedRelease: '0.5.0',
logoUrl: 'https://cdn.activepieces.com/pieces/openai.png',
categories: [PieceCategory.ARTIFICIAL_INTELLIGENCE],
auth: openaiAuth,
actions: [
askOpenAI,
askAssistant,
generateImage,
visionPrompt,
textToSpeech,
transcribeAction,
translateAction,
createCustomApiCallAction({
auth: openaiAuth,
baseUrl: () => baseUrl,
authMapping: (auth) => {
return {
Authorization: `Bearer ${auth}`,
};
},
}),
],
authors: ["aboudzein","astorozhevsky","Willianwg","Nilesh","Salem-Alaa","kishanprmr","MoShizzle","khaledmashaly","abuaboud"],
triggers: [],
displayName: 'OpenAI',
description: 'Use the many tools ChatGPT has to offer.',
minimumSupportedRelease: '0.5.0',
logoUrl: 'https://cdn.activepieces.com/pieces/openai.png',
categories: [PieceCategory.ARTIFICIAL_INTELLIGENCE],
auth: openaiAuth,
actions: [
askOpenAI,
askAssistant,
generateImage,
visionPrompt,
textToSpeech,
transcribeAction,
translateAction,
extractStructuredDataAction,
createCustomApiCallAction({
auth: openaiAuth,
baseUrl: () => baseUrl,
authMapping: (auth) => {
return {
Authorization: `Bearer ${auth}`,
};
},
}),
],
authors: [
'aboudzein',
'astorozhevsky',
'Willianwg',
'Nilesh',
'Salem-Alaa',
'kishanprmr',
'MoShizzle',
'khaledmashaly',
'abuaboud',
],
triggers: [],
});
@@ -0,0 +1,153 @@
import { openaiAuth } from '../../';
import { createAction, Property } from '@activepieces/pieces-framework';
import OpenAI from 'openai';
import { notLLMs } from '../common/common';

export const extractStructuredDataAction = createAction({
auth: openaiAuth,
name: 'extract-structured-data',
displayName: 'Extract Structured Data from Text',
description: 'Returns structured data from provided unstructured text.',
props: {
model: Property.Dropdown({
displayName: 'Model',
required: true,
refreshers: [],
defaultValue: 'gpt-3.5-turbo',
options: async ({ auth }) => {
if (!auth) {
return {
disabled: true,
placeholder: 'Enter your API key first',
options: [],
};
}
try {
const openai = new OpenAI({
apiKey: auth as string,
});
const response = await openai.models.list();
// We need to get only LLM models
const models = response.data.filter((model) => !notLLMs.includes(model.id));
return {
disabled: false,
options: models.map((model) => {
return {
label: model.id,
value: model.id,
};
}),
};
} catch (error) {
return {
disabled: true,
options: [],
placeholder: "Couldn't load models, API key is invalid",
};
}
},
}),
text: Property.LongText({
displayName: 'Unstructured Text',
required: true,
}),
prompt: Property.LongText({
displayName: 'Prompt',
description:
'Provide a brief description of what sort of data you want extracted from the unstructured text.',
required: true,
}),
params: Property.Array({
displayName: 'Structured Data Definition',
required: true,
properties: {
propName: Property.ShortText({
displayName: 'Name',
description:
'Provide the name of the values you want to extract from the unstructured text. Name should be unique and short. ',
required: true,
}),
propDescription: Property.LongText({
displayName: 'Description',
description:
'Brief description of the parameter, defining what data will be extracted to this parameter.',
required: false,
}),
propDataType: Property.StaticDropdown({
displayName: 'Data Type',
description: 'Type of parameter.',
required: true,
defaultValue: 'string',
options: {
disabled: false,
options: [
{ label: 'Text', value: 'string' },
{ label: 'Number', value: 'number' },
{ label: 'Boolean', value: 'boolean' },
],
},
}),
propIsRequired: Property.Checkbox({
displayName: 'Is Property Required?',
description: 'If the property must be present, the action will fail if it is not found.',
required: true,
defaultValue: true,
}),
},
}),
},
async run(context) {
const { model, text, prompt } = context.propsValue;
const paramInputArray = context.propsValue.params as ParamInput[];
const functionParams: Record<string, unknown> = {};
const requiredFunctionParams: string[] = [];
for (const param of paramInputArray) {
functionParams[param.propName] = {
type: param.propDataType,
description: param.propDescription,
};
if (param.propIsRequired) {
requiredFunctionParams.push(param.propName);
}
}

const openai = new OpenAI({
apiKey: context.auth,
});

const response = await openai.chat.completions.create({
model: model,
messages: [{ role: 'user', content: text }],
tools: [
{
type: 'function',
function: {
name: 'extract_structured_data',
description: prompt,
parameters: {
type: 'object',
properties: functionParams,
required: requiredFunctionParams,
},
},
},
],
});

const toolCallsResponse = response.choices[0].message.tool_calls;
if (toolCallsResponse) {
return JSON.parse(toolCallsResponse[0].function.arguments);
} else {
throw Error(JSON.stringify({
message: 'Unable to extract data. Please provide valid params and text.'
}));
}
},
});

interface ParamInput {
propName: string;
propDescription: string;
propDataType: string;
propIsRequired: boolean;
}

0 comments on commit 38ff241

Please sign in to comment.