Plugin System

Create custom tools and plugins to extend PraisonAI TypeScript

PraisonAI TypeScript provides a powerful plugin system that allows you to create custom tools and extend agent capabilities. This matches the Python SDK’s extensibility pattern.

Diagram

Agent

Plugin

Tools

Quick Start

  1. Simple Usage
    typescript
    import { tool } from 'praisonai';
    
    const greet = tool({
      name: 'greet',
      description: 'Greet a user',
      parameters: { type: 'object', properties: { name: { type: 'string' } }, required: ['name'] },
      execute: async ({ name }) => `Hello, ${name}!`,
    });
  2. With Configuration
    typescript
    import { BaseTool } from 'praisonai';
    
    class EchoTool extends BaseTool {
      name = 'echo';
      description = 'Echo input';
      async run(params: { text: string }) {
        return { text: params.text };
      }
    }

Overview

There are three ways to create custom tools:

  1. Class-based – Extend BaseTool for complex tools with state
  2. Function-based – Use createTool() for simple inline tools
  3. Decorator-style – Use tool() function for quick tool creation

BaseTool Class

The BaseTool abstract class is the foundation for creating custom tools:

typescript
import { BaseTool, ToolResult } from 'praisonai';

class WeatherTool extends BaseTool<
  { location: string; units?: string },
  { temp: number; condition: string }
> {
  name = 'get_weather';
  description = 'Get current weather for a location';
  version = '1.0.0';

  parameters = {
    type: 'object' as const,
    properties: {
      location: { type: 'string', description: 'City name' },
      units: { type: 'string', description: 'celsius or fahrenheit', default: 'celsius' }
    },
    required: ['location']
  };

  async run(params: { location: string; units?: string }) {
    // Your implementation here
    const response = await fetch(`https://api.weather.com/${params.location}`);
    const data = await response.json();
    return { temp: data.temperature, condition: data.condition };
  }
}

// Usage
const weather = new WeatherTool();
const result = await weather.run({ location: 'New York' });

BaseTool Methods

MethodDescription
run(params)Execute the tool (must implement)
execute(params)Alias for run()
safeRun(params)Execute with error handling, returns ToolResult
getSchema()Get OpenAI-compatible function schema
validate()Validate tool configuration

Safe Execution

Use safeRun() for error handling:

typescript
const result = await weather.safeRun({ location: 'Paris' });

if (result.success) {
  console.log('Temperature:', result.output.temp);
} else {
  console.error('Error:', result.error);
}

createTool Function

For simple tools, use the createTool() helper:

typescript
import { createTool } from 'praisonai';

const calculator = createTool({
  name: 'calculator',
  description: 'Evaluate a math expression',
  parameters: {
    type: 'object',
    properties: {
      expression: { type: 'string', description: 'Math expression' }
    },
    required: ['expression']
  },
  run: (params: { expression: string }) => {
    return eval(params.expression);
  }
});

// Use it
const result = await calculator.run({ expression: '10 * 5 + 2' });
console.log(result); // 52

tool() Function

The tool() function provides a decorator-style approach:

typescript
import { tool, ToolRegistry } from 'praisonai';

const greeter = tool({
  name: 'greeter',
  description: 'Generate a personalized greeting',
  parameters: {
    type: 'object',
    properties: {
      name: { type: 'string', description: 'Name to greet' },
      style: { type: 'string', enum: ['formal', 'casual'] }
    },
    required: ['name']
  },
  execute: async (params: { name: string; style?: string }) => {
    if (params.style === 'formal') {
      return `Good day, ${params.name}. How may I assist you?`;
    }
    return `Hey ${params.name}! What's up?`;
  }
});

Tool Registry

For the common case, register tools on the global registry with the Python-style register_tool / get_tool:

typescript
import { register_tool, get_tool, list_tools } from 'praisonai';

register_tool(greeter);
register_tool(calculator);

list_tools();              // ['greeter', 'calculator']
get_tool('calculator');    // look up by name

Use a scoped new ToolRegistry() only when you need an isolated registry:

typescript
import { ToolRegistry } from 'praisonai';

const registry = new ToolRegistry();

// The class method throws on duplicate names unless { overwrite: true }.
registry.register(greeter);
registry.register(calculator, { trustLevel: 'trusted' });

const tools = registry.list();
console.log('Available tools:', tools.map(t => t.name));

const calc = registry.get('calculator');

// Definitions advertise the registration key
const openaiTools = registry.toOpenAITools();

Using Tools with Agents

Pass tools to agents for function calling:

typescript
import { Agent, createTool, ToolRegistry } from 'praisonai';

// Create tools
const searchTool = createTool({
  name: 'web_search',
  description: 'Search the web for information',
  parameters: {
    type: 'object',
    properties: {
      query: { type: 'string', description: 'Search query' }
    },
    required: ['query']
  },
  run: async (params) => {
    // Implement search logic
    return `Results for: ${params.query}`;
  }
});

// Create agent with tools
const agent = new Agent({
  name: 'Research Assistant',
  instructions: 'You help users find information',
  tools: [searchTool]
});

// Agent can now use the tool
const response = await agent.chat('Search for TypeScript best practices');

Creating NPM Plugins

You can distribute tools as npm packages:

typescript
// my-praison-plugin/src/index.ts
import { BaseTool } from 'praisonai';

export class MyCustomTool extends BaseTool<{ input: string }, string> {
  name = 'my_custom_tool';
  description = 'A custom tool from my plugin';

  async run(params: { input: string }) {
    return `Processed: ${params.input}`;
  }
}

// Export for easy use
export function createMyTool() {
  return new MyCustomTool();
}

Users can then install and use:

typescript
import { createMyTool } from 'my-praison-plugin';
import { Agent } from 'praisonai';

const agent = new Agent({
  tools: [createMyTool()]
});

Tool Validation

Validate tools before use:

typescript
import { validateTool, ToolValidationError } from 'praisonai';

try {
  validateTool(myTool);
  console.log('Tool is valid');
} catch (error) {
  if (error instanceof ToolValidationError) {
    console.error('Validation failed:', error.message);
  }
}

OpenAI Schema Generation

Get OpenAI-compatible schemas for any tool:

typescript
const schema = weather.getSchema();
console.log(JSON.stringify(schema, null, 2));

// Output:
// {
//   "type": "function",
//   "function": {
//     "name": "get_weather",
//     "description": "Get current weather for a location",
//     "parameters": {
//       "type": "object",
//       "properties": {
//         "location": { "type": "string", "description": "City name" },
//         "units": { "type": "string", "description": "celsius or fahrenheit" }
//       },
//       "required": ["location"]
//     }
//   }
// }

Best Practices

Guidelines
  1. Clear descriptions – Write detailed descriptions for LLM understanding
  2. Type safety – Use TypeScript generics for input/output types
  3. Error handling – Use safeRun() or try/catch in run()
  4. Validation – Define parameters schema for input validation
  5. Versioning – Set version for tracking tool changes
  6. Testing – Write unit tests for your tools

Example: Complete Plugin

typescript
import { BaseTool, createTool, ToolRegistry } from 'praisonai';

// Class-based tool
class DatabaseTool extends BaseTool<
  { query: string; params?: any[] },
  any[]
> {
  name = 'database_query';
  description = 'Execute a database query';
  private db: any;

  constructor(connectionString: string) {
    super();
    // Initialize database connection
  }

  async run(params: { query: string; params?: any[] }) {
    return this.db.query(params.query, params.params);
  }
}

// Function-based tool
const formatTool = createTool({
  name: 'format_json',
  description: 'Format JSON with indentation',
  run: (params: { json: string; indent?: number }) => {
    return JSON.stringify(JSON.parse(params.json), null, params.indent || 2);
  }
});

// Register all tools
const registry = new ToolRegistry();
registry.register(new DatabaseTool('postgres://...'));
registry.register(formatTool);

export { registry, DatabaseTool, formatTool };

Related