UNPKG

helpingai

Version:

The official JavaScript/TypeScript library for the HelpingAI API - Advanced AI with Emotional Intelligence

209 lines 7.12 kB
"use strict"; /** * JSON Schema generation utilities for tools */ Object.defineProperty(exports, "__esModule", { value: true }); exports.validateToolArguments = exports.generateToolSchema = void 0; /** * Generate JSON schema from function signature and JSDoc comments */ // eslint-disable-next-line @typescript-eslint/ban-types function generateToolSchema(name, fn, description) { const functionString = fn.toString(); const parameters = extractParameters(functionString); const docInfo = parseJSDoc(functionString); const schema = { type: 'function', function: { name, description: description || docInfo.description || `Function ${name}`, parameters: { type: 'object', properties: {}, required: [], }, }, }; // Build properties and required array parameters.forEach(param => { const paramDoc = docInfo.params[param.name]; schema.function.parameters.properties[param.name] = { type: mapTypeToJsonSchema(param.type), description: paramDoc?.description || `Parameter ${param.name}`, }; if (param.enum) { schema.function.parameters.properties[param.name].enum = param.enum; } if (param.required) { schema.function.parameters.required.push(param.name); } }); return schema; } exports.generateToolSchema = generateToolSchema; /** * Extract parameter information from function string */ function extractParameters(functionString) { const parameters = []; // Simple regex to extract parameters - this is a basic implementation // In a real implementation, you'd want to use a proper TypeScript parser const paramMatch = functionString.match(/\(([^)]*)\)/); if (!paramMatch) return parameters; const paramString = paramMatch[1]; if (!paramString.trim()) return parameters; const params = paramString.split(',').map(p => p.trim()); params.forEach(param => { const info = parseParameter(param); if (info) { parameters.push(info); } }); return parameters; } /** * Parse individual parameter */ function parseParameter(param) { // Handle TypeScript parameter syntax: name: type = default const match = param.match(/(\w+)(?:\s*:\s*([^=]+?))?(?:\s*=\s*(.+))?$/); if (!match) return null; const [, name, typeAnnotation, defaultValue] = match; return { name: name.trim(), type: typeAnnotation?.trim() || 'any', required: !defaultValue, default: defaultValue ? parseDefaultValue(defaultValue.trim()) : undefined, }; } /** * Parse default value */ // eslint-disable-next-line @typescript-eslint/no-explicit-any function parseDefaultValue(value) { if (value === 'undefined' || value === 'null') return null; if (value === 'true') return true; if (value === 'false') return false; if (/^\d+$/.test(value)) return parseInt(value, 10); if (/^\d*\.\d+$/.test(value)) return parseFloat(value); if (value.startsWith('"') && value.endsWith('"')) return value.slice(1, -1); if (value.startsWith("'") && value.endsWith("'")) return value.slice(1, -1); return value; } /** * Map TypeScript types to JSON Schema types */ function mapTypeToJsonSchema(type) { const cleanType = type.replace(/\s+/g, '').toLowerCase(); if (cleanType.includes('string')) return 'string'; if (cleanType.includes('number')) return 'number'; if (cleanType.includes('boolean')) return 'boolean'; if (cleanType.includes('array') || cleanType.includes('[]')) return 'array'; if (cleanType.includes('object') || cleanType.includes('{}')) return 'object'; return 'string'; // Default fallback } /** * Parse JSDoc comments from function string */ function parseJSDoc(functionString) { const result = { description: '', params: {}, }; // Extract JSDoc comment const jsdocMatch = functionString.match(/\/\*\*([\s\S]*?)\*\//); if (!jsdocMatch) return result; const jsdocContent = jsdocMatch[1]; const lines = jsdocContent .split('\n') .map(line => line.replace(/^\s*\*\s?/, '').trim()) .filter(line => line); let currentSection = 'description'; const descriptionLines = []; lines.forEach(line => { if (line.startsWith('@param')) { currentSection = 'param'; const paramMatch = line.match(/@param\s+(?:\{([^}]+)\}\s+)?(\w+)\s+(.+)/); if (paramMatch) { const [, type, name, description] = paramMatch; result.params[name] = { description, type }; } } else if (line.startsWith('@')) { currentSection = 'other'; } else if (currentSection === 'description') { descriptionLines.push(line); } }); result.description = descriptionLines.join(' ').trim(); return result; } /** * Validate tool arguments against schema */ function validateToolArguments(tool, // eslint-disable-next-line @typescript-eslint/no-explicit-any args) { const errors = []; const schema = tool.function.parameters; // Check required parameters if (schema.required) { schema.required.forEach((param) => { if (!(param in args)) { errors.push(`Missing required parameter: ${param}`); } }); } // Basic type checking Object.entries(args).forEach(([key, value]) => { const propSchema = schema.properties[key]; if (!propSchema) { errors.push(`Unknown parameter: ${key}`); return; } const expectedType = propSchema.type; const actualType = typeof value; if (expectedType === 'number' && actualType !== 'number') { errors.push(`Parameter ${key} should be a number, got ${actualType}`); } else if (expectedType === 'string' && actualType !== 'string') { errors.push(`Parameter ${key} should be a string, got ${actualType}`); } else if (expectedType === 'boolean' && actualType !== 'boolean') { errors.push(`Parameter ${key} should be a boolean, got ${actualType}`); } else if (expectedType === 'array' && !Array.isArray(value)) { errors.push(`Parameter ${key} should be an array`); } else if (expectedType === 'object' && (actualType !== 'object' || Array.isArray(value))) { errors.push(`Parameter ${key} should be an object`); } // Check enum values if (propSchema.enum && !propSchema.enum.includes(value)) { errors.push(`Parameter ${key} must be one of: ${propSchema.enum.join(', ')}`); } }); return { valid: errors.length === 0, errors, }; } exports.validateToolArguments = validateToolArguments; //# sourceMappingURL=schema.js.map