Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ML] Trained models list: disables 'View training data' action if data frame analytics job no longer exists #171061

Merged
merged 8 commits into from
Nov 21, 2023
1 change: 1 addition & 0 deletions x-pack/plugins/ml/common/types/trained_models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ export type TrainedModelConfigResponse = estypes.MlTrainedModelConfig & {
* Associated pipelines. Extends response from the ES endpoint.
*/
pipelines?: Record<string, PipelineDefinition> | null;
origin_job_exists?: boolean;
Copy link
Member

@jgowdyelastic jgowdyelastic Nov 13, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this needs to be optional route param. We should always perform this check when retrieving the models as it's useful information.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated in 7c06abd


metadata?: {
analytics_config: DataFrameAnalyticsConfig;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,18 +130,19 @@ export function useModelActions({
return useMemo(
() => [
{
name: i18n.translate('xpack.ml.trainedModels.modelsList.viewTrainingDataActionLabel', {
name: i18n.translate('xpack.ml.trainedModels.modelsList.viewTrainingDataNameActionLabel', {
defaultMessage: 'View training data',
}),
description: i18n.translate(
'xpack.ml.trainedModels.modelsList.viewTrainingDataActionLabel',
{
defaultMessage: 'View training data',
defaultMessage: 'Training data can be viewed when data frame analytics job exists.',
}
),
icon: 'visTable',
type: 'icon',
available: (item) => !!item.metadata?.analytics_config?.id,
enabled: (item) => item.origin_job_exists === true,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add a tooltip to show why the action is disabled.

Copy link
Contributor Author

@alvarezmelissa87 alvarezmelissa87 Nov 13, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added in 6d8a4e5
Happy to get suggestions on the copy to use.
image

onClick: async (item) => {
if (item.metadata?.analytics_config === undefined) return;

Expand All @@ -164,7 +165,6 @@ export function useModelActions({

await navigateToUrl(url);
},
isPrimary: true,
},
{
name: i18n.translate('xpack.ml.inference.modelsList.analyticsMapActionLabel', {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ export type ModelItem = TrainedModelConfigResponse & {
type?: string[];
stats?: Stats & { deployment_stats: TrainedModelDeploymentStatsResponse[] };
pipelines?: ModelPipelines['pipelines'] | null;
origin_job_exists?: boolean;
deployment_ids: string[];
putModelConfig?: object;
state: ModelState;
Expand Down
43 changes: 35 additions & 8 deletions x-pack/plugins/ml/server/routes/trained_models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,20 +29,19 @@ import {
createIngestPipelineSchema,
modelDownloadsQuery,
} from './schemas/inference_schema';
import type {
import {
PipelineDefinition,
TrainedModelConfigResponse,
type TrainedModelConfigResponse,
} from '../../common/types/trained_models';
import { mlLog } from '../lib/log';
import { forceQuerySchema } from './schemas/anomaly_detectors_schema';
import { modelsProvider } from '../models/model_management';

export const DEFAULT_TRAINED_MODELS_PAGE_SIZE = 10000;

export function filterForEnabledFeatureModels(
models: TrainedModelConfigResponse[] | estypes.MlTrainedModelConfig[],
enabledFeatures: MlFeatures
) {
export function filterForEnabledFeatureModels<
T extends TrainedModelConfigResponse | estypes.MlTrainedModelConfig
>(models: T[], enabledFeatures: MlFeatures) {
let filteredModels = models;
if (enabledFeatures.nlp === false) {
filteredModels = filteredModels.filter((m) => m.model_type === 'tree_ensemble');
Expand Down Expand Up @@ -191,10 +190,38 @@ export function trainedModelsRoutes(
mlLog.debug(e);
}

const body = filterForEnabledFeatureModels(result, getEnabledFeatures());
const filteredModels = filterForEnabledFeatureModels(result, getEnabledFeatures());

try {
const jobIds = filteredModels
.map((model) => {
const id = model.metadata?.analytics_config?.id;
if (id) {
return `${id}*`;
}
})
.filter((id) => id !== undefined);

if (jobIds.length) {
const { data_frame_analytics: jobs } = await mlClient.getDataFrameAnalytics({
jgowdyelastic marked this conversation as resolved.
Show resolved Hide resolved
id: jobIds.join(','),
allow_no_match: true,
});

filteredModels.forEach((model) => {
const dfaId = model?.metadata?.analytics_config?.id;
if (dfaId !== undefined) {
// if this is a dfa model, set origin_job_exists
model.origin_job_exists = jobs.find((job) => job.id === dfaId) !== undefined;
}
});
}
} catch (e) {
// Swallow error to prevent blocking trained models result
jgowdyelastic marked this conversation as resolved.
Show resolved Hide resolved
peteharverson marked this conversation as resolved.
Show resolved Hide resolved
}

return response.ok({
body,
body: filteredModels,
});
} catch (e) {
return response.customError(wrapError(e));
Expand Down
Loading