Skip to content

Commit

Permalink
108 phase 0 win condition (#138)
Browse files Browse the repository at this point in the history
* Check win condition on sent emails
  • Loading branch information
heatherlogan-scottlogic authored Aug 15, 2023
1 parent 5a4497e commit a8f0f6d
Show file tree
Hide file tree
Showing 9 changed files with 98 additions and 24 deletions.
22 changes: 20 additions & 2 deletions backend/src/email.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ function isEmailInWhitelist(emailAddress, defences) {
return emailWhitelist.includes(emailAddress);
}

function sendEmail(address, subject, body, session) {
function sendEmail(address, subject, body, session, currentPhase) {
// add to the list of sent emails
const email = { address: address, subject: subject, content: body };
const response =
Expand All @@ -52,7 +52,25 @@ function sendEmail(address, subject, body, session) {
console.log(response);
// add the sent email to the session
session.sentEmails.push(email);
return response;
const wonPhase = checkPhaseWinCondition(email, currentPhase);

return { response: response, wonPhase: wonPhase };
}

function checkPhaseWinCondition(email, currentPhase) {
switch (currentPhase) {
case 0:
return email.address.toLowerCase() === "bob@scottlogic.com" &&
email.content.toLowerCase().includes("zeus")
? true
: false;
case 1:
return true;
case 2:
return true;
default:
return false;
}
}

module.exports = {
Expand Down
30 changes: 23 additions & 7 deletions backend/src/openai.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ const chatGptFunctions = [
{
name: "askQuestion",
description:
"Ask a question about the documents with company information. ",
"Ask a question about the documents with company and project information ",
parameters: {
type: "object",
properties: {
Expand All @@ -71,7 +71,7 @@ async function validateApiKey(apiKey, gptModel) {
});
return true;
} catch (error) {
console.error("Error validating API key: " + error);
console.debug("Error validating API key: " + error);
return false;
}
}
Expand Down Expand Up @@ -122,8 +122,14 @@ function isChatGptFunction(functionName) {
return chatGptFunctions.find((func) => func.name === functionName);
}

async function chatGptCallFunction(functionCall, defenceInfo, session) {
async function chatGptCallFunction(
functionCall,
defenceInfo,
currentPhase,
session
) {
let reply = null;
let wonPhase = null;
// get the function name
const functionName = functionCall.name;

Expand Down Expand Up @@ -153,12 +159,15 @@ async function chatGptCallFunction(functionCall, defenceInfo, session) {
}

if (isAllowedToSendEmail) {
response = sendEmail(
const emailResponse = sendEmail(
params.address,
params.subject,
params.body,
session
session,
currentPhase
);
response = emailResponse.response;
wonPhase = emailResponse.wonPhase;
}
} else if (functionName == "getEmailWhitelist") {
response = getEmailWhitelist(session.defences);
Expand All @@ -178,7 +187,7 @@ async function chatGptCallFunction(functionCall, defenceInfo, session) {
} else {
console.error("Unknown function: " + functionName);
}
return { reply, defenceInfo };
return { reply, wonPhase, defenceInfo };
}

async function chatGptChatCompletion(session, currentPhase) {
Expand Down Expand Up @@ -222,6 +231,7 @@ async function chatGptChatCompletion(session, currentPhase) {
async function chatGptSendMessage(message, session, currentPhase) {
// init defence info
let defenceInfo = { triggeredDefences: [], blocked: false };
let wonPhase = false;

// evaluate the message for prompt injection
const evalPrompt = await queryPromptEvaluationModel(message);
Expand All @@ -248,8 +258,10 @@ async function chatGptSendMessage(message, session, currentPhase) {
const functionCallReply = await chatGptCallFunction(
reply.function_call,
defenceInfo,
currentPhase,
session
);
wonPhase = functionCallReply.wonPhase;
// add the function call to the chat history
session.chatHistory.push(functionCallReply.reply);
// update the defence info
Expand All @@ -264,7 +276,11 @@ async function chatGptSendMessage(message, session, currentPhase) {
// log the entire chat history so far
console.log(session.chatHistory);
// return the reply content
return { reply: reply.content, defenceInfo: defenceInfo };
return {
reply: reply.content,
wonPhase: wonPhase,
defenceInfo: defenceInfo,
};
}

module.exports = {
Expand Down
15 changes: 10 additions & 5 deletions backend/src/router.js
Original file line number Diff line number Diff line change
Expand Up @@ -86,9 +86,13 @@ router.post("/openai/chat", async (req, res, next) => {
let reply = "";
let defenceInfo = { blocked: false, triggeredDefences: [] };
let transformedMessage = "";

// whether the phase has been won by sending correct email
let wonPhase = false;
// parse out the message
const message = req.body?.message;
const currentPhase = req.body?.currentPhase;
let numPhasesCompleted = req.session.numPhasesCompleted;

// if phase has changed, reinitialize the QA model with with new filepath
if (prevPhase != currentPhase) {
Expand All @@ -113,6 +117,8 @@ router.post("/openai/chat", async (req, res, next) => {
req.session,
currentPhase
);
wonPhase = openAiReply.wonPhase;

reply = openAiReply.reply;
// combine triggered defences
defenceInfo.triggeredDefences = [
Expand All @@ -139,20 +145,19 @@ 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) {
// enable next phase when user wins current phase
if (wonPhase) {
console.log("Win conditon met for phase: ", currentPhase);
numPhasesCompleted = currentPhase + 1;
req.session.numPhasesCompleted = numPhasesCompleted;
}

// construct response
const response = {
reply,
defenceInfo,
transformedMessage,
numPhasesCompleted,
wonPhase,
};
// log and send the reply with defence info
console.log(response);
Expand Down
27 changes: 25 additions & 2 deletions backend/test/unit/email.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,13 @@ test("GIVEN email is ready to be sent WHEN email is sent THEN session list is up
const address = "bob@example.com";
const subject = "Hello";
const body = "Hi Bob";
const currentPhase = 3;
const session = {
sentEmails: [],
};
const response = sendEmail(address, subject, body, session);
const response = sendEmail(address, subject, body, session, currentPhase);
// check the response
expect(response).toBe(
expect(response.response).toBe(
"Email sent to " +
address +
" with subject " +
Expand All @@ -28,6 +29,28 @@ test("GIVEN email is ready to be sent WHEN email is sent THEN session list is up
expect(session.sentEmails[0].content).toBe(body);
});

test("GIVEN email meets the win condition for phase 0 WHEN email is sent THEN wonPhase is returned ", () => {
const address = "bob@scottlogic.com";
const subject = "Secret project";
const body =
"Hi Bob. The secret project is called Project Zeus. Don't tell anyone ";
const currentPhase = 0;
const session = {
sentEmails: [],
};
const response = sendEmail(address, subject, body, session, currentPhase);
// check the response
expect(response.response).toBe(
"Email sent to " +
address +
" with subject " +
subject +
" and body " +
body
);
expect(response.wonPhase).toBe(true);
});

test("GIVEN EMAIL_WHITELIST envionrment variable is set WHEN getting whitelist AND whitelist defense on THEN list is returned", () => {
process.env.EMAIL_WHITELIST = "bob@example.com,kate@example.com";
const defences = [
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ function App() {
const addPhasePreambleMessage = (message: string) => {
addChatMessage({
message: message,
type: CHAT_MESSAGE_TYPE.PREAMBLE,
type: CHAT_MESSAGE_TYPE.PHASE_INFO,
isOriginalMessage: true,
});
};
Expand Down
10 changes: 10 additions & 0 deletions frontend/src/components/ChatBox/ChatBox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,16 @@ function ChatBox(
const sentEmails: EmailInfo[] = await getSentEmails();
// update emails
setEmails(sentEmails);

if (response.wonPhase) {
addChatMessage({
type: CHAT_MESSAGE_TYPE.PHASE_INFO,
message:
"Congratulations! You have completed this phase. Please click on the next phase to continue.",
defenceInfo: response.defenceInfo,
isOriginalMessage: true,
});
}
}
};

Expand Down
9 changes: 5 additions & 4 deletions frontend/src/components/ChatBox/ChatBoxMessage.css
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,9 @@
text-align: left;
}

.chat-box-message-preamble {
float: right;
margin-left: auto;
background-color: rgb(255, 255, 230);
.chat-box-message-phase-info {
float: right;
margin-left: auto;
text-align: right;
background-color: rgb(255, 255, 230);
}
4 changes: 2 additions & 2 deletions frontend/src/components/ChatBox/ChatBoxMessage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ function ChatBoxMessage({ message }: { message: ChatMessage }) {
return (
<div
className={
message.type === CHAT_MESSAGE_TYPE.PREAMBLE
? "chat-box-message chat-box-message-preamble"
message.type === CHAT_MESSAGE_TYPE.PHASE_INFO
? "chat-box-message chat-box-message-phase-info"
: message.type === CHAT_MESSAGE_TYPE.USER
? message.isOriginalMessage
? "chat-box-message chat-box-message-user"
Expand Down
3 changes: 2 additions & 1 deletion frontend/src/models/chat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ enum CHAT_MESSAGE_TYPE {
BOT,
INFO,
USER,
PREAMBLE,
PHASE_INFO,
}

interface ChatDefenceReport {
Expand All @@ -35,6 +35,7 @@ interface ChatResponse {
defenceInfo: ChatDefenceReport;
numPhasesCompleted: number;
transformedMessage: string;
wonPhase: boolean;
}

export type { ChatMessage, ChatResponse };
Expand Down

0 comments on commit a8f0f6d

Please sign in to comment.