@picahq/ai
Version:
Pica AI SDK for Vercel AI SDK integration
623 lines (620 loc) • 32.2 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Pica = void 0;
const axios_1 = __importDefault(require("axios"));
const zod_1 = require("zod");
const ai_1 = require("ai");
const form_data_1 = __importDefault(require("form-data"));
const defaultSystem_1 = require("./prompts/defaultSystem");
const defaultSystemWithAuthkit_1 = require("./prompts/defaultSystemWithAuthkit");
const knowledgeAgentSystem_1 = require("./prompts/knowledgeAgentSystem");
const knowledgeAgentWithAuthkitSystem_1 = require("./prompts/knowledgeAgentWithAuthkitSystem");
const utils_1 = require("./utils");
class Pica {
constructor(secret, options) {
this.baseUrl = "https://api.picaos.com";
this.secret = secret;
this.connections = [];
this.connectionDefinitions = [];
this.systemPromptValue = "Loading connections...";
this.identity = options === null || options === void 0 ? void 0 : options.identity;
this.identityType = options === null || options === void 0 ? void 0 : options.identityType;
this.useAuthkit = (options === null || options === void 0 ? void 0 : options.authkit) || false;
this.useKnowledgeAgent = (options === null || options === void 0 ? void 0 : options.knowledgeAgent) || false;
this.knowledgeAgentConfig = (options === null || options === void 0 ? void 0 : options.knowledgeAgentConfig) || {
includeEnvironmentVariables: true
};
this.options = options;
if (options === null || options === void 0 ? void 0 : options.serverUrl) {
this.baseUrl = options.serverUrl;
}
this.getConnectionUrl = `${this.baseUrl}/v1/vault/connections`;
this.availableActionsUrl = `${this.baseUrl}/v1/knowledge`;
this.getConnectionDefinitionsUrl = `${this.baseUrl}/v1/available-connectors`;
this.initialized = this.initialize()
.then(() => {
var _a, _b, _c;
let filteredConnections = this.connections.filter((conn) => conn.active);
if (!((_a = options === null || options === void 0 ? void 0 : options.connectors) === null || _a === void 0 ? void 0 : _a.length)) {
filteredConnections = [];
}
const connectionsInfo = filteredConnections.length > 0
? '\t* ' + filteredConnections
.map((conn) => `${conn.platform} - Key: ${conn.key}`)
.join('\n\t* ')
: 'No connections available';
const availablePlatformsInfo = this.connectionDefinitions.map((def) => `\n\t* ${def.platform} (${def.name})`).join('');
if ((options === null || options === void 0 ? void 0 : options.knowledgeAgentConfig) && !this.useKnowledgeAgent) {
throw new Error("Cannot provide Knowledge Agent configuration when Knowledge Agent is disabled. Please set useKnowledgeAgent to true if you want to use the Knowledge Agent.");
}
// Choose the appropriate system prompt based on options
if (this.useAuthkit && this.useKnowledgeAgent) {
this.systemPromptValue = (0, knowledgeAgentWithAuthkitSystem_1.getKnowledgeAgentWithAuthkitSystemPrompt)(connectionsInfo, availablePlatformsInfo, (_b = this.knowledgeAgentConfig) === null || _b === void 0 ? void 0 : _b.includeEnvironmentVariables);
}
else if (this.useAuthkit) {
this.systemPromptValue = (0, defaultSystemWithAuthkit_1.getDefaultSystemWithAuthkitPrompt)(connectionsInfo, availablePlatformsInfo);
}
else if (this.useKnowledgeAgent) {
this.systemPromptValue = (0, knowledgeAgentSystem_1.getKnowledgeAgentSystemPrompt)(connectionsInfo, availablePlatformsInfo, (_c = this.knowledgeAgentConfig) === null || _c === void 0 ? void 0 : _c.includeEnvironmentVariables);
}
else {
this.systemPromptValue = (0, defaultSystem_1.getDefaultSystemPrompt)(connectionsInfo, availablePlatformsInfo);
}
})
.catch(error => {
console.error('Error during initialization:', error);
this.systemPromptValue = "Error loading connections";
});
}
async generateSystemPrompt(userSystemPrompt) {
await this.waitForInitialization();
const now = new Date();
const prompt = `${userSystemPrompt ? userSystemPrompt + '\n\n' : ''}=== PICA: INTEGRATION ASSISTANT ===\n
Everything below is for Pica (picaos.com), your integration assistant that can instantly connect your AI agents to 100+ APIs.\n
Current Time: ${now.toLocaleString('en-US', { timeZone: 'GMT' })} (GMT)
--- Tools Information ---
${this.system.trim()}
`;
return prompt;
}
async initialize() {
var _a;
await Promise.all([
this.initializeConnections(undefined, (_a = this.options) === null || _a === void 0 ? void 0 : _a.connectors),
this.initializeConnectionDefinitions(),
]);
}
async waitForInitialization() {
await this.initialized;
return this.system;
}
async initializeConnections(platform, connectionKeys) {
try {
if (!connectionKeys || connectionKeys.length === 0) {
this.connections = [];
return;
}
const headers = this.generateHeaders();
let baseUrl = this.getConnectionUrl;
let hasQueryParam = false;
if (platform) {
baseUrl += `?platform=${platform}`;
hasQueryParam = true;
}
if (!connectionKeys.includes("*")) {
baseUrl += hasQueryParam ? `&key=${connectionKeys.join(',')}` : `?key=${connectionKeys.join(',')}`;
hasQueryParam = true;
}
if (this.identity) {
baseUrl += hasQueryParam ? `&identity=${encodeURIComponent(this.identity)}` : `?identity=${encodeURIComponent(this.identity)}`;
hasQueryParam = true;
}
if (this.identityType) {
baseUrl += hasQueryParam ? `&identityType=${encodeURIComponent(this.identityType)}` : `?identityType=${encodeURIComponent(this.identityType)}`;
hasQueryParam = true;
}
const fetchPage = (skip, limit) => axios_1.default.get(`${baseUrl}${hasQueryParam ? '&' : '?'}limit=${limit}&skip=${skip}`, { headers }).then(response => response.data);
this.connections = await (0, utils_1.paginateResults)(fetchPage);
}
catch (error) {
console.error("Failed to initialize connections:", error);
this.connections = [];
}
}
async initializeConnectionDefinitions() {
try {
const headers = this.generateHeaders();
let url = this.getConnectionDefinitionsUrl;
let hasQueryParam = false;
if (this.useAuthkit) {
url += `?authkit=true`;
hasQueryParam = true;
}
const fetchPage = (skip, limit) => axios_1.default.get(`${url}${hasQueryParam ? '&' : '?'}limit=${limit}&skip=${skip}`, { headers }).then(response => response.data);
this.connectionDefinitions = await (0, utils_1.paginateResults)(fetchPage);
}
catch (error) {
console.error("Failed to initialize connection definitions:", error);
this.connectionDefinitions = [];
}
}
get system() {
return this.systemPromptValue;
}
generateHeaders() {
var _a;
return {
"Content-Type": "application/json",
"x-pica-secret": this.secret,
...(_a = this.options) === null || _a === void 0 ? void 0 : _a.headers
};
}
async getAllAvailableActions(platform, actions) {
var _a;
try {
const fetchPage = (skip, limit) => axios_1.default.get(`${this.availableActionsUrl}?supported=true&connectionPlatform=${platform}&skip=${skip}&limit=${limit}`, { headers: this.generateHeaders() }).then(response => response.data);
const results = await (0, utils_1.paginateResults)(fetchPage);
// Normalize action IDs in the results
const normalizedResults = results.map(action => {
if (action._id) {
action._id = (0, utils_1.normalizeActionId)(action._id);
}
return action;
});
// Filter actions by permissions
let filteredByPermissions = normalizedResults;
const permissions = (_a = this.options) === null || _a === void 0 ? void 0 : _a.permissions;
if (permissions === "read") {
// Filter for GET methods only
filteredByPermissions = normalizedResults.filter(action => {
let method = action.method;
return (method === null || method === void 0 ? void 0 : method.toUpperCase()) === "GET";
});
}
else if (permissions === "write") {
// Filter for POST, PUT, PATCH methods
filteredByPermissions = normalizedResults.filter(action => {
var _a;
let method = (_a = action.method) === null || _a === void 0 ? void 0 : _a.toUpperCase();
return method === "POST" || method === "PUT" || method === "PATCH";
});
}
// For "admin" or no permissions set, return all actions (no filtering)
// Filter actions if actions array is provided
if (actions === null || actions === void 0 ? void 0 : actions.length) {
return filteredByPermissions.filter(action => actions.includes(action._id));
}
return filteredByPermissions;
}
catch (error) {
console.error("Error fetching all available actions:", error);
throw new Error("Failed to fetch all available actions");
}
}
async getAvailablePicaConnectors() {
await this.initializeConnectionDefinitions();
return this.connectionDefinitions;
}
async getAvailableConnectors(platform) {
var _a;
await this.initializeConnections(platform, (_a = this.options) === null || _a === void 0 ? void 0 : _a.connectors);
return this.connections;
}
async getSingleAction(actionId) {
try {
const normalizedActionId = (0, utils_1.normalizeActionId)(actionId);
const response = await axios_1.default.get(`${this.availableActionsUrl}?_id=${normalizedActionId}`, { headers: this.generateHeaders() });
if (!response.data.rows || response.data.rows.length === 0) {
throw new Error(`Action with ID ${normalizedActionId} not found`);
}
return response.data.rows[0];
}
catch (error) {
console.error("Error fetching single action:", error);
throw new Error("Failed to fetch action");
}
}
async getMethodFromKnowledge(actionId) {
try {
const normalizedActionId = (0, utils_1.normalizeActionId)(actionId);
const knowledgeResponse = await axios_1.default.get(`${this.baseUrl}/v1/knowledge?_id=${normalizedActionId}`, { headers: this.generateHeaders() });
if (knowledgeResponse.data.rows && knowledgeResponse.data.rows.length > 0) {
return knowledgeResponse.data.rows[0].method;
}
else {
throw new Error(`Method not found for action ${actionId}`);
}
}
catch (error) {
console.error("Error fetching method from knowledge API:", error);
throw new Error(`Failed to fetch method for action ${actionId}`);
}
}
async getAvailableActions(platform) {
var _a;
try {
const allActions = await this.getAllAvailableActions(platform, (_a = this.options) === null || _a === void 0 ? void 0 : _a.actions);
return {
total: allActions.length,
actions: allActions
};
}
catch (error) {
console.error("Error fetching available actions:", error);
throw new Error("Failed to fetch available actions");
}
}
async executePassthrough(actionId, connectionKey, data, path, method, queryParams, headers, isFormData, isFormUrlEncoded, returnRequestConfigWithoutExecution) {
try {
const allHeaders = {
...this.generateHeaders(),
'x-pica-connection-key': connectionKey,
'x-pica-action-id': actionId,
...(isFormData ? { 'Content-Type': 'multipart/form-data' } : {}),
...(isFormUrlEncoded ? { 'Content-Type': 'application/x-www-form-urlencoded' } : {}),
...headers
};
// Remove Content-Type header if no data is being sent
const finalHeaders = !data
? Object.entries(allHeaders)
.filter(([key]) => key.toLowerCase() !== 'content-type')
.reduce((acc, [key, value]) => ({ ...acc, [key]: value }), {})
: allHeaders;
const url = `${this.baseUrl}/v1/passthrough${path.startsWith('/') ? path : '/' + path}`;
const requestConfig = {
url,
method,
headers: finalHeaders,
params: queryParams
};
if ((method === null || method === void 0 ? void 0 : method.toLowerCase()) !== 'get') {
if (isFormData) {
const formData = new form_data_1.default();
if (data && typeof data === 'object' && !Array.isArray(data)) {
Object.entries(data).forEach(([key, value]) => {
if (typeof value === 'object') {
formData.append(key, JSON.stringify(value));
}
else {
formData.append(key, value);
}
});
}
requestConfig.data = formData;
Object.assign(requestConfig.headers, formData.getHeaders());
}
else if (isFormUrlEncoded) {
const params = new URLSearchParams();
if (data && typeof data === 'object' && !Array.isArray(data)) {
Object.entries(data).forEach(([key, value]) => {
if (typeof value === 'object') {
params.append(key, JSON.stringify(value));
}
else {
params.append(key, String(value));
}
});
}
requestConfig.data = params;
}
else {
requestConfig.data = data;
}
}
if (returnRequestConfigWithoutExecution) {
requestConfig.headers['x-pica-secret'] = "YOUR_PICA_SECRET_KEY_HERE";
return {
executed: false,
requestConfig
};
}
const response = await (0, axios_1.default)(requestConfig);
requestConfig.headers['x-pica-secret'] = "****REDACTED****";
return {
executed: true,
responseData: response.data,
requestConfig
};
}
catch (error) {
console.error("Error executing passthrough:", error);
throw error;
}
}
getPromptToConnectPlatformTool() {
return {
promptToConnectPlatform: (0, ai_1.tool)({
description: "Prompt the user to connect to a platform that they do not currently have access to",
inputSchema: zod_1.z.object({
platformName: zod_1.z.string(),
}),
outputSchema: zod_1.z.object({
response: zod_1.z.string(),
}),
execute: async ({ platformName }) => {
return {
response: platformName
};
}
})
};
}
get intelligenceTool() {
const baseTool = {
getAvailableActions: this.oneTool.getAvailableActions,
getActionKnowledge: this.oneTool.getActionKnowledge,
execute: (0, ai_1.tool)({
description: "Return a request config to the Pica Passthrough API without executing the action. Show the user a typescript code block to make an HTTP request to the Pica Passthrough API using the request config.",
inputSchema: zod_1.z.object({
platform: zod_1.z.string(),
action: zod_1.z.object({
_id: zod_1.z.string(),
path: zod_1.z.string()
}),
method: zod_1.z.string().optional(),
connectionKey: zod_1.z.string(),
data: zod_1.z.any(),
pathVariables: zod_1.z.record(zod_1.z.string(), zod_1.z.union([zod_1.z.string(), zod_1.z.number(), zod_1.z.boolean()])).optional(),
queryParams: zod_1.z.record(zod_1.z.string(), zod_1.z.any()).optional(),
headers: zod_1.z.record(zod_1.z.string(), zod_1.z.any()).optional(),
isFormData: zod_1.z.boolean().optional(),
isFormUrlEncoded: zod_1.z.boolean().optional(),
}),
outputSchema: zod_1.z.object({
success: zod_1.z.boolean(),
title: zod_1.z.string(),
message: zod_1.z.string(),
raw: zod_1.z.string()
}),
execute: async (params) => {
var _a;
try {
if (!this.connections.some(conn => conn.key === params.connectionKey) && this.useAuthkit) {
throw new Error(`Connection not found. Please add a ${params.platform} connection first.`);
}
// Handle path variables
const templateVariables = params.action.path.match(/\{\{([^}]+)\}\}/g);
let resolvedPath = params.action.path;
if (templateVariables) {
const requiredVariables = templateVariables.map(v => v.replace(/\{\{|\}\}/g, ''));
const combinedVariables = {
...(Array.isArray(params.data) ? {} : (params.data || {})),
...(params.pathVariables || {})
};
const missingVariables = requiredVariables.filter(v => !combinedVariables[v]);
if (missingVariables.length > 0) {
throw new Error(`Missing required path variables: ${missingVariables.join(', ')}. ` +
`Please provide values for these variables.`);
}
// Clean up data object and prepare path variables
if (!Array.isArray(params.data)) {
requiredVariables.forEach(v => {
if (params.data && params.data[v] && (!params.pathVariables || !params.pathVariables[v])) {
if (!params.pathVariables)
params.pathVariables = {};
params.pathVariables[v] = params.data[v];
delete params.data[v];
}
});
}
resolvedPath = (0, utils_1.replacePathVariables)(params.action.path, params.pathVariables || {});
}
const normalizedActionId = (0, utils_1.normalizeActionId)(params.action._id);
// If method is not provided, fetch it from the knowledge API
let method = params.method;
if (!method) {
method = await this.getMethodFromKnowledge(normalizedActionId);
}
// Execute the passthrough request with all components
const result = await this.executePassthrough(normalizedActionId, params.connectionKey, params.data, resolvedPath, method, params.queryParams, params.headers, params.isFormData, params.isFormUrlEncoded, true);
return {
success: true,
title: "Request config returned",
message: "Request config returned without execution",
raw: JSON.stringify(result.requestConfig)
};
}
catch (error) {
console.error("Error creating request config:", error);
return {
success: false,
title: "Failed to create request config",
message: error.message,
raw: JSON.stringify(((_a = error === null || error === void 0 ? void 0 : error.response) === null || _a === void 0 ? void 0 : _a.data) || error)
};
}
}
})
};
// Add the promptToConnectPlatform tool if authkit is enabled
if (this.useAuthkit) {
return {
...baseTool,
...this.getPromptToConnectPlatformTool()
};
}
return baseTool;
}
get oneTool() {
const baseTool = {
getAvailableActions: (0, ai_1.tool)({
description: "Get available actions for a specific platform",
inputSchema: zod_1.z.object({
platform: zod_1.z.string(),
}),
outputSchema: zod_1.z.object({
success: zod_1.z.boolean(),
actions: zod_1.z.array(zod_1.z.object({
_id: zod_1.z.string(),
title: zod_1.z.string(),
tags: zod_1.z.array(zod_1.z.string()),
})),
platform: zod_1.z.string(),
content: zod_1.z.string()
}),
execute: async (params) => {
var _a;
try {
const availableActions = await this.getAvailableActions(params.platform);
const simplifiedActions = availableActions.actions.map(action => ({
_id: action._id,
title: action.title,
tags: action.tags,
}));
return {
success: true,
actions: simplifiedActions,
platform: params.platform,
content: `Found ${simplifiedActions.length} available actions for ${params.platform}`
};
}
catch (error) {
console.error("Error getting available actions:", error);
return {
success: false,
title: "Failed to get available actions",
message: error.message,
raw: JSON.stringify(((_a = error === null || error === void 0 ? void 0 : error.response) === null || _a === void 0 ? void 0 : _a.data) || error)
};
}
}
}),
getActionKnowledge: (0, ai_1.tool)({
description: "Get full action details including knowledge documentation for a specific action",
inputSchema: zod_1.z.object({
platform: zod_1.z.string(),
actionId: zod_1.z.string(),
}),
outputSchema: zod_1.z.object({
success: zod_1.z.boolean(),
action: zod_1.z.object({
_id: zod_1.z.string(),
title: zod_1.z.string(),
connectionPlatform: zod_1.z.string(),
knowledge: zod_1.z.string(),
path: zod_1.z.string(),
baseUrl: zod_1.z.string(),
tags: zod_1.z.array(zod_1.z.string()),
method: zod_1.z.string().optional()
}),
platform: zod_1.z.string(),
content: zod_1.z.string()
}),
execute: async (params) => {
var _a;
try {
const normalizedActionId = (0, utils_1.normalizeActionId)(params.actionId);
const action = await this.getSingleAction(normalizedActionId);
return {
success: true,
action,
platform: params.platform,
content: `Found knowledge for action: ${action.title}`
};
}
catch (error) {
console.error("Error getting action knowledge:", error);
return {
success: false,
title: "Failed to get action knowledge",
message: error.message,
raw: JSON.stringify(((_a = error === null || error === void 0 ? void 0 : error.response) === null || _a === void 0 ? void 0 : _a.data) || error)
};
}
}
}),
execute: (0, ai_1.tool)({
description: "Execute a specific action using the passthrough API",
inputSchema: zod_1.z.object({
platform: zod_1.z.string(),
action: zod_1.z.object({
_id: zod_1.z.string(),
path: zod_1.z.string()
}),
method: zod_1.z.string().optional(),
connectionKey: zod_1.z.string(),
data: zod_1.z.any(),
pathVariables: zod_1.z.record(zod_1.z.string(), zod_1.z.union([zod_1.z.string(), zod_1.z.number(), zod_1.z.boolean()])).optional(),
queryParams: zod_1.z.record(zod_1.z.string(), zod_1.z.any()).optional(),
headers: zod_1.z.record(zod_1.z.string(), zod_1.z.any()).optional(),
isFormData: zod_1.z.boolean().optional(),
isFormUrlEncoded: zod_1.z.boolean().optional(),
}),
execute: async (params) => {
var _a;
try {
if (!this.connections.some(conn => conn.key === params.connectionKey)) {
throw new Error(`Connection not found. Please add a ${params.platform} connection first.`);
}
const normalizedActionId = (0, utils_1.normalizeActionId)(params.action._id);
const fullAction = await this.getSingleAction(normalizedActionId);
// If method is not provided, fetch it from the knowledge API
let method = params.method;
if (!method) {
method = await this.getMethodFromKnowledge(normalizedActionId);
}
// Handle path variables
const templateVariables = params.action.path.match(/\{\{([^}]+)\}\}/g);
let resolvedPath = params.action.path;
if (templateVariables) {
const requiredVariables = templateVariables.map(v => v.replace(/\{\{|\}\}/g, ''));
const combinedVariables = {
...(Array.isArray(params.data) ? {} : (params.data || {})),
...(params.pathVariables || {})
};
const missingVariables = requiredVariables.filter(v => !combinedVariables[v]);
if (missingVariables.length > 0) {
throw new Error(`Missing required path variables: ${missingVariables.join(', ')}. ` +
`Please provide values for these variables.`);
}
// Clean up data object and prepare path variables
if (!Array.isArray(params.data)) {
requiredVariables.forEach(v => {
if (params.data && params.data[v] && (!params.pathVariables || !params.pathVariables[v])) {
if (!params.pathVariables)
params.pathVariables = {};
params.pathVariables[v] = params.data[v];
delete params.data[v];
}
});
}
resolvedPath = (0, utils_1.replacePathVariables)(params.action.path, params.pathVariables || {});
}
// Execute the passthrough request with all components
const result = await this.executePassthrough(normalizedActionId, params.connectionKey, params.data, resolvedPath, method, params.queryParams, params.headers, params.isFormData, params.isFormUrlEncoded, false);
return {
success: true,
data: result.executed ? result.responseData : undefined,
connectionKey: params.connectionKey,
platform: params.platform,
action: fullAction.title,
requestConfig: result.requestConfig,
content: `Executed ${fullAction.title} via ${params.platform}`,
};
}
catch (error) {
console.error("Error executing action:", error);
return {
success: false,
title: "Failed to execute action",
message: error.message,
raw: JSON.stringify(((_a = error === null || error === void 0 ? void 0 : error.response) === null || _a === void 0 ? void 0 : _a.data) || error)
};
}
}
})
};
// Add the promptToConnectPlatform tool if authkit is enabled
if (this.useAuthkit) {
return {
...baseTool,
...this.getPromptToConnectPlatformTool()
};
}
return baseTool;
}
}
exports.Pica = Pica;