[ML] ELSER v2 download in the Trained Models UI (#167407)

## Summary

Adds support for ELSER v2 download from the Trained Models UI.

- Marks an appropriate model version for the current cluster
configuration with the recommended flag.
- Updates the state column with better human-readable labels and colour
indicators.
- Adds a callout promoting a new version of ELSER

<img width="1686" alt="image"
src="0deea53a-6d37-4af6-97bc-9f46e36f113b">

#### Notes for reviews
- We need to wait for
https://github.com/elastic/elasticsearch/pull/99584 to get the start
deployment validation functionality. At the moment you can successfully
start deployment of the wrong model version.

### Checklist

- [x] Any text added follows [EUI's writing
guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses
sentence case text and includes [i18n
support](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md)
- [ ]
[Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html)
was added for features that require explanation or tutorials
- [x] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios
- [x] Any UI touched in this PR is usable by keyboard only (learn more
about [keyboard accessibility](https://webaim.org/techniques/keyboard/))
- [x] Any UI touched in this PR does not create any new axe failures
(run axe in browser:
[FF](https://addons.mozilla.org/en-US/firefox/addon/axe-devtools/),
[Chrome](https://chrome.google.com/webstore/detail/axe-web-accessibility-tes/lhdoppojpmngadmnindnejefpokejbdd?hl=
- [x] This renders correctly on smaller devices using a responsive
layout. (You can test this [in your
browser](https://www.browserstack.com/guide/responsive-testing-on-local-server))
- [x] This was checked for [cross-browser
compatibility](https://www.elastic.co/support/matrix#matrix_browsers)
This commit is contained in:
Dima Arnautov 2023-10-03 11:19:49 +02:00 committed by GitHub
parent 105935ace5
commit 0c6dfbf209
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 375 additions and 77 deletions

View file

@ -43,6 +43,89 @@ describe('modelsProvider', () => {
jest.clearAllMocks();
});
describe('getModelDownloads', () => {
test('provides a list of models with recommended and default flag', async () => {
const result = await modelService.getModelDownloads();
expect(result).toEqual([
{
config: { input: { field_names: ['text_field'] } },
description: 'Elastic Learned Sparse EncodeR v1 (Tech Preview)',
hidden: true,
name: '.elser_model_1',
version: 1,
},
{
config: { input: { field_names: ['text_field'] } },
default: true,
description: 'Elastic Learned Sparse EncodeR v2 (Tech Preview)',
name: '.elser_model_2_SNAPSHOT',
version: 2,
},
{
arch: 'amd64',
config: { input: { field_names: ['text_field'] } },
description:
'Elastic Learned Sparse EncodeR v2, optimized for linux-x86_64 (Tech Preview)',
name: '.elser_model_2_linux-x86_64_SNAPSHOT',
os: 'Linux',
recommended: true,
version: 2,
},
]);
});
test('provides a list of models with default model as recommended', async () => {
mockCloud.cloudId = undefined;
(mockClient.asInternalUser.transport.request as jest.Mock).mockResolvedValueOnce({
_nodes: {
total: 1,
successful: 1,
failed: 0,
},
cluster_name: 'default',
nodes: {
yYmqBqjpQG2rXsmMSPb9pQ: {
name: 'node-0',
roles: ['ml'],
attributes: {},
os: {
name: 'Mac OS X',
arch: 'aarch64',
},
},
},
});
const result = await modelService.getModelDownloads();
expect(result).toEqual([
{
config: { input: { field_names: ['text_field'] } },
description: 'Elastic Learned Sparse EncodeR v1 (Tech Preview)',
hidden: true,
name: '.elser_model_1',
version: 1,
},
{
config: { input: { field_names: ['text_field'] } },
recommended: true,
description: 'Elastic Learned Sparse EncodeR v2 (Tech Preview)',
name: '.elser_model_2_SNAPSHOT',
version: 2,
},
{
arch: 'amd64',
config: { input: { field_names: ['text_field'] } },
description:
'Elastic Learned Sparse EncodeR v2, optimized for linux-x86_64 (Tech Preview)',
name: '.elser_model_2_linux-x86_64_SNAPSHOT',
os: 'Linux',
version: 2,
},
]);
});
});
describe('getELSER', () => {
test('provides a recommended definition by default', async () => {
const result = await modelService.getELSER();

View file

@ -25,6 +25,7 @@ import type { CloudSetup } from '@kbn/cloud-plugin/server';
import type { PipelineDefinition } from '../../../common/types/trained_models';
export type ModelService = ReturnType<typeof modelsProvider>;
export const modelsProvider = (client: IScopedClusterClient, cloud?: CloudSetup) =>
new ModelsProvider(client, cloud);
@ -83,6 +84,7 @@ export class ModelsProvider {
return { [index]: null };
}
}
private getNodeId(
elementOriginalId: string,
nodeType: typeof JOB_MAP_NODE_TYPES[keyof typeof JOB_MAP_NODE_TYPES]
@ -446,18 +448,39 @@ export class ModelsProvider {
}
}
const result = Object.entries(ELASTIC_MODEL_DEFINITIONS).map(([name, def]) => {
const modelDefinitionMap = new Map<string, ModelDefinitionResponse[]>();
for (const [name, def] of Object.entries(ELASTIC_MODEL_DEFINITIONS)) {
const recommended =
(isCloud && def.os === 'Linux' && def.arch === 'amd64') ||
(sameArch && !!def?.os && def?.os === osName && def?.arch === arch);
return {
...def,
name,
...(recommended ? { recommended } : {}),
};
});
return result;
const { modelName, ...rest } = def;
const modelDefinitionResponse = {
...rest,
...(recommended ? { recommended } : {}),
name,
};
if (modelDefinitionMap.has(modelName)) {
modelDefinitionMap.get(modelName)!.push(modelDefinitionResponse);
} else {
modelDefinitionMap.set(modelName, [modelDefinitionResponse]);
}
}
// check if there is no recommended, so we mark default as recommended
for (const arr of modelDefinitionMap.values()) {
const defaultModel = arr.find((a) => a.default);
const recommendedModel = arr.find((a) => a.recommended);
if (defaultModel && !recommendedModel) {
delete defaultModel.default;
defaultModel.recommended = true;
}
}
return [...modelDefinitionMap.values()].flat();
}
/**