From dedfdecbaf0297da6166c0218851c5b7677dcd60 Mon Sep 17 00:00:00 2001 From: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Date: Tue, 29 Oct 2024 04:56:52 +1100 Subject: [PATCH] [8.16] [AI Assistant] Set scope and rename to Observability and Search (#196322) (#197608) # Backport This will backport the following commits from `main` to `8.16`: - [[AI Assistant] Set scope and rename to Observability and Search (#196322)](https://github.com/elastic/kibana/pull/196322) ### Questions ? Please refer to the [Backport tool documentation](https://github.com/sqren/backport) Co-authored-by: Sander Philipse <94373878+sphilipse@users.noreply.github.com> --- docs/developer/plugin-list.asciidoc | 2 +- .../ai_assistant_selection_page.tsx | 2 +- .../selection/server/plugin.ts | 6 +- .../src/utils/filter_scopes.ts | 10 +- .../src/conversation/conversation_view.tsx | 11 +- .../__storybook_mocks__/use_conversation.ts | 2 +- .../kbn-ai-assistant/src/hooks/index.ts | 2 +- .../src/hooks/use_conversation.test.tsx | 6 +- .../src/hooks/use_conversation.ts | 6 +- .../src/hooks/{use_scope.ts => use_scopes.ts} | 6 +- .../src/utils/create_mock_chat_service.ts | 2 +- .../get_apm_dataset_info.ts | 3 +- .../get_apm_downstream_dependencies.ts | 3 +- .../get_apm_services_list.ts | 3 +- .../assistant_functions/get_apm_timeseries.ts | 3 +- .../apm/server/assistant_functions/index.ts | 5 +- .../common/functions/types.ts | 2 - .../public/components/insight/insight.tsx | 4 +- .../public/hooks/use_chat.test.ts | 6 +- .../public/hooks/use_chat.ts | 8 +- .../public/mock.tsx | 8 +- .../public/plugin.tsx | 2 +- .../public/service/complete.test.ts | 2 +- .../public/service/complete.ts | 6 +- .../service/create_chat_service.test.ts | 6 +- .../public/service/create_chat_service.ts | 43 ++- .../service/create_mock_chat_service.ts | 2 +- .../public/service/create_service.ts | 24 +- .../public/storybook_mock.tsx | 10 +- .../public/types.ts | 14 +- .../server/functions/context.ts | 3 +- .../server/functions/elasticsearch.ts | 3 +- .../server/functions/execute_connector.ts | 3 +- .../functions/get_dataset_info/index.ts | 3 +- .../server/functions/index.ts | 100 ++++--- .../server/functions/kibana.ts | 3 +- .../server/functions/summarize.ts | 3 +- .../server/routes/chat/route.ts | 16 +- .../server/routes/functions/route.ts | 15 +- .../chat_function_client/index.test.ts | 3 +- .../service/chat_function_client/index.ts | 21 +- .../server/service/client/index.test.ts | 2 +- .../server/service/client/index.ts | 7 +- .../client/operators/continue_conversation.ts | 11 +- .../server/service/index.ts | 9 +- .../server/service/types.ts | 16 +- .../observability_ai_assistant/tsconfig.json | 2 +- .../public/hooks/use_nav_control_scope.ts | 15 +- .../conversation_view_with_props.tsx | 1 + .../scripts/evaluation/evaluation.ts | 2 +- .../scripts/evaluation/kibana_client.ts | 8 +- .../server/functions/alerts.ts | 263 +++++++++--------- .../server/functions/changes/index.ts | 216 +++++++------- .../server/functions/lens.ts | 14 +- .../server/functions/query/index.ts | 8 +- .../server/functions/visualize_esql.ts | 3 +- .../server/rule_connector/index.ts | 5 +- .../README.md | 2 +- .../public/app.tsx | 2 +- .../public/plugin.ts | 4 +- .../routes/components/settings_page.tsx | 2 +- .../components/settings_tab/settings_tab.tsx | 2 +- .../tsconfig.json | 2 +- .../conversation_view_with_props.tsx | 2 +- .../server/functions/index.ts | 15 +- .../serverless_observability/public/plugin.ts | 2 +- .../tests/chat/chat.spec.ts | 6 +- .../tests/complete/complete.spec.ts | 8 +- .../tests/complete/functions/helpers.ts | 6 +- .../knowledge_base_user_instructions.spec.ts | 2 +- .../ai_assistant/tests/chat/chat.spec.ts | 6 +- .../tests/complete/complete.spec.ts | 8 +- .../tests/complete/functions/helpers.ts | 6 +- .../knowledge_base_user_instructions.spec.ts | 2 +- 74 files changed, 523 insertions(+), 518 deletions(-) rename x-pack/packages/kbn-ai-assistant/src/hooks/{use_scope.ts => use_scopes.ts} (79%) diff --git a/docs/developer/plugin-list.asciidoc b/docs/developer/plugin-list.asciidoc index a99e030a4adc..b6ba24df7897 100644 --- a/docs/developer/plugin-list.asciidoc +++ b/docs/developer/plugin-list.asciidoc @@ -744,7 +744,7 @@ Elastic. |{kib-repo}blob/{branch}/x-pack/plugins/observability_solution/observability_ai_assistant_management/README.md[observabilityAiAssistantManagement] -|The observabilityAiAssistantManagement plugin manages the Ai Assistant for Observability management section. +|The observabilityAiAssistantManagement plugin manages the Ai Assistant for Observability and Search management section. |{kib-repo}blob/{branch}/x-pack/plugins/observability_solution/observability_logs_explorer/README.md[observabilityLogsExplorer] diff --git a/src/plugins/ai_assistant_management/selection/public/routes/components/ai_assistant_selection_page.tsx b/src/plugins/ai_assistant_management/selection/public/routes/components/ai_assistant_selection_page.tsx index 5ff10f3b2c4b..c4a537042f60 100644 --- a/src/plugins/ai_assistant_management/selection/public/routes/components/ai_assistant_selection_page.tsx +++ b/src/plugins/ai_assistant_management/selection/public/routes/components/ai_assistant_selection_page.tsx @@ -139,7 +139,7 @@ export function AiAssistantSelectionPage() { isDisabled={!observabilityAIAssistantEnabled} title={i18n.translate( 'aiAssistantManagementSelection.aiAssistantSelectionPage.observabilityLabel', - { defaultMessage: 'Elastic AI Assistant for Observability' } + { defaultMessage: 'Elastic AI Assistant for Observability and Search' } )} titleSize="xs" /> diff --git a/src/plugins/ai_assistant_management/selection/server/plugin.ts b/src/plugins/ai_assistant_management/selection/server/plugin.ts index a8175f2f0bce..4b74b55e571a 100644 --- a/src/plugins/ai_assistant_management/selection/server/plugin.ts +++ b/src/plugins/ai_assistant_management/selection/server/plugin.ts @@ -50,7 +50,7 @@ export class AIAssistantManagementSelectionPlugin core.uiSettings.register({ [PREFERRED_AI_ASSISTANT_TYPE_SETTING_KEY]: { name: i18n.translate('aiAssistantManagementSelection.preferredAIAssistantTypeSettingName', { - defaultMessage: 'Observability AI Assistant scope', + defaultMessage: 'AI Assistant for Observability and Search visibility', }), category: [DEFAULT_APP_CATEGORIES.observability.id], value: this.config.preferredAIAssistantType, @@ -58,7 +58,7 @@ export class AIAssistantManagementSelectionPlugin 'aiAssistantManagementSelection.preferredAIAssistantTypeSettingDescription', { defaultMessage: - '[technical preview] Whether to show the Observability AI Assistant menu item in Observability, everywhere, or nowhere.', + '[technical preview] Whether to show the AI Assistant menu item in Observability and Search, everywhere, or nowhere.', values: { em: (chunks) => `${chunks}`, }, @@ -77,7 +77,7 @@ export class AIAssistantManagementSelectionPlugin optionLabels: { [AIAssistantType.Default]: i18n.translate( 'aiAssistantManagementSelection.preferredAIAssistantTypeSettingValueDefault', - { defaultMessage: 'Observability only (default)' } + { defaultMessage: 'Observability and Search only (default)' } ), [AIAssistantType.Observability]: i18n.translate( 'aiAssistantManagementSelection.preferredAIAssistantTypeSettingValueObservability', diff --git a/x-pack/packages/kbn-ai-assistant-common/src/utils/filter_scopes.ts b/x-pack/packages/kbn-ai-assistant-common/src/utils/filter_scopes.ts index ff8f627b10da..03c3aae1dbd0 100644 --- a/x-pack/packages/kbn-ai-assistant-common/src/utils/filter_scopes.ts +++ b/x-pack/packages/kbn-ai-assistant-common/src/utils/filter_scopes.ts @@ -7,11 +7,15 @@ import type { AssistantScope } from '../types'; -export function filterScopes(scope?: AssistantScope) { +export function filterScopes( + scopeFilters?: AssistantScope[] +) { return function (value: T): boolean { - if (!scope || !value) { + if (!scopeFilters || !value) { return true; } - return value?.scopes ? value.scopes.includes(scope) || value.scopes.includes('all') : true; + return value?.scopes + ? value.scopes.some((scope) => [...scopeFilters, 'all'].includes(scope)) + : true; }; } diff --git a/x-pack/packages/kbn-ai-assistant/src/conversation/conversation_view.tsx b/x-pack/packages/kbn-ai-assistant/src/conversation/conversation_view.tsx index b7d5831e14f9..fe71a9585dd1 100644 --- a/x-pack/packages/kbn-ai-assistant/src/conversation/conversation_view.tsx +++ b/x-pack/packages/kbn-ai-assistant/src/conversation/conversation_view.tsx @@ -10,6 +10,7 @@ import { euiThemeVars } from '@kbn/ui-theme'; import React, { useEffect, useState } from 'react'; import ReactDOM from 'react-dom'; import type { AssistantScope } from '@kbn/ai-assistant-common'; +import { isEqual } from 'lodash'; import { useKibana } from '../hooks/use_kibana'; import { ConversationList, ChatBody, ChatInlineEditingContent } from '../chat'; import { useConversationKey } from '../hooks/use_conversation_key'; @@ -27,7 +28,7 @@ interface ConversationViewProps { navigateToConversation: (nextConversationId?: string) => void; getConversationHref?: (conversationId: string) => string; newConversationHref?: string; - scope?: AssistantScope; + scopes?: AssistantScope[]; } export const ConversationView: React.FC = ({ @@ -35,7 +36,7 @@ export const ConversationView: React.FC = ({ navigateToConversation, getConversationHref, newConversationHref, - scope, + scopes, }) => { const { euiTheme } = useEuiTheme(); @@ -61,10 +62,10 @@ export const ConversationView: React.FC = ({ ); useEffect(() => { - if (scope) { - service.setScope(scope); + if (scopes && !isEqual(scopes, service.getScopes())) { + service.setScopes(scopes); } - }, [scope, service]); + }, [scopes, service]); const { key: bodyKey, updateConversationIdInPlace } = useConversationKey(conversationId); diff --git a/x-pack/packages/kbn-ai-assistant/src/hooks/__storybook_mocks__/use_conversation.ts b/x-pack/packages/kbn-ai-assistant/src/hooks/__storybook_mocks__/use_conversation.ts index 8bc8f54e9ac8..a619164517e2 100644 --- a/x-pack/packages/kbn-ai-assistant/src/hooks/__storybook_mocks__/use_conversation.ts +++ b/x-pack/packages/kbn-ai-assistant/src/hooks/__storybook_mocks__/use_conversation.ts @@ -15,6 +15,6 @@ export function useConversation() { stop: () => {}, messages: [], saveTitle: () => {}, - scope: 'all', + scopes: ['all'], }; } diff --git a/x-pack/packages/kbn-ai-assistant/src/hooks/index.ts b/x-pack/packages/kbn-ai-assistant/src/hooks/index.ts index 41bb8a4906c1..ddfa6c415140 100644 --- a/x-pack/packages/kbn-ai-assistant/src/hooks/index.ts +++ b/x-pack/packages/kbn-ai-assistant/src/hooks/index.ts @@ -8,4 +8,4 @@ export * from './use_ai_assistant_app_service'; export * from './use_ai_assistant_chat_service'; export * from './use_knowledge_base'; -export * from './use_scope'; +export * from './use_scopes'; diff --git a/x-pack/packages/kbn-ai-assistant/src/hooks/use_conversation.test.tsx b/x-pack/packages/kbn-ai-assistant/src/hooks/use_conversation.test.tsx index 02c6018a4216..dffca3addd34 100644 --- a/x-pack/packages/kbn-ai-assistant/src/hooks/use_conversation.test.tsx +++ b/x-pack/packages/kbn-ai-assistant/src/hooks/use_conversation.test.tsx @@ -55,9 +55,9 @@ const mockService: MockedService = { predefinedConversation$: new Observable(), }, navigate: jest.fn().mockReturnValue(of()), - scope$: new BehaviorSubject('all') as MockedService['scope$'], - setScope: jest.fn(), - getScope: jest.fn(), + scope$: new BehaviorSubject(['all']) as MockedService['scope$'], + setScopes: jest.fn(), + getScopes: jest.fn(), }; const mockChatService = createMockChatService(); diff --git a/x-pack/packages/kbn-ai-assistant/src/hooks/use_conversation.ts b/x-pack/packages/kbn-ai-assistant/src/hooks/use_conversation.ts index b40507a09719..d65fa1999133 100644 --- a/x-pack/packages/kbn-ai-assistant/src/hooks/use_conversation.ts +++ b/x-pack/packages/kbn-ai-assistant/src/hooks/use_conversation.ts @@ -20,7 +20,7 @@ import { useAIAssistantAppService } from './use_ai_assistant_app_service'; import { useKibana } from './use_kibana'; import { useOnce } from './use_once'; import { useAbortableAsync } from './use_abortable_async'; -import { useScope } from './use_scope'; +import { useScopes } from './use_scopes'; function createNewConversation({ title = EMPTY_CONVERSATION_TITLE, @@ -62,7 +62,7 @@ export function useConversation({ onConversationUpdate, }: UseConversationProps): UseConversationResult { const service = useAIAssistantAppService(); - const scope = useScope(); + const scopes = useScopes(); const { services: { @@ -122,7 +122,7 @@ export function useConversation({ onConversationUpdate?.({ conversation: event.conversation }); }, persist: true, - scope, + scopes, }); const [displayedConversationId, setDisplayedConversationId] = useState(initialConversationId); diff --git a/x-pack/packages/kbn-ai-assistant/src/hooks/use_scope.ts b/x-pack/packages/kbn-ai-assistant/src/hooks/use_scopes.ts similarity index 79% rename from x-pack/packages/kbn-ai-assistant/src/hooks/use_scope.ts rename to x-pack/packages/kbn-ai-assistant/src/hooks/use_scopes.ts index ed752e501129..0a8bdeed3c82 100644 --- a/x-pack/packages/kbn-ai-assistant/src/hooks/use_scope.ts +++ b/x-pack/packages/kbn-ai-assistant/src/hooks/use_scopes.ts @@ -8,8 +8,8 @@ import { useObservable } from 'react-use/lib'; import { useAIAssistantAppService } from './use_ai_assistant_app_service'; -export const useScope = () => { +export const useScopes = () => { const service = useAIAssistantAppService(); - const scope = useObservable(service.scope$); - return scope || 'all'; + const scopes = useObservable(service.scope$); + return scopes || ['all']; }; diff --git a/x-pack/packages/kbn-ai-assistant/src/utils/create_mock_chat_service.ts b/x-pack/packages/kbn-ai-assistant/src/utils/create_mock_chat_service.ts index d7c332dc042d..7913b3ce7895 100644 --- a/x-pack/packages/kbn-ai-assistant/src/utils/create_mock_chat_service.ts +++ b/x-pack/packages/kbn-ai-assistant/src/utils/create_mock_chat_service.ts @@ -32,7 +32,7 @@ export const createMockChatService = (): MockedChatService => { content: '', }, }), - getScope: jest.fn(), + getScopes: jest.fn(), }; return mockChatService; }; diff --git a/x-pack/plugins/observability_solution/apm/server/assistant_functions/get_apm_dataset_info.ts b/x-pack/plugins/observability_solution/apm/server/assistant_functions/get_apm_dataset_info.ts index a27bdcc3dc81..72fb4c8c7d20 100644 --- a/x-pack/plugins/observability_solution/apm/server/assistant_functions/get_apm_dataset_info.ts +++ b/x-pack/plugins/observability_solution/apm/server/assistant_functions/get_apm_dataset_info.ts @@ -164,7 +164,6 @@ export function registerGetApmDatasetInfoFunction({ `, }, }; - }, - ['observability'] + } ); } diff --git a/x-pack/plugins/observability_solution/apm/server/assistant_functions/get_apm_downstream_dependencies.ts b/x-pack/plugins/observability_solution/apm/server/assistant_functions/get_apm_downstream_dependencies.ts index 8a95fe9c8986..478c96e77e56 100644 --- a/x-pack/plugins/observability_solution/apm/server/assistant_functions/get_apm_downstream_dependencies.ts +++ b/x-pack/plugins/observability_solution/apm/server/assistant_functions/get_apm_downstream_dependencies.ts @@ -67,7 +67,6 @@ export function registerGetApmDownstreamDependenciesFunction({ randomSampler, }), }; - }, - ['observability'] + } ); } diff --git a/x-pack/plugins/observability_solution/apm/server/assistant_functions/get_apm_services_list.ts b/x-pack/plugins/observability_solution/apm/server/assistant_functions/get_apm_services_list.ts index f768c30d8af2..b24c24425b41 100644 --- a/x-pack/plugins/observability_solution/apm/server/assistant_functions/get_apm_services_list.ts +++ b/x-pack/plugins/observability_solution/apm/server/assistant_functions/get_apm_services_list.ts @@ -84,7 +84,6 @@ export function registerGetApmServicesListFunction({ arguments: args, }), }; - }, - ['observability'] + } ); } diff --git a/x-pack/plugins/observability_solution/apm/server/assistant_functions/get_apm_timeseries.ts b/x-pack/plugins/observability_solution/apm/server/assistant_functions/get_apm_timeseries.ts index dc9152d268ad..63bdbd422c65 100644 --- a/x-pack/plugins/observability_solution/apm/server/assistant_functions/get_apm_timeseries.ts +++ b/x-pack/plugins/observability_solution/apm/server/assistant_functions/get_apm_timeseries.ts @@ -138,8 +138,7 @@ export function registerGetApmTimeseriesFunction({ content: timeseries.map((series): Omit => omit(series, 'data')), data: timeseries, }; - }, - ['observability'] + } ); } diff --git a/x-pack/plugins/observability_solution/apm/server/assistant_functions/index.ts b/x-pack/plugins/observability_solution/apm/server/assistant_functions/index.ts index 816f1e17e049..6a65e6126ff2 100644 --- a/x-pack/plugins/observability_solution/apm/server/assistant_functions/index.ts +++ b/x-pack/plugins/observability_solution/apm/server/assistant_functions/index.ts @@ -49,7 +49,10 @@ export function registerAssistantFunctions({ ruleDataClient: IRuleDataClient; plugins: APMRouteHandlerResources['plugins']; }): RegistrationCallback { - return async ({ resources, functions: { registerFunction } }) => { + return async ({ resources, functions: { registerFunction }, scopes }) => { + if (!scopes.includes('observability')) { + return; + } const apmRouteHandlerResources: MinimalAPMRouteHandlerResources = { context: resources.context, request: resources.request, diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant/common/functions/types.ts b/x-pack/plugins/observability_solution/observability_ai_assistant/common/functions/types.ts index 5b93005a7fc2..bd786e9ba3c7 100644 --- a/x-pack/plugins/observability_solution/observability_ai_assistant/common/functions/types.ts +++ b/x-pack/plugins/observability_solution/observability_ai_assistant/common/functions/types.ts @@ -6,7 +6,6 @@ */ import type { JSONSchema7TypeName } from 'json-schema'; import type { Observable } from 'rxjs'; -import type { AssistantScope } from '@kbn/ai-assistant-common'; import { ChatCompletionChunkEvent, MessageAddEvent } from '../conversation_complete'; import { FunctionVisibility } from './function_visibility'; export { FunctionVisibility }; @@ -42,7 +41,6 @@ export interface FunctionDefinition; diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant/public/components/insight/insight.tsx b/x-pack/plugins/observability_solution/observability_ai_assistant/public/components/insight/insight.tsx index 08bf1414b15b..562749f24cc9 100644 --- a/x-pack/plugins/observability_solution/observability_ai_assistant/public/components/insight/insight.tsx +++ b/x-pack/plugins/observability_solution/observability_ai_assistant/public/components/insight/insight.tsx @@ -56,7 +56,7 @@ function ChatContent({ }) { const service = useObservabilityAIAssistant(); const chatService = useObservabilityAIAssistantChatService(); - const scope = chatService.getScope(); + const scopes = chatService.getScopes(); const initialMessagesRef = useRef(initialMessages); @@ -69,7 +69,7 @@ function ChatContent({ initialMessages, persist: false, disableFunctions: true, - scope, + scopes, }); const lastAssistantResponse = getLastMessageOfType( diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant/public/hooks/use_chat.test.ts b/x-pack/plugins/observability_solution/observability_ai_assistant/public/hooks/use_chat.test.ts index 28e2a3709a35..e21eda9e09c6 100644 --- a/x-pack/plugins/observability_solution/observability_ai_assistant/public/hooks/use_chat.test.ts +++ b/x-pack/plugins/observability_solution/observability_ai_assistant/public/hooks/use_chat.test.ts @@ -39,7 +39,7 @@ const mockChatService: MockedChatService = { role: MessageRole.System, }, }), - getScope: jest.fn(), + getScopes: jest.fn(), }; const addErrorMock = jest.fn(); @@ -83,7 +83,7 @@ describe('useChat', () => { service: { getScreenContexts: () => [], } as unknown as ObservabilityAIAssistantService, - scope: 'observability', + scopes: ['observability'], } as UseChatProps, }); }); @@ -113,7 +113,7 @@ describe('useChat', () => { service: { getScreenContexts: () => [], } as unknown as ObservabilityAIAssistantService, - scope: 'observability', + scopes: ['observability'], } as UseChatProps, }); diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant/public/hooks/use_chat.ts b/x-pack/plugins/observability_solution/observability_ai_assistant/public/hooks/use_chat.ts index 48884664ec64..86aeb8f519e8 100644 --- a/x-pack/plugins/observability_solution/observability_ai_assistant/public/hooks/use_chat.ts +++ b/x-pack/plugins/observability_solution/observability_ai_assistant/public/hooks/use_chat.ts @@ -56,7 +56,7 @@ interface UseChatPropsWithoutContext { disableFunctions?: boolean; onConversationUpdate?: (event: ConversationCreateEvent | ConversationUpdateEvent) => void; onChatComplete?: (messages: Message[]) => void; - scope: AssistantScope; + scopes: AssistantScope[]; } export type UseChatProps = Omit; @@ -72,7 +72,7 @@ function useChatWithoutContext({ onChatComplete, persist, disableFunctions, - scope, + scopes, }: UseChatPropsWithoutContext): UseChatResult { const [chatState, setChatState] = useState(ChatState.Ready); const systemMessage = useMemo(() => { @@ -165,7 +165,7 @@ function useChatWithoutContext({ disableFunctions: disableFunctions ?? false, signal: abortControllerRef.current.signal, conversationId, - scope, + scopes, }); function getPendingMessages() { @@ -264,7 +264,7 @@ function useChatWithoutContext({ disableFunctions, service, systemMessage, - scope, + scopes, ] ); diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant/public/mock.tsx b/x-pack/plugins/observability_solution/observability_ai_assistant/public/mock.tsx index 0731f26476da..7be61a65e263 100644 --- a/x-pack/plugins/observability_solution/observability_ai_assistant/public/mock.tsx +++ b/x-pack/plugins/observability_solution/observability_ai_assistant/public/mock.tsx @@ -47,7 +47,7 @@ export const mockChatService: ObservabilityAIAssistantChatService = { content: 'System', }, }), - getScope: jest.fn(), + getScopes: jest.fn(), }; export const mockService: ObservabilityAIAssistantService = { @@ -64,9 +64,9 @@ export const mockService: ObservabilityAIAssistantService = { predefinedConversation$: new Observable(), }, navigate: async () => of(), - setScope: jest.fn(), - getScope: jest.fn(), - scope$: new BehaviorSubject('all'), + setScopes: jest.fn(), + getScopes: jest.fn(), + scope$: new BehaviorSubject(['all']), }; function createSetupContract(): ObservabilityAIAssistantPublicSetup { diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant/public/plugin.tsx b/x-pack/plugins/observability_solution/observability_ai_assistant/public/plugin.tsx index cb9cc7d94114..2753b750dc28 100644 --- a/x-pack/plugins/observability_solution/observability_ai_assistant/public/plugin.tsx +++ b/x-pack/plugins/observability_solution/observability_ai_assistant/public/plugin.tsx @@ -67,7 +67,7 @@ export class ObservabilityAIAssistantPlugin coreStart.application.capabilities.observabilityAIAssistant[ aiAssistantCapabilities.show ] === true, - scope: this.scopeFromConfig || 'observability', + scopes: this.scopeFromConfig ? [this.scopeFromConfig] : ['all'], scopeIsMutable: !!this.scopeFromConfig, })); diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant/public/service/complete.test.ts b/x-pack/plugins/observability_solution/observability_ai_assistant/public/service/complete.test.ts index 9d8338f2d389..dd69c8e30998 100644 --- a/x-pack/plugins/observability_solution/observability_ai_assistant/public/service/complete.test.ts +++ b/x-pack/plugins/observability_solution/observability_ai_assistant/public/service/complete.test.ts @@ -102,7 +102,7 @@ describe('complete', () => { disableFunctions: false, signal: new AbortController().signal, ...params, - scope: 'all', + scopes: ['all'], }, requestCallback ); diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant/public/service/complete.ts b/x-pack/plugins/observability_solution/observability_ai_assistant/public/service/complete.ts index 6e03683b4406..6cc08054e67a 100644 --- a/x-pack/plugins/observability_solution/observability_ai_assistant/public/service/complete.ts +++ b/x-pack/plugins/observability_solution/observability_ai_assistant/public/service/complete.ts @@ -43,7 +43,7 @@ export function complete( disableFunctions, signal, instructions, - scope, + scopes, }: { client: Pick; getScreenContexts: () => ObservabilityAIAssistantScreenContext[]; @@ -66,7 +66,7 @@ export function complete( screenContexts, conversationId, instructions, - scope, + scopes, }, }, }).pipe(shareReplay()); @@ -133,7 +133,7 @@ export function complete( persist, disableFunctions, instructions, - scope, + scopes, }, requestCallback ).subscribe(subscriber); diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant/public/service/create_chat_service.test.ts b/x-pack/plugins/observability_solution/observability_ai_assistant/public/service/create_chat_service.test.ts index 05e0a89c4b7a..f059196f2e68 100644 --- a/x-pack/plugins/observability_solution/observability_ai_assistant/public/service/create_chat_service.test.ts +++ b/x-pack/plugins/observability_solution/observability_ai_assistant/public/service/create_chat_service.test.ts @@ -56,7 +56,7 @@ describe('createChatService', () => { } beforeEach(async () => { - clientSpy.mockImplementationOnce(async () => { + clientSpy.mockImplementation(async () => { return { functionDefinitions: [], contextDefinitions: [], @@ -71,7 +71,7 @@ describe('createChatService', () => { apiClient: clientSpy, registrations: [], signal: new AbortController().signal, - scope$: new BehaviorSubject('observability'), + scope$: new BehaviorSubject(['observability']), }); }); @@ -85,7 +85,7 @@ describe('createChatService', () => { signal, messages: [], connectorId: '', - scope: 'observability', + scopes: ['observability'], }); } diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant/public/service/create_chat_service.ts b/x-pack/plugins/observability_solution/observability_ai_assistant/public/service/create_chat_service.ts index 04520cb70a58..e3ccb3831989 100644 --- a/x-pack/plugins/observability_solution/observability_ai_assistant/public/service/create_chat_service.ts +++ b/x-pack/plugins/observability_solution/observability_ai_assistant/public/service/create_chat_service.ts @@ -144,7 +144,7 @@ class ChatService { private renderFunctionRegistry: Map>; private abortSignal: AbortSignal; private apiClient: ObservabilityAIAssistantAPIClient; - public scope$: BehaviorSubject; + public scope$: BehaviorSubject; private analytics: AnalyticsServiceStart; private registrations: ChatRegistrationRenderFunction[]; private systemMessage: string; @@ -159,7 +159,7 @@ class ChatService { }: { abortSignal: AbortSignal; apiClient: ObservabilityAIAssistantAPIClient; - scope$: BehaviorSubject; + scope$: BehaviorSubject; analytics: AnalyticsServiceStart; registrations: ChatRegistrationRenderFunction[]; }) { @@ -186,15 +186,21 @@ class ChatService { async initialize() { this.functionRegistry = new Map(); - const [{ functionDefinitions, systemMessage }] = await Promise.all([ - this.apiClient('GET /internal/observability_ai_assistant/{scope}/functions', { - signal: this.abortSignal, - params: { - path: { - scope: this.getScope(), - }, + const systemMessages: string[] = []; + const scopePromise = this.apiClient('GET /internal/observability_ai_assistant/functions', { + signal: this.abortSignal, + params: { + query: { + scopes: this.getScopes(), }, - }), + }, + }).then(({ functionDefinitions, systemMessage }) => { + functionDefinitions.forEach((fn) => this.functionRegistry.set(fn.name, fn)); + systemMessages.push(systemMessage); + }); + + await Promise.all([ + scopePromise, ...this.registrations.map((registration) => { return registration({ registerRenderFunction: (name, renderFn) => { @@ -204,10 +210,7 @@ class ChatService { }), ]); - functionDefinitions.forEach((fn) => { - this.functionRegistry.set(fn.name, fn); - }); - this.systemMessage = systemMessage; + this.systemMessage = systemMessages.join('\n'); this.functions$.next(this.getFunctions()); } @@ -249,10 +252,6 @@ class ChatService { return filterFunctionDefinitions({ ...options, definitions: Array.from(this.functionRegistry.values()), - }).filter((value) => { - return value.scopes - ? value.scopes?.includes(this.getScope()) || value.scopes?.includes('all') - : true; }); }; @@ -301,7 +300,7 @@ class ChatService { connectorId, functionCall, functions: functions ?? [], - scope: this.getScope(), + scopes: this.getScopes(), }, }, signal, @@ -334,7 +333,7 @@ class ChatService { signal, client: this.getClient(), instructions, - scope: this.getScope(), + scopes: this.getScopes(), }, ({ params }) => { return this.callStreamingApi('POST /internal/observability_ai_assistant/chat/complete', { @@ -345,7 +344,7 @@ class ChatService { ); }; - public getScope() { + public getScopes() { return this.scope$.value; } } @@ -361,7 +360,7 @@ export async function createChatService({ signal: AbortSignal; registrations: ChatRegistrationRenderFunction[]; apiClient: ObservabilityAIAssistantAPIClient; - scope$: BehaviorSubject; + scope$: BehaviorSubject; }): Promise { return new ChatService({ analytics, diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant/public/service/create_mock_chat_service.ts b/x-pack/plugins/observability_solution/observability_ai_assistant/public/service/create_mock_chat_service.ts index 9af669242e43..0559d65a14a8 100644 --- a/x-pack/plugins/observability_solution/observability_ai_assistant/public/service/create_mock_chat_service.ts +++ b/x-pack/plugins/observability_solution/observability_ai_assistant/public/service/create_mock_chat_service.ts @@ -29,7 +29,7 @@ export const createMockChatService = (): MockedChatService => { content: 'system', }, }), - getScope: jest.fn(), + getScopes: jest.fn(), }; return mockChatService; }; diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant/public/service/create_service.ts b/x-pack/plugins/observability_solution/observability_ai_assistant/public/service/create_service.ts index 22d1b9c792f7..07f967a4028d 100644 --- a/x-pack/plugins/observability_solution/observability_ai_assistant/public/service/create_service.ts +++ b/x-pack/plugins/observability_solution/observability_ai_assistant/public/service/create_service.ts @@ -20,13 +20,13 @@ export function createService({ analytics, coreStart, enabled, - scope, + scopes, scopeIsMutable, }: { analytics: AnalyticsServiceStart; coreStart: CoreStart; enabled: boolean; - scope: AssistantScope; + scopes: [AssistantScope]; scopeIsMutable: boolean; }): ObservabilityAIAssistantService { const apiClient = createCallObservabilityAIAssistantAPI(coreStart); @@ -38,13 +38,13 @@ export function createService({ ]); const predefinedConversation$ = new Subject<{ messages: Message[]; title?: string }>(); - const scope$ = new BehaviorSubject(scope); + const scope$ = new BehaviorSubject(scopes); const getScreenContexts = () => { - const currentScope = scope$.value; + const currentScopes = scope$.value; const screenContexts = screenContexts$.value.map(({ starterPrompts, ...rest }) => ({ ...rest, - starterPrompts: starterPrompts?.filter(filterScopes(currentScope)), + starterPrompts: starterPrompts?.filter(filterScopes(currentScopes)), })); return screenContexts; }; @@ -58,7 +58,13 @@ export function createService({ }, start: async ({ signal }) => { const mod = await import('./create_chat_service'); - return await mod.createChatService({ analytics, apiClient, signal, registrations, scope$ }); + return await mod.createChatService({ + analytics, + apiClient, + signal, + registrations, + scope$, + }); }, callApi: apiClient, getScreenContexts, @@ -103,12 +109,12 @@ export function createService({ }, predefinedConversation$: predefinedConversation$.asObservable(), }, - setScope: (newScope: AssistantScope) => { + setScopes: (newScopes: AssistantScope[]) => { if (!scopeIsMutable) { - scope$.next(newScope); + scope$.next(newScopes); } }, - getScope: () => scope$.value, + getScopes: () => scope$.value, scope$, }; } diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant/public/storybook_mock.tsx b/x-pack/plugins/observability_solution/observability_ai_assistant/public/storybook_mock.tsx index 19d51bf4c66d..004ad25aa4a8 100644 --- a/x-pack/plugins/observability_solution/observability_ai_assistant/public/storybook_mock.tsx +++ b/x-pack/plugins/observability_solution/observability_ai_assistant/public/storybook_mock.tsx @@ -40,7 +40,7 @@ export const createStorybookChatService = (): ObservabilityAIAssistantChatServic functions$: new BehaviorSubject( [] ) as ObservabilityAIAssistantChatService['functions$'], - getScope: () => 'all', + getScopes: () => ['all'], }); export const createStorybookService = (): ObservabilityAIAssistantService => ({ @@ -57,7 +57,9 @@ export const createStorybookService = (): ObservabilityAIAssistantService => ({ predefinedConversation$: new Observable(), }, navigate: async () => of(), - scope$: new BehaviorSubject('all') as ObservabilityAIAssistantService['scope$'], - getScope: () => 'all', - setScope: () => {}, + scope$: new BehaviorSubject([ + 'all', + ]) as ObservabilityAIAssistantService['scope$'], + getScopes: () => ['all'], + setScopes: () => {}, }); diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant/public/types.ts b/x-pack/plugins/observability_solution/observability_ai_assistant/public/types.ts index b13d81faa3a3..becc21f59c5f 100644 --- a/x-pack/plugins/observability_solution/observability_ai_assistant/public/types.ts +++ b/x-pack/plugins/observability_solution/observability_ai_assistant/public/types.ts @@ -54,7 +54,7 @@ export interface ObservabilityAIAssistantChatService { functions?: Array>; functionCall?: string; signal: AbortSignal; - scope: AssistantScope; + scopes: AssistantScope[]; } ) => Observable; complete: (options: { @@ -70,12 +70,12 @@ export interface ObservabilityAIAssistantChatService { }; signal: AbortSignal; instructions?: AdHocInstruction[]; - scope: AssistantScope; + scopes: AssistantScope[]; }) => Observable; getFunctions: (options?: { contexts?: string[]; filter?: string; - scope: AssistantScope; + scopes: AssistantScope[]; }) => FunctionDefinition[]; functions$: BehaviorSubject; hasFunction: (name: string) => boolean; @@ -87,7 +87,7 @@ export interface ObservabilityAIAssistantChatService { response: { data?: string; content?: string }, onActionClick: ChatActionClickHandler ) => React.ReactNode; - getScope: () => AssistantScope; + getScopes: () => AssistantScope[]; } export interface ObservabilityAIAssistantConversationService { @@ -104,9 +104,9 @@ export interface ObservabilityAIAssistantService { getScreenContexts: () => ObservabilityAIAssistantScreenContext[]; conversations: ObservabilityAIAssistantConversationService; navigate: (callback: () => void) => Promise>; - scope$: BehaviorSubject; - setScope: (scope: AssistantScope) => void; - getScope: () => AssistantScope; + scope$: BehaviorSubject; + setScopes: (scope: AssistantScope[]) => void; + getScopes: () => AssistantScope[]; } export type RenderFunction = (options: { diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant/server/functions/context.ts b/x-pack/plugins/observability_solution/observability_ai_assistant/server/functions/context.ts index 61448d297e4d..fd5796861718 100644 --- a/x-pack/plugins/observability_solution/observability_ai_assistant/server/functions/context.ts +++ b/x-pack/plugins/observability_solution/observability_ai_assistant/server/functions/context.ts @@ -115,7 +115,6 @@ export function registerContextFunction({ subscriber.complete(); }); }); - }, - ['all'] + } ); } diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant/server/functions/elasticsearch.ts b/x-pack/plugins/observability_solution/observability_ai_assistant/server/functions/elasticsearch.ts index 71a0cfa4bbde..6008b53dd42c 100644 --- a/x-pack/plugins/observability_solution/observability_ai_assistant/server/functions/elasticsearch.ts +++ b/x-pack/plugins/observability_solution/observability_ai_assistant/server/functions/elasticsearch.ts @@ -48,7 +48,6 @@ export function registerElasticsearchFunction({ }); return { content: { response } }; - }, - ['all'] + } ); } diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant/server/functions/execute_connector.ts b/x-pack/plugins/observability_solution/observability_ai_assistant/server/functions/execute_connector.ts index bfe04cb56e8c..0088e35a6f6a 100644 --- a/x-pack/plugins/observability_solution/observability_ai_assistant/server/functions/execute_connector.ts +++ b/x-pack/plugins/observability_solution/observability_ai_assistant/server/functions/execute_connector.ts @@ -38,7 +38,6 @@ export function registerExecuteConnectorFunction({ ).getActionsClientWithRequest(resources.request); const content = await actionsClient.execute({ actionId: id, params }); return { content }; - }, - ['all'] + } ); } diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant/server/functions/get_dataset_info/index.ts b/x-pack/plugins/observability_solution/observability_ai_assistant/server/functions/get_dataset_info/index.ts index 9b20d364ef7d..57cac3a4e0c0 100644 --- a/x-pack/plugins/observability_solution/observability_ai_assistant/server/functions/get_dataset_info/index.ts +++ b/x-pack/plugins/observability_solution/observability_ai_assistant/server/functions/get_dataset_info/index.ts @@ -94,7 +94,6 @@ export function registerGetDatasetInfoFunction({ stats: relevantFieldNames.stats, }, }; - }, - ['all'] + } ); } diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant/server/functions/index.ts b/x-pack/plugins/observability_solution/observability_ai_assistant/server/functions/index.ts index a5333ee1a7ff..0313d29d3b20 100644 --- a/x-pack/plugins/observability_solution/observability_ai_assistant/server/functions/index.ts +++ b/x-pack/plugins/observability_solution/observability_ai_assistant/server/functions/index.ts @@ -28,20 +28,47 @@ export const registerFunctions: RegistrationCallback = async ({ functions, resources, signal, + scopes, }) => { const registrationParameters: FunctionRegistrationParameters = { client, functions, resources, signal, + scopes, }; const isServerless = !!resources.plugins.serverless; + if (scopes.includes('observability')) { + functions.registerInstruction(`You are a helpful assistant for Elastic Observability. Your goal is to help the Elastic Observability users to quickly assess what is happening in their observed systems. You can help them visualise and analyze data, investigate their systems, perform root cause analysis or identify optimisation opportunities. - functions.registerInstruction({ - instruction: `You are a helpful assistant for Elastic Observability. Your goal is to help the Elastic Observability users to quickly assess what is happening in their observed systems. You can help them visualise and analyze data, investigate their systems, perform root cause analysis or identify optimisation opportunities. + It's very important to not assume what the user is meaning. Ask them for clarification if needed. - It's very important to not assume what the user is meaning. Ask them for clarification if needed. + If you are unsure about which function should be used and with what arguments, ask the user for clarification or confirmation. + + In KQL ("kqlFilter")) escaping happens with double quotes, not single quotes. Some characters that need escaping are: ':()\\\ + /\". Always put a field value in double quotes. Best: service.name:\"opbeans-go\". Wrong: service.name:opbeans-go. This is very important! + + You can use Github-flavored Markdown in your responses. If a function returns an array, consider using a Markdown table to format the response. + + Note that ES|QL (the Elasticsearch Query Language which is a new piped language) is the preferred query language. + + If you want to call a function or tool, only call it a single time per message. Wait until the function has been executed and its results + returned to you, before executing the same tool or another tool again if needed. + + DO NOT UNDER ANY CIRCUMSTANCES USE ES|QL syntax (\`service.name == "foo"\`) with "kqlFilter" (\`service.name:"foo"\`). + + The user is able to change the language which they want you to reply in on the settings page of the AI Assistant for Observability and Search, which can be found in the ${ + isServerless ? `Project settings.` : `Stack Management app under the option AI Assistants` + }. + If the user asks how to change the language, reply in the same language the user asked in.`); + } + + if (scopes.length === 0 || (scopes.length === 1 && scopes[0] === 'all')) { + functions.registerInstruction( + `You are a helpful assistant for Elasticsearch. Your goal is to help Elasticsearch users accomplish tasks using Kibana and Elasticsearch. You can help them construct queries, index data, search data, use Elasticsearch APIs, generate sample data, visualise and analyze data. + + It's very important to not assume what the user means. Ask them for clarification if needed. If you are unsure about which function should be used and with what arguments, ask the user for clarification or confirmation. @@ -50,63 +77,56 @@ export const registerFunctions: RegistrationCallback = async ({ You can use Github-flavored Markdown in your responses. If a function returns an array, consider using a Markdown table to format the response. - Note that ES|QL (the Elasticsearch Query Language which is a new piped language) is the preferred query language. - If you want to call a function or tool, only call it a single time per message. Wait until the function has been executed and its results returned to you, before executing the same tool or another tool again if needed. - DO NOT UNDER ANY CIRCUMSTANCES USE ES|QL syntax (\`service.name == "foo"\`) with "kqlFilter" (\`service.name:"foo"\`). - - The user is able to change the language which they want you to reply in on the settings page of the AI Assistant for Observability, which can be found in the ${ + The user is able to change the language which they want you to reply in on the settings page of the AI Assistant for Observability and Search, which can be found in the ${ isServerless ? `Project settings.` : `Stack Management app under the option AI Assistants` }. - If the user asks how to change the language, reply in the same language the user asked in.`, - scopes: ['observability'], - }); + If the user asks how to change the language, reply in the same language the user asked in.` + ); + } const { ready: isReady } = await client.getKnowledgeBaseStatus(); - functions.registerInstruction({ - instruction: ({ availableFunctionNames }) => { - const instructions: string[] = []; + functions.registerInstruction(({ availableFunctionNames }) => { + const instructions: string[] = []; - if ( - availableFunctionNames.includes(QUERY_FUNCTION_NAME) && - availableFunctionNames.includes(GET_DATASET_INFO_FUNCTION_NAME) - ) { - instructions.push(`You MUST use the "${GET_DATASET_INFO_FUNCTION_NAME}" ${ - functions.hasFunction('get_apm_dataset_info') ? 'or the get_apm_dataset_info' : '' - } function before calling the "${QUERY_FUNCTION_NAME}" or the "changes" functions. + if ( + availableFunctionNames.includes(QUERY_FUNCTION_NAME) && + availableFunctionNames.includes(GET_DATASET_INFO_FUNCTION_NAME) + ) { + instructions.push(`You MUST use the "${GET_DATASET_INFO_FUNCTION_NAME}" ${ + functions.hasFunction('get_apm_dataset_info') ? 'or the get_apm_dataset_info' : '' + } function before calling the "${QUERY_FUNCTION_NAME}" or the "changes" functions. If a function requires an index, you MUST use the results from the dataset info functions.`); - } + } - if (availableFunctionNames.includes(GET_DATA_ON_SCREEN_FUNCTION_NAME)) { - instructions.push(`You have access to data on the screen by calling the "${GET_DATA_ON_SCREEN_FUNCTION_NAME}" function. + if (availableFunctionNames.includes(GET_DATA_ON_SCREEN_FUNCTION_NAME)) { + instructions.push(`You have access to data on the screen by calling the "${GET_DATA_ON_SCREEN_FUNCTION_NAME}" function. Use it to help the user understand what they are looking at. A short summary of what they are looking at is available in the return of the "${CONTEXT_FUNCTION_NAME}" function. Data that is compact enough automatically gets included in the response for the "${CONTEXT_FUNCTION_NAME}" function.`); - } + } - if (isReady) { - if (availableFunctionNames.includes(SUMMARIZE_FUNCTION_NAME)) { - instructions.push(`You can use the "${SUMMARIZE_FUNCTION_NAME}" function to store new information you have learned in a knowledge database. + if (isReady) { + if (availableFunctionNames.includes(SUMMARIZE_FUNCTION_NAME)) { + instructions.push(`You can use the "${SUMMARIZE_FUNCTION_NAME}" function to store new information you have learned in a knowledge database. Only use this function when the user asks for it. All summaries MUST be created in English, even if the conversation was carried out in a different language.`); - } - - if (availableFunctionNames.includes(CONTEXT_FUNCTION_NAME)) { - instructions.push( - `Additionally, you can use the "${CONTEXT_FUNCTION_NAME}" function to retrieve relevant information from the knowledge database.` - ); - } - } else { + } + + if (availableFunctionNames.includes(CONTEXT_FUNCTION_NAME)) { instructions.push( - `You do not have a working memory. If the user expects you to remember the previous conversations, tell them they can set up the knowledge base.` + `Additionally, you can use the "${CONTEXT_FUNCTION_NAME}" function to retrieve relevant information from the knowledge database.` ); } - return instructions.map((instruction) => dedent(instruction)); - }, - scopes: ['all'], + } else { + instructions.push( + `You do not have a working memory. If the user expects you to remember the previous conversations, tell them they can set up the knowledge base.` + ); + } + return instructions.map((instruction) => dedent(instruction)); }); if (isReady) { diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant/server/functions/kibana.ts b/x-pack/plugins/observability_solution/observability_ai_assistant/server/functions/kibana.ts index f55a8ba43292..f939e3a79799 100644 --- a/x-pack/plugins/observability_solution/observability_ai_assistant/server/functions/kibana.ts +++ b/x-pack/plugins/observability_solution/observability_ai_assistant/server/functions/kibana.ts @@ -95,7 +95,6 @@ export function registerKibanaFunction({ }).then((response) => { return { content: response.data }; }); - }, - ['all'] + } ); } diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant/server/functions/summarize.ts b/x-pack/plugins/observability_solution/observability_ai_assistant/server/functions/summarize.ts index a4c34c5caa5a..8865861d81f4 100644 --- a/x-pack/plugins/observability_solution/observability_ai_assistant/server/functions/summarize.ts +++ b/x-pack/plugins/observability_solution/observability_ai_assistant/server/functions/summarize.ts @@ -86,7 +86,6 @@ export function registerSummarizationFunction({ message: `The document has been stored`, }, })); - }, - ['observability'] + } ); } diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant/server/routes/chat/route.ts b/x-pack/plugins/observability_solution/observability_ai_assistant/server/routes/chat/route.ts index 136cc6849756..8bc88cca10b0 100644 --- a/x-pack/plugins/observability_solution/observability_ai_assistant/server/routes/chat/route.ts +++ b/x-pack/plugins/observability_solution/observability_ai_assistant/server/routes/chat/route.ts @@ -61,7 +61,7 @@ const chatCompleteInternalRt = t.intersection([ t.type({ body: t.type({ screenContexts: t.array(screenContextRt), - scope: assistantScopeType, + scopes: t.array(assistantScopeType), }), }), ]); @@ -83,11 +83,11 @@ async function initializeChatRequest({ request, plugins: { cloud, actions }, params: { - body: { connectorId, scope }, + body: { connectorId, scopes }, }, service, }: ObservabilityAIAssistantRouteHandlerResources & { - params: { body: { connectorId: string; scope: AssistantScope } }; + params: { body: { connectorId: string; scopes: AssistantScope[] } }; }) { await withAssistantSpan('guard_against_invalid_connector', async () => { const actionsClient = await (await actions.start()).getActionsClientWithRequest(request); @@ -101,7 +101,7 @@ async function initializeChatRequest({ }); const [client, cloudStart, simulateFunctionCalling] = await Promise.all([ - service.getClient({ request, scope }), + service.getClient({ request, scopes }), cloud?.start(), (await context.core).uiSettings.client.get(aiAssistantSimulatedFunctionCalling), ]); @@ -136,7 +136,7 @@ const chatRoute = createObservabilityAIAssistantServerRoute({ messages: t.array(messageRt), connectorId: t.string, functions: t.array(functionRt), - scope: assistantScopeType, + scopes: t.array(assistantScopeType), }), t.partial({ functionCall: t.string, @@ -182,7 +182,7 @@ const chatRecallRoute = createObservabilityAIAssistantServerRoute({ prompt: t.string, context: t.string, connectorId: t.string, - scope: assistantScopeType, + scopes: t.array(assistantScopeType), }), }), handler: async (resources): Promise => { @@ -248,6 +248,7 @@ async function chatComplete( screenContexts, instructions, disableFunctions, + scopes, }, } = params; @@ -260,6 +261,7 @@ async function chatComplete( resources, client, screenContexts, + scopes, }); const response$ = client.complete({ @@ -310,7 +312,7 @@ const publicChatCompleteRoute = createObservabilityAIAssistantServerRoute({ params: { body: { ...restOfBody, - scope: 'observability', + scopes: ['observability'], screenContexts: [ { actions, diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant/server/routes/functions/route.ts b/x-pack/plugins/observability_solution/observability_ai_assistant/server/routes/functions/route.ts index b31e33148454..8a61248d4e70 100644 --- a/x-pack/plugins/observability_solution/observability_ai_assistant/server/routes/functions/route.ts +++ b/x-pack/plugins/observability_solution/observability_ai_assistant/server/routes/functions/route.ts @@ -15,10 +15,10 @@ import { createObservabilityAIAssistantServerRoute } from '../create_observabili import { assistantScopeType } from '../runtime_types'; const getFunctionsRoute = createObservabilityAIAssistantServerRoute({ - endpoint: 'GET /internal/observability_ai_assistant/{scope}/functions', + endpoint: 'GET /internal/observability_ai_assistant/functions', params: t.type({ - path: t.type({ - scope: assistantScopeType, + query: t.partial({ + scopes: t.union([t.array(assistantScopeType), assistantScopeType]), }), }), options: { @@ -34,10 +34,12 @@ const getFunctionsRoute = createObservabilityAIAssistantServerRoute({ service, request, params: { - path: { scope }, + query: { scopes: inputScopes }, }, } = resources; + const scopes = inputScopes ? (Array.isArray(inputScopes) ? inputScopes : [inputScopes]) : []; + const controller = new AbortController(); request.events.aborted$.subscribe(() => { controller.abort(); @@ -51,19 +53,20 @@ const getFunctionsRoute = createObservabilityAIAssistantServerRoute({ resources, client, screenContexts: [], + scopes, }), // error is caught in client client.getKnowledgeBaseUserInstructions(), ]); - const functionDefinitions = functionClient.getFunctions({ scope }).map((fn) => fn.definition); + const functionDefinitions = functionClient.getFunctions().map((fn) => fn.definition); const availableFunctionNames = functionDefinitions.map((def) => def.name); return { functionDefinitions, systemMessage: getSystemMessageFromInstructions({ - applicationInstructions: functionClient.getInstructions(scope), + applicationInstructions: functionClient.getInstructions(), userInstructions, adHocInstructions: [], availableFunctionNames, diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/chat_function_client/index.test.ts b/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/chat_function_client/index.test.ts index ea265c580b50..3d83c470de0c 100644 --- a/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/chat_function_client/index.test.ts +++ b/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/chat_function_client/index.test.ts @@ -34,8 +34,7 @@ describe('chatFunctionClient', () => { required: ['foo'], }, }, - respondFn, - ['all'] + respondFn ); }); diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/chat_function_client/index.ts b/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/chat_function_client/index.ts index 4413e4fa8b63..97def121e859 100644 --- a/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/chat_function_client/index.ts +++ b/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/chat_function_client/index.ts @@ -9,7 +9,6 @@ import Ajv, { type ErrorObject, type ValidateFunction } from 'ajv'; import dedent from 'dedent'; import { compact, keyBy } from 'lodash'; -import { type AssistantScope, filterScopes } from '@kbn/ai-assistant-common'; import { FunctionVisibility, type FunctionResponse } from '../../../common/functions/types'; import type { Message, ObservabilityAIAssistantScreenContextRequest } from '../../../common/types'; import { filterFunctionDefinitions } from '../../../common/utils/filter_function_definitions'; @@ -18,7 +17,6 @@ import type { FunctionHandler, FunctionHandlerRegistry, InstructionOrCallback, - InstructionOrCallbackWithScopes, RegisterFunction, RegisterInstruction, } from '../types'; @@ -36,7 +34,7 @@ const ajv = new Ajv({ export const GET_DATA_ON_SCREEN_FUNCTION_NAME = 'get_data_on_screen'; export class ChatFunctionClient { - private readonly instructions: InstructionOrCallbackWithScopes[] = []; + private readonly instructions: InstructionOrCallback[] = []; private readonly functionRegistry: FunctionHandlerRegistry = new Map(); private readonly validators: Map = new Map(); @@ -75,8 +73,7 @@ export class ChatFunctionClient { return { content: allData.filter((data) => dataNames.includes(data.name)), }; - }, - ['all'] + } ); } @@ -87,11 +84,11 @@ export class ChatFunctionClient { }); } - registerFunction: RegisterFunction = (definition, respond, scopes) => { + registerFunction: RegisterFunction = (definition, respond) => { if (definition.parameters) { this.validators.set(definition.name, ajv.compile(definition.parameters)); } - this.functionRegistry.set(definition.name, { handler: { definition, respond }, scopes }); + this.functionRegistry.set(definition.name, { handler: { definition, respond } }); }; registerInstruction: RegisterInstruction = (instruction) => { @@ -110,8 +107,8 @@ export class ChatFunctionClient { } } - getInstructions(scope: AssistantScope): InstructionOrCallback[] { - return this.instructions.filter(filterScopes(scope)).map((i) => i.instruction); + getInstructions(): InstructionOrCallback[] { + return this.instructions; } hasAction(name: string) { @@ -120,14 +117,10 @@ export class ChatFunctionClient { getFunctions({ filter, - scope, }: { filter?: string; - scope?: AssistantScope; } = {}): FunctionHandler[] { - const allFunctions = Array.from(this.functionRegistry.values()) - .filter(filterScopes(scope)) - .map(({ handler }) => handler); + const allFunctions = Array.from(this.functionRegistry.values()).map(({ handler }) => handler); const functionsByName = keyBy(allFunctions, (definition) => definition.definition.name); diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/client/index.test.ts b/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/client/index.test.ts index 5a7cf81a4012..0476bda1af8a 100644 --- a/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/client/index.test.ts +++ b/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/client/index.test.ts @@ -187,7 +187,7 @@ describe('Observability AI Assistant client', () => { user: { name: 'johndoe', }, - scope: 'all', + scopes: ['all'], }); } diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/client/index.ts b/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/client/index.ts index 4eb0e54f9feb..19a3dd827107 100644 --- a/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/client/index.ts +++ b/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/client/index.ts @@ -101,7 +101,7 @@ export class ObservabilityAIAssistantClient { name: string; }; knowledgeBaseService: KnowledgeBaseService; - scope: AssistantScope; + scopes: AssistantScope[]; } ) {} @@ -217,11 +217,11 @@ export class ObservabilityAIAssistantClient { // this is what we eventually store in the conversation const messagesWithUpdatedSystemMessage = replaceSystemMessage( getSystemMessageFromInstructions({ - applicationInstructions: functionClient.getInstructions(this.dependencies.scope), + applicationInstructions: functionClient.getInstructions(), userInstructions, adHocInstructions, availableFunctionNames: functionClient - .getFunctions({ scope: this.dependencies.scope }) + .getFunctions() .map((fn) => fn.definition.name), }), initialMessages @@ -301,7 +301,6 @@ export class ObservabilityAIAssistantClient { disableFunctions, tracer: completeTracer, connectorId, - scope: this.dependencies.scope, useSimulatedFunctionCalling: simulateFunctionCalling === true, }) ); diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/client/operators/continue_conversation.ts b/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/client/operators/continue_conversation.ts index 7ebd9d66bf30..66204c96f31c 100644 --- a/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/client/operators/continue_conversation.ts +++ b/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/client/operators/continue_conversation.ts @@ -21,7 +21,6 @@ import { switchMap, throwError, } from 'rxjs'; -import type { AssistantScope } from '@kbn/ai-assistant-common'; import { CONTEXT_FUNCTION_NAME } from '../../../functions/context'; import { createFunctionNotFoundError, Message, MessageRole } from '../../../../common'; import { @@ -138,7 +137,6 @@ function getFunctionDefinitions({ functionClient, functionLimitExceeded, disableFunctions, - scope, }: { functionClient: ChatFunctionClient; functionLimitExceeded: boolean; @@ -147,14 +145,13 @@ function getFunctionDefinitions({ | { except: string[]; }; - scope: AssistantScope; }) { if (functionLimitExceeded || disableFunctions === true) { return []; } let systemFunctions = functionClient - .getFunctions({ scope }) + .getFunctions() .map((fn) => fn.definition) .filter( (def) => @@ -187,7 +184,6 @@ export function continueConversation({ disableFunctions, tracer, connectorId, - scope, useSimulatedFunctionCalling, }: { messages: Message[]; @@ -205,7 +201,6 @@ export function continueConversation({ }; tracer: LangTracer; connectorId: string; - scope: AssistantScope; useSimulatedFunctionCalling: boolean; }): Observable { let nextFunctionCallsLeft = functionCallsLeft; @@ -216,12 +211,11 @@ export function continueConversation({ functionLimitExceeded, functionClient, disableFunctions, - scope, }); const messagesWithUpdatedSystemMessage = replaceSystemMessage( getSystemMessageFromInstructions({ - applicationInstructions: functionClient.getInstructions(scope), + applicationInstructions: functionClient.getInstructions(), userInstructions, adHocInstructions, availableFunctionNames: definitions.map((def) => def.name), @@ -350,7 +344,6 @@ export function continueConversation({ disableFunctions, tracer, connectorId, - scope, useSimulatedFunctionCalling, }); }) diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/index.ts b/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/index.ts index f203dcc350bf..63e2ee240927 100644 --- a/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/index.ts +++ b/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/index.ts @@ -249,10 +249,10 @@ export class ObservabilityAIAssistantService { async getClient({ request, - scope, + scopes, }: { request: KibanaRequest; - scope?: AssistantScope; + scopes?: AssistantScope[]; }): Promise { const controller = new AbortController(); @@ -291,7 +291,7 @@ export class ObservabilityAIAssistantService { } : undefined, knowledgeBaseService: this.kbService!, - scope: scope || 'all', + scopes: scopes || ['all'], }); } @@ -300,11 +300,13 @@ export class ObservabilityAIAssistantService { signal, resources, client, + scopes, }: { screenContexts: ObservabilityAIAssistantScreenContextRequest[]; signal: AbortSignal; resources: RespondFunctionResources; client: ObservabilityAIAssistantClient; + scopes: AssistantScope[]; }): Promise { const fnClient = new ChatFunctionClient(screenContexts); @@ -313,6 +315,7 @@ export class ObservabilityAIAssistantService { functions: fnClient, resources, client, + scopes, }; await Promise.all( diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/types.ts b/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/types.ts index 4857189f2d15..b00da8d6518f 100644 --- a/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/types.ts +++ b/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/types.ts @@ -68,18 +68,13 @@ export interface FunctionHandler { export type InstructionOrCallback = InstructionOrPlainText | RegisterInstructionCallback; -export interface InstructionOrCallbackWithScopes { - instruction: InstructionOrCallback; - scopes: AssistantScope[]; -} - export type RegisterInstructionCallback = ({ availableFunctionNames, }: { availableFunctionNames: string[]; }) => InstructionOrPlainText | InstructionOrPlainText[] | undefined; -export type RegisterInstruction = (...instruction: InstructionOrCallbackWithScopes[]) => void; +export type RegisterInstruction = (...instruction: InstructionOrCallback[]) => void; export type RegisterFunction = < TParameters extends CompatibleJSONSchema = any, @@ -87,17 +82,14 @@ export type RegisterFunction = < TArguments = FromSchema >( definition: FunctionDefinition, - respond: RespondFunction, - scopes: AssistantScope[] + respond: RespondFunction ) => void; -export type FunctionHandlerRegistry = Map< - string, - { handler: FunctionHandler; scopes: AssistantScope[] } ->; +export type FunctionHandlerRegistry = Map; export type RegistrationCallback = ({}: { signal: AbortSignal; resources: RespondFunctionResources; client: ObservabilityAIAssistantClient; functions: ChatFunctionClient; + scopes: AssistantScope[]; }) => Promise; diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant/tsconfig.json b/x-pack/plugins/observability_solution/observability_ai_assistant/tsconfig.json index 63105b2a86c5..7c2f2212ee94 100644 --- a/x-pack/plugins/observability_solution/observability_ai_assistant/tsconfig.json +++ b/x-pack/plugins/observability_solution/observability_ai_assistant/tsconfig.json @@ -46,7 +46,7 @@ "@kbn/core-ui-settings-server", "@kbn/inference-plugin", "@kbn/management-settings-ids", - "@kbn/ai-assistant-common" + "@kbn/ai-assistant-common", ], "exclude": ["target/**/*"] } diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/public/hooks/use_nav_control_scope.ts b/x-pack/plugins/observability_solution/observability_ai_assistant_app/public/hooks/use_nav_control_scope.ts index 39080adc47d4..157fccfb8a1f 100644 --- a/x-pack/plugins/observability_solution/observability_ai_assistant_app/public/hooks/use_nav_control_scope.ts +++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/public/hooks/use_nav_control_scope.ts @@ -10,11 +10,12 @@ import { useAIAssistantAppService } from '@kbn/ai-assistant'; import { AssistantScope } from '@kbn/ai-assistant-common'; import { useObservable } from 'react-use/lib'; import { DEFAULT_APP_CATEGORIES } from '@kbn/core/public'; +import { isEqual } from 'lodash'; import { useKibana } from './use_kibana'; -const scopeUrlLookup: Record = { - [DEFAULT_APP_CATEGORIES.observability.id]: 'observability', - [DEFAULT_APP_CATEGORIES.enterpriseSearch.id]: 'search', +const scopeUrlLookup: Record = { + [DEFAULT_APP_CATEGORIES.observability.id]: ['observability'], + [DEFAULT_APP_CATEGORIES.enterpriseSearch.id]: ['search'], }; export function useNavControlScope() { @@ -31,11 +32,9 @@ export function useNavControlScope() { const currentCategoryId = (currentApplication && applications?.get(currentApplication)?.category?.id) || DEFAULT_APP_CATEGORIES.kibana.id; - const newScope = Object.entries(scopeUrlLookup).find( - ([categoryId]) => categoryId === currentCategoryId - )?.[1]; - if (newScope && newScope !== service.getScope()) { - service.setScope(newScope); + const newScopes = scopeUrlLookup[currentCategoryId]; + if (newScopes?.length && !isEqual(service.getScopes(), newScopes)) { + service.setScopes(newScopes); } }, [applications, currentApplication, service]); } diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/public/routes/conversations/conversation_view_with_props.tsx b/x-pack/plugins/observability_solution/observability_ai_assistant_app/public/routes/conversations/conversation_view_with_props.tsx index c57b8e2c66c7..2d28ee0adbaa 100644 --- a/x-pack/plugins/observability_solution/observability_ai_assistant_app/public/routes/conversations/conversation_view_with_props.tsx +++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/public/routes/conversations/conversation_view_with_props.tsx @@ -38,6 +38,7 @@ export function ConversationViewWithProps() { }, }) } + scopes={['observability']} /> ); } diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/scripts/evaluation/evaluation.ts b/x-pack/plugins/observability_solution/observability_ai_assistant_app/scripts/evaluation/evaluation.ts index 030994fa44ac..a01b276c37bd 100644 --- a/x-pack/plugins/observability_solution/observability_ai_assistant_app/scripts/evaluation/evaluation.ts +++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/scripts/evaluation/evaluation.ts @@ -100,7 +100,7 @@ function runEvaluations() { evaluationConnectorId: evaluationConnector.id!, persist: argv.persist, suite: mocha.suite, - scope: 'all', + scopes: ['all'], }); const header: string[][] = [ diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/scripts/evaluation/kibana_client.ts b/x-pack/plugins/observability_solution/observability_ai_assistant_app/scripts/evaluation/kibana_client.ts index cc1500168e36..f3b5ca357231 100644 --- a/x-pack/plugins/observability_solution/observability_ai_assistant_app/scripts/evaluation/kibana_client.ts +++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/scripts/evaluation/kibana_client.ts @@ -239,13 +239,13 @@ export class KibanaClient { evaluationConnectorId, persist, suite, - scope, + scopes, }: { connectorId: string; evaluationConnectorId: string; persist: boolean; suite?: Mocha.Suite; - scope: AssistantScope; + scopes: AssistantScope[]; }): ChatClient { function getMessages(message: string | Array): Array { if (typeof message === 'string') { @@ -373,7 +373,7 @@ export class KibanaClient { connectorId: connectorIdOverride || connectorId, functions: functions.map((fn) => pick(fn, 'name', 'description', 'parameters')), functionCall, - scope, + scopes, }; return that.axios.post( @@ -463,7 +463,7 @@ export class KibanaClient { connectorId, persist, title: currentTitle, - scope, + scopes, }, { responseType: 'stream', timeout: NaN } ) diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/alerts.ts b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/alerts.ts index 1d0056fa2f66..682f2e2a4b19 100644 --- a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/alerts.ts +++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/alerts.ts @@ -74,154 +74,155 @@ export function registerAlertsFunction({ functions, resources, pluginsStart, + scopes, }: FunctionRegistrationParameters) { - functions.registerFunction( - { - name: 'get_alerts_dataset_info', - visibility: FunctionVisibility.AssistantOnly, - description: `Use this function to get information about alerts data.`, - parameters: { - type: 'object', - properties: { - start: { - type: 'string', - description: - 'The start of the current time range, in datemath, like now-24h or an ISO timestamp', + if (scopes.includes('observability')) { + functions.registerFunction( + { + name: 'get_alerts_dataset_info', + visibility: FunctionVisibility.AssistantOnly, + description: `Use this function to get information about alerts data.`, + parameters: { + type: 'object', + properties: { + start: { + type: 'string', + description: + 'The start of the current time range, in datemath, like now-24h or an ISO timestamp', + }, + end: { + type: 'string', + description: + 'The end of the current time range, in datemath, like now-24h or an ISO timestamp', + }, }, - end: { - type: 'string', - description: - 'The end of the current time range, in datemath, like now-24h or an ISO timestamp', - }, - }, - } as const, - }, - async ( - { arguments: { start, end }, chat, messages }, - signal - ): Promise<{ - content: { - fields: string[]; - }; - }> => { - const core = await resources.context.core; + } as const, + }, + async ( + { arguments: { start, end }, chat, messages }, + signal + ): Promise<{ + content: { + fields: string[]; + }; + }> => { + const core = await resources.context.core; - const { fields } = await getRelevantFieldNames({ - index: `.alerts-observability*`, - messages, - esClient: core.elasticsearch.client.asInternalUser, - dataViews: await resources.plugins.dataViews.start(), - savedObjectsClient: core.savedObjects.client, - signal, - chat: ( - operationName, - { messages: nextMessages, functionCall, functions: nextFunctions } - ) => { - return chat(operationName, { - messages: nextMessages, - functionCall, - functions: nextFunctions, - signal, - }); - }, - }); + const { fields } = await getRelevantFieldNames({ + index: `.alerts-observability*`, + messages, + esClient: core.elasticsearch.client.asInternalUser, + dataViews: await resources.plugins.dataViews.start(), + savedObjectsClient: core.savedObjects.client, + signal, + chat: ( + operationName, + { messages: nextMessages, functionCall, functions: nextFunctions } + ) => { + return chat(operationName, { + messages: nextMessages, + functionCall, + functions: nextFunctions, + signal, + }); + }, + }); - return { - content: { - fields: fields.length === 0 ? defaultFields : fields, - }, - }; - }, - ['observability'] - ); + return { + content: { + fields: fields.length === 0 ? defaultFields : fields, + }, + }; + } + ); - functions.registerFunction( - { - name: 'alerts', - description: `Get alerts for Observability. Make sure get_alerts_dataset_info was called before. + functions.registerFunction( + { + name: 'alerts', + description: `Get alerts for Observability. Make sure get_alerts_dataset_info was called before. Use this to get open (and optionally recovered) alerts for Observability assets, like services, hosts or containers. Display the response in tabular format if appropriate. `, - descriptionForUser: 'Get alerts for Observability', - parameters: { - type: 'object', - properties: { - start: { - type: 'string', - description: 'The start of the time range, in Elasticsearch date math, like `now`.', - }, - end: { - type: 'string', - description: 'The end of the time range, in Elasticsearch date math, like `now-24h`.', + descriptionForUser: 'Get alerts for Observability', + parameters: { + type: 'object', + properties: { + start: { + type: 'string', + description: 'The start of the time range, in Elasticsearch date math, like `now`.', + }, + end: { + type: 'string', + description: 'The end of the time range, in Elasticsearch date math, like `now-24h`.', + }, + kqlFilter: { + type: 'string', + description: `Filter alerts by field:value pairs`, + }, + includeRecovered: { + type: 'boolean', + description: + 'Whether to include recovered/closed alerts. Defaults to false, which means only active alerts will be returned', + }, }, - kqlFilter: { - type: 'string', - description: `Filter alerts by field:value pairs`, - }, - includeRecovered: { - type: 'boolean', - description: - 'Whether to include recovered/closed alerts. Defaults to false, which means only active alerts will be returned', - }, - }, - required: ['start', 'end'], - } as const, - }, - async ( - { arguments: { start: startAsDatemath, end: endAsDatemath, filter, includeRecovered } }, - signal - ) => { - const alertsClient = await pluginsStart.ruleRegistry.getRacClientWithRequest( - resources.request as KibanaRequest - ); + required: ['start', 'end'], + } as const, + }, + async ( + { arguments: { start: startAsDatemath, end: endAsDatemath, filter, includeRecovered } }, + signal + ) => { + const alertsClient = await pluginsStart.ruleRegistry.getRacClientWithRequest( + resources.request as KibanaRequest + ); - const start = datemath.parse(startAsDatemath)!.valueOf(); - const end = datemath.parse(endAsDatemath)!.valueOf(); + const start = datemath.parse(startAsDatemath)!.valueOf(); + const end = datemath.parse(endAsDatemath)!.valueOf(); - const kqlQuery = !filter ? [] : [toElasticsearchQuery(fromKueryExpression(filter))]; + const kqlQuery = !filter ? [] : [toElasticsearchQuery(fromKueryExpression(filter))]; - const response = await alertsClient.find({ - featureIds: DEFAULT_FEATURE_IDS as unknown as string[], - query: { - bool: { - filter: [ - { - range: { - '@timestamp': { - gte: start, - lte: end, + const response = await alertsClient.find({ + featureIds: DEFAULT_FEATURE_IDS as unknown as string[], + query: { + bool: { + filter: [ + { + range: { + '@timestamp': { + gte: start, + lte: end, + }, }, }, - }, - ...kqlQuery, - ...(!includeRecovered - ? [ - { - term: { - [ALERT_STATUS]: ALERT_STATUS_ACTIVE, + ...kqlQuery, + ...(!includeRecovered + ? [ + { + term: { + [ALERT_STATUS]: ALERT_STATUS_ACTIVE, + }, }, - }, - ] - : []), - ], + ] + : []), + ], + }, }, - }, - size: 10, - }); + size: 10, + }); - // trim some fields - const alerts = response.hits.hits.map((hit) => - omit(hit._source, ...OMITTED_ALERT_FIELDS) - ) as unknown as ParsedTechnicalFields[]; + // trim some fields + const alerts = response.hits.hits.map((hit) => + omit(hit._source, ...OMITTED_ALERT_FIELDS) + ) as unknown as ParsedTechnicalFields[]; - return { - content: { - total: (response.hits as { total: { value: number } }).total.value, - alerts, - }, - }; - }, - ['observability'] - ); + return { + content: { + total: (response.hits as { total: { value: number } }).total.value, + alerts, + }, + }; + } + ); + } } diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/changes/index.ts b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/changes/index.ts index 71872782e27b..cc712b7bb9b4 100644 --- a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/changes/index.ts +++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/changes/index.ts @@ -25,131 +25,133 @@ export function registerChangesFunction({ context: { core: corePromise }, }, pluginsStart, + scopes, }: FunctionRegistrationParameters) { - functions.registerFunction( - { - name: CHANGES_FUNCTION_NAME, - description: 'Returns change points like spikes and dips for logs and metrics.', - parameters: changesFunctionParameters, - }, - async ({ - arguments: { start, end, logs = [], metrics = [] }, - }): Promise => { - if (logs.length === 0 && metrics.length === 0) { - throw new Error('No metrics or logs were defined'); - } + if (scopes.includes('observability')) { + functions.registerFunction( + { + name: CHANGES_FUNCTION_NAME, + description: 'Returns change points like spikes and dips for logs and metrics.', + parameters: changesFunctionParameters, + }, + async ({ + arguments: { start, end, logs = [], metrics = [] }, + }): Promise => { + if (logs.length === 0 && metrics.length === 0) { + throw new Error('No metrics or logs were defined'); + } - const core = await corePromise; + const core = await corePromise; - const logSourcesService = - await pluginsStart.logsDataAccess.services.logSourcesServiceFactory.getLogSourcesService( - core.savedObjects.client - ); - const logsIndexPattern = await logSourcesService.getFlattenedLogSources(); + const logSourcesService = + await pluginsStart.logsDataAccess.services.logSourcesServiceFactory.getLogSourcesService( + core.savedObjects.client + ); + const logsIndexPattern = await logSourcesService.getFlattenedLogSources(); - const client = createElasticsearchClient({ - client: core.elasticsearch.client.asCurrentUser, - logger, - inspect: logger.isLevelEnabled('debug'), - }); + const client = createElasticsearchClient({ + client: core.elasticsearch.client.asCurrentUser, + logger, + inspect: logger.isLevelEnabled('debug'), + }); - const commonFilters = [ - { - range: { - '@timestamp': { - gte: start, - lt: end, + const commonFilters = [ + { + range: { + '@timestamp': { + gte: start, + lt: end, + }, }, }, - }, - ]; + ]; - const dateHistogram: AggregationsAutoDateHistogramAggregation = { - field: '@timestamp', - buckets: 100, - }; + const dateHistogram: AggregationsAutoDateHistogramAggregation = { + field: '@timestamp', + buckets: 100, + }; - const [metricChanges, logChanges] = await Promise.all([ - Promise.all([ - ...metrics.map(async (metric) => { - const changes = await getMetricChanges({ - index: metric.index, - client, - filters: [ - ...commonFilters, - ...(metric.kqlFilter - ? [toElasticsearchQuery(fromKueryExpression(metric.kqlFilter))] - : []), - ], - groupBy: metric.groupBy ?? [], - type: metric.type || 'count', - field: metric.field, - dateHistogram, - }); + const [metricChanges, logChanges] = await Promise.all([ + Promise.all([ + ...metrics.map(async (metric) => { + const changes = await getMetricChanges({ + index: metric.index, + client, + filters: [ + ...commonFilters, + ...(metric.kqlFilter + ? [toElasticsearchQuery(fromKueryExpression(metric.kqlFilter))] + : []), + ], + groupBy: metric.groupBy ?? [], + type: metric.type || 'count', + field: metric.field, + dateHistogram, + }); - return changes.map((change) => ({ - name: metric.name, - ...change, - })); - }), - ]), - Promise.all([ - ...logs.map(async (log) => { - const changes = await getLogChanges({ - index: log.index || logsIndexPattern, - client, - filters: [ - ...commonFilters, - ...(log.kqlFilter - ? [toElasticsearchQuery(fromKueryExpression(log.kqlFilter))] - : []), - ], - field: log.field ?? 'message', - dateHistogram, - }); - return changes.map((change) => ({ - name: log.name, - ...change, - })); - }), - ]), - ]); + return changes.map((change) => ({ + name: metric.name, + ...change, + })); + }), + ]), + Promise.all([ + ...logs.map(async (log) => { + const changes = await getLogChanges({ + index: log.index || logsIndexPattern, + client, + filters: [ + ...commonFilters, + ...(log.kqlFilter + ? [toElasticsearchQuery(fromKueryExpression(log.kqlFilter))] + : []), + ], + field: log.field ?? 'message', + dateHistogram, + }); + return changes.map((change) => ({ + name: log.name, + ...change, + })); + }), + ]), + ]); - const allMetricChanges = orderBy(metricChanges.flat(), [ - (item) => ('p_value' in item.changes ? item.changes.p_value : Number.POSITIVE_INFINITY), - ]).slice(0, 25); + const allMetricChanges = orderBy(metricChanges.flat(), [ + (item) => ('p_value' in item.changes ? item.changes.p_value : Number.POSITIVE_INFINITY), + ]).slice(0, 25); - const allMetricChangesWithoutTimeseries = allMetricChanges.flat().map((metricChange) => { - return omit(metricChange, 'over_time'); - }); + const allMetricChangesWithoutTimeseries = allMetricChanges.flat().map((metricChange) => { + return omit(metricChange, 'over_time'); + }); - const allLogChanges = orderBy(logChanges.flat(), [ - (item) => ('p_value' in item.changes ? item.changes.p_value : Number.POSITIVE_INFINITY), - ]).slice(0, 25); + const allLogChanges = orderBy(logChanges.flat(), [ + (item) => ('p_value' in item.changes ? item.changes.p_value : Number.POSITIVE_INFINITY), + ]).slice(0, 25); - const allLogChangesWithoutTimeseries = allLogChanges.flat().map((logChange) => { - return omit(logChange, 'over_time'); - }); + const allLogChangesWithoutTimeseries = allLogChanges.flat().map((logChange) => { + return omit(logChange, 'over_time'); + }); - return { - content: { - description: `For each item, the user can see the type of change, the impact, the timestamp, the trend, and the label. + return { + content: { + description: `For each item, the user can see the type of change, the impact, the timestamp, the trend, and the label. Do not regurgitate these results back to the user. Instead, focus on the interesting changes, mention possible correlations or root causes, and suggest next steps to the user. "indeterminate" means that the system could not detect any changes.`, - changes: { - metrics: allMetricChangesWithoutTimeseries, - logs: allLogChangesWithoutTimeseries, + changes: { + metrics: allMetricChangesWithoutTimeseries, + logs: allLogChangesWithoutTimeseries, + }, }, - }, - data: { - changes: { - metrics: allMetricChanges, - logs: allLogChanges, + data: { + changes: { + metrics: allMetricChanges, + logs: allLogChanges, + }, }, - }, - }; - }, - ['observability'] - ); + }; + } + ); + } } diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/lens.ts b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/lens.ts index bb07d701f170..dbae57c08c9e 100644 --- a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/lens.ts +++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/lens.ts @@ -8,13 +8,9 @@ import type { ChatFunctionClient } from '@kbn/observability-ai-assistant-plugin/ import { lensFunctionDefinition } from '../../common/functions/lens'; export function registerLensFunction({ functions }: { functions: ChatFunctionClient }) { - functions.registerFunction( - lensFunctionDefinition, - async () => { - return { - content: {}, - }; - }, - ['all'] - ); + functions.registerFunction(lensFunctionDefinition, async () => { + return { + content: {}, + }; + }); } diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/index.ts b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/index.ts index 8f7eb7b6b4e1..3643c5436524 100644 --- a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/index.ts +++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/index.ts @@ -53,7 +53,7 @@ export function registerQueryFunction({ When the "visualize_query" function has been called, a visualization has been displayed to the user. DO NOT UNDER ANY CIRCUMSTANCES follow up a "visualize_query" function call with your own visualization attempt. If the "${EXECUTE_QUERY_NAME}" function has been called, summarize these results for the user. The user does not see a visualization in this case.` : undefined; - functions.registerInstruction({ instruction, scopes: ['all'] }); + functions.registerInstruction(instruction); functions.registerFunction( { @@ -103,8 +103,7 @@ export function registerQueryFunction({ rows, }, }; - }, - ['all'] + } ); functions.registerFunction( { @@ -188,7 +187,6 @@ export function registerQueryFunction({ return messageAddEvent; }) ); - }, - ['all'] + } ); } diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/visualize_esql.ts b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/visualize_esql.ts index bda75eafc9ad..4eeba0450e6e 100644 --- a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/visualize_esql.ts +++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/visualize_esql.ts @@ -61,7 +61,6 @@ export function registerVisualizeESQLFunction({ ], }, }; - }, - ['all'] + } ); } diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/rule_connector/index.ts b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/rule_connector/index.ts index d99e822484b6..19f1408275e1 100644 --- a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/rule_connector/index.ts +++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/rule_connector/index.ts @@ -154,12 +154,13 @@ async function executor( } const resources = await initResources(request); - const client = await resources.service.getClient({ request, scope: 'observability' }); + const client = await resources.service.getClient({ request, scopes: ['observability'] }); const functionClient = await resources.service.getFunctionClient({ signal: new AbortController().signal, resources, client, screenContexts: [], + scopes: ['observability'], }); const actionsClient = await ( await resources.plugins.actions.start() @@ -227,7 +228,7 @@ If available, include the link of the conversation at the end of your answer.` role: MessageRole.System, content: getSystemMessageFromInstructions({ availableFunctionNames: functionClient.getFunctions().map((fn) => fn.definition.name), - applicationInstructions: functionClient.getInstructions('observability'), + applicationInstructions: functionClient.getInstructions(), userInstructions: [], adHocInstructions: [], }), diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_management/README.md b/x-pack/plugins/observability_solution/observability_ai_assistant_management/README.md index 43e09378c728..39d0973b1a1f 100644 --- a/x-pack/plugins/observability_solution/observability_ai_assistant_management/README.md +++ b/x-pack/plugins/observability_solution/observability_ai_assistant_management/README.md @@ -1,3 +1,3 @@ # `observabilityAiAssistantManagement` plugin -The `observabilityAiAssistantManagement` plugin manages the `Ai Assistant for Observability` management section. +The `observabilityAiAssistantManagement` plugin manages the `Ai Assistant for Observability and Search` management section. diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_management/public/app.tsx b/x-pack/plugins/observability_solution/observability_ai_assistant_management/public/app.tsx index af8d41223e1d..4522e00fb37d 100644 --- a/x-pack/plugins/observability_solution/observability_ai_assistant_management/public/app.tsx +++ b/x-pack/plugins/observability_solution/observability_ai_assistant_management/public/app.tsx @@ -35,7 +35,7 @@ export const mountManagementSection = async ({ core, mountParams }: MountParams) coreStart.chrome.docTitle.change( i18n.translate('xpack.observabilityAiAssistantManagement.app.titleBar', { - defaultMessage: 'AI Assistant for Observability Settings', + defaultMessage: 'AI Assistant for Observability and Search Settings', }) ); diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_management/public/plugin.ts b/x-pack/plugins/observability_solution/observability_ai_assistant_management/public/plugin.ts index 53da619c7ad1..e2e69ef5600c 100644 --- a/x-pack/plugins/observability_solution/observability_ai_assistant_management/public/plugin.ts +++ b/x-pack/plugins/observability_solution/observability_ai_assistant_management/public/plugin.ts @@ -49,7 +49,7 @@ export class AiAssistantManagementObservabilityPlugin { home, management, observabilityAIAssistant }: SetupDependencies ): AiAssistantManagementObservabilityPluginSetup { const title = i18n.translate('xpack.observabilityAiAssistantManagement.app.title', { - defaultMessage: 'AI Assistant for Observability', + defaultMessage: 'AI Assistant for Observability and Search', }); if (home) { @@ -57,7 +57,7 @@ export class AiAssistantManagementObservabilityPlugin id: 'ai_assistant_observability', title, description: i18n.translate('xpack.observabilityAiAssistantManagement.app.description', { - defaultMessage: 'Manage your AI Assistant for Observability.', + defaultMessage: 'Manage your AI Assistant for Observability and Search.', }), icon: 'sparkles', path: '/app/management/kibana/ai-assistant/observability', diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_management/public/routes/components/settings_page.tsx b/x-pack/plugins/observability_solution/observability_ai_assistant_management/public/routes/components/settings_page.tsx index c329e6de8e67..075aaeb0aeb7 100644 --- a/x-pack/plugins/observability_solution/observability_ai_assistant_management/public/routes/components/settings_page.tsx +++ b/x-pack/plugins/observability_solution/observability_ai_assistant_management/public/routes/components/settings_page.tsx @@ -40,7 +40,7 @@ export function SettingsPage() { text: i18n.translate( 'xpack.observabilityAiAssistantManagement.breadcrumb.serverless.observability', { - defaultMessage: 'AI Assistant for Observability Settings', + defaultMessage: 'AI Assistant for Observability and Search Settings', } ), }, diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_management/public/routes/components/settings_tab/settings_tab.tsx b/x-pack/plugins/observability_solution/observability_ai_assistant_management/public/routes/components/settings_tab/settings_tab.tsx index 4ec17f34610e..71b758f27f58 100644 --- a/x-pack/plugins/observability_solution/observability_ai_assistant_management/public/routes/components/settings_tab/settings_tab.tsx +++ b/x-pack/plugins/observability_solution/observability_ai_assistant_management/public/routes/components/settings_tab/settings_tab.tsx @@ -85,7 +85,7 @@ export function SettingsTab() { 'xpack.observabilityAiAssistantManagement.settingsPage.euiDescribedFormGroup.inOrderToUseLabel', { defaultMessage: - 'In order to use the Observability AI Assistant you must set up a Generative AI connector.', + 'In order to use the AI Assistant you must set up a Generative AI connector.', } )} > diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_management/tsconfig.json b/x-pack/plugins/observability_solution/observability_ai_assistant_management/tsconfig.json index d8a03acbae61..12148ec01472 100644 --- a/x-pack/plugins/observability_solution/observability_ai_assistant_management/tsconfig.json +++ b/x-pack/plugins/observability_solution/observability_ai_assistant_management/tsconfig.json @@ -21,7 +21,7 @@ "@kbn/observability-shared-plugin", "@kbn/config-schema", "@kbn/core-ui-settings-common", - "@kbn/logs-data-access-plugin" + "@kbn/logs-data-access-plugin", ], "exclude": ["target/**/*"] } diff --git a/x-pack/plugins/search_assistant/public/components/routes/conversations/conversation_view_with_props.tsx b/x-pack/plugins/search_assistant/public/components/routes/conversations/conversation_view_with_props.tsx index f0e4a61895f3..28ed6d00863f 100644 --- a/x-pack/plugins/search_assistant/public/components/routes/conversations/conversation_view_with_props.tsx +++ b/x-pack/plugins/search_assistant/public/components/routes/conversations/conversation_view_with_props.tsx @@ -30,7 +30,7 @@ export function ConversationViewWithProps() { getConversationHref={(id: string) => http?.basePath.prepend(`/app/searchAssistant/conversations/${id || ''}`) || '' } - scope="search" + scopes={['search']} /> ); } diff --git a/x-pack/plugins/search_assistant/server/functions/index.ts b/x-pack/plugins/search_assistant/server/functions/index.ts index d1eef69615a6..46da6767f359 100644 --- a/x-pack/plugins/search_assistant/server/functions/index.ts +++ b/x-pack/plugins/search_assistant/server/functions/index.ts @@ -9,9 +9,10 @@ import { RegistrationCallback } from '@kbn/observability-ai-assistant-plugin/ser export const registerFunctions: (isServerless: boolean) => RegistrationCallback = (isServerless: boolean) => - async ({ client, functions, resources, signal }) => { - functions.registerInstruction({ - instruction: `You are a helpful assistant for Elasticsearch. Your goal is to help Elasticsearch users accomplish tasks using Kibana and Elasticsearch. You can help them construct queries, index data, search data, use Elasticsearch APIs, generate sample data, visualise and analyze data. + async ({ client, functions, resources, signal, scopes }) => { + if (scopes.includes('search')) { + functions.registerInstruction( + `You are a helpful assistant for Elasticsearch. Your goal is to help Elasticsearch users accomplish tasks using Kibana and Elasticsearch. You can help them construct queries, index data, search data, use Elasticsearch APIs, generate sample data, visualise and analyze data. It's very important to not assume what the user means. Ask them for clarification if needed. @@ -27,10 +28,10 @@ export const registerFunctions: (isServerless: boolean) => RegistrationCallback If you want to call a function or tool, only call it a single time per message. Wait until the function has been executed and its results returned to you, before executing the same tool or another tool again if needed. - The user is able to change the language which they want you to reply in on the settings page of the AI Assistant for Observability, which can be found in the ${ + The user is able to change the language which they want you to reply in on the settings page of the AI Assistant for Observability and Search, which can be found in the ${ isServerless ? `Project settings.` : `Stack Management app under the option AI Assistants` }. - If the user asks how to change the language, reply in the same language the user asked in.`, - scopes: ['search'], - }); + If the user asks how to change the language, reply in the same language the user asked in.` + ); + } }; diff --git a/x-pack/plugins/serverless_observability/public/plugin.ts b/x-pack/plugins/serverless_observability/public/plugin.ts index 25cb2dae3819..05d598b2b3a7 100644 --- a/x-pack/plugins/serverless_observability/public/plugin.ts +++ b/x-pack/plugins/serverless_observability/public/plugin.ts @@ -59,7 +59,7 @@ export class ServerlessObservabilityPlugin observabilityAiAssistantManagement: { category: appCategories.OTHER, title: i18n.translate('xpack.serverlessObservability.aiAssistantManagementTitle', { - defaultMessage: 'AI Assistant for Observability Settings', + defaultMessage: 'AI Assistant for Observability and Search Settings', }), description: i18n.translate( 'xpack.serverlessObservability.aiAssistantManagementDescription', diff --git a/x-pack/test/observability_ai_assistant_api_integration/tests/chat/chat.spec.ts b/x-pack/test/observability_ai_assistant_api_integration/tests/chat/chat.spec.ts index e0e67066b477..d514d6ddb702 100644 --- a/x-pack/test/observability_ai_assistant_api_integration/tests/chat/chat.spec.ts +++ b/x-pack/test/observability_ai_assistant_api_integration/tests/chat/chat.spec.ts @@ -59,7 +59,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { messages, connectorId: 'does not exist', functions: [], - scope: 'all', + scopes: ['all'], }) .expect(404); }); @@ -88,7 +88,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { messages, connectorId, functions: [], - scope: 'all', + scopes: ['all'], }) .pipe(passThrough); @@ -146,7 +146,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { messages, connectorId, functions: [], - scope: 'all', + scopes: ['all'], }) .expect(200) .pipe(passThrough); diff --git a/x-pack/test/observability_ai_assistant_api_integration/tests/complete/complete.spec.ts b/x-pack/test/observability_ai_assistant_api_integration/tests/complete/complete.spec.ts index aaba5fbc7ba9..a7606d21408c 100644 --- a/x-pack/test/observability_ai_assistant_api_integration/tests/complete/complete.spec.ts +++ b/x-pack/test/observability_ai_assistant_api_integration/tests/complete/complete.spec.ts @@ -84,7 +84,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { connectorId, persist: true, screenContexts: params.screenContexts || [], - scope: 'all', + scopes: ['all'], }) .then((response) => resolve(response)) .catch((err) => reject(err)); @@ -137,7 +137,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { connectorId, persist: false, screenContexts: [], - scope: 'all', + scopes: ['all'], }) .pipe(passThrough); @@ -404,7 +404,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { connectorId, persist: true, screenContexts: [], - scope: 'observability', + scopes: ['observability'], }, }, }) @@ -447,7 +447,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { persist: true, screenContexts: [], conversationId, - scope: 'observability', + scopes: ['observability'], }, }, }) diff --git a/x-pack/test/observability_ai_assistant_api_integration/tests/complete/functions/helpers.ts b/x-pack/test/observability_ai_assistant_api_integration/tests/complete/functions/helpers.ts index dadf270f0df4..b83221869bae 100644 --- a/x-pack/test/observability_ai_assistant_api_integration/tests/complete/functions/helpers.ts +++ b/x-pack/test/observability_ai_assistant_api_integration/tests/complete/functions/helpers.ts @@ -33,14 +33,14 @@ export async function invokeChatCompleteWithFunctionRequest({ connectorId, observabilityAIAssistantAPIClient, functionCall, - scope, + scopes, }: { connectorId: string; observabilityAIAssistantAPIClient: Awaited< ReturnType >; functionCall: Message['message']['function_call']; - scope?: AssistantScope; + scopes?: AssistantScope[]; }) { const { body } = await observabilityAIAssistantAPIClient .editorUser({ @@ -60,7 +60,7 @@ export async function invokeChatCompleteWithFunctionRequest({ connectorId, persist: false, screenContexts: [], - scope: scope || 'observability', + scopes: scopes || ['observability' as AssistantScope], }, }, }) diff --git a/x-pack/test/observability_ai_assistant_api_integration/tests/knowledge_base/knowledge_base_user_instructions.spec.ts b/x-pack/test/observability_ai_assistant_api_integration/tests/knowledge_base/knowledge_base_user_instructions.spec.ts index bf2eef14db55..afec0a0ad636 100644 --- a/x-pack/test/observability_ai_assistant_api_integration/tests/knowledge_base/knowledge_base_user_instructions.spec.ts +++ b/x-pack/test/observability_ai_assistant_api_integration/tests/knowledge_base/knowledge_base_user_instructions.spec.ts @@ -249,7 +249,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { connectorId, persist: true, screenContexts: [], - scope: 'observability', + scopes: ['observability'], }, }, }).expect(200); diff --git a/x-pack/test_serverless/api_integration/test_suites/observability/ai_assistant/tests/chat/chat.spec.ts b/x-pack/test_serverless/api_integration/test_suites/observability/ai_assistant/tests/chat/chat.spec.ts index d30839b60b0f..582f544c7dbf 100644 --- a/x-pack/test_serverless/api_integration/test_suites/observability/ai_assistant/tests/chat/chat.spec.ts +++ b/x-pack/test_serverless/api_integration/test_suites/observability/ai_assistant/tests/chat/chat.spec.ts @@ -84,7 +84,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { messages, connectorId: 'does not exist', functions: [], - scope: 'all', + scopes: ['all'], }) .expect(404); }); @@ -114,7 +114,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { messages, connectorId, functions: [], - scope: 'all', + scopes: ['all'], }) .pipe(passThrough); @@ -174,7 +174,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { messages, connectorId, functions: [], - scope: 'all', + scopes: ['all'], }) .expect(200) .pipe(passThrough); diff --git a/x-pack/test_serverless/api_integration/test_suites/observability/ai_assistant/tests/complete/complete.spec.ts b/x-pack/test_serverless/api_integration/test_suites/observability/ai_assistant/tests/complete/complete.spec.ts index 970b99ab3561..a95c07bce0eb 100644 --- a/x-pack/test_serverless/api_integration/test_suites/observability/ai_assistant/tests/complete/complete.spec.ts +++ b/x-pack/test_serverless/api_integration/test_suites/observability/ai_assistant/tests/complete/complete.spec.ts @@ -91,7 +91,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { connectorId, persist: true, screenContexts: params.screenContexts || [], - scope: 'all', + scopes: ['all'], }) .then((response: Response) => resolve(response)) .catch((err: Error) => reject(err)); @@ -164,7 +164,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { connectorId, persist: false, screenContexts: [], - scope: 'all', + scopes: ['all'], }) .pipe(passThrough); @@ -436,7 +436,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { connectorId, persist: true, screenContexts: [], - scope: 'all', + scopes: ['all'], }, }, }) @@ -483,7 +483,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { persist: true, screenContexts: [], conversationId, - scope: 'all', + scopes: ['all'], }, }, }) diff --git a/x-pack/test_serverless/api_integration/test_suites/observability/ai_assistant/tests/complete/functions/helpers.ts b/x-pack/test_serverless/api_integration/test_suites/observability/ai_assistant/tests/complete/functions/helpers.ts index 857fa71aac9e..758046de72f2 100644 --- a/x-pack/test_serverless/api_integration/test_suites/observability/ai_assistant/tests/complete/functions/helpers.ts +++ b/x-pack/test_serverless/api_integration/test_suites/observability/ai_assistant/tests/complete/functions/helpers.ts @@ -36,12 +36,12 @@ export async function invokeChatCompleteWithFunctionRequest({ functionCall, roleAuthc, internalReqHeader, - scope, + scopes, }: { connectorId: string; observabilityAIAssistantAPIClient: ObservabilityAIAssistantApiClient; functionCall: Message['message']['function_call']; - scope?: AssistantScope; + scopes?: AssistantScope[]; roleAuthc: RoleCredentials; internalReqHeader: InternalRequestHeader; }) { @@ -65,7 +65,7 @@ export async function invokeChatCompleteWithFunctionRequest({ connectorId, persist: false, screenContexts: [], - scope: 'observability', + scopes: scopes || (['observability'] as AssistantScope[]), }, }, }) diff --git a/x-pack/test_serverless/api_integration/test_suites/observability/ai_assistant/tests/knowledge_base/knowledge_base_user_instructions.spec.ts b/x-pack/test_serverless/api_integration/test_suites/observability/ai_assistant/tests/knowledge_base/knowledge_base_user_instructions.spec.ts index 86232035d0c5..4181b6a14ffd 100644 --- a/x-pack/test_serverless/api_integration/test_suites/observability/ai_assistant/tests/knowledge_base/knowledge_base_user_instructions.spec.ts +++ b/x-pack/test_serverless/api_integration/test_suites/observability/ai_assistant/tests/knowledge_base/knowledge_base_user_instructions.spec.ts @@ -266,7 +266,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { connectorId, persist: true, screenContexts: [], - scope: 'observability', + scopes: ['observability'], }, }, roleAuthc,