diff --git a/inspect-ui/src/panels/ActionList.css b/inspect-ui/src/panels/ActionList.css index c322f97..b9a1925 100644 --- a/inspect-ui/src/panels/ActionList.css +++ b/inspect-ui/src/panels/ActionList.css @@ -130,3 +130,54 @@ text-overflow: ellipsis; white-space: nowrap; } + +.action-list-extractor-changes { + grid-column: 2; + display: flex; + flex-direction: column; + gap: 2px; + margin-top: 4px; + padding-top: 4px; + border-top: 1px solid var(--surface-border); +} + +.action-list-extractor-changes-heading { + color: var(--text-muted); + text-transform: lowercase; + letter-spacing: 0.5px; +} + +.action-list-extractor-change-row { + display: grid; + grid-template-columns: minmax(80px, max-content) 1fr; + gap: 6px; + color: var(--text-primary); +} + +.action-list-extractor-change-name { + color: var(--text-muted); +} + +.action-list-extractor-change-diff { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.action-list-extractor-change-row-collapsed > summary { + display: grid; + grid-template-columns: minmax(80px, max-content) 1fr; + gap: 6px; + cursor: pointer; +} + +.action-list-extractor-change-full { + grid-column: 1 / -1; + margin: 4px 0 0; + white-space: pre-wrap; + word-break: break-word; + background: var(--surface-base); + padding: 4px 6px; + border-radius: 2px; + font-size: 10px; +} diff --git a/inspect-ui/src/panels/ActionList.tsx b/inspect-ui/src/panels/ActionList.tsx index 75b03bd..9e6bbc1 100644 --- a/inspect-ui/src/panels/ActionList.tsx +++ b/inspect-ui/src/panels/ActionList.tsx @@ -121,6 +121,24 @@ function contentTextForStep(step: Step): string { return ""; } +const MAX_INLINE_VALUE = 80; + +function formatExtractorValue(value: unknown): string { + if (value === null) return "null"; + if (value === undefined) return "undefined"; + if (typeof value === "string") return JSON.stringify(value); + try { + return JSON.stringify(value); + } catch { + return String(value); + } +} + +function truncateForInline(text: string): string { + if (text.length <= MAX_INLINE_VALUE) return text; + return `${text.slice(0, MAX_INLINE_VALUE - 1)}...`; +} + function positionTextForStep(step: Step): string | null { const action = step.action; if (!action) { @@ -289,6 +307,56 @@ export default function ActionList({ {contentText ? contentText : `""`} + {step.has_violations && selectedStep?.extractor_changes + ? (() => { + const entries = Object.entries(selectedStep.extractor_changes); + if (entries.length === 0) return null; + entries.sort(([a], [b]) => a.localeCompare(b)); + return ( +
+
+ extractor changes +
+ {entries.map(([name, change]) => { + const prev = formatExtractorValue(change.prev); + const curr = formatExtractorValue(change.curr); + const inlineFits = + prev.length + curr.length <= MAX_INLINE_VALUE * 2; + if (inlineFits) { + return ( +
+ + {name} + + + {truncateForInline(prev)} → {truncateForInline(curr)} + +
+ ); + } + return ( +
+ + + {name} + + + {truncateForInline(prev)} → {truncateForInline(curr)} + + +
+                                  {`prev: ${prev}\ncurr: ${curr}`}
+                                
+
+ ); + })} +
+ ); + })() + : null} ) : null} diff --git a/inspect-ui/src/types.ts b/inspect-ui/src/types.ts index 107f069..106c060 100644 --- a/inspect-ui/src/types.ts +++ b/inspect-ui/src/types.ts @@ -115,6 +115,11 @@ export interface Metrics { total_memory_bytes?: number; } +export interface ExtractorChange { + prev: unknown; + curr: unknown; +} + export interface Step { step: number; timestamp: string; @@ -126,4 +131,5 @@ export interface Step { hierarchy?: Hierarchy; residuals?: Record; metrics?: Metrics; + extractor_changes?: Record; }