From 83f2b19fc02cf5cef72dfa1049ad5bb02daf1b70 Mon Sep 17 00:00:00 2001 From: Sander Philipse Date: Tue, 15 Oct 2024 16:04:30 +0200 Subject: [PATCH 01/22] [AI Assistant] Set scope and rename to Observability and Search --- .../selection/common/ui_setting_keys.ts | 1 + .../selection/public/index.ts | 2 + .../ai_assistant_selection_page.tsx | 2 +- .../selection/server/plugin.ts | 40 +++++++++++++++++-- .../observability_ai_assistant/kibana.jsonc | 20 ++++++++-- .../public/plugin.tsx | 4 +- .../server/functions/index.ts | 2 +- .../README.md | 2 +- .../kibana.jsonc | 24 +++++++++-- .../public/app.tsx | 2 +- .../public/plugin.ts | 4 +- .../routes/components/settings_page.tsx | 2 +- .../components/settings_tab/settings_tab.tsx | 2 +- .../components/settings_tab/ui_settings.tsx | 2 + .../server/functions/index.ts | 2 +- .../serverless_observability/public/plugin.ts | 2 +- 16 files changed, 90 insertions(+), 23 deletions(-) diff --git a/src/plugins/ai_assistant_management/selection/common/ui_setting_keys.ts b/src/plugins/ai_assistant_management/selection/common/ui_setting_keys.ts index 033781bbf5ba2..edec76098d431 100644 --- a/src/plugins/ai_assistant_management/selection/common/ui_setting_keys.ts +++ b/src/plugins/ai_assistant_management/selection/common/ui_setting_keys.ts @@ -8,3 +8,4 @@ */ export const PREFERRED_AI_ASSISTANT_TYPE_SETTING_KEY = 'aiAssistant:preferredAIAssistantType'; +export const AI_ASSISTANT_DEFAULT_SCOPE_KEY = 'aiAssistant:defaultScope'; diff --git a/src/plugins/ai_assistant_management/selection/public/index.ts b/src/plugins/ai_assistant_management/selection/public/index.ts index 5540d9fecb5b7..09eac5d8e2a22 100644 --- a/src/plugins/ai_assistant_management/selection/public/index.ts +++ b/src/plugins/ai_assistant_management/selection/public/index.ts @@ -17,6 +17,8 @@ import type { export { AIAssistantType } from '../common/ai_assistant_type'; +export { AI_ASSISTANT_DEFAULT_SCOPE_KEY } from '../common/ui_setting_keys'; + export type { AIAssistantManagementSelectionPluginPublicSetup, AIAssistantManagementSelectionPluginPublicStart, 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..2fe7c298b380a 100644 --- a/src/plugins/ai_assistant_management/selection/server/plugin.ts +++ b/src/plugins/ai_assistant_management/selection/server/plugin.ts @@ -26,7 +26,10 @@ import type { AIAssistantManagementSelectionPluginServerStart, } from './types'; import { AIAssistantType } from '../common/ai_assistant_type'; -import { PREFERRED_AI_ASSISTANT_TYPE_SETTING_KEY } from '../common/ui_setting_keys'; +import { + AI_ASSISTANT_DEFAULT_SCOPE_KEY, + PREFERRED_AI_ASSISTANT_TYPE_SETTING_KEY, +} from '../common/ui_setting_keys'; export class AIAssistantManagementSelectionPlugin implements @@ -50,7 +53,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 +61,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 +80,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', @@ -90,6 +93,35 @@ export class AIAssistantManagementSelectionPlugin }, requiresPageReload: true, }, + [AI_ASSISTANT_DEFAULT_SCOPE_KEY]: { + category: ['observability'], + name: i18n.translate('aiAssistantManagementSelection.defaultScopeName', { + defaultMessage: 'AI assistant default scope', + }), + value: '', + description: i18n.translate( + 'aiAssistantManagementSelection.settingsPage.defaultScopeDescription', + { + defaultMessage: + 'Defines the default behavior of the AI assistant in pages outside Observability and Search. This determines which functions are available to the assistant, and the system prompt it uses.', + } + ), + schema: schema.oneOf([schema.literal('search'), schema.literal('observability')], { + defaultValue: 'search', + }), + options: ['search', 'observability'], + type: 'select', + optionLabels: { + ['observability']: i18n.translate( + 'aiAssistantManagementSelection.defaultScopeValueObservability', + { defaultMessage: 'Observability' } + ), + ['search']: i18n.translate('aiAssistantManagementSelection.defaultScopeSearch', { + defaultMessage: 'Search', + }), + }, + requiresPageReload: true, + }, }); core.capabilities.registerProvider(() => { diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant/kibana.jsonc b/x-pack/plugins/observability_solution/observability_ai_assistant/kibana.jsonc index 39af4d91bc87b..0ca9be4f31391 100644 --- a/x-pack/plugins/observability_solution/observability_ai_assistant/kibana.jsonc +++ b/x-pack/plugins/observability_solution/observability_ai_assistant/kibana.jsonc @@ -6,7 +6,10 @@ "id": "observabilityAIAssistant", "server": true, "browser": true, - "configPath": ["xpack", "observabilityAIAssistant"], + "configPath": [ + "xpack", + "observabilityAIAssistant" + ], "requiredPlugins": [ "actions", "features", @@ -14,10 +17,19 @@ "security", "taskManager", "dataViews", + "aiAssistantManagementSelection" + ], + "requiredBundles": [ + "kibanaReact", + "kibanaUtils" + ], + "optionalPlugins": [ + "cloud", + "serverless" ], - "requiredBundles": ["kibanaReact", "kibanaUtils"], - "optionalPlugins": ["cloud", "serverless"], "extraPublicDirs": [], - "runtimePluginDependencies": [ "ml" ] + "runtimePluginDependencies": [ + "ml" + ] } } 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..30e0ff2255823 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 @@ -11,6 +11,7 @@ import type { Logger } from '@kbn/logging'; import { withSuspense } from '@kbn/shared-ux-utility'; import React, { type ComponentType, lazy, type Ref } from 'react'; import { AssistantScope } from '@kbn/ai-assistant-common'; +import { AI_ASSISTANT_DEFAULT_SCOPE_KEY } from '@kbn/ai-assistant-management-plugin/public'; import { registerTelemetryEventTypes } from './analytics'; import { ObservabilityAIAssistantChatServiceContext } from './context/observability_ai_assistant_chat_service_context'; import { ObservabilityAIAssistantMultipaneFlyoutContext } from './context/observability_ai_assistant_multipane_flyout_context'; @@ -60,6 +61,7 @@ export class ObservabilityAIAssistantPlugin coreStart: CoreStart, pluginsStart: ObservabilityAIAssistantPluginStartDependencies ): ObservabilityAIAssistantPublicStart { + const scopeFromUiSettings = coreStart.uiSettings.get(AI_ASSISTANT_DEFAULT_SCOPE_KEY); const service = (this.service = createService({ analytics: coreStart.analytics, coreStart, @@ -67,7 +69,7 @@ export class ObservabilityAIAssistantPlugin coreStart.application.capabilities.observabilityAIAssistant[ aiAssistantCapabilities.show ] === true, - scope: this.scopeFromConfig || 'observability', + scope: this.scopeFromConfig || scopeFromUiSettings || 'search', scopeIsMutable: !!this.scopeFromConfig, })); 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..2625b8e6970a2 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 @@ -57,7 +57,7 @@ export const registerFunctions: RegistrationCallback = async ({ 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.`, 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/kibana.jsonc b/x-pack/plugins/observability_solution/observability_ai_assistant_management/kibana.jsonc index ddf00c84c0ac3..19298fb24b6c1 100644 --- a/x-pack/plugins/observability_solution/observability_ai_assistant_management/kibana.jsonc +++ b/x-pack/plugins/observability_solution/observability_ai_assistant_management/kibana.jsonc @@ -6,9 +6,25 @@ "id": "observabilityAiAssistantManagement", "server": true, "browser": true, - "configPath": ["xpack", "observabilityAiAssistantManagement"], - "requiredPlugins": ["management", "observabilityAIAssistant", "observabilityShared"], - "optionalPlugins": ["actions", "home", "serverless", "enterpriseSearch"], - "requiredBundles": ["kibanaReact", "logsDataAccess"] + "configPath": [ + "xpack", + "observabilityAiAssistantManagement" + ], + "requiredPlugins": [ + "management", + "observabilityAIAssistant", + "observabilityShared", + "aiAssistantManagementSelection" + ], + "optionalPlugins": [ + "actions", + "home", + "serverless", + "enterpriseSearch" + ], + "requiredBundles": [ + "kibanaReact", + "logsDataAccess" + ] } } 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/public/routes/components/settings_tab/ui_settings.tsx b/x-pack/plugins/observability_solution/observability_ai_assistant_management/public/routes/components/settings_tab/ui_settings.tsx index a8c20a641f042..d4e1f3b2d3a74 100644 --- a/x-pack/plugins/observability_solution/observability_ai_assistant_management/public/routes/components/settings_tab/ui_settings.tsx +++ b/x-pack/plugins/observability_solution/observability_ai_assistant_management/public/routes/components/settings_tab/ui_settings.tsx @@ -18,12 +18,14 @@ import { EuiSpacer } from '@elastic/eui'; import { isEmpty } from 'lodash'; import { i18n } from '@kbn/i18n'; import { LogSourcesSettingSynchronisationInfo } from '@kbn/logs-data-access-plugin/public'; +import { AI_ASSISTANT_DEFAULT_SCOPE_KEY } from '@kbn/ai-assistant-management-plugin/public'; import { useKibana } from '../../../hooks/use_kibana'; const settingsKeys = [ aiAssistantSimulatedFunctionCalling, aiAssistantSearchConnectorIndexPattern, aiAssistantPreferredAIAssistantType, + AI_ASSISTANT_DEFAULT_SCOPE_KEY, ]; export function UISettings() { diff --git a/x-pack/plugins/search_assistant/server/functions/index.ts b/x-pack/plugins/search_assistant/server/functions/index.ts index d1eef69615a61..c7998c4dc7102 100644 --- a/x-pack/plugins/search_assistant/server/functions/index.ts +++ b/x-pack/plugins/search_assistant/server/functions/index.ts @@ -27,7 +27,7 @@ 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.`, 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', From e9da40afc53ea2ab349839373388dae10bd43d39 Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Tue, 15 Oct 2024 14:25:23 +0000 Subject: [PATCH 02/22] [CI] Auto-commit changed files from 'node scripts/lint_packages --fix' --- docs/developer/plugin-list.asciidoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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] From 8f84195041553b1dda08b6782449d9d179107d8d Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Tue, 15 Oct 2024 14:43:10 +0000 Subject: [PATCH 03/22] [CI] Auto-commit changed files from 'node scripts/lint_ts_projects --fix' --- .../observability_ai_assistant/tsconfig.json | 3 ++- .../observability_ai_assistant_management/tsconfig.json | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) 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..6869518102a4b 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,8 @@ "@kbn/core-ui-settings-server", "@kbn/inference-plugin", "@kbn/management-settings-ids", - "@kbn/ai-assistant-common" + "@kbn/ai-assistant-common", + "@kbn/ai-assistant-management-plugin" ], "exclude": ["target/**/*"] } 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..d9706634fa524 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,8 @@ "@kbn/observability-shared-plugin", "@kbn/config-schema", "@kbn/core-ui-settings-common", - "@kbn/logs-data-access-plugin" + "@kbn/logs-data-access-plugin", + "@kbn/ai-assistant-management-plugin" ], "exclude": ["target/**/*"] } From 0fed35fdd15fc94db58e0fda9858940905840258 Mon Sep 17 00:00:00 2001 From: Sander Philipse Date: Wed, 16 Oct 2024 12:35:08 +0200 Subject: [PATCH 04/22] Remove scope UI setting and set default to all --- .../selection/common/ui_setting_keys.ts | 1 - .../selection/public/index.ts | 2 -- .../selection/server/plugin.ts | 34 +------------------ .../observability_ai_assistant/kibana.jsonc | 20 +++-------- .../public/plugin.tsx | 4 +-- .../kibana.jsonc | 24 +++---------- .../components/settings_tab/ui_settings.tsx | 2 -- 7 files changed, 10 insertions(+), 77 deletions(-) diff --git a/src/plugins/ai_assistant_management/selection/common/ui_setting_keys.ts b/src/plugins/ai_assistant_management/selection/common/ui_setting_keys.ts index edec76098d431..033781bbf5ba2 100644 --- a/src/plugins/ai_assistant_management/selection/common/ui_setting_keys.ts +++ b/src/plugins/ai_assistant_management/selection/common/ui_setting_keys.ts @@ -8,4 +8,3 @@ */ export const PREFERRED_AI_ASSISTANT_TYPE_SETTING_KEY = 'aiAssistant:preferredAIAssistantType'; -export const AI_ASSISTANT_DEFAULT_SCOPE_KEY = 'aiAssistant:defaultScope'; diff --git a/src/plugins/ai_assistant_management/selection/public/index.ts b/src/plugins/ai_assistant_management/selection/public/index.ts index 09eac5d8e2a22..5540d9fecb5b7 100644 --- a/src/plugins/ai_assistant_management/selection/public/index.ts +++ b/src/plugins/ai_assistant_management/selection/public/index.ts @@ -17,8 +17,6 @@ import type { export { AIAssistantType } from '../common/ai_assistant_type'; -export { AI_ASSISTANT_DEFAULT_SCOPE_KEY } from '../common/ui_setting_keys'; - export type { AIAssistantManagementSelectionPluginPublicSetup, AIAssistantManagementSelectionPluginPublicStart, diff --git a/src/plugins/ai_assistant_management/selection/server/plugin.ts b/src/plugins/ai_assistant_management/selection/server/plugin.ts index 2fe7c298b380a..4b74b55e571ab 100644 --- a/src/plugins/ai_assistant_management/selection/server/plugin.ts +++ b/src/plugins/ai_assistant_management/selection/server/plugin.ts @@ -26,10 +26,7 @@ import type { AIAssistantManagementSelectionPluginServerStart, } from './types'; import { AIAssistantType } from '../common/ai_assistant_type'; -import { - AI_ASSISTANT_DEFAULT_SCOPE_KEY, - PREFERRED_AI_ASSISTANT_TYPE_SETTING_KEY, -} from '../common/ui_setting_keys'; +import { PREFERRED_AI_ASSISTANT_TYPE_SETTING_KEY } from '../common/ui_setting_keys'; export class AIAssistantManagementSelectionPlugin implements @@ -93,35 +90,6 @@ export class AIAssistantManagementSelectionPlugin }, requiresPageReload: true, }, - [AI_ASSISTANT_DEFAULT_SCOPE_KEY]: { - category: ['observability'], - name: i18n.translate('aiAssistantManagementSelection.defaultScopeName', { - defaultMessage: 'AI assistant default scope', - }), - value: '', - description: i18n.translate( - 'aiAssistantManagementSelection.settingsPage.defaultScopeDescription', - { - defaultMessage: - 'Defines the default behavior of the AI assistant in pages outside Observability and Search. This determines which functions are available to the assistant, and the system prompt it uses.', - } - ), - schema: schema.oneOf([schema.literal('search'), schema.literal('observability')], { - defaultValue: 'search', - }), - options: ['search', 'observability'], - type: 'select', - optionLabels: { - ['observability']: i18n.translate( - 'aiAssistantManagementSelection.defaultScopeValueObservability', - { defaultMessage: 'Observability' } - ), - ['search']: i18n.translate('aiAssistantManagementSelection.defaultScopeSearch', { - defaultMessage: 'Search', - }), - }, - requiresPageReload: true, - }, }); core.capabilities.registerProvider(() => { diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant/kibana.jsonc b/x-pack/plugins/observability_solution/observability_ai_assistant/kibana.jsonc index 0ca9be4f31391..39af4d91bc87b 100644 --- a/x-pack/plugins/observability_solution/observability_ai_assistant/kibana.jsonc +++ b/x-pack/plugins/observability_solution/observability_ai_assistant/kibana.jsonc @@ -6,10 +6,7 @@ "id": "observabilityAIAssistant", "server": true, "browser": true, - "configPath": [ - "xpack", - "observabilityAIAssistant" - ], + "configPath": ["xpack", "observabilityAIAssistant"], "requiredPlugins": [ "actions", "features", @@ -17,19 +14,10 @@ "security", "taskManager", "dataViews", - "aiAssistantManagementSelection" - ], - "requiredBundles": [ - "kibanaReact", - "kibanaUtils" - ], - "optionalPlugins": [ - "cloud", - "serverless" ], + "requiredBundles": ["kibanaReact", "kibanaUtils"], + "optionalPlugins": ["cloud", "serverless"], "extraPublicDirs": [], - "runtimePluginDependencies": [ - "ml" - ] + "runtimePluginDependencies": [ "ml" ] } } 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 30e0ff2255823..32daaf7f4f702 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 @@ -11,7 +11,6 @@ import type { Logger } from '@kbn/logging'; import { withSuspense } from '@kbn/shared-ux-utility'; import React, { type ComponentType, lazy, type Ref } from 'react'; import { AssistantScope } from '@kbn/ai-assistant-common'; -import { AI_ASSISTANT_DEFAULT_SCOPE_KEY } from '@kbn/ai-assistant-management-plugin/public'; import { registerTelemetryEventTypes } from './analytics'; import { ObservabilityAIAssistantChatServiceContext } from './context/observability_ai_assistant_chat_service_context'; import { ObservabilityAIAssistantMultipaneFlyoutContext } from './context/observability_ai_assistant_multipane_flyout_context'; @@ -61,7 +60,6 @@ export class ObservabilityAIAssistantPlugin coreStart: CoreStart, pluginsStart: ObservabilityAIAssistantPluginStartDependencies ): ObservabilityAIAssistantPublicStart { - const scopeFromUiSettings = coreStart.uiSettings.get(AI_ASSISTANT_DEFAULT_SCOPE_KEY); const service = (this.service = createService({ analytics: coreStart.analytics, coreStart, @@ -69,7 +67,7 @@ export class ObservabilityAIAssistantPlugin coreStart.application.capabilities.observabilityAIAssistant[ aiAssistantCapabilities.show ] === true, - scope: this.scopeFromConfig || scopeFromUiSettings || 'search', + scope: this.scopeFromConfig || 'all', scopeIsMutable: !!this.scopeFromConfig, })); diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_management/kibana.jsonc b/x-pack/plugins/observability_solution/observability_ai_assistant_management/kibana.jsonc index 19298fb24b6c1..ddf00c84c0ac3 100644 --- a/x-pack/plugins/observability_solution/observability_ai_assistant_management/kibana.jsonc +++ b/x-pack/plugins/observability_solution/observability_ai_assistant_management/kibana.jsonc @@ -6,25 +6,9 @@ "id": "observabilityAiAssistantManagement", "server": true, "browser": true, - "configPath": [ - "xpack", - "observabilityAiAssistantManagement" - ], - "requiredPlugins": [ - "management", - "observabilityAIAssistant", - "observabilityShared", - "aiAssistantManagementSelection" - ], - "optionalPlugins": [ - "actions", - "home", - "serverless", - "enterpriseSearch" - ], - "requiredBundles": [ - "kibanaReact", - "logsDataAccess" - ] + "configPath": ["xpack", "observabilityAiAssistantManagement"], + "requiredPlugins": ["management", "observabilityAIAssistant", "observabilityShared"], + "optionalPlugins": ["actions", "home", "serverless", "enterpriseSearch"], + "requiredBundles": ["kibanaReact", "logsDataAccess"] } } diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_management/public/routes/components/settings_tab/ui_settings.tsx b/x-pack/plugins/observability_solution/observability_ai_assistant_management/public/routes/components/settings_tab/ui_settings.tsx index d4e1f3b2d3a74..a8c20a641f042 100644 --- a/x-pack/plugins/observability_solution/observability_ai_assistant_management/public/routes/components/settings_tab/ui_settings.tsx +++ b/x-pack/plugins/observability_solution/observability_ai_assistant_management/public/routes/components/settings_tab/ui_settings.tsx @@ -18,14 +18,12 @@ import { EuiSpacer } from '@elastic/eui'; import { isEmpty } from 'lodash'; import { i18n } from '@kbn/i18n'; import { LogSourcesSettingSynchronisationInfo } from '@kbn/logs-data-access-plugin/public'; -import { AI_ASSISTANT_DEFAULT_SCOPE_KEY } from '@kbn/ai-assistant-management-plugin/public'; import { useKibana } from '../../../hooks/use_kibana'; const settingsKeys = [ aiAssistantSimulatedFunctionCalling, aiAssistantSearchConnectorIndexPattern, aiAssistantPreferredAIAssistantType, - AI_ASSISTANT_DEFAULT_SCOPE_KEY, ]; export function UISettings() { From 9130251c73e38bd6114526bbdc088e9cc59eac8a Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Wed, 16 Oct 2024 10:47:54 +0000 Subject: [PATCH 05/22] [CI] Auto-commit changed files from 'node scripts/yarn_deduplicate' --- .../observability_ai_assistant/tsconfig.json | 1 - .../observability_ai_assistant_management/tsconfig.json | 1 - 2 files changed, 2 deletions(-) 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 6869518102a4b..7c2f2212ee946 100644 --- a/x-pack/plugins/observability_solution/observability_ai_assistant/tsconfig.json +++ b/x-pack/plugins/observability_solution/observability_ai_assistant/tsconfig.json @@ -47,7 +47,6 @@ "@kbn/inference-plugin", "@kbn/management-settings-ids", "@kbn/ai-assistant-common", - "@kbn/ai-assistant-management-plugin" ], "exclude": ["target/**/*"] } 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 d9706634fa524..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 @@ -22,7 +22,6 @@ "@kbn/config-schema", "@kbn/core-ui-settings-common", "@kbn/logs-data-access-plugin", - "@kbn/ai-assistant-management-plugin" ], "exclude": ["target/**/*"] } From 3b090bc1ef969a398809ca3050e0993dcafcb0cf Mon Sep 17 00:00:00 2001 From: Sander Philipse Date: Wed, 16 Oct 2024 13:21:32 +0200 Subject: [PATCH 06/22] Make scopes multi-sided on both sides --- .../src/utils/filter_scopes.ts | 10 ++++-- .../public/components/insight/insight.tsx | 4 +-- .../public/hooks/use_chat.ts | 8 ++--- .../public/mock.tsx | 8 ++--- .../public/plugin.tsx | 2 +- .../public/service/complete.ts | 6 ++-- .../service/create_chat_service.test.ts | 4 +-- .../public/service/create_chat_service.ts | 33 +++++++++++-------- .../public/service/create_service.ts | 16 ++++----- .../public/storybook_mock.tsx | 10 +++--- .../public/types.ts | 14 ++++---- .../server/functions/index.ts | 22 +++++++++++++ .../server/routes/chat/route.ts | 14 ++++---- .../server/routes/functions/route.ts | 6 ++-- .../service/chat_function_client/index.ts | 14 +++++--- .../server/service/client/index.ts | 8 ++--- .../client/operators/continue_conversation.ts | 16 ++++----- .../server/service/index.ts | 6 ++-- .../public/hooks/use_nav_control_scope.ts | 15 ++++----- 19 files changed, 127 insertions(+), 89 deletions(-) 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..823531d70d0f3 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) => scope === 'all' || scopeFilters.includes(scope)) + : true; }; } 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.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 32daaf7f4f702..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 || 'all', + scopes: this.scopeFromConfig ? [this.scopeFromConfig] : ['all'], scopeIsMutable: !!this.scopeFromConfig, })); 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..d4145f2ba7ad5 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 @@ -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..d77024ae4df16 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,22 @@ class ChatService { async initialize() { this.functionRegistry = new Map(); - const [{ functionDefinitions, systemMessage }] = await Promise.all([ + const systemMessages: string[] = []; + const scopePromises = this.scope$.value.map((scope) => this.apiClient('GET /internal/observability_ai_assistant/{scope}/functions', { signal: this.abortSignal, params: { path: { - scope: this.getScope(), + scope, }, }, - }), + }).then(({ functionDefinitions, systemMessage }) => { + functionDefinitions.forEach((fn) => this.functionRegistry.set(fn.name, fn)); + systemMessages.push(systemMessage); + }) + ); + await Promise.all([ + ...scopePromises, ...this.registrations.map((registration) => { return registration({ registerRenderFunction: (name, renderFn) => { @@ -204,10 +211,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()); } @@ -251,7 +255,8 @@ class ChatService { definitions: Array.from(this.functionRegistry.values()), }).filter((value) => { return value.scopes - ? value.scopes?.includes(this.getScope()) || value.scopes?.includes('all') + ? value.scopes?.some((scope) => this.getScopes().includes(scope)) || + value.scopes?.includes('all') : true; }); }; @@ -301,7 +306,7 @@ class ChatService { connectorId, functionCall, functions: functions ?? [], - scope: this.getScope(), + scopes: this.getScopes(), }, }, signal, @@ -334,7 +339,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 +350,7 @@ class ChatService { ); }; - public getScope() { + public getScopes() { return this.scope$.value; } } @@ -361,7 +366,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_service.ts b/x-pack/plugins/observability_solution/observability_ai_assistant/public/service/create_service.ts index 22d1b9c792f7f..dc0ce3dc12ce5 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; }; @@ -103,12 +103,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/index.ts b/x-pack/plugins/observability_solution/observability_ai_assistant/server/functions/index.ts index 2625b8e6970a2..6bb6b909dfb9c 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 @@ -64,6 +64,28 @@ export const registerFunctions: RegistrationCallback = async ({ scopes: ['observability'], }); + 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. + + 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. + + 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. + + 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 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: ['all'], + }); + const { ready: isReady } = await client.getKnowledgeBaseStatus(); functions.registerInstruction({ 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..9374036e5206b 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 => { @@ -310,7 +310,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..67253b06c7b4a 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 @@ -56,14 +56,16 @@ const getFunctionsRoute = createObservabilityAIAssistantServerRoute({ client.getKnowledgeBaseUserInstructions(), ]); - const functionDefinitions = functionClient.getFunctions({ scope }).map((fn) => fn.definition); + const functionDefinitions = functionClient + .getFunctions({ scopes: [scope] }) + .map((fn) => fn.definition); const availableFunctionNames = functionDefinitions.map((def) => def.name); return { functionDefinitions, systemMessage: getSystemMessageFromInstructions({ - applicationInstructions: functionClient.getInstructions(scope), + applicationInstructions: functionClient.getInstructions([scope]), userInstructions, adHocInstructions: [], availableFunctionNames, 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..6ff7ba0b8657a 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 @@ -110,8 +110,12 @@ export class ChatFunctionClient { } } - getInstructions(scope: AssistantScope): InstructionOrCallback[] { - return this.instructions.filter(filterScopes(scope)).map((i) => i.instruction); + getInstructions(scopes: AssistantScope[]): InstructionOrCallback[] { + // for instructions we only want to use those explicitly assigned to one of the current scopes + // 'all' does not override scopes for instructions + return this.instructions + .filter((instructions) => instructions.scopes.some((scope) => scopes.includes(scope))) + .map((i) => i.instruction); } hasAction(name: string) { @@ -120,13 +124,13 @@ export class ChatFunctionClient { getFunctions({ filter, - scope, + scopes, }: { filter?: string; - scope?: AssistantScope; + scopes?: AssistantScope[]; } = {}): FunctionHandler[] { const allFunctions = Array.from(this.functionRegistry.values()) - .filter(filterScopes(scope)) + .filter(filterScopes(scopes)) .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.ts b/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/client/index.ts index 4eb0e54f9febe..e462f8ae2890b 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(this.dependencies.scopes), userInstructions, adHocInstructions, availableFunctionNames: functionClient - .getFunctions({ scope: this.dependencies.scope }) + .getFunctions({ scopes: this.dependencies.scopes }) .map((fn) => fn.definition.name), }), initialMessages @@ -301,7 +301,7 @@ export class ObservabilityAIAssistantClient { disableFunctions, tracer: completeTracer, connectorId, - scope: this.dependencies.scope, + scopes: this.dependencies.scopes, 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..1ee7b33fb1175 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 @@ -138,7 +138,7 @@ function getFunctionDefinitions({ functionClient, functionLimitExceeded, disableFunctions, - scope, + scopes, }: { functionClient: ChatFunctionClient; functionLimitExceeded: boolean; @@ -147,14 +147,14 @@ function getFunctionDefinitions({ | { except: string[]; }; - scope: AssistantScope; + scopes: AssistantScope[]; }) { if (functionLimitExceeded || disableFunctions === true) { return []; } let systemFunctions = functionClient - .getFunctions({ scope }) + .getFunctions({ scopes }) .map((fn) => fn.definition) .filter( (def) => @@ -187,7 +187,7 @@ export function continueConversation({ disableFunctions, tracer, connectorId, - scope, + scopes, useSimulatedFunctionCalling, }: { messages: Message[]; @@ -205,7 +205,7 @@ export function continueConversation({ }; tracer: LangTracer; connectorId: string; - scope: AssistantScope; + scopes: AssistantScope[]; useSimulatedFunctionCalling: boolean; }): Observable { let nextFunctionCallsLeft = functionCallsLeft; @@ -216,12 +216,12 @@ export function continueConversation({ functionLimitExceeded, functionClient, disableFunctions, - scope, + scopes, }); const messagesWithUpdatedSystemMessage = replaceSystemMessage( getSystemMessageFromInstructions({ - applicationInstructions: functionClient.getInstructions(scope), + applicationInstructions: functionClient.getInstructions(scopes), userInstructions, adHocInstructions, availableFunctionNames: definitions.map((def) => def.name), @@ -350,7 +350,7 @@ export function continueConversation({ disableFunctions, tracer, connectorId, - scope, + scopes, 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..2929a655eb721 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'], }); } 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]); } From 89b15b0c0e84800eea6fbc50c51e097c88528794 Mon Sep 17 00:00:00 2001 From: Sander Philipse Date: Wed, 16 Oct 2024 15:13:36 +0200 Subject: [PATCH 07/22] Filter functions based on required screencontexts as well --- .../src/utils/create_mock_chat_service.ts | 2 +- .../public/hooks/use_chat.test.ts | 6 +-- .../public/service/complete.test.ts | 2 +- .../service/create_chat_service.test.ts | 2 + .../public/service/create_chat_service.ts | 40 ++++++++++++------- .../service/create_mock_chat_service.ts | 2 +- .../public/service/create_service.ts | 9 ++++- .../server/routes/functions/route.ts | 20 +++++++--- .../service/chat_function_client/index.ts | 7 +++- .../server/service/client/index.test.ts | 2 +- 10 files changed, 62 insertions(+), 30 deletions(-) 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/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/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/create_chat_service.test.ts b/x-pack/plugins/observability_solution/observability_ai_assistant/public/service/create_chat_service.test.ts index d4145f2ba7ad5..a58a28decf8ff 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 @@ -18,6 +18,7 @@ import { concatenateChatCompletionChunks } from '../../common/utils/concatenate_ import type { ObservabilityAIAssistantChatService } from '../types'; import { createChatService } from './create_chat_service'; import { AssistantScope } from '@kbn/ai-assistant-common'; +import { ObservabilityAIAssistantScreenContext } from '../../common/types'; async function getConcatenatedMessage( response$: Observable @@ -72,6 +73,7 @@ describe('createChatService', () => { registrations: [], signal: new AbortController().signal, scope$: new BehaviorSubject(['observability']), + screenContexts$: new BehaviorSubject([]), }); }); 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 d77024ae4df16..6c65acb48686b 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 @@ -25,6 +25,7 @@ import { } from 'rxjs'; import { BehaviorSubject } from 'rxjs'; import type { AssistantScope } from '@kbn/ai-assistant-common'; +import { ObservabilityAIAssistantScreenContext } from '../../common/types'; import { ChatCompletionChunkEvent, Message, MessageRole } from '../../common'; import { StreamingChatResponseEventType, @@ -149,6 +150,7 @@ class ChatService { private registrations: ChatRegistrationRenderFunction[]; private systemMessage: string; public functions$: BehaviorSubject; + private screenContexts$: BehaviorSubject; constructor({ abortSignal, @@ -156,12 +158,14 @@ class ChatService { scope$, analytics, registrations, + screenContexts$, }: { abortSignal: AbortSignal; apiClient: ObservabilityAIAssistantAPIClient; scope$: BehaviorSubject; analytics: AnalyticsServiceStart; registrations: ChatRegistrationRenderFunction[]; + screenContexts$: BehaviorSubject; }) { this.functionRegistry = new Map(); this.renderFunctionRegistry = new Map(); @@ -171,6 +175,7 @@ class ChatService { this.analytics = analytics; this.registrations = registrations; this.systemMessage = ''; + this.screenContexts$ = screenContexts$; this.functions$ = new BehaviorSubject([] as FunctionDefinition[]); scope$.subscribe(() => { this.initialize(); @@ -187,21 +192,24 @@ class ChatService { async initialize() { this.functionRegistry = new Map(); const systemMessages: string[] = []; - const scopePromises = this.scope$.value.map((scope) => - this.apiClient('GET /internal/observability_ai_assistant/{scope}/functions', { - signal: this.abortSignal, - params: { - path: { - scope, - }, - }, - }).then(({ functionDefinitions, systemMessage }) => { - functionDefinitions.forEach((fn) => this.functionRegistry.set(fn.name, fn)); - systemMessages.push(systemMessage); - }) + const requiredFunctions = this.screenContexts$.value.flatMap( + (context) => context.actions?.flatMap((action) => action.parameters?.required || []) || [] ); + const scopePromise = this.apiClient('GET /internal/observability_ai_assistant/functions', { + signal: this.abortSignal, + params: { + query: { + scopes: this.getScopes(), + requiredFunctions: ['query', 'beer'], + }, + }, + }).then(({ functionDefinitions, systemMessage }) => { + functionDefinitions.forEach((fn) => this.functionRegistry.set(fn.name, fn)); + systemMessages.push(systemMessage); + }); + await Promise.all([ - ...scopePromises, + scopePromise, ...this.registrations.map((registration) => { return registration({ registerRenderFunction: (name, renderFn) => { @@ -255,8 +263,7 @@ class ChatService { definitions: Array.from(this.functionRegistry.values()), }).filter((value) => { return value.scopes - ? value.scopes?.some((scope) => this.getScopes().includes(scope)) || - value.scopes?.includes('all') + ? value.scopes?.some((scope) => scope === 'all' || this.getScopes().includes(scope)) : true; }); }; @@ -361,12 +368,14 @@ export async function createChatService({ registrations, apiClient, scope$, + screenContexts$, }: { analytics: AnalyticsServiceStart; signal: AbortSignal; registrations: ChatRegistrationRenderFunction[]; apiClient: ObservabilityAIAssistantAPIClient; scope$: BehaviorSubject; + screenContexts$: BehaviorSubject; }): Promise { return new ChatService({ analytics, @@ -374,5 +383,6 @@ export async function createChatService({ scope$, registrations, abortSignal: setupAbortSignal, + screenContexts$, }); } 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 dc0ce3dc12ce5..62751b8818a68 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 @@ -58,7 +58,14 @@ 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$, + screenContexts$, + }); }, callApi: apiClient, getScreenContexts, 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 67253b06c7b4a..36ddf699e85c3 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,11 @@ 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]), + requiredFunctions: t.union([t.string, t.array(t.string)]), }), }), options: { @@ -34,10 +35,17 @@ const getFunctionsRoute = createObservabilityAIAssistantServerRoute({ service, request, params: { - path: { scope }, + query: { scopes: inputScopes, requiredFunctions: inputFunctions }, }, } = resources; + const scopes = inputScopes ? (Array.isArray(inputScopes) ? inputScopes : [inputScopes]) : []; + const requiredFunctions = inputFunctions + ? Array.isArray(inputFunctions) + ? inputFunctions + : [inputFunctions] + : []; + const controller = new AbortController(); request.events.aborted$.subscribe(() => { controller.abort(); @@ -57,7 +65,7 @@ const getFunctionsRoute = createObservabilityAIAssistantServerRoute({ ]); const functionDefinitions = functionClient - .getFunctions({ scopes: [scope] }) + .getFunctions({ scopes, requiredFunctions }) .map((fn) => fn.definition); const availableFunctionNames = functionDefinitions.map((def) => def.name); @@ -65,7 +73,7 @@ const getFunctionsRoute = createObservabilityAIAssistantServerRoute({ return { functionDefinitions, systemMessage: getSystemMessageFromInstructions({ - applicationInstructions: functionClient.getInstructions([scope]), + applicationInstructions: functionClient.getInstructions(scopes), userInstructions, adHocInstructions: [], availableFunctionNames, 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 6ff7ba0b8657a..9866cd989709d 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 @@ -125,12 +125,17 @@ export class ChatFunctionClient { getFunctions({ filter, scopes, + requiredFunctions, }: { filter?: string; scopes?: AssistantScope[]; + requiredFunctions?: string[]; } = {}): FunctionHandler[] { const allFunctions = Array.from(this.functionRegistry.values()) - .filter(filterScopes(scopes)) + .filter( + (value) => + requiredFunctions?.includes(value.handler.definition.name) || filterScopes(scopes) + ) .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'], }); } From 9cece08f49d79c48d9d533191d0fb9f868fc7125 Mon Sep 17 00:00:00 2001 From: Sander Philipse Date: Wed, 16 Oct 2024 15:15:39 +0200 Subject: [PATCH 08/22] Fix FTRs --- .../tests/chat/chat.spec.ts | 6 +++--- .../tests/complete/complete.spec.ts | 8 ++++---- .../tests/complete/functions/helpers.ts | 2 +- .../knowledge_base_user_instructions.spec.ts | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) 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..f57b95a7c7c2b 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 @@ -60,7 +60,7 @@ export async function invokeChatCompleteWithFunctionRequest({ connectorId, persist: false, screenContexts: [], - scope: scope || 'observability', + 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 04e05fc9ad31b..dc0f991c66ee2 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 @@ -250,7 +250,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { connectorId, persist: true, screenContexts: [], - scope: 'observability', + scopes: ['observability'], }, }, }).expect(200); From 34a5ed038cbab28dd60fb25bd402ed89879d963c Mon Sep 17 00:00:00 2001 From: Sander Philipse Date: Wed, 16 Oct 2024 16:19:40 +0200 Subject: [PATCH 09/22] Remove silly test value from code --- .../public/service/create_chat_service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 6c65acb48686b..8da5e0b631a45 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 @@ -200,7 +200,7 @@ class ChatService { params: { query: { scopes: this.getScopes(), - requiredFunctions: ['query', 'beer'], + requiredFunctions, }, }, }).then(({ functionDefinitions, systemMessage }) => { From 55656906fc6a7cd28741e13d2880ed6d1ebf65b9 Mon Sep 17 00:00:00 2001 From: Sander Philipse Date: Wed, 16 Oct 2024 18:21:02 +0200 Subject: [PATCH 10/22] Update kbn ai assistant package with scopes --- .../src/conversation/conversation_view.tsx | 10 +++++----- .../src/hooks/__storybook_mocks__/use_conversation.ts | 2 +- x-pack/packages/kbn-ai-assistant/src/hooks/index.ts | 2 +- .../src/hooks/use_conversation.test.tsx | 6 +++--- .../kbn-ai-assistant/src/hooks/use_conversation.ts | 6 +++--- .../src/hooks/{use_scope.ts => use_scopes.ts} | 6 +++--- 6 files changed, 16 insertions(+), 16 deletions(-) rename x-pack/packages/kbn-ai-assistant/src/hooks/{use_scope.ts => use_scopes.ts} (79%) 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..27f0f7853c6b0 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 @@ -27,7 +27,7 @@ interface ConversationViewProps { navigateToConversation: (nextConversationId?: string) => void; getConversationHref?: (conversationId: string) => string; newConversationHref?: string; - scope?: AssistantScope; + scopes?: AssistantScope[]; } export const ConversationView: React.FC = ({ @@ -35,7 +35,7 @@ export const ConversationView: React.FC = ({ navigateToConversation, getConversationHref, newConversationHref, - scope, + scopes, }) => { const { euiTheme } = useEuiTheme(); @@ -61,10 +61,10 @@ export const ConversationView: React.FC = ({ ); useEffect(() => { - if (scope) { - service.setScope(scope); + if (scopes) { + 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']; }; From 5f99a455a20b6c6169da1f74545b8d40783c25aa Mon Sep 17 00:00:00 2001 From: Sander Philipse Date: Wed, 16 Oct 2024 18:23:55 +0200 Subject: [PATCH 11/22] Fix serverless FTRs --- .../observability/ai_assistant/tests/chat/chat.spec.ts | 6 +++--- .../ai_assistant/tests/complete/complete.spec.ts | 8 ++++---- .../ai_assistant/tests/complete/functions/helpers.ts | 2 +- .../knowledge_base_user_instructions.spec.ts | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) 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..812231c8f95d0 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 @@ -65,7 +65,7 @@ export async function invokeChatCompleteWithFunctionRequest({ connectorId, persist: false, screenContexts: [], - scope: 'observability', + scopes: ['observability'], }, }, }) 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, From 7962241654f0a076f17efbd8ba7fdbf5ddddbaa4 Mon Sep 17 00:00:00 2001 From: Sander Philipse Date: Wed, 16 Oct 2024 18:38:52 +0200 Subject: [PATCH 12/22] Fix ES function calling FTR --- .../tests/complete/functions/helpers.ts | 6 +++--- .../ai_assistant/tests/complete/functions/helpers.ts | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) 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 f57b95a7c7c2b..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_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 812231c8f95d0..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: [], - scopes: ['observability'], + scopes: scopes || (['observability'] as AssistantScope[]), }, }, }) From d7683148f0f086a44c5359971cfd76e708accaaf Mon Sep 17 00:00:00 2001 From: Sander Philipse Date: Thu, 17 Oct 2024 09:55:55 +0200 Subject: [PATCH 13/22] fix scopes in more places --- .../routes/conversations/conversation_view_with_props.tsx | 1 + .../scripts/evaluation/evaluation.ts | 2 +- .../scripts/evaluation/kibana_client.ts | 8 ++++---- .../server/rule_connector/index.ts | 4 ++-- .../routes/conversations/conversation_view_with_props.tsx | 2 +- 5 files changed, 9 insertions(+), 8 deletions(-) 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/rule_connector/index.ts b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/rule_connector/index.ts index d99e822484b67..f15731fb0c6fb 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, scope: 'observability' }); + const client = await resources.service.getClient({ request, scopes: ['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('observability'), + applicationInstructions: functionClient.getInstructions(['observability']), userInstructions: [], adHocInstructions: [], }), 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']} /> ); } From 3603216b09454f8356af15be82365874392c7d14 Mon Sep 17 00:00:00 2001 From: Sander Philipse Date: Fri, 18 Oct 2024 13:43:09 +0200 Subject: [PATCH 14/22] Remove scopes from functiondefinition --- .../observability_ai_assistant/common/functions/types.ts | 2 -- .../public/service/create_chat_service.ts | 9 ++++----- .../server/service/chat_function_client/index.ts | 2 +- 3 files changed, 5 insertions(+), 8 deletions(-) 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/service/create_chat_service.ts b/x-pack/plugins/observability_solution/observability_ai_assistant/public/service/create_chat_service.ts index 8da5e0b631a45..999caad2608b4 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 @@ -14,11 +14,13 @@ import { filter, from, map, + merge, Observable, of, OperatorFunction, scan, shareReplay, + Subscription, switchMap, throwError, timestamp, @@ -151,6 +153,7 @@ class ChatService { private systemMessage: string; public functions$: BehaviorSubject; private screenContexts$: BehaviorSubject; + private functionSubscription: Subscription; constructor({ abortSignal, @@ -177,7 +180,7 @@ class ChatService { this.systemMessage = ''; this.screenContexts$ = screenContexts$; this.functions$ = new BehaviorSubject([] as FunctionDefinition[]); - scope$.subscribe(() => { + this.functionSubscription = merge(scope$, screenContexts$).subscribe(() => { this.initialize(); }); } @@ -261,10 +264,6 @@ class ChatService { return filterFunctionDefinitions({ ...options, definitions: Array.from(this.functionRegistry.values()), - }).filter((value) => { - return value.scopes - ? value.scopes?.some((scope) => scope === 'all' || this.getScopes().includes(scope)) - : true; }); }; 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 9866cd989709d..19a355890c931 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 @@ -134,7 +134,7 @@ export class ChatFunctionClient { const allFunctions = Array.from(this.functionRegistry.values()) .filter( (value) => - requiredFunctions?.includes(value.handler.definition.name) || filterScopes(scopes) + requiredFunctions?.includes(value.handler.definition.name) || filterScopes(scopes)(value) ) .map(({ handler }) => handler); From 2e2bdb9941e1215ec321e1fbff9b41b87b38c0b1 Mon Sep 17 00:00:00 2001 From: Sander Philipse Date: Fri, 18 Oct 2024 16:23:21 +0200 Subject: [PATCH 15/22] Make special case for system prompts --- .../src/utils/filter_scopes.ts | 2 +- .../src/conversation/conversation_view.tsx | 3 ++- .../common/types.ts | 6 +++++- .../server/functions/index.ts | 12 +++++++++-- .../service/chat_function_client/index.ts | 21 ++++++++++++++++--- .../server/functions/index.ts | 6 +++++- 6 files changed, 41 insertions(+), 9 deletions(-) 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 823531d70d0f3..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 @@ -15,7 +15,7 @@ export function filterScopes( return true; } return value?.scopes - ? value.scopes.some((scope) => scope === 'all' || scopeFilters.includes(scope)) + ? 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 27f0f7853c6b0..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'; @@ -61,7 +62,7 @@ export const ConversationView: React.FC = ({ ); useEffect(() => { - if (scopes) { + if (scopes && !isEqual(scopes, service.getScopes())) { service.setScopes(scopes); } }, [scopes, service]); 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 7595c42e4dc93..597b7f22f1a3a 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 @@ -100,13 +100,17 @@ export interface Instruction { text: string; } +export interface SystemInstruction extends Instruction { + instruction_type: 'system_instruction'; +} + export interface AdHocInstruction { doc_id?: string; text: string; instruction_type: 'user_instruction' | 'application_instruction'; } -export type InstructionOrPlainText = string | Instruction; +export type InstructionOrPlainText = string | Instruction | SystemInstruction; export enum KnowledgeBaseType { // user instructions are included in the system prompt regardless of the user's input 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 6bb6b909dfb9c..b0c3c717c7967 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 @@ -39,7 +39,9 @@ export const registerFunctions: RegistrationCallback = async ({ const isServerless = !!resources.plugins.serverless; 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. + instruction: { + doc_id: 'observability_system_prompt', + text: `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. @@ -61,11 +63,15 @@ export const registerFunctions: RegistrationCallback = async ({ 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.`, + instruction_type: 'system_instruction', + }, scopes: ['observability'], }); 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. + instruction: { + doc_id: 'all_scoped_system_prompt', + text: `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. @@ -83,6 +89,8 @@ export const registerFunctions: RegistrationCallback = async ({ 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.`, + instruction_type: 'system_instruction', + }, scopes: ['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 19a355890c931..f4fd16c0f594f 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 @@ -11,7 +11,11 @@ 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 type { + Message, + ObservabilityAIAssistantScreenContextRequest, + SystemInstruction, +} from '../../../common/types'; import { filterFunctionDefinitions } from '../../../common/utils/filter_function_definitions'; import type { FunctionCallChatFunction, @@ -35,6 +39,13 @@ const ajv = new Ajv({ export const GET_DATA_ON_SCREEN_FUNCTION_NAME = 'get_data_on_screen'; +function instructionIsSystemMessage(value: InstructionOrCallback): value is SystemInstruction { + return ( + typeof value === 'object' && + (value as SystemInstruction).instruction_type === 'system_instruction' + ); +} + export class ChatFunctionClient { private readonly instructions: InstructionOrCallbackWithScopes[] = []; private readonly functionRegistry: FunctionHandlerRegistry = new Map(); @@ -111,10 +122,14 @@ export class ChatFunctionClient { } getInstructions(scopes: AssistantScope[]): InstructionOrCallback[] { - // for instructions we only want to use those explicitly assigned to one of the current scopes + // forsystem instructions we only want to use those explicitly assigned to one of the current scopes // 'all' does not override scopes for instructions return this.instructions - .filter((instructions) => instructions.scopes.some((scope) => scopes.includes(scope))) + .filter((instruction) => + instructionIsSystemMessage(instruction.instruction) + ? scopes.some((scope) => instruction.scopes.includes(scope)) + : filterScopes(scopes)(instruction) + ) .map((i) => i.instruction); } diff --git a/x-pack/plugins/search_assistant/server/functions/index.ts b/x-pack/plugins/search_assistant/server/functions/index.ts index c7998c4dc7102..1ad1733ac0ed7 100644 --- a/x-pack/plugins/search_assistant/server/functions/index.ts +++ b/x-pack/plugins/search_assistant/server/functions/index.ts @@ -11,7 +11,9 @@ 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. + instruction: { + doc_id: 'search_system_prompt', + text: `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. @@ -31,6 +33,8 @@ export const registerFunctions: (isServerless: boolean) => RegistrationCallback 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.`, + instruction_type: 'system_instruction', + }, scopes: ['search'], }); }; From 08e29c1f5ad827c1c006cbda6dbca1c505bc16be Mon Sep 17 00:00:00 2001 From: Sander Philipse Date: Fri, 18 Oct 2024 16:44:38 +0200 Subject: [PATCH 16/22] Remove unused functionsubscription --- .../public/service/create_chat_service.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) 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 999caad2608b4..815edf787bf7a 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 @@ -20,7 +20,6 @@ import { OperatorFunction, scan, shareReplay, - Subscription, switchMap, throwError, timestamp, @@ -153,7 +152,6 @@ class ChatService { private systemMessage: string; public functions$: BehaviorSubject; private screenContexts$: BehaviorSubject; - private functionSubscription: Subscription; constructor({ abortSignal, @@ -180,7 +178,7 @@ class ChatService { this.systemMessage = ''; this.screenContexts$ = screenContexts$; this.functions$ = new BehaviorSubject([] as FunctionDefinition[]); - this.functionSubscription = merge(scope$, screenContexts$).subscribe(() => { + merge(scope$, screenContexts$).subscribe(() => { this.initialize(); }); } From 2e5b00d465bd8d449fd42c5dc58db7148682e7d4 Mon Sep 17 00:00:00 2001 From: Sander Philipse Date: Fri, 18 Oct 2024 16:46:16 +0200 Subject: [PATCH 17/22] Remove unused functionsubscription --- .../public/service/create_chat_service.test.ts | 2 +- .../public/service/create_chat_service.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) 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 a58a28decf8ff..d81eefe5b221c 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 @@ -57,7 +57,7 @@ describe('createChatService', () => { } beforeEach(async () => { - clientSpy.mockImplementationOnce(async () => { + clientSpy.mockImplementation(async () => { return { functionDefinitions: [], contextDefinitions: [], 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 815edf787bf7a..9eaa602e68f57 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 @@ -14,7 +14,7 @@ import { filter, from, map, - merge, + combineLatest, Observable, of, OperatorFunction, @@ -178,7 +178,7 @@ class ChatService { this.systemMessage = ''; this.screenContexts$ = screenContexts$; this.functions$ = new BehaviorSubject([] as FunctionDefinition[]); - merge(scope$, screenContexts$).subscribe(() => { + combineLatest(scope$, screenContexts$).subscribe(() => { this.initialize(); }); } From d9472793da7c0643a85256c6abb5f572eca0712e Mon Sep 17 00:00:00 2001 From: Sander Philipse Date: Fri, 18 Oct 2024 18:01:55 +0200 Subject: [PATCH 18/22] Remove scopes from registering instruction --- .../common/types.ts | 6 +- .../server/functions/index.ts | 110 ++++++++---------- .../service/chat_function_client/index.ts | 21 +--- .../server/service/types.ts | 8 +- .../server/functions/query/index.ts | 2 +- .../server/functions/index.ts | 17 ++- 6 files changed, 63 insertions(+), 101 deletions(-) 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 597b7f22f1a3a..7595c42e4dc93 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 @@ -100,17 +100,13 @@ export interface Instruction { text: string; } -export interface SystemInstruction extends Instruction { - instruction_type: 'system_instruction'; -} - export interface AdHocInstruction { doc_id?: string; text: string; instruction_type: 'user_instruction' | 'application_instruction'; } -export type InstructionOrPlainText = string | Instruction | SystemInstruction; +export type InstructionOrPlainText = string | Instruction; export enum KnowledgeBaseType { // user instructions are included in the system prompt regardless of the user's input 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 b0c3c717c7967..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,50 +28,45 @@ 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: { - doc_id: 'observability_system_prompt', - text: `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. - 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! - 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. - 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. - 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. - 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"\`). + 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.`, - instruction_type: 'system_instruction', - }, - scopes: ['observability'], - }); + 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.`); + } - functions.registerInstruction({ - instruction: { - doc_id: 'all_scoped_system_prompt', - text: `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. + 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. @@ -88,55 +83,50 @@ export const registerFunctions: RegistrationCallback = async ({ 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.`, - instruction_type: 'system_instruction', - }, - scopes: ['all'], - }); + 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/service/chat_function_client/index.ts b/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/chat_function_client/index.ts index f4fd16c0f594f..e407d63c9e31f 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 @@ -11,11 +11,7 @@ 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, - SystemInstruction, -} from '../../../common/types'; +import type { Message, ObservabilityAIAssistantScreenContextRequest } from '../../../common/types'; import { filterFunctionDefinitions } from '../../../common/utils/filter_function_definitions'; import type { FunctionCallChatFunction, @@ -39,13 +35,6 @@ const ajv = new Ajv({ export const GET_DATA_ON_SCREEN_FUNCTION_NAME = 'get_data_on_screen'; -function instructionIsSystemMessage(value: InstructionOrCallback): value is SystemInstruction { - return ( - typeof value === 'object' && - (value as SystemInstruction).instruction_type === 'system_instruction' - ); -} - export class ChatFunctionClient { private readonly instructions: InstructionOrCallbackWithScopes[] = []; private readonly functionRegistry: FunctionHandlerRegistry = new Map(); @@ -124,13 +113,7 @@ export class ChatFunctionClient { getInstructions(scopes: AssistantScope[]): InstructionOrCallback[] { // forsystem instructions we only want to use those explicitly assigned to one of the current scopes // 'all' does not override scopes for instructions - return this.instructions - .filter((instruction) => - instructionIsSystemMessage(instruction.instruction) - ? scopes.some((scope) => instruction.scopes.includes(scope)) - : filterScopes(scopes)(instruction) - ) - .map((i) => i.instruction); + return this.instructions.map((i) => i.instruction); } hasAction(name: string) { 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..72ec2f5d059e2 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, @@ -100,4 +95,5 @@ export type RegistrationCallback = ({}: { resources: RespondFunctionResources; client: ObservabilityAIAssistantClient; functions: ChatFunctionClient; + scopes: AssistantScope[]; }) => Promise; 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..41d509c143785 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( { diff --git a/x-pack/plugins/search_assistant/server/functions/index.ts b/x-pack/plugins/search_assistant/server/functions/index.ts index 1ad1733ac0ed7..46da6767f359d 100644 --- a/x-pack/plugins/search_assistant/server/functions/index.ts +++ b/x-pack/plugins/search_assistant/server/functions/index.ts @@ -9,11 +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: { - doc_id: 'search_system_prompt', - text: `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. @@ -32,9 +31,7 @@ export const registerFunctions: (isServerless: boolean) => RegistrationCallback 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.`, - instruction_type: 'system_instruction', - }, - scopes: ['search'], - }); + If the user asks how to change the language, reply in the same language the user asked in.` + ); + } }; From acdc9c8d39105371a29dcc367e3dd235caafaf0f Mon Sep 17 00:00:00 2001 From: Sander Philipse Date: Mon, 21 Oct 2024 15:35:22 +0200 Subject: [PATCH 19/22] Remove scope from function registration, use it as a parameter instead --- .../server/functions/context.ts | 3 +- .../server/functions/elasticsearch.ts | 3 +- .../server/functions/execute_connector.ts | 3 +- .../functions/get_dataset_info/index.ts | 3 +- .../server/functions/kibana.ts | 3 +- .../server/functions/summarize.ts | 3 +- .../server/routes/chat/route.ts | 2 + .../server/routes/functions/route.ts | 15 +- .../service/chat_function_client/index.ts | 28 +- .../server/service/client/index.ts | 5 +- .../client/operators/continue_conversation.ts | 11 +- .../server/service/index.ts | 3 + .../server/service/types.ts | 8 +- .../server/functions/alerts.ts | 263 +++++++++--------- .../server/functions/changes/index.ts | 216 +++++++------- .../server/functions/lens.ts | 14 +- .../server/functions/query/index.ts | 6 +- .../server/functions/visualize_esql.ts | 3 +- .../server/rule_connector/index.ts | 3 +- 19 files changed, 279 insertions(+), 316 deletions(-) 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/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 9374036e5206b..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 @@ -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({ 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 36ddf699e85c3..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 @@ -19,7 +19,6 @@ const getFunctionsRoute = createObservabilityAIAssistantServerRoute({ params: t.type({ query: t.partial({ scopes: t.union([t.array(assistantScopeType), assistantScopeType]), - requiredFunctions: t.union([t.string, t.array(t.string)]), }), }), options: { @@ -35,16 +34,11 @@ const getFunctionsRoute = createObservabilityAIAssistantServerRoute({ service, request, params: { - query: { scopes: inputScopes, requiredFunctions: inputFunctions }, + query: { scopes: inputScopes }, }, } = resources; const scopes = inputScopes ? (Array.isArray(inputScopes) ? inputScopes : [inputScopes]) : []; - const requiredFunctions = inputFunctions - ? Array.isArray(inputFunctions) - ? inputFunctions - : [inputFunctions] - : []; const controller = new AbortController(); request.events.aborted$.subscribe(() => { @@ -59,21 +53,20 @@ const getFunctionsRoute = createObservabilityAIAssistantServerRoute({ resources, client, screenContexts: [], + scopes, }), // error is caught in client client.getKnowledgeBaseUserInstructions(), ]); - const functionDefinitions = functionClient - .getFunctions({ scopes, requiredFunctions }) - .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(scopes), + applicationInstructions: functionClient.getInstructions(), userInstructions, adHocInstructions: [], availableFunctionNames, 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 e407d63c9e31f..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,10 +107,8 @@ export class ChatFunctionClient { } } - getInstructions(scopes: AssistantScope[]): InstructionOrCallback[] { - // forsystem instructions we only want to use those explicitly assigned to one of the current scopes - // 'all' does not override scopes for instructions - return this.instructions.map((i) => i.instruction); + getInstructions(): InstructionOrCallback[] { + return this.instructions; } hasAction(name: string) { @@ -122,19 +117,10 @@ export class ChatFunctionClient { getFunctions({ filter, - scopes, - requiredFunctions, }: { filter?: string; - scopes?: AssistantScope[]; - requiredFunctions?: string[]; } = {}): FunctionHandler[] { - const allFunctions = Array.from(this.functionRegistry.values()) - .filter( - (value) => - requiredFunctions?.includes(value.handler.definition.name) || filterScopes(scopes)(value) - ) - .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.ts b/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/client/index.ts index e462f8ae2890b..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 @@ -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.scopes), + applicationInstructions: functionClient.getInstructions(), userInstructions, adHocInstructions, availableFunctionNames: functionClient - .getFunctions({ scopes: this.dependencies.scopes }) + .getFunctions() .map((fn) => fn.definition.name), }), initialMessages @@ -301,7 +301,6 @@ export class ObservabilityAIAssistantClient { disableFunctions, tracer: completeTracer, connectorId, - scopes: this.dependencies.scopes, 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 1ee7b33fb1175..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, - scopes, }: { functionClient: ChatFunctionClient; functionLimitExceeded: boolean; @@ -147,14 +145,13 @@ function getFunctionDefinitions({ | { except: string[]; }; - scopes: AssistantScope[]; }) { if (functionLimitExceeded || disableFunctions === true) { return []; } let systemFunctions = functionClient - .getFunctions({ scopes }) + .getFunctions() .map((fn) => fn.definition) .filter( (def) => @@ -187,7 +184,6 @@ export function continueConversation({ disableFunctions, tracer, connectorId, - scopes, useSimulatedFunctionCalling, }: { messages: Message[]; @@ -205,7 +201,6 @@ export function continueConversation({ }; tracer: LangTracer; connectorId: string; - scopes: AssistantScope[]; useSimulatedFunctionCalling: boolean; }): Observable { let nextFunctionCallsLeft = functionCallsLeft; @@ -216,12 +211,11 @@ export function continueConversation({ functionLimitExceeded, functionClient, disableFunctions, - scopes, }); const messagesWithUpdatedSystemMessage = replaceSystemMessage( getSystemMessageFromInstructions({ - applicationInstructions: functionClient.getInstructions(scopes), + applicationInstructions: functionClient.getInstructions(), userInstructions, adHocInstructions, availableFunctionNames: definitions.map((def) => def.name), @@ -350,7 +344,6 @@ export function continueConversation({ disableFunctions, tracer, connectorId, - scopes, 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 2929a655eb721..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 @@ -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 72ec2f5d059e2..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 @@ -82,13 +82,9 @@ 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; 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 41d509c143785..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 @@ -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 f15731fb0c6fb..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 @@ -160,6 +160,7 @@ async function executor( 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: [], }), From fdefbba76dbd204f57bcafd6b6d73b37b6624f57 Mon Sep 17 00:00:00 2001 From: Sander Philipse Date: Mon, 21 Oct 2024 16:30:29 +0200 Subject: [PATCH 20/22] Move function scopes to functions themselves --- .../public/service/create_chat_service.test.ts | 2 -- .../public/service/create_chat_service.ts | 15 +-------------- .../public/service/create_service.ts | 1 - 3 files changed, 1 insertion(+), 17 deletions(-) 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 d81eefe5b221c..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 @@ -18,7 +18,6 @@ import { concatenateChatCompletionChunks } from '../../common/utils/concatenate_ import type { ObservabilityAIAssistantChatService } from '../types'; import { createChatService } from './create_chat_service'; import { AssistantScope } from '@kbn/ai-assistant-common'; -import { ObservabilityAIAssistantScreenContext } from '../../common/types'; async function getConcatenatedMessage( response$: Observable @@ -73,7 +72,6 @@ describe('createChatService', () => { registrations: [], signal: new AbortController().signal, scope$: new BehaviorSubject(['observability']), - screenContexts$: new BehaviorSubject([]), }); }); 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 9eaa602e68f57..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 @@ -14,7 +14,6 @@ import { filter, from, map, - combineLatest, Observable, of, OperatorFunction, @@ -26,7 +25,6 @@ import { } from 'rxjs'; import { BehaviorSubject } from 'rxjs'; import type { AssistantScope } from '@kbn/ai-assistant-common'; -import { ObservabilityAIAssistantScreenContext } from '../../common/types'; import { ChatCompletionChunkEvent, Message, MessageRole } from '../../common'; import { StreamingChatResponseEventType, @@ -151,7 +149,6 @@ class ChatService { private registrations: ChatRegistrationRenderFunction[]; private systemMessage: string; public functions$: BehaviorSubject; - private screenContexts$: BehaviorSubject; constructor({ abortSignal, @@ -159,14 +156,12 @@ class ChatService { scope$, analytics, registrations, - screenContexts$, }: { abortSignal: AbortSignal; apiClient: ObservabilityAIAssistantAPIClient; scope$: BehaviorSubject; analytics: AnalyticsServiceStart; registrations: ChatRegistrationRenderFunction[]; - screenContexts$: BehaviorSubject; }) { this.functionRegistry = new Map(); this.renderFunctionRegistry = new Map(); @@ -176,9 +171,8 @@ class ChatService { this.analytics = analytics; this.registrations = registrations; this.systemMessage = ''; - this.screenContexts$ = screenContexts$; this.functions$ = new BehaviorSubject([] as FunctionDefinition[]); - combineLatest(scope$, screenContexts$).subscribe(() => { + scope$.subscribe(() => { this.initialize(); }); } @@ -193,15 +187,11 @@ class ChatService { async initialize() { this.functionRegistry = new Map(); const systemMessages: string[] = []; - const requiredFunctions = this.screenContexts$.value.flatMap( - (context) => context.actions?.flatMap((action) => action.parameters?.required || []) || [] - ); const scopePromise = this.apiClient('GET /internal/observability_ai_assistant/functions', { signal: this.abortSignal, params: { query: { scopes: this.getScopes(), - requiredFunctions, }, }, }).then(({ functionDefinitions, systemMessage }) => { @@ -365,14 +355,12 @@ export async function createChatService({ registrations, apiClient, scope$, - screenContexts$, }: { analytics: AnalyticsServiceStart; signal: AbortSignal; registrations: ChatRegistrationRenderFunction[]; apiClient: ObservabilityAIAssistantAPIClient; scope$: BehaviorSubject; - screenContexts$: BehaviorSubject; }): Promise { return new ChatService({ analytics, @@ -380,6 +368,5 @@ export async function createChatService({ scope$, registrations, abortSignal: setupAbortSignal, - screenContexts$, }); } 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 62751b8818a68..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 @@ -64,7 +64,6 @@ export function createService({ signal, registrations, scope$, - screenContexts$, }); }, callApi: apiClient, From 29026799363c2990790bf7fb3d28351983258646 Mon Sep 17 00:00:00 2001 From: Sander Philipse Date: Mon, 21 Oct 2024 16:40:23 +0200 Subject: [PATCH 21/22] Fix test type --- .../server/service/chat_function_client/index.test.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) 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 ); }); From 7f23587ada7de3b4fc2cb40f86b075af4e6d9fec Mon Sep 17 00:00:00 2001 From: Sander Philipse Date: Mon, 21 Oct 2024 17:58:04 +0200 Subject: [PATCH 22/22] Fix apm functions --- .../apm/server/assistant_functions/get_apm_dataset_info.ts | 3 +-- .../assistant_functions/get_apm_downstream_dependencies.ts | 3 +-- .../apm/server/assistant_functions/get_apm_services_list.ts | 3 +-- .../apm/server/assistant_functions/get_apm_timeseries.ts | 3 +-- .../apm/server/assistant_functions/index.ts | 5 ++++- 5 files changed, 8 insertions(+), 9 deletions(-) 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,