diff --git a/front/components/assistant_builder/AssistantBuilderPreviewDrawerFeedbacks.tsx b/front/components/assistant_builder/AssistantBuilderPreviewDrawerFeedbacks.tsx index e8a7ca4c2dcf..f41574e557b0 100644 --- a/front/components/assistant_builder/AssistantBuilderPreviewDrawerFeedbacks.tsx +++ b/front/components/assistant_builder/AssistantBuilderPreviewDrawerFeedbacks.tsx @@ -5,15 +5,13 @@ import { HandThumbDownIcon, HandThumbUpIcon, Page, - Pagination, Spinner, } from "@dust-tt/sparkle"; import type { LightAgentConfigurationType, LightWorkspaceType, } from "@dust-tt/types"; -import type { PaginationState } from "@tanstack/react-table"; -import { useCallback, useMemo, useState } from "react"; +import { memo, useCallback, useEffect, useRef } from "react"; import type { AgentMessageFeedbackWithMetadataType } from "@app/lib/api/assistant/feedback"; import { @@ -32,33 +30,53 @@ export const FeedbacksSection = ({ owner, agentConfigurationId, }: FeedbacksSectionProps) => { - // Used for pagination's lastValue: page index -> last feedback id in page - const [lastIdForPage, setLastIdForPage] = useState>( - {} - ); - - const [paginationState, setPaginationState] = useState({ - pageIndex: 0, - pageSize: FEEDBACKS_PAGE_SIZE, + const { + isAgentConfigurationFeedbacksLoading, + isValidating, + agentConfigurationFeedbacks, + hasMore: feedbacksNotExhausted, + setSize, + size, + } = useAgentConfigurationFeedbacksByDescVersion({ + workspaceId: owner.sId, + agentConfigurationId: agentConfigurationId, + limit: FEEDBACKS_PAGE_SIZE, }); - // Decreasing version, paginated decreasing id. - const { agentConfigurationFeedbacks, isAgentConfigurationFeedbacksLoading } = - useAgentConfigurationFeedbacksByDescVersion({ - workspaceId: owner.sId, - agentConfigurationId: agentConfigurationId ?? "", - withMetadata: true, - paginationParams: { - limit: FEEDBACKS_PAGE_SIZE, - lastValue: - paginationState.pageIndex === 0 - ? undefined - : lastIdForPage[paginationState.pageIndex - 1], - orderColumn: "id", - orderDirection: "desc", + // Intersection observer to detect when the user has scrolled to the bottom of the list. + const bottomRef = useRef(null); + useEffect(() => { + const observer = new IntersectionObserver( + (entries) => { + const target = entries[0]; + if ( + target.isIntersecting && + !isValidating && + !isAgentConfigurationFeedbacksLoading && + feedbacksNotExhausted + ) { + setSize(size + 1); + } }, - disabled: !agentConfigurationId, - }); + { + threshold: 0.25, + } + ); + + if (bottomRef.current) { + observer.observe(bottomRef.current); + } + + return () => observer.disconnect(); + }, [ + bottomRef, + isValidating, + isAgentConfigurationFeedbacksLoading, + agentConfigurationFeedbacks, + setSize, + size, + feedbacksNotExhausted, + ]); const { agentConfigurationHistory, isAgentConfigurationHistoryLoading } = useAgentConfigurationHistory({ @@ -67,39 +85,6 @@ export const FeedbacksSection = ({ disabled: !agentConfigurationId, }); - const handleSetPagination = useCallback( - (pagination: PaginationState) => { - // Pagination is not displayed if there are no feedbacks. - if ( - !agentConfigurationFeedbacks || - agentConfigurationFeedbacks.feedbacks.length === 0 - ) { - return; - } - setLastIdForPage((prev) => ({ - ...prev, - ...{ - [paginationState.pageIndex]: - agentConfigurationFeedbacks.feedbacks[ - agentConfigurationFeedbacks.feedbacks.length - 1 - ].id, - }, - })); - setPaginationState(pagination); - }, - [agentConfigurationFeedbacks, paginationState.pageIndex] - ); - - const firstAgentConfigurationInPage = useMemo( - () => - agentConfigurationHistory?.find( - (c) => - c.version === - agentConfigurationFeedbacks?.feedbacks[0].agentConfigurationVersion - ), - [agentConfigurationHistory, agentConfigurationFeedbacks] - ); - if ( isAgentConfigurationFeedbacksLoading || isAgentConfigurationHistoryLoading @@ -109,13 +94,12 @@ export const FeedbacksSection = ({ if ( !isAgentConfigurationFeedbacksLoading && - (!agentConfigurationFeedbacks || - agentConfigurationFeedbacks.feedbacks.length === 0) + (!agentConfigurationFeedbacks || agentConfigurationFeedbacks.length === 0) ) { return
No feedbacks.
; } - if (!agentConfigurationHistory || !firstAgentConfigurationInPage) { + if (!agentConfigurationHistory) { return (
Error loading the previous agent versions. @@ -127,20 +111,16 @@ export const FeedbacksSection = ({
- {agentConfigurationFeedbacks?.feedbacks.map((feedback, index) => { + {agentConfigurationFeedbacks?.map((feedback, index) => { const isFirstFeedback = index === 0; const isNewVersion = !isFirstFeedback && feedback.agentConfigurationVersion !== - agentConfigurationFeedbacks.feedbacks[index - 1] - .agentConfigurationVersion; + agentConfigurationFeedbacks[index - 1].agentConfigurationVersion; return (
{isNewVersion && ( @@ -158,7 +138,7 @@ export const FeedbacksSection = ({
)}
- @@ -167,20 +147,8 @@ export const FeedbacksSection = ({ ); })}
- {agentConfigurationFeedbacks && - agentConfigurationFeedbacks.totalFeedbackCount > 0 && ( -
- -
- )} + {/* Invisible div to act as a scroll anchor for detecting when the user has scrolled to the bottom */} +
); }; @@ -222,6 +190,7 @@ interface FeedbackCardProps { owner: LightWorkspaceType; feedback: AgentMessageFeedbackWithMetadataType; } +const MemoizedFeedbackCard = memo(FeedbackCard); function FeedbackCard({ owner, feedback }: FeedbackCardProps) { const conversationUrl = feedback.conversationId && diff --git a/front/lib/api/assistant/feedback.ts b/front/lib/api/assistant/feedback.ts index 2d35fc18c0d1..7ce4086ab99b 100644 --- a/front/lib/api/assistant/feedback.ts +++ b/front/lib/api/assistant/feedback.ts @@ -183,13 +183,7 @@ export async function getAgentFeedbacks({ paginationParams: PaginationParams; }): Promise< Result< - { - feedbacks: ( - | AgentMessageFeedbackType - | AgentMessageFeedbackWithMetadataType - )[]; - totalFeedbackCount: number; - }, + (AgentMessageFeedbackType | AgentMessageFeedbackWithMetadataType)[], Error > > { @@ -218,17 +212,12 @@ export async function getAgentFeedbacks({ return new Ok(feedbacksRes); } - 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, - }); + const feedbacksWithHiddenConversationId = feedbacksRes.map((feedback) => ({ + ...feedback, + // Redact the conversationId if user did not share the conversation. + conversationId: feedback.isConversationShared + ? (feedback as AgentMessageFeedbackWithMetadataType).conversationId + : null, + })); + return new Ok(feedbacksWithHiddenConversationId); } diff --git a/front/lib/resources/agent_message_feedback_resource.ts b/front/lib/resources/agent_message_feedback_resource.ts index 3edf26598c94..ec5b13b7b6e4 100644 --- a/front/lib/resources/agent_message_feedback_resource.ts +++ b/front/lib/resources/agent_message_feedback_resource.ts @@ -102,24 +102,15 @@ export class AgentMessageFeedbackResource extends BaseResource { + }): Promise< + (AgentMessageFeedbackType | AgentMessageFeedbackWithMetadataType)[] + > { const where: WhereOptions = { // Safety check: global models share ids across workspaces and some have had feedbacks. workspaceId: workspace.id, agentConfigurationId: agentConfiguration.sId, }; - // 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] = { @@ -127,7 +118,7 @@ export class AgentMessageFeedbackResource extends BaseResource = fetcher; const urlParams = new URLSearchParams({ - limit: paginationParams.limit.toString(), - orderColumn: paginationParams.orderColumn, - orderDirection: paginationParams.orderDirection, + limit: limit.toString(), + orderColumn: "id", + orderDirection: "desc", + withMetadata: "true", }); - if (withMetadata) { - urlParams.append("withMetadata", "true"); - } - if (paginationParams.lastValue) { - urlParams.append("lastValue", paginationParams.lastValue.toString()); - } - const { data, error, mutate } = useSWRWithDefaults( - agentConfigurationId - ? `/api/w/${workspaceId}/assistant/agent_configurations/${agentConfigurationId}/feedbacks?${urlParams.toString()}` - : null, - agentConfigurationFeedbacksFetcher, - // If agentConfigurationId is null, we don't want to fetch - { disabled: disabled || !agentConfigurationId } - ); + const [hasMore, setHasMore] = useState(true); + + const { data, error, mutate, size, setSize, isLoading, isValidating } = + useSWRInfiniteWithDefaults( + (pageIndex: number, previousPageData) => { + console.log("previousPageData", previousPageData); + if (!agentConfigurationId) { + return null; + } + + // If we have reached the last page and there are no more + // messages or the previous page has no messages, return null. + if (previousPageData && previousPageData.feedbacks.length < limit) { + setHasMore(false); + return null; + } + + if (previousPageData !== null) { + const lastIdValue = + previousPageData.feedbacks[previousPageData.feedbacks.length - 1] + .id; + urlParams.append("lastValue", lastIdValue.toString()); + } + return `/api/w/${workspaceId}/assistant/agent_configurations/${agentConfigurationId}/feedbacks?${urlParams.toString()}`; + }, + agentConfigurationFeedbacksFetcher, + { + revalidateAll: false, + revalidateOnFocus: false, + } + ); return { - agentConfigurationFeedbacks: data ? data : null, - isAgentConfigurationFeedbacksLoading: !error && !data, + isLoadingInitialData: !error && !data, isAgentConfigurationFeedbacksError: error, + isAgentConfigurationFeedbacksLoading: isLoading, + isValidating, + agentConfigurationFeedbacks: useMemo( + () => (data ? data.flatMap((d) => (d ? d.feedbacks : [])) : []), + [data] + ), + hasMore, mutateAgentConfigurationFeedbacks: mutate, + setSize, + size, }; } 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 7cc2e3ef7301..ee5315167db2 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 @@ -15,7 +15,6 @@ async function handler( res: NextApiResponse< WithAPIErrorResponse<{ feedbacks: AgentMessageFeedbackType[]; - totalFeedbackCount: number; }> >, auth: Authenticator @@ -81,8 +80,7 @@ async function handler( const feedbacks = feedbacksRes.value; res.status(200).json({ - feedbacks: feedbacks.feedbacks, - totalFeedbackCount: feedbacks.totalFeedbackCount, + feedbacks: feedbacks, }); return;