Skip to content

Commit

Permalink
Revert "New resources for feedback loop (#9607)" (#9662)
Browse files Browse the repository at this point in the history
This reverts commit 3b2def6.
  • Loading branch information
overmode authored Dec 30, 2024
1 parent 280f06f commit 489a2a8
Show file tree
Hide file tree
Showing 6 changed files with 260 additions and 594 deletions.
270 changes: 144 additions & 126 deletions front/lib/api/assistant/feedback.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@ import type {
Result,
} from "@dust-tt/types";
import type { UserType } from "@dust-tt/types";
import { ConversationError, Err, Ok } from "@dust-tt/types";
import { ConversationError, Err, GLOBAL_AGENTS_SID, Ok } from "@dust-tt/types";
import { Op } from "sequelize";

import { getAgentConfiguration } from "@app/lib/api/assistant/configuration";
import { canAccessConversation } from "@app/lib/api/assistant/conversation/auth";
import type { AgentMessageFeedbackDirection } from "@app/lib/api/assistant/conversation/feedbacks";
import type { PaginationParams } from "@app/lib/api/pagination";
import type { Authenticator } from "@app/lib/auth";
import { AgentConfiguration } from "@app/lib/models/assistant/agent";
import { AgentMessage } from "@app/lib/models/assistant/conversation";
import { Message } from "@app/lib/models/assistant/conversation";
import { AgentMessageFeedbackResource } from "@app/lib/resources/agent_message_feedback_resource";

/**
Expand All @@ -24,106 +26,164 @@ export type AgentMessageFeedbackType = {
userId: number;
thumbDirection: AgentMessageFeedbackDirection;
content: string | null;
createdAt: Date;
agentConfigurationId: string;
agentConfigurationVersion: number;
isConversationShared: boolean;
};

export type AgentMessageFeedbackWithMetadataType = AgentMessageFeedbackType & {
conversationId: string | null;
userName: string;
userEmail: string;
userImageUrl: string | null;
};

export async function getConversationFeedbacksForUser(
auth: Authenticator,
conversation: ConversationType | ConversationWithoutContentType
): Promise<Result<AgentMessageFeedbackType[], ConversationError | Error>> {
if (!canAccessConversation(auth, conversation)) {
): Promise<Result<AgentMessageFeedbackType[], ConversationError>> {
const owner = auth.workspace();
if (!owner) {
throw new Error("Unexpected `auth` without `workspace`.");
}
const user = auth.user();
if (!canAccessConversation(auth, conversation) || !user) {
return new Err(new ConversationError("conversation_access_restricted"));
}

const messages = await Message.findAll({
where: {
conversationId: conversation.id,
agentMessageId: {
[Op.ne]: null,
},
},
attributes: ["sId", "agentMessageId"],
});

const agentMessages = await AgentMessage.findAll({
where: {
id: {
[Op.in]: messages
.map((m) => m.agentMessageId)
.filter((id): id is number => id !== null),
},
},
});

const feedbacks =
await AgentMessageFeedbackResource.getConversationFeedbacksForUser(
auth,
conversation
await AgentMessageFeedbackResource.fetchByUserAndAgentMessages(
user,
agentMessages
);

return feedbacks;
const feedbacksByMessageId = feedbacks.map(
(feedback) =>
({
id: feedback.id,
messageId: messages.find(
(m) => m.agentMessageId === feedback.agentMessageId
)!.sId,
agentMessageId: feedback.agentMessageId,
userId: feedback.userId,
thumbDirection: feedback.thumbDirection,
content: feedback.content,
}) as AgentMessageFeedbackType
);

return new Ok(feedbacksByMessageId);
}

/**
* We create a feedback for a single message.
* As user can be null (user from Slack), we also store the user context, as we do for messages.
*/
export async function upsertMessageFeedback(
export async function createOrUpdateMessageFeedback(
auth: Authenticator,
{
messageId,
conversation,
user,
thumbDirection,
content,
isConversationShared,
}: {
messageId: string;
conversation: ConversationType | ConversationWithoutContentType;
user: UserType;
thumbDirection: AgentMessageFeedbackDirection;
content?: string;
isConversationShared?: boolean;
}
) {
const feedbackWithConversationContext =
await AgentMessageFeedbackResource.getFeedbackWithConversationContext({
auth,
messageId,
conversation,
user,
});
): Promise<boolean | null> {
const owner = auth.workspace();
if (!owner) {
throw new Error("Unexpected `auth` without `workspace`.");
}

const message = await Message.findOne({
where: {
sId: messageId,
conversationId: conversation.id,
},
});

if (feedbackWithConversationContext.isErr()) {
return feedbackWithConversationContext;
if (!message || !message.agentMessageId) {
return null;
}

const { agentMessage, feedback, agentConfiguration, isGlobalAgent } =
feedbackWithConversationContext.value;
const agentMessage = await AgentMessage.findOne({
where: {
id: message.agentMessageId,
},
});

if (feedback) {
await feedback.updateFields({
content,
thumbDirection,
isConversationShared,
if (!agentMessage) {
return null;
}

let isGlobalAgent = false;
let agentConfigurationId = agentMessage.agentConfigurationId;
if (
Object.values(GLOBAL_AGENTS_SID).includes(
agentMessage.agentConfigurationId as GLOBAL_AGENTS_SID
)
) {
isGlobalAgent = true;
}

if (!isGlobalAgent) {
const agentConfiguration = await AgentConfiguration.findOne({
where: {
sId: agentMessage.agentConfigurationId,
},
});
return new Ok(undefined);

if (!agentConfiguration) {
return null;
}
agentConfigurationId = agentConfiguration.sId;
}

try {
await AgentMessageFeedbackResource.makeNew({
workspaceId: auth.getNonNullableWorkspace().id,
// If the agent is global, we use the agent configuration id from the agent message
// Otherwise, we use the agent configuration id from the agent configuration
agentConfigurationId: isGlobalAgent
? agentMessage.agentConfigurationId
: agentConfiguration.sId,
const feedback =
await AgentMessageFeedbackResource.fetchByUserAndAgentMessage({
user,
agentMessage,
});

if (feedback) {
const updatedFeedback = await feedback.updateContentAndThumbDirection(
content ?? "",
thumbDirection
);

return updatedFeedback.isOk();
} else {
const newFeedback = await AgentMessageFeedbackResource.makeNew({
workspaceId: owner.id,
agentConfigurationId: agentConfigurationId,
agentConfigurationVersion: agentMessage.agentConfigurationVersion,
agentMessageId: agentMessage.id,
userId: user.id,
thumbDirection,
content,
isConversationShared: isConversationShared ?? false,
isConversationShared: false,
});
} catch (e) {
return new Err(e as Error);
return newFeedback !== null;
}
return new Ok(undefined);
}

/**
* The id of a feedback is not exposed on the API so we need to find it from the message id and the user context.
* We destroy feedbacks, no point in soft-deleting them.
* The id of a reaction is not exposed on the API so we need to find it from the message id and the user context.
* We destroy reactions, no point in soft-deleting them.
*/
export async function deleteMessageFeedback(
auth: Authenticator,
Expand All @@ -136,87 +196,45 @@ export async function deleteMessageFeedback(
conversation: ConversationType | ConversationWithoutContentType;
user: UserType;
}
) {
if (!canAccessConversation(auth, conversation)) {
return new Err({
type: "conversation_access_restricted",
message: "You don't have access to this conversation.",
});
}

const feedbackWithContext =
await AgentMessageFeedbackResource.getFeedbackWithConversationContext({
auth,
messageId,
conversation,
user,
});

if (feedbackWithContext.isErr()) {
return feedbackWithContext;
): Promise<boolean | null> {
const owner = auth.workspace();
if (!owner) {
throw new Error("Unexpected `auth` without `workspace`.");
}

const { feedback } = feedbackWithContext.value;
const message = await Message.findOne({
where: {
sId: messageId,
conversationId: conversation.id,
},
attributes: ["agentMessageId"],
});

if (!feedback) {
return new Ok(undefined);
if (!message || !message.agentMessageId) {
return null;
}

const deleteRes = await feedback.delete(auth, {});
const agentMessage = await AgentMessage.findOne({
where: {
id: message.agentMessageId,
},
});

if (deleteRes.isErr()) {
return deleteRes;
if (!agentMessage) {
return null;
}

return new Ok(undefined);
}
const feedback =
await AgentMessageFeedbackResource.fetchByUserAndAgentMessage({
user,
agentMessage,
});

export async function getAgentFeedbacks({
auth,
agentConfigurationId,
withMetadata,
paginationParams,
}: {
auth: Authenticator;
withMetadata: boolean;
agentConfigurationId: string;
paginationParams: PaginationParams;
}): Promise<
Result<
(AgentMessageFeedbackType | AgentMessageFeedbackWithMetadataType)[],
Error
>
> {
const owner = auth.getNonNullableWorkspace();

// Make sure the user has access to the agent
const agentConfiguration = await getAgentConfiguration(
auth,
agentConfigurationId
);
if (!agentConfiguration) {
return new Err(new Error("agent_configuration_not_found"));
if (!feedback) {
return null;
}

const feedbacksRes = await AgentMessageFeedbackResource.fetch({
workspace: owner,
agentConfiguration,
paginationParams,
withMetadata,
});

if (!withMetadata) {
return new Ok(feedbacksRes);
}
const deletedFeedback = await feedback.delete(auth);

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);
return deletedFeedback.isOk();
}
Loading

0 comments on commit 489a2a8

Please sign in to comment.