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
e3136aa
plugin boilerplate
walterra Mar 27, 2024
98bf718
change to non-versioned API
walterra Mar 27, 2024
fcb5b5f
rename aiops-api dir to aiops_api
walterra Mar 27, 2024
0d44ef1
Obs AI Assistant <-> AIOps Log Rate Analysis
walterra Mar 28, 2024
f19074a
working PoC
walterra Mar 29, 2024
a2f0eb2
add link to log rate analysis
walterra Apr 2, 2024
aa598e1
fix useEffect unmount
walterra Apr 2, 2024
e93fa42
reenable log patterns
walterra Apr 5, 2024
7d7a55b
update dummy API endpoint
walterra Apr 5, 2024
69fe938
LRA in Discover early days
walterra Apr 5, 2024
32dd184
use setScreenContext instead of PoC localStorage approach
walterra May 8, 2024
2815f3a
discover start prompt
walterra May 13, 2024
9c5cf2d
aiops ai assistant ui
walterra May 13, 2024
9134f58
aiops_components: refactor document count chart
walterra May 14, 2024
80a179b
aiops_components: simple document count chart
walterra May 14, 2024
17f5a47
aiops_components: consolidate
walterra May 14, 2024
5d487e1
aiops_components: simple analysis results table
walterra May 14, 2024
5229950
aiops_components: consolidate table column code
walterra May 14, 2024
8bedb01
support for dips
walterra May 14, 2024
b715f40
fetch_simple_log_rate_analysis
walterra May 22, 2024
b60ea31
make logger/emitError optional
walterra May 22, 2024
163be0a
update API endpoint
walterra May 24, 2024
aa48700
API: field candidates support
walterra May 24, 2024
aa013fd
comments
walterra Jun 11, 2024
4aa57e8
log rate analysis for alert details page
walterra Jun 17, 2024
59bf1e7
localhost/ssl support for scripts/synthtrace
walterra Jun 17, 2024
f46b6eb
tweak function API
walterra Jul 5, 2024
3fb9adb
tweak function API
walterra Jul 5, 2024
29dacee
tweak function API for fetchSignificantTermPValues
walterra Jul 5, 2024
cd308c9
tweak function API for fetchSignificantCategories
walterra Jul 5, 2024
f35ca17
tweak function API for topItemsHandler/fetchTopCategories
walterra Jul 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
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,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 @@ -1759,4 +1760,4 @@
"zod-to-json-schema": "^3.22.3"
},
"packageManager": "yarn@1.22.21"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { Client } from '@elastic/elasticsearch';
import { ApmSynthtraceEsClient } from '../../..';
import { Logger } from '../../lib/utils/create_logger';
import { RunOptions } from './parse_run_cli_flags';
import { getEsClientTlsSettings } from './ssl';

export function getApmEsClient({
target,
Expand All @@ -23,6 +24,7 @@ export function getApmEsClient({
}) {
const client = new Client({
node: target,
tls: getEsClientTlsSettings(target),
});

const apmEsClient = new ApmSynthtraceEsClient({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { Client } from '@elastic/elasticsearch';
import { AssetsSynthtraceEsClient } from '../../lib/assets/assets_synthtrace_es_client';
import { Logger } from '../../lib/utils/create_logger';
import { RunOptions } from './parse_run_cli_flags';
import { getEsClientTlsSettings } from './ssl';

export function getAssetsEsClient({
target,
Expand All @@ -21,6 +22,7 @@ export function getAssetsEsClient({
}) {
const client = new Client({
node: target,
tls: getEsClientTlsSettings(target),
});

return new AssetsSynthtraceEsClient({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { Client } from '@elastic/elasticsearch';
import { InfraSynthtraceEsClient } from '../../lib/infra/infra_synthtrace_es_client';
import { Logger } from '../../lib/utils/create_logger';
import { RunOptions } from './parse_run_cli_flags';
import { getEsClientTlsSettings } from './ssl';

export function getInfraEsClient({
target,
Expand All @@ -21,6 +22,7 @@ export function getInfraEsClient({
}) {
const client = new Client({
node: target,
tls: getEsClientTlsSettings(target),
});

return new InfraSynthtraceEsClient({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { Client } from '@elastic/elasticsearch';
import { LogsSynthtraceEsClient } from '../../lib/logs/logs_synthtrace_es_client';
import { Logger } from '../../lib/utils/create_logger';
import { RunOptions } from './parse_run_cli_flags';
import { getEsClientTlsSettings } from './ssl';

export function getLogsEsClient({
target,
Expand All @@ -21,6 +22,7 @@ export function getLogsEsClient({
}) {
const client = new Client({
node: target,
tls: getEsClientTlsSettings(target),
});

return new LogsSynthtraceEsClient({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import fetch from 'node-fetch';
import { format, parse, Url } from 'url';
import { Logger } from '../../lib/utils/create_logger';
import { RunOptions } from './parse_run_cli_flags';
import { getFetchAgent } from './ssl';

async function discoverAuth(parsedTarget: Url) {
const possibleCredentials = [`admin:changeme`, `elastic:changeme`, `elastic_serverless:changeme`];
Expand All @@ -20,7 +21,9 @@ async function discoverAuth(parsedTarget: Url) {
});
let status: number;
try {
const response = await fetch(url);
const response = await fetch(url, {
agent: getFetchAgent(url),
});
status = response.status;
} catch (err) {
status = 0;
Expand All @@ -43,6 +46,7 @@ async function getKibanaUrl({ target, logger }: { target: string; logger: Logger
method: 'HEAD',
follow: 1,
redirect: 'manual',
agent: getFetchAgent(target),
});

const discoveredKibanaUrl =
Expand All @@ -62,6 +66,7 @@ async function getKibanaUrl({ target, logger }: { target: string; logger: Logger

const redirectedResponse = await fetch(discoveredKibanaUrlWithAuth, {
method: 'HEAD',
agent: getFetchAgent(discoveredKibanaUrlWithAuth),
});

if (redirectedResponse.status !== 200) {
Expand Down
31 changes: 31 additions & 0 deletions packages/kbn-apm-synthtrace/src/cli/utils/ssl.ts
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 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

import * as Fs from 'fs';
import { CA_CERT_PATH } from '@kbn/dev-utils';
import https from 'https';

export function getFetchAgent(url: string) {
const isHTTPS = new URL(url).protocol === 'https:';
const isLocalhost = new URL(url).hostname === 'localhost';
return isHTTPS && isLocalhost ? new https.Agent({ rejectUnauthorized: false }) : undefined;
}

export function getEsClientTlsSettings(url: string) {
const isHTTPS = new URL(url).protocol === 'https:';
// load the CA cert from disk if necessary
const caCert = isHTTPS ? Fs.readFileSync(CA_CERT_PATH) : null;
const isLocalhost = new URL(url).hostname === 'localhost';

return caCert && isLocalhost
? {
ca: caCert,
rejectUnauthorized: true,
}
: undefined;
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import fetch from 'node-fetch';
import pRetry from 'p-retry';
import { Logger } from '../../utils/create_logger';
import { kibanaHeaders } from '../../shared/client_headers';
import { getFetchAgent } from '../../../cli/utils/ssl';

export class ApmSynthtraceKibanaClient {
private readonly logger: Logger;
Expand All @@ -34,6 +35,7 @@ export class ApmSynthtraceKibanaClient {
const response = await fetch(url, {
method: 'GET',
headers: kibanaHeaders(),
agent: getFetchAgent(url),
});

const responseJson = await response.json();
Expand Down Expand Up @@ -62,6 +64,7 @@ export class ApmSynthtraceKibanaClient {
method: 'POST',
headers: kibanaHeaders(),
body: '{"force":true}',
agent: getFetchAgent(url),
});

if (!res.ok) {
Expand Down Expand Up @@ -109,6 +112,7 @@ export class ApmSynthtraceKibanaClient {
method: 'DELETE',
headers: kibanaHeaders(),
body: '{"force":true}',
agent: getFetchAgent(url),
});

if (!res.ok) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import fetch from 'node-fetch';
import pRetry from 'p-retry';
import { Logger } from '../utils/create_logger';
import { kibanaHeaders } from '../shared/client_headers';
import { getFetchAgent } from '../../cli/utils/ssl';

export class InfraSynthtraceKibanaClient {
private readonly logger: Logger;
Expand All @@ -30,6 +31,7 @@ export class InfraSynthtraceKibanaClient {
const response = await fetch(fleetPackageApiUrl, {
method: 'GET',
headers: kibanaHeaders(),
agent: getFetchAgent(fleetPackageApiUrl),
});

const responseJson = await response.json();
Expand All @@ -54,6 +56,7 @@ export class InfraSynthtraceKibanaClient {
method: 'POST',
headers: kibanaHeaders(),
body: '{"force":true}',
agent: getFetchAgent(url),
});
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,12 +142,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 @@ -181,6 +181,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
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: '/internal/aiops/log_rate_analysis',
CATEGORIZATION_FIELD_VALIDATION: '/internal/aiops/categorization_field_validation',
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}
/>
);
};
Loading