Skip to content

Commit

Permalink
Integrate legacy presets with quick-configure fields (#602)
Browse files Browse the repository at this point in the history
Signed-off-by: Tyler Ohlsen <ohltyler@amazon.com>
  • Loading branch information
ohltyler authored Feb 6, 2025
1 parent e9f6927 commit 8998cf8
Show file tree
Hide file tree
Showing 10 changed files with 128 additions and 21 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ export class TextEmbeddingIngestProcessor extends Processor {
this.id = generateId('text_embedding_processor_ingest');
this.fields = [
{
id: 'model_id',
type: 'string',
id: 'model',
type: 'model',
},
{
id: 'field_map',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ export class TextImageEmbeddingIngestProcessor extends Processor {
this.id = generateId('text_image_embedding_processor_ingest');
this.fields = [
{
id: 'model_id',
type: 'string',
id: 'model',
type: 'model',
},
{
id: 'embedding',
Expand Down
5 changes: 5 additions & 0 deletions public/pages/workflow_detail/workflow_detail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ import {
getWorkflow,
searchConnectors,
searchModels,
setIngestPipelineErrors,
setSearchPipelineErrors,
useAppDispatch,
} from '../../store';
import { ResizableWorkspace } from './resizable_workspace';
Expand Down Expand Up @@ -82,13 +84,16 @@ export function WorkflowDetail(props: WorkflowDetailProps) {
// - fetch workflow
// - fetch available models & connectors as their IDs may be used when building flows
// - fetch all indices
// - clear any processor-level errors
useEffect(() => {
dispatch(getWorkflow({ workflowId, dataSourceId }));
dispatch(searchModels({ apiBody: FETCH_ALL_QUERY_LARGE, dataSourceId }));
dispatch(
searchConnectors({ apiBody: FETCH_ALL_QUERY_LARGE, dataSourceId })
);
dispatch(catIndices({ pattern: OMIT_SYSTEM_INDEX_PATTERN, dataSourceId }));
dispatch(setIngestPipelineErrors({ errors: {} }));
dispatch(setSearchPipelineErrors({ errors: {} }));
}, []);

// data-source-related states
Expand Down
27 changes: 27 additions & 0 deletions public/pages/workflow_detail/workflow_inputs/config_field_list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import {
BooleanField,
NumberField,
JsonField,
MapField,
ModelField,
} from './input_fields';
import { IConfigField } from '../../../../common';
import { camelCaseToTitleString } from '../../../utils';
Expand Down Expand Up @@ -108,6 +110,31 @@ export function ConfigFieldList(props: ConfigFieldListProps) {
);
break;
}
case 'map': {
el = (
<EuiFlexItem key={idx}>
<MapField
label={camelCaseToTitleString(field.id)}
fieldPath={fieldPath}
/>
<EuiSpacer size={CONFIG_FIELD_SPACER_SIZE} />
</EuiFlexItem>
);
break;
}
case 'model': {
el = (
<EuiFlexItem key={idx}>
<ModelField
field={field}
fieldPath={fieldPath}
showMissingInterfaceCallout={false}
/>
<EuiSpacer size={CONFIG_FIELD_SPACER_SIZE} />
</EuiFlexItem>
);
break;
}
}
return el;
})}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -182,18 +182,14 @@ export function MapField(props: MapFieldProps) {
<SelectWithCustomOptions
fieldPath={`${props.fieldPath}.${idx}.key`}
options={props.keyOptions as any[]}
placeholder={
props.keyPlaceholder || 'Input'
}
placeholder={props.keyPlaceholder || 'Key'}
allowCreate={true}
/>
) : (
<TextField
fullWidth={true}
fieldPath={`${props.fieldPath}.${idx}.key`}
placeholder={
props.keyPlaceholder || 'Input'
}
placeholder={props.keyPlaceholder || 'Key'}
showError={false}
/>
)}
Expand All @@ -220,7 +216,7 @@ export function MapField(props: MapFieldProps) {
fieldPath={`${props.fieldPath}.${idx}.value`}
options={props.valueOptions || []}
placeholder={
props.valuePlaceholder || 'Output'
props.valuePlaceholder || 'Value'
}
allowCreate={true}
/>
Expand All @@ -229,7 +225,7 @@ export function MapField(props: MapFieldProps) {
fullWidth={true}
fieldPath={`${props.fieldPath}.${idx}.value`}
placeholder={
props.valuePlaceholder || 'Output'
props.valuePlaceholder || 'Value'
}
showError={false}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,9 @@ import { AppState } from '../../../../store';
interface ModelFieldProps {
field: IConfigField;
fieldPath: string; // the full path in string-form to the field (e.g., 'ingest.enrich.processors.text_embedding_processor.inputField')
hasModelInterface: boolean;
onModelChange: (modelId: string) => void;
hasModelInterface?: boolean;
onModelChange?: (modelId: string) => void;
showMissingInterfaceCallout?: boolean;
}

type ModelItem = ModelFormValue & {
Expand All @@ -47,7 +48,10 @@ export function ModelField(props: ModelFieldProps) {
// re-fetch here as it could overload client-side if user clicks back and forth /
// keeps re-rendering this component (and subsequently re-fetching data) as they're building flows
const models = useSelector((state: AppState) => state.ml.models);
//const models = {};

// Set defaults for optional props
const showMissingInterfaceCallout = props.showMissingInterfaceCallout ?? true;
const hasModelInterface = props.hasModelInterface ?? false;

const { errors, touched, values } = useFormikContext<WorkflowFormValues>();

Expand All @@ -74,7 +78,8 @@ export function ModelField(props: ModelFieldProps) {

return (
<>
{!props.hasModelInterface &&
{showMissingInterfaceCallout &&
!hasModelInterface &&
!isEmpty(getIn(values, props.fieldPath)?.id) && (
<>
<EuiCallOut
Expand Down Expand Up @@ -156,7 +161,9 @@ export function ModelField(props: ModelFieldProps) {
form.setFieldValue(props.fieldPath, {
id: option,
} as ModelFormValue);
props.onModelChange(option);
if (props.onModelChange) {
props.onModelChange(option);
}
}}
isInvalid={
getIn(errors, field.name) && getIn(touched, field.name)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ export function ProcessorsList(props: ProcessorsListProps) {
const isPreV219 =
semver.gte(version, MIN_SUPPORTED_VERSION) &&
semver.lt(version, MINIMUM_FULL_SUPPORTED_VERSION);

const ingestProcessors = [
...(isPreV219
? [
Expand Down
36 changes: 36 additions & 0 deletions public/pages/workflows/new_workflow/quick_configure_modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ import {
isVectorSearchUseCase,
WORKFLOW_NAME_RESTRICTIONS,
MAX_DESCRIPTION_LENGTH,
MapFormValue,
} from '../../../../common';
import { APP_PATH } from '../../../utils';
import { AppState, createWorkflow, useAppDispatch } from '../../../store';
Expand Down Expand Up @@ -411,6 +412,41 @@ function updateIngestProcessors(
field.value = [outputMap] as OutputMapArrayFormValue;
}
});
} else if (processor.type === PROCESSOR_TYPE.TEXT_EMBEDDING) {
config.ingest.enrich.processors[idx].fields.forEach((field) => {
if (field.id === 'model' && fields.embeddingModelId) {
field.value = { id: fields.embeddingModelId };
}
if (field.id === 'field_map') {
field.value = [
{
key: fields.textField || '',
value: fields.vectorField || '',
},
] as MapFormValue;
}
});
} else if (processor.type === PROCESSOR_TYPE.TEXT_IMAGE_EMBEDDING) {
config.ingest.enrich.processors[idx].fields.forEach((field) => {
if (field.id === 'model' && fields.embeddingModelId) {
field.value = { id: fields.embeddingModelId };
}
if (field.id === 'field_map') {
field.value = [
{
key: 'text',
value: fields.textField || '',
},
{
key: 'image',
value: fields.imageField || '',
},
] as MapFormValue;
}
if (field.id === 'embedding') {
field.value = fields.vectorField || '';
}
});
}
});
return config;
Expand Down
35 changes: 35 additions & 0 deletions public/utils/config_to_template_utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -363,9 +363,44 @@ export function processorConfigsToTemplateProcessors(
});
break;
}
case PROCESSOR_TYPE.TEXT_EMBEDDING:
case PROCESSOR_TYPE.TEXT_IMAGE_EMBEDDING: {
const formValues = processorConfigToFormik(processorConfig);
let finalFormValues = {} as FormikValues;
// iterate through the form values, ignoring any empty
// field (empty fields can be possible if the field is optional)
Object.keys(formValues).forEach((formKey: string) => {
const formValue = formValues[formKey];
finalFormValues = optionallyAddToFinalForm(
finalFormValues,
formKey,
formValue
);
});
// remove the model field, update to just the required model ID
const model = finalFormValues.model;
delete finalFormValues.model;
finalFormValues = {
...finalFormValues,
model_id: model.id,
};

// add the field map config obj
finalFormValues = {
...finalFormValues,
field_map: mergeMapIntoSingleObj(
formValues['field_map'] as MapFormValue
),
};
processorsList.push({
[processorConfig.type]: finalFormValues,
});
break;
}
case PROCESSOR_TYPE.SPLIT:
case PROCESSOR_TYPE.SORT:
case PROCESSOR_TYPE.COLLAPSE:
case PROCESSOR_TYPE.COPY:
default: {
const formValues = processorConfigToFormik(processorConfig);
let finalFormValues = {} as FormikValues;
Expand Down
8 changes: 4 additions & 4 deletions public/utils/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -728,14 +728,14 @@ export function getUpdatedIndexSettings(
function getEmbeddingFieldFromConnector(
connector: Connector
): string | undefined {
if (connector.parameters?.model !== undefined) {
if (connector?.parameters?.model !== undefined) {
return (
// @ts-ignore
COHERE_CONFIGS[connector.parameters?.model]?.fieldName ||
COHERE_CONFIGS[connector?.parameters?.model]?.fieldName ||
// @ts-ignore
OPENAI_CONFIGS[connector.parameters?.model]?.fieldName ||
OPENAI_CONFIGS[connector?.parameters?.model]?.fieldName ||
// @ts-ignore
BEDROCK_CONFIGS[connector.parameters?.model]?.fieldName
BEDROCK_CONFIGS[connector?.parameters?.model]?.fieldName
);
} else {
return undefined;
Expand Down

0 comments on commit 8998cf8

Please sign in to comment.