From b46240971dd2bc838890c31002847410c3163b1f Mon Sep 17 00:00:00 2001 From: George Sproston <103250539+gsproston-scottlogic@users.noreply.github.com> Date: Fri, 11 Aug 2023 14:21:44 +0100 Subject: [PATCH] 104 chat box info messages (#118) * Chat message type enum * WIP: Unformatted info messages * Formatted chat info text * Comments on new methods --- frontend/src/App.tsx | 43 +++++++++++++-- frontend/src/components/ChatBox/ChatBox.tsx | 54 ++++++++++--------- .../src/components/ChatBox/ChatBoxFeed.tsx | 9 +++- .../components/ChatBox/ChatBoxInfoText.css | 5 ++ .../components/ChatBox/ChatBoxInfoText.tsx | 7 +++ .../src/components/ChatBox/ChatBoxMessage.tsx | 10 ++-- .../src/components/DefenceBox/DefenceBox.tsx | 14 ++++- frontend/src/models/chat.ts | 12 ++++- 8 files changed, 115 insertions(+), 39 deletions(-) create mode 100644 frontend/src/components/ChatBox/ChatBoxInfoText.css create mode 100644 frontend/src/components/ChatBox/ChatBoxInfoText.tsx diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 5abed3fd2..570f550db 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -1,4 +1,5 @@ -import React from "react"; +import { useState } from "react"; + import "./App.css"; import AttackBox from "./components/AttackBox/AttackBox"; import ChatBox from "./components/ChatBox/ChatBox"; @@ -7,12 +8,14 @@ import EmailBox from "./components/EmailBox/EmailBox"; import ApiKeyBox from "./components/ApiKeyBox/ApiKeyBox"; import Header from "./components/Header"; import ModelSelectionBox from "./components/ModelSelectionBox/ModelSelectionBox"; -import { useState } from "react"; import { EmailInfo } from "./models/email"; +import { CHAT_MESSAGE_TYPE, ChatMessage } from "./models/chat"; +import { DefenceInfo } from "./models/defence"; function App() { const [defenceBoxKey, setDefenceBoxKey] = useState(0); const [emails, setEmails] = useState([]); + const [messages, setMessages] = useState([]); const [triggeredDefences, setTriggeredDefences] = useState([]); const updateTriggeredDefences = (defenceDetails: string[]) => { @@ -22,11 +25,42 @@ function App() { setDefenceBoxKey(defenceBoxKey + 1); }; + // methods to modify messages + const addChatMessage = (message: ChatMessage) => { + setMessages((messages: ChatMessage[]) => [...messages, message]); + }; + const addInfoMessage = (message: string) => { + addChatMessage({ + message: message, + type: CHAT_MESSAGE_TYPE.INFO, + isOriginalMessage: true, + }); + }; + const clearMessages = () => { + setMessages([]); + }; + + // methods to be called when defences are (de)activated + // this adds an info message to the chat + const defenceActivated = (defenceInfo: DefenceInfo) => { + const infoMessage = `${defenceInfo.name} defence activated`; + addInfoMessage(infoMessage.toLowerCase()); + }; + const defenceDeactivated = (defenceInfo: DefenceInfo) => { + const infoMessage = `${defenceInfo.name} defence deactivated`; + addInfoMessage(infoMessage.toLowerCase()); + }; + return (
defence mechanisms
- +
attack mechanisms
@@ -38,8 +72,11 @@ function App() {
diff --git a/frontend/src/components/ChatBox/ChatBox.tsx b/frontend/src/components/ChatBox/ChatBox.tsx index dbe50776c..65711414e 100644 --- a/frontend/src/components/ChatBox/ChatBox.tsx +++ b/frontend/src/components/ChatBox/ChatBox.tsx @@ -3,26 +3,33 @@ import "./ChatBox.css"; import ChatBoxFeed from "./ChatBoxFeed"; import { clearChat, sendMessage } from "../../service/chatService"; import { getSentEmails } from "../../service/emailService"; -import { ChatMessage, ChatResponse } from "../../models/chat"; +import { + CHAT_MESSAGE_TYPE, + ChatMessage, + ChatResponse, +} from "../../models/chat"; import { EmailInfo } from "../../models/email"; function ChatBox( this: any, { + messages, setEmails, updateTriggeredDefences, + addChatMessage, + clearMessages, }: { + messages: ChatMessage[]; setEmails: (emails: EmailInfo[]) => void; updateTriggeredDefences: (defences: string[]) => void; + addChatMessage: (message: ChatMessage) => void; + clearMessages: () => void; } ) { const [isSendingMessage, setIsSendingMessage] = useState(false); - const [messages, setMessages] = useState([]); // called on mount useEffect(() => { - // clear remote messages - clearChat(); // get sent emails getSentEmails().then((sentEmails) => { setEmails(sentEmails); @@ -31,7 +38,7 @@ function ChatBox( const clearClicked = () => { // clear local messages - setMessages([]); + clearMessages(); // clear remote messages clearChat(); }; @@ -45,10 +52,11 @@ function ChatBox( const message = event.currentTarget.value; // if input has been edited, add both messages to the list of messages. otherwise add original message only - setMessages((messages: ChatMessage[]) => [ - ...messages, - { message: message, isUser: true, isOriginalMessage: true }, - ]); + addChatMessage({ + message: message, + type: CHAT_MESSAGE_TYPE.USER, + isOriginalMessage: true, + }); // clear the input event.currentTarget.value = ""; @@ -57,25 +65,19 @@ function ChatBox( const isTransformed = transformedMessage !== message; // add the transformed message to the chat box if it is different from the original message if (isTransformed) { - setMessages((messages: ChatMessage[]) => [ - ...messages, - { - message: transformedMessage, - isUser: true, - isOriginalMessage: false, - }, - ]); + addChatMessage({ + message: transformedMessage, + type: CHAT_MESSAGE_TYPE.USER, + isOriginalMessage: false, + }); } // add it to the list of messages - setMessages((messages: ChatMessage[]) => [ - ...messages, - { - isUser: false, - message: response.reply, - defenceInfo: response.defenceInfo, - isOriginalMessage: true, - }, - ]); + addChatMessage({ + type: CHAT_MESSAGE_TYPE.BOT, + message: response.reply, + defenceInfo: response.defenceInfo, + isOriginalMessage: true, + }); // update triggered defences updateTriggeredDefences(response.defenceInfo.triggeredDefences); diff --git a/frontend/src/components/ChatBox/ChatBoxFeed.tsx b/frontend/src/components/ChatBox/ChatBoxFeed.tsx index 94c2b6095..104621ccb 100644 --- a/frontend/src/components/ChatBox/ChatBoxFeed.tsx +++ b/frontend/src/components/ChatBox/ChatBoxFeed.tsx @@ -1,12 +1,17 @@ import "./ChatBoxFeed.css"; -import { ChatMessage } from "../../models/chat"; +import { CHAT_MESSAGE_TYPE, ChatMessage } from "../../models/chat"; import ChatBoxMessage from "./ChatBoxMessage"; +import ChatBoxInfoText from "./ChatBoxInfoText"; function ChatBoxFeed({ messages }: { messages: ChatMessage[] }) { return (
{[...messages].reverse().map((message, index) => { - return ; + if (message.type === CHAT_MESSAGE_TYPE.INFO) { + return ; + } else { + return ; + } })}
); diff --git a/frontend/src/components/ChatBox/ChatBoxInfoText.css b/frontend/src/components/ChatBox/ChatBoxInfoText.css new file mode 100644 index 000000000..e8c60829d --- /dev/null +++ b/frontend/src/components/ChatBox/ChatBoxInfoText.css @@ -0,0 +1,5 @@ +.chat-box-info-text { + color: #888; + font-size: 14px; + padding-top: 16px; +} diff --git a/frontend/src/components/ChatBox/ChatBoxInfoText.tsx b/frontend/src/components/ChatBox/ChatBoxInfoText.tsx new file mode 100644 index 000000000..dfcafe00a --- /dev/null +++ b/frontend/src/components/ChatBox/ChatBoxInfoText.tsx @@ -0,0 +1,7 @@ +import "./ChatBoxInfoText.css"; + +function ChatBoxInfoText({ text }: { text: string }) { + return
{text}
; +} + +export default ChatBoxInfoText; diff --git a/frontend/src/components/ChatBox/ChatBoxMessage.tsx b/frontend/src/components/ChatBox/ChatBoxMessage.tsx index fe77beb5a..2e42d645b 100644 --- a/frontend/src/components/ChatBox/ChatBoxMessage.tsx +++ b/frontend/src/components/ChatBox/ChatBoxMessage.tsx @@ -1,11 +1,11 @@ -import { ChatMessage } from "../../models/chat"; +import { CHAT_MESSAGE_TYPE, ChatMessage } from "../../models/chat"; import "./ChatBoxMessage.css"; function ChatBoxMessage({ message }: { message: ChatMessage }) { return (
- {(message.isUser && message.isOriginalMessage && Input: ) || - (message.isUser && !message.isOriginalMessage && Edited: )} + {(message.type === CHAT_MESSAGE_TYPE.USER && + message.isOriginalMessage && Input: ) || + (message.type === CHAT_MESSAGE_TYPE.USER && + !message.isOriginalMessage && Edited: )} {message.message}
); diff --git a/frontend/src/components/DefenceBox/DefenceBox.tsx b/frontend/src/components/DefenceBox/DefenceBox.tsx index 150ddf7b4..6881d19bf 100644 --- a/frontend/src/components/DefenceBox/DefenceBox.tsx +++ b/frontend/src/components/DefenceBox/DefenceBox.tsx @@ -8,9 +8,17 @@ import { configureDefence, } from "../../service/defenceService"; import { DEFENCE_DETAILS } from "../../Defences"; -import { DefenceConfig } from "../../models/defence"; +import { DefenceConfig, DefenceInfo } from "../../models/defence"; -function DefenceBox({ triggeredDefences }: { triggeredDefences: string[] }) { +function DefenceBox({ + triggeredDefences, + defenceActivated, + defenceDeactivated, +}: { + triggeredDefences: string[]; + defenceActivated: (defenceInfo: DefenceInfo) => void; + defenceDeactivated: (defenceInfo: DefenceInfo) => void; +}) { // list of defence mechanisms const [defenceDetails, setDefenceDetails] = useState(DEFENCE_DETAILS); @@ -62,6 +70,7 @@ function DefenceBox({ triggeredDefences }: { triggeredDefences: string[] }) { if (defenceDetail.id === defenceType) { defenceDetail.isActive = true; defenceDetail.isTriggered = false; + defenceActivated(defenceDetail); } return defenceDetail; }); @@ -76,6 +85,7 @@ function DefenceBox({ triggeredDefences }: { triggeredDefences: string[] }) { if (defenceDetail.id === defenceType) { defenceDetail.isActive = false; defenceDetail.isTriggered = false; + defenceDeactivated(defenceDetail); } return defenceDetail; }); diff --git a/frontend/src/models/chat.ts b/frontend/src/models/chat.ts index 6762c6091..37dddfa6e 100644 --- a/frontend/src/models/chat.ts +++ b/frontend/src/models/chat.ts @@ -1,3 +1,5 @@ +import { type } from "@testing-library/user-event/dist/type"; + enum CHAT_MODELS { GPT_4 = "gpt-4", GPT_4_0613 = "gpt-4-0613", @@ -9,16 +11,22 @@ enum CHAT_MODELS { GPT_3_5_TURBO_16K_0613 = "gpt-3.5-turbo-16k-0613", } +enum CHAT_MESSAGE_TYPE { + BOT, + INFO, + USER, +} + interface ChatDefenceReport { blocked: boolean; triggeredDefences: string[]; } interface ChatMessage { - isUser: boolean; defenceInfo?: ChatDefenceReport; message: string; isOriginalMessage: boolean; + type: CHAT_MESSAGE_TYPE; } interface ChatResponse { @@ -28,4 +36,4 @@ interface ChatResponse { } export type { ChatMessage, ChatResponse }; -export { CHAT_MODELS }; +export { CHAT_MODELS, CHAT_MESSAGE_TYPE };