Skip to content

Commit

Permalink
[8.x] [Obs AI Assistant] Use architecture-specific elser model (#205851
Browse files Browse the repository at this point in the history
…) (#205951)

# Backport

This will backport the following commits from `main` to `8.x`:
- [[Obs AI Assistant] Use architecture-specific elser model
(#205851)](#205851)

<!--- Backport version: 9.4.3 -->

### Questions ?
Please refer to the [Backport tool
documentation](https://github.com/sqren/backport)

<!--BACKPORT [{"author":{"name":"Søren
Louv-Jansen","email":"[email protected]"},"sourceCommit":{"committedDate":"2025-01-08T18:59:20Z","message":"[Obs
AI Assistant] Use architecture-specific elser model (#205851)\n\nCloses
https://github.com/elastic/kibana/issues/205852\n\nWhen installing the
Obs knowledge base it will always install the
model\n`.elser_model_2`.\nFor Linux with an x86-64 CPU an optimised
version of Elser exists\n(`elser_model_2_linux-x86_64`). We should use
that when possible.\n\nAfter this change the inference endpoint will
use\n`.elser_model_2_linux-x86_64` on supported
hardware:\n\n![image](https://github.com/user-attachments/assets/fedc6700-877a-47ab-a3b8-055db53407d0)","sha":"ad3b9880c792833e7590a60d57b65e08ecbd9b25","branchLabelMapping":{"^v9.0.0$":"main","^v8.18.0$":"8.x","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:fix","v9.0.0","Team:Obs
AI Assistant","backport:version","v8.18.0","v8.17.1"],"title":"[Obs AI
Assistant] Use architecture-specific elser
model","number":205851,"url":"https://github.com/elastic/kibana/pull/205851","mergeCommit":{"message":"[Obs
AI Assistant] Use architecture-specific elser model (#205851)\n\nCloses
https://github.com/elastic/kibana/issues/205852\n\nWhen installing the
Obs knowledge base it will always install the
model\n`.elser_model_2`.\nFor Linux with an x86-64 CPU an optimised
version of Elser exists\n(`elser_model_2_linux-x86_64`). We should use
that when possible.\n\nAfter this change the inference endpoint will
use\n`.elser_model_2_linux-x86_64` on supported
hardware:\n\n![image](https://github.com/user-attachments/assets/fedc6700-877a-47ab-a3b8-055db53407d0)","sha":"ad3b9880c792833e7590a60d57b65e08ecbd9b25"}},"sourceBranch":"main","suggestedTargetBranches":["8.x","8.17"],"targetPullRequestStates":[{"branch":"main","label":"v9.0.0","branchLabelMappingKey":"^v9.0.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/205851","number":205851,"mergeCommit":{"message":"[Obs
AI Assistant] Use architecture-specific elser model (#205851)\n\nCloses
https://github.com/elastic/kibana/issues/205852\n\nWhen installing the
Obs knowledge base it will always install the
model\n`.elser_model_2`.\nFor Linux with an x86-64 CPU an optimised
version of Elser exists\n(`elser_model_2_linux-x86_64`). We should use
that when possible.\n\nAfter this change the inference endpoint will
use\n`.elser_model_2_linux-x86_64` on supported
hardware:\n\n![image](https://github.com/user-attachments/assets/fedc6700-877a-47ab-a3b8-055db53407d0)","sha":"ad3b9880c792833e7590a60d57b65e08ecbd9b25"}},{"branch":"8.x","label":"v8.18.0","branchLabelMappingKey":"^v8.18.0$","isSourceBranch":false,"state":"NOT_CREATED"},{"branch":"8.17","label":"v8.17.1","branchLabelMappingKey":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"state":"NOT_CREATED"}]}]
BACKPORT-->

Co-authored-by: Søren Louv-Jansen <[email protected]>
Co-authored-by: Dario Gieselaar <[email protected]>
  • Loading branch information
3 people authored Jan 9, 2025
1 parent fada2e8 commit 874859d
Show file tree
Hide file tree
Showing 5 changed files with 63 additions and 44 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ import {
} from '../task_manager_definitions/register_migrate_knowledge_base_entries_task';
import { ObservabilityAIAssistantPluginStartDependencies } from '../../types';
import { ObservabilityAIAssistantConfig } from '../../config';
import { getElserModelId } from '../knowledge_base_service/get_elser_model_id';

const MAX_FUNCTION_CALLS = 8;

Expand Down Expand Up @@ -660,6 +661,10 @@ export class ObservabilityAIAssistantClient {
setupKnowledgeBase = async (modelId: string | undefined) => {
const { esClient, core, logger, knowledgeBaseService } = this.dependencies;

if (!modelId) {
modelId = await getElserModelId({ core, logger });
}

// setup the knowledge base
const res = await knowledgeBaseService.setup(esClient, modelId);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,13 @@ export const AI_ASSISTANT_KB_INFERENCE_ID = 'obs_ai_assistant_kb_inference';
export async function createInferenceEndpoint({
esClient,
logger,
modelId = '.elser_model_2',
modelId,
}: {
esClient: {
asCurrentUser: ElasticsearchClient;
};
logger: Logger;
modelId: string | undefined;
modelId: string;
}) {
try {
logger.debug(`Creating inference endpoint "${AI_ASSISTANT_KB_INFERENCE_ID}"`);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
* 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 { Logger } from '@kbn/logging';
import { CoreSetup } from '@kbn/core-lifecycle-server';
import { firstValueFrom } from 'rxjs';
import { ObservabilityAIAssistantPluginStartDependencies } from '../../types';

export async function getElserModelId({
core,
logger,
}: {
core: CoreSetup<ObservabilityAIAssistantPluginStartDependencies>;
logger: Logger;
}) {
const defaultModelId = '.elser_model_2';
const [_, pluginsStart] = await core.getStartServices();

// Wait for the license to be available so the ML plugin's guards pass once we ask for ELSER stats
const license = await firstValueFrom(pluginsStart.licensing.license$);
if (!license.hasAtLeast('enterprise')) {
return defaultModelId;
}

try {
// Wait for the ML plugin's dependency on the internal saved objects client to be ready
const { ml } = await core.plugins.onSetup<{
ml: {
trainedModelsProvider: (
request: {},
soClient: {}
) => { getELSER: () => Promise<{ model_id: string }> };
};
}>('ml');

if (!ml.found) {
throw new Error('Could not find ML plugin');
}

const elserModelDefinition = await ml.contract
.trainedModelsProvider({} as any, {} as any) // request, savedObjectsClient (but we fake it to use the internal user)
.getELSER();

return elserModelDefinition.model_id;
} catch (error) {
logger.error(`Failed to resolve ELSER model definition: ${error}`);
return defaultModelId;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ export class KnowledgeBaseService {
asCurrentUser: ElasticsearchClient;
asInternalUser: ElasticsearchClient;
},
modelId: string | undefined
modelId: string
) {
await deleteInferenceEndpoint({ esClient }).catch((e) => {}); // ensure existing inference endpoint is deleted
return createInferenceEndpoint({ esClient, logger: this.dependencies.logger, modelId });
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ import { IUiSettingsClient } from '@kbn/core-ui-settings-server';
import { isEmpty, orderBy, compact } from 'lodash';
import type { Logger } from '@kbn/logging';
import { CoreSetup } from '@kbn/core-lifecycle-server';
import { firstValueFrom } from 'rxjs';
import { RecalledEntry } from '.';
import { aiAssistantSearchConnectorIndexPattern } from '../../../common';
import { ObservabilityAIAssistantPluginStartDependencies } from '../../types';
import { getElserModelId } from './get_elser_model_id';

export async function recallFromSearchConnectors({
queries,
Expand Down Expand Up @@ -128,7 +128,7 @@ async function recallFromLegacyConnectors({
}): Promise<RecalledEntry[]> {
const ML_INFERENCE_PREFIX = 'ml.inference.';

const modelIdPromise = getElserModelId(core, logger); // pre-fetch modelId in parallel with fieldCaps
const modelIdPromise = getElserModelId({ core, logger }); // pre-fetch modelId in parallel with fieldCaps
const fieldCaps = await esClient.asCurrentUser.fieldCaps({
index: connectorIndices,
fields: `${ML_INFERENCE_PREFIX}*`,
Expand Down Expand Up @@ -230,42 +230,3 @@ async function getConnectorIndices(

return connectorIndices;
}

async function getElserModelId(
core: CoreSetup<ObservabilityAIAssistantPluginStartDependencies>,
logger: Logger
) {
const defaultModelId = '.elser_model_2';
const [_, pluginsStart] = await core.getStartServices();

// Wait for the license to be available so the ML plugin's guards pass once we ask for ELSER stats
const license = await firstValueFrom(pluginsStart.licensing.license$);
if (!license.hasAtLeast('enterprise')) {
return defaultModelId;
}

try {
// Wait for the ML plugin's dependency on the internal saved objects client to be ready
const { ml } = await core.plugins.onSetup('ml');

if (!ml.found) {
throw new Error('Could not find ML plugin');
}

const elserModelDefinition = await (
ml.contract as {
trainedModelsProvider: (
request: {},
soClient: {}
) => { getELSER: () => Promise<{ model_id: string }> };
}
)
.trainedModelsProvider({} as any, {} as any) // request, savedObjectsClient (but we fake it to use the internal user)
.getELSER();

return elserModelDefinition.model_id;
} catch (error) {
logger.error(`Failed to resolve ELSER model definition: ${error}`);
return defaultModelId;
}
}

0 comments on commit 874859d

Please sign in to comment.