From f9ba1c8ff7b7726d8f522c7de07e576e5407a306 Mon Sep 17 00:00:00 2001 From: MervinPraison Date: Mon, 3 Mar 2025 08:24:46 +0000 Subject: [PATCH] Expand tool registration documentation and examples - Added comprehensive TypeScript documentation for tool registration methods in `docs/js/typescript.mdx` - Created new README example file `examples/README-tool-examples.md` with detailed tool registration approaches - Updated example scripts to demonstrate direct function tool registration - Refined agent tool registration logic to support multiple function registration styles - Bumped package version to 1.0.19 --- docs/js/typescript.mdx | 96 +++++++++ .../examples/README-tool-examples.md | 193 ++++++++++++++++++ .../examples/simple/direct-function-tools.ts | 7 +- src/praisonai-ts/package.json | 4 +- src/praisonai-ts/src/agent/simple.ts | 50 ++++- src/praisonai-ts/src/llm/openai.ts | 13 +- .../simple/direct-function-tools.ts | 7 +- 7 files changed, 352 insertions(+), 18 deletions(-) create mode 100644 src/praisonai-ts/examples/README-tool-examples.md diff --git a/docs/js/typescript.mdx b/docs/js/typescript.mdx index 5669e2dd7..be92d39c9 100644 --- a/docs/js/typescript.mdx +++ b/docs/js/typescript.mdx @@ -264,3 +264,99 @@ agents.start() ``` + +## Tool Calls Examples + + + + Create an agent that can use tools to get information: + +```typescript +import { Agent } from 'praisonai'; + +/** + * Example of a simple agent with tool calling capability + * + * This example demonstrates how to create a simple agent that can use tools + * to get weather information for a location. + */ + +// Define a weather tool +const getWeather = { + type: "function", + function: { + name: "get_weather", + description: "Get current temperature for a given location.", + parameters: { + type: "object", + properties: { + location: { + type: "string", + description: "City and country e.g. Bogotá, Colombia" + } + }, + required: ["location"], + additionalProperties: false + }, + strict: true + } +}; + +// Make the function globally available +// The agent will automatically find and use this function +(global as any).get_weather = async function(location: string) { + console.log(`Getting weather for ${location}...`); + return `20°C`; +}; + +// Create an agent with the weather tool +const agent = new Agent({ + instructions: `You provide the current weather for requested locations.`, + name: "WeatherAgent", + tools: [getWeather] +}); + +// Start the agent with a prompt that will trigger tool usage +agent.start("What's the weather in Paris, France?"); +``` + + + + Create an agent with directly registered function tools: + +```typescript +import { Agent } from 'praisonai'; + +/** + * Example of a simple agent with direct function registration + * + * This example demonstrates how to create a simple agent that uses directly + * registered functions as tools without having to define tool schemas manually + * or make functions globally available. + */ + +// Define the functions directly +async function getWeather(location: string) { + console.log(`Getting weather for ${location}...`); + return `${Math.floor(Math.random() * 30)}°C`; +} + +async function getTime(location: string) { + console.log(`Getting time for ${location}...`); + const now = new Date(); + return `${now.getHours()}:${now.getMinutes()}`; +} + +// Create an agent with directly registered functions +const agent = new Agent({ + instructions: `You provide the current weather and time for requested locations.`, + name: "DirectFunctionAgent", + // Register functions directly as an array without needing to make them global + tools: [getWeather, getTime] +}); + +// Start the agent with a prompt that will trigger tool usage +agent.start("What's the weather and time in Paris, France and Tokyo, Japan?"); +``` + + diff --git a/src/praisonai-ts/examples/README-tool-examples.md b/src/praisonai-ts/examples/README-tool-examples.md new file mode 100644 index 000000000..cc2967b2b --- /dev/null +++ b/src/praisonai-ts/examples/README-tool-examples.md @@ -0,0 +1,193 @@ +# PraisonAI Tool Registration Examples + +This document demonstrates the three different ways to register tool functions in PraisonAI. + +## Method 1: Using the `tools` array with function objects directly + +```typescript +import { Agent } from 'praisonai'; + +// Define the functions directly +async function getWeather(location: string) { + console.log(`Getting weather for ${location}...`); + return `${Math.floor(Math.random() * 30)}°C`; +} + +async function getTime(location: string) { + console.log(`Getting time for ${location}...`); + const now = new Date(); + return `${now.getHours()}:${now.getMinutes()}`; +} + +// Create an agent with directly registered functions +const agent = new Agent({ + instructions: `You provide the current weather and time for requested locations.`, + name: "DirectFunctionAgent", + // Register functions directly as an array + tools: [getWeather, getTime] +}); + +// Start the agent with a prompt that will trigger tool usage +agent.start("What's the weather and time in Paris, France?"); +``` + +## Method 2: Using the `toolFunctions` object with name-function pairs + +```typescript +import { Agent } from 'praisonai'; + +// Define the functions directly +async function getWeather(location: string) { + console.log(`Getting weather for ${location}...`); + return `${Math.floor(Math.random() * 30)}°C`; +} + +async function getTime(location: string) { + console.log(`Getting time for ${location}...`); + const now = new Date(); + return `${now.getHours()}:${now.getMinutes()}`; +} + +// Create an agent with directly registered functions +const agent = new Agent({ + instructions: `You provide the current weather and time for requested locations.`, + name: "DirectFunctionAgent", + // Register functions with custom names + toolFunctions: { + get_weather: getWeather, + get_time: getTime + } +}); + +// Start the agent with a prompt that will trigger tool usage +agent.start("What's the weather and time in Paris, France?"); +``` + +## Method 3: Using the `tools` array with pre-defined tool definitions + +```typescript +import { Agent } from 'praisonai'; + +// Define the functions +async function getWeather(location: string) { + console.log(`Getting weather for ${location}...`); + return `${Math.floor(Math.random() * 30)}°C`; +} + +async function getTime(location: string) { + console.log(`Getting time for ${location}...`); + const now = new Date(); + return `${now.getHours()}:${now.getMinutes()}`; +} + +// Register functions globally +import { registerFunction } from 'praisonai'; +registerFunction('get_weather', getWeather); +registerFunction('get_time', getTime); + +// Define tool definitions +const weatherTool = { + type: "function", + function: { + name: "get_weather", + description: "Get the current weather for a location", + parameters: { + type: "object", + properties: { + location: { + type: "string", + description: "The location to get weather for" + } + }, + required: ["location"] + } + } +}; + +const timeTool = { + type: "function", + function: { + name: "get_time", + description: "Get the current time for a location", + parameters: { + type: "object", + properties: { + location: { + type: "string", + description: "The location to get time for" + } + }, + required: ["location"] + } + } +}; + +// Create an agent with pre-defined tool definitions +const agent = new Agent({ + instructions: `You provide the current weather and time for requested locations.`, + name: "ToolDefinitionAgent", + // Register pre-defined tool definitions + tools: [weatherTool, timeTool] +}); + +// Start the agent with a prompt that will trigger tool usage +agent.start("What's the weather and time in Paris, France?"); +``` + +## Combined Approach + +You can also combine these approaches as needed: + +```typescript +import { Agent } from 'praisonai'; + +// Define the functions +async function getWeather(location: string) { + console.log(`Getting weather for ${location}...`); + return `${Math.floor(Math.random() * 30)}°C`; +} + +async function getTime(location: string) { + console.log(`Getting time for ${location}...`); + const now = new Date(); + return `${now.getHours()}:${now.getMinutes()}`; +} + +// Define a custom tool definition +const calculatorTool = { + type: "function", + function: { + name: "calculate", + description: "Perform a calculation", + parameters: { + type: "object", + properties: { + expression: { + type: "string", + description: "The mathematical expression to calculate" + } + }, + required: ["expression"] + } + } +}; + +// Register the calculator function globally +import { registerFunction } from 'praisonai'; +registerFunction('calculate', async (expression: string) => { + console.log(`Calculating ${expression}...`); + // Simple eval for demonstration purposes only + return eval(expression).toString(); +}); + +// Create an agent with mixed tool registration approaches +const agent = new Agent({ + instructions: `You can provide weather, time, and perform calculations.`, + name: "MixedToolAgent", + // Register functions directly as an array + tools: [getWeather, getTime, calculatorTool] +}); + +// Start the agent with a prompt that will trigger tool usage +agent.start("What's the weather in Paris, the time in Tokyo, and what is 25 * 4?"); +``` diff --git a/src/praisonai-ts/examples/simple/direct-function-tools.ts b/src/praisonai-ts/examples/simple/direct-function-tools.ts index 5e780c417..a321aab26 100644 --- a/src/praisonai-ts/examples/simple/direct-function-tools.ts +++ b/src/praisonai-ts/examples/simple/direct-function-tools.ts @@ -24,11 +24,8 @@ async function getTime(location: string) { const agent = new Agent({ instructions: `You provide the current weather and time for requested locations.`, name: "DirectFunctionAgent", - // Register functions directly without needing to make them global - toolFunctions: { - get_weather: getWeather, - get_time: getTime - } + // Register functions directly as an array without needing to make them global + tools: [getWeather, getTime] }); // Start the agent with a prompt that will trigger tool usage diff --git a/src/praisonai-ts/package.json b/src/praisonai-ts/package.json index 4fbe3c220..d5425f856 100644 --- a/src/praisonai-ts/package.json +++ b/src/praisonai-ts/package.json @@ -1,6 +1,6 @@ { "name": "praisonai", - "version": "1.0.18", + "version": "1.0.19", "description": "PraisonAI TypeScript AI Agents Framework - Node.js, npm, and Javascript AI Agents Framework", "main": "dist/index.js", "types": "dist/index.d.ts", @@ -62,7 +62,7 @@ "fast-xml-parser": "^4.5.1", "node-fetch": "^2.6.9", "openai": "^4.81.0", - "praisonai": "^1.0.17" + "praisonai": "^1.0.18" }, "optionalDependencies": { "boxen": "^7.1.1", diff --git a/src/praisonai-ts/src/agent/simple.ts b/src/praisonai-ts/src/agent/simple.ts index 95705ab5c..6a11fff69 100644 --- a/src/praisonai-ts/src/agent/simple.ts +++ b/src/praisonai-ts/src/agent/simple.ts @@ -10,7 +10,7 @@ export interface SimpleAgentConfig { llm?: string; markdown?: boolean; stream?: boolean; - tools?: any[]; + tools?: any[] | Function[]; toolFunctions?: Record; } @@ -41,6 +41,45 @@ export class Agent { Logger.setVerbose(this.verbose); Logger.setPretty(this.pretty); + // Process tools array - handle both tool definitions and functions + if (config.tools && Array.isArray(config.tools)) { + // Convert tools array to proper format if it contains functions + const processedTools: any[] = []; + + for (let i = 0; i < config.tools.length; i++) { + const tool = config.tools[i]; + + if (typeof tool === 'function') { + // If it's a function, extract its name and register it + const funcName = tool.name || `function_${i}`; + + // Skip functions with empty names + if (funcName && funcName.trim() !== '') { + this.registerToolFunction(funcName, tool); + + // Auto-generate tool definition + this.addAutoGeneratedToolDefinition(funcName, tool); + } else { + // Generate a random name for functions without names + const randomName = `function_${Math.random().toString(36).substring(2, 9)}`; + this.registerToolFunction(randomName, tool); + + // Auto-generate tool definition + this.addAutoGeneratedToolDefinition(randomName, tool); + } + } else { + // If it's already a tool definition, add it as is + processedTools.push(tool); + } + } + + // Add any pre-defined tool definitions + if (processedTools.length > 0) { + this.tools = this.tools || []; + this.tools.push(...processedTools); + } + } + // Register directly provided tool functions if any if (config.toolFunctions) { for (const [name, func] of Object.entries(config.toolFunctions)) { @@ -98,6 +137,9 @@ export class Agent { this.tools = []; } + // Ensure we have a valid function name + const functionName = name || func.name || `function_${Math.random().toString(36).substring(2, 9)}`; + // Extract parameter names from function const funcStr = func.toString(); const paramMatch = funcStr.match(/\(([^)]*)\)/); @@ -107,8 +149,8 @@ export class Agent { const toolDef = { type: "function", function: { - name, - description: `Auto-generated function for ${name}`, + name: functionName, + description: `Auto-generated function for ${functionName}`, parameters: { type: "object", properties: {}, @@ -139,7 +181,7 @@ export class Agent { } this.tools.push(toolDef); - Logger.debug(`Auto-generated tool definition for ${name}`); + Logger.debug(`Auto-generated tool definition for ${functionName}`); } /** diff --git a/src/praisonai-ts/src/llm/openai.ts b/src/praisonai-ts/src/llm/openai.ts index 95aee603b..6c2cef98e 100644 --- a/src/praisonai-ts/src/llm/openai.ts +++ b/src/praisonai-ts/src/llm/openai.ts @@ -72,15 +72,24 @@ function convertToOpenAIMessage(message: ChatMessage): ChatCompletionMessagePara function convertToOpenAITool(tool: any): ChatCompletionTool { // If it's already in the correct format, return it if (tool.type === 'function' && typeof tool.type === 'string') { + // Ensure the function name is valid + if (!tool.function?.name || tool.function.name.trim() === '') { + tool.function.name = `function_${Math.random().toString(36).substring(2, 9)}`; + } return tool as ChatCompletionTool; } + // Generate a valid function name if none is provided + const functionName = tool.function?.name && tool.function.name.trim() !== '' + ? tool.function.name + : `function_${Math.random().toString(36).substring(2, 9)}`; + // Otherwise, try to convert it return { type: 'function', function: { - name: tool.function?.name || '', - description: tool.function?.description || '', + name: functionName, + description: tool.function?.description || `Function ${functionName}`, parameters: tool.function?.parameters || {} } }; diff --git a/src/praisonai-ts/tests/development/simple/direct-function-tools.ts b/src/praisonai-ts/tests/development/simple/direct-function-tools.ts index 2665c9085..0785b41ab 100644 --- a/src/praisonai-ts/tests/development/simple/direct-function-tools.ts +++ b/src/praisonai-ts/tests/development/simple/direct-function-tools.ts @@ -24,11 +24,8 @@ async function getTime(location: string) { const agent = new Agent({ instructions: `You provide the current weather and time for requested locations.`, name: "DirectFunctionAgent", - // Register functions directly without needing to make them global - toolFunctions: { - get_weather: getWeather, - get_time: getTime - } + // Register functions directly as an array without needing to make them global + tools: [getWeather, getTime] }); // Start the agent with a prompt that will trigger tool usage