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,30 @@
|
||||
import { createPiece } from "@activepieces/pieces-framework";
|
||||
import { PieceCategory } from "@activepieces/shared";
|
||||
import { lettaAuth } from "./lib/common/auth";
|
||||
import { createAgentFromTemplate } from "./lib/actions/create-agent-from-template";
|
||||
import { createIdentity } from "./lib/actions/create-identity";
|
||||
import { sendMessageToAgent } from "./lib/actions/send-message-to-agent";
|
||||
import { getIdentities } from "./lib/actions/get-identities";
|
||||
import { newAgent } from "./lib/triggers/new-agent";
|
||||
import { newMessage } from "./lib/triggers/new-message";
|
||||
|
||||
export const letta = createPiece({
|
||||
displayName: "Letta",
|
||||
description: "Letta is the platform for building stateful agents: open AI with advanced memory that can learn and self-improve over time.",
|
||||
auth: lettaAuth,
|
||||
minimumSupportedRelease: '0.36.1',
|
||||
logoUrl: "https://cdn.activepieces.com/pieces/letta.png",
|
||||
categories: [PieceCategory.ARTIFICIAL_INTELLIGENCE],
|
||||
authors: ["onyedikachi-david"],
|
||||
actions: [
|
||||
createAgentFromTemplate,
|
||||
createIdentity,
|
||||
sendMessageToAgent,
|
||||
getIdentities,
|
||||
],
|
||||
triggers: [
|
||||
newAgent,
|
||||
newMessage,
|
||||
],
|
||||
});
|
||||
|
||||
@@ -0,0 +1,144 @@
|
||||
import { createAction, Property } from '@activepieces/pieces-framework';
|
||||
import { lettaAuth } from '../common/auth';
|
||||
import { getLettaClient } from '../common/client';
|
||||
import { identityIdsDropdown } from '../common/props';
|
||||
import type {
|
||||
AgentCreateParams,
|
||||
AgentCreateResponse,
|
||||
} from '../common/types';
|
||||
|
||||
export const createAgentFromTemplate = createAction({
|
||||
auth: lettaAuth,
|
||||
name: 'createAgentFromTemplate',
|
||||
displayName: 'Create Agent From Template',
|
||||
description: 'Creates an agent from a template',
|
||||
props: {
|
||||
templateVersion: Property.ShortText({
|
||||
displayName: 'Template Version',
|
||||
description: 'The template version ID to create the agent from',
|
||||
required: true,
|
||||
}),
|
||||
agentName: Property.ShortText({
|
||||
displayName: 'Agent Name',
|
||||
description: 'The name of the agent (optional, a random name will be assigned if not provided)',
|
||||
required: false,
|
||||
}),
|
||||
identityIds: identityIdsDropdown,
|
||||
tags: Property.Array({
|
||||
displayName: 'Tags',
|
||||
description: 'Tags to assign to the agent',
|
||||
required: false,
|
||||
properties: {
|
||||
tag: Property.ShortText({
|
||||
displayName: 'Tag',
|
||||
description: 'Tag name',
|
||||
required: true,
|
||||
}),
|
||||
},
|
||||
}),
|
||||
memoryVariables: Property.Object({
|
||||
displayName: 'Memory Variables',
|
||||
description: 'Memory variables to assign to the agent (key-value pairs)',
|
||||
required: false,
|
||||
}),
|
||||
toolVariables: Property.Object({
|
||||
displayName: 'Tool Variables',
|
||||
description: 'Tool variables to assign to the agent (key-value pairs)',
|
||||
required: false,
|
||||
}),
|
||||
initialMessageSequence: Property.Array({
|
||||
displayName: 'Initial Message Sequence',
|
||||
description: 'Initial sequence of messages to start the agent with',
|
||||
required: false,
|
||||
properties: {
|
||||
content: Property.LongText({
|
||||
displayName: 'Content',
|
||||
description: 'Message content',
|
||||
required: true,
|
||||
}),
|
||||
role: Property.StaticDropdown({
|
||||
displayName: 'Role',
|
||||
description: 'Message role',
|
||||
required: true,
|
||||
options: {
|
||||
disabled: false,
|
||||
options: [
|
||||
{ label: 'User', value: 'user' },
|
||||
{ label: 'System', value: 'system' },
|
||||
{ label: 'Assistant', value: 'assistant' },
|
||||
],
|
||||
},
|
||||
}),
|
||||
name: Property.ShortText({
|
||||
displayName: 'Name',
|
||||
description: 'Optional name of the participant',
|
||||
required: false,
|
||||
}),
|
||||
},
|
||||
}),
|
||||
},
|
||||
async run(context) {
|
||||
const {
|
||||
templateVersion,
|
||||
agentName,
|
||||
identityIds,
|
||||
tags,
|
||||
memoryVariables,
|
||||
toolVariables,
|
||||
initialMessageSequence,
|
||||
} = context.propsValue;
|
||||
|
||||
const client = getLettaClient(context.auth.props);
|
||||
|
||||
const body: AgentCreateParams = {};
|
||||
|
||||
if (agentName) {
|
||||
body.agent_name = agentName;
|
||||
}
|
||||
|
||||
if (identityIds && identityIds.length > 0) {
|
||||
body.identity_ids = identityIds;
|
||||
}
|
||||
|
||||
if (tags && tags.length > 0) {
|
||||
body.tags = tags.map((tagObj: any) => tagObj.tag).filter(Boolean);
|
||||
}
|
||||
|
||||
if (memoryVariables && Object.keys(memoryVariables).length > 0) {
|
||||
const memoryVars: Record<string, string> = {};
|
||||
for (const [key, value] of Object.entries(memoryVariables)) {
|
||||
memoryVars[key] = String(value);
|
||||
}
|
||||
body.memory_variables = memoryVars;
|
||||
}
|
||||
|
||||
if (toolVariables && Object.keys(toolVariables).length > 0) {
|
||||
const toolVars: Record<string, string> = {};
|
||||
for (const [key, value] of Object.entries(toolVariables)) {
|
||||
toolVars[key] = String(value);
|
||||
}
|
||||
body.tool_variables = toolVars;
|
||||
}
|
||||
|
||||
if (initialMessageSequence && initialMessageSequence.length > 0) {
|
||||
body.initial_message_sequence = initialMessageSequence.map((msg: any) => ({
|
||||
content: msg.content,
|
||||
role: msg.role,
|
||||
name: msg.name || undefined,
|
||||
}));
|
||||
}
|
||||
|
||||
const response: AgentCreateResponse = await client.templates.agents.create(
|
||||
templateVersion,
|
||||
body
|
||||
);
|
||||
|
||||
return {
|
||||
agentIds: response.agent_ids,
|
||||
deploymentId: response.deployment_id,
|
||||
groupId: response.group_id,
|
||||
success: true,
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
@@ -0,0 +1,142 @@
|
||||
import { createAction, Property } from '@activepieces/pieces-framework';
|
||||
import { lettaAuth } from '../common/auth';
|
||||
import { getLettaClient } from '../common/client';
|
||||
import type {
|
||||
IdentityCreateParams,
|
||||
Identity,
|
||||
IdentityProperty,
|
||||
} from '../common/types';
|
||||
|
||||
export const createIdentity = createAction({
|
||||
auth: lettaAuth,
|
||||
name: 'createIdentity',
|
||||
displayName: 'Create Identity',
|
||||
description: 'Creates a Letta identity',
|
||||
props: {
|
||||
identifierKey: Property.ShortText({
|
||||
displayName: 'Identifier Key',
|
||||
description: 'External, user-generated identifier key of the identity',
|
||||
required: true,
|
||||
}),
|
||||
name: Property.ShortText({
|
||||
displayName: 'Name',
|
||||
description: 'The name of the identity',
|
||||
required: true,
|
||||
}),
|
||||
identityType: Property.StaticDropdown({
|
||||
displayName: 'Identity Type',
|
||||
description: 'The type of the identity',
|
||||
required: true,
|
||||
options: {
|
||||
disabled: false,
|
||||
options: [
|
||||
{ label: 'Organization', value: 'org' },
|
||||
{ label: 'User', value: 'user' },
|
||||
{ label: 'Other', value: 'other' },
|
||||
],
|
||||
},
|
||||
}),
|
||||
projectId: Property.ShortText({
|
||||
displayName: 'Project ID',
|
||||
description: 'The project ID of the identity (optional)',
|
||||
required: false,
|
||||
}),
|
||||
properties: Property.Array({
|
||||
displayName: 'Properties',
|
||||
description: 'List of properties associated with the identity',
|
||||
required: false,
|
||||
properties: {
|
||||
key: Property.ShortText({
|
||||
displayName: 'Key',
|
||||
description: 'Property key',
|
||||
required: true,
|
||||
}),
|
||||
type: Property.StaticDropdown({
|
||||
displayName: 'Type',
|
||||
description: 'Property type',
|
||||
required: true,
|
||||
options: {
|
||||
disabled: false,
|
||||
options: [
|
||||
{ label: 'String', value: 'string' },
|
||||
{ label: 'Number', value: 'number' },
|
||||
{ label: 'Boolean', value: 'boolean' },
|
||||
{ label: 'JSON', value: 'json' },
|
||||
],
|
||||
},
|
||||
}),
|
||||
value: Property.ShortText({
|
||||
displayName: 'Value',
|
||||
description: 'Property value (for JSON type, enter valid JSON string)',
|
||||
required: true,
|
||||
}),
|
||||
},
|
||||
}),
|
||||
},
|
||||
async run(context) {
|
||||
const {
|
||||
identifierKey,
|
||||
name,
|
||||
identityType,
|
||||
projectId,
|
||||
properties,
|
||||
} = context.propsValue;
|
||||
|
||||
const client = getLettaClient(context.auth.props);
|
||||
|
||||
const body: IdentityCreateParams = {
|
||||
identifier_key: identifierKey,
|
||||
name: name,
|
||||
identity_type: identityType as 'org' | 'user' | 'other',
|
||||
};
|
||||
|
||||
if (projectId) {
|
||||
body.project_id = projectId;
|
||||
}
|
||||
|
||||
if (properties && properties.length > 0) {
|
||||
body.properties = properties.map((prop: any) => {
|
||||
let parsedValue: string | number | boolean | { [key: string]: unknown };
|
||||
|
||||
switch (prop.type) {
|
||||
case 'number':
|
||||
parsedValue = Number(prop.value);
|
||||
break;
|
||||
case 'boolean':
|
||||
parsedValue = prop.value === 'true' || prop.value === true;
|
||||
break;
|
||||
case 'json':
|
||||
try {
|
||||
parsedValue = JSON.parse(prop.value);
|
||||
} catch (e) {
|
||||
throw new Error(`Invalid JSON in property "${prop.key}": ${e instanceof Error ? e.message : 'Unknown error'}`);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
parsedValue = String(prop.value);
|
||||
}
|
||||
|
||||
const identityProperty: IdentityProperty = {
|
||||
key: prop.key,
|
||||
type: prop.type as 'string' | 'number' | 'boolean' | 'json',
|
||||
value: parsedValue,
|
||||
};
|
||||
|
||||
return identityProperty;
|
||||
});
|
||||
}
|
||||
|
||||
const response: Identity = await client.identities.create(body);
|
||||
|
||||
return {
|
||||
id: response.id,
|
||||
identifierKey: response.identifier_key,
|
||||
name: response.name,
|
||||
identityType: response.identity_type,
|
||||
projectId: response.project_id,
|
||||
properties: response.properties,
|
||||
success: true,
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
import { createAction, Property } from '@activepieces/pieces-framework';
|
||||
import { lettaAuth } from '../common/auth';
|
||||
import { getLettaClient } from '../common/client';
|
||||
import type {
|
||||
IdentityListParams,
|
||||
Identity,
|
||||
} from '../common/types';
|
||||
|
||||
export const getIdentities = createAction({
|
||||
auth: lettaAuth,
|
||||
name: 'getIdentities',
|
||||
displayName: 'Get Identities',
|
||||
description: 'Searches for identities in your Letta Project',
|
||||
props: {
|
||||
identifierKey: Property.ShortText({
|
||||
displayName: 'Identifier Key',
|
||||
description: 'Filter by identifier key',
|
||||
required: false,
|
||||
}),
|
||||
name: Property.ShortText({
|
||||
displayName: 'Name',
|
||||
description: 'Filter by identity name',
|
||||
required: false,
|
||||
}),
|
||||
identityType: Property.StaticDropdown({
|
||||
displayName: 'Identity Type',
|
||||
description: 'Filter by identity type',
|
||||
required: false,
|
||||
options: {
|
||||
disabled: false,
|
||||
options: [
|
||||
{ label: 'Organization', value: 'org' },
|
||||
{ label: 'User', value: 'user' },
|
||||
{ label: 'Other', value: 'other' },
|
||||
],
|
||||
},
|
||||
}),
|
||||
projectId: Property.ShortText({
|
||||
displayName: 'Project ID',
|
||||
description: 'Filter by project ID',
|
||||
required: false,
|
||||
}),
|
||||
limit: Property.Number({
|
||||
displayName: 'Limit',
|
||||
description: 'Maximum number of identities to return',
|
||||
required: false,
|
||||
defaultValue: 100,
|
||||
}),
|
||||
},
|
||||
async run(context) {
|
||||
const {
|
||||
identifierKey,
|
||||
name,
|
||||
identityType,
|
||||
projectId,
|
||||
limit,
|
||||
} = context.propsValue;
|
||||
|
||||
const client = getLettaClient(context.auth.props);
|
||||
|
||||
const query: IdentityListParams = {};
|
||||
|
||||
if (identifierKey) {
|
||||
query.identifier_key = identifierKey;
|
||||
}
|
||||
|
||||
if (name) {
|
||||
query.name = name;
|
||||
}
|
||||
|
||||
if (identityType) {
|
||||
query.identity_type = identityType as 'org' | 'user' | 'other';
|
||||
}
|
||||
|
||||
if (projectId) {
|
||||
query.project_id = projectId;
|
||||
}
|
||||
|
||||
if (limit !== undefined && limit !== null) {
|
||||
query.limit = limit;
|
||||
}
|
||||
|
||||
const identitiesPage = await client.identities.list(query);
|
||||
|
||||
const identities: Identity[] = [];
|
||||
for await (const identity of identitiesPage) {
|
||||
identities.push(identity);
|
||||
}
|
||||
|
||||
return {
|
||||
identities,
|
||||
count: identities.length,
|
||||
success: true,
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
import { createAction, Property } from '@activepieces/pieces-framework';
|
||||
import { lettaAuth } from '../common/auth';
|
||||
import { getLettaClient } from '../common/client';
|
||||
import { agentIdDropdown } from '../common/props';
|
||||
import type {
|
||||
MessageCreateParamsNonStreaming,
|
||||
LettaResponse,
|
||||
} from '../common/types';
|
||||
|
||||
export const sendMessageToAgent = createAction({
|
||||
auth: lettaAuth,
|
||||
name: 'sendMessageToAgent',
|
||||
displayName: 'Send Message to Agent',
|
||||
description: 'Send message to an agent',
|
||||
props: {
|
||||
agentId: agentIdDropdown,
|
||||
input: Property.LongText({
|
||||
displayName: 'Message',
|
||||
description: 'The message content to send to the agent',
|
||||
required: true,
|
||||
}),
|
||||
maxSteps: Property.Number({
|
||||
displayName: 'Max Steps',
|
||||
description: 'Maximum number of steps the agent should take to process the request',
|
||||
required: false,
|
||||
}),
|
||||
},
|
||||
async run(context) {
|
||||
const {
|
||||
agentId,
|
||||
input,
|
||||
maxSteps,
|
||||
} = context.propsValue;
|
||||
|
||||
const client = getLettaClient(context.auth.props);
|
||||
|
||||
const body: MessageCreateParamsNonStreaming = {
|
||||
streaming: false,
|
||||
input: input,
|
||||
};
|
||||
|
||||
if (maxSteps !== undefined && maxSteps !== null) {
|
||||
body.max_steps = maxSteps;
|
||||
}
|
||||
|
||||
const response: LettaResponse = await client.agents.messages.create(
|
||||
agentId,
|
||||
body
|
||||
);
|
||||
|
||||
return {
|
||||
messages: response.messages,
|
||||
stopReason: response.stop_reason,
|
||||
success: true,
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
import { PieceAuth, Property } from '@activepieces/pieces-framework';
|
||||
import {
|
||||
Letta,
|
||||
AuthenticationError,
|
||||
PermissionDeniedError,
|
||||
APIConnectionError,
|
||||
APIConnectionTimeoutError,
|
||||
LettaError,
|
||||
} from '@letta-ai/letta-client';
|
||||
import type {
|
||||
ClientOptions,
|
||||
AgentListParams,
|
||||
} from './types';
|
||||
|
||||
const markdown = `
|
||||
To authenticate with Letta:
|
||||
|
||||
1. **For Letta Cloud**: Get your API key from [Letta Cloud](https://cloud.letta.ai)
|
||||
2. **For Self-hosted**: Leave API key empty and provide your server URL (e.g., http://localhost:8283)
|
||||
`;
|
||||
|
||||
export const lettaAuth = PieceAuth.CustomAuth({
|
||||
description: markdown,
|
||||
required: true,
|
||||
props: {
|
||||
apiKey: PieceAuth.SecretText({
|
||||
displayName: 'API Key',
|
||||
description: 'Your Letta API key (required for Letta Cloud, leave empty for self-hosted)',
|
||||
required: false,
|
||||
}),
|
||||
baseUrl: Property.ShortText({
|
||||
displayName: 'Base URL',
|
||||
description: 'Server URL for self-hosted instances (e.g., http://localhost:8283). Leave empty to use Letta Cloud.',
|
||||
required: false,
|
||||
}),
|
||||
},
|
||||
validate: async ({ auth }) => {
|
||||
const { apiKey, baseUrl } = auth;
|
||||
|
||||
if (!apiKey && !baseUrl) {
|
||||
return {
|
||||
valid: false,
|
||||
error: 'Please provide either an API key (for Letta Cloud) or a base URL (for self-hosted).',
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
const clientConfig: ClientOptions = {};
|
||||
if (apiKey) {
|
||||
clientConfig.apiKey = apiKey;
|
||||
}
|
||||
if (baseUrl) {
|
||||
clientConfig.baseURL = baseUrl;
|
||||
}
|
||||
|
||||
const client = new Letta(clientConfig);
|
||||
|
||||
if (apiKey) {
|
||||
const listParams: AgentListParams = { limit: 1 };
|
||||
await client.agents.list(listParams);
|
||||
} else {
|
||||
await client.health();
|
||||
}
|
||||
|
||||
return {
|
||||
valid: true,
|
||||
};
|
||||
} catch (error: unknown) {
|
||||
if (error instanceof AuthenticationError) {
|
||||
return {
|
||||
valid: false,
|
||||
error: 'Invalid API key. Please check your credentials.',
|
||||
};
|
||||
}
|
||||
if (error instanceof PermissionDeniedError) {
|
||||
return {
|
||||
valid: false,
|
||||
error: 'Permission denied. Please check your API key permissions.',
|
||||
};
|
||||
}
|
||||
if (error instanceof APIConnectionError || error instanceof APIConnectionTimeoutError) {
|
||||
return {
|
||||
valid: false,
|
||||
error: 'Connection failed. Please check your base URL and ensure the server is running.',
|
||||
};
|
||||
}
|
||||
if (error instanceof LettaError) {
|
||||
return {
|
||||
valid: false,
|
||||
error: `Authentication failed: ${error instanceof Error ? error.message : 'Unknown error'}. Please verify your credentials.`,
|
||||
};
|
||||
}
|
||||
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
||||
return {
|
||||
valid: false,
|
||||
error: `Authentication failed: ${errorMessage}. Please verify your credentials.`,
|
||||
};
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
export type LettaAuthType = {
|
||||
apiKey?: string;
|
||||
baseUrl?: string;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
import { Letta } from '@letta-ai/letta-client';
|
||||
import type { ClientOptions } from './types';
|
||||
import { LettaAuthType } from './auth';
|
||||
|
||||
|
||||
export function getLettaClient(auth: LettaAuthType): Letta {
|
||||
const clientConfig: ClientOptions = {};
|
||||
|
||||
if (auth.apiKey) {
|
||||
clientConfig.apiKey = auth.apiKey;
|
||||
}
|
||||
|
||||
if (auth.baseUrl) {
|
||||
clientConfig.baseURL = auth.baseUrl;
|
||||
}
|
||||
|
||||
return new Letta(clientConfig);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
import { Property } from '@activepieces/pieces-framework';
|
||||
import { getLettaClient } from './client';
|
||||
import { lettaAuth, type LettaAuthType } from './auth';
|
||||
|
||||
|
||||
export const identityIdsDropdown = Property.MultiSelectDropdown({
|
||||
displayName: 'Identities',
|
||||
description: 'Select identities to assign to the agent',
|
||||
auth: lettaAuth,
|
||||
required: false,
|
||||
refreshers: [],
|
||||
options: async ({ auth }) => {
|
||||
if (!auth) {
|
||||
return {
|
||||
disabled: true,
|
||||
options: [],
|
||||
placeholder: 'Please connect your account first',
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
const client = getLettaClient(auth as LettaAuthType);
|
||||
const identitiesPage = await client.identities.list();
|
||||
|
||||
const identities: Array<{ label: string; value: string }> = [];
|
||||
for await (const identity of identitiesPage) {
|
||||
identities.push({
|
||||
label: identity.name || identity.identifier_key || identity.id,
|
||||
value: identity.id,
|
||||
});
|
||||
}
|
||||
|
||||
if (identities.length === 0) {
|
||||
return {
|
||||
disabled: false,
|
||||
options: [],
|
||||
placeholder: 'No identities found',
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
disabled: false,
|
||||
options: identities,
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
disabled: true,
|
||||
placeholder: 'Failed to load identities. Please check your connection.',
|
||||
options: [],
|
||||
};
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
export const agentIdDropdown = Property.Dropdown({
|
||||
auth: lettaAuth,
|
||||
displayName: 'Agent',
|
||||
description: 'Select the agent to send a message to',
|
||||
required: true,
|
||||
refreshers: [],
|
||||
options: async ({ auth }) => {
|
||||
if (!auth) {
|
||||
return {
|
||||
disabled: true,
|
||||
options: [],
|
||||
placeholder: 'Please connect your account first',
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
const client = getLettaClient(auth as LettaAuthType);
|
||||
const agentsPage = await client.agents.list();
|
||||
|
||||
const agents: Array<{ label: string; value: string }> = [];
|
||||
for await (const agent of agentsPage) {
|
||||
agents.push({
|
||||
label: agent.name || agent.id,
|
||||
value: agent.id,
|
||||
});
|
||||
}
|
||||
|
||||
if (agents.length === 0) {
|
||||
return {
|
||||
disabled: false,
|
||||
options: [],
|
||||
placeholder: 'No agents found',
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
disabled: false,
|
||||
options: agents,
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
disabled: true,
|
||||
placeholder: 'Failed to load agents. Please check your connection.',
|
||||
options: [],
|
||||
};
|
||||
}
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,27 @@
|
||||
export type { ClientOptions } from '@letta-ai/letta-client';
|
||||
|
||||
export type {
|
||||
AgentState,
|
||||
AgentListParams,
|
||||
} from '@letta-ai/letta-client/resources/agents/agents';
|
||||
|
||||
export type {
|
||||
AgentCreateParams,
|
||||
AgentCreateResponse,
|
||||
} from '@letta-ai/letta-client/resources/templates/agents';
|
||||
|
||||
export type {
|
||||
Identity,
|
||||
IdentityCreateParams,
|
||||
IdentityListParams,
|
||||
IdentityProperty,
|
||||
IdentityType,
|
||||
} from '@letta-ai/letta-client/resources/identities/identities';
|
||||
|
||||
export type {
|
||||
Message,
|
||||
MessageCreateParamsNonStreaming,
|
||||
MessageListParams,
|
||||
ToolCallMessage,
|
||||
LettaResponse,
|
||||
} from '@letta-ai/letta-client/resources/agents/messages';
|
||||
@@ -0,0 +1,79 @@
|
||||
import {
|
||||
DedupeStrategy,
|
||||
Polling,
|
||||
pollingHelper,
|
||||
} from '@activepieces/pieces-common';
|
||||
import {
|
||||
TriggerStrategy,
|
||||
createTrigger,
|
||||
AppConnectionValueForAuthProperty,
|
||||
} from '@activepieces/pieces-framework';
|
||||
import { lettaAuth } from '../common/auth';
|
||||
import { getLettaClient } from '../common/client';
|
||||
import type {
|
||||
AgentState,
|
||||
AgentListParams,
|
||||
} from '../common/types';
|
||||
|
||||
const polling: Polling<
|
||||
AppConnectionValueForAuthProperty<typeof lettaAuth>,
|
||||
Record<string, never>
|
||||
> = {
|
||||
strategy: DedupeStrategy.TIMEBASED,
|
||||
items: async ({ auth, lastFetchEpochMS }) => {
|
||||
const client = getLettaClient(auth.props);
|
||||
|
||||
const query: AgentListParams = {
|
||||
limit: 100,
|
||||
};
|
||||
|
||||
const agentsPage = await client.agents.list(query);
|
||||
|
||||
const agents: AgentState[] = [];
|
||||
for await (const agent of agentsPage) {
|
||||
if (agent.created_at) {
|
||||
const createdAtEpoch = Date.parse(agent.created_at);
|
||||
|
||||
if (createdAtEpoch > lastFetchEpochMS) {
|
||||
agents.push(agent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return agents
|
||||
.sort((a, b) => {
|
||||
const aTime = a.created_at ? Date.parse(a.created_at) : 0;
|
||||
const bTime = b.created_at ? Date.parse(b.created_at) : 0;
|
||||
return bTime - aTime;
|
||||
})
|
||||
.map((agent) => ({
|
||||
epochMilliSeconds: agent.created_at
|
||||
? Date.parse(agent.created_at)
|
||||
: Date.now(),
|
||||
data: agent,
|
||||
}));
|
||||
},
|
||||
};
|
||||
|
||||
export const newAgent = createTrigger({
|
||||
auth: lettaAuth,
|
||||
name: 'newAgent',
|
||||
displayName: 'New Agent',
|
||||
description: 'Triggers when a new agent is created',
|
||||
type: TriggerStrategy.POLLING,
|
||||
props: {},
|
||||
sampleData: {},
|
||||
async onEnable(context) {
|
||||
await pollingHelper.onEnable(polling, context);
|
||||
},
|
||||
async onDisable(context) {
|
||||
await pollingHelper.onDisable(polling, context);
|
||||
},
|
||||
async run(context) {
|
||||
return await pollingHelper.poll(polling, context);
|
||||
},
|
||||
async test(context) {
|
||||
return await pollingHelper.test(polling, context);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
import {
|
||||
DedupeStrategy,
|
||||
Polling,
|
||||
pollingHelper,
|
||||
} from '@activepieces/pieces-common';
|
||||
import {
|
||||
TriggerStrategy,
|
||||
createTrigger,
|
||||
PiecePropValueSchema,
|
||||
AppConnectionValueForAuthProperty,
|
||||
} from '@activepieces/pieces-framework';
|
||||
import { lettaAuth } from '../common/auth';
|
||||
import { getLettaClient } from '../common/client';
|
||||
import { agentIdDropdown } from '../common/props';
|
||||
import type {
|
||||
Message,
|
||||
MessageListParams,
|
||||
ToolCallMessage,
|
||||
} from '../common/types';
|
||||
|
||||
const polling: Polling<
|
||||
AppConnectionValueForAuthProperty<typeof lettaAuth>,
|
||||
{ agentId: string }
|
||||
> = {
|
||||
strategy: DedupeStrategy.TIMEBASED,
|
||||
items: async ({ auth, propsValue, lastFetchEpochMS }) => {
|
||||
const { agentId } = propsValue;
|
||||
const client = getLettaClient(auth.props);
|
||||
|
||||
const query: MessageListParams = {
|
||||
limit: 100,
|
||||
};
|
||||
|
||||
const messagesPage = await client.agents.messages.list(agentId, query);
|
||||
|
||||
const sendMessageToolCalls: ToolCallMessage[] = [];
|
||||
for await (const message of messagesPage) {
|
||||
if (message.message_type === 'tool_call_message') {
|
||||
const toolCallMessage = message as ToolCallMessage;
|
||||
|
||||
let hasSendMessage = false;
|
||||
|
||||
if (toolCallMessage.tool_calls) {
|
||||
if (Array.isArray(toolCallMessage.tool_calls)) {
|
||||
hasSendMessage = toolCallMessage.tool_calls.some(
|
||||
(tc) => tc.name === 'send_message'
|
||||
);
|
||||
} else {
|
||||
const delta = toolCallMessage.tool_calls;
|
||||
hasSendMessage = delta.name === 'send_message';
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasSendMessage && toolCallMessage.tool_call) {
|
||||
const toolCall = toolCallMessage.tool_call;
|
||||
if ('name' in toolCall && toolCall.name) {
|
||||
hasSendMessage = toolCall.name === 'send_message';
|
||||
}
|
||||
}
|
||||
|
||||
if (hasSendMessage && toolCallMessage.date) {
|
||||
const messageDateEpoch = Date.parse(toolCallMessage.date);
|
||||
|
||||
if (messageDateEpoch > lastFetchEpochMS) {
|
||||
sendMessageToolCalls.push(toolCallMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return sendMessageToolCalls
|
||||
.sort((a, b) => {
|
||||
const aTime = a.date ? Date.parse(a.date) : 0;
|
||||
const bTime = b.date ? Date.parse(b.date) : 0;
|
||||
return bTime - aTime;
|
||||
})
|
||||
.map((message) => ({
|
||||
epochMilliSeconds: message.date ? Date.parse(message.date) : Date.now(),
|
||||
data: message,
|
||||
}));
|
||||
},
|
||||
};
|
||||
|
||||
export const newMessage = createTrigger({
|
||||
auth: lettaAuth,
|
||||
name: 'newMessage',
|
||||
displayName: 'New Message',
|
||||
description: 'Triggers when an agent uses send_message',
|
||||
type: TriggerStrategy.POLLING,
|
||||
props: {
|
||||
agentId: agentIdDropdown,
|
||||
},
|
||||
sampleData: {},
|
||||
async onEnable(context) {
|
||||
await pollingHelper.onEnable(polling, context);
|
||||
},
|
||||
async onDisable(context) {
|
||||
await pollingHelper.onDisable(polling, context);
|
||||
},
|
||||
async run(context) {
|
||||
return await pollingHelper.poll(polling, context);
|
||||
},
|
||||
async test(context) {
|
||||
return await pollingHelper.test(polling, context);
|
||||
},
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user