UNPKG

semem

Version:

Semantic Memory for Intelligent Agents

444 lines (397 loc) 13.9 kB
/** * Validates ZPT navigation parameters (zoom/pan/tilt) */ export default class ParameterValidator { constructor() { this.schemas = this.initializeSchemas(); } /** * Initialize parameter schemas based on ZPT specification */ initializeSchemas() { return { zoom: { type: 'enum', values: ['entity', 'unit', 'text', 'community', 'corpus'], required: true }, pan: { type: 'object', required: false, properties: { topic: { type: 'string', pattern: /^[a-zA-Z0-9\-_]+$/ }, entity: { type: 'array', items: { type: 'string' } }, temporal: { type: 'object', properties: { start: { type: 'date' }, end: { type: 'date' } } }, geographic: { type: 'object', properties: { bbox: { type: 'array', minItems: 4, maxItems: 4 }, center: { type: 'object', properties: { lat: { type: 'number', min: -90, max: 90 }, lon: { type: 'number', min: -180, max: 180 } } } } } } }, tilt: { type: 'enum', values: ['embedding', 'keywords', 'graph', 'temporal'], required: true }, transform: { type: 'object', required: false, properties: { maxTokens: { type: 'number', default: 4000, min: 100, max: 128000 }, format: { type: 'enum', values: ['json', 'markdown', 'structured'], default: 'structured' }, tokenizer: { type: 'string', default: 'cl100k_base' }, includeMetadata: { type: 'boolean', default: true }, chunkStrategy: { type: 'enum', values: ['semantic', 'fixed', 'adaptive'], default: 'semantic' } } } }; } /** * Validate complete ZPT parameter object * @param {Object} params - Navigation parameters * @returns {Object} Validation result */ validate(params) { if (!params || typeof params !== 'object') { return { valid: false, message: 'Parameters must be an object', errors: [] }; } const errors = []; const warnings = []; // Validate zoom parameter const zoomResult = this.validateZoom(params.zoom); if (!zoomResult.valid) { errors.push({ field: 'zoom', ...zoomResult }); } // Validate tilt parameter const tiltResult = this.validateTilt(params.tilt); if (!tiltResult.valid) { errors.push({ field: 'tilt', ...tiltResult }); } // Validate optional pan parameter if (params.pan !== undefined) { const panResult = this.validatePan(params.pan); if (!panResult.valid) { errors.push({ field: 'pan', ...panResult }); } } // Validate optional transform parameter if (params.transform !== undefined) { const transformResult = this.validateTransform(params.transform); if (!transformResult.valid) { errors.push({ field: 'transform', ...transformResult }); } warnings.push(...(transformResult.warnings || [])); } // Check for unknown parameters const knownParams = ['zoom', 'pan', 'tilt', 'transform']; const unknownParams = Object.keys(params).filter(key => !knownParams.includes(key)); if (unknownParams.length > 0) { warnings.push({ field: 'unknown', message: `Unknown parameters: ${unknownParams.join(', ')}` }); } return { valid: errors.length === 0, errors, warnings, message: errors.length > 0 ? `Validation failed: ${errors[0].message}` : null }; } /** * Validate zoom parameter */ validateZoom(zoom) { const schema = this.schemas.zoom; if (zoom === undefined || zoom === null) { return { valid: false, message: 'Zoom parameter is required' }; } if (!schema.values.includes(zoom)) { return { valid: false, message: `Invalid zoom level. Must be one of: ${schema.values.join(', ')}` }; } return { valid: true }; } /** * Validate tilt parameter */ validateTilt(tilt) { const schema = this.schemas.tilt; if (tilt === undefined || tilt === null) { return { valid: false, message: 'Tilt parameter is required' }; } if (!schema.values.includes(tilt)) { return { valid: false, message: `Invalid tilt value. Must be one of: ${schema.values.join(', ')}` }; } return { valid: true }; } /** * Validate pan parameter */ validatePan(pan) { if (!pan || typeof pan !== 'object') { return { valid: false, message: 'Pan parameter must be an object' }; } const errors = []; // Validate topic filter if (pan.topic !== undefined) { if (typeof pan.topic !== 'string' || pan.topic.length === 0) { errors.push('Topic filter must be a non-empty string'); } else if (!this.schemas.pan.properties.topic.pattern.test(pan.topic)) { errors.push('Topic filter contains invalid characters'); } } // Validate entity filter if (pan.entity !== undefined) { if (!Array.isArray(pan.entity)) { errors.push('Entity filter must be an array'); } else if (pan.entity.some(e => typeof e !== 'string')) { errors.push('All entity filter values must be strings'); } } // Validate temporal filter if (pan.temporal !== undefined) { const temporalResult = this.validateTemporal(pan.temporal); if (!temporalResult.valid) { errors.push(temporalResult.message); } } // Validate geographic filter if (pan.geographic !== undefined) { const geoResult = this.validateGeographic(pan.geographic); if (!geoResult.valid) { errors.push(geoResult.message); } } return { valid: errors.length === 0, message: errors.length > 0 ? errors[0] : null, errors }; } /** * Validate temporal filter */ validateTemporal(temporal) { if (!temporal || typeof temporal !== 'object') { return { valid: false, message: 'Temporal filter must be an object' }; } const { start, end } = temporal; if (start) { const startDate = new Date(start); if (isNaN(startDate.getTime())) { return { valid: false, message: 'Invalid start date format' }; } } if (end) { const endDate = new Date(end); if (isNaN(endDate.getTime())) { return { valid: false, message: 'Invalid end date format' }; } } if (start && end) { const startDate = new Date(start); const endDate = new Date(end); if (startDate > endDate) { return { valid: false, message: 'Start date must be before end date' }; } } return { valid: true }; } /** * Validate geographic filter */ validateGeographic(geographic) { if (!geographic || typeof geographic !== 'object') { return { valid: false, message: 'Geographic filter must be an object' }; } if (geographic.bbox) { if (!Array.isArray(geographic.bbox) || geographic.bbox.length !== 4) { return { valid: false, message: 'Bounding box must be an array of 4 numbers [minLon, minLat, maxLon, maxLat]' }; } const [minLon, minLat, maxLon, maxLat] = geographic.bbox; if (![minLon, minLat, maxLon, maxLat].every(n => typeof n === 'number')) { return { valid: false, message: 'All bounding box values must be numbers' }; } if (minLon >= maxLon || minLat >= maxLat) { return { valid: false, message: 'Invalid bounding box coordinates' }; } } if (geographic.center) { const { lat, lon } = geographic.center; if (typeof lat !== 'number' || typeof lon !== 'number') { return { valid: false, message: 'Center coordinates must be numbers' }; } if (lat < -90 || lat > 90 || lon < -180 || lon > 180) { return { valid: false, message: 'Invalid center coordinates' }; } } return { valid: true }; } /** * Validate transform parameter */ validateTransform(transform) { if (!transform || typeof transform !== 'object') { return { valid: false, message: 'Transform parameter must be an object' }; } const errors = []; const warnings = []; const schema = this.schemas.transform.properties; // Validate maxTokens if (transform.maxTokens !== undefined) { if (typeof transform.maxTokens !== 'number' || transform.maxTokens <= 0) { errors.push('maxTokens must be a positive number'); } else if (transform.maxTokens < schema.maxTokens.min) { errors.push(`maxTokens must be at least ${schema.maxTokens.min}`); } else if (transform.maxTokens > schema.maxTokens.max) { warnings.push(`maxTokens exceeds recommended maximum of ${schema.maxTokens.max}`); } } // Validate format if (transform.format !== undefined) { if (!schema.format.values.includes(transform.format)) { errors.push(`Invalid format. Must be one of: ${schema.format.values.join(', ')}`); } } // Validate tokenizer if (transform.tokenizer !== undefined) { if (typeof transform.tokenizer !== 'string') { errors.push('Tokenizer must be a string'); } } // Validate includeMetadata if (transform.includeMetadata !== undefined) { if (typeof transform.includeMetadata !== 'boolean') { errors.push('includeMetadata must be a boolean'); } } // Validate chunkStrategy if (transform.chunkStrategy !== undefined) { if (!schema.chunkStrategy.values.includes(transform.chunkStrategy)) { errors.push(`Invalid chunkStrategy. Must be one of: ${schema.chunkStrategy.values.join(', ')}`); } } return { valid: errors.length === 0, message: errors.length > 0 ? errors[0] : null, errors, warnings }; } /** * Get parameter schema for documentation */ getSchema() { return this.schemas; } /** * Get default values for optional parameters */ getDefaults() { return { transform: { maxTokens: this.schemas.transform.properties.maxTokens.default, format: this.schemas.transform.properties.format.default, tokenizer: this.schemas.transform.properties.tokenizer.default, includeMetadata: this.schemas.transform.properties.includeMetadata.default, chunkStrategy: this.schemas.transform.properties.chunkStrategy.default } }; } }