diff --git a/front/components/assistant/AssistantDetails.tsx b/front/components/assistant/AssistantDetails.tsx index bb07b3429c9d..cf3db700d00b 100644 --- a/front/components/assistant/AssistantDetails.tsx +++ b/front/components/assistant/AssistantDetails.tsx @@ -16,6 +16,7 @@ import { import type { AgentConfigurationScope, LightAgentConfigurationType, + LightWorkspaceType, WorkspaceType, } from "@dust-tt/types"; import { ExternalLinkIcon } from "lucide-react"; @@ -33,6 +34,7 @@ import { useAgentConfigurationHistory, useUpdateAgentScope, } from "@app/lib/swr/assistants"; +import { useFeedbackConversation } from "@app/lib/swr/feedbacks"; import { useUserDetails } from "@app/lib/swr/user"; import { classNames, timeAgoFrom } from "@app/lib/utils"; @@ -103,19 +105,15 @@ export function AssistantDetails({ ); const TabsSection = () => ( - + - + - + @@ -191,7 +189,7 @@ export function AssistantDetails({ {!agentConfigurationFeedbacks || agentConfigurationFeedbacks.length === 0 || !assistantId ? ( -
No feedbacks.
+
No feedbacks.
) : (
)} - +
))} @@ -278,9 +276,18 @@ function ConfigVersionHeader({ ); } -function FeedbackCard({ feedback }: { feedback: AgentMessageFeedbackType }) { +function FeedbackCard({ + owner, + feedback, +}: { + owner: LightWorkspaceType; + feedback: AgentMessageFeedbackType; +}) { const { userDetails } = useUserDetails(feedback.userId); - const conversationUrl = `https://dust.tt/w/0ec9852c2f/assistant/${feedback.c}`; + const { conversationId } = useFeedbackConversation({ + workspaceId: owner.sId, + feedbackId: feedback.id.toString(), + }); return ( @@ -319,16 +326,18 @@ function FeedbackCard({ feedback }: { feedback: AgentMessageFeedbackType }) { )} -
-
+ {conversationId && ( +
+
+ )}
); } diff --git a/front/lib/models/assistant/conversation.ts b/front/lib/models/assistant/conversation.ts index e93a305eff30..5c7e674e9169 100644 --- a/front/lib/models/assistant/conversation.ts +++ b/front/lib/models/assistant/conversation.ts @@ -416,6 +416,8 @@ AgentMessage.hasMany(AgentMessageFeedback, { User.hasMany(AgentMessageFeedback, { onDelete: "SET NULL", }); +AgentMessageFeedback.belongsTo(User); +AgentMessageFeedback.belongsTo(AgentMessage); export class Message extends Model< InferAttributes, diff --git a/front/lib/resources/agent_message_feedback_resource.ts b/front/lib/resources/agent_message_feedback_resource.ts index 6d5c52538a4b..0dc1fcfd61d6 100644 --- a/front/lib/resources/agent_message_feedback_resource.ts +++ b/front/lib/resources/agent_message_feedback_resource.ts @@ -7,7 +7,12 @@ import { Op } from "sequelize"; import type { AgentMessageFeedbackDirection } from "@app/lib/api/assistant/conversation/feedbacks"; import type { Authenticator } from "@app/lib/auth"; import type { AgentMessage } from "@app/lib/models/assistant/conversation"; -import { AgentMessageFeedback } from "@app/lib/models/assistant/conversation"; +import { + AgentMessage as AgentMessageModel, + AgentMessageFeedback, + Conversation, + Message, +} from "@app/lib/models/assistant/conversation"; import type { Workspace } from "@app/lib/models/workspace"; import { BaseResource } from "@app/lib/resources/base_resource"; import type { ReadonlyAttributesType } from "@app/lib/resources/storage/types"; @@ -140,6 +145,44 @@ export class AgentMessageFeedbackResource extends BaseResource { + const agentMessageFeedback = await AgentMessageFeedback.findByPk( + agentMessageFeedbackId, + { + // Feedback -> AgentMessage -> Message -> Conversation + include: [ + { + model: AgentMessageModel, + attributes: ["id"], + include: [ + { + model: Message, + as: "agentMessage", + attributes: ["id"], + include: [ + { + model: Conversation, + as: "conversation", + attributes: ["sId"], + }, + ], + }, + ], + }, + ], + } + ); + + if (!agentMessageFeedback) { + return null; + } + + // @ts-expect-error: sequelize cannot handle include easily + return agentMessageFeedback.agent_message.agentMessage.conversation.sId; + } + async fetchUser(): Promise { const users = await UserResource.fetchByModelIds([this.userId]); return users[0] ?? null; diff --git a/front/lib/swr/feedbacks.ts b/front/lib/swr/feedbacks.ts new file mode 100644 index 000000000000..a45b0c3cce74 --- /dev/null +++ b/front/lib/swr/feedbacks.ts @@ -0,0 +1,26 @@ +import type { Fetcher } from "swr"; + +import { fetcher, useSWRWithDefaults } from "@app/lib/swr/swr"; + +export function useFeedbackConversation({ + workspaceId, + feedbackId, +}: { + feedbackId: string; + workspaceId: string; +}) { + const feedbackFetcher: Fetcher<{ + conversationId: string; + }> = fetcher; + + const { data, error } = useSWRWithDefaults( + `/api/w/${workspaceId}/assistant/feedbacks/${feedbackId}/conversation`, + feedbackFetcher + ); + + return { + conversationId: data ? data.conversationId : null, + isLoading: !error && !data, + isError: error, + }; +} diff --git a/front/pages/api/w/[wId]/assistant/feedbacks/[fId]/conversation.ts b/front/pages/api/w/[wId]/assistant/feedbacks/[fId]/conversation.ts new file mode 100644 index 000000000000..530faac6cf52 --- /dev/null +++ b/front/pages/api/w/[wId]/assistant/feedbacks/[fId]/conversation.ts @@ -0,0 +1,59 @@ +import type { WithAPIErrorResponse } from "@dust-tt/types"; +import type { NextApiRequest, NextApiResponse } from "next"; + +import { withSessionAuthenticationForWorkspace } from "@app/lib/api/auth_wrappers"; +import type { Authenticator } from "@app/lib/auth"; +import { AgentMessageFeedbackResource } from "@app/lib/resources/agent_message_feedback_resource"; +import { apiError } from "@app/logger/withlogging"; + +export type GetAgentConfigurationsResponseBody = { + conversationId: string; +}; + +async function handler( + req: NextApiRequest, + res: NextApiResponse< + WithAPIErrorResponse + >, + auth: Authenticator +): Promise { + switch (req.method) { + case "GET": + if (typeof req.query.fId !== "string" || req.query.fId === "") { + return apiError(req, res, { + status_code: 400, + api_error: { + type: "invalid_request_error", + message: "Invalid query parameters, `fId` (string) is required.", + }, + }); + } + + const conversationId = + await AgentMessageFeedbackResource.fetchConversationId(req.query.fId); + + if (!conversationId) { + return apiError(req, res, { + status_code: 404, + api_error: { + type: "conversation_not_found", + message: `Conversation not found for feedback ${req.query.fId}`, + }, + }); + } + + return res.status(200).json({ + conversationId, + }); + default: + return apiError(req, res, { + status_code: 405, + api_error: { + type: "method_not_supported_error", + message: "The method passed is not supported, GET is expected.", + }, + }); + } +} + +export default withSessionAuthenticationForWorkspace(handler);