UNPKG

@obayd/agentic

Version:

A powerful agent framework for LLMs.

117 lines (100 loc) 4.07 kB
// src/Tool.js export class Tool { name = ""; #description = ""; #params = {}; #rawDescription = null; #action = async (params, ...args) => {}; constructor(name) { if (typeof name !== "string" || !name.trim()) throw new Error("Tool name must be a non-empty string"); if (!/^[a-zA-Z0-9_]+$/.test(name)) throw new Error( `Invalid tool name: "${name}". Use only letters, numbers, and underscores.` ); this.name = name; } static make(name) { return new this(name); } description(description) { if (typeof description !== "string") throw new Error("Tool description must be a string"); this.#description = description; return this; } desc(description) { return this.description(description); } param(name, description, options = {}) { if (typeof name !== "string" || !name.trim()) throw new Error("Param name must be a non-empty string"); if (typeof description !== "string") throw new Error("Param description must be a string"); let type = "string", required = true, enumValues; if (typeof options === "string") { type = options; } else if (typeof options === "object" && options !== null) { type = options.type ?? type; required = options.required ?? required; enumValues = options.enum; if (enumValues && !Array.isArray(enumValues)) throw new Error(`Enum for param "${name}" must be an array.`); } this.#params[name] = { description, type, required, enum: enumValues }; return this; } raw(description = "Raw text input for the tool.") { if (typeof description !== "string") throw new Error("Raw description must be a string"); this.#rawDescription = description; return this; } action(callback) { if (typeof callback !== "function") throw new Error("Tool action must be a function"); this.#action = async (params, ...args) => callback(params, ...args); return this; } buildPromptString() { const paramStrings = Object.entries(this.#params).map(([name, details]) => { let parts = [`${name}: ${details.type}`]; if (details.description) parts.push(` // ${details.description}`); parts.push(details.required ? " (required)" : " (optional)"); if (details.enum) parts.push(` (enum: ${JSON.stringify(details.enum)})`); return parts.join(""); }); let str = `- ${this.name}: ${this.#description || "No description provided."}`; if (paramStrings.length > 0) { str += `\n PARAMS:\n ${paramStrings.join("\n ")}`; } if (this.#rawDescription) { str += `\n RAW INPUT: // ${this.#rawDescription}`; } return str; } async call(params, raw, args = []) { const finalParams = { ...params }; if (this.#rawDescription !== null && raw !== undefined && raw !== null) { finalParams.raw = raw; } for (const [pName, pDetails] of Object.entries(this.#params)) { if (pDetails.required && !(pName in finalParams)) { if (!(pName === 'raw' && this.#rawDescription !== null && (raw !== undefined && raw !== null))) { throw new Error(`Missing required parameter: ${pName}`); } } } try { return await this.#action(finalParams, ...args); } catch (error) { console.error( `Error during tool action execution for "${this.name}":`, error ); return { error: `Tool execution failed: ${error.message}` }; } } }