Skip to content
Open
Show file tree
Hide file tree
Changes from 26 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
85e33d8
[OPIK-2477] [FE] Add score reason as optional column in trace, thread…
JetoPistola Nov 23, 2025
c58d104
Revision 2: Refactor to use global toggle for score reason columns
JetoPistola Nov 24, 2025
aee575c
Revision 3: Relocate reasons toggle to Columns menu
JetoPistola Nov 25, 2025
4b41460
Revision 2: Show score reasons inline when row height is Medium/Large
JetoPistola Nov 25, 2025
1428bc2
Revision 3: Keep score and reason on same line, add to all resource p…
JetoPistola Nov 26, 2025
cc68af4
Revision 4: Align reason button with section title, match font size
JetoPistola Nov 26, 2025
8a04ef2
Revision 5: Create reusable FeedbackScoreReasonToggle component (DRY)…
JetoPistola Nov 26, 2025
4c401aa
Revision 6: Add self-start alignment to reasons toggle button
JetoPistola Nov 26, 2025
99b4118
Revision 7: Move self-start alignment to wrapper div in ColumnsButton
JetoPistola Nov 26, 2025
d26b94d
Revision 8: Remove bold font, increase spacing, add margin to toggle
JetoPistola Nov 26, 2025
92955e9
Revision 9: Stack title and toggle button vertically (separate lines)
JetoPistola Nov 26, 2025
d8ac5a6
Revision 9: Allow label and toggle to wrap naturally with flex-wrap
JetoPistola Nov 26, 2025
da2e9e2
Revision 10: Keep label and toggle on same line together with whitesp…
JetoPistola Nov 26, 2025
252982f
Revision 11: Increase dropdown menu width to prevent horizontal scroll
JetoPistola Nov 26, 2025
8c0edc0
Revision 12: Remove unused FeedbackScoreReasonCell component
JetoPistola Nov 26, 2025
ec61cdb
Revision 13: Remove obsolete comments from useExperimentsTableConfig
JetoPistola Nov 26, 2025
16ea198
Revision 13: Remove obsolete comments and use cleaner arrow function …
JetoPistola Nov 26, 2025
2ad424f
Revision 14: Restore original scoresColumnsData format with array spread
JetoPistola Nov 26, 2025
7b549dc
Revision 14: Use TableMeta type and destructure meta properly
JetoPistola Nov 26, 2025
7e50fd0
Revision 15: Use array includes for row height check
JetoPistola Nov 26, 2025
a1da3c0
Revision 16: Encapsulate visibility logic in FeedbackScoreReasonToggl…
JetoPistola Nov 26, 2025
16fa908
Revision 17: Return columnSections directly from hook without useMemos
JetoPistola Nov 26, 2025
ccc95f2
Make showReasons and setShowReasons optional props with defaults in u…
JetoPistola Nov 26, 2025
b753f17
Make showReasons and setShowReasons optional props with defaults in u…
JetoPistola Nov 26, 2025
289a9bd
Revision 18: Allow inline reasons to be multiline with proper truncation
JetoPistola Nov 26, 2025
f67a829
Revision 19: Prevent inline reasons from overflowing into cells below
JetoPistola Nov 26, 2025
7d71e05
Revision 20: Add tooltip to show full reasons when truncated
JetoPistola Nov 26, 2025
ce2234e
Revision 21: Refactor to avoid duplicating tooltip wrapper
JetoPistola Nov 26, 2025
f48323d
Revision 22: Apply linter fixes to FeedbackScoreReasonToggle
JetoPistola Nov 26, 2025
fa8e73f
Apply suggestion from @JetoPistola
JetoPistola Nov 26, 2025
8e990a2
Revision 23: Add expand reasons toggle to ExperimentItemsTab
JetoPistola Nov 26, 2025
0b38cb7
Revision 24: Fix inline reasons display in CompareExperimentsFeedback…
JetoPistola Nov 26, 2025
8df202e
Revision 26: Make row height the single source of truth for feedback …
JetoPistola Nov 27, 2025
a452951
Revision 28: Remove action support from ColumnsButton
JetoPistola Nov 27, 2025
c776e29
Update DataTableRowHeightSelector labels for clarity
JetoPistola Nov 27, 2025
89099db
Refactor CompareExperimentsFeedbackScoreCell layout to conditionally …
JetoPistola Nov 27, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React from "react";
import { CellContext } from "@tanstack/react-table";
import { CellContext, TableMeta } from "@tanstack/react-table";

import { ExperimentItem, ExperimentsCompare } from "@/types/datasets";
import VerticallySplitCellWrapper, {
Expand All @@ -16,6 +16,7 @@ import { FeedbackScoreCustomMeta } from "@/types/feedback-scores";
import useFeedbackScoreInlineEdit from "@/hooks/useFeedbackScoreInlineEdit";
import { cn } from "@/lib/utils";
import FeedbackScoreEditDropdown from "@/components/shared/DataTableCells/FeedbackScoreEditDropdown";
import { ROW_HEIGHT } from "@/types/shared";

const CompareExperimentsFeedbackScoreCell: React.FC<
CellContext<ExperimentsCompare, unknown>
Comment on lines 21 to 22
Copy link

Copilot AI Nov 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using React.FC is discouraged in modern TypeScript/React. Use a standard function declaration with explicit props typing instead.

Copilot uses AI. Check for mistakes.
Expand All @@ -37,12 +38,17 @@ const CompareExperimentsFeedbackScoreCell: React.FC<
feedbackScore,
});

const enableUserFeedbackEditing =
const {
enableUserFeedbackEditing = false,
showReasons = false,
rowHeight = ROW_HEIGHT.small,
} = (context.table.options.meta ?? {}) as TableMeta<ExperimentsCompare>;

const isEditingEnabled =
experimentCompare.experiment_items.length === 1 &&
(context.table.options.meta?.enableUserFeedbackEditing ?? false);
enableUserFeedbackEditing;
const isUserFeedbackColumn =
enableUserFeedbackEditing &&
context.column.id === "feedback_scores_User feedback";
isEditingEnabled && context.column.id === "feedback_scores_User feedback";

const renderContent = (item: ExperimentItem | undefined) => {
const feedbackScore = item?.feedback_scores?.find(
Expand Down Expand Up @@ -79,10 +85,15 @@ const CompareExperimentsFeedbackScoreCell: React.FC<

const color = feedbackKey && colorMap ? colorMap[feedbackKey] : undefined;

const shouldShowInlineReasons =
showReasons &&
[ROW_HEIGHT.medium, ROW_HEIGHT.large].includes(rowHeight) &&
reasons.length > 0;

return (
<div
className={cn(
"flex h-4 w-full items-center justify-end gap-1",
"flex h-4 w-full items-center justify-end gap-1 flex-wrap overflow-hidden",
isUserFeedbackColumn && "group",
)}
>
Expand All @@ -92,10 +103,16 @@ const CompareExperimentsFeedbackScoreCell: React.FC<
isUserFeedbackColumn={isUserFeedbackColumn}
onValueChange={handleValueChange}
/>
{reasons.length > 0 && (
<FeedbackScoreReasonTooltip reasons={reasons}>
<MessageSquareMore className="size-3.5 shrink-0 text-light-slate" />
</FeedbackScoreReasonTooltip>
{shouldShowInlineReasons ? (
<span className="line-clamp-3 break-words text-xs text-muted-foreground">
{reasons.map((r) => r.reason).join(", ")}
</span>
) : (
Copy link

Copilot AI Nov 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reasons.map((r) => r.reason).join(", ") operation is performed on every render. Consider memoizing this computed value using useMemo to avoid unnecessary recalculations.

Copilot uses AI. Check for mistakes.
reasons.length > 0 && (
<FeedbackScoreReasonTooltip reasons={reasons}>
<MessageSquareMore className="size-3.5 shrink-0 text-light-slate" />
</FeedbackScoreReasonTooltip>
)
)}
</div>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ import {
COLUMN_TYPE,
COLUMN_DATASET_ID,
COLUMN_METADATA_ID,
ROW_HEIGHT,
} from "@/types/shared";
import FeedbackScoreReasonToggle from "@/components/shared/FeedbackScoreReasonToggle/FeedbackScoreReasonToggle";
import { convertColumnDataToColumn, isColumnSortable } from "@/lib/table";
import {
buildGroupFieldName,
Expand Down Expand Up @@ -54,6 +56,10 @@ export type UseExperimentsTableConfigProps<T> = {
actionsCell?: ColumnDefTemplate<CellContext<T, unknown>>;
sortedColumns: ColumnSort[];
setSortedColumns: OnChangeFn<ColumnSort[]>;
showReasons?: boolean;
setShowReasons?: (show: boolean) => void;
height?: ROW_HEIGHT | string | null | undefined;
setHeight?: (height: ROW_HEIGHT) => void;
};

export const useExperimentsTableConfig = <
Expand All @@ -72,6 +78,10 @@ export const useExperimentsTableConfig = <
actionsCell,
sortedColumns,
setSortedColumns,
height,
setHeight,
showReasons = false,
setShowReasons = () => {},
}: UseExperimentsTableConfigProps<T>) => {
const [selectedColumns, setSelectedColumns] = useLocalStorageState<string[]>(
`${storageKeyPrefix}-selected-columns`,
Expand Down Expand Up @@ -305,9 +315,28 @@ export const useExperimentsTableConfig = <
columns: scoresColumnsData,
order: scoresColumnsOrder,
onOrderChange: setScoresColumnsOrder,
action: (
<FeedbackScoreReasonToggle
showReasons={showReasons}
setShowReasons={setShowReasons}
height={height}
setHeight={setHeight}
scoresColumnsData={scoresColumnsData}
selectedColumns={selectedColumns}
/>
),
},
];
}, [scoresColumnsData, scoresColumnsOrder, setScoresColumnsOrder]);
}, [
scoresColumnsData,
scoresColumnsOrder,
setScoresColumnsOrder,
showReasons,
setShowReasons,
height,
setHeight,
selectedColumns,
]);

return {
// State
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ import SelectBox, {
SelectBoxProps,
} from "@/components/shared/SelectBox/SelectBox";
import { useTruncationEnabled } from "@/components/server-sync-provider";
import FeedbackScoreReasonToggle from "@/components/shared/FeedbackScoreReasonToggle/FeedbackScoreReasonToggle";

const SHARED_COLUMNS: ColumnData<Thread>[] = [
{
Expand Down Expand Up @@ -250,6 +251,13 @@ const ThreadQueueItemsTab: React.FunctionComponent<
syncQueryWithLocalStorageOnInit: true,
});

const [showReasons, setShowReasons] = useLocalStorageState<boolean>(
"queue-thread-show-reasons",
{
defaultValue: false,
},
);

const [filters = [], setFilters] = useQueryParam(
"thread_filters",
JsonParam,
Expand Down Expand Up @@ -479,9 +487,28 @@ const ThreadQueueItemsTab: React.FunctionComponent<
columns: scoresColumnsData,
order: scoresColumnsOrder,
onOrderChange: setScoresColumnsOrder,
action: (
<FeedbackScoreReasonToggle
showReasons={showReasons}
setShowReasons={setShowReasons}
height={height}
setHeight={setHeight}
scoresColumnsData={scoresColumnsData}
selectedColumns={selectedColumns}
/>
),
},
];
}, [scoresColumnsData, scoresColumnsOrder, setScoresColumnsOrder]);
}, [
scoresColumnsData,
scoresColumnsOrder,
setScoresColumnsOrder,
showReasons,
setShowReasons,
height,
setHeight,
selectedColumns,
]);

if (isPending) {
return <Loader />;
Expand Down Expand Up @@ -554,6 +581,9 @@ const ThreadQueueItemsTab: React.FunctionComponent<
getRowId={getRowId}
rowHeight={height as ROW_HEIGHT}
columnPinning={DEFAULT_COLUMN_PINNING}
meta={{
showReasons,
}}
noData={<DataTableNoData title={noDataText} />}
TableWrapper={PageBodyStickyTableWrapper}
stickyHeader
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ import SelectBox, {
SelectBoxProps,
} from "@/components/shared/SelectBox/SelectBox";
import { useTruncationEnabled } from "@/components/server-sync-provider";
import FeedbackScoreReasonToggle from "@/components/shared/FeedbackScoreReasonToggle/FeedbackScoreReasonToggle";

const TRACE_COLUMNS: ColumnData<Trace>[] = [
{
Expand Down Expand Up @@ -281,6 +282,13 @@ const TraceQueueItemsTab: React.FC<TraceQueueItemsTabProps> = ({
syncQueryWithLocalStorageOnInit: true,
});

const [showReasons, setShowReasons] = useLocalStorageState<boolean>(
"queue-trace-show-reasons",
{
defaultValue: false,
},
);

const [filters = [], setFilters] = useQueryParam("trace_filters", JsonParam, {
updateType: "replaceIn",
});
Expand Down Expand Up @@ -524,9 +532,28 @@ const TraceQueueItemsTab: React.FC<TraceQueueItemsTabProps> = ({
columns: scoresColumnsData,
order: scoresColumnsOrder,
onOrderChange: setScoresColumnsOrder,
action: (
<FeedbackScoreReasonToggle
showReasons={showReasons}
setShowReasons={setShowReasons}
height={height}
setHeight={setHeight}
scoresColumnsData={scoresColumnsData}
selectedColumns={selectedColumns}
/>
),
},
];
}, [scoresColumnsData, scoresColumnsOrder, setScoresColumnsOrder]);
}, [
scoresColumnsData,
scoresColumnsOrder,
setScoresColumnsOrder,
showReasons,
setShowReasons,
height,
setHeight,
selectedColumns,
]);

if (isPending) {
return <Loader />;
Expand Down Expand Up @@ -600,6 +627,9 @@ const TraceQueueItemsTab: React.FC<TraceQueueItemsTabProps> = ({
getRowId={getRowId}
rowHeight={height as ROW_HEIGHT}
columnPinning={DEFAULT_COLUMN_PINNING}
meta={{
showReasons,
}}
noData={<DataTableNoData title={noDataText} />}
TableWrapper={PageBodyStickyTableWrapper}
stickyHeader
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,13 @@ const ExperimentsPage: React.FC = () => {
},
);

const [showReasons, setShowReasons] = useLocalStorageState<boolean>(
`${STORAGE_KEY_PREFIX}-show-reasons`,
{
defaultValue: false,
},
);

const { isFeedbackScoresPending, dynamicScoresColumns } =
useExperimentsFeedbackScores();

Expand Down Expand Up @@ -369,6 +376,8 @@ const ExperimentsPage: React.FC = () => {
actionsCell: ExperimentRowActionsCell,
sortedColumns,
setSortedColumns,
showReasons,
setShowReasons,
});

const handleRowClick = useCallback(
Expand Down Expand Up @@ -644,6 +653,9 @@ const ExperimentsPage: React.FC = () => {
groupingConfig={groupingConfig}
getRowId={getRowId}
columnPinning={columnPinningConfig}
meta={{
showReasons,
}}
noData={
<DataTableNoData title={noDataText}>
{noData && (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ import {
USER_FEEDBACK_NAME,
} from "@/constants/shared";
import { useTruncationEnabled } from "@/components/server-sync-provider";
import FeedbackScoreReasonToggle from "@/components/shared/FeedbackScoreReasonToggle/FeedbackScoreReasonToggle";

const getRowId = (d: Thread) => d.id;

Expand Down Expand Up @@ -416,7 +417,9 @@ export const ThreadsTab: React.FC<ThreadsTabProps> = ({
(col) => col.id !== USER_FEEDBACK_COLUMN_ID,
);

return [userFeedbackColumn, ...otherDynamicColumns].map(
const allScoreColumns = [userFeedbackColumn, ...otherDynamicColumns];

return allScoreColumns.map(
({ label, id, columnType }) =>
({
id,
Expand Down Expand Up @@ -458,6 +461,13 @@ export const ThreadsTab: React.FC<ThreadsTabProps> = ({
defaultValue: {},
});

const [showReasons, setShowReasons] = useLocalStorageState<boolean>(
"threads-show-reasons",
{
defaultValue: false,
},
);

const selectedRows: Thread[] = useMemo(() => {
return rows.filter((row) => rowSelection[row.id]);
}, [rowSelection, rows]);
Expand Down Expand Up @@ -492,8 +502,9 @@ export const ThreadsTab: React.FC<ThreadsTabProps> = ({
projectId,
projectName,
enableUserFeedbackEditing: true,
showReasons,
}),
[projectId, projectName],
[projectId, projectName, showReasons],
);

const columns = useMemo(() => {
Expand Down Expand Up @@ -583,9 +594,28 @@ export const ThreadsTab: React.FC<ThreadsTabProps> = ({
columns: scoresColumnsData,
order: scoresColumnsOrder,
onOrderChange: setScoresColumnsOrder,
action: (
<FeedbackScoreReasonToggle
showReasons={showReasons}
setShowReasons={setShowReasons}
height={height}
setHeight={setHeight}
scoresColumnsData={scoresColumnsData}
selectedColumns={selectedColumns}
/>
),
},
];
}, [scoresColumnsData, scoresColumnsOrder, setScoresColumnsOrder]);
}, [
scoresColumnsData,
scoresColumnsOrder,
setScoresColumnsOrder,
showReasons,
setShowReasons,
height,
setHeight,
selectedColumns,
]);

if (isPending || isFeedbackScoresNamesPending) {
return <Loader />;
Expand Down
Loading