diff --git a/public/pages/workflow_detail/workflow_inputs/processor_inputs/ml_processor_inputs/index.ts b/public/pages/workflow_detail/workflow_inputs/processor_inputs/ml_processor_inputs/index.ts
new file mode 100644
index 00000000..ff3459f4
--- /dev/null
+++ b/public/pages/workflow_detail/workflow_inputs/processor_inputs/ml_processor_inputs/index.ts
@@ -0,0 +1,7 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+export * from './ml_processor_inputs';
+export * from './modals';
diff --git a/public/pages/workflow_detail/workflow_inputs/processor_inputs/ml_processor_inputs.tsx b/public/pages/workflow_detail/workflow_inputs/processor_inputs/ml_processor_inputs/ml_processor_inputs.tsx
similarity index 83%
rename from public/pages/workflow_detail/workflow_inputs/processor_inputs/ml_processor_inputs.tsx
rename to public/pages/workflow_detail/workflow_inputs/processor_inputs/ml_processor_inputs/ml_processor_inputs.tsx
index cd70d468..90c525e7 100644
--- a/public/pages/workflow_detail/workflow_inputs/processor_inputs/ml_processor_inputs.tsx
+++ b/public/pages/workflow_detail/workflow_inputs/processor_inputs/ml_processor_inputs/ml_processor_inputs.tsx
@@ -31,25 +31,25 @@ import {
MapArrayFormValue,
MapEntry,
MapFormValue,
- REQUEST_PREFIX,
- REQUEST_PREFIX_WITH_JSONPATH_ROOT_SELECTOR,
-} from '../../../../../common';
-import { MapArrayField, ModelField } from '../input_fields';
+} from '../../../../../../common';
+import { ModelField } from '../../input_fields';
import {
ConfigurePromptModal,
InputTransformModal,
OutputTransformModal,
+ OverrideQueryModal,
} from './modals';
-import { AppState, getMappings, useAppDispatch } from '../../../../store';
+import { ModelInputs } from './model_inputs';
+import { AppState, getMappings, useAppDispatch } from '../../../../../store';
import {
formikToPartialPipeline,
getDataSourceId,
parseModelInputs,
parseModelOutputs,
sanitizeJSONPath,
-} from '../../../../utils';
-import { ConfigFieldList } from '../config_field_list';
-import { OverrideQueryModal } from './modals/override_query_modal';
+} from '../../../../../utils';
+import { ConfigFieldList } from '../../config_field_list';
+import { ModelOutputs } from './model_outputs';
interface MLProcessorInputsProps {
uiConfig: WorkflowConfig;
@@ -83,10 +83,6 @@ export function MLProcessorInputs(props: MLProcessorInputsProps) {
const inputMapValue = getIn(values, inputMapFieldPath);
const outputMapFieldPath = `${props.baseConfigPath}.${props.config.id}.output_map`;
const outputMapValue = getIn(values, outputMapFieldPath);
- const fullResponsePath = getIn(
- values,
- `${props.baseConfigPath}.${props.config.id}.full_response_path`
- );
// contains a configurable prompt field or not. if so, expose some extra
// dedicated UI
@@ -390,44 +386,10 @@ export function MLProcessorInputs(props: MLProcessorInputsProps) {
-
@@ -458,29 +420,10 @@ export function MLProcessorInputs(props: MLProcessorInputsProps) {
-
{inputMapValue.length !== outputMapValue.length &&
diff --git a/public/pages/workflow_detail/workflow_inputs/processor_inputs/modals/configure_prompt_modal.tsx b/public/pages/workflow_detail/workflow_inputs/processor_inputs/ml_processor_inputs/modals/configure_prompt_modal.tsx
similarity index 99%
rename from public/pages/workflow_detail/workflow_inputs/processor_inputs/modals/configure_prompt_modal.tsx
rename to public/pages/workflow_detail/workflow_inputs/processor_inputs/ml_processor_inputs/modals/configure_prompt_modal.tsx
index 12dc9ca1..0a64c752 100644
--- a/public/pages/workflow_detail/workflow_inputs/processor_inputs/modals/configure_prompt_modal.tsx
+++ b/public/pages/workflow_detail/workflow_inputs/processor_inputs/ml_processor_inputs/modals/configure_prompt_modal.tsx
@@ -38,11 +38,11 @@ import {
PromptPreset,
WorkflowFormValues,
customStringify,
-} from '../../../../../../common';
+} from '../../../../../../../common';
import {
parseModelInputs,
parseModelInputsObj,
-} from '../../../../../utils/utils';
+} from '../../../../../../utils/utils';
interface ConfigurePromptModalProps {
config: IProcessorConfig;
diff --git a/public/pages/workflow_detail/workflow_inputs/processor_inputs/modals/index.ts b/public/pages/workflow_detail/workflow_inputs/processor_inputs/ml_processor_inputs/modals/index.ts
similarity index 83%
rename from public/pages/workflow_detail/workflow_inputs/processor_inputs/modals/index.ts
rename to public/pages/workflow_detail/workflow_inputs/processor_inputs/ml_processor_inputs/modals/index.ts
index 790872f3..6874447e 100644
--- a/public/pages/workflow_detail/workflow_inputs/processor_inputs/modals/index.ts
+++ b/public/pages/workflow_detail/workflow_inputs/processor_inputs/ml_processor_inputs/modals/index.ts
@@ -6,3 +6,4 @@
export * from './input_transform_modal';
export * from './output_transform_modal';
export * from './configure_prompt_modal';
+export * from './override_query_modal';
diff --git a/public/pages/workflow_detail/workflow_inputs/processor_inputs/modals/input_transform_modal.tsx b/public/pages/workflow_detail/workflow_inputs/processor_inputs/ml_processor_inputs/modals/input_transform_modal.tsx
similarity index 99%
rename from public/pages/workflow_detail/workflow_inputs/processor_inputs/modals/input_transform_modal.tsx
rename to public/pages/workflow_detail/workflow_inputs/processor_inputs/ml_processor_inputs/modals/input_transform_modal.tsx
index 292fa8a0..a40015b3 100644
--- a/public/pages/workflow_detail/workflow_inputs/processor_inputs/modals/input_transform_modal.tsx
+++ b/public/pages/workflow_detail/workflow_inputs/processor_inputs/ml_processor_inputs/modals/input_transform_modal.tsx
@@ -48,7 +48,7 @@ import {
customStringify,
REQUEST_PREFIX,
REQUEST_PREFIX_WITH_JSONPATH_ROOT_SELECTOR,
-} from '../../../../../../common';
+} from '../../../../../../../common';
import {
formikToPartialPipeline,
generateTransform,
@@ -56,20 +56,20 @@ import {
getInitialValue,
prepareDocsForSimulate,
unwrapTransformedDocs,
-} from '../../../../../utils';
+} from '../../../../../../utils';
import {
searchIndex,
simulatePipeline,
useAppDispatch,
-} from '../../../../../store';
-import { getCore } from '../../../../../services';
+} from '../../../../../../store';
+import { getCore } from '../../../../../../services';
import {
generateArrayTransform,
getDataSourceId,
parseModelInputs,
parseModelInputsObj,
-} from '../../../../../utils/utils';
-import { BooleanField, MapArrayField } from '../../input_fields';
+} from '../../../../../../utils/utils';
+import { BooleanField, MapArrayField } from '../../../input_fields';
interface InputTransformModalProps {
uiConfig: WorkflowConfig;
diff --git a/public/pages/workflow_detail/workflow_inputs/processor_inputs/modals/output_transform_modal.tsx b/public/pages/workflow_detail/workflow_inputs/processor_inputs/ml_processor_inputs/modals/output_transform_modal.tsx
similarity index 98%
rename from public/pages/workflow_detail/workflow_inputs/processor_inputs/modals/output_transform_modal.tsx
rename to public/pages/workflow_detail/workflow_inputs/processor_inputs/ml_processor_inputs/modals/output_transform_modal.tsx
index 7d9b7c41..9d1dc7a4 100644
--- a/public/pages/workflow_detail/workflow_inputs/processor_inputs/modals/output_transform_modal.tsx
+++ b/public/pages/workflow_detail/workflow_inputs/processor_inputs/ml_processor_inputs/modals/output_transform_modal.tsx
@@ -46,7 +46,7 @@ import {
WorkflowConfig,
WorkflowFormValues,
customStringify,
-} from '../../../../../../common';
+} from '../../../../../../../common';
import {
formikToPartialPipeline,
generateTransform,
@@ -54,19 +54,19 @@ import {
getInitialValue,
prepareDocsForSimulate,
unwrapTransformedDocs,
-} from '../../../../../utils';
+} from '../../../../../../utils';
import {
searchIndex,
simulatePipeline,
useAppDispatch,
-} from '../../../../../store';
-import { getCore } from '../../../../../services';
-import { BooleanField, MapArrayField } from '../../input_fields';
+} from '../../../../../../store';
+import { getCore } from '../../../../../../services';
+import { BooleanField, MapArrayField } from '../../../input_fields';
import {
getDataSourceId,
parseModelOutputs,
parseModelOutputsObj,
-} from '../../../../../utils/utils';
+} from '../../../../../../utils/utils';
interface OutputTransformModalProps {
uiConfig: WorkflowConfig;
diff --git a/public/pages/workflow_detail/workflow_inputs/processor_inputs/modals/override_query_modal.tsx b/public/pages/workflow_detail/workflow_inputs/processor_inputs/ml_processor_inputs/modals/override_query_modal.tsx
similarity index 98%
rename from public/pages/workflow_detail/workflow_inputs/processor_inputs/modals/override_query_modal.tsx
rename to public/pages/workflow_detail/workflow_inputs/processor_inputs/ml_processor_inputs/modals/override_query_modal.tsx
index 57a1d001..d4257556 100644
--- a/public/pages/workflow_detail/workflow_inputs/processor_inputs/modals/override_query_modal.tsx
+++ b/public/pages/workflow_detail/workflow_inputs/processor_inputs/ml_processor_inputs/modals/override_query_modal.tsx
@@ -39,9 +39,9 @@ import {
VECTOR_FIELD_PATTERN,
VECTOR_PATTERN,
WorkflowFormValues,
-} from '../../../../../../common';
-import { parseModelOutputs } from '../../../../../utils/utils';
-import { JsonField } from '../../input_fields';
+} from '../../../../../../../common';
+import { parseModelOutputs } from '../../../../../../utils/utils';
+import { JsonField } from '../../../input_fields';
interface OverrideQueryModalProps {
config: IProcessorConfig;
diff --git a/public/pages/workflow_detail/workflow_inputs/processor_inputs/ml_processor_inputs/model_inputs.tsx b/public/pages/workflow_detail/workflow_inputs/processor_inputs/ml_processor_inputs/model_inputs.tsx
new file mode 100644
index 00000000..4d18aa44
--- /dev/null
+++ b/public/pages/workflow_detail/workflow_inputs/processor_inputs/ml_processor_inputs/model_inputs.tsx
@@ -0,0 +1,185 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import React, { useState, useEffect } from 'react';
+import { useFormikContext, getIn } from 'formik';
+import { isEmpty } from 'lodash';
+import { useSelector } from 'react-redux';
+import { flattie } from 'flattie';
+import {
+ IProcessorConfig,
+ IConfigField,
+ PROCESSOR_CONTEXT,
+ WorkflowFormValues,
+ ModelInterface,
+ IndexMappings,
+ REQUEST_PREFIX,
+ REQUEST_PREFIX_WITH_JSONPATH_ROOT_SELECTOR,
+} from '../../../../../../common';
+import { MapArrayField } from '../../input_fields';
+import { AppState, getMappings, useAppDispatch } from '../../../../../store';
+import {
+ getDataSourceId,
+ parseModelInputs,
+ sanitizeJSONPath,
+} from '../../../../../utils';
+
+interface ModelInputsProps {
+ config: IProcessorConfig;
+ baseConfigPath: string;
+ context: PROCESSOR_CONTEXT;
+}
+
+/**
+ * Base component to configure ML inputs.
+ */
+export function ModelInputs(props: ModelInputsProps) {
+ const dispatch = useAppDispatch();
+ const dataSourceId = getDataSourceId();
+ const { models } = useSelector((state: AppState) => state.ml);
+ const indices = useSelector((state: AppState) => state.opensearch.indices);
+ const { values } = useFormikContext();
+
+ // get some current form & config values
+ const modelField = props.config.fields.find(
+ (field) => field.type === 'model'
+ ) as IConfigField;
+ const modelFieldPath = `${props.baseConfigPath}.${props.config.id}.${modelField.id}`;
+ const inputMapFieldPath = `${props.baseConfigPath}.${props.config.id}.input_map`;
+
+ // model interface state
+ const [modelInterface, setModelInterface] = useState<
+ ModelInterface | undefined
+ >(undefined);
+
+ // on initial load of the models, update model interface states
+ useEffect(() => {
+ if (!isEmpty(models)) {
+ const modelId = getIn(values, modelFieldPath)?.id;
+ if (modelId) {
+ setModelInterface(models[modelId]?.interface);
+ }
+ }
+ }, [models]);
+
+ // persisting doc/query/index mapping fields to collect a list
+ // of options to display in the dropdowns when configuring input / output maps
+ const [docFields, setDocFields] = useState<{ label: string }[]>([]);
+ const [queryFields, setQueryFields] = useState<{ label: string }[]>([]);
+ const [indexMappingFields, setIndexMappingFields] = useState<
+ { label: string }[]
+ >([]);
+ useEffect(() => {
+ try {
+ const docObjKeys = Object.keys(
+ flattie((JSON.parse(values.ingest.docs) as {}[])[0])
+ );
+ if (docObjKeys.length > 0) {
+ setDocFields(
+ docObjKeys.map((key) => {
+ return {
+ label:
+ // ingest inputs can handle dot notation, and hence don't need
+ // sanitizing to handle JSONPath edge cases. The other contexts
+ // only support JSONPath, and hence need some post-processing/sanitizing.
+ props.context === PROCESSOR_CONTEXT.INGEST
+ ? key
+ : sanitizeJSONPath(key),
+ };
+ })
+ );
+ }
+ } catch {}
+ }, [values?.ingest?.docs]);
+ useEffect(() => {
+ try {
+ const queryObjKeys = Object.keys(
+ flattie(JSON.parse(values.search.request))
+ );
+ if (queryObjKeys.length > 0) {
+ setQueryFields(
+ queryObjKeys.map((key) => {
+ return {
+ label:
+ // ingest inputs can handle dot notation, and hence don't need
+ // sanitizing to handle JSONPath edge cases. The other contexts
+ // only support JSONPath, and hence need some post-processing/sanitizing.
+ props.context === PROCESSOR_CONTEXT.INGEST
+ ? key
+ : sanitizeJSONPath(key),
+ };
+ })
+ );
+ }
+ } catch {}
+ }, [values?.search?.request]);
+ useEffect(() => {
+ const indexName = values?.search?.index?.name as string | undefined;
+ if (indexName !== undefined && indices[indexName] !== undefined) {
+ dispatch(
+ getMappings({
+ index: indexName,
+ dataSourceId,
+ })
+ )
+ .unwrap()
+ .then((resp: IndexMappings) => {
+ const mappingsObjKeys = Object.keys(resp.properties);
+ if (mappingsObjKeys.length > 0) {
+ setIndexMappingFields(
+ mappingsObjKeys.map((key) => {
+ return {
+ label: key,
+ type: resp.properties[key]?.type,
+ };
+ })
+ );
+ }
+ });
+ }
+ }, [values?.search?.index?.name]);
+
+ return (
+
+ );
+}
diff --git a/public/pages/workflow_detail/workflow_inputs/processor_inputs/ml_processor_inputs/model_outputs.tsx b/public/pages/workflow_detail/workflow_inputs/processor_inputs/ml_processor_inputs/model_outputs.tsx
new file mode 100644
index 00000000..6d143dee
--- /dev/null
+++ b/public/pages/workflow_detail/workflow_inputs/processor_inputs/ml_processor_inputs/model_outputs.tsx
@@ -0,0 +1,85 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import React, { useState, useEffect } from 'react';
+import { getIn, useFormikContext } from 'formik';
+import { isEmpty } from 'lodash';
+import { useSelector } from 'react-redux';
+import {
+ IProcessorConfig,
+ IConfigField,
+ PROCESSOR_CONTEXT,
+ WorkflowFormValues,
+ ModelInterface,
+} from '../../../../../../common';
+import { MapArrayField } from '../../input_fields';
+
+import { AppState } from '../../../../../store';
+import { parseModelOutputs } from '../../../../../utils';
+
+interface ModelOutputsProps {
+ config: IProcessorConfig;
+ baseConfigPath: string;
+ context: PROCESSOR_CONTEXT;
+}
+
+/**
+ * Base component to configure ML outputs.
+ */
+export function ModelOutputs(props: ModelOutputsProps) {
+ const { models } = useSelector((state: AppState) => state.ml);
+ const { values } = useFormikContext();
+
+ // get some current form & config values
+ const modelField = props.config.fields.find(
+ (field) => field.type === 'model'
+ ) as IConfigField;
+ const modelFieldPath = `${props.baseConfigPath}.${props.config.id}.${modelField.id}`;
+ const outputMapFieldPath = `${props.baseConfigPath}.${props.config.id}.output_map`;
+ const fullResponsePath = getIn(
+ values,
+ `${props.baseConfigPath}.${props.config.id}.full_response_path`
+ );
+
+ // model interface state
+ const [modelInterface, setModelInterface] = useState<
+ ModelInterface | undefined
+ >(undefined);
+
+ // on initial load of the models, update model interface states
+ useEffect(() => {
+ if (!isEmpty(models)) {
+ const modelId = getIn(values, modelFieldPath)?.id;
+ if (modelId) {
+ setModelInterface(models[modelId]?.interface);
+ }
+ }
+ }, [models]);
+
+ return (
+
+ );
+}