aios-core
Version:
Synkra AIOS: AI-Orchestrated System for Full Stack Development - Core Framework
498 lines (443 loc) • 18.9 kB
JavaScript
// =============================================================================
// generate-optimization-report.js — Optimization Report & Recommendations
// =============================================================================
// Story: TOK-5 (Tool Usage Analytics Pipeline)
// Layer: L2 (.aios-core/infrastructure/scripts/)
// Purpose:
// - Compare post-optimization metrics against TOK-1.5 baseline (ACs 4-6)
// - Generate promote/demote recommendations (ACs 7-9, 13-15)
// - Produce summary optimization report (ACs 16-17)
//
// Usage:
// node .aios-core/infrastructure/scripts/generate-optimization-report.js [options]
//
// Options:
// --dry-run Show results without writing files
// --json Output results as JSON to stdout
// --verbose Show detailed per-tool breakdown
// =============================================================================
;
const fs = require('fs');
const path = require('path');
const yaml = require('js-yaml');
// --- Paths ---
const ROOT = process.cwd();
const ANALYTICS_DIR = path.resolve(ROOT, '.aios', 'analytics');
const BASELINE_FILE = path.join(ANALYTICS_DIR, 'token-baseline.json');
const USAGE_FILE = path.join(ANALYTICS_DIR, 'tool-usage.json');
const REPORT_FILE = path.join(ANALYTICS_DIR, 'optimization-report.json');
const RECOMMENDATIONS_FILE = path.join(ANALYTICS_DIR, 'recommendations.yaml');
const TOOL_REGISTRY_FILE = path.resolve(ROOT, '.aios-core', 'data', 'tool-registry.yaml');
// --- Default Thresholds (overridden by tool-registry.yaml) ---
const DEFAULT_THRESHOLDS = {
promote: { minUsesPerSession: 10, minSessions: 5 },
demote: { maxUsesPerNSessions: 1, sessionWindow: 5 }
};
// --- Load Thresholds from tool-registry.yaml (AC 15) ---
function loadThresholds() {
try {
const raw = fs.readFileSync(TOOL_REGISTRY_FILE, 'utf8');
const registry = yaml.load(raw);
if (registry && registry.analytics && registry.analytics.thresholds) {
const t = registry.analytics.thresholds;
return {
promote: {
minUsesPerSession: t.promote?.minUsesPerSession ?? DEFAULT_THRESHOLDS.promote.minUsesPerSession,
minSessions: t.promote?.minSessions ?? DEFAULT_THRESHOLDS.promote.minSessions
},
demote: {
maxUsesPerNSessions: t.demote?.maxUsesPerNSessions ?? DEFAULT_THRESHOLDS.demote.maxUsesPerNSessions,
sessionWindow: t.demote?.sessionWindow ?? DEFAULT_THRESHOLDS.demote.sessionWindow
}
};
}
} catch {
// Fall back to defaults
}
return DEFAULT_THRESHOLDS;
}
// --- Load Baseline (TOK-1.5) ---
function loadBaseline() {
try {
const raw = fs.readFileSync(BASELINE_FILE, 'utf8');
return JSON.parse(raw);
} catch {
return null;
}
}
// --- Load Usage Data ---
function loadUsageData() {
try {
const raw = fs.readFileSync(USAGE_FILE, 'utf8');
const data = JSON.parse(raw);
return data && Array.isArray(data.sessions) ? data : null;
} catch {
return null;
}
}
// --- Load Tool Registry ---
function loadToolRegistry() {
try {
const raw = fs.readFileSync(TOOL_REGISTRY_FILE, 'utf8');
return yaml.load(raw);
} catch {
return null;
}
}
// --- Get tool tier from registry ---
function getToolTier(registry, toolName) {
if (!registry || !registry.tools) return 'unknown';
const tool = registry.tools[toolName];
return tool ? `tier_${tool.tier}` : 'unknown';
}
// --- Aggregate tool usage across sessions ---
function aggregateUsage(sessions) {
const toolStats = {};
const sessionCount = sessions.length;
for (const session of sessions) {
if (!session.events) continue;
for (const event of session.events) {
if (!toolStats[event.tool_name]) {
toolStats[event.tool_name] = {
tool_name: event.tool_name,
total_invocations: 0,
total_token_cost_input: 0,
total_token_cost_output: 0,
sessions_used: 0,
invocations_per_session: []
};
}
const stat = toolStats[event.tool_name];
stat.total_invocations += event.invocation_count;
stat.total_token_cost_input += event.token_cost_input;
stat.total_token_cost_output += event.token_cost_output;
stat.sessions_used += 1;
stat.invocations_per_session.push(event.invocation_count);
}
}
// Calculate averages
for (const stat of Object.values(toolStats)) {
stat.avg_invocations_per_session = stat.invocations_per_session.length > 0
? stat.invocations_per_session.reduce((a, b) => a + b, 0) / stat.invocations_per_session.length
: 0;
stat.avg_tokens_per_session = sessionCount > 0
? (stat.total_token_cost_input + stat.total_token_cost_output) / sessionCount
: 0;
}
return { toolStats, sessionCount };
}
// --- Baseline Comparison (ACs 4-6) ---
// Compares two dimensions:
// 1. Static overhead: framework tokens loaded at session start (schemas, rules, agents)
// Baseline source: frameworkOverhead.totalEstimatedTokens
// Post-opt source: sum of tool schema tokenCost for tools actually used (from registry)
// 2. Dynamic usage: total invocation token costs per session
// Baseline source: workflow median totalTokens
// Post-opt source: actual usage data from collect-tool-usage.js
function compareBaseline(baseline, usageData, registry) {
if (!baseline || !usageData) {
return { available: false, reason: 'Baseline or usage data not found' };
}
const sessions = usageData.sessions;
if (sessions.length === 0) {
return { available: false, reason: 'No sessions to compare' };
}
const { toolStats, sessionCount } = aggregateUsage(sessions);
// --- Static overhead comparison (C1 fix: apples-to-apples) ---
// Baseline: total framework overhead loaded at session start
const baselineOverhead = baseline.frameworkOverhead?.totalEstimatedTokens || 0;
// Post-opt: estimate schema overhead for tools actually used in sessions
// Uses tokenCost from tool-registry.yaml for tools that appeared in usage data
let postOptSchemaOverhead = 0;
if (registry && registry.tools) {
for (const toolName of Object.keys(toolStats)) {
const toolDef = registry.tools[toolName];
if (toolDef && toolDef.tokenCost) {
postOptSchemaOverhead += toolDef.tokenCost;
}
}
} else {
// Fallback: estimate from usage data (avg tokens / avg invocations = per-invocation cost)
postOptSchemaOverhead = Object.values(toolStats).reduce((sum, t) => {
return sum + (t.avg_invocations_per_session > 0
? Math.round((t.total_token_cost_input + t.total_token_cost_output) / t.total_invocations)
: 0);
}, 0);
}
// --- Dynamic usage comparison ---
const postOptTokensInput = Object.values(toolStats).reduce((s, t) => s + t.total_token_cost_input, 0);
const postOptTokensOutput = Object.values(toolStats).reduce((s, t) => s + t.total_token_cost_output, 0);
const postOptTotal = postOptTokensInput + postOptTokensOutput;
const avgPostOptPerSession = sessionCount > 0 ? postOptTotal / sessionCount : 0;
const baselineWorkflows = baseline.workflows || {};
// Per-workflow comparison (AC 5)
const workflowComparison = {};
for (const [wfName, wfData] of Object.entries(baselineWorkflows)) {
const baselineMedian = wfData.median?.totalTokens || 0;
const baselineOverheadPct = baseline.comparison?.aiosActual?.overheadPercentOfTypicalSession?.[wfName] || 0;
const baselineOverheadTokens = Math.round(baselineMedian * baselineOverheadPct / 100);
workflowComparison[wfName] = {
baseline_median_total: baselineMedian,
baseline_overhead_tokens: baselineOverheadTokens,
baseline_overhead_pct: baselineOverheadPct,
post_optimization_schema_overhead: postOptSchemaOverhead,
post_optimization_avg_usage_per_session: Math.round(avgPostOptPerSession),
sessions_analyzed: sessionCount
};
}
// Total reduction: compares static overhead (schema tokens loaded)
const absoluteReduction = baselineOverhead - postOptSchemaOverhead;
const percentageReduction = baselineOverhead > 0
? Math.round((absoluteReduction / baselineOverhead) * 1000) / 10
: 0;
// Target assessment (AC 6)
let targetStatus;
if (percentageReduction >= 25) {
targetStatus = 'ACHIEVED';
} else if (percentageReduction >= 15) {
targetStatus = 'PARTIALLY_ACHIEVED';
} else {
targetStatus = 'NOT_ACHIEVED';
}
return {
available: true,
comparison_methodology: 'Static schema overhead: baseline frameworkOverhead vs post-opt tool schema costs from registry. Dynamic usage tracked separately.',
baseline_overhead_tokens: baselineOverhead,
post_optimization_overhead_tokens: postOptSchemaOverhead,
absolute_reduction_tokens: absoluteReduction,
percentage_reduction: percentageReduction,
target_25_45_pct: targetStatus,
target_description: `25-45% reduction target is ${targetStatus}. Measured: ${percentageReduction}%`,
dynamic_usage: {
avg_invocation_tokens_per_session: Math.round(avgPostOptPerSession),
total_invocation_tokens: postOptTotal,
sessions_analyzed: sessionCount
},
workflow_comparison: workflowComparison,
per_tool_breakdown: Object.values(toolStats).map(t => ({
tool_name: t.tool_name,
total_invocations: t.total_invocations,
avg_invocations_per_session: Math.round(t.avg_invocations_per_session * 10) / 10,
total_tokens: t.total_token_cost_input + t.total_token_cost_output,
avg_tokens_per_session: Math.round(t.avg_tokens_per_session)
})).sort((a, b) => b.total_tokens - a.total_tokens)
};
}
// --- Promote/Demote Recommendations (ACs 7-9, 13-14) ---
function generateRecommendations(usageData, registry, thresholds) {
if (!usageData || usageData.sessions.length === 0) {
return [];
}
const { toolStats, sessionCount } = aggregateUsage(usageData.sessions);
const recommendations = [];
for (const [toolName, stat] of Object.entries(toolStats)) {
const currentTier = getToolTier(registry, toolName);
// Promote: tool used >10 times per session average across 5+ sessions (AC 7, 13)
if (
stat.avg_invocations_per_session > thresholds.promote.minUsesPerSession &&
stat.sessions_used >= thresholds.promote.minSessions &&
currentTier !== 'tier_1' // Don't promote if already Tier 1
) {
recommendations.push({
tool_name: toolName,
action: 'promote',
current_tier: currentTier,
recommended_tier: currentTier === 'tier_3' ? 'tier_2' : 'tier_1',
evidence: {
avg_invocations_per_session: Math.round(stat.avg_invocations_per_session * 10) / 10,
sessions_used: stat.sessions_used,
total_sessions: sessionCount,
threshold_invocations: thresholds.promote.minUsesPerSession,
threshold_sessions: thresholds.promote.minSessions
},
rationale: `Tool used ${Math.round(stat.avg_invocations_per_session * 10) / 10} times/session across ${stat.sessions_used} sessions (threshold: >${thresholds.promote.minUsesPerSession}/session, ${thresholds.promote.minSessions}+ sessions)`
});
}
// Demote: tool used <1 time per N sessions (AC 8, 14)
// C2 fix: precise calculation — sessions_used/sessionCount gives the fraction
// of sessions where tool appeared. Compare against maxUsesPerNSessions/sessionWindow
// meaning "less than 1 use per 5 sessions" = usage rate < 1/5 = 0.2
const usageRate = sessionCount > 0 ? stat.sessions_used / sessionCount : 0;
const demoteThresholdRate = thresholds.demote.maxUsesPerNSessions / thresholds.demote.sessionWindow;
if (
usageRate < demoteThresholdRate &&
sessionCount >= thresholds.demote.sessionWindow &&
currentTier !== 'tier_3' // Don't demote if already Tier 3
) {
recommendations.push({
tool_name: toolName,
action: 'demote',
current_tier: currentTier,
recommended_tier: currentTier === 'tier_1' ? 'tier_2' : 'tier_3',
evidence: {
usage_rate: Math.round(usageRate * 1000) / 1000,
demote_threshold_rate: demoteThresholdRate,
sessions_used: stat.sessions_used,
total_sessions: sessionCount,
threshold_max_uses: thresholds.demote.maxUsesPerNSessions,
threshold_session_window: thresholds.demote.sessionWindow
},
rationale: `Tool used in ${stat.sessions_used}/${sessionCount} sessions (rate: ${Math.round(usageRate * 100)}%). Threshold: <${thresholds.demote.maxUsesPerNSessions} per ${thresholds.demote.sessionWindow} sessions (${Math.round(demoteThresholdRate * 100)}%)`
});
}
}
// Check for tools in registry that are never used
if (registry && registry.tools && sessionCount >= thresholds.demote.sessionWindow) {
for (const [toolName, toolDef] of Object.entries(registry.tools)) {
if (!toolStats[toolName] && toolDef.tier < 3) {
recommendations.push({
tool_name: toolName,
action: 'demote',
current_tier: `tier_${toolDef.tier}`,
recommended_tier: 'tier_3',
evidence: {
usage_rate: 0,
sessions_used: 0,
total_sessions: sessionCount
},
rationale: `Tool never used in ${sessionCount} analyzed sessions. Consider demotion to Tier 3 (deferred).`
});
}
}
}
return recommendations;
}
// --- Generate Summary Report (ACs 16-17) ---
function generateReport(comparison, recommendations, usageData, thresholds) {
const sessions = usageData ? usageData.sessions : [];
const sessionCount = sessions.length;
// Measurement period
let periodStart = null;
let periodEnd = null;
if (sessionCount > 0) {
const timestamps = sessions.map(s => s.timestamp).filter(Boolean).sort();
periodStart = timestamps[0];
periodEnd = timestamps[timestamps.length - 1];
}
return {
version: '1.0.0',
generated_at: new Date().toISOString(),
story: 'TOK-5',
epic: 'Token Optimization — Intelligent Tool Loading',
measurement_period: {
start: periodStart,
end: periodEnd,
sessions_analyzed: sessionCount
},
baseline_comparison: comparison,
total_tokens_saved: comparison.available ? comparison.absolute_reduction_tokens : 0,
percentage_reduction: comparison.available ? comparison.percentage_reduction : 0,
target_status: comparison.available ? comparison.target_25_45_pct : 'NO_DATA',
recommendations_count: recommendations.length,
promote_count: recommendations.filter(r => r.action === 'promote').length,
demote_count: recommendations.filter(r => r.action === 'demote').length,
thresholds_used: thresholds
};
}
// --- Save Recommendations as YAML (AC 9) ---
function saveRecommendations(recommendations, dryRun) {
const yamlContent = yaml.dump({
version: '1.0.0',
generated_at: new Date().toISOString(),
story: 'TOK-5',
recommendations_count: recommendations.length,
recommendations: recommendations
}, { lineWidth: 120, noRefs: true });
if (!dryRun) {
if (!fs.existsSync(ANALYTICS_DIR)) {
fs.mkdirSync(ANALYTICS_DIR, { recursive: true });
}
fs.writeFileSync(RECOMMENDATIONS_FILE, yamlContent, 'utf8');
}
return yamlContent;
}
// --- Save Report as JSON (AC 16) ---
function saveReport(report, dryRun) {
const content = JSON.stringify(report, null, 2);
if (!dryRun) {
if (!fs.existsSync(ANALYTICS_DIR)) {
fs.mkdirSync(ANALYTICS_DIR, { recursive: true });
}
fs.writeFileSync(REPORT_FILE, content, 'utf8');
}
return content;
}
// --- Main ---
function main() {
const args = process.argv.slice(2);
const dryRun = args.includes('--dry-run');
const jsonOutput = args.includes('--json');
const verbose = args.includes('--verbose');
// Load data
const baseline = loadBaseline();
const usageData = loadUsageData();
const registry = loadToolRegistry();
const thresholds = loadThresholds();
if (!baseline) {
console.error('[TOK-5] Error: Baseline not found at', BASELINE_FILE);
console.error('[TOK-5] Run TOK-1.5 baseline collection first.');
process.exit(1);
}
if (!usageData || usageData.sessions.length === 0) {
console.error('[TOK-5] Error: No usage data found at', USAGE_FILE);
console.error('[TOK-5] Run collect-tool-usage.js to collect session data first.');
process.exit(1);
}
// Baseline comparison (ACs 4-6)
const comparison = compareBaseline(baseline, usageData, registry);
// Promote/demote recommendations (ACs 7-9, 13-14)
const recommendations = generateRecommendations(usageData, registry, thresholds);
// Summary report (ACs 16-17)
const report = generateReport(comparison, recommendations, usageData, thresholds);
// Save outputs
saveReport(report, dryRun);
saveRecommendations(recommendations, dryRun);
// Console output
if (jsonOutput) {
console.log(JSON.stringify({ report, recommendations }, null, 2));
} else {
console.log('=== TOK-5 Optimization Report ===');
console.log(`Sessions analyzed: ${report.measurement_period.sessions_analyzed}`);
console.log(`Period: ${report.measurement_period.start || 'N/A'} → ${report.measurement_period.end || 'N/A'}`);
console.log('');
if (comparison.available) {
console.log('--- Baseline Comparison ---');
console.log(`Baseline overhead: ${comparison.baseline_overhead_tokens} tokens`);
console.log(`Post-optimization: ${comparison.post_optimization_overhead_tokens} tokens`);
console.log(`Reduction: ${comparison.absolute_reduction_tokens} tokens (${comparison.percentage_reduction}%)`);
console.log(`Target (25-45%): ${comparison.target_25_45_pct}`);
} else {
console.log(`Baseline comparison: ${comparison.reason}`);
}
console.log('');
console.log('--- Recommendations ---');
console.log(`Total: ${recommendations.length} (${report.promote_count} promote, ${report.demote_count} demote)`);
if (verbose && recommendations.length > 0) {
for (const rec of recommendations) {
console.log(` ${rec.action.toUpperCase()}: ${rec.tool_name} (${rec.current_tier} → ${rec.recommended_tier})`);
console.log(` Rationale: ${rec.rationale}`);
}
}
console.log('');
if (dryRun) {
console.log('[TOK-5] Dry run — no files written.');
} else {
console.log(`[TOK-5] Report: ${REPORT_FILE}`);
console.log(`[TOK-5] Recommendations: ${RECOMMENDATIONS_FILE}`);
}
}
}
// Run main only when executed directly (not when required as module)
if (require.main === module) {
main();
}
// --- Exports for testing ---
module.exports = {
loadThresholds,
compareBaseline,
generateRecommendations,
generateReport,
aggregateUsage,
DEFAULT_THRESHOLDS
};