Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

98 phase switching #120

Merged
merged 5 commits into from
Aug 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions backend/src/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@ const router = require("./router");
const dotenv = require("dotenv");
const cors = require("cors");
const session = require("express-session");
const { initOpenAi, setOpenAiApiKey } = require("./openai");
const { initQAModel, initPromptEvaluationModel } = require("./langchain");
const { setOpenAiApiKey } = require("./openai");
const { getInitialDefences } = require("./defence");

dotenv.config();
Expand Down Expand Up @@ -61,6 +60,9 @@ app.use(function (req, res, next) {
if (!req.session.gptModel) {
req.session.gptModel = defaultModel;
}
if (!req.session.numPhasesCompleted) {
req.session.numPhasesCompleted = 0;
}
next();
});

Expand Down
25 changes: 23 additions & 2 deletions backend/src/router.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,11 @@ router.post("/openai/chat", async (req, res, next) => {
let transformedMessage = "";
// parse out the message
const message = req.body?.message;
const currentPhase = req.body?.currentPhase;

// TODO remove, this is just for demonstration purposes
console.log("Current phase " + currentPhase);

if (message) {
transformedMessage = message;
// see if this message triggers any defences
Expand Down Expand Up @@ -120,8 +125,21 @@ router.post("/openai/chat", async (req, res, next) => {
reply = "Missing message";
console.error(reply);
}

// TODO remove, this is just for demonstration purposes
let numPhasesCompleted = req.session.numPhasesCompleted;
if (numPhasesCompleted < 3 && currentPhase === numPhasesCompleted) {
numPhasesCompleted = currentPhase + 1;
req.session.numPhasesCompleted = numPhasesCompleted;
}

// construct response
const response = { reply, defenceInfo, transformedMessage };
const response = {
reply,
defenceInfo,
transformedMessage,
numPhasesCompleted,
};
// log and send the reply with defence info
console.log(response);
res.send(response);
Expand Down Expand Up @@ -168,5 +186,8 @@ router.get("/openai/model", (req, res, next) => {
res.send(req.session.gptModel);
});

// Importing the router
router.get("/phase/completed", (req, res, next) => {
res.send(req.session.numPhasesCompleted.toString());
});

module.exports = router;
22 changes: 21 additions & 1 deletion frontend/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,29 @@
import { useState } from "react";

import { useEffect } from "react";
import "./App.css";
import AttackBox from "./components/AttackBox/AttackBox";
import ChatBox from "./components/ChatBox/ChatBox";
import DefenceBox from "./components/DefenceBox/DefenceBox";
import EmailBox from "./components/EmailBox/EmailBox";
import ApiKeyBox from "./components/ApiKeyBox/ApiKeyBox";
import PhaseSelectionBox from "./components/PhaseSelectionBox/PhaseSelectionBox";
import Header from "./components/Header";
import ModelSelectionBox from "./components/ModelSelectionBox/ModelSelectionBox";
import { EmailInfo } from "./models/email";
import { CHAT_MESSAGE_TYPE, ChatMessage } from "./models/chat";
import { DefenceInfo } from "./models/defence";
import { getCompletedPhases } from "./service/phaseService";

function App() {
const [defenceBoxKey, setDefenceBoxKey] = useState<number>(0);
const [emails, setEmails] = useState<EmailInfo[]>([]);
const [messages, setMessages] = useState<ChatMessage[]>([]);
const [triggeredDefences, setTriggeredDefences] = useState<string[]>([]);

// start on sandbox mode
const [currentPhase, setCurrentPhase] = useState<number>(0);
const [numCompletedPhases, setNumCompletedPhases] = useState<number>(0);

const updateTriggeredDefences = (defenceDetails: string[]) => {
// set the new triggered defences
setTriggeredDefences(defenceDetails);
Expand Down Expand Up @@ -50,6 +56,12 @@ function App() {
const infoMessage = `${defenceInfo.name} defence deactivated`;
addInfoMessage(infoMessage.toLowerCase());
};
// called on mount
useEffect(() => {
getCompletedPhases().then((numCompletedPhases) => {
setNumCompletedPhases(numCompletedPhases);
});
}, []);

return (
<span id="main-area">
Expand All @@ -73,13 +85,21 @@ function App() {
<Header />
<ChatBox
messages={messages}
currentPhase={currentPhase}
setNumCompletedPhases={setNumCompletedPhases}
setEmails={setEmails}
updateTriggeredDefences={updateTriggeredDefences}
addChatMessage={addChatMessage}
clearMessages={clearMessages}
/>
</div>
<div className="side-bar">
<div className="side-bar-header">phases</div>
<PhaseSelectionBox
currentPhase={currentPhase}
numCompletedPhases={numCompletedPhases}
setCurrentPhase={setCurrentPhase}
/>
<div className="side-bar-header">sent emails</div>
<EmailBox emails={emails} />
</div>
Expand Down
34 changes: 34 additions & 0 deletions frontend/src/Phases.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { Phase, PHASE_NAMES } from "./models/phase";

const PHASES: Phase[] = [
{
id: PHASE_NAMES.PHASE_0,
name: "phase 0",
preamble: "preamble",
isComplete: false,
isCurrent: false,
},
{
id: PHASE_NAMES.PHASE_1,
name: "phase 1",
preamble: "preamble",
isComplete: false,
isCurrent: false,
},
{
id: PHASE_NAMES.PHASE_2,
name: "phase 2",
preamble: "preamble",
isComplete: false,
isCurrent: false,
},
{
id: PHASE_NAMES.SANDBOX,
name: "sandbox",
preamble: "preamble",
isComplete: false,
isCurrent: false,
},
];

export { PHASES };
7 changes: 6 additions & 1 deletion frontend/src/components/ChatBox/ChatBox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,16 @@ function ChatBox(
this: any,
{
messages,
currentPhase,
setNumCompletedPhases,
setEmails,
updateTriggeredDefences,
addChatMessage,
clearMessages,
}: {
messages: ChatMessage[];
currentPhase: number;
setNumCompletedPhases: (numCompletedPhases: number) => void;
setEmails: (emails: EmailInfo[]) => void;
updateTriggeredDefences: (defences: string[]) => void;
addChatMessage: (message: ChatMessage) => void;
Expand Down Expand Up @@ -60,7 +64,8 @@ function ChatBox(
// clear the input
event.currentTarget.value = "";

const response: ChatResponse = await sendMessage(message);
const response: ChatResponse = await sendMessage(message, currentPhase);
setNumCompletedPhases(response.numPhasesCompleted);
const transformedMessage = response.transformedMessage;
const isTransformed = transformedMessage !== message;
// add the transformed message to the chat box if it is different from the original message
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/EmailBox/EmailBox.css
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
flex-grow: 1;
display: flex;
flex-direction: column;
height: 100%;
height: 80%;
}

#email-box-feed {
Expand Down
17 changes: 17 additions & 0 deletions frontend/src/components/PhaseSelectionBox/PhaseSelectionBox.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#phase-selection-box {
padding: 16px 32px;
position: relative;
display: flex;
justify-content: center;
align-items: center;
}

.phase-selection-button {
margin: 5px;
padding: 5px 5px;
}

.selected {
color: white;
background-color: rgb(0, 153, 255);
}
48 changes: 48 additions & 0 deletions frontend/src/components/PhaseSelectionBox/PhaseSelectionBox.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { useState, useEffect } from "react";
import { Phase } from "../../models/phase";
import { PHASES } from "../../Phases";

import "./PhaseSelectionBox.css";
import { getCompletedPhases } from "../../service/phaseService";

function PhaseSelectionBox(
this: any,
{
currentPhase,
numCompletedPhases,
setCurrentPhase,
}: {
currentPhase: number;
numCompletedPhases: number;
setCurrentPhase: (newPhase: number) => void;
}
) {
const handlePhaseChange = async (index: number) => {
if (index !== currentPhase) {
const newPhase = index;
console.log(`Changing phase to ${newPhase}`);
setCurrentPhase(newPhase);
}
};

return (
<div id="phase-selection-box">
{PHASES.map((phase: Phase, index: number) => {
return (
<button
className={`phase-selection-button ${
index === currentPhase ? "selected" : ""
}`}
key={phase.name}
onClick={() => handlePhaseChange(index)}
disabled={index !== numCompletedPhases && index !== 3}
>
{phase.name}
</button>
);
})}
</div>
);
}

export default PhaseSelectionBox;
1 change: 1 addition & 0 deletions frontend/src/models/chat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ interface ChatMessage {
interface ChatResponse {
reply: string;
defenceInfo: ChatDefenceReport;
numPhasesCompleted: number;
transformedMessage: string;
}

Expand Down
17 changes: 17 additions & 0 deletions frontend/src/models/phase.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
enum PHASE_NAMES {
PHASE_0 = "phase 0",
PHASE_1 = "phase 1",
PHASE_2 = "phase 2",
SANDBOX = "sandbox",
}

interface Phase {
id: PHASE_NAMES;
name: string;
preamble: string;
isCurrent: boolean;
isComplete: boolean;
}

export { PHASE_NAMES };
export type { Phase };
7 changes: 5 additions & 2 deletions frontend/src/service/chatService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,15 @@ const clearChat = async (): Promise<boolean> => {
return response.status === 200;
};

const sendMessage = async (message: string): Promise<ChatResponse> => {
const sendMessage = async (
message: string,
currentPhase: number
): Promise<ChatResponse> => {
const response = await sendRequest(
PATH + "chat",
"POST",
{ "Content-Type": "application/json" },
JSON.stringify({ message })
JSON.stringify({ message, currentPhase })
);
const data = await response.json();
console.log(data);
Expand Down
11 changes: 11 additions & 0 deletions frontend/src/service/phaseService.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { sendRequest } from "./backendService";

const PATH = "phase/";

async function getCompletedPhases(): Promise<number> {
const response = await sendRequest(PATH + "completed", "GET");
const numCompletedPhases: number = await response.json();
return numCompletedPhases;
}

export { getCompletedPhases };