Skip to content

Commit af5d330

Browse files
committed
feat(webapp): sample from any run that has an inline value for the source
The smart-column sample now scans the recent runs and uses the first one with an inline (non-offloaded, non-empty) value for the chosen source, paging through only those. Payload/metadata/output samples are picked independently, so an offloaded newest run no longer blocks the preview; the offloaded/empty message shows only when no recent run has one.
1 parent 296dd0f commit af5d330

1 file changed

Lines changed: 52 additions & 33 deletions

File tree

apps/webapp/app/components/runs/v3/AddSmartColumnDialog.tsx

Lines changed: 52 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,12 @@ import {
1717
type SmartColumnDisplay,
1818
type SmartColumnSource,
1919
} from "./runColumns";
20-
import { extractSmartValue, labelFromPath, parseSource } from "./smartColumnData";
20+
import {
21+
extractSmartValue,
22+
labelFromPath,
23+
parseSource,
24+
type ParsedSource,
25+
} from "./smartColumnData";
2126
import { SmartColumnSample } from "./SmartColumnSample";
2227
import type { loader as sampleLoader } from "~/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.smart-column-sample";
2328

@@ -84,29 +89,43 @@ export function AddSmartColumnDialog({
8489
// eslint-disable-next-line react-hooks/exhaustive-deps
8590
}, [open, sampleUrl]);
8691

92+
useEffect(() => {
93+
setSampleIndex(0);
94+
}, [source]);
95+
8796
const effectiveLabel = labelEdited ? label : labelFromPath(path);
8897

8998
const sampleLoaded = sample.data !== undefined && sample.state === "idle";
90-
const sampleRuns = sample.data?.runs ?? [];
91-
const clampedIndex = sampleRuns.length > 0 ? Math.min(sampleIndex, sampleRuns.length - 1) : 0;
92-
const sampleRun = sampleRuns[clampedIndex] ?? null;
99+
const sampleData = sample.data;
93100

94-
const parsed = useMemo(() => {
95-
if (!sampleRun) return undefined;
96-
switch (source) {
97-
case "payload":
98-
return parseSource({ data: sampleRun.payload, dataType: sampleRun.payloadType });
99-
case "metadata":
100-
return parseSource({ data: sampleRun.metadata, dataType: sampleRun.metadataType });
101-
case "output":
102-
return parseSource({ data: sampleRun.output, dataType: sampleRun.outputType });
103-
}
104-
}, [sampleRun, source]);
101+
const { usable, anyOffloaded, runCount } = useMemo(() => {
102+
const runs = sampleData?.runs ?? [];
103+
const parsed = runs.map((run) => {
104+
switch (source) {
105+
case "payload":
106+
return parseSource({ data: run.payload, dataType: run.payloadType });
107+
case "metadata":
108+
return parseSource({ data: run.metadata, dataType: run.metadataType });
109+
case "output":
110+
return parseSource({ data: run.output, dataType: run.outputType });
111+
}
112+
});
113+
return {
114+
runCount: runs.length,
115+
anyOffloaded: parsed.some((p) => p.state === "offloaded"),
116+
usable: parsed.filter(
117+
(p): p is Extract<ParsedSource, { state: "parsed" }> => p.state === "parsed"
118+
),
119+
};
120+
}, [sampleData, source]);
121+
122+
const activeIndex = usable.length > 0 ? Math.min(sampleIndex, usable.length - 1) : 0;
123+
const activeSample = usable[activeIndex];
105124

106125
const resolved = useMemo(() => {
107-
if (!parsed || path.trim().length === 0) return undefined;
108-
return extractSmartValue(parsed, path);
109-
}, [parsed, path]);
126+
if (!activeSample || path.trim().length === 0) return undefined;
127+
return extractSmartValue(activeSample, path);
128+
}, [activeSample, path]);
110129

111130
const canSubmit = path.trim().length > 0;
112131

@@ -202,37 +221,37 @@ export function AddSmartColumnDialog({
202221
<div className="flex flex-col gap-1.5 self-start rounded-lg border border-grid-dimmed bg-background-dimmed p-3">
203222
<div className="flex items-center justify-between gap-2">
204223
<Paragraph variant="extra-extra-small/dimmed/caps">Sample — {source}</Paragraph>
205-
{sampleRuns.length > 0 && (
224+
{usable.length > 1 && (
206225
<SampleRunPicker
207-
index={clampedIndex}
208-
total={sampleRuns.length}
226+
index={activeIndex}
227+
total={usable.length}
209228
onPrev={() => setSampleIndex((i) => Math.max(0, i - 1))}
210-
onNext={() => setSampleIndex((i) => Math.min(sampleRuns.length - 1, i + 1))}
229+
onNext={() => setSampleIndex((i) => Math.min(usable.length - 1, i + 1))}
211230
/>
212231
)}
213232
</div>
214233
{!sampleLoaded ? (
215234
<Paragraph variant="extra-small" className="text-text-dimmed">
216235
Loading…
217236
</Paragraph>
218-
) : !parsed ? (
237+
) : activeSample ? (
238+
<SmartColumnSample
239+
value={activeSample.value}
240+
activePath={path.trim()}
241+
onSelectPath={setPath}
242+
/>
243+
) : runCount === 0 ? (
219244
<Paragraph variant="extra-small" className="text-text-dimmed">
220245
No runs to sample.
221246
</Paragraph>
222-
) : parsed.state === "offloaded" ? (
247+
) : anyOffloaded ? (
223248
<Paragraph variant="extra-small" className="text-text-dimmed">
224-
This {source} is offloaded to object storage, too large to sample here.
249+
Recent {source}s are offloaded to object storage, too large to sample here.
225250
</Paragraph>
226-
) : parsed.state === "empty" ? (
251+
) : (
227252
<Paragraph variant="extra-small" className="text-text-dimmed">
228-
No {source} value for this run.
253+
No recent run has a {source} value to sample.
229254
</Paragraph>
230-
) : (
231-
<SmartColumnSample
232-
value={parsed.value}
233-
activePath={path.trim()}
234-
onSelectPath={setPath}
235-
/>
236255
)}
237256
<Paragraph variant="extra-extra-small/dimmed/caps" className="mt-2">
238257
Resolves to

0 commit comments

Comments
 (0)