Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
ca57268
Add chatContext functionality
CoenWarmer Jan 30, 2024
d05bfce
Correctly set URL
CoenWarmer Jan 30, 2024
9862d17
Merge branch 'main' of github.com:elastic/kibana into feat/chat-context
CoenWarmer Feb 6, 2024
3018007
Add starter and contextual suggestions
CoenWarmer Feb 7, 2024
d14a1ab
Merge branch 'main' of github.com:elastic/kibana into feat/chat-context
dgieselaar Feb 9, 2024
e4d60dd
Revert "Add starter and contextual suggestions"
dgieselaar Feb 9, 2024
f4a1fb9
Support for data in appContext
dgieselaar Feb 9, 2024
37defe5
Merge branch 'main' of github.com:elastic/kibana into feat/chat-context
dgieselaar Feb 9, 2024
e5c77c8
Rename recall to context for Bedrock as well
dgieselaar Feb 9, 2024
5631579
[CI] Auto-commit changed files from 'node scripts/eslint --no-cache -…
kibanamachine Feb 9, 2024
998bfa4
Review feedback, fix tests
dgieselaar Feb 9, 2024
28269fe
Fix API test
dgieselaar Feb 9, 2024
f0fad69
Fix types
dgieselaar Feb 9, 2024
91d4c25
Lint errors
dgieselaar Feb 9, 2024
88c94a9
Merge branch 'main' of github.com:elastic/kibana into feat/chat-context
dgieselaar Feb 12, 2024
96b9a4e
Add SLO to screen data
dgieselaar Feb 12, 2024
7fade90
Merge branch 'main' of github.com:elastic/kibana into feat/chat-context
dgieselaar Feb 12, 2024
4c932c6
Fix API/functional tests
dgieselaar Feb 13, 2024
cd540f6
Merge branch 'main' of github.com:elastic/kibana into feat/chat-context
dgieselaar Feb 13, 2024
3bbe608
Add screen description as a query
dgieselaar Feb 13, 2024
23457ab
Merge branch 'main' of github.com:elastic/kibana into feat/chat-context
dgieselaar Feb 13, 2024
2177d37
Review feedback
dgieselaar Feb 13, 2024
67e1f75
Rename setApplicationContext to setScreenContext
dgieselaar Feb 13, 2024
1ee69c5
Merge branch 'main' of github.com:elastic/kibana into feat/chat-context
dgieselaar Feb 13, 2024
d87b4f9
Remove console.log statement
dgieselaar Feb 13, 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
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

import { EuiEmptyPrompt, EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import React, { useCallback, useMemo, useState } from 'react';
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { v4 as uuidv4 } from 'uuid';
import { APIReturnType } from '../../../services/rest/create_call_apm_api';
import { useStateDebounced } from '../../../hooks/use_debounce';
Expand All @@ -18,7 +18,7 @@ import {
} from '../../../../common/service_inventory';
import { useAnomalyDetectionJobsContext } from '../../../context/anomaly_detection_jobs/use_anomaly_detection_jobs_context';
import { useApmParams } from '../../../hooks/use_apm_params';
import { FETCH_STATUS } from '../../../hooks/use_fetcher';
import { FETCH_STATUS, isFailure, isPending } from '../../../hooks/use_fetcher';
import { useLocalStorage } from '../../../hooks/use_local_storage';
import { usePreferredDataSourceAndBucketSize } from '../../../hooks/use_preferred_data_source_and_bucket_size';
import { useProgressiveFetcher } from '../../../hooks/use_progressive_fetcher';
Expand All @@ -29,6 +29,7 @@ import { isTimeComparison } from '../../shared/time_comparison/get_comparison_op
import { ServiceList } from './service_list';
import { orderServiceItems } from './service_list/order_service_items';
import { SortFunction } from '../../shared/managed_table';
import { useApmPluginContext } from '../../../context/apm_plugin/use_apm_plugin_context';

type MainStatisticsApiResponse = APIReturnType<'GET /internal/apm/services'>;

Expand Down Expand Up @@ -265,6 +266,33 @@ export function ServiceInventory() {
[tiebreakerField]
);

const { setScreenContext } =
useApmPluginContext().observabilityAIAssistant.service;

useEffect(() => {
if (isFailure(mainStatisticsStatus)) {
return setScreenContext({
screenDescription: 'The services have failed to load',
});
}

if (isPending(mainStatisticsStatus)) {
return setScreenContext({
screenDescription: 'The services are still loading',
});
}

return setScreenContext({
data: [
{
name: 'services',
description: 'The list of services that the user is looking at',
value: mainStatisticsData.items,
},
],
});
}, [mainStatisticsStatus, mainStatisticsData.items, setScreenContext]);

return (
<>
<SearchBar showTimeComparison />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@
*/

import { EuiFlexGroup, EuiFlexItem, EuiPanel, EuiSpacer } from '@elastic/eui';
import React from 'react';
import React, { useEffect } from 'react';
import { useHistory } from 'react-router-dom';
import { isServerlessAgentName } from '../../../../common/agent_name';
import { useApmPluginContext } from '../../../context/apm_plugin/use_apm_plugin_context';
import { useApmServiceContext } from '../../../context/apm_service/use_apm_service_context';
import { useApmParams } from '../../../hooks/use_apm_params';
import { useLocalStorage } from '../../../hooks/use_local_storage';
Expand Down Expand Up @@ -55,6 +56,15 @@ export function TransactionOverview() {
false
);

const { setScreenContext } =
useApmPluginContext().observabilityAIAssistant.service;

useEffect(() => {
return setScreenContext({
screenDescription: `The user is looking at the transactions overview for ${serviceName}, and the transaction type is ${transactionType}`,
});
}, [setScreenContext, serviceName, transactionType]);

return (
<>
{!sloCalloutDismissed && (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { EuiFlexGroup, EuiFlexItem, EuiPageHeaderProps } from '@elastic/eui';
import { useKibana } from '@kbn/kibana-react-plugin/public';
import { ObservabilityPageTemplateProps } from '@kbn/observability-shared-plugin/public';
import type { KibanaPageTemplateProps } from '@kbn/shared-ux-page-kibana-template';
import React, { useContext } from 'react';
import React, { useContext, useEffect } from 'react';
import { useLocation } from 'react-router-dom';
import { FeatureFeedbackButton } from '@kbn/observability-shared-plugin/public';
import { KibanaEnvironmentContext } from '../../../context/kibana_environment_context/kibana_environment_context';
Expand Down Expand Up @@ -66,6 +66,8 @@ export function ApmMainTemplate({
const basePath = http?.basePath.get();
const { config } = useApmPluginContext();

const aiAssistant = services.observabilityAIAssistant.service;

const ObservabilityPageTemplate = observabilityShared.navigation.PageTemplate;

const { data, status } = useFetcher((callApmApi) => {
Expand Down Expand Up @@ -103,16 +105,35 @@ export function ApmMainTemplate({
status === FETCH_STATUS.LOADING ||
fleetApmPoliciesStatus === FETCH_STATUS.LOADING;

const hasApmData = !!data?.hasData;
const hasApmIntegrations = !!fleetApmPoliciesData?.hasApmPolicies;

const noDataConfig = getNoDataConfig({
basePath,
docsLink: docLinks!.links.observability.guide,
hasApmData: data?.hasData,
hasApmIntegrations: fleetApmPoliciesData?.hasApmPolicies,
hasApmData,
hasApmIntegrations,
shouldBypassNoDataScreen,
loading: isLoading,
isServerless: config?.serverlessOnboarding,
});

useEffect(() => {
return aiAssistant.setScreenContext({
screenDescription: [
hasApmData
? 'The user has APM data.'
: 'The user does not have APM data.',
hasApmIntegrations
? 'The user has the APM integration installed. '
: 'The user does not have the APM integration installed',
noDataConfig !== undefined
? 'The user is looking at a screen that tells them they do not have any data.'
: '',
].join('\n'),
});
}, [hasApmData, hasApmIntegrations, noDataConfig, aiAssistant]);

const rightSideItems = [
...(showServiceGroupSaveButton ? [<ServiceGroupSaveButton />] : []),
];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import type { CoreStart } from '@kbn/core/public';
import type { Meta, Story } from '@storybook/react';
import { noop } from 'lodash';
import React, { ComponentProps } from 'react';
import type { ApmPluginContextValue } from '../../../context/apm_plugin/apm_plugin_context';
import { MockApmPluginStorybook } from '../../../context/apm_plugin/mock_apm_plugin_storybook';
Expand All @@ -23,6 +24,11 @@ const coreMock = {
},
},
},
observabilityAIAssistant: {
service: {
setScreenContext: () => noop,
},
},
} as unknown as Partial<CoreStart>;

const configMock = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { i18n } from '@kbn/i18n';
import { v4 as uuidv4 } from 'uuid';
import { FormattedMessage } from '@kbn/i18n-react';
import { compact } from 'lodash';
import React, { useMemo, useState } from 'react';
import React, { useEffect, useMemo, useState } from 'react';
import { ApmDocumentType } from '../../../../common/document_type';
import {
getLatencyAggregationType,
Expand All @@ -33,6 +33,7 @@ import { ManagedTable, TableSearchBar } from '../managed_table';
import { OverviewTableContainer } from '../overview_table_container';
import { isTimeComparison } from '../time_comparison/get_comparison_options';
import { getColumns } from './get_columns';
import { useApmPluginContext } from '../../../context/apm_plugin/use_apm_plugin_context';

type ApiResponse =
APIReturnType<'GET /internal/apm/services/{serviceName}/transactions/groups/main_statistics'>;
Expand Down Expand Up @@ -163,6 +164,26 @@ export function TransactionsTable({
};
}, [mainStatistics.maxCountExceeded, setSearchQueryDebounced]);

const { setScreenContext } =
useApmPluginContext().observabilityAIAssistant.service;

useEffect(() => {
return setScreenContext({
data: [
{
name: 'top_transactions',
description: 'The visible transaction groups',
value: mainStatistics.transactionGroups.map((group) => {
return {
name: group.name,
alertsCount: group.alertsCount,
};
}),
},
],
});
}, [setScreenContext, mainStatistics]);

return (
<EuiFlexGroup
direction="column"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import React, { ReactNode, useMemo } from 'react';
import { RouterProvider } from '@kbn/typed-react-router-config';
import { useHistory } from 'react-router-dom';
import { createMemoryHistory, History } from 'history';
import { merge } from 'lodash';
import { merge, noop } from 'lodash';
import { coreMock } from '@kbn/core/public/mocks';
import { UrlService } from '@kbn/share-plugin/common/url_service';
import { createObservabilityRuleTypeRegistryMock } from '@kbn/observability-plugin/public';
Expand Down Expand Up @@ -171,6 +171,11 @@ export const mockApmPluginContextValue = {
uiActions: {
getTriggerCompatibleActions: () => Promise.resolve([]),
},
observabilityAIAssistant: {
service: {
setScreenContext: jest.fn().mockImplementation(() => noop),
},
},
};

export function MockApmPluginContextWrapper({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { UI_SETTINGS } from '@kbn/observability-shared-plugin/public/hooks/use_k
import { UrlService } from '@kbn/share-plugin/common/url_service';
import { RouterProvider } from '@kbn/typed-react-router-config';
import { createMemoryHistory } from 'history';
import { merge } from 'lodash';
import { merge, noop } from 'lodash';
import React, { ReactNode } from 'react';
import { __IntlProvider as IntlProvider } from '@kbn/i18n-react';
import { Observable, of } from 'rxjs';
Expand Down Expand Up @@ -128,6 +128,11 @@ const mockCore = {
const mockApmPluginContext = {
core: mockCore,
plugins: mockPlugin,
observabilityAIAssistant: {
service: {
setScreenContext: () => noop,
},
},
} as unknown as ApmPluginContextValue;

export function MockApmPluginStorybook({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
import { RuleTypeModel } from '@kbn/triggers-actions-ui-plugin/public';
import { useBreadcrumbs } from '@kbn/observability-shared-plugin/public';

import dedent from 'dedent';
import { useKibana } from '../../utils/kibana_react';
import { useFetchRule } from '../../hooks/use_fetch_rule';
import { usePluginContext } from '../../hooks/use_plugin_context';
Expand Down Expand Up @@ -56,6 +57,9 @@ export function AlertDetails() {
},
http,
triggersActionsUi: { ruleTypeRegistry },
observabilityAIAssistant: {
service: { setScreenContext },
},
uiSettings,
} = useKibana().services;

Expand All @@ -71,6 +75,39 @@ export function AlertDetails() {
const [summaryFields, setSummaryFields] = useState<AlertSummaryField[]>();
const [alertStatus, setAlertStatus] = useState<AlertStatus>();

useEffect(() => {
if (!alertDetail) {
return;
}

const screenDescription = dedent(`The user is looking at an ${
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i18n?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@shahzad31 We always use English when communicating with the LLM (think of this as an API call where you wouldn't translate the params either)

alertDetail.formatted.active ? 'active' : 'recovered'
} alert.
It started at ${new Date(
alertDetail.formatted.start
).toISOString()}, and was last updated at ${new Date(
alertDetail.formatted.lastUpdated
).toISOString()}.

${
alertDetail.formatted.reason
? `The reason given for the alert is ${alertDetail.formatted.reason}.`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i18n?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not needed, everything we sent to the LLM needs to be in English.

: ''
}
`);

return setScreenContext({
screenDescription,
data: [
{
name: 'alert_fields',
description: 'The fields and values for the alert',
value: alertDetail.formatted.fields,
},
],
});
}, [setScreenContext, alertDetail]);

useEffect(() => {
if (alertDetail) {
setRuleTypeModel(ruleTypeRegistry.get(alertDetail?.formatted.fields[ALERT_RULE_TYPE_ID]!));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* 2.0.
*/

import React, { useState } from 'react';
import React, { useEffect, useState } from 'react';
import { useParams } from 'react-router-dom';
import { useIsMutating } from '@tanstack/react-query';
import { EuiLoadingSpinner } from '@elastic/eui';
Expand All @@ -15,6 +15,7 @@ import type { ChromeBreadcrumb } from '@kbn/core-chrome-browser';
import type { SLOWithSummaryResponse } from '@kbn/slo-schema';
import { useBreadcrumbs } from '@kbn/observability-shared-plugin/public';

import dedent from 'dedent';
import { useKibana } from '../../utils/kibana_react';
import { usePluginContext } from '../../hooks/use_plugin_context';
import { useFetchSloDetails } from '../../hooks/slo/use_fetch_slo_details';
Expand All @@ -34,6 +35,9 @@ export function SloDetailsPage() {
const {
application: { navigateToUrl },
http: { basePath },
observabilityAIAssistant: {
service: { setScreenContext },
},
} = useKibana().services;
const { ObservabilityPageTemplate } = usePluginContext();

Expand All @@ -53,6 +57,31 @@ export function SloDetailsPage() {

useBreadcrumbs(getBreadcrumbs(basePath, slo));

useEffect(() => {
if (!slo) {
return;
}

return setScreenContext({
screenDescription: dedent(`
The user is looking at the detail page for the following SLO

Name: ${slo.name}.
Id: ${slo.id}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i guess we also need to slo.instanceId if it's not equal to *

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's available in the metadata below

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What metadata are you referring to?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if using the actual doc fields slo.name, slo.id etc, would help the LLM be able to match the data to parameters for functions when those parameters use the field name.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the data prop - that's also sent over to the LLM

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

re: your second question: yes, that's useful to the LLM, but also available in the data prop

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dominiqueclarke sorry I spoke to soon, I have not actually verified whether field names are available in the metadata. Where are the field names used (outside of SOs)?

Description: ${slo.description}
Observed value: ${slo.summary.sliValue}
Status: ${slo.summary.status}
`),
data: [
{
name: 'slo',
description: 'The SLO and its metadata',
value: slo,
},
],
});
}, [setScreenContext, slo]);

const isSloNotFound = !isLoading && slo === undefined;
if (isSloNotFound) {
return <PageNotFound />;
Expand Down
Loading