Spaces:
Sleeping
Sleeping
| 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 ( | |
| <Plot | |
| data={[{ | |
| type: 'bar', orientation: 'h', | |
| y: names, x: vs, | |
| marker: { color: colors, line: { color: '#1f1d1a', width: 1 } }, | |
| text: labels, textposition: 'outside', cliponaxis: false, | |
| textfont: { color: '#f3f0e8', size: 11 }, | |
| hovertemplate: '%{y}<br>contribution=%{x:+.3f}<extra></extra>', | |
| }]} | |
| 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 | |
| /> | |
| ) | |
| } | |