mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
[Index Management/ML] Fix broken retries on inference id creation (#186961)
## Summary This fixes the inference endpoint creation API being called multiple times on error. The call will often time out because downloading/deploying the model takes longer than the Kibana request timeout limit. Setting the timeout limit higher would still be fragile, so ignoring the timeout error makes more sense. This PR also contains a few small language fixes and variable renames for clarity.
This commit is contained in:
parent
5020721fdd
commit
69c0858731
10 changed files with 44 additions and 25 deletions
|
@ -40,7 +40,7 @@ import { TrainedModelConfigResponse } from '@kbn/ml-plugin/common/types/trained_
|
|||
import { getFieldConfig } from '../../../lib';
|
||||
import { useAppContext } from '../../../../../app_context';
|
||||
import { Form, UseField, useForm } from '../../../shared_imports';
|
||||
import { useLoadInferenceModels } from '../../../../../services/api';
|
||||
import { useLoadInferenceEndpoints } from '../../../../../services/api';
|
||||
import { getTrainedModelStats } from '../../../../../../hooks/use_details_page_mappings_model_management';
|
||||
import { InferenceToModelIdMap } from '../fields';
|
||||
import { useMLModelNotificationToasts } from '../../../../../../hooks/use_ml_model_status_toasts';
|
||||
|
@ -134,7 +134,7 @@ export const SelectInferenceId = ({
|
|||
];
|
||||
}, []);
|
||||
|
||||
const { isLoading, data: models } = useLoadInferenceModels();
|
||||
const { isLoading, data: models } = useLoadInferenceEndpoints();
|
||||
|
||||
const [options, setOptions] = useState<EuiSelectableOption[]>([...defaultInferenceIds]);
|
||||
const inferenceIdOptionsFromModels = useMemo(() => {
|
||||
|
|
|
@ -104,7 +104,7 @@ jest.mock('../../../../../../component_templates/component_templates_context', (
|
|||
}));
|
||||
|
||||
jest.mock('../../../../../../../services/api', () => ({
|
||||
getInferenceModels: jest.fn().mockResolvedValue({
|
||||
getInferenceEndpoints: jest.fn().mockResolvedValue({
|
||||
data: [
|
||||
{
|
||||
model_id: 'e5',
|
||||
|
|
|
@ -17,7 +17,7 @@ import { FormHook } from '../../../../../shared_imports';
|
|||
import { CustomInferenceEndpointConfig, DefaultInferenceModels, Field } from '../../../../../types';
|
||||
import { useMLModelNotificationToasts } from '../../../../../../../../hooks/use_ml_model_status_toasts';
|
||||
|
||||
import { getInferenceModels } from '../../../../../../../services/api';
|
||||
import { getInferenceEndpoints } from '../../../../../../../services/api';
|
||||
interface UseSemanticTextProps {
|
||||
form: FormHook<Field, Field>;
|
||||
ml?: MlPluginStart;
|
||||
|
@ -83,7 +83,7 @@ export function useSemanticText(props: UseSemanticTextProps) {
|
|||
if (data.inferenceId === undefined) {
|
||||
throw new Error(
|
||||
i18n.translate('xpack.idxMgmt.mappingsEditor.createField.undefinedInferenceIdError', {
|
||||
defaultMessage: 'InferenceId is undefined while creating the inference endpoint.',
|
||||
defaultMessage: 'Inference ID is undefined',
|
||||
})
|
||||
);
|
||||
}
|
||||
|
@ -138,18 +138,17 @@ export function useSemanticText(props: UseSemanticTextProps) {
|
|||
dispatch({ type: 'field.addSemanticText', value: data });
|
||||
|
||||
try {
|
||||
// if model exists already, do not create inference endpoint
|
||||
const inferenceModels = await getInferenceModels();
|
||||
// if inference endpoint exists already, do not create inference endpoint
|
||||
const inferenceModels = await getInferenceEndpoints();
|
||||
const inferenceModel: InferenceAPIConfigResponse[] = inferenceModels.data.some(
|
||||
(e: InferenceAPIConfigResponse) => e.model_id === inferenceValue
|
||||
);
|
||||
if (inferenceModel) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (trainedModelId) {
|
||||
// show toasts only if it's elastic models
|
||||
showSuccessToasts();
|
||||
// Only show toast if it's an internal Elastic model that hasn't been deployed yet
|
||||
if (trainedModelId && inferenceData.isDeployable && !inferenceData.isDeployed) {
|
||||
showSuccessToasts(trainedModelId);
|
||||
}
|
||||
|
||||
await createInferenceEndpoint(trainedModelId, data, customInferenceEndpointConfig);
|
||||
|
|
|
@ -227,7 +227,7 @@ export const DetailsPageMappingsContent: FunctionComponent<{
|
|||
if (!error) {
|
||||
notificationService.showSuccessToast(
|
||||
i18n.translate('xpack.idxMgmt.indexDetails.mappings.successfullyUpdatedIndexMappings', {
|
||||
defaultMessage: 'Index Mapping was successfully updated',
|
||||
defaultMessage: 'Updated index mapping',
|
||||
})
|
||||
);
|
||||
refetchMapping();
|
||||
|
|
|
@ -442,14 +442,14 @@ export function updateIndexMappings(indexName: string, newFields: Fields) {
|
|||
});
|
||||
}
|
||||
|
||||
export function getInferenceModels() {
|
||||
export function getInferenceEndpoints() {
|
||||
return sendRequest({
|
||||
path: `${API_BASE_PATH}/inference/all`,
|
||||
method: 'get',
|
||||
});
|
||||
}
|
||||
|
||||
export function useLoadInferenceModels() {
|
||||
export function useLoadInferenceEndpoints() {
|
||||
return useRequest<InferenceAPIConfigResponse[]>({
|
||||
path: `${API_BASE_PATH}/inference/all`,
|
||||
method: 'get',
|
||||
|
|
|
@ -28,7 +28,7 @@ export {
|
|||
loadIndexStatistics,
|
||||
useLoadIndexSettings,
|
||||
createIndex,
|
||||
useLoadInferenceModels,
|
||||
useLoadInferenceEndpoints,
|
||||
} from './api';
|
||||
|
||||
export { sortTable } from './sort_table';
|
||||
|
|
|
@ -36,7 +36,7 @@ jest.mock('../application/app_context', () => ({
|
|||
}));
|
||||
|
||||
jest.mock('../application/services/api', () => ({
|
||||
getInferenceModels: jest.fn().mockResolvedValue({
|
||||
getInferenceEndpoints: jest.fn().mockResolvedValue({
|
||||
data: [
|
||||
{
|
||||
model_id: 'e5',
|
||||
|
|
|
@ -18,7 +18,7 @@ import {
|
|||
DeploymentState,
|
||||
NormalizedFields,
|
||||
} from '../application/components/mappings_editor/types';
|
||||
import { getInferenceModels } from '../application/services/api';
|
||||
import { getInferenceEndpoints } from '../application/services/api';
|
||||
|
||||
interface InferenceModel {
|
||||
data: InferenceAPIConfigResponse[];
|
||||
|
@ -91,7 +91,7 @@ export const useDetailsPageMappingsModelManagement = (
|
|||
const dispatch = useDispatch();
|
||||
|
||||
const fetchInferenceModelsAndTrainedModelStats = useCallback(async () => {
|
||||
const inferenceModels = await getInferenceModels();
|
||||
const inferenceModels = await getInferenceEndpoints();
|
||||
|
||||
const trainedModelStats = await ml?.mlApi?.trainedModels.getTrainedModelStats();
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ import { useComponentTemplatesContext } from '../application/components/componen
|
|||
|
||||
export function useMLModelNotificationToasts() {
|
||||
const { toasts } = useComponentTemplatesContext();
|
||||
const showSuccessToasts = () => {
|
||||
const showSuccessToasts = (modelName: string) => {
|
||||
return toasts.addSuccess({
|
||||
title: i18n.translate(
|
||||
'xpack.idxMgmt.mappingsEditor.createField.modelDeploymentStartedNotification',
|
||||
|
@ -20,7 +20,10 @@ export function useMLModelNotificationToasts() {
|
|||
}
|
||||
),
|
||||
text: i18n.translate('xpack.idxMgmt.mappingsEditor.createField.modelDeploymentNotification', {
|
||||
defaultMessage: '1 model is being deployed on your ml_node.',
|
||||
defaultMessage: 'Model {modelName} is being deployed on your machine learning node.',
|
||||
values: {
|
||||
modelName,
|
||||
},
|
||||
}),
|
||||
});
|
||||
};
|
||||
|
|
|
@ -597,11 +597,28 @@ export class ModelsProvider {
|
|||
taskType: InferenceTaskType,
|
||||
modelConfig: InferenceModelConfig
|
||||
) {
|
||||
return await this._client.asCurrentUser.inference.putModel({
|
||||
inference_id: inferenceId,
|
||||
task_type: taskType,
|
||||
model_config: modelConfig,
|
||||
});
|
||||
try {
|
||||
const result = await this._client.asCurrentUser.inference.putModel(
|
||||
{
|
||||
inference_id: inferenceId,
|
||||
task_type: taskType,
|
||||
model_config: modelConfig,
|
||||
},
|
||||
{ maxRetries: 0 }
|
||||
);
|
||||
return result;
|
||||
} catch (error) {
|
||||
// Request timeouts will usually occur when the model is being downloaded/deployed
|
||||
// Erroring out is misleading in these cases, so we return the model_id and task_type
|
||||
if (error.name === 'TimeoutError') {
|
||||
return {
|
||||
model_id: modelConfig.service,
|
||||
task_type: taskType,
|
||||
};
|
||||
} else {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async getModelsDownloadStatus() {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue