UNPKG

semem

Version:

Semantic Memory for Intelligent Agents

479 lines (430 loc) 18 kB
/** * Maps zoom levels to specific Ragno element types and selection strategies */ export default class ZoomLevelMapper { constructor(options = {}) { this.ragnoNamespace = options.ragnoNamespace || 'http://purl.org/stuff/ragno/'; this.initializeZoomMappings(); this.initializeSelectionStrategies(); } /** * Initialize zoom level to RDF type mappings */ initializeZoomMappings() { this.zoomMappings = { 'corpus': { primaryTypes: ['ragno:Corpus'], secondaryTypes: [], granularity: 1, scope: 'global', description: 'Entire corpus overview and metadata', typicalResultCount: 1, aggregationLevel: 'corpus' }, 'community': { primaryTypes: ['ragno:Community'], secondaryTypes: ['ragno:CommunityCluster', 'ragno:Topic'], granularity: 2, scope: 'thematic', description: 'Thematic communities and topic clusters', typicalResultCount: 50, aggregationLevel: 'community' }, 'unit': { primaryTypes: ['ragno:SemanticUnit', 'ragno:Unit'], secondaryTypes: ['ragno:Chunk', 'ragno:Segment'], granularity: 3, scope: 'semantic', description: 'Semantic units and meaningful segments', typicalResultCount: 200, aggregationLevel: 'unit' }, 'entity': { primaryTypes: ['ragno:Entity'], secondaryTypes: ['ragno:Concept', 'ragno:NamedEntity'], granularity: 4, scope: 'conceptual', description: 'Individual entities and concepts', typicalResultCount: 500, aggregationLevel: 'entity' }, 'text': { primaryTypes: ['ragno:TextElement', 'ragno:Text'], secondaryTypes: ['ragno:Sentence', 'ragno:Paragraph', 'ragno:Document'], granularity: 5, scope: 'textual', description: 'Raw text elements and documents', typicalResultCount: 1000, aggregationLevel: 'text' } }; } /** * Initialize selection strategies for each zoom level */ initializeSelectionStrategies() { this.selectionStrategies = { 'corpus': { primaryStrategy: 'metadata_aggregation', scoringFactors: ['completeness', 'recency', 'size'], weights: { completeness: 0.5, recency: 0.3, size: 0.2 }, requiresAggregation: true, maxResults: 5, sortBy: 'created_desc' }, 'community': { primaryStrategy: 'community_detection', scoringFactors: ['cohesion', 'diversity', 'size', 'relevance'], weights: { cohesion: 0.3, diversity: 0.25, size: 0.2, relevance: 0.25 }, requiresAggregation: true, maxResults: 50, sortBy: 'cohesion_desc' }, 'unit': { primaryStrategy: 'semantic_similarity', scoringFactors: ['relevance', 'completeness', 'connectivity'], weights: { relevance: 0.5, completeness: 0.3, connectivity: 0.2 }, requiresAggregation: false, maxResults: 200, sortBy: 'relevance_desc' }, 'entity': { primaryStrategy: 'entity_ranking', scoringFactors: ['relevance', 'importance', 'connectivity', 'frequency'], weights: { relevance: 0.4, importance: 0.3, connectivity: 0.2, frequency: 0.1 }, requiresAggregation: false, maxResults: 500, sortBy: 'importance_desc' }, 'text': { primaryStrategy: 'text_retrieval', scoringFactors: ['relevance', 'recency', 'quality'], weights: { relevance: 0.6, recency: 0.25, quality: 0.15 }, requiresAggregation: false, maxResults: 1000, sortBy: 'relevance_desc' } }; } /** * Get mapping configuration for a zoom level * @param {string} zoomLevel - The zoom level (corpus, community, unit, entity, text) * @returns {Object} Zoom level configuration */ getZoomMapping(zoomLevel) { const mapping = this.zoomMappings[zoomLevel]; if (!mapping) { throw new Error(`Unsupported zoom level: ${zoomLevel}`); } return { ...mapping }; } /** * Get selection strategy for a zoom level * @param {string} zoomLevel - The zoom level * @returns {Object} Selection strategy configuration */ getSelectionStrategy(zoomLevel) { const strategy = this.selectionStrategies[zoomLevel]; if (!strategy) { throw new Error(`No selection strategy defined for zoom level: ${zoomLevel}`); } return { ...strategy }; } /** * Get all RDF types for a zoom level (primary + secondary) * @param {string} zoomLevel - The zoom level * @returns {Array<string>} Array of RDF type URIs */ getAllTypes(zoomLevel) { const mapping = this.getZoomMapping(zoomLevel); return [...mapping.primaryTypes, ...mapping.secondaryTypes]; } /** * Get primary RDF types for a zoom level * @param {string} zoomLevel - The zoom level * @returns {Array<string>} Array of primary RDF type URIs */ getPrimaryTypes(zoomLevel) { const mapping = this.getZoomMapping(zoomLevel); return [...mapping.primaryTypes]; } /** * Build type filter clause for SPARQL queries * @param {string} zoomLevel - The zoom level * @param {boolean} includePrimary - Include primary types (default: true) * @param {boolean} includeSecondary - Include secondary types (default: false) * @returns {string} SPARQL type filter clause */ buildTypeFilter(zoomLevel, includePrimary = true, includeSecondary = false) { const mapping = this.getZoomMapping(zoomLevel); const types = []; if (includePrimary) { types.push(...mapping.primaryTypes); } if (includeSecondary) { types.push(...mapping.secondaryTypes); } if (types.length === 0) { return ''; } if (types.length === 1) { return `?uri a ${types[0]} .`; } const typeUnion = types.map(type => `{ ?uri a ${type} }`).join(' UNION '); return `{ ${typeUnion} }`; } /** * Get optimal result limit for a zoom level * @param {string} zoomLevel - The zoom level * @param {number} tokenBudget - Available token budget * @returns {number} Recommended result limit */ getOptimalResultLimit(zoomLevel, tokenBudget = 4000) { const mapping = this.getZoomMapping(zoomLevel); const strategy = this.getSelectionStrategy(zoomLevel); // Estimate tokens per result based on zoom level const tokensPerResult = this.estimateTokensPerResult(zoomLevel); const budgetBasedLimit = Math.floor(tokenBudget * 0.8 / tokensPerResult); // Use the smaller of strategy max or budget-based limit return Math.min(strategy.maxResults, budgetBasedLimit, mapping.typicalResultCount); } /** * Estimate tokens per result for a zoom level * @param {string} zoomLevel - The zoom level * @returns {number} Estimated tokens per result */ estimateTokensPerResult(zoomLevel) { const tokenEstimates = { 'corpus': 200, // High-level metadata 'community': 100, // Community summaries 'unit': 80, // Semantic unit content 'entity': 30, // Entity information 'text': 150 // Text content }; return tokenEstimates[zoomLevel] || 50; } /** * Get aggregation requirements for a zoom level * @param {string} zoomLevel - The zoom level * @returns {Object} Aggregation configuration */ getAggregationConfig(zoomLevel) { const strategy = this.getSelectionStrategy(zoomLevel); const mapping = this.getZoomMapping(zoomLevel); return { required: strategy.requiresAggregation, level: mapping.aggregationLevel, strategy: strategy.primaryStrategy, groupBy: this.getAggregationGroupBy(zoomLevel), metrics: this.getAggregationMetrics(zoomLevel) }; } /** * Get GROUP BY clause for aggregation * @param {string} zoomLevel - The zoom level * @returns {Array<string>} Fields to group by */ getAggregationGroupBy(zoomLevel) { const groupByMap = { 'corpus': ['?corpus'], 'community': ['?community', '?topic'], 'unit': [], // No aggregation 'entity': [], // No aggregation 'text': [] // No aggregation }; return groupByMap[zoomLevel] || []; } /** * Get aggregation metrics for a zoom level * @param {string} zoomLevel - The zoom level * @returns {Array<Object>} Aggregation metrics configuration */ getAggregationMetrics(zoomLevel) { const metricsMap = { 'corpus': [ { name: 'totalEntities', function: 'COUNT(?entity)', alias: 'entityCount' }, { name: 'totalUnits', function: 'COUNT(?unit)', alias: 'unitCount' }, { name: 'avgQuality', function: 'AVG(?quality)', alias: 'avgQuality' }, { name: 'lastModified', function: 'MAX(?modified)', alias: 'lastModified' } ], 'community': [ { name: 'memberCount', function: 'COUNT(?member)', alias: 'memberCount' }, { name: 'avgCohesion', function: 'AVG(?cohesion)', alias: 'avgCohesion' }, { name: 'topicDiversity', function: 'COUNT(DISTINCT ?topic)', alias: 'topicCount' }, { name: 'totalConnections', function: 'SUM(?connections)', alias: 'connectionCount' } ], 'unit': [], 'entity': [], 'text': [] }; return metricsMap[zoomLevel] || []; } /** * Determine if zoom level supports a specific tilt representation * @param {string} zoomLevel - The zoom level * @param {string} tiltRepresentation - The tilt representation * @returns {boolean} Whether the combination is supported */ supportsTilt(zoomLevel, tiltRepresentation) { const supportMatrix = { 'corpus': ['keywords', 'temporal', 'graph'], 'community': ['keywords', 'graph', 'embedding'], 'unit': ['keywords', 'embedding', 'temporal', 'graph'], 'entity': ['keywords', 'embedding', 'graph'], 'text': ['keywords', 'embedding', 'temporal'] }; const supportedTilts = supportMatrix[zoomLevel] || []; return supportedTilts.includes(tiltRepresentation); } /** * Get recommended tilt representation for a zoom level * @param {string} zoomLevel - The zoom level * @returns {string} Recommended tilt representation */ getRecommendedTilt(zoomLevel) { const recommendations = { 'corpus': 'keywords', 'community': 'graph', 'unit': 'embedding', 'entity': 'embedding', 'text': 'keywords' }; return recommendations[zoomLevel] || 'keywords'; } /** * Build zoom-specific SPARQL SELECT clause * @param {string} zoomLevel - The zoom level * @param {string} tiltRepresentation - The tilt representation * @returns {string} SPARQL SELECT clause */ buildSelectClause(zoomLevel, tiltRepresentation) { const baseFields = this.getBaseFields(zoomLevel); const tiltFields = this.getTiltFields(tiltRepresentation); const aggregationFields = this.getAggregationFields(zoomLevel); const allFields = [...baseFields, ...tiltFields, ...aggregationFields]; return `SELECT DISTINCT ${allFields.join(' ')}`; } /** * Get base fields for a zoom level * @param {string} zoomLevel - The zoom level * @returns {Array<string>} Base SPARQL fields */ getBaseFields(zoomLevel) { const fieldMap = { 'corpus': ['?uri', '?label', '?description', '?created', '?modified'], 'community': ['?uri', '?label', '?description', '?memberCount', '?cohesion'], 'unit': ['?uri', '?content', '?entity', '?unit', '?created'], 'entity': ['?uri', '?label', '?type', '?prefLabel', '?importance'], 'text': ['?uri', '?text', '?source', '?position', '?created'] }; return fieldMap[zoomLevel] || ['?uri', '?label']; } /** * Get tilt-specific fields * @param {string} tiltRepresentation - The tilt representation * @returns {Array<string>} Tilt-specific SPARQL fields */ getTiltFields(tiltRepresentation) { const fieldMap = { 'embedding': ['?embedding', '?similarity'], 'keywords': ['?keywords', '?keywordScore'], 'graph': ['?connections', '?centrality'], 'temporal': ['?created', '?modified', '?sequence'] }; return fieldMap[tiltRepresentation] || []; } /** * Get aggregation fields for a zoom level * @param {string} zoomLevel - The zoom level * @returns {Array<string>} Aggregation SPARQL fields */ getAggregationFields(zoomLevel) { const metrics = this.getAggregationMetrics(zoomLevel); return metrics.map(metric => `(${metric.function} AS ?${metric.alias})`); } /** * Validate zoom level compatibility with parameters * @param {string} zoomLevel - The zoom level * @param {Object} panFilters - Pan filter parameters * @param {string} tiltRepresentation - Tilt representation * @returns {Object} Validation result */ validateCompatibility(zoomLevel, panFilters, tiltRepresentation) { const issues = []; const warnings = []; // Check tilt compatibility if (!this.supportsTilt(zoomLevel, tiltRepresentation)) { issues.push(`Tilt '${tiltRepresentation}' not optimal for zoom level '${zoomLevel}'`); warnings.push(`Consider using '${this.getRecommendedTilt(zoomLevel)}' instead`); } // Check filter compatibility if (zoomLevel === 'corpus' && panFilters.entity) { warnings.push('Entity filters may not be effective at corpus zoom level'); } if (zoomLevel === 'text' && panFilters.geographic) { warnings.push('Geographic filters may have limited effect on text elements'); } // Check result count expectations const mapping = this.getZoomMapping(zoomLevel); if (panFilters.topic && mapping.typicalResultCount > 500) { warnings.push('Topic filters may return many results at this zoom level'); } return { valid: issues.length === 0, issues, warnings, recommendations: this.getRecommendations(zoomLevel, panFilters, tiltRepresentation) }; } /** * Get optimization recommendations * @param {string} zoomLevel - The zoom level * @param {Object} panFilters - Pan filter parameters * @param {string} tiltRepresentation - Tilt representation * @returns {Array<string>} Optimization recommendations */ getRecommendations(zoomLevel, panFilters, tiltRepresentation) { const recommendations = []; if (zoomLevel === 'entity' && tiltRepresentation === 'temporal') { recommendations.push('Consider using embedding or graph tilt for better entity analysis'); } if (zoomLevel === 'corpus' && Object.keys(panFilters).length > 2) { recommendations.push('Simplify filters for corpus-level analysis'); } if (zoomLevel === 'text' && !panFilters.topic) { recommendations.push('Add topic filter to improve text relevance'); } return recommendations; } /** * Get zoom level metadata for API documentation * @returns {Object} Complete zoom level documentation */ getZoomLevelDocumentation() { const documentation = {}; for (const [level, mapping] of Object.entries(this.zoomMappings)) { const strategy = this.selectionStrategies[level]; documentation[level] = { description: mapping.description, granularity: mapping.granularity, scope: mapping.scope, types: { primary: mapping.primaryTypes, secondary: mapping.secondaryTypes }, strategy: { name: strategy.primaryStrategy, factors: strategy.scoringFactors, maxResults: strategy.maxResults }, supportedTilts: Object.keys(this.selectionStrategies).filter(tilt => this.supportsTilt(level, tilt) ), recommendedTilt: this.getRecommendedTilt(level), typicalResultCount: mapping.typicalResultCount, estimatedTokensPerResult: this.estimateTokensPerResult(level) }; } return documentation; } }