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

[ML] AIOps: @kbn/aiops-api-plugin #179695

Draft
wants to merge 31 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
e01919d
plugin boilerplate
walterra Mar 27, 2024
c61d9d2
change to non-versioned API
walterra Mar 27, 2024
f0dd21e
rename aiops-api dir to aiops_api
walterra Mar 27, 2024
8d4f49c
Obs AI Assistant <-> AIOps Log Rate Analysis
walterra Mar 28, 2024
ef2c55f
working PoC
walterra Mar 29, 2024
e6db9fd
add link to log rate analysis
walterra Apr 2, 2024
637cfa0
fix useEffect unmount
walterra Apr 2, 2024
d63612d
reenable log patterns
walterra Apr 5, 2024
82ae838
update dummy API endpoint
walterra Apr 5, 2024
dbfaf6d
LRA in Discover early days
walterra Apr 5, 2024
a12a1f3
use setScreenContext instead of PoC localStorage approach
walterra May 8, 2024
cbeb97c
discover start prompt
walterra May 13, 2024
1d4a1c0
aiops ai assistant ui
walterra May 13, 2024
defc63a
aiops_components: refactor document count chart
walterra May 14, 2024
77838d6
aiops_components: simple document count chart
walterra May 14, 2024
aab18a7
aiops_components: consolidate
walterra May 14, 2024
12dd266
aiops_components: simple analysis results table
walterra May 14, 2024
22c79aa
aiops_components: consolidate table column code
walterra May 14, 2024
fd61923
support for dips
walterra May 14, 2024
9e62a87
fetch_simple_log_rate_analysis
walterra May 22, 2024
0a8710a
make logger/emitError optional
walterra May 22, 2024
51a9eee
update API endpoint
walterra May 24, 2024
e5ac1af
API: field candidates support
walterra May 24, 2024
66596ce
comments
walterra Jun 11, 2024
d51953f
log rate analysis for alert details page
walterra Jun 17, 2024
bbcbb69
tweak function API
walterra Jul 5, 2024
1d88a83
tweak function API
walterra Jul 5, 2024
7f4d953
tweak function API for fetchSignificantTermPValues
walterra Jul 5, 2024
1893c25
tweak function API for fetchSignificantCategories
walterra Jul 5, 2024
cec58e7
fix refactor
walterra Jul 30, 2024
0180982
remove extend-expect
walterra Aug 29, 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
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@
"@kbn/actions-types": "link:packages/kbn-actions-types",
"@kbn/advanced-settings-plugin": "link:src/plugins/advanced_settings",
"@kbn/ai-assistant-management-plugin": "link:src/plugins/ai_assistant_management/selection",
"@kbn/aiops-api-plugin": "link:x-pack/plugins/aiops_api",
"@kbn/aiops-change-point-detection": "link:x-pack/packages/ml/aiops_change_point_detection",
"@kbn/aiops-common": "link:x-pack/packages/ml/aiops_common",
"@kbn/aiops-components": "link:x-pack/packages/ml/aiops_components",
Expand Down Expand Up @@ -1820,4 +1821,4 @@
"zod-to-json-schema": "^3.23.0"
},
"packageManager": "[email protected]"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
import { render, screen, fireEvent } from '@testing-library/react';
import React from 'react';
import { CancelSyncJobModal } from './sync_job_cancel_modal';
import '@testing-library/jest-dom/extend-expect';
import { I18nProvider } from '@kbn/i18n-react';

describe('CancelSyncJobModal', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -152,12 +152,38 @@ export function DiscoverLayout({ stateContainer }: DiscoverLayoutProps) {
});

// The assistant is getting the state from the url correctly
// expect from the index pattern where we have only the dataview id
// except from the index pattern where we have only the dataview id.
useEffect(() => {
const indexPattern = dataView?.getIndexPattern() || null;
const indexPatternTimeField = dataView?.getTimeField()?.spec.name || null;
const dataViewId = dataView?.id;

let indexPatternText =
'The current Elasticsearch index pattern could not be identified or the current page does not make use of an Elasticsearch index pattern.';

if (indexPattern !== null && indexPatternTimeField === null) {
indexPatternText = `The current Elasticsearch index is '${indexPattern}' and it has no time field specified.`;
} else if (indexPattern !== null && indexPatternTimeField !== null) {
indexPatternText = `The current Elasticsearch index is '${indexPattern}' and its time field is '${indexPatternTimeField}'.`;
}

const dataViewIdText = dataViewId ? ` The Kibana Data View Id is ${dataViewId}.` : '';

return observabilityAIAssistant?.service.setScreenContext({
screenDescription: `The user is looking at the Discover view on the ${
isEsqlMode ? 'ES|QL' : 'dataView'
} mode. The index pattern is the ${dataView.getIndexPattern()}`,
} mode. ${indexPatternText}${dataViewIdText}`,
starterPrompts: [
{
title: i18n.translate('discover.aiAssistant.starterPrompts.logRateAnalysis.title', {
defaultMessage: 'Explain',
}),
prompt: i18n.translate('discover.aiAssistant.starterPrompts.logRateAnalysis.title', {
defaultMessage: 'Can you analyze the log rate?',
}),
icon: 'inspect',
},
],
});
}, [dataView, isEsqlMode, observabilityAIAssistant?.service]);

Expand Down
23 changes: 23 additions & 0 deletions src/plugins/unified_histogram/public/chart/chart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,29 @@ export function Chart({

const actions: IconButtonGroupProps['buttons'] = [];

if (!breakdown.field) {
actions.push({
label: i18n.translate('unifiedHistogram.logRateAnalysisButton', {
defaultMessage: 'Run log rate analysis',
}),
iconType: 'stats',
isDisabled: isFlyoutVisible,
'data-test-subj': 'unifiedHistogramLogRateAnalysis',
onClick: async () => {
console.log('services', services);
console.log('log rate analysis time range', getTimeRange());
console.log('log rate analysis request', request);
console.log('log rate analysis chart', chart);

const timeRange = getTimeRange();
const resp = await services.http.post('/api/aiops/log_rate_analysis', {
body: {},
});
console.log('resp', resp);
},
});
}

if (canEditVisualizationOnTheFly) {
actions.push({
label: i18n.translate('unifiedHistogram.editVisualizationButton', {
Expand Down
30 changes: 30 additions & 0 deletions src/plugins/unified_histogram/public/chart/histogram.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,36 @@ export function Histogram({
visContext,
onLoad,
});
console.log('lensProps', lensProps);

const annotation = undefined;
if (annotation) {
lensProps.attributes.state.visualization.layers.push({
layerId: '8d26ab67-b841-4877-9d02-55bf270f9caf',
layerType: 'annotations',
annotations: [
{
type: 'manual',
icon: 'triangle',
textVisibility: true,
label: 'MY LABEL',
key: {
type: 'point_in_time',
timestamp: annotation.timestamp,
},
id: 'a8fb297c-8d96-4011-93c0-45af110d5302',
isHidden: false,
color: '#F04E98',
lineStyle: 'solid',
lineWidth: 1,
outside: false,
},
],
// TODO check if we need to set filter from
// the filterManager
ignoreGlobalFilters: false,
});
}

const { euiTheme } = useEuiTheme();
const boxShadow = `0 2px 2px -1px ${euiTheme.colors.mediumShade},
Expand Down
2 changes: 2 additions & 0 deletions tsconfig.base.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
"@kbn/advanced-settings-plugin/*": ["src/plugins/advanced_settings/*"],
"@kbn/ai-assistant-management-plugin": ["src/plugins/ai_assistant_management/selection"],
"@kbn/ai-assistant-management-plugin/*": ["src/plugins/ai_assistant_management/selection/*"],
"@kbn/aiops-api-plugin": ["x-pack/plugins/aiops_api"],
"@kbn/aiops-api-plugin/*": ["x-pack/plugins/aiops_api/*"],
"@kbn/aiops-change-point-detection": ["x-pack/packages/ml/aiops_change_point_detection"],
"@kbn/aiops-change-point-detection/*": ["x-pack/packages/ml/aiops_change_point_detection/*"],
"@kbn/aiops-common": ["x-pack/packages/ml/aiops_common"],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
*/

import { render, screen } from '@testing-library/react';
import '@testing-library/jest-dom/extend-expect';
import React from 'react';
import { I18nProvider } from '@kbn/i18n-react';
import { ControlSlider } from '.';
Expand Down
3 changes: 3 additions & 0 deletions x-pack/packages/ml/agg_utils/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,9 @@ export interface SignificantItem extends FieldValuePair {
/** The normalized score for the significant item. */
normalizedScore: number;

/** A human readable description of the log rate change. */
changeDescription?: string;

/** An optional histogram for the significant item. */
histogram?: SignificantItemHistogramItem[];

Expand Down
5 changes: 5 additions & 0 deletions x-pack/packages/ml/aiops_common/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@
*/
export const AIOPS_PLUGIN_ID = 'aiops';

/**
* AIOPS_API_PLUGIN_ID is used as a unique identifier for the aiops API plugin for public APIs.
*/
export const AIOPS_API_PLUGIN_ID = 'aiops';

export const AIOPS_API_ENDPOINT = {
LOG_RATE_ANALYSIS_FIELD_CANDIDATES: '/internal/aiops/log_rate_analysis/field_candidates',
LOG_RATE_ANALYSIS: '/internal/aiops/log_rate_analysis',
Expand Down
14 changes: 12 additions & 2 deletions x-pack/packages/ml/aiops_components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,16 @@ export { ProgressControls } from './src/progress_controls';
export {
DocumentCountChart,
DocumentCountChartRedux,
type BrushSettings,
SimpleDocumentCountChart,
getLogRateAnalysisBarStyleAccessor,
} from './src/document_count_chart';
export type { DocumentCountChartProps } from './src/document_count_chart';
export type {
BrushSettings,
BrushSelectionUpdateHandler,
DocumentCountChartProps,
} from './src/document_count_chart';
export {
getFieldValueColumn,
SimpleAnalysisResultsTable,
NARROW_COLUMN_WIDTH,
} from './src/results_table';
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* 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 React, { type FC } from 'react';

import { Axis, Position } from '@elastic/charts';

import type { FieldFormat } from '@kbn/field-formats-plugin/common';
import { MULTILAYER_TIME_AXIS_STYLE } from '@kbn/charts-plugin/common';

export const DocumentCountChartAxisX: FC = () => (
<Axis id="aiops-histogram-left-axis" position={Position.Left} ticks={2} integersOnly />
);

interface DocumentCountChartAxisYProps {
formatter: FieldFormat;
useLegacyTimeAxis: boolean;
}

export const DocumentCountChartAxisY: FC<DocumentCountChartAxisYProps> = ({
formatter,
useLegacyTimeAxis,
}) => {
return (
<Axis
id="aiops-histogram-bottom-axis"
position={Position.Bottom}
showOverlappingTicks={true}
tickFormat={(value) => formatter.convert(value)}
labelFormat={useLegacyTimeAxis ? undefined : () => ''}
timeAxisLayerCount={useLegacyTimeAxis ? 0 : 2}
style={useLegacyTimeAxis ? {} : MULTILAYER_TIME_AXIS_STYLE}
/>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
* 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 type { BarStyleAccessor } from '@elastic/charts/dist/chart_types/xy_chart/utils/specs';

import { LOG_RATE_ANALYSIS_HIGHLIGHT_COLOR } from '@kbn/aiops-log-rate-analysis';
import type { DocumentCountStatsChangePoint } from '@kbn/aiops-log-rate-analysis';

import { DOCUMENT_COUNT_CHART_OVERALL_SERIES_SPEC_ID } from './constants';

const logRateAnalysisHighlightedBarStyle = {
rect: {
opacity: 1,
fill: LOG_RATE_ANALYSIS_HIGHLIGHT_COLOR,
},
};

export const getLogRateAnalysisBarStyleAccessor =
(changePoint: DocumentCountStatsChangePoint): BarStyleAccessor =>
(d, g) => {
return g.specId === DOCUMENT_COUNT_CHART_OVERALL_SERIES_SPEC_ID &&
changePoint &&
d.x > changePoint.startTs &&
d.x < changePoint.endTs
? logRateAnalysisHighlightedBarStyle
: null;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/*
* 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.
*/

export const DOCUMENT_COUNT_CHART_DEFFAULT_HEIGHT = 120;
export const DOCUMENT_COUNT_CHART_OVERALL_SERIES_SPEC_ID = 'document_count';
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import type {
XYChartElementEvent,
XYBrushEvent,
} from '@elastic/charts';
import { Axis, Chart, HistogramBarSeries, Position, ScaleType, Settings } from '@elastic/charts';
import { Chart, HistogramBarSeries, ScaleType, Settings } from '@elastic/charts';
import type {
BarStyleAccessor,
RectAnnotationSpec,
Expand All @@ -33,14 +33,22 @@ import {
type WindowParameters,
} from '@kbn/aiops-log-rate-analysis';
import { type BrushSelectionUpdatePayload } from '@kbn/aiops-log-rate-analysis/state';
import { MULTILAYER_TIME_AXIS_STYLE } from '@kbn/charts-plugin/common';
import type { DataPublicPluginStart } from '@kbn/data-plugin/public';
import type { ChartsPluginStart } from '@kbn/charts-plugin/public';
import type { FieldFormatsStart } from '@kbn/field-formats-plugin/public';

import { DualBrush, DualBrushAnnotation } from '../..';

import { DocumentCountChartAxisX, DocumentCountChartAxisY } from './axis';
import { BrushBadge } from './brush_badge';
import {
DOCUMENT_COUNT_CHART_DEFFAULT_HEIGHT,
DOCUMENT_COUNT_CHART_OVERALL_SERIES_SPEC_ID,
} from './constants';
import {
documentCountChartOverallSeriesName,
documentCountChartOverallSeriesNameWithSplit,
} from './i18n';

declare global {
interface Window {
Expand Down Expand Up @@ -82,7 +90,7 @@ type SetAutoRunAnalysisFn = (isAutoRun: boolean) => void;
/**
* Brush selection update handler
*/
type BrushSelectionUpdateHandler = (
export type BrushSelectionUpdateHandler = (
/** Payload for the brush selection update */
d: BrushSelectionUpdatePayload
) => void;
Expand Down Expand Up @@ -138,8 +146,6 @@ export interface DocumentCountChartProps {
changePoint?: DocumentCountStatsChangePoint;
}

const SPEC_ID = 'document_count';

const BADGE_HEIGHT = 20;
const BADGE_WIDTH = 75;

Expand Down Expand Up @@ -199,20 +205,6 @@ export const DocumentCountChart: FC<DocumentCountChartProps> = (props) => {
const xAxisFormatter = fieldFormats.deserialize({ id: 'date' });
const useLegacyTimeAxis = uiSettings.get('visualization:useLegacyTimeAxis', false);

const overallSeriesName = i18n.translate(
'xpack.aiops.dataGrid.field.documentCountChart.seriesLabel',
{
defaultMessage: 'document count',
}
);

const overallSeriesNameWithSplit = i18n.translate(
'xpack.aiops.dataGrid.field.documentCountChartSplit.seriesLabel',
{
defaultMessage: 'Other document count',
}
);

// TODO Let user choose between ZOOM and BRUSH mode.
const [viewMode] = useState<VIEW_MODE>(VIEW_MODE.BRUSH);

Expand Down Expand Up @@ -476,7 +468,7 @@ export const DocumentCountChart: FC<DocumentCountChartProps> = (props) => {
<Chart
size={{
width: '100%',
height: height ?? 120,
height: height ?? DOCUMENT_COUNT_CHART_DEFFAULT_HEIGHT,
}}
>
<Settings
Expand All @@ -491,20 +483,19 @@ export const DocumentCountChart: FC<DocumentCountChartProps> = (props) => {
showLegend={false}
locale={i18n.getLocale()}
/>
<Axis id="aiops-histogram-left-axis" position={Position.Left} ticks={2} integersOnly />
<Axis
id="aiops-histogram-bottom-axis"
position={Position.Bottom}
showOverlappingTicks={true}
tickFormat={(value) => xAxisFormatter.convert(value)}
labelFormat={useLegacyTimeAxis ? undefined : () => ''}
timeAxisLayerCount={useLegacyTimeAxis ? 0 : 2}
style={useLegacyTimeAxis ? {} : MULTILAYER_TIME_AXIS_STYLE}
<DocumentCountChartAxisX />
<DocumentCountChartAxisY
formatter={xAxisFormatter}
useLegacyTimeAxis={useLegacyTimeAxis}
/>
{adjustedChartPoints?.length && (
<HistogramBarSeries
id={SPEC_ID}
name={chartPointsSplit ? overallSeriesNameWithSplit : overallSeriesName}
id={DOCUMENT_COUNT_CHART_OVERALL_SERIES_SPEC_ID}
name={
chartPointsSplit
? documentCountChartOverallSeriesNameWithSplit
: documentCountChartOverallSeriesName
}
xScaleType={ScaleType.Time}
yScaleType={ScaleType.Linear}
xAccessor="time"
Expand All @@ -519,7 +510,7 @@ export const DocumentCountChart: FC<DocumentCountChartProps> = (props) => {
)}
{adjustedChartPointsSplit?.length && (
<HistogramBarSeries
id={`${SPEC_ID}_split`}
id={`${DOCUMENT_COUNT_CHART_OVERALL_SERIES_SPEC_ID}_split`}
name={chartPointsSplitLabel}
xScaleType={ScaleType.Time}
yScaleType={ScaleType.Linear}
Expand Down
Loading