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

Misc tidying #837

Merged
merged 30 commits into from
Feb 21, 2024
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
54077bb
tidy chatGptSendMessage
pmarsh-scottlogic Feb 1, 2024
33e6512
renames to getChatCompletionsInLimitedContextWindow1~
pmarsh-scottlogic Feb 1, 2024
833132f
moves message property out of chatGptSendMessage
pmarsh-scottlogic Feb 1, 2024
40aa2dd
makes email decleration more concise
pmarsh-scottlogic Feb 1, 2024
e9cd877
updates some comments
pmarsh-scottlogic Feb 1, 2024
7e77988
more coment juggling
pmarsh-scottlogic Feb 1, 2024
74898dc
more comment sweepup
pmarsh-scottlogic Feb 1, 2024
b945b59
moves handbook files into pages
pmarsh-scottlogic Feb 1, 2024
977a1bb
moves Attacks.ts into HandbookAttacks
pmarsh-scottlogic Feb 1, 2024
7283b2c
refactors and renames queryPromptEval and fixes tests
pmarsh-scottlogic Feb 2, 2024
fb48e1b
simplify output of queryDocuments
pmarsh-scottlogic Feb 2, 2024
63a88c0
rename evaluatePrompt
pmarsh-scottlogic Feb 2, 2024
541c331
removes object wrapping around simple strings
pmarsh-scottlogic Feb 2, 2024
7cfca12
removes unused chatReponse property from ToolCallResponse
pmarsh-scottlogic Feb 2, 2024
00036bc
return initialised eval chain instead of assigning to variable first
pmarsh-scottlogic Feb 2, 2024
4874b0d
merge dev
pmarsh-scottlogic Feb 15, 2024
3a65b9d
finish merge
pmarsh-scottlogic Feb 15, 2024
45d7713
add user message console log to handle chat without defences
pmarsh-scottlogic Feb 15, 2024
426f214
stops some instance in openai.ts where things were declared to be ins…
pmarsh-scottlogic Feb 15, 2024
2503b65
improves a comment
pmarsh-scottlogic Feb 16, 2024
1901912
improve error message
pmarsh-scottlogic Feb 16, 2024
0b96563
adds link in comment to context window page
pmarsh-scottlogic Feb 19, 2024
d73c086
renames method to getChatCompletionsInContextWindow
pmarsh-scottlogic Feb 19, 2024
241d42c
replace reduce with filter and map
pmarsh-scottlogic Feb 19, 2024
dff646f
move openAI instantiation back outside do while loop
pmarsh-scottlogic Feb 19, 2024
e0c44ff
update imports to handbook pages
pmarsh-scottlogic Feb 19, 2024
496c30f
removes some more unhelpful comments
pmarsh-scottlogic Feb 19, 2024
0d36671
renames ChatDefenceReport to DefenceReport
pmarsh-scottlogic Feb 19, 2024
f189429
remove more unhelpful comments
pmarsh-scottlogic Feb 19, 2024
ef3c567
fixes typo
pmarsh-scottlogic Feb 21, 2024
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
12 changes: 5 additions & 7 deletions backend/src/controller/chatController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { OpenAiChatRequest } from '@src/models/api/OpenAiChatRequest';
import { OpenAiClearRequest } from '@src/models/api/OpenAiClearRequest';
import { OpenAiGetHistoryRequest } from '@src/models/api/OpenAiGetHistoryRequest';
import {
ChatDefenceReport,
DefenceReport,
ChatHttpResponse,
ChatModel,
LevelHandlerResponse,
Expand All @@ -20,7 +20,7 @@ import {
import {
ChatMessage,
ChatInfoMessage,
chatInfoMessageType,
chatInfoMessageTypes,
} from '@src/models/chatMessage';
import { Defence } from '@src/models/defence';
import { EmailInfo } from '@src/models/email';
Expand All @@ -33,9 +33,7 @@ import {

import { handleChatError } from './handleError';

function combineChatDefenceReports(
reports: ChatDefenceReport[]
): ChatDefenceReport {
function combineDefenceReports(reports: DefenceReport[]): DefenceReport {
return {
blockedReason: reports
.filter((report) => report.blockedReason !== null)
Expand Down Expand Up @@ -173,7 +171,7 @@ async function handleChatWithDefenceDetection(
const defenceReports = outputDefenceReport
? [inputDefenceReport, outputDefenceReport]
: [inputDefenceReport];
const combinedDefenceReport = combineChatDefenceReports(defenceReports);
const combinedDefenceReport = combineDefenceReports(defenceReports);

// if blocked, restore original chat history and add user message to chat history without completion
const updatedChatHistory = combinedDefenceReport.isBlocked
Expand Down Expand Up @@ -379,7 +377,7 @@ function handleAddInfoToChatHistory(
if (
infoMessage &&
chatMessageType &&
chatInfoMessageType.includes(chatMessageType) &&
chatInfoMessageTypes.includes(chatMessageType) &&
level !== undefined &&
level >= LEVEL_NAMES.LEVEL_1
) {
Expand Down
15 changes: 3 additions & 12 deletions backend/src/defence.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { defaultDefences } from './defaultDefences';
import { evaluatePrompt } from './langchain';
import {
ChatDefenceReport,
DefenceReport,
MessageTransformation,
SingleDefenceReport,
TransformedChatMessage,
Expand All @@ -20,14 +20,12 @@ import {
} from './promptTemplates';

function activateDefence(id: DEFENCE_ID, defences: Defence[]) {
// return the updated list of defences
return defences.map((defence) =>
defence.id === id ? { ...defence, isActive: true } : defence
);
}

function deactivateDefence(id: DEFENCE_ID, defences: Defence[]) {
// return the updated list of defences
return defences.map((defence) =>
defence.id === id ? { ...defence, isActive: false } : defence
);
Expand All @@ -38,7 +36,6 @@ function configureDefence(
defences: Defence[],
config: DefenceConfigItem[]
): Defence[] {
// return the updated list of defences
return defences.map((defence) =>
defence.id === id ? { ...defence, config } : defence
);
Expand Down Expand Up @@ -95,7 +92,6 @@ function getFilterList(defences: Defence[], type: DEFENCE_ID) {
}
function getSystemRole(
defences: Defence[],
// by default, use sandbox
currentLevel: LEVEL_NAMES = LEVEL_NAMES.SANDBOX
) {
switch (currentLevel) {
Expand Down Expand Up @@ -183,14 +179,12 @@ function escapeXml(unsafe: string) {
});
}

// function to detect any XML tags in user input
function containsXMLTags(input: string) {
const tagRegex = /<\/?[a-zA-Z][\w-]*(?:\b[^>]*\/\s*|[^>]*>|[?]>)/g;
const foundTags: string[] = input.match(tagRegex) ?? [];
return foundTags.length > 0;
}

// apply XML tagging defence to input message
function transformXmlTagging(
message: string,
defences: Defence[]
Expand All @@ -213,7 +207,6 @@ function generateRandomString(length: number) {
).join('');
}

// apply random sequence enclosure defence to input message
function transformRandomSequenceEnclosure(
message: string,
defences: Defence[]
Expand Down Expand Up @@ -250,7 +243,6 @@ function combineTransformedMessage(transformedMessage: TransformedChatMessage) {
);
}

//apply defence string transformations to original message
function transformMessage(
message: string,
defences: Defence[]
Expand Down Expand Up @@ -284,7 +276,6 @@ function transformMessage(
};
}

// detects triggered defences in original message and blocks the message if necessary
async function detectTriggeredInputDefences(
message: string,
defences: Defence[]
Expand All @@ -299,15 +290,14 @@ async function detectTriggeredInputDefences(
return combineDefenceReports(singleDefenceReports);
}

// detects triggered defences in bot output and blocks the message if necessary
function detectTriggeredOutputDefences(message: string, defences: Defence[]) {
const singleDefenceReports = [detectFilterBotOutput(message, defences)];
return combineDefenceReports(singleDefenceReports);
}

function combineDefenceReports(
defenceReports: SingleDefenceReport[]
): ChatDefenceReport {
): DefenceReport {
const isBlocked = defenceReports.some((report) => report.blockedReason);
const blockedReason = isBlocked
? defenceReports
Expand Down Expand Up @@ -451,6 +441,7 @@ async function detectEvaluationLLM(
): Promise<SingleDefenceReport> {
const defence = DEFENCE_ID.PROMPT_EVALUATION_LLM;
// to save money and processing time, and to reduce risk of rate limiting, we only run if defence is active
// this means that, contrary to the other defences, the user won't get alerts when the defence is not active, i.e. "your last prompt would have been blocked by the prompt evaluation LLM"
if (isDefenceActive(DEFENCE_ID.PROMPT_EVALUATION_LLM, defences)) {
const promptEvalLLMPrompt = getPromptEvalPromptFromConfig(defences);

Expand Down
6 changes: 3 additions & 3 deletions backend/src/models/chat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ interface ChatModelConfiguration {
presencePenalty: number;
}

interface ChatDefenceReport {
interface DefenceReport {
blockedReason: string | null;
isBlocked: boolean;
alertedDefences: DEFENCE_ID[];
Expand Down Expand Up @@ -93,7 +93,7 @@ interface MessageTransformation {

interface ChatHttpResponse {
reply: string;
defenceReport: ChatDefenceReport;
defenceReport: DefenceReport;
transformedMessage?: TransformedChatMessage;
wonLevel: boolean;
isError: boolean;
Expand All @@ -118,7 +118,7 @@ const defaultChatModel: ChatModel = {
};

export type {
ChatDefenceReport,
DefenceReport,
ChatGptReply,
ChatMalicious,
ChatModel,
Expand Down
2 changes: 1 addition & 1 deletion backend/src/models/chatMessage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,4 +67,4 @@ export type {
CHAT_INFO_MESSAGE_TYPES,
};

export { chatInfoMessageTypes as chatInfoMessageType };
export { chatInfoMessageTypes };
38 changes: 22 additions & 16 deletions backend/src/openai.ts
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,10 @@ async function handleAskQuestionFunction(
: '';
return await queryDocuments(params.question, configQAPrompt, currentLevel);
} else {
console.error('No arguments provided to askQuestion function');
console.error(
'Incorrect arguments provided to askQuestion function:',
functionCallArgs
);
return "Reply with 'I don't know what to ask'";
}
}
Expand Down Expand Up @@ -237,7 +240,8 @@ async function chatGptCallFunction(

async function chatGptChatCompletion(
chatHistory: ChatMessage[],
chatModel: ChatModel
chatModel: ChatModel,
openAI: OpenAI
) {
const updatedChatHistory = [...chatHistory];

Expand All @@ -247,13 +251,13 @@ async function chatGptChatCompletion(
console.debug('Calling OpenAI chat completion...');

try {
const chat_completion = await getOpenAI().chat.completions.create({
const chat_completion = await openAI.chat.completions.create({
model: chatModel.id,
temperature: chatModel.configuration.temperature,
top_p: chatModel.configuration.topP,
frequency_penalty: chatModel.configuration.frequencyPenalty,
presence_penalty: chatModel.configuration.presencePenalty,
messages: getChatCompletionsInLimitedContextWindow(
messages: getChatCompletionsInContextWindow(
updatedChatHistory,
chatModel.id
),
Expand Down Expand Up @@ -287,19 +291,17 @@ async function chatGptChatCompletion(
}
}

function getChatCompletionsInLimitedContextWindow(
function getChatCompletionsInContextWindow(
chatHistory: ChatMessage[],
gptModel: CHAT_MODELS
): ChatCompletionMessageParam[] {
const completions = chatHistory.reduce<ChatCompletionMessageParam[]>(
(result, chatMessage) => {
if ('completion' in chatMessage && chatMessage.completion) {
result.push(chatMessage.completion);
}
return result;
},
[]
);
const completions = chatHistory
.map((chatMessage) =>
'completion' in chatMessage ? chatMessage.completion : null
)
.filter(
(completion) => completion !== null
) as ChatCompletionMessageParam[];

console.debug(
'Number of tokens in total chat history. prompt_tokens=',
Expand Down Expand Up @@ -365,9 +367,13 @@ async function getFinalReplyAfterAllToolCalls(
let wonLevel = false;

let gptReply: ChatGptReply | null = null;

const openAI = getOpenAI();
do {
gptReply = await chatGptChatCompletion(updatedChatHistory, chatModel);
gptReply = await chatGptChatCompletion(
updatedChatHistory,
chatModel,
openAI
);
updatedChatHistory = gptReply.chatHistory;

if (gptReply.completion?.tool_calls) {
Expand Down
1 change: 1 addition & 0 deletions backend/src/utils/token.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { promptTokensEstimate, stringTokens } from 'openai-chat-tokens';
import { CHAT_MODELS } from '@src/models/chat';
import { chatGptTools } from '@src/openai';

// The size of each model's context window in muber of tokens. https://platform.openai.com/docs/models
pmarsh-scottlogic marked this conversation as resolved.
Show resolved Hide resolved
const chatModelMaxTokens = {
[CHAT_MODELS.GPT_4_TURBO]: 128000,
[CHAT_MODELS.GPT_4]: 8192,
Expand Down
10 changes: 5 additions & 5 deletions backend/test/unit/controller/chatController.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { OpenAiChatRequest } from '@src/models/api/OpenAiChatRequest';
import { OpenAiClearRequest } from '@src/models/api/OpenAiClearRequest';
import { OpenAiGetHistoryRequest } from '@src/models/api/OpenAiGetHistoryRequest';
import {
ChatDefenceReport,
DefenceReport,
ChatModel,
ChatResponse,
MessageTransformation,
Expand Down Expand Up @@ -232,15 +232,15 @@ describe('handleChatToGPT unit tests', () => {
function triggeredDefencesMockReturn(
blockedReason: string,
triggeredDefence: DEFENCE_ID
): Promise<ChatDefenceReport> {
): Promise<DefenceReport> {
return new Promise((resolve, reject) => {
try {
resolve({
blockedReason,
isBlocked: true,
alertedDefences: [],
triggeredDefences: [triggeredDefence],
} as ChatDefenceReport);
} as DefenceReport);
} catch (err) {
reject(err);
}
Expand Down Expand Up @@ -601,7 +601,7 @@ describe('handleChatToGPT unit tests', () => {
isBlocked: false,
alertedDefences: [],
triggeredDefences: [],
} as ChatDefenceReport);
} as DefenceReport);

await handleChatToGPT(req, res);

Expand Down Expand Up @@ -701,7 +701,7 @@ describe('handleChatToGPT unit tests', () => {
isBlocked: false,
alertedDefences: [],
triggeredDefences: [],
} as ChatDefenceReport);
} as DefenceReport);

await handleChatToGPT(req, res);

Expand Down
5 changes: 1 addition & 4 deletions frontend/src/components/ChatBox/ChatBox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -98,9 +98,7 @@ function ChatBox({
message: response.reply,
type: 'ERROR_MSG',
});
}
// add it to the list of messages
else if (response.defenceReport.isBlocked) {
} else if (response.defenceReport.isBlocked) {
addChatMessage({
type: 'BOT_BLOCKED',
message: response.defenceReport.blockedReason,
Expand All @@ -111,7 +109,6 @@ function ChatBox({
message: response.reply,
});
}
// add altered defences to the chat
response.defenceReport.alertedDefences.forEach((triggeredDefence) => {
// get user-friendly defence name
const defenceName = ALL_DEFENCES.find((defence) => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,9 @@
import { render, screen } from '@testing-library/react';
import { describe, expect, test } from 'vitest';

import {
ATTACKS_LEVEL_2,
ATTACKS_LEVEL_3,
ATTACKS_ALL,
} from '@src/components/HandbookOverlay/Pages/Attacks';
import { LEVEL_NAMES } from '@src/models/level';

import { ATTACKS_LEVEL_2, ATTACKS_LEVEL_3, ATTACKS_ALL } from './Attacks';
import HandbookAttacks from './HandbookAttacks';

describe('HandbookAttacks component tests', () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
import {
ATTACKS_ALL,
ATTACKS_LEVEL_2,
ATTACKS_LEVEL_3,
} from '@src/components/HandbookOverlay/Pages/Attacks';
import { AttackInfo } from '@src/models/attack';
import { LEVEL_NAMES } from '@src/models/level';

import { ATTACKS_ALL, ATTACKS_LEVEL_2, ATTACKS_LEVEL_3 } from './Attacks';

import './HandbookPage.css';

function HandbookAttacks({ currentLevel }: { currentLevel: LEVEL_NAMES }) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { render, screen } from '@testing-library/react';
import { describe, expect, test } from 'vitest';

import { GLOSSARY } from '@src/components/HandbookOverlay/Pages/Glossary';
import { LEVEL_NAMES } from '@src/models/level';

import { GLOSSARY } from './Glossary';
import HandbookGlossary from './HandbookGlossary';

describe('HandbookGlossary component tests', () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { GLOSSARY } from '@src/components/HandbookOverlay/Pages/Glossary';
import { LEVEL_NAMES } from '@src/models/level';

import { GLOSSARY } from './Glossary';

import './HandbookPage.css';

function HandbookGlossary({ currentLevel }: { currentLevel: LEVEL_NAMES }) {
Expand Down
Loading
Loading