- 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
190 lines
7.4 KiB
JavaScript
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);
|
|
} |