diff --git a/x-pack/plugins/observability_solution/apm/public/components/app/error_group_details/error_sampler/error_sample_contextual_insight.tsx b/x-pack/plugins/observability_solution/apm/public/components/app/error_group_details/error_sampler/error_sample_contextual_insight.tsx index 0f370aabe1731..2e91865083b8c 100644 --- a/x-pack/plugins/observability_solution/apm/public/components/app/error_group_details/error_sampler/error_sample_contextual_insight.tsx +++ b/x-pack/plugins/observability_solution/apm/public/components/app/error_group_details/error_sampler/error_sample_contextual_insight.tsx @@ -38,19 +38,19 @@ export function ErrorSampleContextualInsight({ instructions: `I'm an SRE. I am looking at an exception and trying to understand what it means. Your task is to describe what the error means and what it could be caused by. - + The error occurred on a service called ${serviceName}, which is a ${runtimeName} service written in ${languageName}. The runtime version is ${runtimeVersion}. - + The request it occurred for is called ${transactionName}. - + ${ logStacktrace ? `The log stacktrace: ${logStacktrace}` : '' } - + ${ exceptionStacktrace ? `The exception stacktrace: 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 72fb4c8c7d200..a27bdcc3dc813 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,6 +164,7 @@ 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 9b80d694151ff..8a95fe9c89869 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 @@ -22,16 +22,16 @@ export function registerGetApmDownstreamDependenciesFunction({ registerFunction( { name: 'get_apm_downstream_dependencies', - description: `Get the downstream dependencies (services or uninstrumented backends) for a - service. This allows you to map the downstream dependency name to a service, by - returning both span.destination.service.resource and service.name. Use this to + description: `Get the downstream dependencies (services or uninstrumented backends) for a + service. This allows you to map the downstream dependency name to a service, by + returning both span.destination.service.resource and service.name. Use this to drilldown further if needed.`, descriptionForUser: i18n.translate( 'xpack.apm.observabilityAiAssistant.functions.registerGetApmDownstreamDependencies.descriptionForUser', { - defaultMessage: `Get the downstream dependencies (services or uninstrumented backends) for a - service. This allows you to map the dowstream dependency name to a service, by - returning both span.destination.service.resource and service.name. Use this to + defaultMessage: `Get the downstream dependencies (services or uninstrumented backends) for a + service. This allows you to map the dowstream dependency name to a service, by + returning both span.destination.service.resource and service.name. Use this to drilldown further if needed.`, } ), @@ -67,6 +67,7 @@ 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 b24c24425b413..f768c30d8af21 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,6 +84,7 @@ 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 63bdbd422c658..dc9152d268adb 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,7 +138,8 @@ export function registerGetApmTimeseriesFunction({ content: timeseries.map((series): Omit => omit(series, 'data')), data: timeseries, }; - } + }, + ['observability'] ); } diff --git a/x-pack/plugins/observability_solution/infra/public/components/asset_details/tabs/processes/process_row.tsx b/x-pack/plugins/observability_solution/infra/public/components/asset_details/tabs/processes/process_row.tsx index 17657e9366d7d..93d6b6e8efd91 100644 --- a/x-pack/plugins/observability_solution/infra/public/components/asset_details/tabs/processes/process_row.tsx +++ b/x-pack/plugins/observability_solution/infra/public/components/asset_details/tabs/processes/process_row.tsx @@ -48,7 +48,7 @@ export const ContextualInsightProcessRow = ({ command }: { command: string }) => with the arguments to the process you should then explain its arguments and how they influence the behaviour of the process. If I do not provide any arguments then explain the behaviour of the process when no arguments are provided. - + Here is an example with arguments. Process: metricbeat -c /etc/metricbeat.yml -d autodiscover,kafka -e -system.hostfs=/hostfs Explanation: Metricbeat is part of the Elastic Stack. It is a lightweight shipper that you can install on your 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 bd786e9ba3c75..8bdb76388cb56 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 @@ -8,6 +8,7 @@ import type { JSONSchema7TypeName } from 'json-schema'; import type { Observable } from 'rxjs'; import { ChatCompletionChunkEvent, MessageAddEvent } from '../conversation_complete'; import { FunctionVisibility } from './function_visibility'; +import { AssistantScope } from '../types'; export { FunctionVisibility }; type JSONSchemaOrPrimitive = CompatibleJSONSchema | string | number | boolean; @@ -41,6 +42,7 @@ export interface FunctionDefinition; diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant/common/types.ts b/x-pack/plugins/observability_solution/observability_ai_assistant/common/types.ts index 7f8a2be739dd1..2c65ee6dc06b2 100644 --- a/x-pack/plugins/observability_solution/observability_ai_assistant/common/types.ts +++ b/x-pack/plugins/observability_solution/observability_ai_assistant/common/types.ts @@ -157,3 +157,5 @@ export interface ObservabilityAIAssistantScreenContext { actions?: Array>; starterPrompts?: StarterPrompt[]; } + +export type AssistantScope = 'observability' | 'search' | 'all'; 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 acd5e2710c9dd..97dac18d4f733 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,6 +56,7 @@ function ChatContent({ }) { const service = useObservabilityAIAssistant(); const chatService = useObservabilityAIAssistantChatService(); + const { scope } = service; const initialMessagesRef = useRef(initialMessages); @@ -68,6 +69,7 @@ function ChatContent({ initialMessages, persist: false, disableFunctions: true, + scope, }); 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 1f36b49175eea..7a88e4bd5486b 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 @@ -80,6 +80,7 @@ describe('useChat', () => { service: { getScreenContexts: () => [], } as unknown as ObservabilityAIAssistantService, + scope: 'observability', } as UseChatProps, }); }); @@ -109,6 +110,7 @@ describe('useChat', () => { service: { getScreenContexts: () => [], } as unknown as ObservabilityAIAssistantService, + scope: '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 712b102c36f85..b51b33797e285 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 @@ -10,6 +10,7 @@ import { merge } from 'lodash'; import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { AbortError } from '@kbn/kibana-utils-plugin/common'; import type { NotificationsStart } from '@kbn/core/public'; +import { AssistantScope } from '../../common/types'; import { MessageRole, type Message, @@ -55,6 +56,7 @@ interface UseChatPropsWithoutContext { disableFunctions?: boolean; onConversationUpdate?: (event: ConversationCreateEvent | ConversationUpdateEvent) => void; onChatComplete?: (messages: Message[]) => void; + scope: AssistantScope; } export type UseChatProps = Omit; @@ -70,6 +72,7 @@ function useChatWithoutContext({ onChatComplete, persist, disableFunctions, + scope, }: UseChatPropsWithoutContext): UseChatResult { const [chatState, setChatState] = useState(ChatState.Ready); const systemMessage = useMemo(() => { @@ -161,6 +164,7 @@ function useChatWithoutContext({ disableFunctions: disableFunctions ?? false, signal: abortControllerRef.current.signal, conversationId, + scope, }); function getPendingMessages() { @@ -259,6 +263,7 @@ function useChatWithoutContext({ disableFunctions, service, systemMessage, + scope, ] ); 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 b5d85be11dfe2..349f044206267 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 @@ -60,6 +60,7 @@ export const mockService: ObservabilityAIAssistantService = { predefinedConversation$: new Observable(), }, navigate: async () => of(), + scope: '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 fd2a60dcdfc3a..05c4bc93cd2ae 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 @@ -65,6 +65,7 @@ export class ObservabilityAIAssistantPlugin coreStart.application.capabilities.observabilityAIAssistant[ aiAssistantCapabilities.show ] === true, + scope: 'observability', })); const withProviders =

( 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 59b3e7c9087d9..9d8338f2d3892 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,6 +102,7 @@ describe('complete', () => { disableFunctions: false, signal: new AbortController().signal, ...params, + scope: '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 85f5f0397ff31..6e03683b44064 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,6 +43,7 @@ export function complete( disableFunctions, signal, instructions, + scope, }: { client: Pick; getScreenContexts: () => ObservabilityAIAssistantScreenContext[]; @@ -65,6 +66,7 @@ export function complete( screenContexts, conversationId, instructions, + scope, }, }, }).pipe(shareReplay()); @@ -131,6 +133,7 @@ export function complete( persist, disableFunctions, instructions, + scope, }, 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 6d0332602c869..c495d33301882 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 @@ -70,6 +70,7 @@ describe('createChatService', () => { apiClient: clientSpy, registrations: [], signal: new AbortController().signal, + scope: 'observability', }); }); @@ -79,7 +80,12 @@ describe('createChatService', () => { describe('chat', () => { function chat({ signal }: { signal: AbortSignal } = { signal: new AbortController().signal }) { - return service.chat('my_test', { signal, messages: [], connectorId: '' }); + return service.chat('my_test', { + signal, + messages: [], + connectorId: '', + scope: 'observability', + }); } it('correctly parses a stream of JSON lines', async () => { 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 fc8c06ef55f51..e1829308fe69a 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 @@ -23,6 +23,7 @@ import { throwError, timestamp, } from 'rxjs'; +import { AssistantScope } from '../../common/types'; import { ChatCompletionChunkEvent, Message, MessageRole } from '../../common'; import { StreamingChatResponseEventType, @@ -137,19 +138,26 @@ export async function createChatService({ signal: setupAbortSignal, registrations, apiClient, + scope, }: { analytics: AnalyticsServiceStart; signal: AbortSignal; registrations: ChatRegistrationRenderFunction[]; apiClient: ObservabilityAIAssistantAPIClient; + scope: AssistantScope; }): Promise { const functionRegistry: FunctionRegistry = new Map(); const renderFunctionRegistry: Map> = new Map(); const [{ functionDefinitions, systemMessage }] = await Promise.all([ - apiClient('GET /internal/observability_ai_assistant/functions', { + apiClient('GET /internal/observability_ai_assistant/{scope}/functions', { signal: setupAbortSignal, + params: { + path: { + scope, + }, + }, }), ...registrations.map((registration) => { return registration({ @@ -196,6 +204,7 @@ export async function createChatService({ connectorId, functionCall, functions: functions ?? [], + scope, }, }, signal, @@ -228,6 +237,7 @@ export async function createChatService({ signal, client, instructions, + scope, }, ({ params }) => { return callStreamingApi('POST /internal/observability_ai_assistant/chat/complete', { 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 7232078d2efe8..ae95cb26148e9 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 @@ -8,7 +8,11 @@ import type { AnalyticsServiceStart, CoreStart } from '@kbn/core/public'; import { compact, without } from 'lodash'; import { BehaviorSubject, debounceTime, filter, lastValueFrom, of, Subject, take } from 'rxjs'; -import type { Message, ObservabilityAIAssistantScreenContext } from '../../common/types'; +import type { + AssistantScope, + Message, + ObservabilityAIAssistantScreenContext, +} from '../../common/types'; import { createFunctionRequestMessage } from '../../common/utils/create_function_request_message'; import { createFunctionResponseMessage } from '../../common/utils/create_function_response_message'; import { createCallObservabilityAIAssistantAPI } from '../api'; @@ -19,10 +23,12 @@ export function createService({ analytics, coreStart, enabled, + scope, }: { analytics: AnalyticsServiceStart; coreStart: CoreStart; enabled: boolean; + scope: AssistantScope; }): ObservabilityAIAssistantService { const apiClient = createCallObservabilityAIAssistantAPI(coreStart); @@ -42,7 +48,7 @@ export function createService({ }, start: async ({ signal }) => { const mod = await import('./create_chat_service'); - return await mod.createChatService({ analytics, apiClient, signal, registrations }); + return await mod.createChatService({ analytics, apiClient, signal, registrations, scope }); }, callApi: apiClient, getScreenContexts() { @@ -89,5 +95,6 @@ export function createService({ }, predefinedConversation$: predefinedConversation$.asObservable(), }, + 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 d3b52f6803621..f48d7868def87 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 @@ -52,4 +52,5 @@ export const createStorybookService = (): ObservabilityAIAssistantService => ({ predefinedConversation$: new Observable(), }, navigate: async () => of(), + scope: 'observability', }); 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 71a8a7e402748..8b265d433f515 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 @@ -19,6 +19,7 @@ import type { ObservabilityAIAssistantScreenContext, PendingMessage, AdHocInstruction, + AssistantScope, } from '../common/types'; import type { TelemetryEventTypeWithPayload } from './analytics'; import type { ObservabilityAIAssistantAPIClient } from './api'; @@ -52,6 +53,7 @@ export interface ObservabilityAIAssistantChatService { functions?: Array>; functionCall?: string; signal: AbortSignal; + scope: AssistantScope; } ) => Observable; complete: (options: { @@ -67,8 +69,13 @@ export interface ObservabilityAIAssistantChatService { }; signal: AbortSignal; instructions?: AdHocInstruction[]; + scope: AssistantScope; }) => Observable; - getFunctions: (options?: { contexts?: string[]; filter?: string }) => FunctionDefinition[]; + getFunctions: (options?: { + contexts?: string[]; + filter?: string; + scope: AssistantScope; + }) => FunctionDefinition[]; hasFunction: (name: string) => boolean; getSystemMessage: () => Message; hasRenderFunction: (name: string) => boolean; @@ -76,7 +83,8 @@ export interface ObservabilityAIAssistantChatService { name: string, args: string | undefined, response: { data?: string; content?: string }, - onActionClick: ChatActionClickHandler + onActionClick: ChatActionClickHandler, + scope?: AssistantScope ) => React.ReactNode; } @@ -94,6 +102,7 @@ export interface ObservabilityAIAssistantService { getScreenContexts: () => ObservabilityAIAssistantScreenContext[]; conversations: ObservabilityAIAssistantConversationService; navigate: (callback: () => void) => Promise>; + scope: 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 fd57968617187..61448d297e4d3 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,6 +115,7 @@ 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 6008b53dd42c5..71a0cfa4bbde0 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,6 +48,7 @@ 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 0088e35a6f6af..bfe04cb56e8cf 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,6 +38,7 @@ 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 57cac3a4e0c0f..9b20d364ef7d9 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,6 +94,7 @@ 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 5b16b79bd9980..a5333ee1a7ffc 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 @@ -38,7 +38,8 @@ export const registerFunctions: RegistrationCallback = async ({ const isServerless = !!resources.plugins.serverless; - 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. @@ -48,59 +49,64 @@ export const registerFunctions: RegistrationCallback = async ({ /\". 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, 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 the user asks how to change the language, reply in the same language the user asked in.`, + scopes: ['observability'], + }); const { ready: isReady } = await client.getKnowledgeBaseStatus(); - 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. - + functions.registerInstruction({ + instruction: ({ 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 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)) { + } + + 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 { instructions.push( - `Additionally, you can use the "${CONTEXT_FUNCTION_NAME}" function to retrieve relevant information from the knowledge database.` + `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.` ); } - } 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)); + return instructions.map((instruction) => dedent(instruction)); + }, + scopes: ['all'], }); 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 f939e3a79799b..f55a8ba432922 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,6 +95,7 @@ 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 8865861d81f45..a4c34c5caa5a3 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,6 +86,7 @@ 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 d57051cf9fb62..f5f235af12544 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 @@ -10,6 +10,7 @@ import { context as otelContext } from '@opentelemetry/api'; import * as t from 'io-ts'; import { from, map } from 'rxjs'; import { Readable } from 'stream'; +import { AssistantScope } from '../../../common/types'; import { aiAssistantSimulatedFunctionCalling } from '../..'; import { createFunctionResponseMessage } from '../../../common/utils/create_function_response_message'; import { withoutTokenCountEvents } from '../../../common/utils/without_token_count_events'; @@ -20,7 +21,7 @@ import { observableIntoStream } from '../../service/util/observable_into_stream' import { withAssistantSpan } from '../../service/util/with_assistant_span'; import { recallAndScore } from '../../utils/recall/recall_and_score'; import { createObservabilityAIAssistantServerRoute } from '../create_observability_ai_assistant_server_route'; -import { functionRt, messageRt, screenContextRt } from '../runtime_types'; +import { assistantScopeType, functionRt, messageRt, screenContextRt } from '../runtime_types'; import { ObservabilityAIAssistantRouteHandlerResources } from '../types'; const chatCompleteBaseRt = t.type({ @@ -60,6 +61,7 @@ const chatCompleteInternalRt = t.intersection([ t.type({ body: t.type({ screenContexts: t.array(screenContextRt), + scope: assistantScopeType, }), }), ]); @@ -81,10 +83,12 @@ async function initializeChatRequest({ request, plugins: { cloud, actions }, params: { - body: { connectorId }, + body: { connectorId, scope }, }, service, -}: ObservabilityAIAssistantRouteHandlerResources & { params: { body: { connectorId: string } } }) { +}: ObservabilityAIAssistantRouteHandlerResources & { + params: { body: { connectorId: string; scope: AssistantScope } }; +}) { await withAssistantSpan('guard_against_invalid_connector', async () => { const actionsClient = await (await actions.start()).getActionsClientWithRequest(request); @@ -97,7 +101,7 @@ async function initializeChatRequest({ }); const [client, cloudStart, simulateFunctionCalling] = await Promise.all([ - service.getClient({ request }), + service.getClient({ request, scope }), cloud?.start(), (await context.core).uiSettings.client.get(aiAssistantSimulatedFunctionCalling), ]); @@ -132,6 +136,7 @@ const chatRoute = createObservabilityAIAssistantServerRoute({ messages: t.array(messageRt), connectorId: t.string, functions: t.array(functionRt), + scope: assistantScopeType, }), t.partial({ functionCall: t.string, @@ -177,6 +182,7 @@ const chatRecallRoute = createObservabilityAIAssistantServerRoute({ prompt: t.string, context: t.string, connectorId: t.string, + scope: assistantScopeType, }), }), handler: async (resources): Promise => { @@ -304,6 +310,7 @@ const publicChatCompleteRoute = createObservabilityAIAssistantServerRoute({ params: { body: { ...restOfBody, + scope: '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 fae7077953699..1506db576275d 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 @@ -12,9 +12,15 @@ import { KnowledgeBaseEntryRole } from '../../../common/types'; import type { RecalledEntry } from '../../service/knowledge_base_service'; import { getSystemMessageFromInstructions } from '../../service/util/get_system_message_from_instructions'; import { createObservabilityAIAssistantServerRoute } from '../create_observability_ai_assistant_server_route'; +import { assistantScopeType } from '../runtime_types'; const getFunctionsRoute = createObservabilityAIAssistantServerRoute({ - endpoint: 'GET /internal/observability_ai_assistant/functions', + endpoint: 'GET /internal/observability_ai_assistant/{scope}/functions', + params: t.type({ + path: t.type({ + scope: assistantScopeType, + }), + }), options: { tags: ['access:ai_assistant'], }, @@ -24,7 +30,13 @@ const getFunctionsRoute = createObservabilityAIAssistantServerRoute({ functionDefinitions: FunctionDefinition[]; systemMessage: string; }> => { - const { service, request } = resources; + const { + service, + request, + params: { + path: { scope }, + }, + } = resources; const controller = new AbortController(); request.events.aborted$.subscribe(() => { @@ -44,14 +56,14 @@ const getFunctionsRoute = createObservabilityAIAssistantServerRoute({ client.getKnowledgeBaseUserInstructions(), ]); - const functionDefinitions = functionClient.getFunctions().map((fn) => fn.definition); + const functionDefinitions = functionClient.getFunctions({ scope }).map((fn) => fn.definition); const availableFunctionNames = functionDefinitions.map((def) => def.name); return { functionDefinitions: functionClient.getFunctions().map((fn) => fn.definition), systemMessage: getSystemMessageFromInstructions({ - applicationInstructions: functionClient.getInstructions(), + applicationInstructions: functionClient.getInstructions(scope), userInstructions, adHocInstructions: [], availableFunctionNames, diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant/server/routes/runtime_types.ts b/x-pack/plugins/observability_solution/observability_ai_assistant/server/routes/runtime_types.ts index b7f8b9daa8a58..3ead874f22ca1 100644 --- a/x-pack/plugins/observability_solution/observability_ai_assistant/server/routes/runtime_types.ts +++ b/x-pack/plugins/observability_solution/observability_ai_assistant/server/routes/runtime_types.ts @@ -73,6 +73,12 @@ export const baseConversationRt: t.Type = t.type({ public: toBooleanRt, }); +export const assistantScopeType = t.union([ + t.literal('observability'), + t.literal('search'), + t.literal('all'), +]); + export const conversationCreateRt: t.Type = t.intersection([ baseConversationRt, t.type({ 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 3d83c470de0c5..ea265c580b50f 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,7 +34,8 @@ describe('chatFunctionClient', () => { required: ['foo'], }, }, - respondFn + respondFn, + ['all'] ); }); 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 039d7347c715e..d8fbe456da879 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 @@ -10,13 +10,18 @@ import Ajv, { type ErrorObject, type ValidateFunction } from 'ajv'; import dedent from 'dedent'; import { compact, keyBy } from 'lodash'; import { FunctionVisibility, type FunctionResponse } from '../../../common/functions/types'; -import type { Message, ObservabilityAIAssistantScreenContextRequest } from '../../../common/types'; +import type { + AssistantScope, + Message, + ObservabilityAIAssistantScreenContextRequest, +} from '../../../common/types'; import { filterFunctionDefinitions } from '../../../common/utils/filter_function_definitions'; import type { FunctionCallChatFunction, FunctionHandler, FunctionHandlerRegistry, InstructionOrCallback, + InstructionOrCallbackWithScopes, RegisterFunction, RegisterInstruction, } from '../types'; @@ -34,7 +39,7 @@ const ajv = new Ajv({ export const GET_DATA_ON_SCREEN_FUNCTION_NAME = 'get_data_on_screen'; export class ChatFunctionClient { - private readonly instructions: InstructionOrCallback[] = []; + private readonly instructions: InstructionOrCallbackWithScopes[] = []; private readonly functionRegistry: FunctionHandlerRegistry = new Map(); private readonly validators: Map = new Map(); @@ -73,7 +78,8 @@ export class ChatFunctionClient { return { content: allData.filter((data) => dataNames.includes(data.name)), }; - } + }, + ['all'] ); } @@ -84,11 +90,11 @@ export class ChatFunctionClient { }); } - registerFunction: RegisterFunction = (definition, respond) => { + registerFunction: RegisterFunction = (definition, respond, scopes) => { if (definition.parameters) { this.validators.set(definition.name, ajv.compile(definition.parameters)); } - this.functionRegistry.set(definition.name, { definition, respond }); + this.functionRegistry.set(definition.name, { handler: { definition, respond }, scopes }); }; registerInstruction: RegisterInstruction = (instruction) => { @@ -107,8 +113,12 @@ export class ChatFunctionClient { } } - getInstructions(): InstructionOrCallback[] { - return this.instructions; + getInstructions(scope: AssistantScope): InstructionOrCallback[] { + return this.instructions + .filter( + (instruction) => instruction.scopes.includes(scope) || instruction.scopes.includes('all') + ) + .map((i) => i.instruction); } hasAction(name: string) { @@ -117,10 +127,16 @@ export class ChatFunctionClient { getFunctions({ filter, + scope, }: { filter?: string; + scope?: AssistantScope; } = {}): FunctionHandler[] { - const allFunctions = Array.from(this.functionRegistry.values()); + const allFunctions = Array.from(this.functionRegistry.values()) + .filter(({ handler, scopes }) => + scope ? scopes.includes(scope) || scopes.includes('all') : true + ) + .map(({ handler }) => handler); const functionsByName = keyBy(allFunctions, (definition) => definition.definition.name); @@ -167,7 +183,7 @@ export class ChatFunctionClient { this.validate(name, parsedArguments); - return await fn.respond( + return await fn.handler.respond( { arguments: parsedArguments, messages, 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 a3c1d72fefbab..5a7cf81a40122 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,6 +187,7 @@ describe('Observability AI Assistant client', () => { user: { name: 'johndoe', }, + scope: '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 1e995b66059c2..fc14558776434 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 @@ -52,6 +52,7 @@ import { type KnowledgeBaseEntry, type Message, type AdHocInstruction, + AssistantScope, } from '../../../common/types'; import { withoutTokenCountEvents } from '../../../common/utils/without_token_count_events'; import { CONTEXT_FUNCTION_NAME } from '../../functions/context'; @@ -100,6 +101,7 @@ export class ObservabilityAIAssistantClient { name: string; }; knowledgeBaseService: KnowledgeBaseService; + scope: AssistantScope; } ) {} @@ -215,11 +217,11 @@ export class ObservabilityAIAssistantClient { // this is what we eventually store in the conversation const messagesWithUpdatedSystemMessage = replaceSystemMessage( getSystemMessageFromInstructions({ - applicationInstructions: functionClient.getInstructions(), + applicationInstructions: functionClient.getInstructions(this.dependencies.scope), userInstructions, adHocInstructions, availableFunctionNames: functionClient - .getFunctions() + .getFunctions({ scope: this.dependencies.scope }) .map((fn) => fn.definition.name), }), initialMessages @@ -299,6 +301,7 @@ 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 66204c96f31cb..b91600323d41e 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 @@ -28,7 +28,7 @@ import { MessageOrChatEvent, } from '../../../../common/conversation_complete'; import { FunctionVisibility } from '../../../../common/functions/types'; -import { AdHocInstruction, Instruction } from '../../../../common/types'; +import { AdHocInstruction, AssistantScope, Instruction } from '../../../../common/types'; import { createFunctionResponseMessage } from '../../../../common/utils/create_function_response_message'; import { emitWithConcatenatedMessage } from '../../../../common/utils/emit_with_concatenated_message'; import { withoutTokenCountEvents } from '../../../../common/utils/without_token_count_events'; @@ -184,6 +184,7 @@ export function continueConversation({ disableFunctions, tracer, connectorId, + scope, useSimulatedFunctionCalling, }: { messages: Message[]; @@ -201,6 +202,7 @@ export function continueConversation({ }; tracer: LangTracer; connectorId: string; + scope: AssistantScope; useSimulatedFunctionCalling: boolean; }): Observable { let nextFunctionCallsLeft = functionCallsLeft; @@ -215,7 +217,7 @@ export function continueConversation({ const messagesWithUpdatedSystemMessage = replaceSystemMessage( getSystemMessageFromInstructions({ - applicationInstructions: functionClient.getInstructions(), + applicationInstructions: functionClient.getInstructions(scope), userInstructions, adHocInstructions, availableFunctionNames: definitions.map((def) => def.name), @@ -344,6 +346,7 @@ 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 c087e5940f0b7..359692809f3a4 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 @@ -13,6 +13,7 @@ import { getSpaceIdFromPath } from '@kbn/spaces-plugin/common'; import type { TaskManagerSetupContract } from '@kbn/task-manager-plugin/server'; import { once } from 'lodash'; import { + AssistantScope, KnowledgeBaseEntryRole, ObservabilityAIAssistantScreenContextRequest, } from '../../common/types'; @@ -248,8 +249,10 @@ export class ObservabilityAIAssistantService { async getClient({ request, + scope, }: { request: KibanaRequest; + scope?: AssistantScope; }): Promise { const controller = new AbortController(); @@ -288,6 +291,7 @@ export class ObservabilityAIAssistantService { } : undefined, knowledgeBaseService: this.kbService!, + scope: scope || '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 ebc54daf36739..66510008df967 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 @@ -17,6 +17,7 @@ import type { Message, ObservabilityAIAssistantScreenContextRequest, InstructionOrPlainText, + AssistantScope, } from '../../common/types'; import type { ObservabilityAIAssistantRouteHandlerResources } from '../routes/types'; import { ChatFunctionClient } from './chat_function_client'; @@ -67,13 +68,18 @@ export interface FunctionHandler { export type InstructionOrCallback = InstructionOrPlainText | RegisterInstructionCallback; -type RegisterInstructionCallback = ({ +export interface InstructionOrCallbackWithScopes { + instruction: InstructionOrCallback; + scopes: AssistantScope[]; +} + +export type RegisterInstructionCallback = ({ availableFunctionNames, }: { availableFunctionNames: string[]; }) => InstructionOrPlainText | InstructionOrPlainText[] | undefined; -export type RegisterInstruction = (...instructions: InstructionOrCallback[]) => void; +export type RegisterInstruction = (...instruction: InstructionOrCallbackWithScopes[]) => void; export type RegisterFunction = < TParameters extends CompatibleJSONSchema = any, @@ -81,9 +87,13 @@ export type RegisterFunction = < TArguments = FromSchema >( definition: FunctionDefinition, - respond: RespondFunction + respond: RespondFunction, + scopes: AssistantScope[] ) => void; -export type FunctionHandlerRegistry = Map; +export type FunctionHandlerRegistry = Map< + string, + { handler: FunctionHandler; scopes: AssistantScope[] } +>; export type RegistrationCallback = ({}: { signal: AbortSignal; diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/public/hooks/__storybook_mocks__/use_conversation.ts b/x-pack/plugins/observability_solution/observability_ai_assistant_app/public/hooks/__storybook_mocks__/use_conversation.ts index a6795c13cab2c..8bc8f54e9ac8d 100644 --- a/x-pack/plugins/observability_solution/observability_ai_assistant_app/public/hooks/__storybook_mocks__/use_conversation.ts +++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/public/hooks/__storybook_mocks__/use_conversation.ts @@ -15,5 +15,6 @@ export function useConversation() { stop: () => {}, messages: [], saveTitle: () => {}, + scope: 'all', }; } diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/public/hooks/use_conversation.test.tsx b/x-pack/plugins/observability_solution/observability_ai_assistant_app/public/hooks/use_conversation.test.tsx index 3ebca243a56b3..150847a011207 100644 --- a/x-pack/plugins/observability_solution/observability_ai_assistant_app/public/hooks/use_conversation.test.tsx +++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/public/hooks/use_conversation.test.tsx @@ -55,6 +55,7 @@ const mockService: MockedService = { predefinedConversation$: new Observable(), }, navigate: jest.fn().mockReturnValue(of()), + scope: 'all', }; const mockChatService = createMockChatService(); diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/public/hooks/use_conversation.ts b/x-pack/plugins/observability_solution/observability_ai_assistant_app/public/hooks/use_conversation.ts index 0616e9dbaca32..617b1b302473f 100644 --- a/x-pack/plugins/observability_solution/observability_ai_assistant_app/public/hooks/use_conversation.ts +++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/public/hooks/use_conversation.ts @@ -63,6 +63,7 @@ export function useConversation({ onConversationUpdate, }: UseConversationProps): UseConversationResult { const service = useObservabilityAIAssistantAppService(); + const { scope } = service; const { services: { @@ -126,6 +127,7 @@ export function useConversation({ onConversationUpdate?.({ conversation: event.conversation }); }, persist: true, + scope, }); const [displayedConversationId, setDisplayedConversationId] = useState(initialConversationId); 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 6fa07af24208c..030994fa44acf 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,6 +100,7 @@ function runEvaluations() { evaluationConnectorId: evaluationConnector.id!, persist: argv.persist, suite: mocha.suite, + scope: '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 61ed156530100..8246a9ceae71f 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 @@ -18,7 +18,10 @@ import { StreamingChatResponseEvent, StreamingChatResponseEventType, } from '@kbn/observability-ai-assistant-plugin/common'; -import type { ObservabilityAIAssistantScreenContext } from '@kbn/observability-ai-assistant-plugin/common/types'; +import type { + AssistantScope, + ObservabilityAIAssistantScreenContext, +} from '@kbn/observability-ai-assistant-plugin/common/types'; import { throwSerializedChatCompletionErrors } from '@kbn/observability-ai-assistant-plugin/common/utils/throw_serialized_chat_completion_errors'; import { isSupportedConnectorType, @@ -238,11 +241,13 @@ export class KibanaClient { evaluationConnectorId, persist, suite, + scope, }: { connectorId: string; evaluationConnectorId: string; persist: boolean; suite?: Mocha.Suite; + scope: AssistantScope; }): ChatClient { function getMessages(message: string | Array): Array { if (typeof message === 'string') { @@ -370,6 +375,7 @@ export class KibanaClient { connectorId: connectorIdOverride || connectorId, functions: functions.map((fn) => pick(fn, 'name', 'description', 'parameters')), functionCall, + scope, }; return that.axios.post( @@ -459,6 +465,7 @@ export class KibanaClient { connectorId, persist, title: currentTitle, + scope, }, { responseType: 'stream', timeout: NaN } ) @@ -534,7 +541,7 @@ export class KibanaClient { which helps our users make sense of their Observability data. Your goal is to verify whether a conversation between the user and the assistant matches the given criteria. - + For each criterion, calculate a score. Explain your score, by describing what the assistant did right, and describing and quoting what the assistant did wrong, where it could improve, and what the root cause was in case of a failure.`, }, @@ -544,13 +551,13 @@ export class KibanaClient { message: { role: MessageRole.User, content: `Evaluate the conversation according to the following criteria, using the "scores" tool: - + ${criteria.map((criterion, index) => { return `${index}: ${criterion}`; })} - + This is the conversation: - + ${JSON.stringify( messages .filter((msg) => msg.role !== MessageRole.System) 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 03c61843e702a..1d0056fa2f66c 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 @@ -131,13 +131,14 @@ export function registerAlertsFunction({ fields: fields.length === 0 ? defaultFields : fields, }, }; - } + }, + ['observability'] ); functions.registerFunction( { name: 'alerts', - description: `Get alerts for Observability. Make sure get_alerts_dataset_info was called before. + 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. @@ -220,6 +221,7 @@ export function registerAlertsFunction({ alerts, }, }; - } + }, + ['observability'] ); } 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 dc0e26ea9c777..71872782e27b0 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 @@ -149,6 +149,7 @@ export function registerChangesFunction({ }, }, }; - } + }, + ['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 dbae57c08c9e2..bb07d701f1708 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,9 +8,13 @@ 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: {}, - }; - }); + functions.registerFunction( + lensFunctionDefinition, + async () => { + return { + content: {}, + }; + }, + ['all'] + ); } 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 c5cd40aade7c4..8f7eb7b6b4e1f 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 @@ -20,6 +20,7 @@ import { import { createFunctionResponseMessage } from '@kbn/observability-ai-assistant-plugin/common/utils/create_function_response_message'; import { map } from 'rxjs'; import { v4 } from 'uuid'; +import { RegisterInstructionCallback } from '@kbn/observability-ai-assistant-plugin/server/service/types'; import type { FunctionRegistrationParameters } from '..'; import { runAndValidateEsqlQuery } from './validate_esql_query'; import { convertMessagesForInference } from '../../../common/convert_messages_for_inference'; @@ -32,7 +33,7 @@ export function registerQueryFunction({ resources, pluginsStart, }: FunctionRegistrationParameters) { - functions.registerInstruction(({ availableFunctionNames }) => + const instruction: RegisterInstructionCallback = ({ availableFunctionNames }) => availableFunctionNames.includes(QUERY_FUNCTION_NAME) ? `You MUST use the "${QUERY_FUNCTION_NAME}" function when the user wants to: - visualize data @@ -51,8 +52,8 @@ 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 - ); + : undefined; + functions.registerInstruction({ instruction, scopes: ['all'] }); functions.registerFunction( { @@ -65,7 +66,7 @@ export function registerQueryFunction({ such as a metric or list of things, but does not want to visualize it in a table or chart. You do NOT need to ask permission to execute the query after generating it, use the "${EXECUTE_QUERY_NAME}" function directly instead. - + Do not use when the user just asks for an example.`, parameters: { type: 'object', @@ -102,7 +103,8 @@ export function registerQueryFunction({ rows, }, }; - } + }, + ['all'] ); functions.registerFunction( { @@ -186,6 +188,7 @@ 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 4eeba0450e6e4..bda75eafc9ade 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,6 +61,7 @@ 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 34b9dd36ea77f..d99e822484b67 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,7 +154,7 @@ async function executor( } const resources = await initResources(request); - const client = await resources.service.getClient({ request }); + const client = await resources.service.getClient({ request, scope: 'observability' }); const functionClient = await resources.service.getFunctionClient({ signal: new AbortController().signal, resources, @@ -227,7 +227,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(), + applicationInstructions: functionClient.getInstructions('observability'), userInstructions: [], adHocInstructions: [], }), diff --git a/x-pack/plugins/observability_solution/profiling/public/components/frame_information_window/frame_information_ai_assistant.tsx b/x-pack/plugins/observability_solution/profiling/public/components/frame_information_window/frame_information_ai_assistant.tsx index fb68611a74b7d..4464a4de4fe7b 100644 --- a/x-pack/plugins/observability_solution/profiling/public/components/frame_information_window/frame_information_ai_assistant.tsx +++ b/x-pack/plugins/observability_solution/profiling/public/components/frame_information_window/frame_information_ai_assistant.tsx @@ -29,7 +29,7 @@ export function FrameInformationAIAssistant({ frame }: Props) { instructions: `The library is: ${library} The function is: ${functionName} - Your have two tasks. Your first task is to desribe what the library is and what its use cases are, and to + You have two tasks. Your first task is to desribe what the library is and what its use cases are, and to describe what the function does. The output format should look as follows: Library description: Provide a concise description of the library 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 ffd1ad09c7ccd..e0e67066b4777 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,6 +59,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { messages, connectorId: 'does not exist', functions: [], + scope: 'all', }) .expect(404); }); @@ -87,6 +88,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { messages, connectorId, functions: [], + scope: 'all', }) .pipe(passThrough); @@ -144,6 +146,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { messages, connectorId, functions: [], + scope: '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 04d85d6dc282c..aaba5fbc7ba99 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,6 +84,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { connectorId, persist: true, screenContexts: params.screenContexts || [], + scope: 'all', }) .then((response) => resolve(response)) .catch((err) => reject(err)); @@ -136,6 +137,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { connectorId, persist: false, screenContexts: [], + scope: 'all', }) .pipe(passThrough); @@ -402,6 +404,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { connectorId, persist: true, screenContexts: [], + scope: 'observability', }, }, }) @@ -444,6 +447,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { persist: true, screenContexts: [], conversationId, + scope: '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 a32c22abcf7aa..552b779d2c0aa 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 @@ -12,6 +12,7 @@ import { StreamingChatResponseEvent, } from '@kbn/observability-ai-assistant-plugin/common'; import { Readable } from 'stream'; +import { AssistantScope } from '@kbn/observability-ai-assistant-plugin/common/types'; import { CreateTest } from '../../../common/config'; function decodeEvents(body: Readable | string) { @@ -32,12 +33,14 @@ export async function invokeChatCompleteWithFunctionRequest({ connectorId, observabilityAIAssistantAPIClient, functionCall, + scope, }: { connectorId: string; observabilityAIAssistantAPIClient: Awaited< ReturnType >; functionCall: Message['message']['function_call']; + scope?: AssistantScope; }) { const { body } = await observabilityAIAssistantAPIClient .editorUser({ @@ -57,6 +60,7 @@ export async function invokeChatCompleteWithFunctionRequest({ connectorId, persist: false, screenContexts: [], + scope: scope || 'observability', }, }, }) 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 4cad8079dc0b2..bf2eef14db553 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,6 +249,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { connectorId, persist: true, screenContexts: [], + scope: 'observability', }, }, }).expect(200);