diff --git a/docs/developer/plugin-list.asciidoc b/docs/developer/plugin-list.asciidoc index a99e030a4adc1..b6ba24df78976 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 5ff10f3b2c4ba..c4a537042f605 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 a8175f2f0bce8..4b74b55e571ab 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 ff8f627b10dac..03c3aae1dbd0c 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 b7d5831e14f94..fe71a9585dd1e 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 8bc8f54e9ac8d..a619164517e29 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 41bb8a4906c11..ddfa6c415140a 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 02c6018a4216c..dffca3addd34e 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 b40507a09719e..d65fa19991334 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 ed752e5011293..0a8bdeed3c823 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 d7c332dc042d3..7913b3ce78957 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 a27bdcc3dc813..72fb4c8c7d200 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 8a95fe9c89869..478c96e77e568 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 f768c30d8af21..b24c24425b413 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 dc9152d268adb..63bdbd422c658 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 816f1e17e0499..6a65e6126ff22 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 5b93005a7fc26..bd786e9ba3c75 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 08bf1414b15b5..562749f24cc9d 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 28e2a3709a355..e21eda9e09c66 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 48884664ec646..86aeb8f519e87 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 0731f26476da3..7be61a65e263d 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 cb9cc7d941147..2753b750dc288 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 9d8338f2d3892..dd69c8e309989 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 6e03683b44064..6cc08054e67a3 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 05e0a89c4b7ad..f059196f2e681 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 04520cb70a588..e3ccb38319896 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 9af669242e436..0559d65a14a81 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 22d1b9c792f7f..07f967a4028d9 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 19d51bf4c66d1..004ad25aa4a86 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 b13d81faa3a3b..becc21f59c5f4 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 61448d297e4d3..fd57968617187 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 71a0cfa4bbde0..6008b53dd42c5 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 bfe04cb56e8cf..0088e35a6f6af 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 9b20d364ef7d9..57cac3a4e0c0f 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 a5333ee1a7ffc..0313d29d3b209 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 f55a8ba432922..f939e3a79799b 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 a4c34c5caa5a3..8865861d81f45 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 136cc68497563..8bc88cca10b01 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 b31e33148454c..8a61248d4e70e 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 ea265c580b50f..3d83c470de0c5 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 4413e4fa8b634..97def121e8593 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 5a7cf81a40122..0476bda1af8a2 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 4eb0e54f9febe..19a3dd827107b 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 7ebd9d66bf30f..66204c96f31cb 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 f203dcc350bfd..63e2ee240927c 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 4857189f2d156..b00da8d6518fa 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 63105b2a86c59..7c2f2212ee946 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 39080adc47d48..157fccfb8a1f2 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 c57b8e2c66c71..2d28ee0adbaa6 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 030994fa44acf..a01b276c37bdf 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 cc1500168e368..f3b5ca357231b 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 1d0056fa2f66c..682f2e2a4b19b 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 71872782e27b0..cc712b7bb9b4f 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 bb07d701f1708..dbae57c08c9e2 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 8f7eb7b6b4e1f..3643c54365248 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 bda75eafc9ade..4eeba0450e6e4 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 d99e822484b67..19f1408275e1f 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 43e09378c7288..39d0973b1a1f0 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 af8d41223e1d8..4522e00fb37d2 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 53da619c7ad1c..e2e69ef5600cf 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 c329e6de8e673..075aaeb0aeb75 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 4ec17f34610e2..71b758f27f580 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 d8a03acbae61b..12148ec014725 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 f0e4a61895f39..28ed6d00863f3 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 d1eef69615a61..46da6767f359d 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 25cb2dae38192..05d598b2b3a7e 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 e0e67066b4777..d514d6ddb7025 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 aaba5fbc7ba99..a7606d21408c5 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 dadf270f0df41..b83221869baec 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 bf2eef14db553..afec0a0ad6362 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 d30839b60b0f1..582f544c7dbfa 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 970b99ab35613..a95c07bce0eb9 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 857fa71aac9e6..758046de72f2b 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 86232035d0c58..4181b6a14ffde 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,