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

Cache form across ML transform types #678

Merged
merged 4 commits into from
Mar 19, 2025
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
4 changes: 4 additions & 0 deletions common/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,10 @@ export type OutputMapEntry = InputMapEntry;
export type InputMapFormValue = InputMapEntry[];
export type OutputMapFormValue = OutputMapEntry[];

export type MapCache = {
[idx: number]: Transform[];
};

export type InputMapArrayFormValue = InputMapFormValue[];
export type OutputMapArrayFormValue = OutputMapFormValue[];

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,13 @@ import {
IndexMappings,
InputMapEntry,
InputMapFormValue,
Transform,
TRANSFORM_TYPE,
EMPTY_INPUT_MAP_ENTRY,
WorkflowConfig,
getCharacterLimitedString,
INPUT_TRANSFORM_OPTIONS,
MapCache,
} from '../../../../../../common';
import {
TextField,
Expand All @@ -49,6 +51,7 @@ import {
sanitizeJSONPath,
} from '../../../../../utils';
import { ConfigureExpressionModal, ConfigureTemplateModal } from './modals/';
import { updateCache } from './utils';

interface ModelInputsProps {
config: IProcessorConfig;
Expand Down Expand Up @@ -98,14 +101,6 @@ export function ModelInputs(props: ModelInputsProps) {
ModelInterface | undefined
>(undefined);

// various modal states
const [templateModalIdx, setTemplateModalIdx] = useState<number | undefined>(
undefined
);
const [expressionModalIdx, setExpressionModalIdx] = useState<
number | undefined
>(undefined);

// get the model interface based on the selected ID and list of known models
useEffect(() => {
if (!isEmpty(models)) {
Expand All @@ -116,6 +111,19 @@ export function ModelInputs(props: ModelInputsProps) {
}
}, [models, getIn(values, modelFieldPath)?.id]);

// various modal states
const [templateModalIdx, setTemplateModalIdx] = useState<number | undefined>(
undefined
);
const [expressionModalIdx, setExpressionModalIdx] = useState<
number | undefined
>(undefined);

// Temporarily cache any configured transformations for different transform types.
// For example, if a user configures a prompt, swaps the transform
// type to "Data field", and swaps back to "Prompt", the prompt will be persisted.
const [inputMapCache, setInputMapCache] = useState<MapCache>({});

// 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 }[]>([]);
Expand Down Expand Up @@ -346,6 +354,7 @@ export function ModelInputs(props: ModelInputsProps) {
<EuiFlexItem grow={TYPE_FLEX_RATIO}>
<EuiFlexItem>
<EuiCompressedSuperSelect
fullWidth={true}
disabled={false}
options={INPUT_TRANSFORM_OPTIONS.map(
(option) =>
Expand Down Expand Up @@ -381,20 +390,32 @@ export function ModelInputs(props: ModelInputsProps) {
) || ''
}
onChange={(option) => {
// before updating, cache any form values
const updatedCache = updateCache(
inputMapCache,
mapEntry,
idx
);
setFieldValue(
`${inputMapFieldPath}.${idx}.value.transformType`,
option
);
// If the transform type changes, clear any set value and/or nested vars,
// as it will likely not make sense under other types/contexts.
// Pre-populate with any cached values, if found
const curCacheForOption = updatedCache[
idx
]?.find(
(transform: Transform) =>
transform.transformType === option
);
setFieldValue(
`${inputMapFieldPath}.${idx}.value.value`,
''
curCacheForOption?.value || ''
);
setFieldValue(
`${inputMapFieldPath}.${idx}.value.nestedVars`,
[]
curCacheForOption?.nestedVars || []
);
setInputMapCache(updatedCache);
}}
/>
</EuiFlexItem>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,13 @@ import {
EMPTY_OUTPUT_MAP_ENTRY,
ExpressionVar,
OUTPUT_TRANSFORM_OPTIONS,
Transform,
MapCache,
} from '../../../../../../common';
import { TextField } from '../../input_fields';
import { AppState } from '../../../../../store';
import { ConfigureMultiExpressionModal } from './modals';
import { updateCache } from './utils';

interface ModelOutputsProps {
config: IProcessorConfig;
Expand Down Expand Up @@ -93,6 +96,11 @@ export function ModelOutputs(props: ModelOutputsProps) {
}
}, [models, getIn(values, modelFieldPath)?.id]);

// Temporarily cache any configured transformations for different transform types.
// For example, if a user configures a prompt, swaps the transform
// type to "Data field", and swaps back to "Prompt", the prompt will be persisted.
const [outputMapCache, setOutputMapCache] = useState<MapCache>({});

// Adding a map entry to the end of the existing arr
function addMapEntry(curEntries: OutputMapFormValue): void {
const updatedEntries = [...curEntries, EMPTY_OUTPUT_MAP_ENTRY];
Expand Down Expand Up @@ -211,6 +219,7 @@ export function ModelOutputs(props: ModelOutputsProps) {
<EuiFlexItem grow={TYPE_FLEX_RATIO}>
<EuiFlexItem>
<EuiCompressedSuperSelect
fullWidth={true}
disabled={false}
options={OUTPUT_TRANSFORM_OPTIONS.map(
(option) =>
Expand Down Expand Up @@ -246,20 +255,32 @@ export function ModelOutputs(props: ModelOutputsProps) {
) || ''
}
onChange={(option) => {
// before updating, cache any form values
const updatedCache = updateCache(
outputMapCache,
mapEntry,
idx
);
setFieldValue(
`${outputMapFieldPath}.${idx}.value.transformType`,
option
);
// If the transform type changes, clear any set value and/or nested vars,
// as it will likely not make sense under other types/contexts.
// Pre-populate with any cached values, if found
const curCacheForOption = updatedCache[
idx
]?.find(
(transform: Transform) =>
transform.transformType === option
);
setFieldValue(
`${outputMapFieldPath}.${idx}.value.value`,
''
curCacheForOption?.value || ''
);
setFieldValue(
`${outputMapFieldPath}.${idx}.value.nestedVars`,
[]
curCacheForOption?.nestedVars || []
);
setOutputMapCache(updatedCache);
}}
/>
</EuiFlexItem>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

import { isEmpty } from 'lodash';
import {
MapCache,
InputMapEntry,
OutputMapEntry,
Transform,
} from '../../../../../../common';

// Update a cache of data transform values based on a given form value
export function updateCache(
cache: MapCache,
mapEntry: InputMapEntry | OutputMapEntry,
idx: number // the mapEntry index
): MapCache {
const updatedCache = cache;
const curCache = updatedCache[idx];
if (curCache === undefined || isEmpty(curCache)) {
// case 1: there is no persisted state for this entry index. create a fresh arr
updatedCache[idx] = [mapEntry.value];
} else if (
!curCache.some(
(transform: Transform) =>
transform.transformType === mapEntry.value.transformType
)
) {
// case 2: there is persisted state for this entry index, but not for the particular
// transform type. append to the arr
updatedCache[idx] = [...updatedCache[idx], mapEntry.value];
} else {
// case 3: there is persisted state for this entry index, and for the particular transform type.
// Update the cache with the current form value(s)
updatedCache[idx] = updatedCache[idx].map((cachedEntry) => {
if (cachedEntry.transformType === mapEntry.value.transformType) {
return mapEntry.value;
} else {
return cachedEntry;
}
});
}
return updatedCache;
}
Loading