Files
APAW/agent-evolution/scripts/export-data-direct.cjs
Deploy Bot 9b0f160587 feat(dashboard): unified data pipeline, verified benchmarks, and browser testing
- build-standalone-fixed.cjs: reads from 4 real sources (agents md, kilo-meta.json, model-benchmarks-verified.json, agent-versions.json); computes recommendations dynamically
- build-standalone-direct.cjs: direct data export + HTML embed pipeline
- dashboard-smoke-test.ts: Playwright E2E smoke test covering all 6 tabs
- model-benchmarks-verified.json: verified IF scores from artificialanalysis.ai for 15 models (SWE-bench unverifiable → null)
- agent-versions.json: 347 git history entries extracted for 34 agents
- kilo-meta.json: prompt-optimizer → qwen3.5-122b, memory-manager → deepseek-v4-pro-max
- index.html: Recommendations tab rendering updated for dynamic data
- Dockerfile + docker-compose.yml: mount-driven build, no image rebuild for data changes
- README.md: updated dashboard docs and verified benchmark sources
2026-05-25 21:05:14 +01:00

190 lines
7.4 KiB
JavaScript

#!/usr/bin/env node
/**
* Export unified dashboard data to JSON by reading files directly:
* - .kilo/agents/*.md (YAML frontmatter: model, mode, color, description)
* - kilo-meta.json (model assignments, categories, fallback info)
* - model-benchmarks-verified.json (IF scores, context window)
* - agent-versions.json (real history with dates, commits, reasons)
*
* Run: node agent-evolution/scripts/export-data-direct.cjs
*/
const fs = require('fs');
const path = require('path');
const META_FILE = path.join(__dirname, '../../kilo-meta.json');
const BENCHMARK_FILE = path.join(__dirname, '../data/model-benchmarks-verified.json');
const AGENTS_DIR = path.join(__dirname, '../../.kilo/agents');
const HISTORY_FILE = path.join(__dirname, '../data/agent-versions.json');
const OUTPUT_FILE = path.join(__dirname, '../data/evolution-export.json');
// ---------- YAML frontmatter parser (lightweight, no deps) ----------
function parseYamlFrontmatter(text) {
if (!text.startsWith('---')) return null;
const end = text.indexOf('---', 4);
if (end === -1) return null;
const lines = text.slice(4, end).trim().split('\n');
const fm = {};
for (const raw of lines) {
const line = raw.trim();
if (!line || line.startsWith('#')) continue;
const m = line.match(/^([a-z_]+):\s*(.*)$/);
if (!m) continue;
const key = m[1];
let val = m[2].replace(/"/g, '').trim();
fm[key] = val;
}
return fm;
}
// ---------- Compute composite score (v2 formula) ----------
function computeScore(modelName, bmMap) {
const key = Object.keys(bmMap).find(k => modelName.includes(k));
if (!key) return 60;
const m = bmMap[key];
let score = (m.if_score || 70) * 0.85;
const ctx = m.context_window || 128;
score += ctx >= 1000 ? 15 : ctx >= 256 ? 8 : 4;
return Math.round(Math.min(100, score));
}
// ---------- Main ----------
try {
// Load model benchmarks
console.log('Reading benchmarks from:', BENCHMARK_FILE);
const bmData = JSON.parse(fs.readFileSync(BENCHMARK_FILE, 'utf-8'));
const bmMap = {};
for (const m of bmData.models || []) {
bmMap[m.id] = {
if_score: m.if_score,
context_window: typeof m.context_window === 'number' ? m.context_window : parseInt(String(m.context_window).replace(/\D/g, '')) || 128,
organization: m.organization,
parameters: m.parameters
};
}
const modelIds = Object.keys(bmMap);
// Load meta
console.log('Reading meta from:', META_FILE);
const metaRaw = JSON.parse(fs.readFileSync(META_FILE, 'utf-8'));
const meta = metaRaw.agents || {};
// Load agent history (real data from Git/Gitea with dates, commits, reasons)
console.log('Reading history from:', HISTORY_FILE);
let historyData = { agents: {} };
try {
historyData = JSON.parse(fs.readFileSync(HISTORY_FILE, 'utf-8'));
} catch (e) {
console.warn(' No history file found, using empty history');
}
// Scan agent files
console.log('Reading agents from:', AGENTS_DIR);
const agentFiles = fs.readdirSync(AGENTS_DIR).filter(f => f.endsWith('.md'));
const agents = {};
let withHistory = 0;
for (const fn of agentFiles) {
const text = fs.readFileSync(path.join(AGENTS_DIR, fn), 'utf-8');
const fm = parseYamlFrontmatter(text);
if (!fm) continue;
const name = fn.replace('.md', '');
const metaAgent = meta[name] || {};
const model = (fm.model || metaAgent.model || 'unknown');
const provider = model.startsWith('ollama-cloud/') ? 'Ollama Cloud' : 'Unknown';
const category = metaAgent.category || 'General';
const mode = fm.mode || metaAgent.mode || fm.subagent ? 'subagent' : 'subagent';
const description = fm.description || metaAgent.description || '';
const color = (fm.color || metaAgent.color || '#6B7280');
const fitScore = computeScore(model, bmMap);
// Real history from agent-versions.json
const agentHistory = historyData.agents?.[name]?.history || [];
if (agentHistory.length > 0) {
withHistory++;
}
// Compute heatmap scores for all models
const heatmapScores = {};
for (const mid of modelIds) {
heatmapScores[mid] = computeScore(`ollama-cloud/${mid}`, bmMap);
}
// Generate recommendations: compare current model vs best alternative
let bestModel = model;
let bestScore = fitScore;
for (const mid of modelIds) {
const s = computeScore(`ollama-cloud/${mid}`, bmMap);
if (s > bestScore) { bestScore = s; bestModel = mid; }
}
const recommendations = [];
if (bestScore > fitScore + 2 && !model.includes(bestModel)) {
recommendations.push({
priority: (bestScore - fitScore >= 8) ? 'critical' : (bestScore - fitScore >= 5 ? 'high' : 'medium'),
target: `ollama-cloud/${bestModel}`,
reason: `${name} could improve from ${model} to ${bestModel}. Score: ${fitScore}${bestScore} (+${bestScore - fitScore}). Verified IF scores from artificialanalysis.ai.`,
score_before: fitScore,
score_after: bestScore,
score_delta: bestScore - fitScore,
applied: false
});
}
agents[name] = {
current: {
description,
mode,
model,
provider,
color,
category,
capabilities: metaAgent.capabilities || [],
recommendations,
benchmark: { fit_score: fitScore, instruction_following: bmMap[model.split('/').pop()]?.if_score || 0 }
},
history: agentHistory,
heatmap_scores: heatmapScores,
performance_log: historyData.agents?.[name]?.performance_log || []
};
}
const totalAgents = Object.keys(agents).length;
const pendingRecs = Object.values(agents).reduce((s, a) => s + a.current.recommendations.length, 0);
const unifiedData = {
"$schema": "./data/evolution.schema.json",
"version": "2.1.0",
"lastUpdated": new Date().toISOString(),
"agents": agents,
"model_benchmarks": bmMap,
"evolution_metrics": {
"total_agents": totalAgents,
"agents_with_history": withHistory,
"pending_recommendations": pendingRecs,
"last_sync": new Date().toISOString(),
"sync_sources": [".kilo/agents/*.md", "kilo-meta.json", "model-benchmarks-verified.json"]
}
};
console.log(`Unified data: ${totalAgents} agents, ${modelIds.length} models, ${pendingRecs} recommendations`);
// Write to JSON file
fs.writeFileSync(OUTPUT_FILE, JSON.stringify(unifiedData, null, 2));
console.log('\nExported data to JSON');
console.log(' Output:', OUTPUT_FILE);
console.log(' Size:', (fs.statSync(OUTPUT_FILE).size / 1024).toFixed(1), 'KB');
// Also copy to data/evolution.json for the container to consume
fs.copyFileSync(OUTPUT_FILE, path.join(__dirname, '../data/evolution.json'));
console.log('Also written:', path.join(__dirname, '../data/evolution.json'));
// Return the data for use by other scripts
module.exports = unifiedData;
} catch (error) {
console.error('Error:', error.message);
console.error(error.stack);
process.exit(1);
}