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

Context aware alert analysis #996

Merged
Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
710d35e
support date_nanos type
Hailong-am May 23, 2024
660fa08
support context aware alert analysis
Hailong-am May 29, 2024
3e02bf9
Register summary generation type of IncontextInsight for alert summar…
songkant-aws Jul 11, 2024
36c4b8c
Fix dashboard unit test failure
songkant-aws Jul 16, 2024
2cabf62
Make each alert register its own IncontextInsight
songkant-aws Jul 22, 2024
bab04a7
Enable context aware alert only if feature flag is enabled
songkant-aws Jul 22, 2024
3a9dd55
Merge remote-tracking branch 'origin/main' into context-aware-alert-a…
songkant-aws Aug 6, 2024
7f8689c
Merge remote-tracking branch 'origin/main' into context-aware-alert-a…
songkant-aws Aug 22, 2024
9b4f175
Avoid unnecessary change and minorly change summary question
songkant-aws Aug 22, 2024
a2a1d9a
Fix undefined alert name
songkant-aws Aug 27, 2024
81785ca
Pass monitor type to additional info object of contextProvider
songkant-aws Aug 27, 2024
175d897
Address some comments and change feature flag
songkant-aws Aug 28, 2024
40796e4
Add assistant capabilities check to control component rendering
songkant-aws Sep 3, 2024
35f5f8e
Fix mismatched unit test snapshots
songkant-aws Sep 3, 2024
1c502e0
Handle the edge case of multiple indices in search and return more in…
songkant-aws Sep 5, 2024
40da2ca
Reduce llm context input size by taking topN active alerts
songkant-aws Sep 6, 2024
d168513
Distinguish source data and aggregation that trigger the alert
songkant-aws Sep 9, 2024
18e38dd
Merge remote-tracking branch 'origin/main' into context-aware-alert-a…
songkant-aws Sep 9, 2024
3cfafd4
Rename the capability UI rendering flag per assistant plugin change
songkant-aws Sep 11, 2024
6ef6588
Remove alert sample data per current requirement from context
songkant-aws Sep 11, 2024
f9722ef
Merge remote-tracking branch 'origin/main' into context-aware-alert-a…
songkant-aws Sep 13, 2024
1bdc80f
Merge branch 'main' into context-aware-alert-analysis
songkant-aws Sep 18, 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
1 change: 1 addition & 0 deletions opensearch_dashboards.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"opensearchDashboardsUtils",
"contentManagement"
],
"optionalPlugins": ["assistantDashboards"],
"server": true,
"ui": true,
"supportedOSDataSourceVersions": ">=2.13.0",
Expand Down
4 changes: 3 additions & 1 deletion public/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,9 @@ export function renderApp(coreStart, params, defaultRoute) {
defaultRoute: defaultRoute,
}}
>
<Route render={(props) => <Main title="Alerting" {...mdsProps} {...navProps} {...props} />} />
<Route
render={(props) => <Main title="Alerting" {...mdsProps} {...navProps} {...props} />}
/>
</CoreContext.Provider>
</ServicesContext.Provider>
</Router>,
Expand Down
3 changes: 3 additions & 0 deletions public/pages/Dashboard/containers/Dashboard.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { mount } from 'enzyme';
import Dashboard from './Dashboard';
import { historyMock, httpClientMock } from '../../../../test/mocks';
import { setupCoreStart } from '../../../../test/utils/helpers';
import { setAssistantDashboards } from '../../../services';

const location = {
hash: '',
Expand Down Expand Up @@ -62,6 +63,8 @@ beforeAll(() => {
});

describe('Dashboard', () => {
setAssistantDashboards({ getFeatureStatus: () => ({ chat: false, alertInsight: false }) });

beforeEach(() => {
jest.clearAllMocks();
});
Expand Down
105 changes: 103 additions & 2 deletions public/pages/Dashboard/utils/tableUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ import React from 'react';
import _ from 'lodash';
import { EuiLink, EuiToolTip } from '@elastic/eui';
import moment from 'moment';
import { ALERT_STATE, DEFAULT_EMPTY_DATA } from '../../../utils/constants';
import { ALERT_STATE, DEFAULT_EMPTY_DATA, MONITOR_TYPE } from '../../../utils/constants';
import { getApplication, getAssistantDashboards } from '../../../services';
import { getDataSourceQueryObj } from '../../../pages/utils/helpers';

export const renderTime = (time, options = { showFromNow: false }) => {
const momentTime = moment(time);
Expand Down Expand Up @@ -131,8 +133,10 @@ export const alertColumns = (
sortable: true,
truncateText: false,
render: (total, alert) => {
return (
const alertId = `alerts_${alert.alerts[0].id}`;
const component = (
<EuiLink
key={alertId}
onClick={() => {
openFlyout({
...alert,
Expand All @@ -152,6 +156,103 @@ export const alertColumns = (
{`${total} alerts`}
</EuiLink>
);
const contextProvider = async () => {
// 1. get monitor definition
const dataSourceQuery = getDataSourceQueryObj();
const monitorResp = await httpClient.get(
amsiglan marked this conversation as resolved.
Show resolved Hide resolved
`../api/alerting/monitors/${alert.monitor_id}`,
dataSourceQuery
);
const monitorDefinition = monitorResp.resp;
delete monitorDefinition.ui_metadata;
delete monitorDefinition.data_sources;

let monitorDefinitionStr = JSON.stringify(monitorDefinition);

// 2. get data triggers the alert
let alertTriggeredByValue = '';
let dsl = '';
let index = '';
if (
monitorResp.resp.monitor_type === MONITOR_TYPE.QUERY_LEVEL ||
monitorResp.resp.monitor_type === MONITOR_TYPE.BUCKET_LEVEL
) {
amsiglan marked this conversation as resolved.
Show resolved Hide resolved
const search = monitorResp.resp.inputs[0].search;
const indices = String(search.indices);
const splitIndices = indices.split(',');
index = splitIndices.length > 0 ? splitIndices[0].trim() : '';
let query = JSON.stringify(search.query);
// Only keep the query part
dsl = JSON.stringify({ query: search.query.query });
if (query.indexOf('{{period_end}}') !== -1) {
query = query.replaceAll('{{period_end}}', alert.start_time);
const alertStartTime = moment.utc(alert.start_time).format('YYYY-MM-DDTHH:mm:ss');
dsl = dsl.replaceAll('{{period_end}}', alertStartTime);
// as we changed the format, remove it
dsl = dsl.replaceAll('"format":"epoch_millis",', '');
monitorDefinitionStr = monitorDefinitionStr.replaceAll(
'{{period_end}}',
alertStartTime
);
// as we changed the format, remove it
monitorDefinitionStr = monitorDefinitionStr.replaceAll('"format":"epoch_millis",', '');
}
if (index) {
const alertData = await httpClient.post(`/api/console/proxy`, {
query: {
path: `${index}/_search`,
method: 'GET',
dataSourceId: dataSourceQuery ? dataSourceQuery.query.dataSourceId : '',
},
body: query,
prependBasePath: true,
asResponse: true,
withLongNumeralsSupport: true,
});

alertTriggeredByValue = JSON.stringify(
alertData.body.aggregations?.metric.value || alertData.body.hits.total.value
);
}
}

const filteredAlert = { ...alert };
const topN = 10;
const activeAlerts = alert.alerts.filter((alert) => alert.state === 'ACTIVE');
// Reduce llm input token size by taking topN active alerts
filteredAlert.alerts = activeAlerts.slice(0, topN);

// 3. build the context
return {
context: `
Here is the detail information about alert ${alert.trigger_name}
### Monitor definition\n ${monitorDefinitionStr}\n
### Active Alert\n ${JSON.stringify(filteredAlert)}\n
### Value triggers this alert\n ${alertTriggeredByValue}\n
### Alert query DSL ${dsl} \n`,
additionalInfo: {
monitorType: monitorResp.resp.monitor_type,
dsl: dsl,
index: index,
},
};
};

const assistantEnabled = getApplication().capabilities?.assistant?.enabled === true;
const assistantFeatureStatus = getAssistantDashboards().getFeatureStatus();
if (assistantFeatureStatus.alertInsight && assistantEnabled) {
getAssistantDashboards().registerIncontextInsight([
{
key: alertId,
type: 'generate',
suggestions: [`Please summarize this alert, do not use any tool.`],
contextProvider,
},
]);
return getAssistantDashboards().renderIncontextInsight({ children: component });
} else {
return component;
}
},
},
{
Expand Down
8 changes: 2 additions & 6 deletions public/pages/Home/Home.js
amsiglan marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -81,11 +81,7 @@ export default class Home extends Component {
const { httpClient, notifications, setFlyout, landingDataSourceId, defaultRoute } = this.props;
return (
<div>
{!defaultRoute && (
<EuiTabs size="s">
{this.tabs.map(this.renderTab)}
</EuiTabs>
)}
{!defaultRoute && <EuiTabs size="s">{this.tabs.map(this.renderTab)}</EuiTabs>}
<div style={{ padding: '25px 25px' }}>
<Switch>
<Route
Expand Down Expand Up @@ -125,7 +121,7 @@ export default class Home extends Component {
/>
)}
/>
<Redirect to={defaultRoute || "/dashboard"} />
<Redirect to={defaultRoute || '/dashboard'} />
</Switch>
</div>
</div>
Expand Down
2 changes: 1 addition & 1 deletion public/pages/Main/Main.js
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ class Main extends Component {

handleDataSourceChange = ([dataSource]) => {
const dataSourceId = dataSource?.id;
const dataSourceLabel = dataSource?.label
const dataSourceLabel = dataSource?.label;
if (this.props.dataSourceEnabled && dataSourceId === undefined) {
getNotifications().toasts.addDanger('Unable to set data source.');
} else if (this.state.selectedDataSourceId != dataSourceId) {
Expand Down
9 changes: 6 additions & 3 deletions public/plugin.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,10 @@ import { alertingTriggerAd } from './utils/contextMenu/triggers';
import { ExpressionsSetup } from '../../../src/plugins/expressions/public';
import { UiActionsSetup } from '../../../src/plugins/ui_actions/public';
import { overlayAlertsFunction } from './expressions/overlay_alerts';
import { setClient, setEmbeddable, setNotifications, setOverlays, setSavedAugmentVisLoader, setUISettings, setQueryService, setSavedObjectsClient, setDataSourceEnabled, setDataSourceManagementPlugin, setNavigationUI, setApplication, setContentManagementStart } from './services';
import { setClient, setEmbeddable, setNotifications, setOverlays, setSavedAugmentVisLoader, setUISettings, setQueryService, setSavedObjectsClient, setDataSourceEnabled, setDataSourceManagementPlugin, setNavigationUI, setApplication, setContentManagementStart, setAssistantDashboards } from './services';
import { VisAugmenterStart } from '../../../src/plugins/vis_augmenter/public';
import { DataPublicPluginStart } from '../../../src/plugins/data/public';
import { AssistantSetup } from './types';
import { DataSourceManagementPluginSetup } from '../../../src/plugins/data_source_management/public';
import { DataSourcePluginSetup } from '../../../src/plugins/data_source/public';
import { NavigationPublicPluginStart } from '../../../src/plugins/navigation/public';
Expand All @@ -47,6 +48,7 @@ export interface AlertingSetupDeps {
uiActions: UiActionsSetup;
dataSourceManagement: DataSourceManagementPluginSetup;
dataSource: DataSourcePluginSetup;
assistantDashboards?: AssistantSetup;
}

export interface AlertingStartDeps {
Expand All @@ -69,14 +71,13 @@ export class AlertingPlugin implements Plugin<void, AlertingStart, AlertingSetup
private appStateUpdater = new BehaviorSubject<AppUpdater>(this.updateDefaultRouteOfManagementApplications);


public setup(core: CoreSetup<AlertingStartDeps, AlertingStart>, { expressions, uiActions, dataSourceManagement, dataSource }: AlertingSetupDeps) {
public setup(core: CoreSetup<AlertingStartDeps, AlertingStart>, { expressions, uiActions, dataSourceManagement, dataSource, assistantDashboards }: AlertingSetupDeps) {

const mountWrapper = async (params: AppMountParameters, redirect: string) => {
const { renderApp } = await import("./app");
const [coreStart] = await core.getStartServices();
return renderApp(coreStart, params, redirect);
};

core.application.register({
id: PLUGIN_NAME,
title: 'Alerting',
Expand Down Expand Up @@ -178,6 +179,8 @@ export class AlertingPlugin implements Plugin<void, AlertingStart, AlertingSetup
);
}

setAssistantDashboards(assistantDashboards || { getFeatureStatus: () => ({ chat: false, alertInsight: false }) });

setUISettings(core.uiSettings);

// Set the HTTP client so it can be pulled into expression fns to make
Expand Down
5 changes: 5 additions & 0 deletions public/services/services.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { EmbeddableStart } from '../../../../src/plugins/embeddable/public';
import { NavigationPublicPluginStart } from '../../../../src/plugins/navigation/public';
import { ContentManagementPluginStart } from '../../../../src/plugins/content_management/public';
import { createNullableGetterSetter } from './utils/helper';
import { AssistantSetup } from '../types';

const ServicesContext = createContext<BrowserServices | null>(null);

Expand All @@ -30,6 +31,10 @@ export const [getSavedAugmentVisLoader, setSavedAugmentVisLoader] = createGetter

export const [getUISettings, setUISettings] = createGetterSetter<IUiSettingsClient>('UISettings');

export const [getAssistantDashboards, setAssistantDashboards] = createGetterSetter<
AssistantSetup | {}
>('assistantDashboards');

export const [getEmbeddable, setEmbeddable] = createGetterSetter<EmbeddableStart>('embeddable');

export const [getOverlays, setOverlays] =
Expand Down
12 changes: 12 additions & 0 deletions public/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

/**
* Introduce a compile dependency on dashboards-assistant
* as alerting need some types from the plugin.
* It will give a type error when dashboards-assistant is not installed so add a ts-ignore to suppress the error.
*/
// @ts-ignore
export type { AssistantSetup } from '../../dashboards-assistant/public';
amsiglan marked this conversation as resolved.
Show resolved Hide resolved
Loading