diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index f86823fc4d720..4d23236be6e5d 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1941,7 +1941,6 @@ x-pack/test/functional/services/index.ts @elastic/appex-qa /x-pack/platform/test/serverless @elastic/appex-qa /x-pack/test/api_integration/deployment_agnostic @elastic/appex-qa /x-pack/platform/test/functional/services/ml/api.ts @elastic/appex-qa # temporarily due to SKA tests relocation -/x-pack/test/api_integration/deployment_agnostic/apis/ @elastic/appex-qa # temporarily due to SKA tests relocation /x-pack/solutions/observability/test/functional/config.base.ts @elastic/appex-qa /x-pack/solutions/observability/test/functional/ftr_provider_context.d.ts @elastic/appex-qa /x-pack/solutions/observability/test/functional/page_objects/index.ts @elastic/appex-qa diff --git a/x-pack/solutions/observability/test/api_integration_deployment_agnostic/apis/ai_assistant/complete/functions/retrieve_elastic_doc.spec.ts b/x-pack/solutions/observability/test/api_integration_deployment_agnostic/apis/ai_assistant/complete/functions/retrieve_elastic_doc.spec.ts index 518e7128bd06c..c4def77899a05 100644 --- a/x-pack/solutions/observability/test/api_integration_deployment_agnostic/apis/ai_assistant/complete/functions/retrieve_elastic_doc.spec.ts +++ b/x-pack/solutions/observability/test/api_integration_deployment_agnostic/apis/ai_assistant/complete/functions/retrieve_elastic_doc.spec.ts @@ -10,12 +10,12 @@ import { ChatCompletionStreamParams } from 'openai/lib/ChatCompletionStream'; import { ChatCompletionMessageParam } from 'openai/resources'; import { last } from 'lodash'; import { MessageAddEvent, MessageRole } from '@kbn/observability-ai-assistant-plugin/common'; -import { TINY_ELSER_INFERENCE_ID } from '@kbn/test-suites-xpack/api_integration/deployment_agnostic/apis/observability/ai_assistant/utils/model_and_inference'; import { LlmProxy, createLlmProxy } from '../../utils/create_llm_proxy'; import { chatComplete } from '../../utils/conversation'; import type { DeploymentAgnosticFtrProviderContext } from '../../../../ftr_provider_context'; import { installProductDoc, uninstallProductDoc } from '../../utils/product_doc_base'; import { + TINY_ELSER_INFERENCE_ID, deployTinyElserAndSetupKb, teardownTinyElserModelAndInferenceEndpoint, } from '../../utils/model_and_inference'; diff --git a/x-pack/solutions/observability/test/tsconfig.json b/x-pack/solutions/observability/test/tsconfig.json index e949c29ed5fb2..472dba9c2196f 100644 --- a/x-pack/solutions/observability/test/tsconfig.json +++ b/x-pack/solutions/observability/test/tsconfig.json @@ -90,6 +90,5 @@ "@kbn/typed-react-router-config", "@kbn/observability-ai-assistant-app-plugin", "@kbn/dev-utils", - "@kbn/test-suites-xpack", ] } diff --git a/x-pack/test/api_integration/deployment_agnostic/apis/observability/ai_assistant/utils/advanced_settings.ts b/x-pack/test/api_integration/deployment_agnostic/apis/observability/ai_assistant/utils/advanced_settings.ts deleted file mode 100644 index a0a60ced56f62..0000000000000 --- a/x-pack/test/api_integration/deployment_agnostic/apis/observability/ai_assistant/utils/advanced_settings.ts +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { - ELASTIC_HTTP_VERSION_HEADER, - X_ELASTIC_INTERNAL_ORIGIN_REQUEST, -} from '@kbn/core-http-common'; - -import type SuperTest from 'supertest'; - -export const setAdvancedSettings = async ( - supertest: SuperTest.Agent, - settings: Record -) => { - return supertest - .post('/internal/kibana/settings') - .set('kbn-xsrf', 'true') - .set(ELASTIC_HTTP_VERSION_HEADER, '1') - .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana') - .send({ changes: settings }) - .expect(200); -}; diff --git a/x-pack/test/api_integration/deployment_agnostic/apis/observability/ai_assistant/utils/alerts.ts b/x-pack/test/api_integration/deployment_agnostic/apis/observability/ai_assistant/utils/alerts.ts deleted file mode 100644 index 70104d85c8fd3..0000000000000 --- a/x-pack/test/api_integration/deployment_agnostic/apis/observability/ai_assistant/utils/alerts.ts +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { InternalRequestHeader, RoleCredentials } from '@kbn/ftr-common-functional-services'; -import { ApmRuleType } from '@kbn/rule-data-utils'; -import { DeploymentAgnosticFtrProviderContext } from '../../../../ftr_provider_context'; -import { APM_ALERTS_INDEX, ApmAlertFields } from '../../apm/alerts/helpers/alerting_helper'; - -export const createRule = async ({ - getService, - roleAuthc, - data, -}: { - getService: DeploymentAgnosticFtrProviderContext['getService']; - roleAuthc: RoleCredentials; - internalReqHeader: InternalRequestHeader; - data?: any; -}) => { - const alertingApi = getService('alertingApi'); - const logger = getService('log'); - - try { - const createdRule = await alertingApi.createRule({ - ruleTypeId: data?.ruleTypeId || ApmRuleType.TransactionErrorRate, - name: data?.ruleName || 'APM transaction error rate', - consumer: data?.consumer || 'apm', - schedule: { interval: data?.interval || '1m' }, - tags: data?.tags || ['apm'], - params: { - environment: data?.environment || 'production', - threshold: data?.threshold || 1, - windowSize: data?.windowSize || 1, - windowUnit: data?.windowUnit || 'h', - }, - roleAuthc, - }); - - const ruleId = createdRule.id as string; - - await alertingApi.waitForDocumentInIndex({ - indexName: data?.indexName || APM_ALERTS_INDEX, - ruleId, - docCountTarget: data?.docCountTarget || 1, - }); - - return ruleId; - } catch (e) { - logger.error(`Failed to create alerting rule: ${e}`); - throw e; - } -}; - -export const runRule = async ({ - getService, - roleAuthc, - ruleId, -}: { - getService: DeploymentAgnosticFtrProviderContext['getService']; - roleAuthc: RoleCredentials; - internalReqHeader: InternalRequestHeader; - ruleId: string; -}) => { - const alertingApi = getService('alertingApi'); - return alertingApi.runRule(roleAuthc, ruleId); -}; - -export const deleteRules = async ({ - getService, - roleAuthc, -}: { - getService: DeploymentAgnosticFtrProviderContext['getService']; - roleAuthc: RoleCredentials; - internalReqHeader: InternalRequestHeader; -}) => { - const alertingApi = getService('alertingApi'); - return alertingApi.deleteRules({ roleAuthc }); -}; diff --git a/x-pack/test/api_integration/deployment_agnostic/apis/observability/ai_assistant/utils/conversation.ts b/x-pack/test/api_integration/deployment_agnostic/apis/observability/ai_assistant/utils/conversation.ts deleted file mode 100644 index 54c4dd53805fe..0000000000000 --- a/x-pack/test/api_integration/deployment_agnostic/apis/observability/ai_assistant/utils/conversation.ts +++ /dev/null @@ -1,225 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ -import expect from '@kbn/expect'; -import { Client } from '@elastic/elasticsearch'; -import { - ConversationCreateEvent, - ConversationUpdateEvent, - Message, - MessageAddEvent, - MessageRole, - StreamingChatResponseEvent, - StreamingChatResponseEventType, - type ConversationCreateRequest, -} from '@kbn/observability-ai-assistant-plugin/common'; -import { Readable } from 'stream'; -import type { AssistantScope } from '@kbn/ai-assistant-common'; -import { ObservabilityAIAssistantScreenContextRequest } from '@kbn/observability-ai-assistant-plugin/common/types'; -import { DeploymentAgnosticFtrProviderContext } from '../../../../ftr_provider_context'; -import type { ObservabilityAIAssistantApiClient } from '../../../../services/observability_ai_assistant_api'; - -export function decodeEvents(body: Readable | string) { - return String(body) - .split('\n') - .map((line) => line.trim()) - .filter(Boolean) - .map((line) => JSON.parse(line) as StreamingChatResponseEvent); -} - -export function getMessageAddedEvents(body: Readable | string) { - return decodeEvents(body).filter( - (event): event is MessageAddEvent => event.type === 'messageAdd' - ); -} - -export async function invokeChatCompleteWithFunctionRequest({ - connectorId, - observabilityAIAssistantAPIClient, - functionCall, - scopes, -}: { - connectorId: string; - observabilityAIAssistantAPIClient: ObservabilityAIAssistantApiClient; - functionCall: Message['message']['function_call']; - scopes?: AssistantScope[]; -}) { - const { status, body } = await observabilityAIAssistantAPIClient.editor({ - endpoint: 'POST /internal/observability_ai_assistant/chat/complete', - params: { - body: { - messages: [ - { - '@timestamp': new Date().toISOString(), - message: { - role: MessageRole.User, - content: 'Hello from user', - }, - }, - { - '@timestamp': new Date().toISOString(), - message: { - role: MessageRole.Assistant, - content: '', - function_call: functionCall, - }, - }, - ], - connectorId, - persist: false, - screenContexts: [], - scopes: scopes || ['observability' as AssistantScope], - }, - }, - }); - - expect(status).to.be(200); - - return body; -} - -export async function chatComplete({ - userPrompt, - screenContexts = [], - connectorId, - persist = false, - observabilityAIAssistantAPIClient, -}: { - userPrompt: string; - screenContexts?: ObservabilityAIAssistantScreenContextRequest[]; - connectorId: string; - persist?: boolean; - observabilityAIAssistantAPIClient: ObservabilityAIAssistantApiClient; -}) { - const { status, body } = await observabilityAIAssistantAPIClient.editor({ - endpoint: 'POST /internal/observability_ai_assistant/chat/complete', - params: { - body: { - messages: [ - { - '@timestamp': new Date().toISOString(), - message: { - role: MessageRole.User, - content: userPrompt, - }, - }, - ], - connectorId, - persist, - screenContexts, - scopes: ['observability' as const], - }, - }, - }); - - expect(status).to.be(200); - const messageEvents = decodeEvents(body); - const messageAddedEvents = getMessageAddedEvents(body); - const conversationCreateEvent = getConversationCreatedEvent(body); - return { messageAddedEvents, conversationCreateEvent, messageEvents, status }; -} - -// order of instructions can vary, so we sort to compare them -export function systemMessageSorted(message: string) { - return message - .split('\n\n') - .map((line) => line.trim()) - .sort(); -} - -export async function getSystemMessage( - getService: DeploymentAgnosticFtrProviderContext['getService'] -) { - const apiClient = getService('observabilityAIAssistantApi'); - - const { body } = await apiClient.editor({ - endpoint: 'GET /internal/observability_ai_assistant/functions', - params: { - query: { - scopes: ['observability'], - }, - }, - }); - - return body.systemMessage; -} - -export async function clearConversations(es: Client) { - const KB_INDEX = '.kibana-observability-ai-assistant-conversations-*'; - - return es.deleteByQuery({ - index: KB_INDEX, - conflicts: 'proceed', - query: { match_all: {} }, - refresh: true, - }); -} - -export function getConversationCreatedEvent(body: Readable | string) { - const decodedEvents = decodeEvents(body); - const conversationCreatedEvent = decodedEvents.find( - (event) => event.type === StreamingChatResponseEventType.ConversationCreate - ) as ConversationCreateEvent; - - return conversationCreatedEvent; -} - -export function getConversationUpdatedEvent(body: Readable | string) { - const decodedEvents = decodeEvents(body); - const conversationUpdatedEvent = decodedEvents.find( - (event) => event.type === StreamingChatResponseEventType.ConversationUpdate - ) as ConversationUpdateEvent; - - if (!conversationUpdatedEvent) { - throw new Error( - `No conversation updated event found: ${JSON.stringify(decodedEvents, null, 2)}` - ); - } - - return conversationUpdatedEvent; -} - -export const conversationCreate: ConversationCreateRequest = { - '@timestamp': new Date().toISOString(), - conversation: { - title: 'My title', - }, - labels: {}, - numeric_labels: {}, - systemMessage: 'this is a system message', - messages: [ - { - '@timestamp': new Date().toISOString(), - message: { - role: MessageRole.User, - content: 'My message', - }, - }, - ], - public: false, - archived: false, -}; - -export async function createConversation({ - observabilityAIAssistantAPIClient, - user = 'editor', - isPublic = false, -}: { - observabilityAIAssistantAPIClient: ObservabilityAIAssistantApiClient; - user?: 'admin' | 'editor' | 'viewer'; - isPublic?: boolean; -}) { - const response = await observabilityAIAssistantAPIClient[user]({ - endpoint: 'POST /internal/observability_ai_assistant/conversation', - params: { - body: { - conversation: { ...conversationCreate, public: isPublic }, - }, - }, - }); - - return response; -} diff --git a/x-pack/test/api_integration/deployment_agnostic/apis/observability/ai_assistant/utils/index_assets.ts b/x-pack/test/api_integration/deployment_agnostic/apis/observability/ai_assistant/utils/index_assets.ts deleted file mode 100644 index 14ff7b8286b01..0000000000000 --- a/x-pack/test/api_integration/deployment_agnostic/apis/observability/ai_assistant/utils/index_assets.ts +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import expect from '@kbn/expect'; -import { Client } from '@elastic/elasticsearch'; -import { - getResourceName, - resourceNames, -} from '@kbn/observability-ai-assistant-plugin/server/service'; -import { DeploymentAgnosticFtrProviderContext } from '../../../../ftr_provider_context'; -import type { ObservabilityAIAssistantApiClient } from '../../../../services/observability_ai_assistant_api'; -import { TINY_ELSER_INFERENCE_ID } from './model_and_inference'; -import { getConcreteWriteIndexFromAlias } from './knowledge_base'; - -export async function runStartupMigrations( - observabilityAIAssistantAPIClient: ObservabilityAIAssistantApiClient -) { - const { status } = await observabilityAIAssistantAPIClient.editor({ - endpoint: 'POST /internal/observability_ai_assistant/kb/migrations/startup', - }); - expect(status).to.be(200); -} - -export async function createOrUpdateIndexAssets( - observabilityAIAssistantAPIClient: ObservabilityAIAssistantApiClient -) { - const { status } = await observabilityAIAssistantAPIClient.editor({ - endpoint: 'POST /internal/observability_ai_assistant/index_assets', - params: { - query: { - inference_id: TINY_ELSER_INFERENCE_ID, - }, - }, - }); - expect(status).to.be(200); -} - -export async function deleteIndexAssets( - getService: DeploymentAgnosticFtrProviderContext['getService'] -) { - const es = getService('es'); - const log = getService('log'); - - // delete write indices - const response = await es.indices.get({ index: getResourceName('*') }); - const indicesToDelete = Object.keys(response); - - if (indicesToDelete.length > 0) { - log.debug(`Deleting indices: ${indicesToDelete.join(', ')}`); - try { - await Promise.all( - indicesToDelete.map(async (index) => es.indices.delete({ index, ignore_unavailable: true })) - ); - } catch (err) { - log.error(`Failed to delete indices: ${err}`); - } - } - - await es.indices.deleteIndexTemplate({ name: getResourceName('*') }, { ignore: [404] }); - await es.cluster.deleteComponentTemplate({ name: getResourceName('*') }, { ignore: [404] }); -} - -export async function restoreIndexAssets( - getService: DeploymentAgnosticFtrProviderContext['getService'] -) { - const observabilityAIAssistantAPIClient = getService('observabilityAIAssistantApi'); - const retry = getService('retry'); - const es = getService('es'); - const log = getService('log'); - - await retry.try(async () => { - log.debug('Restoring index assets'); - await deleteIndexAssets(getService); - await createOrUpdateIndexAssets(observabilityAIAssistantAPIClient); - expect(await getConcreteWriteIndexFromAlias(es)).to.be(resourceNames.concreteWriteIndexName.kb); - }); -} - -export async function getComponentTemplate(es: Client) { - const res = await es.cluster.getComponentTemplate({ - name: resourceNames.componentTemplate.kb, - }); - - return res.component_templates[0]; -} diff --git a/x-pack/test/api_integration/deployment_agnostic/apis/observability/ai_assistant/utils/kibana_mocks.ts b/x-pack/test/api_integration/deployment_agnostic/apis/observability/ai_assistant/utils/kibana_mocks.ts deleted file mode 100644 index 0968b5429fed6..0000000000000 --- a/x-pack/test/api_integration/deployment_agnostic/apis/observability/ai_assistant/utils/kibana_mocks.ts +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { Client } from '@elastic/elasticsearch'; -import { CoreSetup } from '@kbn/core/server'; -import { Logger } from '@kbn/logging'; -import { ObservabilityAIAssistantConfig } from '@kbn/observability-ai-assistant-plugin/server/config'; -import { ObservabilityAIAssistantPluginStartDependencies } from '@kbn/observability-ai-assistant-plugin/server/types'; -import { ToolingLog } from '@kbn/tooling-log'; - -export function getLoggerMock(toolingLog: ToolingLog) { - return { - debug: (...args: any[]) => toolingLog.debug(...args), - error: (errorOrMessage: Error) => toolingLog.error(errorOrMessage), - info: (...args: any[]) => toolingLog.info(...args), - warn: (...args: any[]) => toolingLog.warning(...args), - fatal: (...args: any[]) => toolingLog.warning(...args), - trace: (...args: any[]) => toolingLog.debug(...args), - get: () => getLoggerMock(toolingLog), - } as unknown as Logger; -} - -export function getCoreMock(es: Client) { - return { - getStartServices: async () => [{ elasticsearch: { client: { asInternalUser: es } } }], - } as unknown as CoreSetup; -} - -export function getConfigMock(config: Partial) { - return { - enableKnowledgeBase: true, - ...config, - } as ObservabilityAIAssistantConfig; -} diff --git a/x-pack/test/api_integration/deployment_agnostic/apis/observability/ai_assistant/utils/knowledge_base.ts b/x-pack/test/api_integration/deployment_agnostic/apis/observability/ai_assistant/utils/knowledge_base.ts deleted file mode 100644 index b2322bbc670a1..0000000000000 --- a/x-pack/test/api_integration/deployment_agnostic/apis/observability/ai_assistant/utils/knowledge_base.ts +++ /dev/null @@ -1,297 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { Client } from '@elastic/elasticsearch'; -import { - Instruction, - KnowledgeBaseEntry, - KnowledgeBaseState, -} from '@kbn/observability-ai-assistant-plugin/common/types'; -import { resourceNames } from '@kbn/observability-ai-assistant-plugin/server/service'; -import expect from '@kbn/expect'; -import pRetry from 'p-retry'; -import { DeploymentAgnosticFtrProviderContext } from '../../../../ftr_provider_context'; -import { setAdvancedSettings } from './advanced_settings'; -import { TINY_ELSER_INFERENCE_ID } from './model_and_inference'; -import type { ObservabilityAIAssistantApiClient } from '../../../../services/observability_ai_assistant_api'; - -export async function clearKnowledgeBase(es: Client) { - return pRetry( - () => { - return es.deleteByQuery({ - index: resourceNames.indexPatterns.kb, - conflicts: 'proceed', - query: { match_all: {} }, - refresh: true, - }); - }, - { retries: 5 } - ); -} - -export async function waitForKnowledgeBaseIndex( - getService: DeploymentAgnosticFtrProviderContext['getService'], - expectedIndex: string -) { - const retry = getService('retry'); - const es = getService('es'); - - await retry.try(async () => { - const currentIndex = await getConcreteWriteIndexFromAlias(es); - expect(currentIndex).to.be(expectedIndex); - }); -} - -export async function getKnowledgeBaseStatus( - observabilityAIAssistantAPIClient: ObservabilityAIAssistantApiClient -) { - return observabilityAIAssistantAPIClient.editor({ - endpoint: 'GET /internal/observability_ai_assistant/kb/status', - }); -} - -export async function waitForKnowledgeBaseReady( - getService: DeploymentAgnosticFtrProviderContext['getService'] -) { - const retry = getService('retry'); - const log = getService('log'); - const observabilityAIAssistantAPIClient = getService('observabilityAIAssistantApi'); - - await retry.tryForTime(5 * 60 * 1000, async () => { - log.debug(`Waiting for knowledge base to be ready...`); - const { body, status } = await getKnowledgeBaseStatus(observabilityAIAssistantAPIClient); - - const { kbState, isReIndexing, concreteWriteIndex, currentInferenceId } = body; - if (status !== 200 || kbState !== KnowledgeBaseState.READY) { - log.warning(`Knowledge base is not ready yet: - Status code: ${status} - State: ${kbState} - isReIndexing: ${isReIndexing} - concreteWriteIndex: ${concreteWriteIndex} - currentInferenceId: ${currentInferenceId}`); - } - - expect(status).to.be(200); - expect(kbState).to.be(KnowledgeBaseState.READY); - expect(isReIndexing).to.be(false); - log.info(`Knowledge base is in ready state.`); - }); -} - -export async function setupKnowledgeBase( - getService: DeploymentAgnosticFtrProviderContext['getService'], - inferenceId: string -) { - const observabilityAIAssistantAPIClient = getService('observabilityAIAssistantApi'); - const log = getService('log'); - - const statusResult = await getKnowledgeBaseStatus(observabilityAIAssistantAPIClient); - - log.debug( - `Setting up knowledge base with inferenceId = "${inferenceId}", concreteWriteIndex = ${statusResult.body.concreteWriteIndex}, currentInferenceId = ${statusResult.body.currentInferenceId}, isReIndexing = ${statusResult.body.isReIndexing}` - ); - const { body, status } = await observabilityAIAssistantAPIClient.admin({ - endpoint: 'POST /internal/observability_ai_assistant/kb/setup', - params: { - query: { inference_id: inferenceId, wait_until_complete: true }, - }, - }); - - if (status !== 200) { - log.warning(`Failed to setup knowledge base: - Status code: ${status} - Body: ${JSON.stringify(body, null, 2)}`); - } - - return { body, status }; -} - -export async function addSampleDocsToInternalKb( - getService: DeploymentAgnosticFtrProviderContext['getService'], - sampleDocs: Array -) { - const observabilityAIAssistantAPIClient = getService('observabilityAIAssistantApi'); - const es = getService('es'); - const log = getService('log'); - const retry = getService('retry'); - - await observabilityAIAssistantAPIClient.editor({ - endpoint: 'POST /internal/observability_ai_assistant/kb/entries/import', - params: { - body: { - entries: sampleDocs, - }, - }, - }); - - await refreshKbIndex(es); - - await retry.try(async () => { - const itemsInKb = await getKnowledgeBaseEntriesFromEs(es); - log.debug( - `Waiting for at least ${sampleDocs.length} docs to be available for search in KB. Currently ${itemsInKb.length} docs available.` - ); - expect(itemsInKb.length >= sampleDocs.length).to.be(true); - }); -} - -// refresh the index to make sure the documents are searchable -export function refreshKbIndex(es: Client) { - return es.indices.refresh({ index: resourceNames.indexPatterns.kb }); -} - -export async function addSampleDocsToCustomIndex( - getService: DeploymentAgnosticFtrProviderContext['getService'], - sampleDocs: Array, - customSearchConnectorIndex: string -) { - const es = getService('es'); - const supertest = getService('supertest'); - const log = getService('log'); - - // create index with semantic_text mapping for `text` field - log.info('Creating custom index with sample animal docs...'); - await es.indices.create({ - index: customSearchConnectorIndex, - mappings: { - properties: { - title: { type: 'text' }, - text: { type: 'semantic_text', inference_id: TINY_ELSER_INFERENCE_ID }, - }, - }, - }); - - log.info('Indexing sample animal docs...'); - // ingest sampleDocs - await Promise.all( - sampleDocs.map(async (doc) => { - const { id, ...restDoc } = doc; - return es.index({ - refresh: 'wait_for', - index: customSearchConnectorIndex, - id, - body: restDoc, - }); - }) - ); - - // update the advanced settings (`observability:aiAssistantSearchConnectorIndexPattern`) to include the custom index - await setAdvancedSettings(supertest, { - 'observability:aiAssistantSearchConnectorIndexPattern': customSearchConnectorIndex, - }); -} - -export async function getKbIndices(es: Client) { - const res = await es.cat.indices({ - index: resourceNames.indexPatterns.kb, - format: 'json', - h: 'index', - }); - - return res.map(({ index }) => index!); -} - -export async function deleteKbIndices(es: Client) { - const index = await getKbIndices(es); - if (index.length > 0) { - await es.indices.delete({ index, ignore_unavailable: true }); - } -} - -export async function getConcreteWriteIndexFromAlias(es: Client) { - const response = await es.indices.getAlias({ index: resourceNames.writeIndexAlias.kb }); - const writeIndex = Object.entries(response).find( - ([index, aliasInfo]) => aliasInfo.aliases[resourceNames.writeIndexAlias.kb]?.is_write_index - )?.[0]; - - if (!writeIndex) { - throw new Error(`Could not find write index for alias ${resourceNames.writeIndexAlias.kb}`); - } - - return writeIndex; -} - -export async function getKbIndexCreatedVersion(es: Client) { - const indexSettings = await es.indices.getSettings({ - index: resourceNames.writeIndexAlias.kb, - human: true, - }); - - const { settings } = Object.values(indexSettings)[0]; - const createdVersion = settings?.index?.version?.created_string; - if (!createdVersion) { - throw new Error(`Could not find created version for index ${resourceNames.writeIndexAlias.kb}`); - } - return createdVersion; -} - -export async function reIndexKnowledgeBase( - observabilityAIAssistantAPIClient: ObservabilityAIAssistantApiClient -) { - return observabilityAIAssistantAPIClient.admin({ - endpoint: 'POST /internal/observability_ai_assistant/kb/reindex', - }); -} - -interface SemanticTextField { - semantic_text: string; - _inference_fields: { - semantic_text: { - inference: { - inference_id: string; - chunks: { - semantic_text: Array<{ - embeddings: - | Record // sparse embedding - | number[]; // dense embedding; - }>; - }; - }; - }; - }; -} - -export async function getKnowledgeBaseEntriesFromEs(es: Client, size = 1000) { - const res = await es.search({ - size, - index: resourceNames.writeIndexAlias.kb, - // Add fields parameter to include inference metadata - fields: ['_inference_fields'], - query: { - match_all: {}, - }, - }); - - return res.hits.hits; -} - -export async function addKnowledgeBaseEntryToEs(es: Client, entry: KnowledgeBaseEntry) { - const result = await es.index({ - index: resourceNames.writeIndexAlias.kb, - document: entry, - refresh: true, - }); - - return result; -} - -export function getKnowledgeBaseEntriesFromApi({ - observabilityAIAssistantAPIClient, - query = '', - sortBy = 'title', - sortDirection = 'asc', -}: { - observabilityAIAssistantAPIClient: ObservabilityAIAssistantApiClient; - query?: string; - sortBy?: string; - sortDirection?: 'asc' | 'desc'; -}) { - return observabilityAIAssistantAPIClient.editor({ - endpoint: 'GET /internal/observability_ai_assistant/kb/entries', - params: { query: { query, sortBy, sortDirection } }, - }); -} diff --git a/x-pack/test/api_integration/deployment_agnostic/apis/observability/ai_assistant/utils/model_and_inference.ts b/x-pack/test/api_integration/deployment_agnostic/apis/observability/ai_assistant/utils/model_and_inference.ts deleted file mode 100644 index 5acfe03d4c61a..0000000000000 --- a/x-pack/test/api_integration/deployment_agnostic/apis/observability/ai_assistant/utils/model_and_inference.ts +++ /dev/null @@ -1,273 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { Client, errors } from '@elastic/elasticsearch'; -import { ToolingLog } from '@kbn/tooling-log'; -import { InferenceTaskType } from '@elastic/elasticsearch/lib/api/types'; -import pRetry, { AbortError } from 'p-retry'; -import pTimeout, { TimeoutError } from 'p-timeout'; -import { SUPPORTED_TRAINED_MODELS } from '@kbn/test-suites-xpack-platform/functional/services/ml/api'; -import { DeploymentAgnosticFtrProviderContext } from '../../../../ftr_provider_context'; -import { setupKnowledgeBase, waitForKnowledgeBaseReady } from './knowledge_base'; - -// tiny models -export const TINY_ELSER_MODEL_ID = SUPPORTED_TRAINED_MODELS.TINY_ELSER.name; -export const TINY_TEXT_EMBEDDING_MODEL_ID = SUPPORTED_TRAINED_MODELS.TINY_TEXT_EMBEDDING.name; - -// tiny inference endpoints -export const TINY_ELSER_INFERENCE_ID = 'pt_tiny_elser_inference_id'; -export const TINY_TEXT_EMBEDDING_INFERENCE_ID = 'pt_tiny_text_embedding_inference_id'; - -export async function importModel( - getService: DeploymentAgnosticFtrProviderContext['getService'], - { - modelId, - }: { - modelId: typeof TINY_ELSER_MODEL_ID | typeof TINY_TEXT_EMBEDDING_MODEL_ID; - } -) { - const ml = getService('ml'); - const log = getService('log'); - - const config = ml.api.getTrainedModelConfig(modelId); - await ml.api.assureMlStatsIndexExists(); - - try { - await ml.api.importTrainedModel(modelId, modelId, config); - log.info(`Model "${modelId}" imported successfully.`); - } catch (error) { - if ( - error.message.includes('resource_already_exists_exception') || - error.message.includes( - 'the model id is the same as the deployment id of a current model deployment' - ) - ) { - log.info(`Model "${modelId}" is already imported. Skipping import.`); - return; - } - - log.error(`Could not import model "${modelId}": ${error}`); - throw error; - } -} - -export async function startModelDeployment( - getService: DeploymentAgnosticFtrProviderContext['getService'], - { - modelId, - }: { - modelId: typeof TINY_ELSER_MODEL_ID | typeof TINY_TEXT_EMBEDDING_MODEL_ID; - } -) { - const ml = getService('ml'); - const log = getService('log'); - - try { - await ml.api.startTrainedModelDeploymentES(modelId); - } catch (error) { - if ( - error.message.includes( - 'Could not start model deployment because an existing deployment with the same id' - ) - ) { - log.info(`Model "${modelId}" is already started. Skipping starting deployment.`); - return; - } - - throw error; - } -} - -export async function setupTinyElserModelAndInferenceEndpoint( - getService: DeploymentAgnosticFtrProviderContext['getService'] -) { - await retryOnTimeout(() => importModel(getService, { modelId: TINY_ELSER_MODEL_ID })); - await retryOnTimeout(() => - createTinyElserInferenceEndpoint(getService, { inferenceId: TINY_ELSER_INFERENCE_ID }) - ); -} - -export async function teardownTinyElserModelAndInferenceEndpoint( - getService: DeploymentAgnosticFtrProviderContext['getService'] -) { - await deleteModel(getService, { modelId: TINY_ELSER_MODEL_ID }); - await deleteInferenceEndpoint(getService, { inferenceId: TINY_ELSER_INFERENCE_ID }); -} - -export function createTinyElserInferenceEndpoint( - getService: DeploymentAgnosticFtrProviderContext['getService'], - { inferenceId }: { inferenceId: string } -) { - const es = getService('es'); - const log = getService('log'); - - return createInferenceEndpoint({ - es, - log, - modelId: TINY_ELSER_MODEL_ID, - inferenceId, - taskType: 'sparse_embedding', - }); -} - -export function createTinyTextEmbeddingInferenceEndpoint( - getService: DeploymentAgnosticFtrProviderContext['getService'], - { inferenceId }: { inferenceId: string } -) { - const es = getService('es'); - const log = getService('log'); - - return createInferenceEndpoint({ - es, - log, - modelId: TINY_TEXT_EMBEDDING_MODEL_ID, - inferenceId, - taskType: 'text_embedding', - }); -} - -export async function deployTinyElserAndSetupKb( - getService: DeploymentAgnosticFtrProviderContext['getService'] -) { - await setupTinyElserModelAndInferenceEndpoint(getService); - - const { status, body } = await setupKnowledgeBase(getService, TINY_ELSER_INFERENCE_ID); - await waitForKnowledgeBaseReady(getService); - - return { status, body }; -} - -export async function deleteInferenceEndpoint( - getService: DeploymentAgnosticFtrProviderContext['getService'], - { - inferenceId, - }: { - inferenceId: string; - } -) { - const es = getService('es'); - const log = getService('log'); - - try { - await es.inference.delete({ inference_id: inferenceId, force: true }); - log.info(`Inference endpoint "${inferenceId}" deleted.`); - } catch (e) { - if (e.message.includes('resource_not_found_exception')) { - log.debug(`Inference endpoint "${inferenceId}" was already deleted. Skipping deletion.`); - return; - } - - log.error(`Could not delete inference endpoint "${inferenceId}": ${e}`); - throw e; - } -} - -export async function createInferenceEndpoint({ - es, - log, - inferenceId, - modelId, - taskType, -}: { - es: Client; - log: ToolingLog; - inferenceId: string; - modelId: string; - taskType?: InferenceTaskType; -}) { - return pRetry( - async () => { - try { - const res = await es.inference.put({ - inference_id: inferenceId, - task_type: taskType, - inference_config: { - service: 'elasticsearch', - service_settings: { - model_id: modelId, - adaptive_allocations: { enabled: true, min_number_of_allocations: 1 }, - num_threads: 1, - }, - task_settings: {}, - }, - }); - - log.info(`Inference endpoint ${inferenceId} created.`); - return res; - } catch (error) { - if ( - error instanceof errors.ResponseError && - (error.body?.error?.type === 'resource_not_found_exception' || - error.body?.error?.type === 'status_exception') - ) { - log.debug(`Inference endpoint "${inferenceId}" already exists. Skipping creation.`); - return; - } - - log.error(`Error creating inference endpoint "${inferenceId}": ${error}`); - throw error; - } - }, - { retries: 2 } - ); -} - -export async function deleteModel( - getService: DeploymentAgnosticFtrProviderContext['getService'], - { - modelId, - }: { - modelId: typeof TINY_ELSER_MODEL_ID | typeof TINY_TEXT_EMBEDDING_MODEL_ID; - } -) { - const log = getService('log'); - const ml = getService('ml'); - - try { - await ml.api.stopTrainedModelDeploymentES(modelId, true); - await ml.api.deleteTrainedModelES(modelId); - await ml.testResources.cleanMLSavedObjects(); - log.info(`Knowledge base model deleted.`); - } catch (e) { - if (e.message.includes('resource_not_found_exception')) { - log.debug(`Knowledge base model was already deleted.`); - } else { - log.error(`Could not delete knowledge base model: ${e}`); - } - } -} - -export async function stopTinyElserModel( - getService: DeploymentAgnosticFtrProviderContext['getService'] -) { - const log = getService('log'); - const ml = getService('ml'); - - try { - await ml.api.stopTrainedModelDeploymentES(TINY_ELSER_INFERENCE_ID, true); - log.info(`Knowledge base model (${TINY_ELSER_MODEL_ID}) stopped.`); - } catch (e) { - log.error(`Could not stop knowledge base model (${TINY_ELSER_MODEL_ID}): ${e}`); - } -} - -async function retryOnTimeout(fn: () => Promise, timeout = 60_000): Promise { - return pRetry( - async () => { - try { - return await pTimeout(fn(), { milliseconds: timeout }); - } catch (err: any) { - if (!(err instanceof TimeoutError)) { - throw new AbortError(err); // don't retry on non-timeout errors - } - - throw err; // retry on timeout errors - } - }, - { retries: 2, minTimeout: 5_000, maxTimeout: 5_000 } - ); -} diff --git a/x-pack/test/api_integration/deployment_agnostic/apis/observability/ai_assistant/utils/observability_ai_assistant_api_client.ts b/x-pack/test/api_integration/deployment_agnostic/apis/observability/ai_assistant/utils/observability_ai_assistant_api_client.ts deleted file mode 100644 index e54e0660caa53..0000000000000 --- a/x-pack/test/api_integration/deployment_agnostic/apis/observability/ai_assistant/utils/observability_ai_assistant_api_client.ts +++ /dev/null @@ -1,168 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import type { - APIReturnType, - ObservabilityAIAssistantAPIClientRequestParamsOf, - ObservabilityAIAssistantAPIEndpoint, -} from '@kbn/observability-ai-assistant-plugin/public'; -import { formatRequest } from '@kbn/server-route-repository'; -import supertest from 'supertest'; -import { Subtract } from 'utility-types'; -import { format, UrlObject } from 'url'; -import { kbnTestConfig } from '@kbn/test'; - -export function getScopedApiClient(kibanaServer: UrlObject, username: string) { - const { password } = kbnTestConfig.getUrlParts(); - const baseUrlWithAuth = format({ - ...kibanaServer, - auth: `${username}:${password}`, - }); - - return createObservabilityAIAssistantApiClient(supertest(baseUrlWithAuth)); -} - -export type ObservabilityAIAssistantApiClient = ReturnType< - typeof createObservabilityAIAssistantApiClient ->; -export function createObservabilityAIAssistantApiClient(st: supertest.Agent) { - return ( - options: { - type?: 'form-data'; - endpoint: TEndpoint; - } & ObservabilityAIAssistantAPIClientRequestParamsOf & { - params?: { query?: { _inspect?: boolean } }; - } - ): SupertestReturnType => { - const { endpoint, type } = options; - - const params = 'params' in options ? (options.params as Record) : {}; - - const { method, pathname, version } = formatRequest(endpoint, params.path); - const url = format({ pathname, query: params?.query }); - - const headers: Record = { 'kbn-xsrf': 'foo' }; - - if (version) { - headers['Elastic-Api-Version'] = version; - } - - let res: supertest.Test; - if (type === 'form-data') { - const fields: Array<[string, any]> = Object.entries(params.body); - const formDataRequest = st[method](url) - .set(headers) - .set('Content-type', 'multipart/form-data'); - - for (const field of fields) { - void formDataRequest.field(field[0], field[1]); - } - - res = formDataRequest; - } else if (params.body) { - res = st[method](url).send(params.body).set(headers); - } else { - res = st[method](url).set(headers); - } - - return res as unknown as SupertestReturnType; - }; -} - -export type ObservabilityAIAssistantAPIClient = ReturnType< - typeof createObservabilityAIAssistantApiClient ->; - -type WithoutPromise> = Subtract>; - -// this is a little intense, but without it, method overrides are lost -// e.g., { -// end(one:string) -// end(one:string, two:string) -// } -// would lose the first signature. This keeps up to eight signatures. -type OverloadedParameters = T extends { - (...args: infer A1): any; - (...args: infer A2): any; - (...args: infer A3): any; - (...args: infer A4): any; - (...args: infer A5): any; - (...args: infer A6): any; - (...args: infer A7): any; - (...args: infer A8): any; -} - ? A1 | A2 | A3 | A4 | A5 | A6 | A7 | A8 - : T extends { - (...args: infer A1): any; - (...args: infer A2): any; - (...args: infer A3): any; - (...args: infer A4): any; - (...args: infer A5): any; - (...args: infer A6): any; - (...args: infer A7): any; - } - ? A1 | A2 | A3 | A4 | A5 | A6 | A7 - : T extends { - (...args: infer A1): any; - (...args: infer A2): any; - (...args: infer A3): any; - (...args: infer A4): any; - (...args: infer A5): any; - (...args: infer A6): any; - } - ? A1 | A2 | A3 | A4 | A5 | A6 - : T extends { - (...args: infer A1): any; - (...args: infer A2): any; - (...args: infer A3): any; - (...args: infer A4): any; - (...args: infer A5): any; - } - ? A1 | A2 | A3 | A4 | A5 - : T extends { - (...args: infer A1): any; - (...args: infer A2): any; - (...args: infer A3): any; - (...args: infer A4): any; - } - ? A1 | A2 | A3 | A4 - : T extends { - (...args: infer A1): any; - (...args: infer A2): any; - (...args: infer A3): any; - } - ? A1 | A2 | A3 - : T extends { - (...args: infer A1): any; - (...args: infer A2): any; - } - ? A1 | A2 - : T extends (...args: infer A) => any - ? A - : any; - -type OverrideReturnType any, TNextReturnType> = ( - ...args: OverloadedParameters -) => WithoutPromise> & TNextReturnType; - -type OverwriteThisMethods, TNextReturnType> = TNextReturnType & { - [key in keyof T]: T[key] extends (...args: infer TArgs) => infer TReturnType - ? TReturnType extends Promise - ? OverrideReturnType - : (...args: TArgs) => TReturnType - : T[key]; -}; - -export type SupertestReturnType = - OverwriteThisMethods< - WithoutPromise, - Promise<{ - text: string; - status: number; - body: APIReturnType; - }> - >; diff --git a/x-pack/test/api_integration/deployment_agnostic/apis/observability/ai_assistant/utils/product_doc_base.ts b/x-pack/test/api_integration/deployment_agnostic/apis/observability/ai_assistant/utils/product_doc_base.ts deleted file mode 100644 index 97cdf016ee785..0000000000000 --- a/x-pack/test/api_integration/deployment_agnostic/apis/observability/ai_assistant/utils/product_doc_base.ts +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { - ELASTIC_HTTP_VERSION_HEADER, - X_ELASTIC_INTERNAL_ORIGIN_REQUEST, -} from '@kbn/core-http-common'; - -import type SuperTest from 'supertest'; - -export async function installProductDoc(supertest: SuperTest.Agent, inferenceId: string) { - return supertest - .post('/internal/product_doc_base/install') - .set(ELASTIC_HTTP_VERSION_HEADER, '1') - .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana') - .set('kbn-xsrf', 'foo') - .send({ - inferenceId, - }) - .expect(200); -} - -export async function uninstallProductDoc(supertest: SuperTest.Agent, inferenceId: string) { - return supertest - .post('/internal/product_doc_base/uninstall') - .set(ELASTIC_HTTP_VERSION_HEADER, '1') - .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana') - .set('kbn-xsrf', 'foo') - .send({ - inferenceId, - }) - .expect(200); -} diff --git a/x-pack/test/api_integration/deployment_agnostic/apis/observability/ai_assistant/utils/sample_docs.ts b/x-pack/test/api_integration/deployment_agnostic/apis/observability/ai_assistant/utils/sample_docs.ts deleted file mode 100644 index 892b99079bc98..0000000000000 --- a/x-pack/test/api_integration/deployment_agnostic/apis/observability/ai_assistant/utils/sample_docs.ts +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -export const animalSampleDocs = [ - { - id: 'animal_elephants_social_structure', - title: 'Elephants and Their Social Structure', - text: 'Elephants are highly social animals that live in matriarchal herds led by the oldest female. These animals communicate through low-frequency sounds, called infrasound, that travel long distances. They are known for their intelligence, strong memory, and deep emotional bonds with each other.', - }, - { - id: 'animal_cheetah_life_speed', - title: 'The Life of a Cheetah', - text: 'Cheetahs are the fastest land animals, capable of reaching speeds up to 60 miles per hour in short bursts. They rely on their speed to catch prey, such as gazelles. Unlike other big cats, cheetahs cannot roar, but they make distinctive chirping sounds, especially when communicating with their cubs.', - }, - { - id: 'animal_whale_migration_patterns', - title: 'Whales and Their Migration Patterns', - text: 'Whales are known for their long migration patterns, traveling thousands of miles between feeding and breeding grounds.', - }, - { - id: 'animal_giraffe_habitat_feeding', - title: 'Giraffes: Habitat and Feeding Habits', - text: 'Giraffes are the tallest land animals, with long necks that help them reach leaves high up in trees. They live in savannas and grasslands, where they feed on leaves, twigs, and fruits from acacia trees.', - }, - { - id: 'animal_penguin_antarctic_adaptations', - title: 'Penguins and Their Antarctic Adaptations', - text: 'Penguins are flightless birds that have adapted to life in the cold Antarctic environment. They have a thick layer of blubber to keep warm, and their wings have evolved into flippers for swimming in the icy waters.', - }, -]; - -export const technicalSampleDocs = [ - { - id: 'technical_db_outage_slow_queries', - title: 'Database Outage: Slow Query Execution', - text: 'At 03:15 AM UTC, the production database experienced a significant outage, leading to slow query execution and increased response times across multiple services. A surge in database load was detected, with 90% of queries exceeding 2 seconds. A detailed log analysis pointed to locking issues within the transaction queue and inefficient index usage.', - }, - { - id: 'technical_api_gateway_timeouts', - title: 'Service Timeout: API Gateway Bottleneck', - text: 'At 10:45 AM UTC, the API Gateway encountered a timeout issue, causing a 500 error for all incoming requests. Detailed traces indicated a significant bottleneck at the gateway level, where requests stalled while waiting for upstream service responses. The upstream service was overwhelmed due to a sudden spike in inbound traffic and failed to release resources promptly.', - }, - { - id: 'technical_cache_misses_thirdparty_api', - title: 'Cache Misses and Increased Latency: Third-Party API Failure', - text: 'At 04:30 PM UTC, a dramatic increase in cache misses and latency was observed. The failure of a third-party API prevented critical data from being cached, leading to unnecessary re-fetching of resources from external sources. This caused significant delays in response times, with up to 10-second delays in some key services.', - }, -]; diff --git a/x-pack/test/api_integration/deployment_agnostic/apis/observability/ai_assistant/utils/snapshots.ts b/x-pack/test/api_integration/deployment_agnostic/apis/observability/ai_assistant/utils/snapshots.ts deleted file mode 100644 index e0543a5246dbe..0000000000000 --- a/x-pack/test/api_integration/deployment_agnostic/apis/observability/ai_assistant/utils/snapshots.ts +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { Client } from '@elastic/elasticsearch'; -import { resourceNames } from '@kbn/observability-ai-assistant-plugin/server/service'; -import { ToolingLog } from '@kbn/tooling-log'; -import path from 'path'; -import { AI_ASSISTANT_SNAPSHOT_REPO_PATH } from '@kbn/test-suites-xpack-platform/api_integration_deployment_agnostic/default_configs/common_paths'; - -export async function restoreKbSnapshot({ - log, - es, - snapshotFolderName, - snapshotName, -}: { - log: ToolingLog; - es: Client; - snapshotFolderName: string; - snapshotName: string; -}) { - const snapshotLocation = path.join(AI_ASSISTANT_SNAPSHOT_REPO_PATH, snapshotFolderName); - - const snapshotRepoName = `my_repo_${snapshotFolderName}`; - log.debug(`Creating snapshot repository "${snapshotRepoName}" from "${snapshotLocation}"`); - await es.snapshot.createRepository({ - name: snapshotFolderName, - repository: { - type: 'fs', - settings: { location: snapshotLocation }, - }, - }); - - try { - log.debug(`Restoring snapshot of "${resourceNames.concreteWriteIndexName.kb}"`); - await es.snapshot.restore({ - repository: snapshotFolderName, - snapshot: snapshotName, - wait_for_completion: true, - indices: resourceNames.concreteWriteIndexName.kb, - }); - } catch (error) { - log.error(`Error restoring snapshot: ${error.message}`); - throw error; - } finally { - log.debug(`Deleting snapshot repository "${snapshotFolderName}"`); - await es.snapshot.deleteRepository({ name: snapshotFolderName }); - } -} diff --git a/x-pack/test/api_integration/deployment_agnostic/apis/observability/ai_assistant/utils/tasks.ts b/x-pack/test/api_integration/deployment_agnostic/apis/observability/ai_assistant/utils/tasks.ts deleted file mode 100644 index 17dfd6dc37457..0000000000000 --- a/x-pack/test/api_integration/deployment_agnostic/apis/observability/ai_assistant/utils/tasks.ts +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { Client } from '@elastic/elasticsearch'; -import { getActiveReindexingTaskId } from '@kbn/observability-ai-assistant-plugin/server/service/knowledge_base_service/reindex_knowledge_base'; -import pRetry from 'p-retry'; - -export async function waitForIndexTaskToComplete(es: Client) { - await pRetry( - async () => { - const taskId = await getActiveReindexingTaskId({ asInternalUser: es }); - if (!taskId) { - throw new Error('Waiting for reindexing task to start'); - } - }, - { retries: 50, factor: 1, minTimeout: 500 } - ); - - await pRetry( - async () => { - const taskId = await getActiveReindexingTaskId({ asInternalUser: es }); - if (taskId) { - throw new Error('Waiting for reindexing task to complete'); - } - }, - { retries: 10, factor: 1, minTimeout: 500 } - ); -} diff --git a/x-pack/test/api_integration/deployment_agnostic/apis/observability/ai_assistant/utils/time.ts b/x-pack/test/api_integration/deployment_agnostic/apis/observability/ai_assistant/utils/time.ts deleted file mode 100644 index d91b60d9f991a..0000000000000 --- a/x-pack/test/api_integration/deployment_agnostic/apis/observability/ai_assistant/utils/time.ts +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { duration, unitOfTime } from 'moment'; - -export function durationAsMs(value: number, unit: unitOfTime.DurationConstructor) { - return duration(value, unit).asMilliseconds(); -} - -export function dateAsTimestamp(value: string) { - return new Date(value).getTime(); -} - -export function sleep(value: number) { - return new Promise((resolve) => setTimeout(resolve, value)); -} diff --git a/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/alerts/helpers/alerting_helper.ts b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/alerts/helpers/alerting_helper.ts deleted file mode 100644 index dd51745179380..0000000000000 --- a/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/alerts/helpers/alerting_helper.ts +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { ParsedTechnicalFields } from '@kbn/rule-registry-plugin/common'; -import { ObservabilityApmAlert } from '@kbn/alerts-as-data-utils'; - -export const APM_ALERTS_INDEX = '.alerts-observability.apm.alerts-*'; -export const APM_ACTION_VARIABLE_INDEX = 'apm-index-connector-test'; -export type ApmAlertFields = ParsedTechnicalFields & ObservabilityApmAlert; diff --git a/x-pack/test/functional/apps/search_playground/playground_overview.ess.ts b/x-pack/test/functional/apps/search_playground/playground_overview.ess.ts index 7da6b85d0241a..8f09675193765 100644 --- a/x-pack/test/functional/apps/search_playground/playground_overview.ess.ts +++ b/x-pack/test/functional/apps/search_playground/playground_overview.ess.ts @@ -9,10 +9,7 @@ import { ELASTIC_HTTP_VERSION_HEADER } from '@kbn/core-http-common'; import { FtrProviderContext } from '../../ftr_provider_context'; import { createOpenAIConnector } from './utils/create_openai_connector'; -import { - createLlmProxy, - LlmProxy, -} from '../../../api_integration/deployment_agnostic/apis/observability/ai_assistant/utils/create_llm_proxy'; +import { createLlmProxy, LlmProxy } from './utils/create_llm_proxy'; const COMMON_REQUEST_HEADERS = { 'kbn-xsrf': 'some-xsrf-token', diff --git a/x-pack/test/api_integration/deployment_agnostic/apis/observability/ai_assistant/utils/create_llm_proxy.ts b/x-pack/test/functional/apps/search_playground/utils/create_llm_proxy.ts similarity index 100% rename from x-pack/test/api_integration/deployment_agnostic/apis/observability/ai_assistant/utils/create_llm_proxy.ts rename to x-pack/test/functional/apps/search_playground/utils/create_llm_proxy.ts diff --git a/x-pack/test/api_integration/deployment_agnostic/apis/observability/ai_assistant/utils/create_openai_chunk.ts b/x-pack/test/functional/apps/search_playground/utils/create_openai_chunk.ts similarity index 100% rename from x-pack/test/api_integration/deployment_agnostic/apis/observability/ai_assistant/utils/create_openai_chunk.ts rename to x-pack/test/functional/apps/search_playground/utils/create_openai_chunk.ts diff --git a/x-pack/test/functional/apps/search_playground/utils/create_openai_connector.ts b/x-pack/test/functional/apps/search_playground/utils/create_openai_connector.ts index ddbe83605454f..c2188aec10fb2 100644 --- a/x-pack/test/functional/apps/search_playground/utils/create_openai_connector.ts +++ b/x-pack/test/functional/apps/search_playground/utils/create_openai_connector.ts @@ -6,7 +6,7 @@ */ import type SuperTest from 'supertest'; -import { LlmProxy } from '../../../../api_integration/deployment_agnostic/apis/observability/ai_assistant/utils/create_llm_proxy'; +import { LlmProxy } from './create_llm_proxy'; export async function createOpenAIConnector({ supertest, diff --git a/x-pack/test/tsconfig.json b/x-pack/test/tsconfig.json index 6074844d568c5..9442d63bcb76a 100644 --- a/x-pack/test/tsconfig.json +++ b/x-pack/test/tsconfig.json @@ -54,13 +54,11 @@ "@kbn/rule-data-utils", "@kbn/test-subj-selector", "@kbn/rison", - "@kbn/logging", "@kbn/data-views-plugin", "@kbn/datemath", "@kbn/es-archiver", "@kbn/es-query", "@kbn/visualizations-plugin", - "@kbn/rule-registry-plugin", "@kbn/apm-synthtrace-client", "@kbn/field-formats-plugin", "@kbn/server-route-repository", @@ -82,10 +80,6 @@ "@kbn/test-suites-xpack-platform", "@kbn/test-suites-xpack-platform", "@kbn/core-provider-plugin", - "@kbn/ai-assistant-common", - "@kbn/core", - "@kbn/logging", - "@kbn/alerts-as-data-utils", "@kbn/response-ops-rule-params", "@kbn/ml-string-hash", "@kbn/uptime-plugin",