diff --git a/front/components/assistant/AssistantDetails.tsx b/front/components/assistant/AssistantDetails.tsx
index b853a294cf53..a282d0240d02 100644
--- a/front/components/assistant/AssistantDetails.tsx
+++ b/front/components/assistant/AssistantDetails.tsx
@@ -1,23 +1,42 @@
import {
Avatar,
+ Button,
ContentMessage,
ElementModal,
+ HandThumbDownIcon,
+ HandThumbUpIcon,
InformationCircleIcon,
Page,
+ Spinner,
+ Tabs,
+ TabsContent,
+ TabsList,
+ TabsTrigger,
} from "@dust-tt/sparkle";
-import type { AgentConfigurationScope, WorkspaceType } from "@dust-tt/types";
-import { useCallback, useState } from "react";
+import type {
+ AgentConfigurationScope,
+ LightAgentConfigurationType,
+ LightWorkspaceType,
+ WorkspaceType,
+} from "@dust-tt/types";
+import { ExternalLinkIcon } from "lucide-react";
+import { useCallback, useMemo, useState } from "react";
import { AssistantDetailsButtonBar } from "@app/components/assistant/AssistantDetailsButtonBar";
import { AssistantActionsSection } from "@app/components/assistant/details/AssistantActionsSection";
import { AssistantUsageSection } from "@app/components/assistant/details/AssistantUsageSection";
import { ReadOnlyTextArea } from "@app/components/assistant/ReadOnlyTextArea";
import { SharingDropdown } from "@app/components/assistant_builder/Sharing";
+import type { AgentMessageFeedbackType } from "@app/lib/api/assistant/feedback";
import {
useAgentConfiguration,
+ useAgentConfigurationFeedbacks,
+ useAgentConfigurationHistory,
useUpdateAgentScope,
} from "@app/lib/swr/assistants";
-import { classNames } from "@app/lib/utils";
+import { useFeedbackConversationContext } from "@app/lib/swr/feedbacks";
+import { useUserDetails } from "@app/lib/swr/user";
+import { classNames, timeAgoFrom } from "@app/lib/utils";
type AssistantDetailsProps = {
owner: WorkspaceType;
@@ -31,6 +50,7 @@ export function AssistantDetails({
owner,
}: AssistantDetailsProps) {
const [isUpdatingScope, setIsUpdatingScope] = useState(false);
+ const [activeTab, setActiveTab] = useState("info");
const { agentConfiguration } = useAgentConfiguration({
workspaceId: owner.sId,
@@ -55,7 +75,7 @@ export function AssistantDetails({
return <>>;
}
- const DescriptionSection = () => (
+ const TopSection = () => (
+ );
+
+ const TabsSection = () => (
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+
+ const InfoSection = () => (
+
{agentConfiguration.status === "active" && (
+
+
);
@@ -121,6 +170,70 @@ export function AssistantDetails({
"This assistant has no instructions."
);
+ const FeedbacksSection = () => {
+ const {
+ agentConfigurationFeedbacks,
+ isAgentConfigurationFeedbacksLoading,
+ } = useAgentConfigurationFeedbacks({
+ workspaceId: owner.sId,
+ agentConfigurationId: assistantId ?? "",
+ });
+
+ const sortedFeedbacks = useMemo(() => {
+ if (!agentConfigurationFeedbacks) {
+ return null;
+ }
+ return agentConfigurationFeedbacks.sort(
+ (a, b) =>
+ new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()
+ );
+ }, [agentConfigurationFeedbacks]);
+
+ const { agentConfigurationHistory, isAgentConfigurationHistoryLoading } =
+ useAgentConfigurationHistory({
+ workspaceId: owner.sId,
+ agentConfigurationId: assistantId || "",
+ disabled: !assistantId,
+ });
+
+ return isAgentConfigurationFeedbacksLoading ||
+ isAgentConfigurationHistoryLoading ? (
+
+ ) : (
+
+ {!sortedFeedbacks || sortedFeedbacks.length === 0 || !assistantId ? (
+
No feedbacks.
+ ) : (
+
+
+ {sortedFeedbacks.map((feedback, index) => (
+
+ {index > 0 &&
+ feedback.agentConfigurationVersion !==
+ sortedFeedbacks[index - 1].agentConfigurationVersion && (
+
c.version === feedback.agentConfigurationVersion
+ )}
+ agentConfigurationVersion={
+ feedback.agentConfigurationVersion
+ }
+ isLatestVersion={false}
+ />
+ )}
+
+
+ ))}
+
+ )}
+
+ );
+ };
+
return (
);
}
+
+function AgentConfigurationVersionHeader({
+ agentConfigurationVersion,
+ agentConfiguration,
+ isLatestVersion,
+}: {
+ agentConfigurationVersion: number;
+ agentConfiguration: LightAgentConfigurationType | undefined;
+ isLatestVersion: boolean;
+}) {
+ const getAgentConfigurationVersionString = useCallback(
+ (config: LightAgentConfigurationType) => {
+ return isLatestVersion
+ ? "Latest Version"
+ : !config.versionCreatedAt
+ ? `v${config.version}`
+ : new Date(config.versionCreatedAt).toLocaleDateString("en-US", {
+ year: "numeric",
+ month: "long",
+ day: "numeric",
+ });
+ },
+ [isLatestVersion]
+ );
+
+ return (
+
+
+ {agentConfiguration
+ ? getAgentConfigurationVersionString(agentConfiguration)
+ : `v${agentConfigurationVersion}`}
+
+
+ );
+}
+
+function FeedbackCard({
+ owner,
+ feedback,
+}: {
+ owner: LightWorkspaceType;
+ feedback: AgentMessageFeedbackType;
+}) {
+ const { userDetails } = useUserDetails(feedback.userId);
+ const { conversationContext } = useFeedbackConversationContext({
+ workspaceId: owner.sId,
+ feedbackId: feedback.id.toString(),
+ });
+ const conversationUrl = conversationContext
+ ? `${process.env.NEXT_PUBLIC_DUST_CLIENT_FACING_URL}/w/${owner.sId}/assistant/${conversationContext.conversationId}#${conversationContext.messageId}`
+ : null;
+
+ return (
+
+
+
+ {userDetails?.image ? (
+
+ ) : (
+
+ )}
+ {userDetails?.firstName} {userDetails?.lastName}
+
+
+ {timeAgoFrom(
+ feedback.createdAt instanceof Date
+ ? feedback.createdAt.getTime()
+ : new Date(feedback.createdAt).getTime(),
+ {
+ useLongFormat: true,
+ }
+ )}{" "}
+ ago
+
+
+
+
{feedback.content}
+
+ {feedback.thumbDirection === "up" ? (
+
+ ) : (
+
+ )}
+
+
+ {conversationContext && conversationUrl && (
+
+
+
+ )}
+
+ );
+}
diff --git a/front/components/assistant/conversation/AgentMessage.tsx b/front/components/assistant/conversation/AgentMessage.tsx
index 3c969244a3e0..6511a13f6219 100644
--- a/front/components/assistant/conversation/AgentMessage.tsx
+++ b/front/components/assistant/conversation/AgentMessage.tsx
@@ -126,20 +126,27 @@ export const FeedbackSelectorPopoverContent = ({
return (
agentLastAuthor && (
-
- {agentLastAuthor?.image && (
-
- )}
-
- Your feedback will be sent to:
-
- {agentLastAuthor?.firstName} {agentLastAuthor?.lastName}
-
-
+ <>
+
+ {agentLastAuthor?.image && (
+
+ )}
+
+ Your feedback will be sent to:
+
+ {agentLastAuthor?.firstName} {agentLastAuthor?.lastName}
+
+
+
+
+ Your full conversation with the assistant will be shared.
+
+
+ >
)
);
};
diff --git a/front/components/assistant/conversation/MessageItem.tsx b/front/components/assistant/conversation/MessageItem.tsx
index 14172841d871..73bf6defb59e 100644
--- a/front/components/assistant/conversation/MessageItem.tsx
+++ b/front/components/assistant/conversation/MessageItem.tsx
@@ -15,7 +15,8 @@ import type {
UserType,
WorkspaceType,
} from "@dust-tt/types";
-import React from "react";
+import { useRouter } from "next/router";
+import React, { useEffect, useState } from "react";
import { useSWRConfig } from "swr";
import { AgentMessage } from "@app/components/assistant/conversation/AgentMessage";
@@ -90,10 +91,6 @@ const MessageItem = React.forwardRef(
}
);
- if (message.visibility === "deleted") {
- return null;
- }
-
const messageFeedbackWithSubmit: FeedbackSelectorProps = {
feedback: messageFeedback
? {
@@ -105,6 +102,36 @@ const MessageItem = React.forwardRef(
isSubmittingThumb,
};
+ const router = useRouter();
+ const [hasScrolledToMessage, setHasScrolledToMessage] = useState(false);
+ const [highlighted, setHighlighted] = useState(false);
+
+ // Effect: scroll to the message and temporarily highlight if it is the anchor's target
+ useEffect(() => {
+ if (!router.asPath.includes("#")) {
+ return;
+ }
+ const messageIdToScrollTo = router.asPath.split("#")[1];
+ if (messageIdToScrollTo === sId && !hasScrolledToMessage) {
+ setTimeout(() => {
+ setHasScrolledToMessage(true);
+ document
+ .getElementById(`message-id-${sId}`)
+ ?.scrollIntoView({ behavior: "smooth", block: "center" });
+ setHighlighted(true);
+
+ // Highlight the message for a short time
+ setTimeout(() => {
+ setHighlighted(false);
+ }, 2000);
+ }, 100);
+ }
+ }, [hasScrolledToMessage, router.asPath, sId, ref]);
+
+ if (message.visibility === "deleted") {
+ return null;
+ }
+
switch (type) {
case "user_message":
const citations = message.contenFragments
@@ -153,7 +180,12 @@ const MessageItem = React.forwardRef(
: undefined;
return (
-
+
(
case "agent_message":
return (
-
+
> {
+ const feedbacksRes =
+ await AgentMessageFeedbackResource.fetchByAgentConfigurationId(
+ agentConfigurationId
+ );
+
+ const feedbacks = feedbacksRes.map(
+ (feedback) =>
+ ({
+ id: feedback.id,
+ userId: feedback.userId,
+ thumbDirection: feedback.thumbDirection,
+ content: feedback.content,
+ agentConfigurationVersion: feedback.agentConfigurationVersion,
+ agentConfigurationId: feedback.agentConfigurationId,
+ createdAt: feedback.createdAt,
+ }) as AgentMessageFeedbackType
+ );
+ return new Ok(feedbacks);
+}
+
export async function getConversationFeedbacksForUser(
auth: Authenticator,
conversation: ConversationType | ConversationWithoutContentType
diff --git a/front/lib/models/assistant/conversation.ts b/front/lib/models/assistant/conversation.ts
index cae554ffc2ba..3bfa6c811213 100644
--- a/front/lib/models/assistant/conversation.ts
+++ b/front/lib/models/assistant/conversation.ts
@@ -366,6 +366,8 @@ AgentMessage.hasMany(AgentMessageFeedback, {
UserModel.hasMany(AgentMessageFeedback, {
onDelete: "SET NULL",
});
+AgentMessageFeedback.belongsTo(UserModel);
+AgentMessageFeedback.belongsTo(AgentMessage);
export class Message extends BaseModel {
declare createdAt: CreationOptional;
diff --git a/front/lib/resources/agent_message_feedback_resource.ts b/front/lib/resources/agent_message_feedback_resource.ts
index 8d47d2d712d1..ff3dd2783aa7 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";
@@ -102,6 +107,21 @@ export class AgentMessageFeedbackResource extends BaseResource {
+ const agentMessageFeedback = await AgentMessageFeedback.findAll({
+ where: {
+ agentConfigurationId,
+ },
+ order: [["agentConfigurationVersion", "DESC"]],
+ });
+
+ return agentMessageFeedback.map(
+ (feedback) => new this(this.model, feedback.get())
+ );
+ }
+
static async listByWorkspaceAndDateRange({
workspace,
startDate,
@@ -125,6 +145,49 @@ 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", "sId"],
+ include: [
+ {
+ model: Conversation,
+ as: "conversation",
+ attributes: ["sId"],
+ },
+ ],
+ },
+ ],
+ },
+ ],
+ }
+ );
+
+ if (!agentMessageFeedback) {
+ return null;
+ }
+
+ return {
+ // @ts-expect-error: sequelize cannot handle include easily
+ messageId: agentMessageFeedback.agent_message.agentMessage.sId,
+ conversationId:
+ // @ts-expect-error: sequelize cannot handle include easily
+ 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/assistants.ts b/front/lib/swr/assistants.ts
index dc80fcded342..ddb5c52fa63b 100644
--- a/front/lib/swr/assistants.ts
+++ b/front/lib/swr/assistants.ts
@@ -11,6 +11,7 @@ import { useCallback, useMemo, useState } from "react";
import type { Fetcher } from "swr";
import { useSWRConfig } from "swr";
+import type { AgentMessageFeedbackType } from "@app/lib/api/assistant/feedback";
import {
fetcher,
getErrorFromResponse,
@@ -242,6 +243,32 @@ export function useAgentConfiguration({
};
}
+export function useAgentConfigurationFeedbacks({
+ workspaceId,
+ agentConfigurationId,
+}: {
+ workspaceId: string;
+ agentConfigurationId: string | null;
+}) {
+ const agentConfigurationFeedbacksFetcher: Fetcher<{
+ feedbacks: AgentMessageFeedbackType[];
+ }> = fetcher;
+
+ const { data, error, mutate } = useSWRWithDefaults(
+ agentConfigurationId
+ ? `/api/w/${workspaceId}/assistant/agent_configurations/${agentConfigurationId}/feedbacks`
+ : null,
+ agentConfigurationFeedbacksFetcher
+ );
+
+ return {
+ agentConfigurationFeedbacks: data ? data.feedbacks : null,
+ isAgentConfigurationFeedbacksLoading: !error && !data,
+ isAgentConfigurationFeedbacksError: error,
+ mutateAgentConfigurationFeedbacks: mutate,
+ };
+}
+
export function useAgentConfigurationHistory({
workspaceId,
agentConfigurationId,
diff --git a/front/lib/swr/feedbacks.ts b/front/lib/swr/feedbacks.ts
new file mode 100644
index 000000000000..c238793fde64
--- /dev/null
+++ b/front/lib/swr/feedbacks.ts
@@ -0,0 +1,27 @@
+import type { Fetcher } from "swr";
+
+import { fetcher, useSWRWithDefaults } from "@app/lib/swr/swr";
+
+export function useFeedbackConversationContext({
+ workspaceId,
+ feedbackId,
+}: {
+ feedbackId: string;
+ workspaceId: string;
+}) {
+ const feedbackFetcher: Fetcher<{
+ conversationId: string;
+ messageId: string;
+ }> = fetcher;
+
+ const { data, error } = useSWRWithDefaults(
+ `/api/w/${workspaceId}/assistant/feedbacks/${feedbackId}/conversation-context`,
+ feedbackFetcher
+ );
+
+ return {
+ conversationContext: data ? data : null,
+ isLoading: !error && !data,
+ isError: error,
+ };
+}
diff --git a/front/lib/swr/user.ts b/front/lib/swr/user.ts
index b661938d7bbd..635d3cfb0d43 100644
--- a/front/lib/swr/user.ts
+++ b/front/lib/swr/user.ts
@@ -2,6 +2,7 @@ import type { Fetcher } from "swr";
import { fetcher, useSWRWithDefaults } from "@app/lib/swr/swr";
import type { GetUserResponseBody } from "@app/pages/api/user";
+import type { GetUserDetailsResponseBody } from "@app/pages/api/user/[uId]/details";
import type { GetUserMetadataResponseBody } from "@app/pages/api/user/metadata/[key]";
export function useUser() {
@@ -30,3 +31,17 @@ export function useUserMetadata(key: string) {
mutateMetadata: mutate,
};
}
+
+export function useUserDetails(userId: number) {
+ const userFetcher: Fetcher = fetcher;
+ const { data, error } = useSWRWithDefaults(
+ `/api/user/${userId}/details`,
+ userFetcher
+ );
+
+ return {
+ userDetails: data,
+ isUserDetailsLoading: !error && !data,
+ isUserDetailsError: error,
+ };
+}
diff --git a/front/pages/api/user/[uId]/details/index.ts b/front/pages/api/user/[uId]/details/index.ts
new file mode 100644
index 000000000000..d0cb2872d6a7
--- /dev/null
+++ b/front/pages/api/user/[uId]/details/index.ts
@@ -0,0 +1,70 @@
+import type { UserType, WithAPIErrorResponse } from "@dust-tt/types";
+import type { NextApiRequest, NextApiResponse } from "next";
+
+import { withSessionAuthentication } from "@app/lib/api/auth_wrappers";
+import { UserResource } from "@app/lib/resources/user_resource";
+import { apiError } from "@app/logger/withlogging";
+
+export type GetUserDetailsResponseBody = Pick<
+ UserType,
+ "firstName" | "lastName" | "image"
+>;
+
+async function handler(
+ req: NextApiRequest,
+ res: NextApiResponse>
+): Promise {
+ switch (req.method) {
+ case "GET":
+ const userId = req.query.uId;
+ if (typeof userId !== "string" || userId === "") {
+ return apiError(req, res, {
+ status_code: 400,
+ api_error: {
+ type: "invalid_request_error",
+ message: "The query parameter `uId` is not a string or is empty.",
+ },
+ });
+ }
+
+ try {
+ parseInt(userId);
+ } catch (e) {
+ return apiError(req, res, {
+ status_code: 400,
+ api_error: {
+ type: "invalid_request_error",
+ message: "The query parameter `uId` is not a number.",
+ },
+ });
+ }
+
+ const user = await UserResource.fetchByModelId(parseInt(userId));
+ if (!user) {
+ return apiError(req, res, {
+ status_code: 404,
+ api_error: {
+ type: "user_not_found",
+ message: "The user was not found.",
+ },
+ });
+ }
+
+ return res.status(200).json({
+ firstName: user.firstName,
+ lastName: user.lastName,
+ image: user.imageUrl,
+ });
+
+ 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 withSessionAuthentication(handler);
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
new file mode 100644
index 000000000000..cc647de587f4
--- /dev/null
+++ b/front/pages/api/w/[wId]/assistant/agent_configurations/[aId]/feedbacks.ts
@@ -0,0 +1,50 @@
+import type { WithAPIErrorResponse } from "@dust-tt/types";
+import type { NextApiRequest, NextApiResponse } from "next";
+
+import { apiErrorForConversation } from "@app/lib/api/assistant/conversation/helper";
+import type { AgentMessageFeedbackType } from "@app/lib/api/assistant/feedback";
+import { getAgentConfigurationFeedbacks } from "@app/lib/api/assistant/feedback";
+import { withSessionAuthenticationForWorkspace } from "@app/lib/api/auth_wrappers";
+import { apiError } from "@app/logger/withlogging";
+
+async function handler(
+ req: NextApiRequest,
+ res: NextApiResponse<
+ WithAPIErrorResponse<{ feedbacks: AgentMessageFeedbackType[] }>
+ >
+): Promise {
+ if (!(typeof req.query.aId === "string")) {
+ return apiError(req, res, {
+ status_code: 400,
+ api_error: {
+ type: "invalid_request_error",
+ message: "Invalid query parameters, `aId` (string) is required.",
+ },
+ });
+ }
+
+ switch (req.method) {
+ case "GET":
+ const feedbacksRes = await getAgentConfigurationFeedbacks(req.query.aId);
+
+ if (feedbacksRes.isErr()) {
+ return apiErrorForConversation(req, res, feedbacksRes.error);
+ }
+
+ const feedbacks = feedbacksRes.value;
+
+ res.status(200).json({ feedbacks });
+ return;
+
+ 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);
diff --git a/front/pages/api/w/[wId]/assistant/feedbacks/[fId]/conversation-context.ts b/front/pages/api/w/[wId]/assistant/feedbacks/[fId]/conversation-context.ts
new file mode 100644
index 000000000000..50620b25c083
--- /dev/null
+++ b/front/pages/api/w/[wId]/assistant/feedbacks/[fId]/conversation-context.ts
@@ -0,0 +1,108 @@
+import type { WithAPIErrorResponse } from "@dust-tt/types";
+import type { NextApiRequest, NextApiResponse } from "next";
+
+import { getAgentConfiguration } from "@app/lib/api/assistant/configuration";
+import { withSessionAuthenticationForWorkspace } from "@app/lib/api/auth_wrappers";
+import { 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;
+ messageId: string;
+};
+
+async function handler(
+ req: NextApiRequest,
+ res: NextApiResponse<
+ WithAPIErrorResponse
+ >,
+ auth: Authenticator
+): Promise {
+ switch (req.method) {
+ case "GET":
+ const feedbackId = req.query.fId;
+ if (typeof feedbackId !== "string" || feedbackId === "") {
+ return apiError(req, res, {
+ status_code: 400,
+ api_error: {
+ type: "invalid_request_error",
+ message: "Invalid query parameters, `fId` (string) is required.",
+ },
+ });
+ }
+
+ // Make sure that user is one of the authors
+ const feedback =
+ await AgentMessageFeedbackResource.fetchByModelId(feedbackId);
+ if (!feedback) {
+ return apiError(req, res, {
+ status_code: 404,
+ api_error: {
+ type: "feedback_not_found",
+ message: `Feedback not found for id ${feedbackId}`,
+ },
+ });
+ }
+ const agent = await getAgentConfiguration(
+ auth,
+ feedback.agentConfigurationId
+ );
+ if (!agent) {
+ return apiError(req, res, {
+ status_code: 404,
+ api_error: {
+ type: "agent_configuration_not_found",
+ message: `Agent configuration not found for id ${feedback.agentConfigurationId}`,
+ },
+ });
+ }
+ if (
+ !auth.canRead(
+ Authenticator.createResourcePermissionsFromGroupIds(
+ agent.requestedGroupIds
+ )
+ )
+ ) {
+ return apiError(req, res, {
+ status_code: 404,
+ api_error: {
+ type: "feedback_not_found",
+ message: "Feedback not found.",
+ },
+ });
+ }
+
+ const messageAndConversation =
+ await AgentMessageFeedbackResource.fetchMessageAndConversationId(
+ feedbackId
+ );
+
+ if (!messageAndConversation) {
+ return apiError(req, res, {
+ status_code: 404,
+ api_error: {
+ type: "conversation_not_found",
+ message: `Conversation not found for feedback ${feedbackId}`,
+ },
+ });
+ }
+
+ const { conversationId, messageId } = messageAndConversation;
+
+ return res.status(200).json({
+ conversationId,
+ messageId,
+ });
+ 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);
diff --git a/types/src/front/lib/error.ts b/types/src/front/lib/error.ts
index d6e439478d80..b5c9d423855c 100644
--- a/types/src/front/lib/error.ts
+++ b/types/src/front/lib/error.ts
@@ -103,7 +103,9 @@ export type APIErrorType =
| ConversationErrorType
// Plugins:
| "plugin_not_found"
- | "plugin_execution_failed";
+ | "plugin_execution_failed"
+ // feedbacks
+ | "feedback_not_found";
export type APIError = {
type: APIErrorType;