"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.getUserInputModelDeploymentParamsProvider = exports.StartUpdateDeploymentModal = exports.DeploymentSetup = void 0; var _react = _interopRequireWildcard(require("react")); var _i18n = require("@kbn/i18n"); var _i18nReact = require("@kbn/i18n-react"); var _eui = require("@elastic/eui"); var _react2 = require("@emotion/react"); var _mlAggUtils = require("@kbn/ml-agg-utils"); var _reactKibanaMount = require("@kbn/react-kibana-mount"); var _ml_server_info = require("../services/ml_server_info"); var _validators = require("../../../common/util/validators"); function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); } function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; } /* * 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. */ const THREADS_MAX_EXPONENT = 5; /** * Form for setting threading params. */ const DeploymentSetup = ({ config, onConfigChange, errors, isUpdate, deploymentsParams }) => { var _config$deploymentId; const numOfAllocation = config.numOfAllocations; const threadsPerAllocations = config.threadsPerAllocations; const defaultDeploymentId = (0, _react.useMemo)(() => { return config.deploymentId; // eslint-disable-next-line react-hooks/exhaustive-deps }, []); const threadsPerAllocationsOptions = (0, _react.useMemo)(() => new Array(THREADS_MAX_EXPONENT).fill(null).map((v, i) => { const value = Math.pow(2, i); const id = value.toString(); return { id, label: id, value }; }), []); const disableThreadingControls = config.priority === 'low'; return /*#__PURE__*/_react.default.createElement(_eui.EuiForm, { component: 'form', id: 'startDeploymentForm' }, /*#__PURE__*/_react.default.createElement(_eui.EuiDescribedFormGroup, { titleSize: 'xxs', title: /*#__PURE__*/_react.default.createElement("h3", null, /*#__PURE__*/_react.default.createElement(_i18nReact.FormattedMessage, { id: "xpack.ml.trainedModels.modelsList.startDeployment.deploymentIdLabel", defaultMessage: "Deployment ID" })), description: /*#__PURE__*/_react.default.createElement(_i18nReact.FormattedMessage, { id: "xpack.ml.trainedModels.modelsList.startDeployment.deploymentIdHelp", defaultMessage: "Specify unique identifier for the model deployment." }) }, /*#__PURE__*/_react.default.createElement(_eui.EuiFormRow, { label: /*#__PURE__*/_react.default.createElement(_i18nReact.FormattedMessage, { id: "xpack.ml.trainedModels.modelsList.startDeployment.deploymentIdLabel", defaultMessage: "Deployment ID" }), hasChildLabel: false, isInvalid: !!errors.deploymentId, error: /*#__PURE__*/_react.default.createElement(_i18nReact.FormattedMessage, { id: "xpack.ml.trainedModels.modelsList.startDeployment.deploymentIdError", defaultMessage: "Deployment with this ID already exists." }) }, !isUpdate ? /*#__PURE__*/_react.default.createElement(_eui.EuiFieldText, { placeholder: defaultDeploymentId, isInvalid: !!errors.deploymentId, value: (_config$deploymentId = config.deploymentId) !== null && _config$deploymentId !== void 0 ? _config$deploymentId : '', onChange: e => { onConfigChange({ ...config, deploymentId: e.target.value }); }, "data-test-subj": 'mlModelsStartDeploymentModalDeploymentId' }) : /*#__PURE__*/_react.default.createElement(_eui.EuiSelect, { fullWidth: true, options: Object.keys(deploymentsParams).map(v => { return { text: v, value: v }; }), value: config.deploymentId, onChange: e => { const update = e.target.value; onConfigChange({ ...config, deploymentId: update, numOfAllocations: deploymentsParams[update].numOfAllocations }); }, "data-test-subj": 'mlModelsStartDeploymentModalDeploymentSelectId' }))), config.priority !== undefined ? /*#__PURE__*/_react.default.createElement(_eui.EuiDescribedFormGroup, { titleSize: 'xxs', title: /*#__PURE__*/_react.default.createElement("h3", null, /*#__PURE__*/_react.default.createElement(_i18nReact.FormattedMessage, { id: "xpack.ml.trainedModels.modelsList.startDeployment.priorityLabel", defaultMessage: "Priority" })), description: /*#__PURE__*/_react.default.createElement(_i18nReact.FormattedMessage, { id: "xpack.ml.trainedModels.modelsList.startDeployment.priorityHelp", defaultMessage: "Select low priority for demonstrations where each model will be very lightly used." }) }, /*#__PURE__*/_react.default.createElement(_eui.EuiFormRow, { label: /*#__PURE__*/_react.default.createElement(_i18nReact.FormattedMessage, { id: "xpack.ml.trainedModels.modelsList.startDeployment.priorityLabel", defaultMessage: "Priority" }), hasChildLabel: false }, /*#__PURE__*/_react.default.createElement(_eui.EuiButtonGroup, { legend: _i18n.i18n.translate('xpack.ml.trainedModels.modelsList.startDeployment.priorityLegend', { defaultMessage: 'Priority selector' }), name: 'priority', isFullWidth: true, idSelected: config.priority, onChange: optionId => { onConfigChange({ ...config, priority: optionId }); }, options: [{ id: 'low', value: 'low', label: _i18n.i18n.translate('xpack.ml.trainedModels.modelsList.startDeployment.lowPriorityLabel', { defaultMessage: 'low' }) }, { id: 'normal', value: 'normal', label: _i18n.i18n.translate('xpack.ml.trainedModels.modelsList.startDeployment.normalPriorityLabel', { defaultMessage: 'normal' }) }], "data-test-subj": 'mlModelsStartDeploymentModalPriority' }))) : null, /*#__PURE__*/_react.default.createElement(_eui.EuiDescribedFormGroup, { titleSize: 'xxs', title: /*#__PURE__*/_react.default.createElement("h3", null, /*#__PURE__*/_react.default.createElement(_i18nReact.FormattedMessage, { id: "xpack.ml.trainedModels.modelsList.startDeployment.numbersOfAllocationsLabel", defaultMessage: "Number of allocations" })), description: /*#__PURE__*/_react.default.createElement(_i18nReact.FormattedMessage, { id: "xpack.ml.trainedModels.modelsList.startDeployment.numbersOfAllocationsHelp", defaultMessage: "Increase to improve document ingest throughput." }) }, /*#__PURE__*/_react.default.createElement(_eui.EuiFormRow, { label: /*#__PURE__*/_react.default.createElement(_i18nReact.FormattedMessage, { id: "xpack.ml.trainedModels.modelsList.startDeployment.numbersOfAllocationsLabel", defaultMessage: "Number of allocations" }), hasChildLabel: false, isDisabled: disableThreadingControls }, /*#__PURE__*/_react.default.createElement(_eui.EuiFieldNumber, { disabled: disableThreadingControls, fullWidth: true, min: 1, step: 1, name: 'numOfAllocations', value: disableThreadingControls ? 1 : numOfAllocation, onChange: event => { onConfigChange({ ...config, numOfAllocations: Number(event.target.value) }); }, "data-test-subj": 'mlModelsStartDeploymentModalNumOfAllocations' }))), threadsPerAllocations !== undefined ? /*#__PURE__*/_react.default.createElement(_eui.EuiDescribedFormGroup, { titleSize: 'xxs', title: /*#__PURE__*/_react.default.createElement("h3", null, /*#__PURE__*/_react.default.createElement(_i18nReact.FormattedMessage, { id: "xpack.ml.trainedModels.modelsList.startDeployment.threadsPerAllocationLabel", defaultMessage: "Threads per allocation" })), description: /*#__PURE__*/_react.default.createElement(_i18nReact.FormattedMessage, { id: "xpack.ml.trainedModels.modelsList.startDeployment.threadsPerAllocationHelp", defaultMessage: "Increase to improve inference latency." }) }, /*#__PURE__*/_react.default.createElement(_eui.EuiFormRow, { label: /*#__PURE__*/_react.default.createElement(_i18nReact.FormattedMessage, { id: "xpack.ml.trainedModels.modelsList.startDeployment.threadsPerAllocationLabel", defaultMessage: "Threads per allocation" }), hasChildLabel: false, isDisabled: disableThreadingControls }, /*#__PURE__*/_react.default.createElement(_eui.EuiButtonGroup, { isDisabled: disableThreadingControls, legend: _i18n.i18n.translate('xpack.ml.trainedModels.modelsList.startDeployment.threadsPerAllocationLegend', { defaultMessage: 'Threads per allocation selector' }), name: 'threadsPerAllocation', isFullWidth: true, idSelected: disableThreadingControls ? '1' : threadsPerAllocationsOptions.find(v => v.value === threadsPerAllocations).id, onChange: optionId => { const value = threadsPerAllocationsOptions.find(v => v.id === optionId).value; onConfigChange({ ...config, threadsPerAllocations: value }); }, options: threadsPerAllocationsOptions, "data-test-subj": 'mlModelsStartDeploymentModalThreadsPerAllocation' }))) : null); }; exports.DeploymentSetup = DeploymentSetup; /** * Modal window wrapper for {@link DeploymentSetup} */ const StartUpdateDeploymentModal = ({ model, onConfigChange, onClose, startModelDeploymentDocUrl, initialParams, modelAndDeploymentIds }) => { var _config$deploymentId2, _model$stats; const isUpdate = !!initialParams; const [config, setConfig] = (0, _react.useState)(initialParams !== null && initialParams !== void 0 ? initialParams : { numOfAllocations: 1, threadsPerAllocations: 1, priority: (0, _ml_server_info.isCloudTrial)() ? 'low' : 'normal', deploymentId: model.model_id }); const deploymentIdValidator = (0, _react.useMemo)(() => { if (isUpdate) { return () => null; } const otherModelAndDeploymentIds = [...(modelAndDeploymentIds !== null && modelAndDeploymentIds !== void 0 ? modelAndDeploymentIds : [])]; otherModelAndDeploymentIds.splice(otherModelAndDeploymentIds === null || otherModelAndDeploymentIds === void 0 ? void 0 : otherModelAndDeploymentIds.indexOf(model.model_id), 1); return (0, _validators.dictionaryValidator)([...model.deployment_ids, ...otherModelAndDeploymentIds, // check for deployment with the default ID ...(model.deployment_ids.includes(model.model_id) ? [''] : [])]); }, [modelAndDeploymentIds, model.deployment_ids, model.model_id, isUpdate]); const numOfAllocationsValidator = (0, _validators.composeValidators)((0, _validators.requiredValidator)(), (0, _mlAggUtils.numberValidator)({ min: 1, integerOnly: true })); const numOfAllocationsErrors = numOfAllocationsValidator(config.numOfAllocations); const deploymentIdErrors = deploymentIdValidator((_config$deploymentId2 = config.deploymentId) !== null && _config$deploymentId2 !== void 0 ? _config$deploymentId2 : ''); const errors = { ...(numOfAllocationsErrors ? { numOfAllocations: numOfAllocationsErrors } : {}), ...(deploymentIdErrors ? { deploymentId: deploymentIdErrors } : {}) }; return /*#__PURE__*/_react.default.createElement(_eui.EuiModal, { onClose: onClose, initialFocus: "[name=numOfAllocations]", maxWidth: false, "data-test-subj": "mlModelsStartDeploymentModal" }, /*#__PURE__*/_react.default.createElement(_eui.EuiModalHeader, null, /*#__PURE__*/_react.default.createElement(_eui.EuiModalHeaderTitle, { size: "s" }, isUpdate ? /*#__PURE__*/_react.default.createElement(_i18nReact.FormattedMessage, { id: "xpack.ml.trainedModels.modelsList.updateDeployment.modalTitle", defaultMessage: "Update {modelId} deployment", values: { modelId: model.model_id } }) : /*#__PURE__*/_react.default.createElement(_i18nReact.FormattedMessage, { id: "xpack.ml.trainedModels.modelsList.startDeployment.modalTitle", defaultMessage: "Start {modelId} deployment", values: { modelId: model.model_id } }))), /*#__PURE__*/_react.default.createElement(_eui.EuiModalBody, null, /*#__PURE__*/_react.default.createElement(_eui.EuiCallOut, { size: 's', title: /*#__PURE__*/_react.default.createElement(_i18nReact.FormattedMessage, { id: "xpack.ml.trainedModels.modelsList.startDeployment.maxNumOfProcessorsWarning", defaultMessage: "The product of the number of allocations and threads per allocation should be less than the total number of processors on your ML nodes." }), iconType: "iInCircle", color: 'primary' }), /*#__PURE__*/_react.default.createElement(_eui.EuiSpacer, { size: 'm' }), /*#__PURE__*/_react.default.createElement(DeploymentSetup, { config: config, onConfigChange: setConfig, errors: errors, isUpdate: isUpdate, deploymentsParams: (_model$stats = model.stats) === null || _model$stats === void 0 ? void 0 : _model$stats.deployment_stats.reduce((acc, curr) => { acc[curr.deployment_id] = { numOfAllocations: curr.number_of_allocations }; return acc; }, {}) }), /*#__PURE__*/_react.default.createElement(_eui.EuiSpacer, { size: 'm' })), /*#__PURE__*/_react.default.createElement(_eui.EuiModalFooter, null, /*#__PURE__*/_react.default.createElement(_eui.EuiLink, { href: startModelDeploymentDocUrl, external: true, target: '_blank', css: (0, _react2.css)` align-self: center; margin-right: auto; ` }, /*#__PURE__*/_react.default.createElement(_i18nReact.FormattedMessage, { id: "xpack.ml.trainedModels.modelsList.startDeployment.docLinkTitle", defaultMessage: "Learn more" })), /*#__PURE__*/_react.default.createElement(_eui.EuiButtonEmpty, { onClick: onClose }, /*#__PURE__*/_react.default.createElement(_i18nReact.FormattedMessage, { id: "xpack.ml.trainedModels.modelsList.startDeployment.cancelButton", defaultMessage: "Cancel" })), /*#__PURE__*/_react.default.createElement(_eui.EuiButton, { type: "submit", form: 'startDeploymentForm', onClick: onConfigChange.bind(null, config), fill: true, disabled: Object.keys(errors).length > 0, "data-test-subj": 'mlModelsStartDeploymentModalStartButton' }, isUpdate ? /*#__PURE__*/_react.default.createElement(_i18nReact.FormattedMessage, { id: "xpack.ml.trainedModels.modelsList.startDeployment.updateButton", defaultMessage: "Update" }) : /*#__PURE__*/_react.default.createElement(_i18nReact.FormattedMessage, { id: "xpack.ml.trainedModels.modelsList.startDeployment.startButton", defaultMessage: "Start" })))); }; /** * Returns a callback for requesting user's input for threading params * with a form rendered in a modal window. * * @param overlays * @param theme$ */ exports.StartUpdateDeploymentModal = StartUpdateDeploymentModal; const getUserInputModelDeploymentParamsProvider = (overlays, theme, i18nStart, startModelDeploymentDocUrl) => (model, initialParams, deploymentIds) => { return new Promise(async resolve => { try { const modalSession = overlays.openModal((0, _reactKibanaMount.toMountPoint)( /*#__PURE__*/_react.default.createElement(StartUpdateDeploymentModal, { startModelDeploymentDocUrl: startModelDeploymentDocUrl, initialParams: initialParams, modelAndDeploymentIds: deploymentIds, model: model, onConfigChange: config => { modalSession.close(); const resultConfig = { ...config }; if (resultConfig.priority === 'low') { resultConfig.numOfAllocations = 1; resultConfig.threadsPerAllocations = 1; } resolve(resultConfig); }, onClose: () => { modalSession.close(); resolve(); } }), { theme, i18n: i18nStart })); } catch (e) { resolve(); } }); }; exports.getUserInputModelDeploymentParamsProvider = getUserInputModelDeploymentParamsProvider;