Skip to content

Commit

Permalink
Address comments and add UTs
Browse files Browse the repository at this point in the history
Signed-off-by: Heng Qian <[email protected]>
  • Loading branch information
qianheng-aws committed Jul 23, 2024
1 parent 6f81cd3 commit 958ea97
Show file tree
Hide file tree
Showing 5 changed files with 184 additions and 70 deletions.
124 changes: 124 additions & 0 deletions server/parsers/ParseHelper.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

import { parseAdditionalActions } from './ParserHelper';

describe('ParseHelper', () => {
it('return additional actions when there is CreateAlertTool.output', async () => {
const output: string =
'\n' +
'{\n' +
' "name": "Flight Delay Alert",\n' +
' "search": {\n' +
' "indices": ["opensearch_dashboards_sample_data_flights"],\n' +
' "timeField": "timestamp",\n' +
' "bucketValue": 12,\n' +
' "bucketUnitOfTime": "h",\n' +
' "filters": [\n' +
' {\n' +
' "fieldName": [\n' +
' {\n' +
' "label": "FlightDelayMin",\n' +
' "type": "integer"\n' +
' }\n' +
' ],\n' +
' "fieldValue": 0,\n' +
' "operator": "is_greater"\n' +
' }\n' +
' ],\n' +
' "aggregations": [\n' +
' {\n' +
' "aggregationType": "sum",\n' +
' "fieldName": "FlightDelayMin"\n' +
' }\n' +
' ]\n' +
' },\n' +
' "triggers": [\n' +
' {\n' +
' "name": "Delayed Time Exceeds 1000 Minutes",\n' +
' "severity": 2,\n' +
' "thresholdValue": 1000,\n' +
' "thresholdEnum": "ABOVE"\n' +
' }\n' +
' ]\n' +
'}\n';
const expectedContent: string =
'name=Flight%20Delay%20Alert&index=opensearch_dashboards_sample_data_flights&timeField=timestamp&bucketValue=12&bucketUnitOfTime=h&filters=%5B%7B%22fieldName%22%3A%5B%7B%22label%22%3A%22FlightDelayMin%22%2C%22type%22%3A%22integer%22%7D%5D%2C%22fieldValue%22%3A0%2C%22operator%22%3A%22is_greater%22%7D%5D&aggregations=%5B%7B%22aggregationType%22%3A%22sum%22%2C%22fieldName%22%3A%22FlightDelayMin%22%7D%5D&triggers=%5B%7B%22name%22%3A%22Delayed%20Time%20Exceeds%201000%20Minutes%22%2C%22severity%22%3A2%2C%22thresholdValue%22%3A1000%2C%22thresholdEnum%22%3A%22ABOVE%22%7D%5D';
expect(
parseAdditionalActions({
input: 'input',
response: 'response',
conversation_id: '',
interaction_id: 'interaction_id',
create_time: '',
additional_info: {
'CreateAlertTool.output': [output],
},
})
).toEqual([
{
actionType: 'create_alert_button',
message: 'Create Alert',
content: expectedContent,
},
]);
});

it('do not return additional actions when CreateAlertTool.output is null', async () => {
expect(
parseAdditionalActions({
input: 'input',
response: 'response',
conversation_id: '',
interaction_id: 'interaction_id',
create_time: '',
additional_info: {},
})
).toEqual([]);
});

// Normally It won't happen since backend will never put a non-json output inCreateAlertTool.output. But we still want to handle it and add `create alert` button as additional action as it has invoked create alert tool.
it('return additional actions with empty content when CreateAlertTool.output is wrong format', async () => {
expect(
parseAdditionalActions({
input: 'input',
response: 'response',
conversation_id: '',
interaction_id: 'interaction_id',
create_time: '',
additional_info: {
'CreateAlertTool.output': ['None json output'],
},
})
).toEqual([
{
actionType: 'create_alert_button',
message: 'Create Alert',
content: '',
},
]);
});

it('return additional actions with existing info when CreateAlertTool.output missing part of parameters', async () => {
expect(
parseAdditionalActions({
input: 'input',
response: 'response',
conversation_id: '',
interaction_id: 'interaction_id',
create_time: '',
additional_info: {
'CreateAlertTool.output': ['{"name": "Test name"}'],
},
})
).toEqual([
{
actionType: 'create_alert_button',
message: 'Create Alert',
content: 'name=Test%20name&triggers=',
},
]);
});
});
24 changes: 15 additions & 9 deletions server/parsers/ParserHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,27 @@ import {
Interaction,
} from '../../common/types/chat_saved_object_attributes';

export const CreateMonitorParserHelper = (interaction: Interaction): IAdditionalAction[] => {
/*
* Add additional actions following the basic output in the same message bubble.
* Currently, only CreateAlertTool.output will add additional action, may be extended in the future.
*/
export const parseAdditionalActions = (interaction: Interaction): IAdditionalAction[] => {
const monitorParameters =
(interaction.additional_info?.['CreateAlertTool.output'] as string[] | null)?.flatMap(
(interaction.additional_info?.['CreateAlertTool.output'] as string[] | null)?.map(
(item: string): {} => {
// @typescript-eslint/no-explicit-any
let parameters: { [key: string]: string } = {};
try {
const parsedItem = JSON.parse(item);
parameters.name = parsedItem.name;
parameters.index = parsedItem.search.indices;
parameters.timeField = parsedItem.search.timeField;
parameters.bucketValue = parsedItem.search.bucketValue;
parameters.bucketUnitOfTime = parsedItem.search.bucketUnitOfTime;
parameters.filters = JSON.stringify(parsedItem.search.filters);
parameters.aggregations = JSON.stringify(parsedItem.search.aggregations);
if (parsedItem.search) {
parameters.index = parsedItem.search.indices;
parameters.timeField = parsedItem.search.timeField;
parameters.bucketValue = parsedItem.search.bucketValue;
parameters.bucketUnitOfTime = parsedItem.search.bucketUnitOfTime;
parameters.filters = JSON.stringify(parsedItem.search.filters);
parameters.aggregations = JSON.stringify(parsedItem.search.aggregations);
}
parameters.triggers = JSON.stringify(parsedItem.triggers);
} catch (e) {
parameters = {};
Expand All @@ -36,7 +42,7 @@ export const CreateMonitorParserHelper = (interaction: Interaction): IAdditional

if (!monitorParameters.length) return [];

return [...new Set(monitorParameters)]
return monitorParameters
.filter((parameters) => parameters)
.map((parameters) => ({
actionType: 'create_alert_button',
Expand Down
42 changes: 42 additions & 0 deletions server/parsers/basic_input_output_parser.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ describe('BasicInputOutputParser', () => {
type: 'output',
contentType: 'markdown',
content: 'response',
additionalActions: [],
interactionId: 'interaction_id',
suggestedActions: [],
},
Expand Down Expand Up @@ -59,6 +60,7 @@ describe('BasicInputOutputParser', () => {
type: 'output',
contentType: 'markdown',
content: 'response',
additionalActions: [],
interactionId: 'interaction_id',
suggestedActions: [
{
Expand All @@ -74,6 +76,44 @@ describe('BasicInputOutputParser', () => {
]);
});

it('return additional actions when additional_info has related info', async () => {
const item = {
input: 'input',
response: 'response',
conversation_id: '',
interaction_id: 'interaction_id',
create_time: '',
additional_info: {
'CreateAlertTool.output': ['{"name": "Test name"}'],
},
};
expect(
await BasicInputOutputParser.parserProvider(item, {
interactions: [item],
})
).toEqual([
{
type: 'input',
contentType: 'text',
content: 'input',
},
{
type: 'output',
contentType: 'markdown',
content: 'response',
additionalActions: [
{
actionType: 'create_alert_button',
message: 'Create Alert',
content: 'name=Test%20name&triggers=',
},
],
interactionId: 'interaction_id',
suggestedActions: [],
},
]);
});

it("should only parse latest interaction's suggestions field", async () => {
const item = {
input: 'input',
Expand Down Expand Up @@ -105,6 +145,7 @@ describe('BasicInputOutputParser', () => {
type: 'output',
contentType: 'markdown',
content: 'response',
additionalActions: [],
interactionId: 'interaction_id',
suggestedActions: [],
},
Expand Down Expand Up @@ -133,6 +174,7 @@ describe('BasicInputOutputParser', () => {
{
content:
'normal text<b></b> [](http://evil.com/) [image](http://evil.com/) [good link](https://link)',
additionalActions: [],
contentType: 'markdown',
interactionId: 'interaction_id',
type: 'output',
Expand Down
9 changes: 3 additions & 6 deletions server/parsers/basic_input_output_parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import createDOMPurify from 'dompurify';
import { JSDOM } from 'jsdom';
import { IInput, IOutput } from '../../common/types/chat_saved_object_attributes';
import { MessageParser } from '../types';
import { CreateMonitorParserHelper } from './ParserHelper';
import { parseAdditionalActions } from './ParserHelper';

const sanitize = (content: string) => {
const window = new JSDOM('').window;
Expand Down Expand Up @@ -93,16 +93,13 @@ export const BasicInputOutputParser: MessageParser = {
};

// TODO: make it more general by using registration of all internal parsers.
const alertActions =
interaction.additional_info && 'CreateAlertTool.output' in interaction.additional_info
? CreateMonitorParserHelper(interaction)
: undefined;
const additionalActions = parseAdditionalActions(interaction);
const outputItems: IOutput[] = [
{
type: 'output',
contentType: 'markdown',
content: sanitize(interaction.response),
additionalActions: alertActions,
additionalActions,
interactionId: interaction.interaction_id,
suggestedActions: suggestedActions
.filter((item) => item)
Expand Down
55 changes: 0 additions & 55 deletions server/parsers/create_monitor_parser.ts

This file was deleted.

0 comments on commit 958ea97

Please sign in to comment.