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:
poduck
2025-12-18 22:59:37 -05:00
parent 9848268d34
commit 3aa7199503
16292 changed files with 1284892 additions and 4708 deletions

View File

@@ -0,0 +1,90 @@
{
"Integrate with Contextual AI to automate document processing and AI workflows": "Integrate with Contextual AI to automate document processing and AI workflows",
"API Key": "API Key",
"Base URL": "Base URL",
"Your Contextual AI API key": "Your Contextual AI API key",
"API base URL (leave blank for default)": "API base URL (leave blank for default)",
"\n## Contextual AI Connection Setup\n\n### Prerequisites\n- Create a Contextual AI account at [Contextual AI](https://contextual.ai)\n- Generate an API key from your workspace settings\n- You'll receive $25 in free credits (or $50 with work email)\n\n### Authentication Fields\n\n**API Key**: Your Contextual AI API key (required)\n- Sign in to your Contextual AI workspace\n- Navigate to API Keys in the sidebar\n- Click \"Create API Key\" and follow the instructions\n- Copy the generated key and paste it here\n\n**Base URL**: ": "\n## Contextual AI Connection Setup\n\n### Prerequisites\n- Create a Contextual AI account at [Contextual AI](https://contextual.ai)\n- Generate an API key from your workspace settings\n- You'll receive $25 in free credits (or $50 with work email)\n\n### Authentication Fields\n\n**API Key**: Your Contextual AI API key (required)\n- Sign in to your Contextual AI workspace\n- Navigate to API Keys in the sidebar\n- Click \"Create API Key\" and follow the instructions\n- Copy the generated key and paste it here\n\n**Base URL**: The API base URL (optional)\n- Leave blank to use the default: `https://api.contextual.ai/v1`\n- Only change if you have a custom deployment\n",
"Query Agent": "Query Agent",
"Generate Text": "Generate Text",
"Ingest Document": "Ingest Document",
"Parse File": "Parse File",
"Create Agent": "Create Agent",
"Invite Users": "Invite Users",
"Create Datastore": "Create Datastore",
"Send a message to a Contextual AI agent and get a response": "Send a message to a Contextual AI agent and get a response",
"Generate text using Contextual AI's Grounded Language Model": "Generate text using Contextual AI's Grounded Language Model",
"Upload and ingest a document into a Contextual AI datastore": "Upload and ingest a document into a Contextual AI datastore",
"Parse a document file into structured Markdown and/or JSON format": "Parse a document file into structured Markdown and/or JSON format",
"Create a new Contextual AI agent with specified configuration": "Create a new Contextual AI agent with specified configuration",
"Invite new users to the Contextual AI workspace": "Invite new users to the Contextual AI workspace",
"Create a new datastore for organizing documents": "Create a new datastore for organizing documents",
"Agent": "Agent",
"Message": "Message",
"Conversation ID": "Conversation ID",
"Include Retrieval Content": "Include Retrieval Content",
"Prompt": "Prompt",
"Model Version": "Model Version",
"Knowledge Sources": "Knowledge Sources",
"System Prompt": "System Prompt",
"Max Tokens": "Max Tokens",
"Temperature": "Temperature",
"Top P": "Top P",
"Avoid Commentary": "Avoid Commentary",
"Datastore": "Datastore",
"Document File": "Document File",
"Custom Metadata": "Custom Metadata",
"Configuration Override": "Configuration Override",
"Parse Mode": "Parse Mode",
"Page Range": "Page Range",
"Enable Document Hierarchy": "Enable Document Hierarchy",
"Enable Split Tables": "Enable Split Tables",
"Max Split Table Cells": "Max Split Table Cells",
"Figure Caption Mode": "Figure Caption Mode",
"Agent Name": "Agent Name",
"Description": "Description",
"Datastores": "Datastores",
"Filter Prompt": "Filter Prompt",
"Users to Invite": "Users to Invite",
"Tenant Short Name": "Tenant Short Name",
"Datastore Name": "Datastore Name",
"Select the agent to query": "Select the agent to query",
"The message to send to the agent": "The message to send to the agent",
"Optional conversation ID to continue an existing conversation (leave empty for new conversation)": "Optional conversation ID to continue an existing conversation (leave empty for new conversation)",
"Include the text of retrieved contents in the response": "Include the text of retrieved contents in the response",
"The text prompt to generate a response for": "The text prompt to generate a response for",
"The version of Contextual's GLM to use": "The version of Contextual's GLM to use",
"Optional knowledge sources to ground the generation (leave empty for general generation)": "Optional knowledge sources to ground the generation (leave empty for general generation)",
"Optional system instructions for the model": "Optional system instructions for the model",
"Maximum number of tokens to generate (default: 1024)": "Maximum number of tokens to generate (default: 1024)",
"Sampling temperature (0.0 to 1.0, lower = more focused, higher = more creative)": "Sampling temperature (0.0 to 1.0, lower = more focused, higher = more creative)",
"Nucleus sampling parameter (0.0 to 1.0)": "Nucleus sampling parameter (0.0 to 1.0)",
"Avoid providing additional conversational commentary not grounded in context": "Avoid providing additional conversational commentary not grounded in context",
"Select the datastore to upload the document to": "Select the datastore to upload the document to",
"The document file to upload (PDF, HTML, DOC, DOCX, PPT, PPTX)": "The document file to upload (PDF, HTML, DOC, DOCX, PPT, PPTX)",
"Optional custom metadata as key-value pairs (max 15 fields, 2KB total)": "Optional custom metadata as key-value pairs (max 15 fields, 2KB total)",
"Optional configuration override in JSON format for this specific document": "Optional configuration override in JSON format for this specific document",
"The document file to parse (PDF, DOC, DOCX, PPT, PPTX)": "The document file to parse (PDF, DOC, DOCX, PPT, PPTX)",
"Parsing mode - basic for simple text, standard for complex documents": "Parsing mode - basic for simple text, standard for complex documents",
"Optional page range to parse (e.g., \"0,1,2\" or \"0-2,5,6\")": "Optional page range to parse (e.g., \"0,1,2\" or \"0-2,5,6\")",
"Add table of contents with document structure (beta feature)": "Add table of contents with document structure (beta feature)",
"Split large tables into multiple tables with headers": "Split large tables into multiple tables with headers",
"Threshold for splitting large tables (only used when split tables is enabled)": "Threshold for splitting large tables (only used when split tables is enabled)",
"How thorough figure captions should be": "How thorough figure captions should be",
"Name for the new agent": "Name for the new agent",
"Optional description of the agent": "Optional description of the agent",
"Select datastores to associate with this agent (leave empty to create new datastore)": "Select datastores to associate with this agent (leave empty to create new datastore)",
"Optional system prompt for the agent": "Optional system prompt for the agent",
"Optional prompt for filtering retrieved chunks": "Optional prompt for filtering retrieved chunks",
"List of users to invite": "List of users to invite",
"The short name of the tenant/workspace": "The short name of the tenant/workspace",
"Name for the new datastore": "Name for the new datastore",
"GLM v2": "GLM v2",
"GLM v1": "GLM v1",
"Basic (text-only)": "Basic (text-only)",
"Standard (complex documents)": "Standard (complex documents)",
"Concise": "Concise",
"Detailed (beta)": "Detailed (beta)",
"New Agent": "New Agent",
"Triggers when a new Contextual AI agent is created": "Triggers when a new Contextual AI agent is created"
}

View File

@@ -0,0 +1,84 @@
import { PieceAuth, createPiece, Property } from "@activepieces/pieces-framework";
import { PieceCategory } from '@activepieces/shared';
import { ContextualAI } from 'contextual-client';
import { queryAgentAction } from './lib/actions/query-agent';
import { generateAction } from './lib/actions/generate';
import { ingestDocumentAction } from './lib/actions/ingest-document';
import { parseFileAction } from './lib/actions/parse-file';
import { createAgentAction } from './lib/actions/create-agent';
import { inviteUsersAction } from './lib/actions/invite-users';
import { createDatastoreAction } from './lib/actions/create-datastore';
import { newAgentTrigger } from './lib/triggers/new-agent';
const markdown = `
## Contextual AI Connection Setup
### Prerequisites
- Create a Contextual AI account at [Contextual AI](https://contextual.ai)
- Generate an API key from your workspace settings
- You'll receive $25 in free credits (or $50 with work email)
### Authentication Fields
**API Key**: Your Contextual AI API key (required)
- Sign in to your Contextual AI workspace
- Navigate to API Keys in the sidebar
- Click "Create API Key" and follow the instructions
- Copy the generated key and paste it here
**Base URL**: The API base URL (optional)
- Leave blank to use the default: \`https://api.contextual.ai/v1\`
- Only change if you have a custom deployment
`;
export const contextualAiAuth = PieceAuth.CustomAuth({
required: true,
description: markdown,
props: {
apiKey: PieceAuth.SecretText({
displayName: 'API Key',
description: 'Your Contextual AI API key',
required: true,
}),
baseUrl: Property.ShortText({
displayName: 'Base URL',
description: 'API base URL (leave blank for default)',
required: false,
}),
},
validate: async ({ auth }) => {
try {
const { apiKey, baseUrl } = auth;
const client = new ContextualAI({
apiKey: apiKey,
baseURL: baseUrl || 'https://api.contextual.ai/v1',
});
await client.datastores.list();
return {
valid: true,
};
} catch (error) {
return {
valid: false,
error: `Authentication failed: ${error instanceof Error ? error.message : 'Unknown error'}. Please verify your API key and base URL.`,
};
}
},
});
export const contextualAi = createPiece({
displayName: "Contextual AI",
description: "Integrate with Contextual AI to automate document processing and AI workflows",
auth: contextualAiAuth,
minimumSupportedRelease: '0.36.1',
logoUrl: "https://cdn.activepieces.com/pieces/contextual-ai.png",
categories: [PieceCategory.ARTIFICIAL_INTELLIGENCE],
authors: ["onyedikachi-david"],
actions: [queryAgentAction, generateAction, ingestDocumentAction, parseFileAction, createAgentAction, inviteUsersAction, createDatastoreAction],
triggers: [newAgentTrigger],
});

View File

@@ -0,0 +1,101 @@
import { createAction, Property } from "@activepieces/pieces-framework";
import { contextualAiAuth } from "../../index";
import { ContextualAI } from 'contextual-client';
import type { Datastore } from 'contextual-client/resources/datastores';
export const createAgentAction = createAction({
auth: contextualAiAuth,
name: 'create_agent',
displayName: 'Create Agent',
description: 'Create a new Contextual AI agent with specified configuration',
props: {
name: Property.ShortText({
displayName: 'Agent Name',
description: 'Name for the new agent',
required: true,
}),
description: Property.ShortText({
displayName: 'Description',
description: 'Optional description of the agent',
required: false,
}),
datastoreIds: Property.MultiSelectDropdown({
auth: contextualAiAuth,
displayName: 'Datastores',
description: 'Select datastores to associate with this agent (leave empty to create new datastore)',
required: false,
refreshers: [],
options: async ({ auth }) => {
try {
if (!auth) {
return {
disabled: true,
options: [],
placeholder: 'Please connect your account first',
};
}
const { apiKey, baseUrl } = auth.props;
const client = new ContextualAI({
apiKey: apiKey,
baseURL: baseUrl || 'https://api.contextual.ai/v1',
});
const allDatastores: Datastore[] = [];
for await (const datastore of client.datastores.list()) {
allDatastores.push(datastore);
}
return {
options: allDatastores.map((datastore: Datastore) => ({
label: datastore.name,
value: datastore.id,
})),
};
} catch (error) {
return {
options: [],
error: 'Failed to fetch datastores. Please check your API key.',
};
}
},
}),
systemPrompt: Property.LongText({
displayName: 'System Prompt',
description: 'Optional system prompt for the agent',
required: false,
}),
filterPrompt: Property.LongText({
displayName: 'Filter Prompt',
description: 'Optional prompt for filtering retrieved chunks',
required: false,
}),
},
async run({ auth, propsValue }) {
const { apiKey, baseUrl } = auth.props;
const { name, description, datastoreIds, systemPrompt, filterPrompt } = propsValue;
const client = new ContextualAI({
apiKey: apiKey,
baseURL: baseUrl || 'https://api.contextual.ai/v1',
});
const datastoreIdsArray = datastoreIds || [];
const agentParams: any = {
name,
};
if (description) agentParams.description = description;
if (datastoreIdsArray.length > 0) agentParams.datastore_ids = datastoreIdsArray;
if (systemPrompt) agentParams.multiturn_system_prompt = systemPrompt;
if (filterPrompt) agentParams.filter_prompt = filterPrompt;
const response = await client.agents.create(agentParams);
return {
agent_id: response.id,
datastore_ids: response.datastore_ids,
status: 'Agent created successfully',
};
},
});

View File

@@ -0,0 +1,35 @@
import { createAction, Property } from "@activepieces/pieces-framework";
import { contextualAiAuth } from "../../index";
import { ContextualAI } from 'contextual-client';
export const createDatastoreAction = createAction({
auth: contextualAiAuth,
name: 'create_datastore',
displayName: 'Create Datastore',
description: 'Create a new datastore for organizing documents',
props: {
name: Property.ShortText({
displayName: 'Datastore Name',
description: 'Name for the new datastore',
required: true,
}),
},
async run({ auth, propsValue }) {
const { apiKey, baseUrl } = auth.props;
const { name } = propsValue;
const client = new ContextualAI({
apiKey: apiKey,
baseURL: baseUrl || 'https://api.contextual.ai/v1',
});
const response = await client.datastores.create({
name,
});
return {
datastore_id: response.id,
status: 'Datastore created successfully',
};
},
});

View File

@@ -0,0 +1,100 @@
import { createAction, Property } from "@activepieces/pieces-framework";
import { contextualAiAuth } from "../../index";
import { ContextualAI } from 'contextual-client';
export const generateAction = createAction({
auth: contextualAiAuth,
name: 'generate',
displayName: 'Generate Text',
description: 'Generate text using Contextual AI\'s Grounded Language Model',
props: {
prompt: Property.LongText({
displayName: 'Prompt',
description: 'The text prompt to generate a response for',
required: true,
}),
model: Property.StaticDropdown({
displayName: 'Model Version',
description: 'The version of Contextual\'s GLM to use',
required: true,
options: {
options: [
{ label: 'GLM v2', value: 'v2' },
{ label: 'GLM v1', value: 'v1' },
],
},
defaultValue: 'v2',
}),
knowledge: Property.Array({
displayName: 'Knowledge Sources',
description: 'Optional knowledge sources to ground the generation (leave empty for general generation)',
required: false,
}),
systemPrompt: Property.LongText({
displayName: 'System Prompt',
description: 'Optional system instructions for the model',
required: false,
}),
maxTokens: Property.Number({
displayName: 'Max Tokens',
description: 'Maximum number of tokens to generate (default: 1024)',
required: false,
defaultValue: 1024,
}),
temperature: Property.Number({
displayName: 'Temperature',
description: 'Sampling temperature (0.0 to 1.0, lower = more focused, higher = more creative)',
required: false,
defaultValue: 0.7,
}),
topP: Property.Number({
displayName: 'Top P',
description: 'Nucleus sampling parameter (0.0 to 1.0)',
required: false,
defaultValue: 0.9,
}),
avoidCommentary: Property.Checkbox({
displayName: 'Avoid Commentary',
description: 'Avoid providing additional conversational commentary not grounded in context',
required: false,
defaultValue: false,
}),
},
async run({ auth, propsValue }) {
const { apiKey, baseUrl } = auth.props;
const {
prompt,
model,
knowledge,
systemPrompt,
maxTokens,
temperature,
topP,
avoidCommentary,
} = propsValue;
const client = new ContextualAI({
apiKey: apiKey,
baseURL: baseUrl || 'https://api.contextual.ai/v1',
});
const messages: Array<{ role: 'user' | 'assistant'; content: string }> = [
{ role: 'user', content: prompt },
];
const response = await client.generate.create({
messages,
model,
knowledge: (knowledge || []) as string[],
system_prompt: systemPrompt,
max_new_tokens: maxTokens,
temperature,
top_p: topP,
avoid_commentary: avoidCommentary,
});
return {
response: response.response,
};
},
});

View File

@@ -0,0 +1,101 @@
import { createAction, Property } from "@activepieces/pieces-framework";
import { contextualAiAuth } from "../../index";
import { ContextualAI, toFile } from 'contextual-client';
import type { Datastore } from 'contextual-client/resources/datastores';
export const ingestDocumentAction = createAction({
auth: contextualAiAuth,
name: 'ingest_document',
displayName: 'Ingest Document',
description: 'Upload and ingest a document into a Contextual AI datastore',
props: {
datastoreId: Property.Dropdown({
auth: contextualAiAuth,
displayName: 'Datastore',
description: 'Select the datastore to upload the document to',
required: true,
refreshers: [],
options: async ({ auth }) => {
try {
if (!auth) {
return {
disabled: true,
options: [],
placeholder: 'Please connect your account first',
};
}
const { apiKey, baseUrl } = auth.props;
const client = new ContextualAI({
apiKey: apiKey,
baseURL: baseUrl || 'https://api.contextual.ai/v1',
});
const allDatastores: Datastore[] = [];
for await (const datastore of client.datastores.list()) {
allDatastores.push(datastore);
}
return {
options: allDatastores.map((datastore: Datastore) => ({
label: datastore.name,
value: datastore.id,
})),
};
} catch (error) {
return {
options: [],
error: 'Failed to fetch datastores. Please check your API key.',
};
}
},
}),
file: Property.File({
displayName: 'Document File',
description: 'The document file to upload (PDF, HTML, DOC, DOCX, PPT, PPTX)',
required: true,
}),
customMetadata: Property.Object({
displayName: 'Custom Metadata',
description: 'Optional custom metadata as key-value pairs (max 15 fields, 2KB total)',
required: false,
}),
configuration: Property.LongText({
displayName: 'Configuration Override',
description: 'Optional configuration override in JSON format for this specific document',
required: false,
}),
},
async run({ auth, propsValue }) {
const { apiKey, baseUrl } = auth.props;
const { datastoreId, file, customMetadata, configuration } = propsValue;
const client = new ContextualAI({
apiKey: apiKey,
baseURL: baseUrl || 'https://api.contextual.ai/v1',
});
let metadataString: string | undefined;
if (customMetadata && Object.keys(customMetadata).length > 0) {
metadataString = JSON.stringify({
custom_metadata: customMetadata,
});
}
const uploadableFile = await toFile(file.data, file.filename || 'uploaded-file', {
type: file.extension ? `application/${file.extension}` : 'application/octet-stream',
});
const fileData = {
file: uploadableFile,
custom_metadata: metadataString,
configuration: configuration,
};
const response = await client.datastores.documents.ingest(datastoreId, fileData);
return {
document_id: response.id,
status: 'Document ingestion started. Use the document ID to check status.',
};
},
});

View File

@@ -0,0 +1,55 @@
import { createAction, Property } from "@activepieces/pieces-framework";
import { contextualAiAuth } from "../../index";
import { ContextualAI } from 'contextual-client';
export const inviteUsersAction = createAction({
auth: contextualAiAuth,
name: 'invite_users',
displayName: 'Invite Users',
description: 'Invite new users to the Contextual AI workspace',
props: {
users: Property.Array({
displayName: 'Users to Invite',
description: 'List of users to invite',
required: true,
properties: {
email: Property.ShortText({
displayName: 'Email',
description: 'Email address of the user to invite',
required: true,
}),
},
}),
tenantShortName: Property.ShortText({
displayName: 'Tenant Short Name',
description: 'The short name of the tenant/workspace',
required: true,
}),
},
async run({ auth, propsValue }) {
const { apiKey, baseUrl } = auth.props;
const { users, tenantShortName } = propsValue;
const client = new ContextualAI({
apiKey: apiKey,
baseURL: baseUrl || 'https://api.contextual.ai/v1',
});
const newUsers = users.map((user: any) => ({
email: user.email,
agent_level_roles: ['AGENT_LEVEL_USER' as const],
}));
const response = await client.users.invite({
new_users: newUsers,
tenant_short_name: tenantShortName,
});
return {
invited_users: response.invited_user_emails,
errors: response.error_details,
total_invited: response.invited_user_emails.length,
total_errors: Object.keys(response.error_details).length,
};
},
});

View File

@@ -0,0 +1,98 @@
import { createAction, Property } from "@activepieces/pieces-framework";
import { contextualAiAuth } from "../../index";
import { ContextualAI } from 'contextual-client';
export const parseFileAction = createAction({
auth: contextualAiAuth,
name: 'parse_file',
displayName: 'Parse File',
description: 'Parse a document file into structured Markdown and/or JSON format',
props: {
file: Property.File({
displayName: 'Document File',
description: 'The document file to parse (PDF, DOC, DOCX, PPT, PPTX)',
required: true,
}),
parseMode: Property.StaticDropdown({
displayName: 'Parse Mode',
description: 'Parsing mode - basic for simple text, standard for complex documents',
required: true,
options: {
options: [
{ label: 'Basic (text-only)', value: 'basic' },
{ label: 'Standard (complex documents)', value: 'standard' },
],
},
defaultValue: 'standard',
}),
pageRange: Property.ShortText({
displayName: 'Page Range',
description: 'Optional page range to parse (e.g., "0,1,2" or "0-2,5,6")',
required: false,
}),
enableDocumentHierarchy: Property.Checkbox({
displayName: 'Enable Document Hierarchy',
description: 'Add table of contents with document structure (beta feature)',
required: false,
defaultValue: false,
}),
enableSplitTables: Property.Checkbox({
displayName: 'Enable Split Tables',
description: 'Split large tables into multiple tables with headers',
required: false,
defaultValue: false,
}),
maxSplitTableCells: Property.Number({
displayName: 'Max Split Table Cells',
description: 'Threshold for splitting large tables (only used when split tables is enabled)',
required: false,
}),
figureCaptionMode: Property.StaticDropdown({
displayName: 'Figure Caption Mode',
description: 'How thorough figure captions should be',
required: false,
options: {
options: [
{ label: 'Concise', value: 'concise' },
{ label: 'Detailed (beta)', value: 'detailed' },
],
},
defaultValue: 'concise',
}),
},
async run({ auth, propsValue }) {
const { apiKey, baseUrl } = auth.props;
const {
file,
parseMode,
pageRange,
enableDocumentHierarchy,
enableSplitTables,
maxSplitTableCells,
figureCaptionMode,
} = propsValue;
const client = new ContextualAI({
apiKey: apiKey,
baseURL: baseUrl || 'https://api.contextual.ai/v1',
});
const parseParams: any = {
raw_file: file.data,
parse_mode: parseMode,
};
if (pageRange) parseParams.page_range = pageRange;
if (enableDocumentHierarchy !== undefined) parseParams.enable_document_hierarchy = enableDocumentHierarchy;
if (enableSplitTables !== undefined) parseParams.enable_split_tables = enableSplitTables;
if (maxSplitTableCells !== undefined) parseParams.max_split_table_cells = maxSplitTableCells;
if (figureCaptionMode) parseParams.figure_caption_mode = figureCaptionMode;
const response = await client.parse.create(parseParams);
return {
job_id: response.job_id,
status: 'Parse job started. Use the job ID to check status and get results.',
};
},
});

View File

@@ -0,0 +1,96 @@
import { createAction, Property } from "@activepieces/pieces-framework";
import { contextualAiAuth } from "../../index";
import { ContextualAI } from 'contextual-client';
import type { Agent } from 'contextual-client/resources/agents';
export const queryAgentAction = createAction({
auth: contextualAiAuth,
name: 'query_agent',
displayName: 'Query Agent',
description: 'Send a message to a Contextual AI agent and get a response',
props: {
agentId: Property.Dropdown({
auth: contextualAiAuth,
displayName: 'Agent',
description: 'Select the agent to query',
required: true,
refreshers: [],
options: async ({ auth }) => {
try {
if (!auth) {
return {
disabled: true,
options: [],
placeholder: 'Please connect your account first',
};
}
const { apiKey, baseUrl } = auth.props;
const client = new ContextualAI({
apiKey: apiKey,
baseURL: baseUrl || 'https://api.contextual.ai/v1',
});
const allAgents: Agent[] = [];
for await (const agent of client.agents.list()) {
allAgents.push(agent);
}
return {
options: allAgents.map((agent: Agent) => ({
label: agent.name,
value: agent.id,
})),
};
} catch (error) {
return {
options: [],
error: 'Failed to fetch agents. Please check your API key.',
};
}
},
}),
message: Property.LongText({
displayName: 'Message',
description: 'The message to send to the agent',
required: true,
}),
conversationId: Property.ShortText({
displayName: 'Conversation ID',
description: 'Optional conversation ID to continue an existing conversation (leave empty for new conversation)',
required: false,
}),
includeRetrievalContent: Property.Checkbox({
displayName: 'Include Retrieval Content',
description: 'Include the text of retrieved contents in the response',
required: false,
defaultValue: false,
}),
},
async run({ auth, propsValue }) {
const { apiKey, baseUrl } = auth.props;
const { agentId, message, conversationId, includeRetrievalContent } = propsValue;
const client = new ContextualAI({
apiKey: apiKey,
baseURL: baseUrl || 'https://api.contextual.ai/v1',
});
const messages: Array<{ role: 'user' | 'system' | 'assistant' | 'knowledge'; content: string }> =
conversationId ? [] : [{ role: 'user' as const, content: message }];
const response = await client.agents.query.create(agentId, {
messages: messages,
conversation_id: conversationId,
include_retrieval_content_text: includeRetrievalContent,
});
return {
conversation_id: response.conversation_id,
message: response.message,
retrieval_contents: response.retrieval_contents,
attributions: response.attributions,
groundedness_scores: response.groundedness_scores,
message_id: response.message_id,
};
},
});

View File

@@ -0,0 +1,72 @@
import { createTrigger, TriggerStrategy, AppConnectionValueForAuthProperty } from '@activepieces/pieces-framework';
import { DedupeStrategy, Polling, pollingHelper } from '@activepieces/pieces-common';
import { contextualAiAuth } from '../../index';
import { ContextualAI } from 'contextual-client';
import type { Agent } from 'contextual-client/resources/agents';
const polling: Polling<AppConnectionValueForAuthProperty<typeof contextualAiAuth>, Record<string, never>> = {
strategy: DedupeStrategy.TIMEBASED,
items: async ({ auth, lastFetchEpochMS }) => {
const { apiKey, baseUrl } = auth.props;
const client = new ContextualAI({
apiKey: apiKey,
baseURL: baseUrl || 'https://api.contextual.ai/v1',
});
const allAgents: Agent[] = [];
for await (const agent of client.agents.list()) {
allAgents.push(agent);
}
const newAgents = lastFetchEpochMS
? allAgents.filter(agent => {
return true;
})
: allAgents;
const items = newAgents.map((agent: Agent) => ({
epochMilliSeconds: Date.now(),
data: {
id: agent.id,
name: agent.name,
description: agent.description,
},
}));
return items;
},
};
export const newAgentTrigger = createTrigger({
auth: contextualAiAuth,
name: 'new_agent',
displayName: 'New Agent',
description: 'Triggers when a new Contextual AI agent is created',
props: {},
type: TriggerStrategy.POLLING,
sampleData: {
id: 'agent_123',
name: 'Sample Agent',
description: 'A sample agent for testing',
},
async onEnable(context) {
await pollingHelper.onEnable(polling, {
auth: context.auth,
store: context.store,
propsValue: context.propsValue,
});
},
async onDisable(context) {
await pollingHelper.onDisable(polling, {
auth: context.auth,
store: context.store,
propsValue: context.propsValue,
});
},
async test(context) {
return await pollingHelper.test(polling, context);
},
async run(context) {
return await pollingHelper.poll(polling, context);
},
});