diff --git a/x-pack/plugins/enterprise_search/common/ml_inference_pipeline/index.test.ts b/x-pack/plugins/enterprise_search/common/ml_inference_pipeline/index.test.ts index cd14102719a21..5cc0bdad3e121 100644 --- a/x-pack/plugins/enterprise_search/common/ml_inference_pipeline/index.test.ts +++ b/x-pack/plugins/enterprise_search/common/ml_inference_pipeline/index.test.ts @@ -381,12 +381,59 @@ describe('parseMlInferenceParametersFromPipeline', () => { model_id: 'test-model', pipeline_name: 'unit-test', source_field: 'body', + field_mappings: [ + { + sourceField: 'body', + targetField: 'ml.inference.test', + }, + ], + }); + }); + it('returns pipeline parameters from ingest pipeline with multiple inference processors', () => { + expect( + parseMlInferenceParametersFromPipeline('unit-test', { + processors: [ + { + inference: { + field_map: { + body: 'text_field', + }, + model_id: 'test-model', + target_field: 'ml.inference.body', + }, + }, + { + inference: { + field_map: { + title: 'text_field', + }, + model_id: 'test-model', + target_field: 'ml.inference.title', + }, + }, + ], + }) + ).toEqual({ + destination_field: 'body', + model_id: 'test-model', + pipeline_name: 'unit-test', + source_field: 'body', + field_mappings: [ + { + sourceField: 'body', + targetField: 'ml.inference.body', + }, + { + sourceField: 'title', + targetField: 'ml.inference.title', + }, + ], }); }); - it('return null if pipeline missing inference processor', () => { + it('return null if pipeline is missing inference processor', () => { expect(parseMlInferenceParametersFromPipeline('unit-test', { processors: [] })).toBeNull(); }); - it('return null if pipeline missing field_map', () => { + it('return null if pipeline is missing field_map', () => { expect( parseMlInferenceParametersFromPipeline('unit-test', { processors: [ diff --git a/x-pack/plugins/enterprise_search/common/ml_inference_pipeline/index.ts b/x-pack/plugins/enterprise_search/common/ml_inference_pipeline/index.ts index 3744e45b0e585..7870b22977a34 100644 --- a/x-pack/plugins/enterprise_search/common/ml_inference_pipeline/index.ts +++ b/x-pack/plugins/enterprise_search/common/ml_inference_pipeline/index.ts @@ -216,24 +216,37 @@ export const parseMlInferenceParametersFromPipeline = ( name: string, pipeline: IngestPipeline ): CreateMlInferencePipelineParameters | null => { - const processor = pipeline?.processors?.find((proc) => proc.inference !== undefined); - if (!processor || processor?.inference === undefined) { + const inferenceProcessors = pipeline?.processors + ?.filter((p) => p.inference) + .map((p) => p.inference) as IngestInferenceProcessor[]; + if (!inferenceProcessors || inferenceProcessors.length === 0) { return null; } - const { inference: inferenceProcessor } = processor; - const sourceFields = Object.keys(inferenceProcessor.field_map ?? {}); - const sourceField = sourceFields.length === 1 ? sourceFields[0] : null; - if (!sourceField) { - return null; - } - return { - destination_field: inferenceProcessor.target_field - ? stripMlInferencePrefix(inferenceProcessor.target_field) - : inferenceProcessor.target_field, - model_id: inferenceProcessor.model_id, - pipeline_name: name, - source_field: sourceField, - }; + + // Extract source -> target field mappings from all inference processors in pipeline + const fieldMappings = inferenceProcessors + .map((p) => { + const sourceFields = Object.keys(p.field_map ?? {}); + // We assume that there is only one source field per inference processor + const sourceField = sourceFields.length >= 1 ? sourceFields[0] : null; + return { + sourceField, + targetField: p.target_field, // Prefixed target field + }; + }) + .filter((f) => f.sourceField) as FieldMapping[]; + + return fieldMappings.length === 0 + ? null + : { + destination_field: fieldMappings[0].targetField // Backward compatibility - TODO: remove after multi-field selector is implemented for all inference types + ? stripMlInferencePrefix(fieldMappings[0].targetField) + : '', + model_id: inferenceProcessors[0].model_id, + pipeline_name: name, + source_field: fieldMappings[0].sourceField, // Backward compatibility - TODO: remove after multi-field selector is implemented for all inference types + field_mappings: fieldMappings, + }; }; export const parseModelStateFromStats = ( diff --git a/x-pack/plugins/enterprise_search/common/types/pipelines.ts b/x-pack/plugins/enterprise_search/common/types/pipelines.ts index ea86183ce9ea8..13f5ebb01b948 100644 --- a/x-pack/plugins/enterprise_search/common/types/pipelines.ts +++ b/x-pack/plugins/enterprise_search/common/types/pipelines.ts @@ -80,6 +80,7 @@ export interface CreateMlInferencePipelineParameters { model_id: string; pipeline_name: string; source_field: string; + field_mappings?: FieldMapping[]; } export interface CreateMLInferencePipelineDefinition { diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/ml_inference_logic.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/ml_inference_logic.ts index 45fc4f124cc3b..93f73f389df99 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/ml_inference_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/ml_inference_logic.ts @@ -326,6 +326,7 @@ export const MLInferenceLogic = kea< modelID: params.model_id, pipelineName, sourceField: params.source_field, + fieldMappings: params.field_mappings, }); }, setIndexName: ({ indexName }) => {