Skip to content

Commit

Permalink
[Obs AI Assistant] Move deserializeMessage to utils (elastic#181216)
Browse files Browse the repository at this point in the history
  • Loading branch information
viduni94 committed Oct 15, 2024
1 parent 089a881 commit 40a6ae1
Show file tree
Hide file tree
Showing 3 changed files with 155 additions and 27 deletions.
29 changes: 2 additions & 27 deletions x-pack/packages/kbn-ai-assistant/src/chat/chat_body.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import {
} from '@kbn/observability-ai-assistant-plugin/public';
import type { AuthenticatedUser } from '@kbn/security-plugin/common';
import { euiThemeVars } from '@kbn/ui-theme';
import { findLastIndex, cloneDeep } from 'lodash';
import { findLastIndex } from 'lodash';
import React, { useCallback, useEffect, useRef, useState } from 'react';
import type { UseKnowledgeBaseResult } from '../hooks/use_knowledge_base';
import { ASSISTANT_SETUP_TITLE, EMPTY_CONVERSATION_TITLE, UPGRADE_LICENSE_TITLE } from '../i18n';
Expand All @@ -45,7 +45,7 @@ import { SimulatedFunctionCallingCallout } from './simulated_function_calling_ca
import { WelcomeMessage } from './welcome_message';
import { useLicense } from '../hooks/use_license';
import { PromptEditor } from '../prompt_editor/prompt_editor';
import { safeJsonParse } from '../utils/safe_json_parse';
import { deserializeMessage } from '../utils/deserialize_message';

const fullHeightClassName = css`
height: 100%;
Expand Down Expand Up @@ -227,31 +227,6 @@ export function ChatBody({
});

const handleCopyConversation = () => {
const deserializeMessage = (message: Message): Message => {
const copiedMessage = cloneDeep(message);

if (
copiedMessage.message.function_call?.arguments &&
typeof copiedMessage.message.function_call?.arguments === 'string'
) {
copiedMessage.message.function_call.arguments = safeJsonParse(
copiedMessage.message.function_call.arguments ?? '{}'
);
}

if (copiedMessage.message.name) {
if (copiedMessage.message.content && typeof copiedMessage.message.content === 'string') {
copiedMessage.message.content = safeJsonParse(copiedMessage.message.content);
}

if (copiedMessage.message.data && typeof copiedMessage.message.data === 'string') {
copiedMessage.message.data = safeJsonParse(copiedMessage.message.data);
}
}

return copiedMessage;
};

const deserializedMessages = (conversation.value?.messages ?? messages).map(deserializeMessage);

const content = JSON.stringify({
Expand Down
118 changes: 118 additions & 0 deletions x-pack/packages/kbn-ai-assistant/src/utils/deserialize_message.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import { cloneDeep } from 'lodash';
import { Message, MessageRole } from '@kbn/observability-ai-assistant-plugin/common';
import { deserializeMessage } from './deserialize_message';
import { safeJsonParse } from './safe_json_parse';

jest.mock('lodash', () => ({
cloneDeep: jest.fn(),
}));

jest.mock('./safe_json_parse', () => ({
safeJsonParse: jest.fn((value) => {
try {
return JSON.parse(value);
} catch {
return value;
}
}),
}));

describe('deserializeMessage', () => {
const baseMessage: Message = {
'@timestamp': '2024-10-15T00:00:00Z',
message: {
role: MessageRole.User,
content: 'This is a message',
},
};

beforeEach(() => {
(cloneDeep as jest.Mock).mockImplementation((obj) => JSON.parse(JSON.stringify(obj)));
});

it('should clone the original message', () => {
const message = { ...baseMessage };
deserializeMessage(message);

expect(cloneDeep).toHaveBeenCalledWith(message);
});

it('should deserialize function_call.arguments if it is a string', () => {
const messageWithFunctionCall: Message = {
...baseMessage,
message: {
...baseMessage.message,
function_call: {
name: 'testFunction',
arguments: '{"key": "value"}',
trigger: MessageRole.Assistant,
},
},
};

const result = deserializeMessage(messageWithFunctionCall);

expect(safeJsonParse).toHaveBeenCalledWith('{"key": "value"}');
expect(result.message.function_call!.arguments).toEqual({ key: 'value' });
});

it('should deserialize message.content if it is a string', () => {
const messageWithContent: Message = {
...baseMessage,
message: {
...baseMessage.message,
name: 'testMessage',
content: '{"key": "value"}',
},
};

const result = deserializeMessage(messageWithContent);

expect(safeJsonParse).toHaveBeenCalledWith('{"key": "value"}');
expect(result.message.content).toEqual({ key: 'value' });
});

it('should deserialize message.data if it is a string', () => {
const messageWithData: Message = {
...baseMessage,
message: {
...baseMessage.message,
name: 'testMessage',
data: '{"key": "value"}',
},
};

const result = deserializeMessage(messageWithData);

expect(safeJsonParse).toHaveBeenCalledWith('{"key": "value"}');
expect(result.message.data).toEqual({ key: 'value' });
});

it('should return the copied message as is if no deserialization is needed', () => {
const messageWithoutSerialization: Message = {
...baseMessage,
message: {
...baseMessage.message,
function_call: {
name: 'testFunction',
arguments: '',
trigger: MessageRole.Assistant,
},
content: '',
},
};

const result = deserializeMessage(messageWithoutSerialization);

expect(result.message.function_call!.name).toEqual('testFunction');
expect(result.message.function_call!.arguments).toEqual('');
expect(result.message.content).toEqual('');
});
});
35 changes: 35 additions & 0 deletions x-pack/packages/kbn-ai-assistant/src/utils/deserialize_message.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import { cloneDeep } from 'lodash';
import type { Message } from '@kbn/observability-ai-assistant-plugin/common';
import { safeJsonParse } from './safe_json_parse';

export const deserializeMessage = (message: Message): Message => {
const copiedMessage = cloneDeep(message);

if (
copiedMessage.message.function_call?.arguments &&
typeof copiedMessage.message.function_call?.arguments === 'string'
) {
copiedMessage.message.function_call.arguments = safeJsonParse(
copiedMessage.message.function_call.arguments ?? '{}'
);
}

if (copiedMessage.message.name) {
if (copiedMessage.message.content && typeof copiedMessage.message.content === 'string') {
copiedMessage.message.content = safeJsonParse(copiedMessage.message.content);
}

if (copiedMessage.message.data && typeof copiedMessage.message.data === 'string') {
copiedMessage.message.data = safeJsonParse(copiedMessage.message.data);
}
}

return copiedMessage;
};

0 comments on commit 40a6ae1

Please sign in to comment.