-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Expand file tree
/
Copy pathPlaygroundOutputCell.tsx
More file actions
178 lines (161 loc) · 6.79 KB
/
Copy pathPlaygroundOutputCell.tsx
File metadata and controls
178 lines (161 loc) · 6.79 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
import React from "react";
import { CellContext } from "@tanstack/react-table";
import { ListTree } from "lucide-react";
import CellWrapper from "@/shared/DataTableCells/CellWrapper";
import {
useOutputByPromptDatasetItemId,
useDatasetType,
useExperimentIdByPromptId,
} from "@/store/PlaygroundStore";
import { DATASET_TYPE } from "@/types/datasets";
import MarkdownPreview from "@/shared/MarkdownPreview/MarkdownPreview";
import PlaygroundOutputLoader from "@/v2/pages/PlaygroundPage/PlaygroundOutputs/PlaygroundOutputLoader/PlaygroundOutputLoader";
import PlaygroundOutputScoresContainer from "@/v2/pages/PlaygroundPage/PlaygroundOutputs/PlaygroundOutputScores/PlaygroundOutputScoresContainer";
import PlaygroundOutputAssertionStatus from "@/v2/pages/PlaygroundPage/PlaygroundOutputs/PlaygroundOutputScores/PlaygroundOutputAssertionStatus";
import PlaygroundTestSuiteLastRunOutput from "@/v2/pages/PlaygroundPage/PlaygroundOutputs/PlaygroundOutputScores/PlaygroundTestSuiteLastRunOutput";
import { usePlaygroundDataset } from "@/hooks/usePlaygroundDataset";
import { parseDatasetVersionKey } from "@/utils/datasetVersionStorage";
import { PLAYGROUND_PROMPT_COLORS } from "@/constants/llm";
import PlaygroundNoRunsYet from "@/v2/pages/PlaygroundPage/PlaygroundOutputs/PlaygroundNoRunsYet";
import { generateTracesURL } from "@/lib/annotation-queues";
import { EXPERIMENT_TAB } from "@/lib/experiments";
import useAppStore, { useActiveProjectId } from "@/store/AppStore";
import TooltipWrapper from "@/shared/TooltipWrapper/TooltipWrapper";
import { Button } from "@/ui/button";
interface PlaygroundOutputCellData {
dataItemId: string;
}
interface CustomMeta {
promptId: string;
promptIndex: number;
}
const PlaygroundOutputCell: React.FunctionComponent<
CellContext<PlaygroundOutputCellData, unknown>
> = (context) => {
const { custom } = context.column.columnDef.meta ?? {};
const { promptId, promptIndex } = (custom ?? {}) as CustomMeta;
const originalRow = context.row.original;
const workspaceName = useAppStore((state) => state.activeWorkspaceName);
// One store subscription per cell, not five. Each of the fields below used to
// come from its own hook, and every one of those hooks runs the *same*
// selector, so a single streaming token re-ran it (dataset items x prompts x 5)
// times. Reading the output object once and picking the fields off it is
// equivalent — the defaults below are the ones those hooks applied — while
// cutting the per-token selector work by 5x. The selector returns the stored
// object reference, which only changes when this cell's own output changes, so
// unrelated updates still don't re-render this cell.
const output = useOutputByPromptDatasetItemId(
promptId,
originalRow.dataItemId,
);
const value = output?.value ?? null;
const isLoading = output?.isLoading ?? false;
const stale = output?.stale ?? false;
const traceId = output?.traceId ?? null;
const selectedRuleIds = output?.selectedRuleIds;
const datasetType = useDatasetType();
const experimentId = useExperimentIdByPromptId(promptId);
const { datasetId: versionedDatasetId } = usePlaygroundDataset();
const plainDatasetId =
parseDatasetVersionKey(versionedDatasetId)?.datasetId || versionedDatasetId;
const isTestSuite = datasetType === DATASET_TYPE.TEST_SUITE;
const activeProjectId = useActiveProjectId();
const handleTraceLinkClick = (event: React.MouseEvent<HTMLButtonElement>) => {
event.stopPropagation();
if (!traceId || !activeProjectId) return;
// For dataset/experiment runs, open the trace on the experiment page's Logs tab, so the
// surrounding list holds the experiment's traces. The main project Logs list filters to
// source=sdk and would exclude them by design. The tab scopes itself to the experiment, so
// only the tab and the trace to open need to travel in the URL.
if (experimentId && plainDatasetId) {
const search = new URLSearchParams({
experiments: JSON.stringify([experimentId]),
tab: EXPERIMENT_TAB.logs,
tls_trace: traceId,
}).toString();
const basePath = import.meta.env.VITE_BASE_URL || "/";
const normalizedBasePath =
basePath === "/" ? "" : basePath.replace(/\/$/, "");
const relativePath = `${workspaceName}/projects/${activeProjectId}/experiments/${plainDatasetId}/compare?${search}`;
const url = new URL(
`${normalizedBasePath}/${relativePath}`,
window.location.origin,
).toString();
window.open(url, "_blank");
return;
}
const url = generateTracesURL(
workspaceName,
activeProjectId,
"traces",
traceId,
);
window.open(url, "_blank");
};
const hasOutput =
!stale && (value !== null || isLoading || (isTestSuite && !!experimentId));
const promptColor =
PLAYGROUND_PROMPT_COLORS[
(promptIndex ?? 0) % PLAYGROUND_PROMPT_COLORS.length
];
const noRunsColor = isTestSuite ? "var(--click-blue)" : promptColor.bg;
const renderContent = () => {
if (isLoading && !value) {
return <PlaygroundOutputLoader />;
}
return <MarkdownPreview>{value}</MarkdownPreview>;
};
return (
<CellWrapper
metadata={context.column.columnDef.meta}
tableMetadata={context.table.options.meta}
className="flex pt-5"
>
{hasOutput ? (
<div className="group relative flex size-full flex-col">
{traceId && activeProjectId && (
<TooltipWrapper content="Click to open original trace">
<Button
size="icon-xs"
variant="outline"
onClick={handleTraceLinkClick}
className="absolute right-1 top-1 z-10 hidden group-hover:flex"
>
<ListTree />
</Button>
</TooltipWrapper>
)}
<div className="mb-2 min-h-[var(--cell-top-height)]">
{isTestSuite ? (
<PlaygroundOutputAssertionStatus
experimentId={experimentId}
datasetItemId={originalRow.dataItemId}
datasetId={plainDatasetId || ""}
/>
) : (
<PlaygroundOutputScoresContainer
traceId={traceId}
selectedRuleIds={selectedRuleIds}
stale={stale}
/>
)}
</div>
<div className="flex-1 overflow-y-auto">
{isTestSuite ? (
<PlaygroundTestSuiteLastRunOutput
experimentId={experimentId}
datasetItemId={originalRow.dataItemId}
datasetId={plainDatasetId || ""}
/>
) : (
renderContent()
)}
</div>
</div>
) : (
<PlaygroundNoRunsYet color={noRunsColor} />
)}
</CellWrapper>
);
};
export default PlaygroundOutputCell;