& {
onEdit: () => void;
onCopy: () => void;
onDelete: () => void;
+ onEvaluate?: (value: boolean | null) => void;
+ isBotMessage?: boolean;
+ evaluation?: boolean | null;
}) {
const [isCopied, setIsCopied] = useState(false);
const handleCopy = () => {
onCopy();
setIsCopied(true);
- setTimeout(() => setIsCopied(false), 2000); // Reset after 2 seconds
+ setTimeout(() => setIsCopied(false), 2000);
+ };
+
+ const handleEvaluate = (value: boolean) => {
+ onEvaluate?.(evaluation === value ? null : value);
};
return (
@@ -56,6 +66,46 @@ export function EditMessageButton({
+
+ {isBotMessage && (
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )}
);
}
diff --git a/src/frontend/src/modals/IOModal/components/chatView/chatMessage/newChatMessage.tsx b/src/frontend/src/modals/IOModal/components/chatView/chatMessage/newChatMessage.tsx
index 75f1cb016..2d3cdc87a 100644
--- a/src/frontend/src/modals/IOModal/components/chatView/chatMessage/newChatMessage.tsx
+++ b/src/frontend/src/modals/IOModal/components/chatView/chatMessage/newChatMessage.tsx
@@ -212,6 +212,35 @@ export default function ChatMessage({
},
);
};
+
+ const handleEvaluateAnswer = (evaluation: boolean | null) => {
+ updateMessageMutation(
+ {
+ message: {
+ ...chat,
+ files: convertFiles(chat.files),
+ sender_name: chat.sender_name ?? "AI",
+ text: chat.message.toString(),
+ sender: chat.isSend ? "User" : "Machine",
+ flow_id,
+ session_id: chat.session ?? "",
+ properties: {
+ ...chat.properties,
+ positive_feedback: evaluation,
+ },
+ },
+ refetch: true,
+ },
+ {
+ onError: () => {
+ setErrorData({
+ title: "Error updating messages.",
+ });
+ },
+ },
+ );
+ };
+
const editedFlag = chat.edit ? (
(Edited)
) : null;
@@ -742,6 +771,9 @@ export default function ChatMessage({
onDelete={() => {}}
onEdit={() => setEditMessage(true)}
className="h-fit group-hover:visible"
+ isBotMessage={!chat.isSend}
+ onEvaluate={handleEvaluateAnswer}
+ evaluation={chat.properties?.positive_feedback}
/>
diff --git a/src/frontend/src/modals/IOModal/components/chatView/newChatView.tsx b/src/frontend/src/modals/IOModal/components/chatView/newChatView.tsx
index 6785ed863..551fd33d7 100644
--- a/src/frontend/src/modals/IOModal/components/chatView/newChatView.tsx
+++ b/src/frontend/src/modals/IOModal/components/chatView/newChatView.tsx
@@ -177,7 +177,10 @@ export default function ChatView({
New chat
-
+
Test your flow with a chat prompt
diff --git a/src/frontend/src/modals/IOModal/newModal.tsx b/src/frontend/src/modals/IOModal/newModal.tsx
index 2531c0db1..10802df65 100644
--- a/src/frontend/src/modals/IOModal/newModal.tsx
+++ b/src/frontend/src/modals/IOModal/newModal.tsx
@@ -4,7 +4,7 @@ import {
useGetMessagesQuery,
} from "@/controllers/API/queries/messages";
import { useUtilityStore } from "@/stores/utilityStore";
-import { useEffect, useState } from "react";
+import { useCallback, useEffect, useState } from "react";
import IconComponent from "../../components/common/genericIconComponent";
import ShadTooltip from "../../components/common/shadTooltipComponent";
import { Button } from "../../components/ui/button";
@@ -118,7 +118,6 @@ export default function IOModal({
const setLockChat = useFlowStore((state) => state.setLockChat);
const [chatValue, setChatValue] = useState("");
const isBuilding = useFlowStore((state) => state.isBuilding);
- const setNode = useFlowStore((state) => state.setNode);
const messages = useMessagesStore((state) => state.messages);
const [sessions, setSessions] = useState(
Array.from(
@@ -129,7 +128,6 @@ export default function IOModal({
),
),
);
- const flowPool = useFlowStore((state) => state.flowPool);
const [sessionId, setSessionId] = useState(currentFlowId);
const { isFetched: messagesFetched } = useGetMessagesQuery(
{
@@ -139,33 +137,44 @@ export default function IOModal({
{ enabled: open },
);
- async function sendMessage({
- repeat = 1,
- files,
- }: {
- repeat: number;
- files?: string[];
- }): Promise {
- if (isBuilding) return;
- setIsBuilding(true);
- setLockChat(true);
- setChatValue("");
- for (let i = 0; i < repeat; i++) {
- await buildFlow({
- input_value: chatValue,
- startNodeId: chatInput?.id,
- files: files,
- silent: true,
- session: sessionId,
- setLockChat,
- }).catch((err) => {
- console.error(err);
- setLockChat(false);
- });
- }
- // refetch();
- setLockChat(false);
- }
+ const sendMessage = useCallback(
+ async ({
+ repeat = 1,
+ files,
+ }: {
+ repeat: number;
+ files?: string[];
+ }): Promise => {
+ if (isBuilding) return;
+ setIsBuilding(true);
+ setLockChat(true);
+ setChatValue("");
+ for (let i = 0; i < repeat; i++) {
+ await buildFlow({
+ input_value: chatValue,
+ startNodeId: chatInput?.id,
+ files: files,
+ silent: true,
+ session: sessionId,
+ setLockChat,
+ }).catch((err) => {
+ console.error(err);
+ setLockChat(false);
+ });
+ }
+ // refetch();
+ setLockChat(false);
+ },
+ [
+ isBuilding,
+ setIsBuilding,
+ setLockChat,
+ chatValue,
+ chatInput?.id,
+ sessionId,
+ buildFlow,
+ ],
+ );
useEffect(() => {
setSelectedTab(inputs.length > 0 ? 1 : outputs.length > 0 ? 2 : 0);
diff --git a/src/frontend/src/types/chat/index.ts b/src/frontend/src/types/chat/index.ts
index 1bfac3d20..2fa10a036 100644
--- a/src/frontend/src/types/chat/index.ts
+++ b/src/frontend/src/types/chat/index.ts
@@ -37,6 +37,7 @@ export type PropertiesType = {
edited?: boolean;
allow_markdown?: boolean;
state?: string;
+ positive_feedback?: boolean | null;
};
export type ChatOutputType = {
diff --git a/src/frontend/src/utils/styleUtils.ts b/src/frontend/src/utils/styleUtils.ts
index 4e7849f5e..e13b3b783 100644
--- a/src/frontend/src/utils/styleUtils.ts
+++ b/src/frontend/src/utils/styleUtils.ts
@@ -15,6 +15,7 @@ import { ZepMemoryIcon } from "@/icons/ZepMemory";
import { AthenaIcon } from "@/icons/athena/index";
import { freezeAllIcon } from "@/icons/freezeAll";
import { GlobeOkIcon } from "@/icons/globe-ok";
+import { ThumbDownIconCustom, ThumbUpIconCustom } from "@/icons/thumbs";
import { TwitterLogoIcon } from "@radix-ui/react-icons";
import {
AlertCircle,
@@ -205,6 +206,8 @@ import {
TextCursorInput,
TextSearch,
TextSearchIcon,
+ ThumbsDown,
+ ThumbsUp,
ToyBrick,
Trash2,
Type,
@@ -927,4 +930,8 @@ export const nodeIconsLucide: iconsType = {
Cog,
ArrowRightLeft,
FolderSync,
+ ThumbsUp,
+ ThumbsDown,
+ ThumbDownIconCustom,
+ ThumbUpIconCustom,
};
diff --git a/src/frontend/tests/core/features/playground.spec.ts b/src/frontend/tests/core/features/playground.spec.ts
index db7eb6c27..7ad06be57 100644
--- a/src/frontend/tests/core/features/playground.spec.ts
+++ b/src/frontend/tests/core/features/playground.spec.ts
@@ -22,7 +22,9 @@ test(
await page.getByTestId("sidebar-search-input").click();
await page.getByTestId("sidebar-search-input").fill("chat output");
- await page.waitForTimeout(1000);
+ await page.waitForSelector('[data-testid="outputsChat Output"]', {
+ timeout: 100000,
+ });
await page
.getByTestId("outputsChat Output")
@@ -39,7 +41,9 @@ test(
await page.getByTestId("sidebar-search-input").click();
await page.getByTestId("sidebar-search-input").fill("chat input");
- await page.waitForTimeout(1000);
+ await page.waitForSelector('[data-testid="inputsChat Input"]', {
+ timeout: 100000,
+ });
await page
.getByTestId("inputsChat Input")
@@ -53,7 +57,9 @@ test(
await page.getByTestId("sidebar-search-input").click();
await page.getByTestId("sidebar-search-input").fill("text output");
- await page.waitForTimeout(1000);
+ await page.waitForSelector('[data-testid="outputsText Output"]', {
+ timeout: 100000,
+ });
await page
.getByTestId("outputsText Output")
@@ -168,7 +174,6 @@ test(
.filter({ hasText: /^Usermessage 1$/ })
.getByTestId("icon-Pen")
.click();
- await page.waitForTimeout(500);
await page.getByTestId("textarea").fill("edit_1");
await page.getByTestId("save-button").click();
@@ -177,8 +182,6 @@ test(
// check cancel edit
await page.getByTestId("sender_name_user").hover();
await page.getByTestId("icon-Pen").first().click();
- await page.waitForTimeout(500);
-
await page.getByTestId("textarea").fill("cancel_edit");
await page.getByTestId("cancel-button").click();
await page.getByTestId("chat-message-User-edit_1").click();
@@ -190,7 +193,6 @@ test(
.click();
await page.getByTestId("chat-message-AI-message 1").hover();
await page.getByTestId("icon-Pen").last().click();
- await page.waitForTimeout(500);
await page.getByTestId("textarea").fill("edit_bot_1");
await page.getByTestId("save-button").click();
@@ -198,7 +200,6 @@ test(
// check cancel edit bot
await page.getByTestId("chat-message-AI-edit_bot_1").hover();
await page.getByTestId("icon-Pen").last().click();
- await page.waitForTimeout(500);
await page.getByTestId("textarea").fill("edit_bot_cancel");
await page.getByTestId("cancel-button").click();
@@ -241,23 +242,47 @@ test(
await page.getByTestId("chat-message-User-session_after_delete").click();
await expect(page.getByTestId("session-selector")).toBeVisible();
- // check new chat
- await page.getByTestId("new-chat").click();
- await page.waitForTimeout(5000);
- await page.getByText("New chat").click();
- await page.getByTestId("input-chat-playground").click();
- await page.getByTestId("input-chat-playground").fill("second session");
- await page.keyboard.press("Enter");
- await page.waitForTimeout(5000);
-
- await page.getByTestId("chat-message-User-second session").click();
- await page
- .getByTestId("chat-message-AI-second session")
- .getByText("second session")
- .click();
- expect(await page.getByTestId("session-selector").count()).toBe(2);
-
- const sessionElements = await page.getByTestId("session-selector").all();
- expect(sessionElements.length).toBe(2);
+ // check helpful button
+ await page.getByTestId("chat-message-AI-session_after_delete").hover();
+ await page.getByTestId("helpful-button").click();
+ await page.getByTestId("chat-message-AI-session_after_delete").hover();
+ await expect(page.getByTestId("icon-ThumbUpIconCustom")).toBeVisible({
+ timeout: 10000,
+ });
+ await page.getByTestId("helpful-button").click();
+ await page.getByTestId("chat-message-AI-session_after_delete").hover();
+ await expect(page.getByTestId("icon-ThumbUpIconCustom")).toBeVisible({
+ timeout: 10000,
+ visible: false,
+ });
+ // check not helpful button
+ await page.getByTestId("chat-message-AI-session_after_delete").hover();
+ await page.getByTestId("not-helpful-button").click();
+ await page.getByTestId("chat-message-AI-session_after_delete").hover();
+ await expect(page.getByTestId("icon-ThumbDownIconCustom")).toBeVisible({
+ timeout: 10000,
+ });
+ await page.getByTestId("not-helpful-button").click();
+ await page.getByTestId("chat-message-AI-session_after_delete").hover();
+ await expect(page.getByTestId("icon-ThumbDownIconCustom")).toBeVisible({
+ timeout: 10000,
+ visible: false,
+ });
+ // check switch feedback
+ await page.getByTestId("chat-message-AI-session_after_delete").hover();
+ await page.getByTestId("helpful-button").click();
+ await page.getByTestId("chat-message-AI-session_after_delete").hover();
+ await expect(page.getByTestId("icon-ThumbUpIconCustom")).toBeVisible({
+ timeout: 10000,
+ });
+ await page.getByTestId("not-helpful-button").click();
+ await page.getByTestId("chat-message-AI-session_after_delete").hover();
+ await expect(page.getByTestId("icon-ThumbDownIconCustom")).toBeVisible({
+ timeout: 10000,
+ });
+ await expect(page.getByTestId("icon-ThumbUpIconCustom")).toBeVisible({
+ timeout: 10000,
+ visible: false,
+ });
},
);