diff --git a/front/components/assistant_builder/AssistantBuilderPreviewDrawer.tsx b/front/components/assistant_builder/AssistantBuilderPreviewDrawer.tsx index 90ce4b28f1b5..5257b538921a 100644 --- a/front/components/assistant_builder/AssistantBuilderPreviewDrawer.tsx +++ b/front/components/assistant_builder/AssistantBuilderPreviewDrawer.tsx @@ -15,6 +15,7 @@ import { Markdown, MoreIcon, Page, + Pagination, Spinner, Tabs, TabsList, @@ -29,7 +30,8 @@ import type { WorkspaceType, } from "@dust-tt/types"; import { Separator } from "@radix-ui/react-select"; -import { useCallback, useContext, useEffect, useState } from "react"; +import type { PaginationState } from "@tanstack/react-table"; +import { useCallback, useContext, useEffect, useMemo, useState } from "react"; import ConversationViewer from "@app/components/assistant/conversation/ConversationViewer"; import { GenerationContextProvider } from "@app/components/assistant/conversation/GenerationContextProvider"; @@ -56,8 +58,7 @@ import { useUser } from "@app/lib/swr/user"; import { timeAgoFrom } from "@app/lib/utils"; import type { FetchAssistantTemplateResponse } from "@app/pages/api/w/[wId]/assistant/builder/templates/[tId]"; -const MAX_FEEDBACKS_TO_DISPLAY = 500; -const FEEDBACKS_BATCH_SIZE = 100; +const FEEDBACKS_BATCH_SIZE = 50; interface AssistantBuilderRightPanelProps { screen: BuilderScreen; @@ -422,16 +423,16 @@ const FeedbacksSection = ({ owner: LightWorkspaceType; assistantId: string; }) => { - // Used for pagination - const [feedbacksExhausted, setFeedbacksExhausted] = useState(false); - const [currentOldestFeedbackId, setCurrentOldestFeedbackId] = useState< - number | undefined - >(undefined); + // Used for pagination's lastValue: BatchId -> LastFeedbackId + const [lastIdForBatches, setLastIdForBatches] = useState< + Record + >({}); + + const [paginationState, setPaginationState] = useState({ + pageIndex: 0, + pageSize: FEEDBACKS_BATCH_SIZE, + }); - // All retrieved Feedbacks - const [feedbacks, setFeedbacks] = useState< - AgentMessageFeedbackWithMetadataType[] - >([]); // Decreasing version, paginated decreasing id. const { agentConfigurationFeedbacks, isAgentConfigurationFeedbacksLoading } = useAgentConfigurationFeedbacksByDescVersion({ @@ -440,53 +441,53 @@ const FeedbacksSection = ({ withMetadata: true, paginationParams: { limit: FEEDBACKS_BATCH_SIZE, - lastValue: currentOldestFeedbackId, + lastValue: + paginationState.pageIndex === 0 + ? undefined + : lastIdForBatches[paginationState.pageIndex - 1], orderColumn: "id", orderDirection: "desc", }, }); - // Handle pagination updates. - useEffect(() => { - if ( - !!agentConfigurationFeedbacks && - agentConfigurationFeedbacks.length > 0 - ) { - setFeedbacks((prevFeedbacks) => { - const newFeedbacks = agentConfigurationFeedbacks.filter( - (f) => !prevFeedbacks.some((pf) => pf.id === f.id) - ) as AgentMessageFeedbackWithMetadataType[]; - return [...prevFeedbacks, ...newFeedbacks]; - }); - } - }, [agentConfigurationFeedbacks]); - - // Determine when feedback is exhausted - useEffect(() => { - if (!agentConfigurationFeedbacks) { - return; - } - setFeedbacksExhausted( - agentConfigurationFeedbacks.length === 0 || - // We limit the number of feedbacks to prevent the page from becoming too slow. - feedbacks.length + FEEDBACKS_BATCH_SIZE > MAX_FEEDBACKS_TO_DISPLAY - ); - }, [agentConfigurationFeedbacks, feedbacks]); - const { agentConfigurationHistory, isAgentConfigurationHistoryLoading } = useAgentConfigurationHistory({ workspaceId: owner.sId, agentConfigurationId: assistantId, }); - const handleLoadMoreFeedbacks = useCallback(() => { - if (agentConfigurationFeedbacks && agentConfigurationFeedbacks.length > 0) { - // This triggers a re-fetch of the feedbacks. - setCurrentOldestFeedbackId( - agentConfigurationFeedbacks[agentConfigurationFeedbacks.length - 1].id - ); - } - }, [agentConfigurationFeedbacks]); + const handleSetPagination = useCallback( + (pagination: PaginationState) => { + // Pagination is not displayed if there are no feedbacks. + if ( + !agentConfigurationFeedbacks || + agentConfigurationFeedbacks.feedbacks.length === 0 + ) { + return; + } + setLastIdForBatches((prev) => ({ + ...prev, + ...{ + [paginationState.pageIndex]: + agentConfigurationFeedbacks.feedbacks[ + agentConfigurationFeedbacks.feedbacks.length - 1 + ].id, + }, + })); + setPaginationState(pagination); + }, + [agentConfigurationFeedbacks, paginationState.pageIndex] + ); + + const firstAgentConfigurationInBatch = useMemo( + () => + agentConfigurationHistory?.find( + (c) => + c.version === + agentConfigurationFeedbacks?.feedbacks[0].agentConfigurationVersion + ), + [agentConfigurationHistory, agentConfigurationFeedbacks] + ); if ( isAgentConfigurationFeedbacksLoading || @@ -497,12 +498,13 @@ const FeedbacksSection = ({ if ( !isAgentConfigurationFeedbacksLoading && - (!feedbacks || feedbacks.length === 0) + (!agentConfigurationFeedbacks || + agentConfigurationFeedbacks.feedbacks.length === 0) ) { return
No feedbacks.
; } - if (!agentConfigurationHistory) { + if (!agentConfigurationHistory || !firstAgentConfigurationInBatch) { return (
Error loading the previous agent versions. @@ -514,16 +516,20 @@ const FeedbacksSection = ({
- {feedbacks.map((feedback, index) => { + {agentConfigurationFeedbacks?.feedbacks.map((feedback, index) => { const isFirstFeedback = index === 0; const isNewVersion = !isFirstFeedback && feedback.agentConfigurationVersion !== - feedbacks[index - 1].agentConfigurationVersion; + agentConfigurationFeedbacks.feedbacks[index - 1] + .agentConfigurationVersion; return (
{isNewVersion && ( @@ -550,16 +556,20 @@ const FeedbacksSection = ({ ); })}
- {feedbacks && !feedbacksExhausted && ( -
-
- )} + {agentConfigurationFeedbacks && + agentConfigurationFeedbacks.totalFeedbackCount > 0 && ( +
+ +
+ )}
); }; diff --git a/front/lib/api/assistant/feedback.ts b/front/lib/api/assistant/feedback.ts index e020c2ce74e7..2d35fc18c0d1 100644 --- a/front/lib/api/assistant/feedback.ts +++ b/front/lib/api/assistant/feedback.ts @@ -183,7 +183,13 @@ export async function getAgentFeedbacks({ paginationParams: PaginationParams; }): Promise< Result< - (AgentMessageFeedbackType | AgentMessageFeedbackWithMetadataType)[], + { + feedbacks: ( + | AgentMessageFeedbackType + | AgentMessageFeedbackWithMetadataType + )[]; + totalFeedbackCount: number; + }, Error > > { @@ -212,14 +218,17 @@ export async function getAgentFeedbacks({ return new Ok(feedbacksRes); } - const feedbacks = ( - feedbacksRes as AgentMessageFeedbackWithMetadataType[] - ).map((feedback) => ({ - ...feedback, - // Only display conversationId if the feedback was shared - conversationId: feedback.isConversationShared - ? feedback.conversationId - : null, - })); - return new Ok(feedbacks); + const feedbacksWithHiddenConversationId = feedbacksRes.feedbacks.map( + (feedback) => ({ + ...feedback, + // Redact the conversationId if user did not share the conversation. + conversationId: feedback.isConversationShared + ? (feedback as AgentMessageFeedbackWithMetadataType).conversationId + : null, + }) + ); + return new Ok({ + feedbacks: feedbacksWithHiddenConversationId, + totalFeedbackCount: feedbacksRes.totalFeedbackCount, + }); } diff --git a/front/lib/resources/agent_message_feedback_resource.ts b/front/lib/resources/agent_message_feedback_resource.ts index fe977829b775..5f68f0d1d38e 100644 --- a/front/lib/resources/agent_message_feedback_resource.ts +++ b/front/lib/resources/agent_message_feedback_resource.ts @@ -102,15 +102,24 @@ export class AgentMessageFeedbackResource extends BaseResource { + }): Promise<{ + feedbacks: ( + | AgentMessageFeedbackType + | AgentMessageFeedbackWithMetadataType + )[]; + totalFeedbackCount: number; + }> { const where: WhereOptions = { // IMPORTANT: Necessary for global models who share ids across workspaces. workspaceId: workspace.id, agentConfigurationId: agentConfiguration.sId.toString(), }; + // Get the total feedback count, because needed for pagination + const totalFeedbackCountPromise = AgentMessageFeedback.count({ + where, + }); + if (paginationParams.lastValue) { const op = paginationParams.orderDirection === "desc" ? Op.lt : Op.gt; where[paginationParams.orderColumn as any] = { @@ -118,7 +127,7 @@ export class AgentMessageFeedbackResource extends BaseResource = fetcher; const urlParams = new URLSearchParams({ @@ -274,7 +275,7 @@ export function useAgentConfigurationFeedbacksByDescVersion({ ); return { - agentConfigurationFeedbacks: data ? data.feedbacks : null, + agentConfigurationFeedbacks: data ? data : null, isAgentConfigurationFeedbacksLoading: !error && !data, isAgentConfigurationFeedbacksError: error, mutateAgentConfigurationFeedbacks: mutate, diff --git a/front/pages/api/w/[wId]/assistant/agent_configurations/[aId]/feedbacks.ts b/front/pages/api/w/[wId]/assistant/agent_configurations/[aId]/feedbacks.ts index 332f812296ee..7cc2e3ef7301 100644 --- a/front/pages/api/w/[wId]/assistant/agent_configurations/[aId]/feedbacks.ts +++ b/front/pages/api/w/[wId]/assistant/agent_configurations/[aId]/feedbacks.ts @@ -13,7 +13,10 @@ import { apiError } from "@app/logger/withlogging"; async function handler( req: NextApiRequest, res: NextApiResponse< - WithAPIErrorResponse<{ feedbacks: AgentMessageFeedbackType[] }> + WithAPIErrorResponse<{ + feedbacks: AgentMessageFeedbackType[]; + totalFeedbackCount: number; + }> >, auth: Authenticator ): Promise { @@ -77,7 +80,10 @@ async function handler( const feedbacks = feedbacksRes.value; - res.status(200).json({ feedbacks }); + res.status(200).json({ + feedbacks: feedbacks.feedbacks, + totalFeedbackCount: feedbacks.totalFeedbackCount, + }); return; default: