Skip to content

Commit 512a9f3

Browse files
abhizipstackclaude
andcommitted
feat(run-history): per-run insights on expanded row
Expandable rows now cover more than just FAILURE. Clicking the expander on any SUCCESS / FAILURE / RETRY / REVOKED run opens an insights panel that reads as an extension of the row: - Status header with a tinted icon (green check for success, red cross for failure, etc.), start_time, and duration. - Scope tag (Single model vs Full job) plus the list of models attempted. Model list is derived from kwargs.models_override for scoped runs or from kwargs.model_configs.enabled for full runs. Back-compat handling for legacy kwargs.source === "quick_deploy". - For FAILURE runs, the error_message renders in a preformatted, scrollable Alert so stack traces preserve whitespace and don't blow out the page. - Whole panel uses theme tokens (colorSuccessBg / colorErrorBg / colorInfoBg etc.) so light and dark both track. Runtime-captured metrics (rows processed, tables written, schemas touched) are not yet surfaced because the scheduler pipeline doesn't persist them to TaskRunHistory.result today. Documented in the PR for follow-up instrumentation. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 9cacf64 commit 512a9f3

1 file changed

Lines changed: 145 additions & 8 deletions

File tree

frontend/src/ide/run-history/Runhistory.jsx

Lines changed: 145 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
import { useEffect, useState, useMemo, useCallback } from "react";
22
import {
3+
Alert,
34
Select,
45
Table,
56
Tag,
7+
theme,
68
Typography,
79
Empty,
810
Button,
@@ -13,6 +15,10 @@ import {
1315
ReloadOutlined,
1416
CalendarOutlined,
1517
DatabaseOutlined,
18+
CheckCircleFilled,
19+
CloseCircleFilled,
20+
ClockCircleOutlined,
21+
SyncOutlined,
1622
} from "@ant-design/icons";
1723
import { useSearchParams } from "react-router-dom";
1824

@@ -87,6 +93,7 @@ const Runhistory = () => {
8793
const { selectedOrgId } = orgStore();
8894
const { notify } = useNotificationService();
8995
const [searchParams, setSearchParams] = useSearchParams();
96+
const { token } = theme.useToken();
9097

9198
/* ─── API calls ─── */
9299
const getRunHistoryList = async (
@@ -268,6 +275,62 @@ const Runhistory = () => {
268275
[]
269276
);
270277

278+
/* ─── derive scope summary from a run row ─── */
279+
const getRunInsights = (record) => {
280+
const kw = record?.kwargs || {};
281+
const modelsOverride = kw.models_override || [];
282+
const legacyQuick = kw.source === "quick_deploy";
283+
const scope =
284+
kw.scope || (modelsOverride.length > 0 || legacyQuick ? "model" : "job");
285+
let models = modelsOverride;
286+
if (!models.length && scope === "job") {
287+
const configs = kw.model_configs || {};
288+
models = Object.entries(configs)
289+
.filter(([, cfg]) => cfg?.enabled !== false)
290+
.map(([name]) => name);
291+
}
292+
return { scope, models };
293+
};
294+
295+
const STATUS_META = {
296+
SUCCESS: {
297+
icon: <CheckCircleFilled />,
298+
label: "Succeeded",
299+
color: token.colorSuccess,
300+
bg: token.colorSuccessBg,
301+
},
302+
FAILURE: {
303+
icon: <CloseCircleFilled />,
304+
label: "Failed",
305+
color: token.colorError,
306+
bg: token.colorErrorBg,
307+
},
308+
STARTED: {
309+
icon: <SyncOutlined spin />,
310+
label: "Running",
311+
color: token.colorInfo,
312+
bg: token.colorInfoBg,
313+
},
314+
RETRY: {
315+
icon: <SyncOutlined spin />,
316+
label: "Retrying",
317+
color: token.colorWarning,
318+
bg: token.colorWarningBg,
319+
},
320+
REVOKED: {
321+
icon: <ClockCircleOutlined />,
322+
label: "Revoked",
323+
color: token.colorTextSecondary,
324+
bg: token.colorFillQuaternary,
325+
},
326+
PENDING: {
327+
icon: <ClockCircleOutlined />,
328+
label: "Pending",
329+
color: token.colorTextSecondary,
330+
bg: token.colorFillQuaternary,
331+
},
332+
};
333+
271334
/* ─── empty text ─── */
272335
const emptyDescription = useMemo(() => {
273336
if (!jobListItems.length) return "No jobs created yet";
@@ -344,15 +407,89 @@ const Runhistory = () => {
344407
size="small"
345408
bordered
346409
expandable={{
347-
expandedRowRender: (record) => (
348-
<div className="runhistory-error-row">
349-
<Typography.Text type="danger">
350-
{record.error_message}
351-
</Typography.Text>
352-
</div>
353-
),
410+
expandedRowRender: (record) => {
411+
const meta = STATUS_META[record.status] || STATUS_META.PENDING;
412+
const { scope, models } = getRunInsights(record);
413+
const isFailure = record.status === "FAILURE";
414+
return (
415+
<div
416+
style={{
417+
borderLeft: `3px solid ${meta.color}`,
418+
padding: "10px 12px",
419+
background: meta.bg,
420+
}}
421+
>
422+
<div
423+
style={{
424+
display: "flex",
425+
alignItems: "center",
426+
gap: 8,
427+
marginBottom: 8,
428+
}}
429+
>
430+
<span style={{ color: meta.color }}>{meta.icon}</span>
431+
<Typography.Text strong style={{ color: meta.color }}>
432+
{meta.label}
433+
</Typography.Text>
434+
{record.start_time && (
435+
<Typography.Text
436+
type="secondary"
437+
style={{ fontSize: 12 }}
438+
>
439+
· {record.start_time}
440+
</Typography.Text>
441+
)}
442+
{record.duration && (
443+
<Typography.Text
444+
type="secondary"
445+
style={{ fontSize: 12 }}
446+
>
447+
· {formatDuration(record.duration)}
448+
</Typography.Text>
449+
)}
450+
</div>
451+
<Space
452+
size={8}
453+
wrap
454+
style={{ marginBottom: isFailure ? 8 : 0 }}
455+
>
456+
<Tag color={scope === "model" ? "purple" : "default"}>
457+
{scope === "model" ? "Single model" : "Full job"}
458+
</Tag>
459+
<Typography.Text type="secondary" style={{ fontSize: 12 }}>
460+
{models.length > 0
461+
? `Models attempted: ${models.join(", ")}`
462+
: "No model configuration recorded for this run."}
463+
</Typography.Text>
464+
</Space>
465+
{isFailure && record.error_message && (
466+
<Alert
467+
type="error"
468+
showIcon={false}
469+
message={
470+
<pre
471+
style={{
472+
margin: 0,
473+
maxHeight: 240,
474+
overflow: "auto",
475+
whiteSpace: "pre-wrap",
476+
wordBreak: "break-word",
477+
fontFamily: token.fontFamilyCode,
478+
fontSize: 12,
479+
}}
480+
>
481+
{record.error_message}
482+
</pre>
483+
}
484+
/>
485+
)}
486+
</div>
487+
);
488+
},
354489
rowExpandable: (record) =>
355-
record.status === "FAILURE" && !!record.error_message,
490+
["SUCCESS", "FAILURE", "RETRY", "REVOKED"].includes(
491+
record.status
492+
),
356493
expandedRowKeys,
357494
onExpand: handleExpand,
358495
}}

0 commit comments

Comments
 (0)