Add Activepieces integration for workflow automation
- Add Activepieces fork with SmoothSchedule custom piece - Create integrations app with Activepieces service layer - Add embed token endpoint for iframe integration - Create Automations page with embedded workflow builder - Add sidebar visibility fix for embed mode - Add list inactive customers endpoint to Public API - Include SmoothSchedule triggers: event created/updated/cancelled - Include SmoothSchedule actions: create/update/cancel events, list resources/services/customers 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,29 @@
|
||||
import { propsValidation } from '@activepieces/pieces-common';
|
||||
import { createAction } from '@activepieces/pieces-framework';
|
||||
import { vlmRunAuth, vlmRunCommon } from '../common';
|
||||
import { analyzeAudioProperties } from '../common/properties';
|
||||
import { analyzeAudioSchema } from '../common/schemas';
|
||||
|
||||
export const analyzeAudio = createAction({
|
||||
auth: vlmRunAuth,
|
||||
name: 'analyzeAudio',
|
||||
displayName: 'Analyze Audio',
|
||||
description: 'Process an audio file, extracting features or transcription',
|
||||
props: analyzeAudioProperties,
|
||||
async run({ auth: apiKey, propsValue }) {
|
||||
await propsValidation.validateZod(propsValue, analyzeAudioSchema);
|
||||
|
||||
|
||||
const uploadResponse = await vlmRunCommon.uploadFile({
|
||||
apiKey:apiKey.secret_text,
|
||||
file: propsValue.audio,
|
||||
});
|
||||
|
||||
const response = await vlmRunCommon.analyzeAudio({
|
||||
apiKey:apiKey.secret_text,
|
||||
file_id: uploadResponse.id,
|
||||
});
|
||||
|
||||
return await vlmRunCommon.getresponse(apiKey.secret_text, response.id, response.status);
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,32 @@
|
||||
import { propsValidation } from '@activepieces/pieces-common';
|
||||
import { createAction } from '@activepieces/pieces-framework';
|
||||
import { vlmRunAuth, vlmRunCommon } from '../common';
|
||||
import { analyzeDocumentProperties } from '../common/properties';
|
||||
import { analyzeDocumentSchema } from '../common/schemas';
|
||||
|
||||
export const analyzeDocument = createAction({
|
||||
auth: vlmRunAuth,
|
||||
name: 'analyzeDocument',
|
||||
displayName: 'Analyze Document',
|
||||
description:
|
||||
'Process a document (PDF, DOCX, etc.), extracting structured data or text.',
|
||||
props: analyzeDocumentProperties,
|
||||
async run({ auth: apiKey, propsValue }) {
|
||||
await propsValidation.validateZod(propsValue, analyzeDocumentSchema);
|
||||
|
||||
const { document, domain } = propsValue;
|
||||
|
||||
const uploadResponse = await vlmRunCommon.uploadFile({
|
||||
apiKey:apiKey.secret_text,
|
||||
file: document,
|
||||
});
|
||||
|
||||
const response = await vlmRunCommon.analyzeDocument({
|
||||
apiKey:apiKey.secret_text,
|
||||
file_id: uploadResponse.id,
|
||||
domain,
|
||||
});
|
||||
|
||||
return await vlmRunCommon.getresponse(apiKey.secret_text, response.id, response.status);
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,28 @@
|
||||
import { propsValidation } from '@activepieces/pieces-common';
|
||||
import { createAction } from '@activepieces/pieces-framework';
|
||||
import { vlmRunAuth, vlmRunCommon } from '../common';
|
||||
import { analyzeImageProperties } from '../common/properties';
|
||||
import { analyzeImageSchema } from '../common/schemas';
|
||||
|
||||
export const analyzeImage = createAction({
|
||||
auth: vlmRunAuth,
|
||||
name: 'analyzeImage',
|
||||
displayName: 'Analyze Image',
|
||||
description:
|
||||
'Process an image (file or URL), extracting descriptions, detecting objects, etc.',
|
||||
props: analyzeImageProperties,
|
||||
async run({ auth: apiKey, propsValue }) {
|
||||
await propsValidation.validateZod(propsValue, analyzeImageSchema);
|
||||
|
||||
const { image, domain } = propsValue;
|
||||
|
||||
|
||||
const response = await vlmRunCommon.analyzeImage({
|
||||
apiKey:apiKey.secret_text,
|
||||
images: [image],
|
||||
domain,
|
||||
});
|
||||
|
||||
return await vlmRunCommon.getresponse(apiKey.secret_text, response.id, response.status);
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,32 @@
|
||||
import { propsValidation } from '@activepieces/pieces-common';
|
||||
import { createAction } from '@activepieces/pieces-framework';
|
||||
import { vlmRunAuth, vlmRunCommon } from '../common';
|
||||
import { analyzeVideoProperties } from '../common/properties';
|
||||
import { analyzeVideoSchema } from '../common/schemas';
|
||||
|
||||
export const analyzeVideo = createAction({
|
||||
auth: vlmRunAuth,
|
||||
name: 'analyzeVideo',
|
||||
displayName: 'Analyze Video',
|
||||
description:
|
||||
'Analyze a video file or URL, e.g. extract frames, detect content, etc.',
|
||||
props: analyzeVideoProperties,
|
||||
async run({ auth: apiKey, propsValue }) {
|
||||
await propsValidation.validateZod(propsValue, analyzeVideoSchema);
|
||||
|
||||
const { video, domain } = propsValue;
|
||||
|
||||
const uploadResponse = await vlmRunCommon.uploadFile({
|
||||
apiKey:apiKey.secret_text,
|
||||
file: video,
|
||||
});
|
||||
|
||||
const response = await vlmRunCommon.analyzeVideo({
|
||||
apiKey:apiKey.secret_text,
|
||||
file_id: uploadResponse.id,
|
||||
domain,
|
||||
});
|
||||
|
||||
return await vlmRunCommon.getresponse(apiKey.secret_text, response.id, response.status);
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,17 @@
|
||||
import { propsValidation } from '@activepieces/pieces-common';
|
||||
import { createAction } from '@activepieces/pieces-framework';
|
||||
import { vlmRunAuth, vlmRunCommon } from '../common';
|
||||
import { getFileProperties } from '../common/properties';
|
||||
import { getFileSchema } from '../common/schemas';
|
||||
|
||||
export const getFile = createAction({
|
||||
auth: vlmRunAuth,
|
||||
name: 'getFile',
|
||||
displayName: 'Get File',
|
||||
description: "Gets file's metadata by ID.",
|
||||
props: getFileProperties,
|
||||
async run({ auth: apiKey, propsValue }) {
|
||||
await propsValidation.validateZod(propsValue, getFileSchema);
|
||||
return await vlmRunCommon.getFile({ apiKey: apiKey.secret_text, file_id: propsValue.fileId });
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,132 @@
|
||||
import { httpClient, HttpMethod } from '@activepieces/pieces-common';
|
||||
import { PieceAuth } from '@activepieces/pieces-framework';
|
||||
import FormData from 'form-data';
|
||||
import {
|
||||
AnalyzeAudioParams,
|
||||
AnalyzeDocumentParams,
|
||||
AnalyzeImageParams,
|
||||
AnalyzeVideoParams,
|
||||
FileResponse,
|
||||
GetFileParams,
|
||||
PredictionResponse,
|
||||
UploadFileParams,
|
||||
} from './types';
|
||||
export const vlmRunAuth = PieceAuth.SecretText({
|
||||
displayName: 'API Key',
|
||||
description: 'Your VLM Run API Key',
|
||||
required: true,
|
||||
});
|
||||
|
||||
export const vlmRunCommon = {
|
||||
baseUrl: 'https://api.vlm.run/v1',
|
||||
baseHeaders: (apiKey: string) => ({
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${apiKey}`,
|
||||
}),
|
||||
endpoints: {
|
||||
uploadFile: '/files',
|
||||
listFiles: '/files',
|
||||
analyzeAudio: '/audio/generate',
|
||||
analyzeImage: '/image/generate',
|
||||
analyzeDocument: '/document/generate',
|
||||
analyzeVideo: '/video/generate',
|
||||
getresponse: '/predictions',
|
||||
getFile: (fileId: string) => `/files/${fileId}`,
|
||||
},
|
||||
|
||||
// Methods
|
||||
getresponse: async (apiKey: string, requestId: string, status: string) => {
|
||||
let statusnow = status;
|
||||
const timeoutAt = Date.now() + 5 * 60 * 1000;
|
||||
|
||||
while (statusnow !== 'completed' && Date.now() < timeoutAt) {
|
||||
await new Promise((resolve) => setTimeout(resolve, 5000));
|
||||
|
||||
const pollRes = await httpClient.sendRequest<any>({
|
||||
method: HttpMethod.GET,
|
||||
url: `${vlmRunCommon.baseUrl}${vlmRunCommon.endpoints.getresponse}/${requestId}`,
|
||||
headers: vlmRunCommon.baseHeaders(apiKey),
|
||||
});
|
||||
|
||||
statusnow = pollRes.body?.status;
|
||||
console.log('first', statusnow);
|
||||
if (statusnow === 'completed') {
|
||||
return pollRes.body.response;
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error('generation timed out or failed.');
|
||||
},
|
||||
uploadFile: async ({ apiKey, file }: UploadFileParams) => {
|
||||
const formData = new FormData();
|
||||
formData.append('file', file.data, { filename: file.filename });
|
||||
|
||||
const response = await httpClient.sendRequest<FileResponse>({
|
||||
method: HttpMethod.POST,
|
||||
url: `${vlmRunCommon.baseUrl}${vlmRunCommon.endpoints.uploadFile}`,
|
||||
headers: {
|
||||
...vlmRunCommon.baseHeaders(apiKey),
|
||||
'Content-Type': 'multipart/form-data',
|
||||
},
|
||||
body: formData,
|
||||
});
|
||||
return response.body;
|
||||
},
|
||||
listFiles: async (apiKey: string) => {
|
||||
const response = await httpClient.sendRequest<FileResponse[]>({
|
||||
method: HttpMethod.GET,
|
||||
url: `${vlmRunCommon.baseUrl}${vlmRunCommon.endpoints.listFiles}`,
|
||||
headers: vlmRunCommon.baseHeaders(apiKey),
|
||||
});
|
||||
return response.body;
|
||||
},
|
||||
analyzeAudio: async ({ apiKey, ...params }: AnalyzeAudioParams) => {
|
||||
const response = await httpClient.sendRequest<PredictionResponse>({
|
||||
method: HttpMethod.POST,
|
||||
url: `${vlmRunCommon.baseUrl}${vlmRunCommon.endpoints.analyzeAudio}`,
|
||||
headers: vlmRunCommon.baseHeaders(apiKey),
|
||||
body: {
|
||||
domain: 'audio.transcription',
|
||||
...params,
|
||||
},
|
||||
});
|
||||
return response.body;
|
||||
},
|
||||
analyzeImage: async ({ apiKey, ...params }: AnalyzeImageParams) => {
|
||||
const response = await httpClient.sendRequest<PredictionResponse>({
|
||||
method: HttpMethod.POST,
|
||||
url: `${vlmRunCommon.baseUrl}${vlmRunCommon.endpoints.analyzeImage}`,
|
||||
headers: vlmRunCommon.baseHeaders(apiKey),
|
||||
body: params,
|
||||
});
|
||||
return response.body;
|
||||
},
|
||||
analyzeDocument: async ({ apiKey, ...params }: AnalyzeDocumentParams) => {
|
||||
const response = await httpClient.sendRequest<PredictionResponse>({
|
||||
method: HttpMethod.POST,
|
||||
url: `${vlmRunCommon.baseUrl}${vlmRunCommon.endpoints.analyzeDocument}`,
|
||||
headers: vlmRunCommon.baseHeaders(apiKey),
|
||||
body: params,
|
||||
});
|
||||
return response.body;
|
||||
},
|
||||
analyzeVideo: async ({ apiKey, ...params }: AnalyzeVideoParams) => {
|
||||
const response = await httpClient.sendRequest<PredictionResponse>({
|
||||
method: HttpMethod.POST,
|
||||
url: `${vlmRunCommon.baseUrl}${vlmRunCommon.endpoints.analyzeVideo}`,
|
||||
headers: vlmRunCommon.baseHeaders(apiKey),
|
||||
body: params,
|
||||
});
|
||||
return response.body;
|
||||
},
|
||||
getFile: async ({ apiKey, file_id }: GetFileParams) => {
|
||||
const response = await httpClient.sendRequest<FileResponse>({
|
||||
method: HttpMethod.GET,
|
||||
url: `${vlmRunCommon.baseUrl}${vlmRunCommon.endpoints.getFile(file_id)}`,
|
||||
headers: vlmRunCommon.baseHeaders(apiKey),
|
||||
});
|
||||
return response.body;
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,118 @@
|
||||
import { Property } from '@activepieces/pieces-framework';
|
||||
import { vlmRunAuth, vlmRunCommon } from '.';
|
||||
|
||||
// Action Properties
|
||||
export const analyzeAudioProperties = {
|
||||
audio: Property.File({
|
||||
displayName: 'Audio File',
|
||||
description: 'The audio file to get the transcription from. Only MP3 supported.',
|
||||
required: true,
|
||||
}),
|
||||
};
|
||||
|
||||
export const analyzeImageProperties = {
|
||||
image: Property.ShortText({
|
||||
displayName: 'Image File',
|
||||
description: 'The image file to be analyzed.',
|
||||
required: true,
|
||||
}),
|
||||
domain: Property.StaticDropdown({
|
||||
displayName: 'Domain',
|
||||
description: 'The specific analysis domain for the image.',
|
||||
required: true,
|
||||
options: {
|
||||
options: [
|
||||
{ label: 'Image Classification', value: 'image.classification' },
|
||||
{ label: 'Image Caption', value: 'image.caption' },
|
||||
{ label: 'Image TV News', value: 'image.tv-news' },
|
||||
{ label: 'Image Q and A', value: 'image.q-and-a' },
|
||||
],
|
||||
},
|
||||
}),
|
||||
};
|
||||
|
||||
export const analyzeDocumentProperties = {
|
||||
document: Property.File({
|
||||
displayName: 'Document File',
|
||||
description: 'The document file to be analyzed.',
|
||||
required: true,
|
||||
}),
|
||||
domain: Property.StaticDropdown({
|
||||
displayName: 'Domain',
|
||||
description: 'The specific analysis domain for the document.',
|
||||
required: true,
|
||||
options: {
|
||||
options: [
|
||||
{ label: 'Bank Statement', value: 'document.bank-statement' },
|
||||
{ label: 'Document Classification', value: 'document.classification' },
|
||||
{ label: 'Invoice', value: 'document.invoice' },
|
||||
{ label: 'Markdown', value: 'document.markdown' },
|
||||
{ label: 'Q and A', value: 'document.q-and-a' },
|
||||
{ label: 'Receipt', value: 'document.receipt' },
|
||||
{ label: 'Resume', value: 'document.resume' },
|
||||
{ label: "US Driver's License", value: 'document.us-drivers-license' },
|
||||
{ label: 'Utility Bill', value: 'document.utility-bill' },
|
||||
],
|
||||
},
|
||||
}),
|
||||
};
|
||||
|
||||
export const analyzeVideoProperties = {
|
||||
video: Property.File({
|
||||
displayName: 'Video File',
|
||||
description: 'The video file to be analyzed.',
|
||||
required: true,
|
||||
}),
|
||||
domain: Property.StaticDropdown({
|
||||
displayName: 'Domain',
|
||||
description: 'The specific analysis domain for the video.',
|
||||
required: true,
|
||||
options: {
|
||||
options: [
|
||||
{ label: 'Video Transcription', value: 'video.transcription' },
|
||||
{
|
||||
label: 'Video Transcription Summary',
|
||||
value: 'video.transcription-summary',
|
||||
},
|
||||
{
|
||||
label: 'Video Product Demo Summary',
|
||||
value: 'video.product-demo-summary',
|
||||
},
|
||||
{
|
||||
label: 'Video Conferencing Summary',
|
||||
value: 'video.conferencing-summary',
|
||||
},
|
||||
{ label: 'Video Podcast Summary', value: 'video.podcast-summary' },
|
||||
{ label: 'Video Summary', value: 'video.summary' },
|
||||
{ label: 'Video Dashcam Analytics', value: 'video.dashcam-analytics' },
|
||||
],
|
||||
},
|
||||
}),
|
||||
};
|
||||
|
||||
export const getFileProperties = {
|
||||
fileId: Property.Dropdown({
|
||||
auth: vlmRunAuth,
|
||||
displayName: 'File',
|
||||
description: 'Select a file to retrieve its details.',
|
||||
required: true,
|
||||
refreshers: ['auth'],
|
||||
options: async ({ auth }) => {
|
||||
if (!auth) {
|
||||
return {
|
||||
disabled: true,
|
||||
placeholder: 'Connect your VLM Run account to see files',
|
||||
options: [],
|
||||
};
|
||||
}
|
||||
const files = await vlmRunCommon.listFiles(auth.secret_text);
|
||||
return {
|
||||
disabled: false,
|
||||
options: files.map((file) => ({
|
||||
label: file.filename,
|
||||
value: file.id,
|
||||
})),
|
||||
};
|
||||
},
|
||||
}),
|
||||
};
|
||||
@@ -0,0 +1,59 @@
|
||||
import z from 'zod';
|
||||
|
||||
const imageDomains = [
|
||||
'image.classification',
|
||||
'image.caption',
|
||||
'image.tv-news',
|
||||
'image.q-and-a',
|
||||
] as const;
|
||||
|
||||
const documentDomains = [
|
||||
'document.bank-statement',
|
||||
'document.classification',
|
||||
'document.invoice',
|
||||
'document.markdown',
|
||||
'document.q-and-a',
|
||||
'document.receipt',
|
||||
'document.resume',
|
||||
'document.us-drivers-license',
|
||||
'document.utility-bill',
|
||||
] as const;
|
||||
|
||||
const videoDomains = [
|
||||
'video.transcription',
|
||||
'video.transcription-summary',
|
||||
'video.product-demo-summary',
|
||||
'video.conferencing-summary',
|
||||
'video.podcast-summary',
|
||||
'video.summary',
|
||||
'video.dashcam-analytics'
|
||||
] as const;
|
||||
|
||||
const apFileSchema = z.object({
|
||||
filename: z.string(),
|
||||
data: z.instanceof(Buffer),
|
||||
extension: z.string()
|
||||
});
|
||||
// Schemas
|
||||
export const analyzeAudioSchema = {
|
||||
audio: apFileSchema,
|
||||
};
|
||||
|
||||
export const analyzeImageSchema = {
|
||||
image: z.string().url(),
|
||||
domain: z.enum(imageDomains)
|
||||
};
|
||||
|
||||
export const analyzeDocumentSchema = {
|
||||
document: apFileSchema,
|
||||
domain: z.enum(documentDomains)
|
||||
};
|
||||
|
||||
export const analyzeVideoSchema = {
|
||||
video: apFileSchema,
|
||||
domain: z.enum(videoDomains)
|
||||
};
|
||||
|
||||
export const getFileSchema = {
|
||||
fileId: z.string()
|
||||
};
|
||||
@@ -0,0 +1,59 @@
|
||||
import { ApFile } from "@activepieces/pieces-framework";
|
||||
|
||||
export interface AuthenticationParams {
|
||||
apiKey: string;
|
||||
}
|
||||
|
||||
// Request types
|
||||
|
||||
export interface UploadFileParams extends AuthenticationParams {
|
||||
file: ApFile;
|
||||
}
|
||||
|
||||
export interface AnalyzeAudioParams extends AuthenticationParams {
|
||||
file_id: string;
|
||||
}
|
||||
|
||||
export interface AnalyzeImageParams extends AuthenticationParams {
|
||||
images: string[];
|
||||
domain: string;
|
||||
}
|
||||
|
||||
export interface AnalyzeDocumentParams extends AuthenticationParams {
|
||||
file_id: string;
|
||||
domain: string;
|
||||
}
|
||||
|
||||
export interface AnalyzeVideoParams extends AuthenticationParams {
|
||||
file_id: string;
|
||||
domain: string;
|
||||
}
|
||||
|
||||
export interface GetFileParams extends AuthenticationParams {
|
||||
file_id: string;
|
||||
}
|
||||
|
||||
// Response types
|
||||
export interface FileResponse {
|
||||
id: string;
|
||||
filename: string;
|
||||
bytes: number;
|
||||
purpose: string;
|
||||
created_at: string;
|
||||
object: 'file';
|
||||
}
|
||||
type JobStatus = string;
|
||||
interface CreditUsage {
|
||||
elements_processed?: number;
|
||||
element_type?: 'image' | 'page' | 'video' | 'audio';
|
||||
credits_used?: number;
|
||||
}
|
||||
export interface PredictionResponse {
|
||||
id: string;
|
||||
created_at: string;
|
||||
completed_at?: string;
|
||||
response?: any;
|
||||
status: JobStatus;
|
||||
message?: string;
|
||||
usage?: CreditUsage;
|
||||
}
|
||||
Reference in New Issue
Block a user