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

[Backport 2.x] Improve and simplify processor form configs #340

Merged
merged 1 commit into from
Sep 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions common/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -396,6 +396,7 @@ export const DATE_FORMAT_PATTERN = 'MM/DD/YY hh:mm A';
export const EMPTY_FIELD_STRING = '--';
export const INDEX_NOT_FOUND_EXCEPTION = 'index_not_found_exception';
export const ERROR_GETTING_WORKFLOW_MSG = 'Failed to retrieve template';
export const NO_TEMPLATES_FOUND_MSG = 'There are no templates';
export const NO_MODIFICATIONS_FOUND_TEXT =
'Template does not contain any modifications';
export const JSONPATH_ROOT_SELECTOR = '$.';
Expand Down
4 changes: 3 additions & 1 deletion public/pages/workflow_detail/workflow_detail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import {
ERROR_GETTING_WORKFLOW_MSG,
FETCH_ALL_QUERY,
MAX_WORKFLOW_NAME_TO_DISPLAY,
NO_TEMPLATES_FOUND_MSG,
getCharacterLimitedString,
} from '../../../common';
import { MountPoint } from '../../../../../src/core/public';
Expand Down Expand Up @@ -105,7 +106,8 @@ export function WorkflowDetail(props: WorkflowDetailProps) {
dispatch(searchModels({ apiBody: FETCH_ALL_QUERY, dataSourceId }));
}, []);

return errorMessage.includes(ERROR_GETTING_WORKFLOW_MSG) ? (
return errorMessage.includes(ERROR_GETTING_WORKFLOW_MSG) ||
errorMessage.includes(NO_TEMPLATES_FOUND_MSG) ? (
<EuiFlexGroup direction="column" alignItems="center">
<EuiFlexItem grow={3}>
<EuiEmptyPrompt
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,9 +108,6 @@ export function MapField(props: MapFieldProps) {
fieldPath={`${props.fieldPath}.${idx}.key`}
options={props.keyOptions as any[]}
placeholder={props.keyPlaceholder || 'Input'}
autofill={
props.keyOptions?.length === 1 && idx === 0
}
/>
) : (
<TextField
Expand All @@ -124,7 +121,7 @@ export function MapField(props: MapFieldProps) {
</EuiFlexItem>
<EuiFlexItem
grow={false}
style={{ marginTop: '14px' }}
style={{ marginTop: '10px' }}
>
<EuiIcon type="sortRight" />
</EuiFlexItem>
Expand All @@ -137,10 +134,6 @@ export function MapField(props: MapFieldProps) {
placeholder={
props.valuePlaceholder || 'Output'
}
autofill={
props.valueOptions?.length === 1 &&
idx === 0
}
/>
) : (
<TextField
Expand All @@ -158,7 +151,6 @@ export function MapField(props: MapFieldProps) {
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiSmallButtonIcon
style={{ marginTop: '8px' }}
iconType={'trash'}
color="danger"
aria-label="Delete"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ interface SelectWithCustomOptionsProps {
fieldPath: string;
placeholder: string;
options: any[];
autofill: boolean;
}

/**
Expand All @@ -27,19 +26,11 @@ export function SelectWithCustomOptions(props: SelectWithCustomOptionsProps) {
// selected option state
const [selectedOption, setSelectedOption] = useState<any[]>([]);

// update the selected option when the form is updated. if the form is empty,
// default to the top option. by default, this will re-trigger this hook with a populated
// value, to then finally update the displayed option.
// set the visible option when the underlying form is updated.
useEffect(() => {
if (props.autofill) {
const formValue = getIn(values, props.fieldPath);
if (!isEmpty(formValue)) {
setSelectedOption([{ label: getIn(values, props.fieldPath) }]);
} else {
if (props.options.length > 0) {
setFieldValue(props.fieldPath, props.options[0].label);
}
}
const formValue = getIn(values, props.fieldPath);
if (!isEmpty(formValue)) {
setSelectedOption([{ label: formValue }]);
}
}, [getIn(values, props.fieldPath)]);

Expand Down Expand Up @@ -73,7 +64,7 @@ export function SelectWithCustomOptions(props: SelectWithCustomOptionsProps) {
return (
<EuiComboBox
fullWidth={true}
compressed={false}
compressed={true}
placeholder={props.placeholder}
singleSelection={{ asPlainText: true }}
isClearable={false}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import {
JSONPATH_ROOT_SELECTOR,
ML_INFERENCE_DOCS_LINK,
MapArrayFormValue,
ModelInterface,
PROCESSOR_CONTEXT,
SearchHit,
SimulateIngestPipelineResponse,
Expand All @@ -47,7 +48,7 @@ import {
useAppDispatch,
} from '../../../../store';
import { getCore } from '../../../../services';
import { getDataSourceId } from '../../../../utils/utils';
import { getDataSourceId, parseModelInputs } from '../../../../utils/utils';
import { MapArrayField } from '../input_fields';

interface InputTransformModalProps {
Expand All @@ -56,7 +57,7 @@ interface InputTransformModalProps {
context: PROCESSOR_CONTEXT;
inputMapField: IConfigField;
inputMapFieldPath: string;
inputFields: any[];
modelInterface: ModelInterface | undefined;
onClose: () => void;
}

Expand Down Expand Up @@ -88,6 +89,10 @@ export function InputTransformModal(props: InputTransformModalProps) {
number | undefined
>((outputOptions[0]?.value as number) ?? undefined);

// TODO: integrated with Ajv to fetch any model interface and perform validation
// on the produced output on-the-fly. For examples, see
// https://www.npmjs.com/package/ajv

return (
<EuiModal onClose={props.onClose} style={{ width: '70vw' }}>
<EuiModalHeader>
Expand Down Expand Up @@ -254,7 +259,7 @@ export function InputTransformModal(props: InputTransformModalProps) {
? 'Query field'
: 'Document field'
}
keyOptions={props.inputFields}
keyOptions={parseModelInputs(props.modelInterface)}
// If the map we are adding is the first one, populate the selected option to index 0
onMapAdd={(curArray) => {
if (isEmpty(curArray)) {
Expand All @@ -274,15 +279,19 @@ export function InputTransformModal(props: InputTransformModalProps) {
</EuiFlexItem>
<EuiFlexItem>
<>
<EuiCompressedSelect
prepend={<EuiText>Expected output for</EuiText>}
options={outputOptions}
value={selectedOutputOption}
onChange={(e) => {
setSelectedOutputOption(Number(e.target.value));
setTransformedOutput('{}');
}}
/>
{outputOptions.length === 1 ? (
<EuiText>Expected output</EuiText>
) : (
<EuiCompressedSelect
prepend={<EuiText>Expected output for</EuiText>}
options={outputOptions}
value={selectedOutputOption}
onChange={(e) => {
setSelectedOutputOption(Number(e.target.value));
setTransformedOutput('{}');
}}
/>
)}
<EuiSpacer size="s" />
<EuiSmallButton
style={{ width: '100px' }}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,9 @@ import {
PROCESSOR_CONTEXT,
WorkflowConfig,
JSONPATH_ROOT_SELECTOR,
ModelInputFormField,
ModelOutputFormField,
ML_INFERENCE_DOCS_LINK,
WorkflowFormValues,
ModelInterface,
} from '../../../../../common';
import { MapArrayField, ModelField } from '../input_fields';
import { isEmpty } from 'lodash';
Expand Down Expand Up @@ -108,9 +107,9 @@ export function MLProcessorInputs(props: MLProcessorInputsProps) {
>(false);

// model interface state
const [hasModelInterface, setHasModelInterface] = useState<boolean>(true);
const [inputFields, setInputFields] = useState<ModelInputFormField[]>([]);
const [outputFields, setOutputFields] = useState<ModelOutputFormField[]>([]);
const [modelInterface, setModelInterface] = useState<
ModelInterface | undefined
>(undefined);

// Hook to listen when the selected model has changed. We do a few checks here:
// 1: update model interface states
Expand All @@ -136,15 +135,7 @@ export function MLProcessorInputs(props: MLProcessorInputsProps) {
// reusable function to update interface states based on the model ID
function updateModelInterfaceStates(modelId: string) {
const newSelectedModel = models[modelId];
if (newSelectedModel?.interface !== undefined) {
setInputFields(parseModelInputs(newSelectedModel.interface));
setOutputFields(parseModelOutputs(newSelectedModel.interface));
setHasModelInterface(true);
} else {
setInputFields([]);
setOutputFields([]);
setHasModelInterface(false);
}
setModelInterface(newSelectedModel?.interface);
}

return (
Expand All @@ -156,7 +147,7 @@ export function MLProcessorInputs(props: MLProcessorInputsProps) {
context={props.context}
inputMapField={inputMapField}
inputMapFieldPath={inputMapFieldPath}
inputFields={inputFields}
modelInterface={modelInterface}
onClose={() => setIsInputTransformModalOpen(false)}
/>
)}
Expand All @@ -167,14 +158,14 @@ export function MLProcessorInputs(props: MLProcessorInputsProps) {
context={props.context}
outputMapField={outputMapField}
outputMapFieldPath={outputMapFieldPath}
outputFields={outputFields}
modelInterface={modelInterface}
onClose={() => setIsOutputTransformModalOpen(false)}
/>
)}
<ModelField
field={modelField}
fieldPath={modelFieldPath}
hasModelInterface={hasModelInterface}
hasModelInterface={modelInterface !== undefined}
onModelChange={onModelChange}
/>
{!isEmpty(getIn(values, modelFieldPath)?.id) && (
Expand Down Expand Up @@ -226,7 +217,7 @@ export function MLProcessorInputs(props: MLProcessorInputsProps) {
? 'Query field'
: 'Document field'
}
keyOptions={inputFields}
keyOptions={parseModelInputs(modelInterface)}
/>
<EuiSpacer size="l" />
<EuiFlexGroup direction="row">
Expand Down Expand Up @@ -274,7 +265,7 @@ export function MLProcessorInputs(props: MLProcessorInputsProps) {
: 'Document field'
}
valuePlaceholder="Model output field"
valueOptions={outputFields}
valueOptions={parseModelOutputs(modelInterface)}
/>
<EuiSpacer size="s" />
{inputMapValue.length !== outputMapValue.length &&
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import {
JSONPATH_ROOT_SELECTOR,
ML_INFERENCE_DOCS_LINK,
MapArrayFormValue,
ModelInterface,
PROCESSOR_CONTEXT,
SearchHit,
SearchPipelineConfig,
Expand All @@ -49,15 +50,15 @@ import {
} from '../../../../store';
import { getCore } from '../../../../services';
import { MapArrayField } from '../input_fields';
import { getDataSourceId } from '../../../../utils/utils';
import { getDataSourceId, parseModelOutputs } from '../../../../utils/utils';

interface OutputTransformModalProps {
uiConfig: WorkflowConfig;
config: IProcessorConfig;
context: PROCESSOR_CONTEXT;
outputMapField: IConfigField;
outputMapFieldPath: string;
outputFields: any[];
modelInterface: ModelInterface | undefined;
onClose: () => void;
}

Expand All @@ -79,7 +80,7 @@ export function OutputTransformModal(props: OutputTransformModalProps) {
// selected output state
const outputOptions = map.map((_, idx) => ({
value: idx,
text: `Prediction output ${idx + 1}`,
text: `Prediction ${idx + 1}`,
})) as EuiSelectOption[];
const [selectedOutputOption, setSelectedOutputOption] = useState<
number | undefined
Expand Down Expand Up @@ -237,7 +238,7 @@ export function OutputTransformModal(props: OutputTransformModalProps) {
helpLink={ML_INFERENCE_DOCS_LINK}
keyPlaceholder="Document field"
valuePlaceholder="Model output field"
valueOptions={props.outputFields}
valueOptions={parseModelOutputs(props.modelInterface)}
// If the map we are adding is the first one, populate the selected option to index 0
onMapAdd={(curArray) => {
if (isEmpty(curArray)) {
Expand All @@ -257,15 +258,19 @@ export function OutputTransformModal(props: OutputTransformModalProps) {
</EuiFlexItem>
<EuiFlexItem>
<>
<EuiCompressedSelect
prepend={<EuiText>Expected output for</EuiText>}
options={outputOptions}
value={selectedOutputOption}
onChange={(e) => {
setSelectedOutputOption(Number(e.target.value));
setTransformedOutput('{}');
}}
/>
{outputOptions.length === 1 ? (
<EuiText>Expected output</EuiText>
) : (
<EuiCompressedSelect
prepend={<EuiText>Expected output for</EuiText>}
options={outputOptions}
value={selectedOutputOption}
onChange={(e) => {
setSelectedOutputOption(Number(e.target.value));
setTransformedOutput('{}');
}}
/>
)}
<EuiSpacer size="s" />
<EuiSmallButton
style={{ width: '100px' }}
Expand Down
Loading
Loading