import Plot from 'react-plotly.js' const PLOTLY_BASE = { font: { family: 'Inter', color: '#f3f0e8', size: 12 }, paper_bgcolor: '#2a2824', plot_bgcolor: '#1f1d1a', hoverlabel: { bgcolor: '#f3f0e8', font: { color: '#1f1d1a' } }, } const AXIS = { gridcolor: '#403b34', zerolinecolor: '#554e45', showline: true, linecolor: '#554e45', tickfont: { color: '#b5ada0' }, } // Per-component weights from rewards.py. Breakdown bars show weighted // contributions (sum → total reward) rather than raw component values, so // the chart is directly readable as "which components are hurting / helping". const WEIGHTS = { r_regret: 1.0, r_convergence: 0.3, r_robustness: 0.3, r_novelty: 0.1, r_budget: -0.05, // subtractive r_eval_failures: -0.5, // subtractive } const DISPLAY_NAMES = { r_regret: 'regret (×1.0)', r_convergence: 'convergence (×0.3)', r_robustness: 'robustness (×0.3)', r_novelty: 'novelty (×0.1)', r_budget: 'budget (−0.05×)', r_eval_failures: 'eval crashes (−0.5×)', } export function RewardBreakdown({ breakdown, total }) { // Weighted contributions — sum to the terminal reward. const entries = Object.entries(WEIGHTS).map(([k, w]) => ({ key: k, label: DISPLAY_NAMES[k], value: (breakdown[k] ?? 0) * w, })) const names = entries.map(e => e.label) const vs = entries.map(e => e.value) const colors = vs.map(v => v >= 0 ? '#3d6b4c' : '#a0483a') const labels = vs.map(v => (v >= 0 ? '+' : '') + v.toFixed(3)) return ( contribution=%{x:+.3f}', }]} layout={{ ...PLOTLY_BASE, title: { text: `Reward breakdown · total = ${(total >= 0 ? '+' : '') + total.toFixed(3)}`, x: 0.02, xanchor: 'left', font: { size: 14, color: '#f3f0e8' }, }, height: 260, margin: { l: 160, r: 50, t: 50, b: 30 }, xaxis: { title: 'contribution to total reward', range: [Math.min(...vs, 0) - 0.15, Math.max(...vs, 0) + 0.15], ...AXIS, }, yaxis: { autorange: 'reversed', ...AXIS }, showlegend: false, bargap: 0.25, shapes: [{ type: 'line', x0: 0, x1: 0, y0: -0.5, y1: names.length - 0.5, line: { color: '#554e45', width: 1 }, }], }} config={{ displayModeBar: false, responsive: true }} style={{ width: '100%' }} useResizeHandler /> ) }