@razzium/piece-aimwork-backend 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,254 @@
1
+ import {
2
+ createAction,
3
+ Property,
4
+ PieceAuth,
5
+ StoreScope,
6
+ } from '@activepieces/pieces-framework';
7
+ import OpenAI from 'openai';
8
+ import { openaiAuth } from '../..';
9
+ import {
10
+ baseUrl,
11
+ calculateMessagesTokenSize,
12
+ exceedsHistoryLimit,
13
+ notLLMs,
14
+ reduceContextSize,
15
+ } from '../common/common';
16
+ import { z } from 'zod';
17
+ import { propsValidation } from '@activepieces/pieces-common';
18
+
19
+ export const askOpenAI = createAction({
20
+ auth: openaiAuth,
21
+ name: 'ask_aimw_model',
22
+ displayName: 'Ask aimw Model',
23
+ description: 'Ask to any aimw Models',
24
+ props: {
25
+ model: Property.Dropdown({
26
+ displayName: 'Model',
27
+ required: true,
28
+ description:
29
+ 'The model which will generate the completion. Some models are suitable for natural language tasks, others specialize in code.',
30
+ refreshers: [],
31
+ defaultValue: 'gpt-3.5-turbo',
32
+ options: async ({ auth }) => {
33
+ // if (!auth) {
34
+ // return {
35
+ // disabled: true,
36
+ // placeholder: 'Enter your API key first',
37
+ // options: [],
38
+ // };
39
+ // }
40
+ try {
41
+ const openai = new OpenAI({
42
+ //apiKey: auth as string,
43
+ apiKey: auth as string,
44
+ baseURL: baseUrl
45
+ });
46
+ const response = await openai.models.list();
47
+
48
+ // We need to get only LLM models
49
+ const models = response.data.filter(
50
+ (model) => !notLLMs.includes(model.id)
51
+ );
52
+ return {
53
+ disabled: false,
54
+ options: models.map((model) => {
55
+ return {
56
+ label: model.id,
57
+ value: model.id,
58
+ };
59
+ }),
60
+ };
61
+ } catch (error) {
62
+ return {
63
+ disabled: true,
64
+ options: [],
65
+ placeholder: "Couldn't load models, API key is invalid",
66
+ };
67
+ }
68
+ },
69
+ }),
70
+ prompt: Property.LongText({
71
+ displayName: 'Prompt',
72
+ required: true,
73
+ }),
74
+ // V1
75
+ // image: Property.File({
76
+ // displayName: 'Image',
77
+ // description: "The image URL or file you want model's vision to read.",
78
+ // required: false,
79
+ // }),
80
+ image: Property.LongText({
81
+ displayName: 'Image (Base64 => use Piece Base64 if needed)',
82
+ description: "The image URL or file you want model's vision to read.",
83
+ required: false,
84
+ }),
85
+ // temperature: Property.Number({
86
+ // displayName: 'Temperature',
87
+ // required: false,
88
+ // description:
89
+ // 'Controls randomness: Lowering results in less random completions. As the temperature approaches zero, the model will become deterministic and repetitive.',
90
+ // defaultValue: 0.9,
91
+ // }),
92
+ maxTokens: Property.Number({
93
+ displayName: 'Maximum Tokens',
94
+ required: true,
95
+ description:
96
+ "The maximum number of tokens to generate. Requests can use up to 2,048 or 4,096 tokens shared between prompt and completion depending on the model. Don't set the value to maximum and leave some tokens for the input. (One token is roughly 4 characters for normal English text)",
97
+ defaultValue: 2048,
98
+ }),
99
+ // topP: Property.Number({
100
+ // displayName: 'Top P',
101
+ // required: false,
102
+ // description:
103
+ // 'An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered.',
104
+ // defaultValue: 1,
105
+ // }),
106
+ // frequencyPenalty: Property.Number({
107
+ // displayName: 'Frequency penalty',
108
+ // required: false,
109
+ // description:
110
+ // "Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim.",
111
+ // defaultValue: 0,
112
+ // }),
113
+ // presencePenalty: Property.Number({
114
+ // displayName: 'Presence penalty',
115
+ // required: false,
116
+ // description:
117
+ // "Number between -2.0 and 2.0. Positive values penalize new tokens based on whether they appear in the text so far, increasing the mode's likelihood to talk about new topics.",
118
+ // defaultValue: 0.6,
119
+ // }),
120
+ memoryKey: Property.ShortText({
121
+ displayName: 'Memory Key',
122
+ description:
123
+ 'A memory key that will keep the chat history shared across runs and flows. Keep it empty to leave ChatGPT without memory of previous messages.',
124
+ required: false,
125
+ }),
126
+ systemPrompt: Property.LongText({
127
+ displayName: 'Additional system prompt',
128
+ required: false,
129
+ description: 'Add more informations to model system prompt',
130
+
131
+ }),
132
+ // roles: Property.Json({
133
+ // displayName: 'Roles',
134
+ // required: false,
135
+ // description: 'Array of roles to specify more accurate response',
136
+ // defaultValue: [
137
+ // { role: 'system', content: 'You are a helpful assistant.' },
138
+ // ],
139
+ // }),
140
+ },
141
+ async run({ auth, propsValue, store }) {
142
+ await propsValidation.validateZod(propsValue, {
143
+ //temperature: z.number().min(0).max(1).optional(),
144
+ memoryKey: z.string().max(128).optional(),
145
+ });
146
+ const openai = new OpenAI({
147
+ //apiKey: auth,
148
+ apiKey: auth,
149
+ baseURL: baseUrl
150
+ });
151
+ const {
152
+ model,
153
+ // temperature,
154
+ maxTokens,
155
+ // topP,
156
+ // frequencyPenalty,
157
+ // presencePenalty,
158
+ image,
159
+ systemPrompt,
160
+ prompt,
161
+ memoryKey,
162
+ } = propsValue;
163
+
164
+ let messageHistory: any[] | null = [];
165
+ // Add system prompt to message history
166
+ if (systemPrompt) {
167
+ messageHistory.push({
168
+ role: 'system',
169
+ content: systemPrompt,
170
+ });
171
+ }
172
+
173
+ // If memory key is set, retrieve messages stored in history
174
+ if (memoryKey) {
175
+ messageHistory = (await store.get(memoryKey, StoreScope.PROJECT)) ?? [];
176
+ }
177
+
178
+ // Add user prompt to message history
179
+ // V1 if (image && image.extension && image.base64) {
180
+ if (image) {
181
+ messageHistory.push({
182
+ role: 'user',
183
+ content: [
184
+ {
185
+ type: 'text',
186
+ text: prompt,
187
+ },
188
+ {
189
+ type: 'image_url',
190
+ image_url: {
191
+ // V1 url: `data:image/${image.extension};base64,${image.base64}`,
192
+ url: image,
193
+ },
194
+ },
195
+ ],
196
+ });
197
+ }
198
+ else {
199
+ messageHistory.push({
200
+ role: 'user',
201
+ content: prompt,
202
+ });
203
+ }
204
+
205
+ // Add system instructions if set by user
206
+ // const rolesArray = propsValue.roles ? (propsValue.roles as any) : [];
207
+ // const roles = rolesArray.map((item: any) => {
208
+ // const rolesEnum = ['system', 'user', 'assistant'];
209
+ // if (!rolesEnum.includes(item.role)) {
210
+ // throw new Error(
211
+ // 'The only available roles are: [system, user, assistant]'
212
+ // );
213
+ // }
214
+
215
+ // return {
216
+ // role: item.role,
217
+ // content: item.content,
218
+ // };
219
+ // });
220
+
221
+ // Send prompt
222
+ const completion = await openai.chat.completions.create({
223
+ model: model,
224
+ messages: [...messageHistory],
225
+ // messages: [...roles, ...messageHistory],
226
+ // temperature: temperature,
227
+ // top_p: topP,
228
+ // frequency_penalty: frequencyPenalty,
229
+ // presence_penalty: presencePenalty,
230
+ // max_completion_tokens: maxTokens,
231
+ });
232
+
233
+ // Add response to message history
234
+ messageHistory = [...messageHistory, completion.choices[0].message];
235
+
236
+ // Check message history token size
237
+ // System limit is 32K tokens, we can probably make it bigger but this is a safe spot
238
+ const tokenLength = await calculateMessagesTokenSize(messageHistory, model);
239
+ if (memoryKey) {
240
+ // If tokens exceed 90% system limit or 90% of model limit - maxTokens, reduce history token size
241
+ if (exceedsHistoryLimit(tokenLength, model, maxTokens)) {
242
+ messageHistory = await reduceContextSize(
243
+ messageHistory,
244
+ model,
245
+ maxTokens
246
+ );
247
+ }
248
+ // Store history
249
+ await store.put(memoryKey, messageHistory, StoreScope.PROJECT);
250
+ }
251
+
252
+ return completion.choices[0].message.content;
253
+ },
254
+ });
@@ -0,0 +1,132 @@
1
+ import { createAction, Property } from '@activepieces/pieces-framework';
2
+ import OpenAI from 'openai';
3
+ import { openaiAuth } from '../..';
4
+ import { streamToBuffer } from '../common/common';
5
+
6
+ export const textToSpeech = createAction({
7
+ auth: openaiAuth,
8
+ name: 'text_to_speech',
9
+ displayName: 'Text-to-Speech',
10
+ description: 'Generate an audio recording from text',
11
+ props: {
12
+ text: Property.LongText({
13
+ displayName: 'Text',
14
+ description: 'The text you want to hear.',
15
+ required: true,
16
+ }),
17
+ model: Property.Dropdown({
18
+ displayName: 'Model',
19
+ required: true,
20
+ description: 'The model which will generate the audio.',
21
+ defaultValue: 'tts-1',
22
+ refreshers: [],
23
+ options: async () => {
24
+ return {
25
+ options: [
26
+ {
27
+ label: 'tts-1',
28
+ value: 'tts-1',
29
+ },
30
+ {
31
+ label: 'tts-1-hd',
32
+ value: 'tts-1-hd',
33
+ },
34
+ ],
35
+ };
36
+ },
37
+ }),
38
+ speed: Property.Number({
39
+ displayName: 'Speed',
40
+ description:
41
+ 'The speed of the audio. Minimum is 0.25 and maximum is 4.00.',
42
+ defaultValue: 1.0,
43
+ required: false,
44
+ }),
45
+ voice: Property.Dropdown({
46
+ displayName: 'Voice',
47
+ description: 'The voice to generate the audio in.',
48
+ required: false,
49
+ refreshers: [],
50
+ defaultValue: 'alloy',
51
+ options: async () => {
52
+ return {
53
+ options: [
54
+ {
55
+ label: 'alloy',
56
+ value: 'alloy',
57
+ },
58
+ {
59
+ label: 'echo',
60
+ value: 'echo',
61
+ },
62
+ {
63
+ label: 'fable',
64
+ value: 'fable',
65
+ },
66
+ {
67
+ label: 'onyx',
68
+ value: 'onyx',
69
+ },
70
+ {
71
+ label: 'nova',
72
+ value: 'nova',
73
+ },
74
+ {
75
+ label: 'shimmer',
76
+ value: 'shimmer',
77
+ },
78
+ ],
79
+ };
80
+ },
81
+ }),
82
+ format: Property.Dropdown({
83
+ displayName: 'Output Format',
84
+ required: false,
85
+ description: 'The format you want the audio file in.',
86
+ defaultValue: 'mp3',
87
+ refreshers: [],
88
+ options: async () => {
89
+ return {
90
+ options: [
91
+ {
92
+ label: 'mp3',
93
+ value: 'mp3',
94
+ },
95
+ {
96
+ label: 'opus',
97
+ value: 'opus',
98
+ },
99
+ {
100
+ label: 'aac',
101
+ value: 'aac',
102
+ },
103
+ {
104
+ label: 'flac',
105
+ value: 'flac',
106
+ },
107
+ ],
108
+ };
109
+ },
110
+ }),
111
+ },
112
+ async run({ auth, propsValue, files }) {
113
+ const openai = new OpenAI({
114
+ apiKey: auth,
115
+ });
116
+ const { voice, format, model, text, speed } = propsValue;
117
+
118
+ const audio = await openai.audio.speech.create({
119
+ model: model,
120
+ input: text,
121
+ response_format: format as any,
122
+ voice: voice as any,
123
+ speed: speed,
124
+ });
125
+ const result = await streamToBuffer((audio as any).body);
126
+
127
+ return files.write({
128
+ fileName: 'test',
129
+ data: result as Buffer,
130
+ });
131
+ },
132
+ });
@@ -0,0 +1,70 @@
1
+ import {
2
+ HttpRequest,
3
+ HttpMethod,
4
+ httpClient,
5
+ } from '@activepieces/pieces-common';
6
+ import { Property, createAction } from '@activepieces/pieces-framework';
7
+ import { openaiAuth } from '../..';
8
+ import FormData from 'form-data';
9
+ import mime from 'mime-types';
10
+ import { Languages, baseUrl } from '../common/common';
11
+
12
+ export const transcribeAction = createAction({
13
+ name: 'transcribe',
14
+ displayName: 'Transcribe Audio',
15
+ description: 'Transcribe audio to text using whisper-1 model',
16
+ auth: openaiAuth,
17
+ props: {
18
+ audio: Property.File({
19
+ displayName: 'Audio',
20
+ required: true,
21
+ description: 'Audio file to transcribe',
22
+ }),
23
+ language: Property.StaticDropdown({
24
+ displayName: 'Language of the Audio',
25
+ description: 'Language of the audio file the default is en (English).',
26
+ required: false,
27
+ options: {
28
+ options: Languages,
29
+ },
30
+ defaultValue: 'en',
31
+ }),
32
+ },
33
+ run: async (context) => {
34
+ const fileData = context.propsValue.audio;
35
+ const mimeType = mime.lookup(fileData.extension ? fileData.extension : '');
36
+ let language = context.propsValue.language;
37
+ // if language is not in languages list, default to english
38
+ if (!Languages.some((l) => l.value === language)) {
39
+ language = 'en';
40
+ }
41
+
42
+ const form = new FormData();
43
+ form.append('file', fileData.data, {
44
+ filename: fileData.filename,
45
+ contentType: mimeType as string,
46
+ });
47
+ form.append('model', 'whisper-1');
48
+ form.append('language', language);
49
+
50
+ const headers = {
51
+ Authorization: `Bearer ${context.auth}`,
52
+ };
53
+
54
+ const request: HttpRequest = {
55
+ method: HttpMethod.POST,
56
+ url: `${baseUrl}/audio/transcriptions`,
57
+ body: form,
58
+ headers: {
59
+ ...form.getHeaders(),
60
+ ...headers,
61
+ },
62
+ };
63
+ try {
64
+ const response = await httpClient.sendRequest(request);
65
+ return response.body;
66
+ } catch (e) {
67
+ throw new Error(`Error while execution:\n${e}`);
68
+ }
69
+ },
70
+ });
@@ -0,0 +1,54 @@
1
+ import {
2
+ HttpRequest,
3
+ HttpMethod,
4
+ httpClient,
5
+ } from '@activepieces/pieces-common';
6
+ import { Property, createAction } from '@activepieces/pieces-framework';
7
+ import { openaiAuth } from '../..';
8
+ import FormData from 'form-data';
9
+ import mime from 'mime-types';
10
+ import { baseUrl } from '../common/common';
11
+
12
+ export const translateAction = createAction({
13
+ name: 'translate',
14
+ displayName: 'Translate Audio',
15
+ description: 'Translate audio to text using whisper-1 model',
16
+ auth: openaiAuth,
17
+ props: {
18
+ audio: Property.File({
19
+ displayName: 'Audio',
20
+ required: true,
21
+ description: 'Audio file to translate',
22
+ }),
23
+ },
24
+ run: async (context) => {
25
+ const fileData = context.propsValue.audio;
26
+ const mimeType = mime.lookup(fileData.extension ? fileData.extension : '');
27
+ const form = new FormData();
28
+ form.append('file', fileData.data, {
29
+ filename: fileData.filename,
30
+ contentType: mimeType as string,
31
+ });
32
+ form.append('model', 'whisper-1');
33
+
34
+ const headers = {
35
+ Authorization: `Bearer ${context.auth}`,
36
+ };
37
+
38
+ const request: HttpRequest = {
39
+ method: HttpMethod.POST,
40
+ url: `${baseUrl}/audio/translations`,
41
+ body: form,
42
+ headers: {
43
+ ...form.getHeaders(),
44
+ ...headers,
45
+ },
46
+ };
47
+ try {
48
+ const response = await httpClient.sendRequest(request);
49
+ return response.body;
50
+ } catch (e) {
51
+ throw new Error(`Error while execution:\n${e}`);
52
+ }
53
+ },
54
+ });
@@ -0,0 +1,151 @@
1
+ import {
2
+ createAction,
3
+ Property,
4
+ } from '@activepieces/pieces-framework';
5
+ import OpenAI from 'openai';
6
+ import { openaiAuth } from '../..';
7
+ import { z } from 'zod';
8
+ import { propsValidation } from '@activepieces/pieces-common';
9
+
10
+ export const visionPrompt = createAction({
11
+ auth: openaiAuth,
12
+ name: 'vision_prompt',
13
+ displayName: 'Vision Prompt',
14
+ description: 'Ask GPT a question about an image',
15
+ props: {
16
+ image: Property.File({
17
+ displayName: 'Image',
18
+ description: "The image URL or file you want GPT's vision to read.",
19
+ required: true,
20
+ }),
21
+ prompt: Property.LongText({
22
+ displayName: 'Question',
23
+ description: 'What do you want ChatGPT to tell you about the image?',
24
+ required: true,
25
+ }),
26
+ detail: Property.Dropdown({
27
+ displayName: 'Detail',
28
+ required: false,
29
+ description:
30
+ 'Control how the model processes the image and generates textual understanding.',
31
+ defaultValue: 'auto',
32
+ refreshers: [],
33
+ options: async () => {
34
+ return {
35
+ options: [
36
+ {
37
+ label: 'low',
38
+ value: 'low',
39
+ },
40
+ {
41
+ label: 'high',
42
+ value: 'high',
43
+ },
44
+ {
45
+ label: 'auto',
46
+ value: 'auto',
47
+ },
48
+ ],
49
+ };
50
+ },
51
+ }),
52
+ temperature: Property.Number({
53
+ displayName: 'Temperature',
54
+ required: false,
55
+ description:
56
+ 'Controls randomness: Lowering results in less random completions. As the temperature approaches zero, the model will become deterministic and repetitive.',
57
+ defaultValue: 0.9,
58
+ }),
59
+ maxTokens: Property.Number({
60
+ displayName: 'Maximum Tokens',
61
+ required: false,
62
+ description:
63
+ "The maximum number of tokens to generate. Requests can use up to 2,048 or 4,096 tokens shared between prompt and completion, don't set the value to maximum and leave some tokens for the input. The exact limit varies by model. (One token is roughly 4 characters for normal English text)",
64
+ defaultValue: 2048,
65
+ }),
66
+ topP: Property.Number({
67
+ displayName: 'Top P',
68
+ required: false,
69
+ description:
70
+ 'An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered.',
71
+ defaultValue: 1,
72
+ }),
73
+ frequencyPenalty: Property.Number({
74
+ displayName: 'Frequency penalty',
75
+ required: false,
76
+ description:
77
+ "Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim.",
78
+ defaultValue: 0,
79
+ }),
80
+ presencePenalty: Property.Number({
81
+ displayName: 'Presence penalty',
82
+ required: false,
83
+ description:
84
+ "Number between -2.0 and 2.0. Positive values penalize new tokens based on whether they appear in the text so far, increasing the mode's likelihood to talk about new topics.",
85
+ defaultValue: 0.6,
86
+ }),
87
+ roles: Property.Json({
88
+ displayName: 'Roles',
89
+ required: false,
90
+ description: 'Array of roles to specify more accurate response',
91
+ defaultValue: [
92
+ { role: 'system', content: 'You are a helpful assistant.' },
93
+ ],
94
+ }),
95
+ },
96
+ async run({ auth, propsValue }) {
97
+ await propsValidation.validateZod(propsValue, {
98
+ temperature: z.number().min(0).max(1),
99
+ });
100
+
101
+ const openai = new OpenAI({
102
+ apiKey: auth,
103
+ });
104
+ const { temperature, maxTokens, topP, frequencyPenalty, presencePenalty } =
105
+ propsValue;
106
+
107
+ const rolesArray = propsValue.roles ? (propsValue.roles as any) : [];
108
+ const roles = rolesArray.map((item: any) => {
109
+ const rolesEnum = ['system', 'user', 'assistant'];
110
+ if (!rolesEnum.includes(item.role)) {
111
+ throw new Error(
112
+ 'The only available roles are: [system, user, assistant]'
113
+ );
114
+ }
115
+
116
+ return {
117
+ role: item.role,
118
+ content: item.content,
119
+ };
120
+ });
121
+
122
+ const completion = await openai.chat.completions.create({
123
+ model: 'gpt-4o',
124
+ messages: [
125
+ ...roles,
126
+ {
127
+ role: 'user',
128
+ content: [
129
+ {
130
+ type: 'text',
131
+ text: propsValue['prompt'],
132
+ },
133
+ {
134
+ type: 'image_url',
135
+ image_url: {
136
+ url: `data:image/${propsValue.image.extension};base64,${propsValue.image.base64}`,
137
+ },
138
+ },
139
+ ],
140
+ },
141
+ ],
142
+ temperature: temperature,
143
+ max_tokens: maxTokens,
144
+ top_p: topP,
145
+ frequency_penalty: frequencyPenalty,
146
+ presence_penalty: presencePenalty,
147
+ });
148
+
149
+ return completion.choices[0].message.content;
150
+ },
151
+ });