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

741 transformed messages not showing correctly #800

Merged
merged 30 commits into from
Feb 5, 2024
Merged
Show file tree
Hide file tree
Changes from 28 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
46c6f33
combines two separate bits of logic for winning level in processChatR…
pmarsh-scottlogic Jan 15, 2024
ac5130c
renames increamentNumCompletedLevels to updateNumCompletedLevels
pmarsh-scottlogic Jan 15, 2024
59cba81
renames ChatHistoryMessage to ChatMessageDTO
pmarsh-scottlogic Jan 15, 2024
293bd8d
refactors getChatHistory
pmarsh-scottlogic Jan 15, 2024
2c34238
further refactors getChatHistory to remove immutability
pmarsh-scottlogic Jan 15, 2024
a47bcab
refactors makeChatMessageFromDTO
pmarsh-scottlogic Jan 15, 2024
d663d97
removed some outdated comments
pmarsh-scottlogic Jan 17, 2024
8dd475f
adds reminder comment and transformedMessage as propery to chatHistor…
pmarsh-scottlogic Jan 17, 2024
7b34914
Merge branch 'dev' into 741-transformed-messages-not-showing-correctly
pmarsh-scottlogic Jan 23, 2024
a2a9aca
sets transformed message inn chat history
pmarsh-scottlogic Jan 23, 2024
0f2ff35
adds ability to retrieve transformed message from the dto
pmarsh-scottlogic Jan 23, 2024
b6116fa
add the transformed info message in backend rather than frontend
pmarsh-scottlogic Jan 25, 2024
edc5d42
Merge branch 'dev' into 741-transformed-messages-not-showing-correctly
pmarsh-scottlogic Jan 25, 2024
816f394
adds the transformedMessageInfo to the chat response so it can be sho…
pmarsh-scottlogic Jan 25, 2024
0a9dacf
combine message transformation objects into one object
pmarsh-scottlogic Jan 25, 2024
527cde6
tidies random sequence transformation test
pmarsh-scottlogic Jan 25, 2024
b506fc6
finalise random sequence transformation test
pmarsh-scottlogic Jan 25, 2024
2feaa6a
tidy up xml tagging transformation test
pmarsh-scottlogic Jan 25, 2024
8304931
tidy up xml tagging transformation test with escaping
pmarsh-scottlogic Jan 25, 2024
1452f13
removes unnecessary test and reorders
pmarsh-scottlogic Jan 25, 2024
2aa5512
moves no transformation into transformation test block and removes un…
pmarsh-scottlogic Jan 25, 2024
53b471f
moves transform message tests into separate test file
pmarsh-scottlogic Jan 25, 2024
10d5304
remove isTriggered from defence object
pmarsh-scottlogic Jan 25, 2024
f22ffda
complete message transformation test
pmarsh-scottlogic Jan 25, 2024
21ed43c
use undefined instead of null for transofrmed messages
pmarsh-scottlogic Jan 31, 2024
4fa8eb0
updates test
pmarsh-scottlogic Jan 31, 2024
f4cd714
Merge branch 'dev' into 741-transformed-messages-not-showing-correctly
pmarsh-scottlogic Jan 31, 2024
1d77ddc
removes isTriggered from test to make it pass
pmarsh-scottlogic Jan 31, 2024
8d620f5
implements undefined tricks
pmarsh-scottlogic Feb 2, 2024
535ba0d
Merge branch 'dev' into 741-transformed-messages-not-showing-correctly
pmarsh-scottlogic Feb 5, 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
35 changes: 16 additions & 19 deletions backend/src/controller/chatController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { Response } from 'express';
import {
transformMessage,
detectTriggeredInputDefences,
combineTransformedMessage,
detectTriggeredOutputDefences,
} from '@src/defence';
import { OpenAiAddHistoryRequest } from '@src/models/api/OpenAiAddHistoryRequest';
Expand All @@ -17,6 +16,7 @@ import {
ChatHttpResponse,
ChatModel,
LevelHandlerResponse,
MessageTransformation,
defaultChatModel,
} from '@src/models/chat';
import { Defence } from '@src/models/defence';
Expand Down Expand Up @@ -46,28 +46,30 @@ function combineChatDefenceReports(

function createNewUserMessages(
message: string,
transformedMessage: string | null
messageTransformation: MessageTransformation | undefined
pmarsh-scottlogic marked this conversation as resolved.
Show resolved Hide resolved
): ChatHistoryMessage[] {
if (transformedMessage) {
// if message has been transformed
if (messageTransformation) {
return [
// original message
{
completion: null,
chatMessageType: CHAT_MESSAGE_TYPE.USER,
infoMessage: message,
},
// transformed message
{
completion: null,
chatMessageType: CHAT_MESSAGE_TYPE.INFO,
infoMessage: messageTransformation.transformedMessageInfo,
},
{
completion: {
role: 'user',
content: transformedMessage,
content: messageTransformation.transformedMessageCombined,
},
chatMessageType: CHAT_MESSAGE_TYPE.USER_TRANSFORMED,
transformedMessage: messageTransformation.transformedMessage,
pmarsh-scottlogic marked this conversation as resolved.
Show resolved Hide resolved
},
];
} else {
// not transformed, so just return the original message
return [
{
completion: {
Expand All @@ -88,7 +90,7 @@ async function handleChatWithoutDefenceDetection(
chatHistory: ChatHistoryMessage[],
defences: Defence[]
): Promise<LevelHandlerResponse> {
const updatedChatHistory = createNewUserMessages(message, null).reduce(
const updatedChatHistory = createNewUserMessages(message, undefined).reduce(
pmarsh-scottlogic marked this conversation as resolved.
Show resolved Hide resolved
pushMessageToHistory,
chatHistory
);
Expand Down Expand Up @@ -123,28 +125,22 @@ async function handleChatWithDefenceDetection(
chatHistory: ChatHistoryMessage[],
defences: Defence[]
): Promise<LevelHandlerResponse> {
// transform the message according to active defences
const transformedMessage = transformMessage(message, defences);
const transformedMessageCombined = transformedMessage
? combineTransformedMessage(transformedMessage)
: null;
const messageTransformation = transformMessage(message, defences);
const chatHistoryWithNewUserMessages = createNewUserMessages(
message,
transformedMessageCombined ?? null
messageTransformation
).reduce(pushMessageToHistory, chatHistory);

// detect defences on input message
const triggeredInputDefencesPromise = detectTriggeredInputDefences(
message,
defences
);

// get the chatGPT reply
const openAiReplyPromise = chatGptSendMessage(
chatHistoryWithNewUserMessages,
defences,
chatModel,
transformedMessageCombined ?? message,
messageTransformation?.transformedMessageCombined ?? message,
currentLevel
);

Expand Down Expand Up @@ -178,10 +174,11 @@ async function handleChatWithDefenceDetection(
defenceReport: combinedDefenceReport,
openAIErrorMessage: openAiReply.chatResponse.openAIErrorMessage,
reply: !combinedDefenceReport.isBlocked && botReply ? botReply : '',
transformedMessage: transformedMessage ?? undefined,
transformedMessage: messageTransformation?.transformedMessage,
wonLevel:
openAiReply.chatResponse.wonLevel && !combinedDefenceReport.isBlocked,
sentEmails: combinedDefenceReport.isBlocked ? [] : openAiReply.sentEmails,
transformedMessageInfo: messageTransformation?.transformedMessageInfo,
};
return {
chatResponse: updatedChatResponse,
Expand Down
1 change: 0 additions & 1 deletion backend/src/defaultDefences.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ function createDefence(id: DEFENCE_ID, config: DefenceConfigItem[]): Defence {
id,
config,
isActive: false,
isTriggered: false,
};
}

Expand Down
23 changes: 16 additions & 7 deletions backend/src/defence.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { defaultDefences } from './defaultDefences';
import { queryPromptEvaluationModel } from './langchain';
import {
ChatDefenceReport,
MessageTransformation,
SingleDefenceReport,
TransformedChatMessage,
} from './models/chat';
Expand Down Expand Up @@ -255,26 +256,34 @@ function combineTransformedMessage(transformedMessage: TransformedChatMessage) {
function transformMessage(
message: string,
defences: Defence[]
): TransformedChatMessage | null {
): MessageTransformation | undefined {
const transformedMessage = isDefenceActive(DEFENCE_ID.XML_TAGGING, defences)
? transformXmlTagging(message, defences)
: isDefenceActive(DEFENCE_ID.RANDOM_SEQUENCE_ENCLOSURE, defences)
? transformRandomSequenceEnclosure(message, defences)
: isDefenceActive(DEFENCE_ID.INSTRUCTION, defences)
? transformInstructionDefence(message, defences)
: null;
: undefined;

if (!transformedMessage) {
console.debug('No defences applied. Message unchanged.');
return null;
return undefined;
pmarsh-scottlogic marked this conversation as resolved.
Show resolved Hide resolved
}

const transformedMessageCombined =
combineTransformedMessage(transformedMessage);

const transformedMessageInfo =
`${transformedMessage.transformationName} enabled, your message has been transformed`.toLocaleLowerCase();

console.debug(
`Defences applied. Transformed message: ${combineTransformedMessage(
transformedMessage
)}`
`Defences applied. Transformed message: ${transformedMessageCombined}`
);
return transformedMessage;
return {
transformedMessage,
transformedMessageCombined,
transformedMessageInfo,
};
}

// detects triggered defences in original message and blocks the message if necessary
Expand Down
9 changes: 9 additions & 0 deletions backend/src/models/chat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,12 @@ interface TransformedChatMessage {
transformationName: string;
}

interface MessageTransformation {
transformedMessage: TransformedChatMessage;
transformedMessageInfo: string;
transformedMessageCombined: string;
}

interface ChatHttpResponse {
reply: string;
defenceReport: ChatDefenceReport;
Expand All @@ -112,6 +118,7 @@ interface ChatHttpResponse {
isError: boolean;
openAIErrorMessage: string | null;
sentEmails: EmailInfo[];
transformedMessageInfo?: string;
}

interface LevelHandlerResponse {
Expand All @@ -123,6 +130,7 @@ interface ChatHistoryMessage {
completion: ChatCompletionMessageParam | null;
chatMessageType: CHAT_MESSAGE_TYPE;
infoMessage?: string | null;
transformedMessage?: TransformedChatMessage;
}

// default settings for chat model
Expand All @@ -148,6 +156,7 @@ export type {
TransformedChatMessage,
FunctionCallResponse,
ToolCallResponse,
MessageTransformation,
};
export {
CHAT_MODELS,
Expand Down
1 change: 0 additions & 1 deletion backend/src/models/defence.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ type Defence = {
id: DEFENCE_ID;
config: DefenceConfigItem[];
isActive: boolean;
isTriggered: boolean;
};

export { DEFENCE_ID };
Expand Down
113 changes: 112 additions & 1 deletion backend/test/unit/controller/chatController.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
handleClearChatHistory,
handleGetChatHistory,
} from '@src/controller/chatController';
import { detectTriggeredInputDefences } from '@src/defence';
import { detectTriggeredInputDefences, transformMessage } from '@src/defence';
import { OpenAiAddHistoryRequest } from '@src/models/api/OpenAiAddHistoryRequest';
import { OpenAiChatRequest } from '@src/models/api/OpenAiChatRequest';
import { OpenAiClearRequest } from '@src/models/api/OpenAiClearRequest';
Expand All @@ -18,6 +18,7 @@ import {
ChatHistoryMessage,
ChatModel,
ChatResponse,
MessageTransformation,
} from '@src/models/chat';
import { DEFENCE_ID, Defence } from '@src/models/defence';
import { EmailInfo } from '@src/models/email';
Expand Down Expand Up @@ -54,6 +55,9 @@ const mockDetectTriggeredDefences =
detectTriggeredInputDefences as jest.MockedFunction<
typeof detectTriggeredInputDefences
>;
const mockTransformMessage = transformMessage as jest.MockedFunction<
typeof transformMessage
>;

function responseMock() {
return {
Expand Down Expand Up @@ -542,6 +546,113 @@ describe('handleChatToGPT unit tests', () => {
];
expect(history).toEqual(expectedHistory);
});

test('Given sandbox AND message transformation defence active WHEN message sent THEN send reply AND session chat history is updated', async () => {
const transformedMessage = {
preMessage: '[pre message] ',
message: 'hello bot',
postMessage: '[post message]',
transformationName: 'one of the transformation defences',
};
const newTransformationChatHistoryMessages = [
{
completion: null,
chatMessageType: CHAT_MESSAGE_TYPE.USER,
infoMessage: 'hello bot',
},
{
completion: null,
chatMessageType: CHAT_MESSAGE_TYPE.INFO,
infoMessage: 'your message has been transformed by a defence',
},
{
completion: {
role: 'user',
content: '[pre message] hello bot [post message]',
},
chatMessageType: CHAT_MESSAGE_TYPE.USER_TRANSFORMED,
transformedMessage,
},
] as ChatHistoryMessage[];

const newBotChatHistoryMessage = {
chatMessageType: CHAT_MESSAGE_TYPE.BOT,
completion: {
role: 'assistant',
content: 'hello user',
},
} as ChatHistoryMessage;

const req = openAiChatRequestMock(
'hello bot',
LEVEL_NAMES.SANDBOX,
existingHistory
);
const res = responseMock();

mockChatGptSendMessage.mockResolvedValueOnce({
chatResponse: {
completion: { content: 'hello user', role: 'assistant' },
wonLevel: true,
openAIErrorMessage: null,
},
chatHistory: [
...existingHistory,
...newTransformationChatHistoryMessages,
],
sentEmails: [] as EmailInfo[],
});

mockTransformMessage.mockReturnValueOnce({
transformedMessage,
transformedMessageCombined: '[pre message] hello bot [post message]',
transformedMessageInfo:
'your message has been transformed by a defence',
} as MessageTransformation);

mockDetectTriggeredDefences.mockResolvedValueOnce({
blockedReason: null,
isBlocked: false,
alertedDefences: [],
triggeredDefences: [], // do these get updated when the message is transformed?
} as ChatDefenceReport);

await handleChatToGPT(req, res);

expect(mockChatGptSendMessage).toHaveBeenCalledWith(
[...existingHistory, ...newTransformationChatHistoryMessages],
[],
mockChatModel,
'[pre message] hello bot [post message]',
LEVEL_NAMES.SANDBOX
);

expect(res.send).toHaveBeenCalledWith({
reply: 'hello user',
defenceReport: {
blockedReason: '',
isBlocked: false,
alertedDefences: [],
triggeredDefences: [],
},
wonLevel: true,
isError: false,
sentEmails: [],
openAIErrorMessage: null,
transformedMessage,
transformedMessageInfo:
'your message has been transformed by a defence',
});

const history =
req.session.levelState[LEVEL_NAMES.SANDBOX.valueOf()].chatHistory;
const expectedHistory = [
...existingHistory,
...newTransformationChatHistoryMessages,
newBotChatHistoryMessage,
];
expect(history).toEqual(expectedHistory);
});
});
});

Expand Down
Loading
Loading