diff --git a/ROADMAP.md b/ROADMAP.md index 2af1f0c..d94fcc2 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -16,6 +16,6 @@ ## Medium Priority -- [ ] `ContextManager` implementation (attention/relevance filtering). -- [ ] `CapabilitiesManager` for tool pre-selection. +- [ ] `ContextManager` implementation (attention/relevance filtering). *(In PR #14 - waiting for review)* +- [x] `CapabilitiesManager` for tool pre-selection. - [ ] Define Sensor and Actuator capability contracts. diff --git a/packages/reference-implementation/package.json b/packages/reference-implementation/package.json index 083c25f..a0dc237 100644 --- a/packages/reference-implementation/package.json +++ b/packages/reference-implementation/package.json @@ -6,7 +6,7 @@ "types": "dist/index.d.ts", "scripts": { "build": "tsc", - "test": "node --test __tests__/*.test.js", + "test": "node --test dist/__tests__/*.test.js", "demo": "tsx src/demo.ts", "dev": "tsx --watch src/demo.ts" }, diff --git a/packages/reference-implementation/src/__tests__/capabilities-manager.test.ts b/packages/reference-implementation/src/__tests__/capabilities-manager.test.ts new file mode 100644 index 0000000..573d502 --- /dev/null +++ b/packages/reference-implementation/src/__tests__/capabilities-manager.test.ts @@ -0,0 +1,254 @@ +/** + * Tests for CapabilitiesManager and CapabilitiesManagerModule + */ + +import { describe, it, beforeEach } from 'node:test'; +import assert from 'node:assert'; +import { + CapabilitiesManager, + PatternBasedAnalysisStrategy, + type TaskAnalysisRequest, + type CapabilityAnalysisStrategy +} from '../capabilities-manager.js'; +import { CapabilitiesManagerModule } from '../capabilities-manager-module.js'; +import { ReferenceCognitiveBus } from '../cognitive-bus.js'; + +describe('PatternBasedAnalysisStrategy', () => { + let strategy: PatternBasedAnalysisStrategy; + + beforeEach(() => { + strategy = new PatternBasedAnalysisStrategy(); + }); + + it('should suggest TTS for speech tasks', async () => { + const request: TaskAnalysisRequest = { + description: 'Please say hello to the user' + }; + + const suggestions = await strategy.analyze(request); + + assert(suggestions.length > 0, 'Should have suggestions'); + const ttsSuggestion = suggestions.find(s => s.id === 'tts'); + assert(ttsSuggestion !== undefined, 'Should suggest TTS'); + assert(ttsSuggestion.confidence > 0.8, 'Should have high confidence for TTS'); + }); + + it('should suggest vision system for image tasks', async () => { + const request: TaskAnalysisRequest = { + description: 'Take a photo and analyze the image' + }; + + const suggestions = await strategy.analyze(request); + + const visionSuggestion = suggestions.find(s => s.id === 'vision'); + assert(visionSuggestion !== undefined, 'Should suggest vision system'); + assert(visionSuggestion.confidence > 0.7, 'Should have good confidence for vision'); + }); + + it('should suggest semantic memory for search tasks', async () => { + const request: TaskAnalysisRequest = { + description: 'Find information about artificial intelligence' + }; + + const suggestions = await strategy.analyze(request); + + const memorySuggestion = suggestions.find(s => s.id === 'semantic-memory'); + assert(memorySuggestion !== undefined, 'Should suggest semantic memory'); + assert(memorySuggestion.confidence > 0.7, 'Should have good confidence for memory'); + }); + + it('should boost confidence for available tools', async () => { + const request: TaskAnalysisRequest = { + description: 'Send a message to the user', + availableTools: ['messaging'] + }; + + const suggestions = await strategy.analyze(request); + + const messagingSuggestion = suggestions.find(s => s.id === 'messaging'); + assert(messagingSuggestion !== undefined, 'Should suggest messaging'); + assert(messagingSuggestion.reason.includes('tool available in environment'), + 'Should note tool availability'); + }); + + it('should handle empty descriptions gracefully', async () => { + const request: TaskAnalysisRequest = { + description: '' + }; + + const suggestions = await strategy.analyze(request); + assert.deepStrictEqual(suggestions, [], 'Should return empty array for empty description'); + }); +}); + +describe('CapabilitiesManager', () => { + let manager: CapabilitiesManager; + + beforeEach(() => { + manager = new CapabilitiesManager({ + strategy: new PatternBasedAnalysisStrategy(), + maxSuggestions: 5, + minConfidence: 0.2 + }); + }); + + it('should analyze tasks and return structured results', async () => { + const request: TaskAnalysisRequest = { + description: 'Write a summary and send it via email', + priority: 'medium' + }; + + const analysis = await manager.analyzeTask(request); + + assert.deepStrictEqual(analysis.task, request, 'Should preserve original task'); + assert(Array.isArray(analysis.suggestions), 'Should return suggestions array'); + assert(typeof analysis.timestamp === 'string', 'Should have timestamp'); + assert(typeof analysis.processingTime === 'number' && analysis.processingTime >= 0, 'Should track processing time'); + }); + + it('should limit suggestions to maxSuggestions', async () => { + const request: TaskAnalysisRequest = { + description: 'Complex task involving text writing, image analysis, web search, memory storage, and audio output' + }; + + const analysis = await manager.analyzeTask(request); + assert(analysis.suggestions.length <= 5, 'Should limit suggestions to maxSuggestions'); + }); + + it('should filter suggestions below minConfidence', async () => { + const request: TaskAnalysisRequest = { + description: 'Vague task description that might not match patterns well' + }; + + const analysis = await manager.analyzeTask(request); + + for (const suggestion of analysis.suggestions) { + assert(suggestion.confidence >= 0.2, 'All suggestions should meet minimum confidence'); + } + }); + + it('should return top suggestions correctly', async () => { + const topSuggestions = await manager.getTopSuggestions('Say hello to the user', 2); + + assert(topSuggestions.length <= 2, 'Should limit to requested count'); + if (topSuggestions.length > 1) { + assert(topSuggestions[0].confidence >= topSuggestions[1].confidence, + 'Should sort by confidence descending'); + } + }); + + it('should check capability recommendations', async () => { + const result = await manager.isCapabilityRecommended('Speak to the user', 'tts', 0.5); + + assert(result.recommended === true, 'Should recommend TTS for speech task'); + assert(typeof result.confidence === 'number' && result.confidence > 0.5, + 'Should provide high confidence'); + assert(typeof result.reason === 'string', 'Should provide reason'); + }); + + it('should handle analysis errors gracefully', async () => { + // Create manager with failing strategy + const failingStrategy: CapabilityAnalysisStrategy = { + analyze: async () => { throw new Error('Analysis failed'); } + }; + + const errorManager = new CapabilitiesManager({ + strategy: failingStrategy + }); + + const analysis = await errorManager.analyzeTask({ + description: 'Any task' + }); + + assert.deepStrictEqual(analysis.suggestions, [], 'Should return empty suggestions on error'); + assert(typeof analysis.processingTime === 'number' && analysis.processingTime >= 0, 'Should still track processing time'); + }); + + it('should track metrics when enabled', async () => { + const metricManager = new CapabilitiesManager({ + strategy: new PatternBasedAnalysisStrategy(), + enableMetrics: true + }); + + await metricManager.analyzeTask({ description: 'Test task' }); + + const metrics = metricManager.getMetrics(); + assert(metrics.totalAnalyses === 1, 'Should track analysis count'); + assert(typeof metrics.averageProcessingTime === 'number' && metrics.averageProcessingTime >= 0, 'Should track average processing time'); + }); +}); + +describe('CapabilitiesManagerModule', () => { + let module: CapabilitiesManagerModule; + let bus: ReferenceCognitiveBus; + + beforeEach(() => { + bus = new ReferenceCognitiveBus(); + module = new CapabilitiesManagerModule({ + enableLogging: false // Disable for tests + }); + }); + + it('should initialize and register correctly', async () => { + assert.strictEqual(module.id, 'processor.capabilities-manager'); + assert.strictEqual(module.name, 'Capabilities Manager'); + assert.deepStrictEqual(module.capabilities, ['processor']); + assert.strictEqual(module.status, 'registered'); + + await module.init(bus, {}); + assert.strictEqual(module.status, 'ready'); + }); + + it('should handle task analysis events', async () => { + await module.init(bus, {}); + + const analysisResults: any[] = []; + bus.on('capabilities.suggested', (event) => { + analysisResults.push(event.payload); + }); + + const taskRequest: TaskAnalysisRequest = { + description: 'Send a voice message to the user' + }; + + bus.emit('task.analyze', taskRequest); + + // Wait a bit for async processing + await new Promise(resolve => setTimeout(resolve, 50)); + + assert(analysisResults.length === 1, 'Should emit capabilities.suggested event'); + assert.deepStrictEqual(analysisResults[0].task, taskRequest, 'Should preserve task request'); + assert(Array.isArray(analysisResults[0].suggestions), 'Should include suggestions'); + }); + + it('should cleanup properly on destroy', async () => { + await module.init(bus, {}); + await module.destroy(); + + assert.strictEqual(module.status, 'stopped', 'Should be stopped after destroy'); + }); + + it('should provide manual analysis interface', async () => { + await module.init(bus, {}); + + const result = await module.analyzeTask({ + description: 'Test manual analysis' + }); + + assert(result !== null, 'Should return analysis result'); + assert(Array.isArray(result?.suggestions), 'Should include suggestions array'); + }); + + it('should provide metrics when available', async () => { + await module.init(bus, {}); + + // Perform an analysis to generate metrics + await module.analyzeTask({ + description: 'Test task for metrics' + }); + + const metrics = module.getMetrics(); + assert(metrics !== null, 'Should return metrics'); + assert(typeof metrics?.totalAnalyses === 'number', 'Should track analyses count'); + }); +}); \ No newline at end of file diff --git a/packages/reference-implementation/src/capabilities-demo.ts b/packages/reference-implementation/src/capabilities-demo.ts new file mode 100644 index 0000000..6b87a90 --- /dev/null +++ b/packages/reference-implementation/src/capabilities-demo.ts @@ -0,0 +1,188 @@ +/** + * Capabilities Demo — Interactive demo of the CapabilitiesManager. + * + * This demo shows how the CapabilitiesManager analyzes different types of tasks + * and suggests appropriate capabilities for handling them. + * + * Run with: node dist/capabilities-demo.js + */ + +import { + CapabilitiesManager, + PatternBasedAnalysisStrategy, + type TaskAnalysisRequest, + type CapabilitiesAnalysis +} from './capabilities-manager.js'; +import { CapabilitiesManagerModule } from './capabilities-manager-module.js'; +import { ReferenceCognitiveBus } from './cognitive-bus.js'; +import { ReferenceCognitiveRegistry } from './cognitive-registry.js'; + +function formatSuggestion(suggestion: any, index: number): string { + const confidence = (suggestion.confidence * 100).toFixed(1); + const costIcons: Record = { low: 'šŸ’š', medium: 'šŸ”¶', high: 'šŸ”“' }; + const costIcon = costIcons[suggestion.cost] || 'ā“'; + + return ` ${index + 1}. ${suggestion.name} (${confidence}%) ${costIcon}\n` + + ` Type: ${suggestion.type} | Cost: ${suggestion.cost}\n` + + ` Reason: ${suggestion.reason}`; +} + +async function demoTaskAnalysis() { + console.log('🧠 AMI Capabilities Manager Demo\n'); + console.log('═'.repeat(50)); + + // Create the capabilities manager + const manager = new CapabilitiesManager({ + strategy: new PatternBasedAnalysisStrategy(), + maxSuggestions: 5, + minConfidence: 0.1, + enableMetrics: true + }); + + // Test cases representing different types of tasks + const testCases: TaskAnalysisRequest[] = [ + { + description: 'Say hello to the user with a friendly voice', + priority: 'medium' + }, + { + description: 'Take a photo and analyze what objects are in the image', + availableTools: ['camera', 'vision', 'object-detection'] + }, + { + description: 'Find information about machine learning and summarize it', + priority: 'high' + }, + { + description: 'Remember this conversation and store important facts', + context: ['user preference: dark mode', 'location: office'] + }, + { + description: 'Send an urgent email notification to the team', + priority: 'urgent', + availableTools: ['email', 'slack', 'messaging'] + }, + { + description: 'Process audio input and convert speech to text for analysis', + constraints: { 'language': 'en-US', 'realtime': true } + } + ]; + + for (let i = 0; i < testCases.length; i++) { + const testCase = testCases[i]; + + console.log(`\nšŸ“‹ Test Case ${i + 1}: "${testCase.description}"`); + console.log(`Priority: ${testCase.priority || 'normal'}`); + + if (testCase.availableTools) { + console.log(`Available tools: ${testCase.availableTools.join(', ')}`); + } + + if (testCase.context) { + console.log(`Context: ${testCase.context.join(', ')}`); + } + + console.log('\nšŸ’” Capability Analysis Results:'); + + const analysis = await manager.analyzeTask(testCase); + + if (analysis.suggestions.length === 0) { + console.log(' No capabilities suggested for this task.'); + } else { + analysis.suggestions.forEach((suggestion, index) => { + console.log(formatSuggestion(suggestion, index)); + }); + } + + console.log(`\nā±ļø Processing time: ${analysis.processingTime}ms`); + console.log('─'.repeat(50)); + } + + // Show performance metrics + console.log('\nšŸ“Š Performance Metrics:'); + const metrics = manager.getMetrics(); + console.log(` Total analyses: ${metrics.totalAnalyses}`); + console.log(` Average processing time: ${metrics.averageProcessingTime.toFixed(2)}ms`); + console.log(` Accuracy score: ${metrics.accuracyScore.toFixed(2)}`); +} + +async function demoModuleIntegration() { + console.log('\n\nšŸ”„ Module Integration Demo\n'); + console.log('═'.repeat(50)); + + // Set up the cognitive system + const bus = new ReferenceCognitiveBus(); + const registry = new ReferenceCognitiveRegistry(bus); + + // Create and register the capabilities manager module + const capabilitiesModule = new CapabilitiesManagerModule({ + autoAnalyzeContext: true, + enableLogging: true + }); + + registry.register(capabilitiesModule); + await registry.initAll(); + + console.log('\nšŸŽÆ Testing event-driven analysis...\n'); + + // Set up event listeners + bus.on('capabilities.suggested', (event) => { + const analysis = event.payload as CapabilitiesAnalysis; + console.log(`šŸ“ˆ Capability suggestions for: "${analysis.task.description.substring(0, 40)}..."`); + + if (analysis.suggestions.length > 0) { + console.log(` Top suggestion: ${analysis.suggestions[0].name} (${(analysis.suggestions[0].confidence * 100).toFixed(1)}%)`); + console.log(` Total suggestions: ${analysis.suggestions.length}`); + } + console.log(''); + }); + + // Emit some task analysis requests + const tasks = [ + 'Create a presentation and display it on screen', + 'Listen for voice commands and respond with speech', + 'Analyze the current context and suggest next actions', + 'Store these meeting notes in long-term memory' + ]; + + for (const task of tasks) { + console.log(`šŸš€ Analyzing: "${task}"`); + bus.emit('task.analyze', { + description: task, + priority: 'medium' + }); + + // Brief pause for processing + await new Promise(resolve => setTimeout(resolve, 50)); + } + + // Test context change auto-analysis + console.log('šŸ” Testing auto-analysis on context change...\n'); + bus.emit('context.changed', { + focusTopics: ['audio processing', 'real-time transcription'], + summary: 'User is working with speech recognition system' + }); + + await new Promise(resolve => setTimeout(resolve, 100)); + + // Clean shutdown + await registry.destroyAll(); + console.log('āœ… Demo completed successfully!'); +} + +// Main demo runner +async function runDemo() { + try { + await demoTaskAnalysis(); + await demoModuleIntegration(); + } catch (error) { + console.error('āŒ Demo failed:', error); + } +} + +// Run the demo if this file is executed directly +if (import.meta.url === `file://${process.argv[1]}`) { + runDemo(); +} + +export { runDemo as capabilitiesDemo }; \ No newline at end of file diff --git a/packages/reference-implementation/src/capabilities-manager-module.ts b/packages/reference-implementation/src/capabilities-manager-module.ts new file mode 100644 index 0000000..39637ac --- /dev/null +++ b/packages/reference-implementation/src/capabilities-manager-module.ts @@ -0,0 +1,207 @@ +/** + * CapabilitiesManagerModule — CapabilitiesManager wrapped as a CognitiveModule. + * + * Subscribes to `task.analyze` events on the CognitiveBus, analyzes the task + * for appropriate capabilities, and emits `capabilities.suggested` events + * with ranked tool/module recommendations. + * + * This enables intelligent tool pre-selection throughout the cognitive system. + * + * Events: + * - Listens to: 'task.analyze' + * - Emits: 'capabilities.suggested' + * + * @see ROADMAP.md — "CapabilitiesManager for tool pre-selection" + * @see DEC-003 — Capability-based Modular Architecture + */ + +import type { + CognitiveBus, + CognitiveCapability, + CognitiveModule, +} from '@ami/skeleton'; + +import { + CapabilitiesManager, + PatternBasedAnalysisStrategy, + type CapabilitiesManagerConfig, + type TaskAnalysisRequest, + type CapabilitiesAnalysis +} from './capabilities-manager.js'; + +export interface CapabilitiesManagerModuleConfig { + /** Configuration for the underlying capabilities manager */ + managerConfig?: Partial; + /** Whether to auto-analyze context changes */ + autoAnalyzeContext?: boolean; + /** Whether to log analysis results */ + enableLogging?: boolean; +} + +export class CapabilitiesManagerModule implements CognitiveModule { + readonly id = 'processor.capabilities-manager'; + readonly name = 'Capabilities Manager'; + readonly capabilities: CognitiveCapability[] = ['processor']; + + status: CognitiveModule['status'] = 'registered'; + + private bus: CognitiveBus | null = null; + private manager: CapabilitiesManager | null = null; + private readonly moduleConfig: CapabilitiesManagerModuleConfig; + + constructor(config: CapabilitiesManagerModuleConfig = {}) { + this.moduleConfig = config; + } + + async init(bus: CognitiveBus, _config: Record): Promise { + this.status = 'initializing'; + + this.bus = bus; + + // Create the capabilities manager with default strategy + const managerConfig: CapabilitiesManagerConfig = { + strategy: new PatternBasedAnalysisStrategy(), + maxSuggestions: 10, + minConfidence: 0.1, + enableMetrics: true, + ...this.moduleConfig.managerConfig + }; + + this.manager = new CapabilitiesManager(managerConfig); + + // Subscribe to task analysis requests + bus.on('task.analyze', this.handleTaskAnalysis); + + // Optionally subscribe to context changes for auto-analysis + if (this.moduleConfig.autoAnalyzeContext) { + bus.on('context.changed', this.handleContextChange); + } + + this.status = 'ready'; + + if (this.moduleConfig.enableLogging) { + console.log('[CapabilitiesManager] Module initialized and ready for task analysis'); + } + } + + async destroy(): Promise { + if (this.bus) { + this.bus.off('task.analyze', this.handleTaskAnalysis); + if (this.moduleConfig.autoAnalyzeContext) { + this.bus.off('context.changed', this.handleContextChange); + } + } + this.bus = null; + this.manager = null; + this.status = 'stopped'; + } + + /** + * Handle an incoming task analysis request. + * Analyzes the task and emits capability suggestions. + */ + private handleTaskAnalysis = async (event: { payload: TaskAnalysisRequest }): Promise => { + if (!this.manager || !this.bus) return; + + try { + const analysis: CapabilitiesAnalysis = await this.manager.analyzeTask(event.payload); + + if (this.moduleConfig.enableLogging && analysis.suggestions.length > 0) { + console.log( + `[CapabilitiesManager] Analyzed task: "${analysis.task.description.substring(0, 50)}..." ` + + `→ ${analysis.suggestions.length} suggestions (${analysis.processingTime}ms)` + ); + + // Log top 3 suggestions + const topSuggestions = analysis.suggestions.slice(0, 3); + for (const suggestion of topSuggestions) { + console.log( + ` • ${suggestion.name} (${suggestion.confidence.toFixed(2)}) - ${suggestion.reason}` + ); + } + } + + // Emit the analysis results + this.bus.emit('capabilities.suggested', analysis); + + } catch (error) { + console.error('[CapabilitiesManagerModule] Task analysis failed:', error); + this.status = 'degraded'; + + if (this.bus) { + this.bus.emit('module.degraded', { + moduleId: this.id, + reason: `Task analysis failed: ${String(error)}` + }); + } + } + }; + + /** + * Handle context changes for auto-analysis. + * Extracts potential tasks from context and suggests capabilities. + */ + private handleContextChange = async (event: { payload: any }): Promise => { + if (!this.manager || !this.bus || !this.moduleConfig.autoAnalyzeContext) return; + + try { + // Extract task hints from context change + const contextPayload = event.payload; + let taskDescription = ''; + + // Try to extract meaningful task description from context + if (typeof contextPayload === 'string') { + taskDescription = contextPayload; + } else if (contextPayload?.focusTopics && Array.isArray(contextPayload.focusTopics)) { + taskDescription = contextPayload.focusTopics.join(' '); + } else if (contextPayload?.summary) { + taskDescription = contextPayload.summary; + } + + if (taskDescription && taskDescription.length > 10) { + const analysisRequest: TaskAnalysisRequest = { + description: taskDescription, + context: contextPayload?.context || [], + priority: 'low' // Auto-analysis has low priority + }; + + const analysis = await this.manager.analyzeTask(analysisRequest); + + // Only emit if we have confident suggestions + const confidentSuggestions = analysis.suggestions.filter(s => s.confidence >= 0.6); + if (confidentSuggestions.length > 0) { + this.bus.emit('capabilities.suggested', { + ...analysis, + suggestions: confidentSuggestions + }); + + if (this.moduleConfig.enableLogging) { + console.log( + `[CapabilitiesManager] Auto-analyzed context change → ${confidentSuggestions.length} suggestions` + ); + } + } + } + } catch (error) { + if (this.moduleConfig.enableLogging) { + console.warn('[CapabilitiesManagerModule] Context auto-analysis failed:', error); + } + // Don't degrade the module for auto-analysis failures + } + }; + + /** + * Get current performance metrics from the capabilities manager. + */ + getMetrics() { + return this.manager?.getMetrics() || null; + } + + /** + * Manually trigger task analysis (for testing/debugging). + */ + async analyzeTask(request: TaskAnalysisRequest): Promise { + if (!this.manager) return null; + return await this.manager.analyzeTask(request); + } +} \ No newline at end of file diff --git a/packages/reference-implementation/src/capabilities-manager.ts b/packages/reference-implementation/src/capabilities-manager.ts new file mode 100644 index 0000000..62df37c --- /dev/null +++ b/packages/reference-implementation/src/capabilities-manager.ts @@ -0,0 +1,380 @@ +/** + * CapabilitiesManager — Tool pre-selection and capability routing. + * + * This module analyzes incoming tasks/requests and suggests the most appropriate + * cognitive modules and tools to handle them. It acts as an intelligent router + * that helps the system decide which capabilities to activate for a given context. + * + * Features: + * - Tool pre-selection based on task analysis + * - Capability ranking by relevance and availability + * - Context-aware routing suggestions + * - Performance tracking for continuous improvement + * + * Events: + * - Listens to: 'task.analyze' (task description + context) + * - Emits: 'capabilities.suggested' (ranked list of tools/modules) + * + * @see ROADMAP.md — "CapabilitiesManager for tool pre-selection" + * @see DEC-003 — Capability-based Modular Architecture + */ + +export interface TaskAnalysisRequest { + /** The task description or user intent */ + description: string; + /** Current conversation context */ + context?: string[]; + /** Available tools/capabilities in the environment */ + availableTools?: string[]; + /** User preferences or constraints */ + constraints?: Record; + /** Priority level */ + priority?: 'low' | 'medium' | 'high' | 'urgent'; +} + +export interface CapabilitySuggestion { + /** Capability or tool identifier */ + id: string; + /** Capability type */ + type: 'sensor' | 'processor' | 'actuator' | 'memory' | 'tool'; + /** Human-readable name */ + name: string; + /** Confidence score (0-1) */ + confidence: number; + /** Why this capability is suggested */ + reason: string; + /** Estimated cost/complexity */ + cost: 'low' | 'medium' | 'high'; + /** Prerequisites or dependencies */ + dependencies?: string[]; +} + +export interface CapabilitiesAnalysis { + /** Original task request */ + task: TaskAnalysisRequest; + /** Suggested capabilities ranked by relevance */ + suggestions: CapabilitySuggestion[]; + /** Analysis timestamp */ + timestamp: string; + /** Processing time in ms */ + processingTime: number; +} + +/** + * Strategy interface for capability analysis. + * Enables different approaches to tool pre-selection. + */ +export interface CapabilityAnalysisStrategy { + analyze(request: TaskAnalysisRequest): Promise; +} + +/** + * Configuration for the CapabilitiesManager. + */ +export interface CapabilitiesManagerConfig { + /** Strategy for analyzing tasks and suggesting capabilities */ + strategy: CapabilityAnalysisStrategy; + /** Maximum number of suggestions to return */ + maxSuggestions?: number; + /** Minimum confidence threshold for suggestions */ + minConfidence?: number; + /** Whether to track performance metrics */ + enableMetrics?: boolean; +} + +/** + * Pattern-based capability analysis strategy. + * + * Uses keyword patterns and heuristics to match tasks to capabilities. + * This is a simple, fast implementation suitable for most use cases. + */ +export class PatternBasedAnalysisStrategy implements CapabilityAnalysisStrategy { + private readonly patterns = new Map([ + // Text and communication + ['write|compose|draft|create.*text', [{ + id: 'text-generator', + type: 'processor', + name: 'Text Generator', + confidence: 0.9, + reason: 'Task involves text creation', + cost: 'medium' + }]], + + ['send|message|email|notify|alert', [{ + id: 'messaging', + type: 'actuator', + name: 'Messaging System', + confidence: 0.85, + reason: 'Task involves sending communications', + cost: 'low' + }]], + + // Audio and speech + ['speak|say|voice|audio|tts|speech', [{ + id: 'tts', + type: 'actuator', + name: 'Text-to-Speech', + confidence: 0.9, + reason: 'Task requires audio output', + cost: 'medium' + }]], + + ['listen|hear|transcribe|stt', [{ + id: 'stt', + type: 'sensor', + name: 'Speech-to-Text', + confidence: 0.9, + reason: 'Task involves audio input processing', + cost: 'medium' + }]], + + // Visual and image processing + ['image|photo|picture|visual|see|look|camera', [{ + id: 'vision', + type: 'sensor', + name: 'Vision System', + confidence: 0.85, + reason: 'Task involves visual processing', + cost: 'high' + }]], + + ['display|show|render|ui|screen', [{ + id: 'display', + type: 'actuator', + name: 'Display System', + confidence: 0.8, + reason: 'Task requires visual output', + cost: 'medium' + }]], + + // Memory and knowledge + ['remember|store|save|archive|memory', [{ + id: 'episodic-memory', + type: 'memory', + name: 'Episodic Memory', + confidence: 0.8, + reason: 'Task involves storing experiences', + cost: 'low' + }]], + + ['search|find|lookup|query|recall|know', [{ + id: 'semantic-memory', + type: 'memory', + name: 'Semantic Memory', + confidence: 0.85, + reason: 'Task involves knowledge retrieval', + cost: 'medium' + }]], + + // Analysis and processing + ['analyze|process|distill|extract|learn', [{ + id: 'knowledge-distiller', + type: 'processor', + name: 'Knowledge Distiller', + confidence: 0.75, + reason: 'Task involves data analysis or learning', + cost: 'high' + }]], + + // Control and automation + ['control|automate|execute|run|manage', [{ + id: 'automation', + type: 'actuator', + name: 'Automation System', + confidence: 0.7, + reason: 'Task involves system control', + cost: 'medium' + }]], + + // Web and external services + ['web|internet|browse|url|api|http', [{ + id: 'web-access', + type: 'actuator', + name: 'Web Access', + confidence: 0.8, + reason: 'Task requires internet connectivity', + cost: 'medium' + }]], + + // File operations + ['file|document|read|write|edit|folder', [{ + id: 'file-system', + type: 'actuator', + name: 'File System', + confidence: 0.85, + reason: 'Task involves file operations', + cost: 'low' + }]] + ]); + + async analyze(request: TaskAnalysisRequest): Promise { + const suggestions: CapabilitySuggestion[] = []; + const description = request.description.toLowerCase(); + + // Score each pattern against the task description + for (const [pattern, capabilities] of this.patterns) { + const regex = new RegExp(pattern, 'i'); + if (regex.test(description)) { + suggestions.push(...capabilities); + } + } + + // Context analysis - boost suggestions if related tools are available + if (request.availableTools) { + for (const suggestion of suggestions) { + if (request.availableTools.includes(suggestion.id)) { + suggestion.confidence = Math.min(1.0, suggestion.confidence + 0.1); + suggestion.reason += ' (tool available in environment)'; + } + } + } + + // Priority adjustment + if (request.priority === 'urgent' || request.priority === 'high') { + for (const suggestion of suggestions) { + if (suggestion.cost === 'low' || suggestion.cost === 'medium') { + suggestion.confidence = Math.min(1.0, suggestion.confidence + 0.05); + } + } + } + + // Remove duplicates and sort by confidence + const uniqueSuggestions = Array.from( + new Map(suggestions.map(s => [s.id, s])).values() + ).sort((a, b) => b.confidence - a.confidence); + + return uniqueSuggestions; + } +} + +/** + * Main CapabilitiesManager class. + * + * Provides intelligent tool pre-selection and capability routing for cognitive tasks. + */ +export class CapabilitiesManager { + private readonly config: Required; + private readonly metrics: { + totalAnalyses: number; + averageProcessingTime: number; + accuracyScore: number; + } = { + totalAnalyses: 0, + averageProcessingTime: 0, + accuracyScore: 0 + }; + + constructor(config: CapabilitiesManagerConfig) { + this.config = { + maxSuggestions: 10, + minConfidence: 0.1, + enableMetrics: true, + ...config + }; + } + + /** + * Analyze a task and suggest appropriate capabilities. + */ + async analyzeTask(request: TaskAnalysisRequest): Promise { + const startTime = performance.now(); + + try { + // Run the analysis strategy + const rawSuggestions = await this.config.strategy.analyze(request); + + // Apply filtering and limits + const filteredSuggestions = rawSuggestions + .filter(s => s.confidence >= this.config.minConfidence) + .slice(0, this.config.maxSuggestions); + + const processingTime = Math.max(0.001, performance.now() - startTime); + + // Update metrics if enabled + if (this.config.enableMetrics) { + this.updateMetrics(processingTime); + } + + return { + task: request, + suggestions: filteredSuggestions, + timestamp: new Date().toISOString(), + processingTime + }; + } catch (error) { + const processingTime = Math.max(0.001, performance.now() - startTime); + console.error('[CapabilitiesManager] Analysis failed:', error); + + return { + task: request, + suggestions: [], + timestamp: new Date().toISOString(), + processingTime + }; + } + } + + /** + * Get the top N capability suggestions for a task. + */ + async getTopSuggestions( + description: string, + count: number = 3, + context?: Partial + ): Promise { + const analysis = await this.analyzeTask({ + description, + ...context + }); + + return analysis.suggestions.slice(0, count); + } + + /** + * Check if a specific capability is recommended for a task. + */ + async isCapabilityRecommended( + description: string, + capabilityId: string, + minConfidence: number = 0.5 + ): Promise<{ recommended: boolean; confidence?: number; reason?: string }> { + const analysis = await this.analyzeTask({ description }); + const suggestion = analysis.suggestions.find(s => s.id === capabilityId); + + if (!suggestion) { + return { recommended: false }; + } + + return { + recommended: suggestion.confidence >= minConfidence, + confidence: suggestion.confidence, + reason: suggestion.reason + }; + } + + /** + * Get performance metrics. + */ + getMetrics() { + return { ...this.metrics }; + } + + /** + * Reset performance metrics. + */ + resetMetrics(): void { + this.metrics.totalAnalyses = 0; + this.metrics.averageProcessingTime = 0; + this.metrics.accuracyScore = 0; + } + + private updateMetrics(processingTime: number): void { + this.metrics.totalAnalyses++; + + // Update rolling average processing time + const weight = 1 / this.metrics.totalAnalyses; + this.metrics.averageProcessingTime = + (1 - weight) * this.metrics.averageProcessingTime + + weight * processingTime; + } +} \ No newline at end of file diff --git a/packages/reference-implementation/src/index.ts b/packages/reference-implementation/src/index.ts index f04d91c..3bfa53f 100644 --- a/packages/reference-implementation/src/index.ts +++ b/packages/reference-implementation/src/index.ts @@ -9,5 +9,18 @@ export { ReferenceCognitiveBus } from './cognitive-bus.js'; export { ReferenceCognitiveRegistry } from './cognitive-registry.js'; export { DistillerModule } from './distiller-module.js'; export type { DistillerModuleConfig } from './distiller-module.js'; +export { + CapabilitiesManager, + PatternBasedAnalysisStrategy +} from './capabilities-manager.js'; +export type { + TaskAnalysisRequest, + CapabilitySuggestion, + CapabilitiesAnalysis, + CapabilityAnalysisStrategy, + CapabilitiesManagerConfig +} from './capabilities-manager.js'; +export { CapabilitiesManagerModule } from './capabilities-manager-module.js'; +export type { CapabilitiesManagerModuleConfig } from './capabilities-manager-module.js'; export { Ami } from './ami.js'; export type { AmiConfig } from './ami.js';