Skip to content

Commit 5ad01e4

Browse files
authored
Merge branch 'main' into tests_5
Signed-off-by: Sai Medhini Reddy Maryada <117196660+saimedhi@users.noreply.github.com>
2 parents 169a8c4 + 7c5ad9a commit 5ad01e4

23 files changed

+424
-218
lines changed

common/constants.ts

+18-2
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ export enum WORKFLOW_TYPE {
9090
SEMANTIC_SEARCH = 'Semantic search',
9191
MULTIMODAL_SEARCH = 'Multimodal search',
9292
HYBRID_SEARCH = 'Hybrid search',
93+
SENTIMENT_ANALYSIS = 'Sentiment analysis',
9394
CUSTOM = 'Custom',
9495
UNKNOWN = 'Unknown',
9596
}
@@ -181,9 +182,14 @@ export const SHARED_OPTIONAL_FIELDS = ['max_chunk_limit', 'description', 'tag'];
181182
/**
182183
* QUERY PRESETS
183184
*/
185+
export const DEFAULT_TEXT_FIELD = 'my_text';
186+
export const DEFAULT_VECTOR_FIELD = 'my_embedding';
187+
export const DEFAULT_IMAGE_FIELD = 'my_image';
188+
export const DEFAULT_LABEL_FIELD = 'label';
184189
export const VECTOR_FIELD_PATTERN = `{{vector_field}}`;
185190
export const TEXT_FIELD_PATTERN = `{{text_field}}`;
186191
export const IMAGE_FIELD_PATTERN = `{{image_field}}`;
192+
export const LABEL_FIELD_PATTERN = `{{label_field}}`;
187193
export const QUERY_TEXT_PATTERN = `{{query_text}}`;
188194
export const QUERY_IMAGE_PATTERN = `{{query_image}}`;
189195
export const MODEL_ID_PATTERN = `{{model_id}}`;
@@ -198,7 +204,7 @@ export const FETCH_ALL_QUERY = {
198204
},
199205
size: 1000,
200206
};
201-
export const TERM_QUERY = {
207+
export const TERM_QUERY_TEXT = {
202208
query: {
203209
term: {
204210
[TEXT_FIELD_PATTERN]: {
@@ -207,6 +213,15 @@ export const TERM_QUERY = {
207213
},
208214
},
209215
};
216+
export const TERM_QUERY_LABEL = {
217+
query: {
218+
term: {
219+
[LABEL_FIELD_PATTERN]: {
220+
value: QUERY_TEXT_PATTERN,
221+
},
222+
},
223+
},
224+
};
210225
export const KNN_QUERY = {
211226
_source: {
212227
excludes: [VECTOR_FIELD_PATTERN],
@@ -353,7 +368,7 @@ export const QUERY_PRESETS = [
353368
},
354369
{
355370
name: 'Term',
356-
query: customStringify(TERM_QUERY),
371+
query: customStringify(TERM_QUERY_TEXT),
357372
},
358373
{
359374
name: 'Basic k-NN',
@@ -395,6 +410,7 @@ export const DEFAULT_NEW_WORKFLOW_STATE = WORKFLOW_STATE.NOT_STARTED;
395410
export const DEFAULT_NEW_WORKFLOW_STATE_TYPE = ('NOT_STARTED' as any) as typeof WORKFLOW_STATE;
396411
export const DATE_FORMAT_PATTERN = 'MM/DD/YY hh:mm A';
397412
export const EMPTY_FIELD_STRING = '--';
413+
export const OMIT_SYSTEM_INDEX_PATTERN = '*,-.*';
398414
export const INDEX_NOT_FOUND_EXCEPTION = 'index_not_found_exception';
399415
export const ERROR_GETTING_WORKFLOW_MSG = 'Failed to retrieve template';
400416
export const NO_TEMPLATES_FOUND_MSG = 'There are no templates';

common/interfaces.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -483,10 +483,11 @@ export type QueryPreset = {
483483
};
484484

485485
export type QuickConfigureFields = {
486-
embeddingModelId?: string;
486+
modelId?: string;
487487
vectorField?: string;
488488
textField?: string;
489489
imageField?: string;
490+
labelField?: string;
490491
embeddingLength?: number;
491492
};
492493

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
},
2929
"dependencies": {
3030
"@types/jsonpath": "^0.2.4",
31+
"flattie": "^1.1.1",
3132
"formik": "2.4.2",
3233
"jsonpath": "^1.1.1",
3334
"reactflow": "^11.8.3",

public/pages/workflow_detail/workflow_detail.tsx

+2-1
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import {
3232
FETCH_ALL_QUERY,
3333
MAX_WORKFLOW_NAME_TO_DISPLAY,
3434
NO_TEMPLATES_FOUND_MSG,
35+
OMIT_SYSTEM_INDEX_PATTERN,
3536
getCharacterLimitedString,
3637
} from '../../../common';
3738
import { MountPoint } from '../../../../../src/core/public';
@@ -106,7 +107,7 @@ export function WorkflowDetail(props: WorkflowDetailProps) {
106107
useEffect(() => {
107108
dispatch(getWorkflow({ workflowId, dataSourceId }));
108109
dispatch(searchModels({ apiBody: FETCH_ALL_QUERY, dataSourceId }));
109-
dispatch(catIndices({ pattern: '*,-.*', dataSourceId }));
110+
dispatch(catIndices({ pattern: OMIT_SYSTEM_INDEX_PATTERN, dataSourceId }));
110111
}, []);
111112

112113
return errorMessage.includes(ERROR_GETTING_WORKFLOW_MSG) ||

public/pages/workflow_detail/workflow_inputs/input_fields/map_array_field.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,8 @@ interface MapArrayFieldProps {
3737
valuePlaceholder?: string;
3838
onMapAdd?: (curArray: MapArrayFormValue) => void;
3939
onMapDelete?: (idxToDelete: number) => void;
40-
keyOptions?: any[];
41-
valueOptions?: any[];
40+
keyOptions?: { label: string }[];
41+
valueOptions?: { label: string }[];
4242
}
4343

4444
/**

public/pages/workflow_detail/workflow_inputs/input_fields/map_field.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@ interface MapFieldProps {
3131
helpText?: string;
3232
keyPlaceholder?: string;
3333
valuePlaceholder?: string;
34-
keyOptions?: any[];
35-
valueOptions?: any[];
34+
keyOptions?: { label: string }[];
35+
valueOptions?: { label: string }[];
3636
}
3737

3838
/**

public/pages/workflow_detail/workflow_inputs/input_fields/select_with_custom_options.tsx

+9-7
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import { WorkspaceFormValues } from '../../../../../common';
1212
interface SelectWithCustomOptionsProps {
1313
fieldPath: string;
1414
placeholder: string;
15-
options: any[];
15+
options: { label: string }[];
1616
}
1717

1818
/**
@@ -50,13 +50,15 @@ export function SelectWithCustomOptions(props: SelectWithCustomOptionsProps) {
5050
return (
5151
<EuiFlexGroup direction="row" alignItems="flexStart" gutterSize="s">
5252
<EuiFlexItem grow={false}>
53-
<EuiText size="s">{option.label}</EuiText>
54-
</EuiFlexItem>
55-
<EuiFlexItem grow={false}>
56-
<EuiText size="xs" color="subdued" style={{ marginTop: '2px' }}>
57-
{`(${option.type || 'unknown type'})`}
58-
</EuiText>
53+
<EuiText size="s">{option.label || ''}</EuiText>
5954
</EuiFlexItem>
55+
{option.type && (
56+
<EuiFlexItem grow={false}>
57+
<EuiText size="xs" color="subdued" style={{ marginTop: '2px' }}>
58+
{`(${option.type})`}
59+
</EuiText>
60+
</EuiFlexItem>
61+
)}
6062
</EuiFlexGroup>
6163
);
6264
}

public/pages/workflow_detail/workflow_inputs/processor_inputs/input_transform_modal.tsx

+7-5
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ interface InputTransformModalProps {
6868
inputMapField: IConfigField;
6969
inputMapFieldPath: string;
7070
modelInterface: ModelInterface | undefined;
71+
valueOptions: { label: string }[];
7172
onClose: () => void;
7273
}
7374

@@ -163,7 +164,7 @@ export function InputTransformModal(props: InputTransformModalProps) {
163164
Fetch some sample input data and see how it is transformed.
164165
</EuiText>
165166
<EuiSpacer size="s" />
166-
<EuiText>Expected input</EuiText>
167+
<EuiText>Source input</EuiText>
167168
<EuiSmallButton
168169
style={{ width: '100px' }}
169170
onClick={async () => {
@@ -266,7 +267,7 @@ export function InputTransformModal(props: InputTransformModalProps) {
266267
})
267268
.catch((error: any) => {
268269
getCore().notifications.toasts.addDanger(
269-
`Failed to fetch input data`
270+
`Failed to fetch source input data`
270271
);
271272
});
272273
break;
@@ -307,12 +308,13 @@ export function InputTransformModal(props: InputTransformModalProps) {
307308
root object selector "${JSONPATH_ROOT_SELECTOR}"`}
308309
helpLink={ML_INFERENCE_DOCS_LINK}
309310
keyPlaceholder="Model input field"
311+
keyOptions={parseModelInputs(props.modelInterface)}
310312
valuePlaceholder={
311313
props.context === PROCESSOR_CONTEXT.SEARCH_REQUEST
312314
? 'Query field'
313315
: 'Document field'
314316
}
315-
keyOptions={parseModelInputs(props.modelInterface)}
317+
valueOptions={props.valueOptions}
316318
// If the map we are adding is the first one, populate the selected option to index 0
317319
onMapAdd={(curArray) => {
318320
if (isEmpty(curArray)) {
@@ -356,10 +358,10 @@ export function InputTransformModal(props: InputTransformModalProps) {
356358
)}
357359
<EuiFlexItem grow={true}>
358360
{outputOptions.length === 1 ? (
359-
<EuiText>Expected output</EuiText>
361+
<EuiText>Transformed input</EuiText>
360362
) : (
361363
<EuiCompressedSelect
362-
prepend={<EuiText>Expected output for</EuiText>}
364+
prepend={<EuiText>Transformed input for</EuiText>}
363365
options={outputOptions}
364366
value={selectedOutputOption}
365367
onChange={(e) => {

public/pages/workflow_detail/workflow_inputs/processor_inputs/ml_processor_inputs.tsx

+90-11
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@
55

66
import React, { useState, useEffect } from 'react';
77
import { getIn, useFormikContext } from 'formik';
8+
import { isEmpty } from 'lodash';
89
import { useSelector } from 'react-redux';
10+
import { flattie } from 'flattie';
911
import {
1012
EuiAccordion,
1113
EuiSmallButtonEmpty,
@@ -25,14 +27,15 @@ import {
2527
ML_INFERENCE_DOCS_LINK,
2628
WorkflowFormValues,
2729
ModelInterface,
30+
IndexMappings,
2831
} from '../../../../../common';
2932
import { MapArrayField, ModelField } from '../input_fields';
30-
import { isEmpty } from 'lodash';
3133
import { InputTransformModal } from './input_transform_modal';
3234
import { OutputTransformModal } from './output_transform_modal';
33-
import { AppState } from '../../../../store';
35+
import { AppState, getMappings, useAppDispatch } from '../../../../store';
3436
import {
3537
formikToPartialPipeline,
38+
getDataSourceId,
3639
parseModelInputs,
3740
parseModelOutputs,
3841
} from '../../../../utils';
@@ -52,7 +55,10 @@ interface MLProcessorInputsProps {
5255
* output map configuration forms, respectively.
5356
*/
5457
export function MLProcessorInputs(props: MLProcessorInputsProps) {
58+
const dispatch = useAppDispatch();
59+
const dataSourceId = getDataSourceId();
5560
const models = useSelector((state: AppState) => state.ml.models);
61+
const indices = useSelector((state: AppState) => state.opensearch.indices);
5662
const { values, setFieldValue, setFieldTouched } = useFormikContext<
5763
WorkflowFormValues
5864
>();
@@ -115,7 +121,7 @@ export function MLProcessorInputs(props: MLProcessorInputsProps) {
115121
// 1: update model interface states
116122
// 2. clear out any persisted input_map/output_map form values, as those would now be invalid
117123
function onModelChange(modelId: string) {
118-
updateModelInterfaceStates(modelId);
124+
setModelInterface(models[modelId]?.interface);
119125
setFieldValue(inputMapFieldPath, []);
120126
setFieldValue(outputMapFieldPath, []);
121127
setFieldTouched(inputMapFieldPath, false);
@@ -127,16 +133,75 @@ export function MLProcessorInputs(props: MLProcessorInputsProps) {
127133
if (!isEmpty(models)) {
128134
const modelId = getIn(values, `${modelFieldPath}.id`);
129135
if (modelId) {
130-
updateModelInterfaceStates(modelId);
136+
setModelInterface(models[modelId]?.interface);
131137
}
132138
}
133139
}, [models]);
134140

135-
// reusable function to update interface states based on the model ID
136-
function updateModelInterfaceStates(modelId: string) {
137-
const newSelectedModel = models[modelId];
138-
setModelInterface(newSelectedModel?.interface);
139-
}
141+
// persisting doc/query/index mapping fields to collect a list
142+
// of options to display in the dropdowns when configuring input / output maps
143+
const [docFields, setDocFields] = useState<{ label: string }[]>([]);
144+
const [queryFields, setQueryFields] = useState<{ label: string }[]>([]);
145+
const [indexMappingFields, setIndexMappingFields] = useState<
146+
{ label: string }[]
147+
>([]);
148+
useEffect(() => {
149+
try {
150+
const docObjKeys = Object.keys(
151+
flattie((JSON.parse(values.ingest.docs) as {}[])[0])
152+
);
153+
if (docObjKeys.length > 0) {
154+
setDocFields(
155+
docObjKeys.map((key) => {
156+
return {
157+
label: key,
158+
};
159+
})
160+
);
161+
}
162+
} catch {}
163+
}, [values?.ingest?.docs]);
164+
useEffect(() => {
165+
try {
166+
const queryObjKeys = Object.keys(
167+
flattie(JSON.parse(values.search.request))
168+
);
169+
if (queryObjKeys.length > 0) {
170+
setQueryFields(
171+
queryObjKeys.map((key) => {
172+
return {
173+
label: key,
174+
};
175+
})
176+
);
177+
}
178+
} catch {}
179+
}, [values?.search?.request]);
180+
useEffect(() => {
181+
const indexName = values?.search?.index?.name as string | undefined;
182+
if (indexName !== undefined && indices[indexName] !== undefined) {
183+
dispatch(
184+
getMappings({
185+
index: indexName,
186+
dataSourceId,
187+
})
188+
)
189+
.unwrap()
190+
.then((resp: IndexMappings) => {
191+
const mappingsObjKeys = Object.keys(resp.properties);
192+
if (mappingsObjKeys.length > 0) {
193+
setIndexMappingFields(
194+
mappingsObjKeys.map((key) => {
195+
return {
196+
label: key,
197+
type: resp.properties[key]?.type,
198+
};
199+
})
200+
);
201+
}
202+
});
203+
}
204+
}, [values?.search?.index?.name]);
140205

141206
return (
142207
<>
@@ -148,6 +213,13 @@ export function MLProcessorInputs(props: MLProcessorInputsProps) {
148213
inputMapField={inputMapField}
149214
inputMapFieldPath={inputMapFieldPath}
150215
modelInterface={modelInterface}
216+
valueOptions={
217+
props.context === PROCESSOR_CONTEXT.INGEST
218+
? docFields
219+
: props.context === PROCESSOR_CONTEXT.SEARCH_REQUEST
220+
? queryFields
221+
: indexMappingFields
222+
}
151223
onClose={() => setIsInputTransformModalOpen(false)}
152224
/>
153225
)}
@@ -212,12 +284,19 @@ export function MLProcessorInputs(props: MLProcessorInputsProps) {
212284
root object selector "${JSONPATH_ROOT_SELECTOR}"`}
213285
helpLink={ML_INFERENCE_DOCS_LINK}
214286
keyPlaceholder="Model input field"
287+
keyOptions={parseModelInputs(modelInterface)}
215288
valuePlaceholder={
216289
props.context === PROCESSOR_CONTEXT.SEARCH_REQUEST
217290
? 'Query field'
218291
: 'Document field'
219292
}
220-
keyOptions={parseModelInputs(modelInterface)}
293+
valueOptions={
294+
props.context === PROCESSOR_CONTEXT.INGEST
295+
? docFields
296+
: props.context === PROCESSOR_CONTEXT.SEARCH_REQUEST
297+
? queryFields
298+
: indexMappingFields
299+
}
221300
/>
222301
<EuiSpacer size="l" />
223302
<EuiFlexGroup direction="row">
@@ -262,7 +341,7 @@ export function MLProcessorInputs(props: MLProcessorInputsProps) {
262341
keyPlaceholder={
263342
props.context === PROCESSOR_CONTEXT.SEARCH_REQUEST
264343
? 'Query field'
265-
: 'Document field'
344+
: 'New document field'
266345
}
267346
valuePlaceholder="Model output field"
268347
valueOptions={parseModelOutputs(modelInterface)}

0 commit comments

Comments
 (0)