diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 9bebac788..cd0201352 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -6,11 +6,11 @@ import EmailBox from "./components/EmailBox/EmailBox"; import Header from "./components/Header"; import ModelSelectionBox from "./components/ModelSelectionBox/ModelSelectionBox"; import { useState } from "react"; -import { OpenAIEmail } from "./service/emailService"; +import { EmailInfo } from "./models/email"; function App() { const [defenceBoxKey, setDefenceBoxKey] = useState(0); - const [emails, setEmails] = useState([]); + const [emails, setEmails] = useState([]); const [triggeredDefences, setTriggeredDefences] = useState([]); const updateTriggeredDefences = (defenceDetails: string[]) => { diff --git a/frontend/src/Defences.ts b/frontend/src/Defences.ts new file mode 100644 index 000000000..55cb29d53 --- /dev/null +++ b/frontend/src/Defences.ts @@ -0,0 +1,31 @@ +import { DEFENCE_TYPES, DefenceInfo } from "./models/defence"; + +const DEFENCE_DETAILS: DefenceInfo[] = [ + new DefenceInfo( + DEFENCE_TYPES.CHARACTER_LIMIT, + "Character Limit", + "Limit the number of characters in the user input. This is a form of prompt validation." + ), + new DefenceInfo( + DEFENCE_TYPES.EMAIL_WHITELIST, + "Email Whitelist", + "Only allow emails to those on a whitelist. They can be full email addresses, or domains in the format '*@scottlogic.com'" + ), + new DefenceInfo( + DEFENCE_TYPES.RANDOM_SEQUENCE_ENCLOSURE, + "Random Sequence Enclosure", + "Enclose the prompt between a random string and instruct bot to only follow enclosed instructions. This is a form of prompt validation." + ), + new DefenceInfo( + DEFENCE_TYPES.SYSTEM_ROLE, + "System Role", + "Tell the chat bot to follow a specific role." + ), + new DefenceInfo( + DEFENCE_TYPES.XML_TAGGING, + "XML Tagging", + "Enclose the users prompt between tags and escapes xml characters in raw input. This is a form of prompt validation." + ), +]; + +export { DEFENCE_DETAILS }; diff --git a/frontend/src/Models.ts b/frontend/src/Models.ts new file mode 100644 index 000000000..dcc03c2c6 --- /dev/null +++ b/frontend/src/Models.ts @@ -0,0 +1,12 @@ +const MODELS = [ + "gpt-4", + "gpt-4-0613", + "gpt-4-32k", + "gpt-4-32k-0613", + "gpt-3.5-turbo", + "gpt-3.5-turbo-0613", + "gpt-3.5-turbo-16k", + "gpt-3.5-turbo-16k-0613", +]; + +export { MODELS }; diff --git a/frontend/src/components/ChatBox/ChatBox.tsx b/frontend/src/components/ChatBox/ChatBox.tsx index fa1a4c5c5..dbe50776c 100644 --- a/frontend/src/components/ChatBox/ChatBox.tsx +++ b/frontend/src/components/ChatBox/ChatBox.tsx @@ -1,13 +1,10 @@ import React, { useEffect, useState } from "react"; import "./ChatBox.css"; import ChatBoxFeed from "./ChatBoxFeed"; -import { - OpenAIMessage, - OpenAIResponse, - clearOpenAiChat, - openAiSendMessage, -} from "../../service/openaiService"; -import { OpenAIEmail, getSentEmails } from "../../service/emailService"; +import { clearChat, sendMessage } from "../../service/chatService"; +import { getSentEmails } from "../../service/emailService"; +import { ChatMessage, ChatResponse } from "../../models/chat"; +import { EmailInfo } from "../../models/email"; function ChatBox( this: any, @@ -15,17 +12,17 @@ function ChatBox( setEmails, updateTriggeredDefences, }: { - setEmails: (emails: any) => void; + setEmails: (emails: EmailInfo[]) => void; updateTriggeredDefences: (defences: string[]) => void; } ) { const [isSendingMessage, setIsSendingMessage] = useState(false); - const [messages, setMessages] = useState([]); + const [messages, setMessages] = useState([]); // called on mount useEffect(() => { // clear remote messages - clearOpenAiChat(); + clearChat(); // get sent emails getSentEmails().then((sentEmails) => { setEmails(sentEmails); @@ -36,7 +33,7 @@ function ChatBox( // clear local messages setMessages([]); // clear remote messages - clearOpenAiChat(); + clearChat(); }; const sendChatMessage = async ( @@ -48,19 +45,19 @@ 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: OpenAIMessage[]) => [ + setMessages((messages: ChatMessage[]) => [ ...messages, { message: message, isUser: true, isOriginalMessage: true }, ]); // clear the input event.currentTarget.value = ""; - const response: OpenAIResponse = await openAiSendMessage(message); + const response: ChatResponse = await sendMessage(message); const transformedMessage = response.transformedMessage; const isTransformed = transformedMessage !== message; // add the transformed message to the chat box if it is different from the original message if (isTransformed) { - setMessages((messages: OpenAIMessage[]) => [ + setMessages((messages: ChatMessage[]) => [ ...messages, { message: transformedMessage, @@ -70,7 +67,7 @@ function ChatBox( ]); } // add it to the list of messages - setMessages((messages: OpenAIMessage[]) => [ + setMessages((messages: ChatMessage[]) => [ ...messages, { isUser: false, @@ -86,7 +83,7 @@ function ChatBox( setIsSendingMessage(false); // get sent emails - const sentEmails: OpenAIEmail[] = await getSentEmails(); + const sentEmails: EmailInfo[] = await getSentEmails(); // update emails setEmails(sentEmails); } diff --git a/frontend/src/components/ChatBox/ChatBoxFeed.tsx b/frontend/src/components/ChatBox/ChatBoxFeed.tsx index f9f616df0..94c2b6095 100644 --- a/frontend/src/components/ChatBox/ChatBoxFeed.tsx +++ b/frontend/src/components/ChatBox/ChatBoxFeed.tsx @@ -1,9 +1,8 @@ -import React from "react"; -import { OpenAIMessage } from "../../service/openaiService"; import "./ChatBoxFeed.css"; +import { ChatMessage } from "../../models/chat"; import ChatBoxMessage from "./ChatBoxMessage"; -function ChatBoxFeed({ messages }: { messages: OpenAIMessage[] }) { +function ChatBoxFeed({ messages }: { messages: ChatMessage[] }) { return (
{[...messages].reverse().map((message, index) => { diff --git a/frontend/src/components/ChatBox/ChatBoxMessage.tsx b/frontend/src/components/ChatBox/ChatBoxMessage.tsx index 180e65772..522581aad 100644 --- a/frontend/src/components/ChatBox/ChatBoxMessage.tsx +++ b/frontend/src/components/ChatBox/ChatBoxMessage.tsx @@ -1,8 +1,7 @@ -import React from "react"; -import { OpenAIMessage } from "../../service/openaiService"; +import { ChatMessage } from "../../models/chat"; import "./ChatBoxMessage.css"; -function ChatBoxMessage({ message }: { message: OpenAIMessage }) { +function ChatBoxMessage({ message }: { message: ChatMessage }) { return (
{ - return { - ...defence, - isActive: false, - isTriggered: false, - }; - }) - ); + const [defenceDetails, setDefenceDetails] = useState(DEFENCE_DETAILS); // called on mount useEffect(() => { // fetch defences from backend getActiveDefences().then((activeDefences) => { const newDefencesDetails = defenceDetails.map((defencesDetail) => { - defencesDetail.isActive = activeDefences.includes(defencesDetail.type); + defencesDetail.isActive = activeDefences.includes(defencesDetail.id); return defencesDetail; }); setDefenceDetails(newDefencesDetails); @@ -39,9 +30,7 @@ function DefenceBox({ triggeredDefences }: { triggeredDefences: string[] }) { console.log("updating triggered defences: ", triggeredDefences); // update state const newDefences = defenceDetails.map((defenceDetail) => { - defenceDetail.isTriggered = triggeredDefences.includes( - defenceDetail.type - ); + defenceDetail.isTriggered = triggeredDefences.includes(defenceDetail.id); return defenceDetail; }); setDefenceDetails(newDefences); @@ -52,7 +41,7 @@ function DefenceBox({ triggeredDefences }: { triggeredDefences: string[] }) { activateDefence(defenceType).then(() => { // update state const newDefenceDetails = defenceDetails.map((defenceDetail) => { - if (defenceDetail.type === defenceType) { + if (defenceDetail.id === defenceType) { defenceDetail.isActive = true; defenceDetail.isTriggered = false; } @@ -66,7 +55,7 @@ function DefenceBox({ triggeredDefences }: { triggeredDefences: string[] }) { deactivateDefence(defenceType).then(() => { // update state const newDefenceDetails = defenceDetails.map((defenceDetail) => { - if (defenceDetail.type === defenceType) { + if (defenceDetail.id === defenceType) { defenceDetail.isActive = false; defenceDetail.isTriggered = false; } diff --git a/frontend/src/components/DefenceBox/DefenceMechanism.tsx b/frontend/src/components/DefenceBox/DefenceMechanism.tsx index da31dcde2..a09c44ccd 100644 --- a/frontend/src/components/DefenceBox/DefenceMechanism.tsx +++ b/frontend/src/components/DefenceBox/DefenceMechanism.tsx @@ -1,5 +1,5 @@ -import React, { useState } from "react"; -import { OpenAIDefence } from "../../service/defenceService"; +import { useState } from "react"; +import { DefenceInfo } from "../../models/defence"; import "./DefenceMechanism.css"; const ANIMATION_FLASH_TIME_SECONDS = 1; @@ -12,7 +12,7 @@ function DefenceMechanism({ setDefenceInactive, }: { key: number; - defenceDetail: OpenAIDefence; + defenceDetail: DefenceInfo; setDefenceActive: (defenceId: string) => void; setDefenceInactive: (defenceId: string) => void; }) { @@ -50,8 +50,8 @@ function DefenceMechanism({ } onClick={() => { defenceDetail.isActive - ? setDefenceInactive(defenceDetail.type) - : setDefenceActive(defenceDetail.type); + ? setDefenceInactive(defenceDetail.id) + : setDefenceActive(defenceDetail.id); }} >
diff --git a/frontend/src/components/EmailBox/EmailBox.tsx b/frontend/src/components/EmailBox/EmailBox.tsx index 92b96f1aa..cebf54891 100644 --- a/frontend/src/components/EmailBox/EmailBox.tsx +++ b/frontend/src/components/EmailBox/EmailBox.tsx @@ -1,9 +1,8 @@ -import React from "react"; -import SentEmail from "./SentEmail"; import "./EmailBox.css"; -import { OpenAIEmail } from "../../service/emailService"; +import SentEmail from "./SentEmail"; +import { EmailInfo } from "../../models/email"; -function EmailBox({ emails }: { emails: OpenAIEmail[] }) { +function EmailBox({ emails }: { emails: EmailInfo[] }) { return (
{[...emails].reverse().map((email, index) => { diff --git a/frontend/src/components/EmailBox/SentEmail.tsx b/frontend/src/components/EmailBox/SentEmail.tsx index 4bc619e4a..26af4e0d6 100644 --- a/frontend/src/components/EmailBox/SentEmail.tsx +++ b/frontend/src/components/EmailBox/SentEmail.tsx @@ -1,12 +1,11 @@ -import React from "react"; -import { OpenAIEmail } from "../../service/emailService"; import "./SentEmail.css"; +import { EmailInfo } from "../../models/email"; function SentEmail({ emailDetails, key, }: { - emailDetails: OpenAIEmail; + emailDetails: EmailInfo; key: number; }) { return ( diff --git a/frontend/src/components/ModelSelectionBox/ModelSelectionBox.css b/frontend/src/components/ModelSelectionBox/ModelSelectionBox.css index 10ad56174..43cbda3d2 100644 --- a/frontend/src/components/ModelSelectionBox/ModelSelectionBox.css +++ b/frontend/src/components/ModelSelectionBox/ModelSelectionBox.css @@ -16,6 +16,7 @@ #model-selection-info p { margin: 0; padding: 0; + color:cadetblue } #model-selection-row p { @@ -24,7 +25,7 @@ } #model-selection-menu { - width: 50%; + width: 60%; padding: 5px; margin-right: 10px; } diff --git a/frontend/src/components/ModelSelectionBox/ModelSelectionBox.tsx b/frontend/src/components/ModelSelectionBox/ModelSelectionBox.tsx index ef338bf4c..930b46e4a 100644 --- a/frontend/src/components/ModelSelectionBox/ModelSelectionBox.tsx +++ b/frontend/src/components/ModelSelectionBox/ModelSelectionBox.tsx @@ -2,26 +2,21 @@ import React, { useEffect, useState } from "react"; import "./ModelSelectionBox.css"; import { setGptModel, getGptModel } from "../../service/openaiService"; +import { MODELS } from "../../Models"; // return a drop down menu with the models function ModelSelectionBox() { - const modelList = [ - "gpt-4", - "gpt-4-0613", - "gpt-4-32k", - "gpt-4-32k-0613", - "gpt-3.5-turbo", - "gpt-3.5-turbo-0613", - "gpt-3.5-turbo-16k", - "gpt-3.5-turbo-16k-0613", - ]; - + // model currently selected in the dropdown const [selectedModel, setSelectedModel] = useState("gpt-4"); + // model in use by the app + const [modelInUse, setModelInUse] = useState("gpt-4"); // handle button click to log the selected model const submitSelectedModel = async () => { console.log("selected model: " + selectedModel); - await setGptModel(selectedModel); + if (await setGptModel(selectedModel)) { + setModelInUse(selectedModel); + } }; // get the model @@ -29,8 +24,7 @@ function ModelSelectionBox() { const getModel = async () => { const model = await getGptModel(); if (model) { - console.log("Previous set model: " + model); - setSelectedModel(model); + setModelInUse(model); } }; getModel(); @@ -40,14 +34,14 @@ function ModelSelectionBox() { return (
-

Select a model: {selectedModel}

+

Select a model: