aboutsummaryrefslogtreecommitdiff
path: root/modules/core/module-predictor.js
diff options
context:
space:
mode:
authorAgando <roduccomm@gmail.com>2026-06-05 18:01:01 +0200
committerAgando <roduccomm@gmail.com>2026-06-05 18:01:01 +0200
commitb538577cd0a4292e4cd4ca1c9f9829fc7e133caa (patch)
tree04d76bf4f0d680cdb26b1a205481bc8628cd77b4 /modules/core/module-predictor.js
parent5092f9a0ce02691dc476d7b64fa731a1518dd834 (diff)
auto: 2026-06-05_18:01 changes
Diffstat (limited to 'modules/core/module-predictor.js')
-rw-r--r--modules/core/module-predictor.js279
1 files changed, 279 insertions, 0 deletions
diff --git a/modules/core/module-predictor.js b/modules/core/module-predictor.js
new file mode 100644
index 0000000..312325b
--- /dev/null
+++ b/modules/core/module-predictor.js
@@ -0,0 +1,279 @@
+// module-predictor.js — Predictive resource allocation engine
+// Analyzes historical telemetry + memory graph data to predict task duration/turns
+// Commands: predict, predictor-status, predictor-config
+class PredictorModule {
+ constructor() {
+ this.commands = {};
+ this.terminal = null;
+ this._dataPoints = [];
+ this._predictions = 0;
+ this._accuracy = { correct: 0, total: 0 };
+ this._CONFIDENCE_THRESHOLD = 0.5;
+ this._MIN_DATA_POINTS = 5;
+ this._loaded = false;
+ }
+
+ getHelpInfo() {
+ return {
+ 'predict': 'predict <task description> — Predict task duration, tool turns, and confidence',
+ 'predictor-status': 'predictor-status — Model accuracy, data points, prediction count',
+ 'predictor-config': 'predictor-config <key> <value> — Tune: confidence-threshold, min-data-points',
+ };
+ }
+
+ registerCommands() {
+ this.commands['predict'] = (args) => this.predict(args.join(' '));
+ this.commands['predictor-status'] = () => this.showStatus();
+ this.commands['predictor-config'] = (args) => this.setConfig(args);
+ }
+
+ init(terminal) {
+ this.terminal = terminal;
+ this._loadFromVFS();
+ this._loaded = true;
+ this.terminal.print('📊 Predictor loaded — analyzing task patterns', 'info');
+ }
+
+ // ─── Prediction Engine ───
+
+ async predict(task) {
+ if (!task) return this.terminal.print('Usage: predict <task description>', 'error');
+
+ const startTime = performance.now();
+
+ // Extract features
+ const features = this._extractFeatures(task);
+
+ // Find similar historical tasks
+ const similar = this._findSimilar(features);
+
+ // Generate prediction
+ const prediction = this._computePrediction(similar, features);
+
+ // Record this prediction for accuracy tracking
+ const duration = ((performance.now() - startTime) / 1000).toFixed(2);
+
+ this._predictions++;
+ this._dataPoints.push({
+ features,
+ predictedDuration: prediction.duration,
+ predictedTurns: prediction.toolTurns,
+ confidence: prediction.confidence,
+ similarCount: similar.length,
+ timestamp: new Date().toISOString()
+ });
+
+ this._debounceSave();
+
+ // Display result
+ this.terminal.print('📈 Task Prediction', 'header');
+ this.terminal.print(` Task: ${task.substring(0, 50)}${task.length > 50 ? '...' : ''}`, 'info');
+
+ if (prediction.confidence >= this._CONFIDENCE_THRESHOLD) {
+ this.terminal.print(` ⏱ Estimated duration: ${prediction.duration}s`, 'info');
+ this.terminal.print(` 🔧 Estimated tool turns: ${prediction.toolTurns}`, 'info');
+ this.terminal.print(` 📊 Confidence: ${(prediction.confidence * 100).toFixed(0)}% (based on ${similar.length} similar tasks)`, 'success');
+ } else if (similar.length > 0) {
+ this.terminal.print(` ⏱ Estimated duration: ${prediction.duration}s (low confidence)`, 'warning');
+ this.terminal.print(` 🔧 Tool turns: ~${prediction.toolTurns}`, 'warning');
+ this.terminal.print(` 📊 Confidence: ${(prediction.confidence * 100).toFixed(0)}% — only ${similar.length} similar data point(s)`, 'warning');
+ this.terminal.print(' Tip: More task data will improve predictions.', 'info');
+ } else {
+ this.terminal.print(' ❓ No historical data to base prediction on.', 'warning');
+ this.terminal.print(' Tip: Run `orchestrate` with various tasks to build a baseline.', 'info');
+ // Provide hardcoded defaults
+ this.terminal.print(' Default estimate: ~30s duration, ~5 tool turns', 'info');
+ }
+
+ // If got memory graph, offer recommendations
+ if (prediction.confidence >= this._CONFIDENCE_THRESHOLD && prediction.duration > 30) {
+ this.terminal.print(` 💡 Recommendation: Set TTL to ${Math.ceil(prediction.duration * 1.5)}s for predictable completion`, 'info');
+ }
+
+ return prediction;
+ }
+
+ _extractFeatures(task) {
+ const lower = task.toLowerCase();
+ return {
+ length: task.length,
+ wordCount: task.split(/\s+/).length,
+ hasSearch: /\b(search|find|look)\b/.test(lower),
+ hasWrite: /\b(write|create|save|edit)\b/.test(lower),
+ hasDelete: /\b(delete|remove|rm)\b/.test(lower),
+ hasInstall: /\b(install|wpm|package)\b/.test(lower),
+ hasRead: /\b(read|cat|view|list)\b/.test(lower),
+ hasWeb: /\b(fetch|url|http|web)\b/.test(lower),
+ hasOrchestrate: /\b(orchestrat|delegate|agent)\b/.test(lower),
+ hasModule: /\b(module|edit_module|view_module)\b/.test(lower),
+ complexity: Math.min(task.length / 50, 10), // 0-10 scale
+ keywords: [...new Set(lower.split(/\s+/).filter(w => w.length > 3))].slice(0, 10)
+ };
+ }
+
+ _findSimilar(features) {
+ return this._dataPoints.filter(dp => {
+ // Skip predictions (only use actual executions with observed results)
+ if (!dp.observedDuration) return false;
+ const f = dp.features;
+ if (!f) return false;
+ // Feature similarity scoring
+ let score = 0;
+ const matches = [
+ features.hasSearch === f.hasSearch,
+ features.hasWrite === f.hasWrite,
+ features.hasDelete === f.hasDelete,
+ features.hasInstall === f.hasInstall,
+ features.hasRead === f.hasRead,
+ features.hasWeb === f.hasWeb,
+ features.hasOrchestrate === f.hasOrchestrate,
+ features.hasModule === f.hasModule,
+ ];
+ score = matches.filter(Boolean).length / matches.length;
+ // Keyword overlap bonus
+ const kOverlap = features.keywords.filter(k => f.keywords?.includes(k)).length;
+ const kScore = kOverlap / Math.max(features.keywords.length, 1);
+ score = score * 0.6 + kScore * 0.4;
+ return score > 0.3;
+ });
+ }
+
+ _computePrediction(similar, features) {
+ if (similar.length === 0) {
+ // Hardcoded defaults
+ let baseDuration = 15;
+ let baseTurns = 3;
+ if (features.hasSearch || features.hasWeb) { baseDuration = 25; baseTurns = 5; }
+ if (features.hasWrite) { baseDuration = 20; baseTurns = 4; }
+ if (features.hasOrchestrate) { baseDuration = 40; baseTurns = 8; }
+ if (features.complexity > 5) baseDuration *= 1.5;
+ return {
+ duration: baseDuration,
+ toolTurns: baseTurns,
+ confidence: 0.1,
+ similarCount: 0
+ };
+ }
+
+ const avgDuration = similar.reduce((s, dp) => s + (dp.observedDuration || 30), 0) / similar.length;
+ const avgTurns = similar.reduce((s, dp) => s + (dp.observedTurns || 3), 0) / similar.length;
+ const confidence = Math.min(similar.length / 20, 1) * 0.8 + 0.1;
+
+ // Apply task complexity multiplier
+ const complexityMultiplier = 1 + (features.complexity - 3) * 0.1;
+ const adjustedDuration = Math.round(avgDuration * Math.max(complexityMultiplier, 0.5));
+
+ return {
+ duration: adjustedDuration,
+ toolTurns: Math.round(avgTurns),
+ confidence: Math.min(confidence, 0.95),
+ similarCount: similar.length
+ };
+ }
+
+ // ─── Learning from Results ───
+
+ recordResult(features, duration, toolTurns, success) {
+ this._dataPoints.push({
+ features,
+ observedDuration: duration,
+ observedTurns: toolTurns,
+ success,
+ timestamp: new Date().toISOString()
+ });
+ this._accuracy.total++;
+ // Compare with last prediction for this feature set
+ const lastPrediction = [...this._dataPoints]
+ .reverse()
+ .find(dp => dp.predictedDuration && dp.features?.hasSearch === features.hasSearch);
+ if (lastPrediction) {
+ const error = Math.abs(lastPrediction.predictedDuration - duration) / duration;
+ if (error < 0.3) this._accuracy.correct++;
+ }
+ this._debounceSave();
+ }
+
+ // ─── Commands ───
+
+ showStatus() {
+ const actualExecutions = this._dataPoints.filter(dp => dp.observedDuration).length;
+ const accPct = this._accuracy.total > 0
+ ? ((this._accuracy.correct / this._accuracy.total) * 100).toFixed(0)
+ : 'N/A';
+
+ this.terminal.print('📊 Predictor Status', 'header');
+ this.terminal.print(` Total data points: ${this._dataPoints.length}`, 'info');
+ this.terminal.print(` Actual executions: ${actualExecutions}`, 'info');
+ this.terminal.print(` Predictions made: ${this._predictions}`, 'info');
+ this.terminal.print(` Accuracy: ${accPct}% (${this._accuracy.correct}/${this._accuracy.total})`, 'info');
+ this.terminal.print(` Confidence threshold: ${this._CONFIDENCE_THRESHOLD}`, 'info');
+ this.terminal.print(` Min data points: ${this._MIN_DATA_POINTS}`, 'info');
+ }
+
+ setConfig(args) {
+ if (args.length < 2) return this.terminal.print('Usage: predictor-config <key> <value>', 'error');
+ const key = args[0];
+ const val = args[1];
+
+ const configMap = {
+ 'confidence-threshold': () => {
+ const n = parseFloat(val);
+ if (isNaN(n) || n < 0 || n > 1) return 'Must be 0.0-1.0';
+ this._CONFIDENCE_THRESHOLD = n;
+ return `confidence-threshold set to ${n}`;
+ },
+ 'min-data-points': () => {
+ const n = parseInt(val);
+ if (isNaN(n) || n < 1) return 'Must be >= 1';
+ this._MIN_DATA_POINTS = n;
+ return `min-data-points set to ${n}`;
+ },
+ };
+
+ if (!configMap[key]) return this.terminal.print(`Unknown: ${key}. Valid: ${Object.keys(configMap).join(', ')}`, 'error');
+ const msg = configMap[key]();
+ this.terminal.print(`✅ ${msg}`, 'success');
+ }
+
+ // ─── Persistence ───
+
+ _loadFromVFS() {
+ try {
+ const fs = this.terminal.fs;
+ if (!fs) return;
+ const data = fs.fsReadJSON('/system/metrics/predictor.json');
+ if (data) {
+ this._dataPoints = data.dataPoints || [];
+ this._predictions = data.predictions || 0;
+ this._accuracy = data.accuracy || { correct: 0, total: 0 };
+ }
+ } catch (e) {
+ console.warn('[Predictor] Load error (fresh start):', e.message);
+ }
+ }
+
+ _saveNow() {
+ try {
+ const fs = this.terminal.fs;
+ if (!fs) return;
+ if (!fs.readDir('/system/metrics')) fs.mkdirRecursive('/system/metrics');
+ fs.fsWriteJSON('/system/metrics/predictor.json', {
+ dataPoints: this._dataPoints.slice(-500), // keep last 500
+ predictions: this._predictions,
+ accuracy: this._accuracy
+ });
+ } catch (e) {
+ console.error('[Predictor] Save error:', e.message);
+ }
+ }
+
+ _debounceSave() {
+ clearTimeout(this._saveTimer);
+ this._saveTimer = setTimeout(() => this._saveNow(), 3000);
+ }
+}
+
+// Required for module system
+if (typeof window !== 'undefined') {
+ window.PredictorModule = PredictorModule;
+} \ No newline at end of file