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

108 phase 0 win condition #138

Merged
merged 14 commits into from
Aug 15, 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
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