Skip to content

Commit 7576f3a

Browse files
More preset automation & usability improvements (#324) (#325)
Signed-off-by: Tyler Ohlsen <ohltyler@amazon.com> (cherry picked from commit 831f71e) Co-authored-by: Tyler Ohlsen <ohltyler@amazon.com>
1 parent fd51b93 commit 7576f3a

File tree

6 files changed

+171
-71
lines changed

6 files changed

+171
-71
lines changed

common/constants.ts

+39-6
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,10 @@ export const IMAGE_FIELD_PATTERN = `{{image_field}}`;
157157
export const QUERY_TEXT_PATTERN = `{{query_text}}`;
158158
export const QUERY_IMAGE_PATTERN = `{{query_image}}`;
159159
export const MODEL_ID_PATTERN = `{{model_id}}`;
160+
export const VECTOR = 'vector';
161+
export const VECTOR_PATTERN = `{{${VECTOR}}}`;
162+
export const VECTOR_TEMPLATE_PLACEHOLDER = `\$\{${VECTOR}\}`;
163+
export const DEFAULT_K = 10;
160164

161165
export const FETCH_ALL_QUERY = {
162166
query: {
@@ -180,10 +184,9 @@ export const KNN_QUERY = {
180184
query: {
181185
knn: {
182186
[VECTOR_FIELD_PATTERN]: {
183-
vector: `{{vector}}`,
187+
vector: VECTOR_PATTERN,
188+
k: DEFAULT_K,
184189
},
185-
k: 10,
186-
model_id: MODEL_ID_PATTERN,
187190
},
188191
},
189192
};
@@ -196,7 +199,7 @@ export const SEMANTIC_SEARCH_QUERY_NEURAL = {
196199
[VECTOR_FIELD_PATTERN]: {
197200
query_text: QUERY_TEXT_PATTERN,
198201
model_id: MODEL_ID_PATTERN,
199-
k: 100,
202+
k: DEFAULT_K,
200203
},
201204
},
202205
},
@@ -211,7 +214,7 @@ export const MULTIMODAL_SEARCH_QUERY_NEURAL = {
211214
query_text: QUERY_TEXT_PATTERN,
212215
query_image: QUERY_IMAGE_PATTERN,
213216
model_id: MODEL_ID_PATTERN,
214-
k: 100,
217+
k: DEFAULT_K,
215218
},
216219
},
217220
},
@@ -234,6 +237,32 @@ export const MULTIMODAL_SEARCH_QUERY_BOOL = {
234237
},
235238
},
236239
};
240+
export const HYBRID_SEARCH_QUERY_MATCH_KNN = {
241+
_source: {
242+
excludes: [VECTOR_FIELD_PATTERN],
243+
},
244+
query: {
245+
hybrid: {
246+
queries: [
247+
{
248+
match: {
249+
[TEXT_FIELD_PATTERN]: {
250+
query: QUERY_TEXT_PATTERN,
251+
},
252+
},
253+
},
254+
{
255+
knn: {
256+
[VECTOR_FIELD_PATTERN]: {
257+
vector: VECTOR_PATTERN,
258+
k: DEFAULT_K,
259+
},
260+
},
261+
},
262+
],
263+
},
264+
},
265+
};
237266
export const HYBRID_SEARCH_QUERY_MATCH_NEURAL = {
238267
_source: {
239268
excludes: [VECTOR_FIELD_PATTERN],
@@ -253,7 +282,7 @@ export const HYBRID_SEARCH_QUERY_MATCH_NEURAL = {
253282
[VECTOR_FIELD_PATTERN]: {
254283
query_text: QUERY_TEXT_PATTERN,
255284
model_id: MODEL_ID_PATTERN,
256-
k: 5,
285+
k: DEFAULT_K,
257286
},
258287
},
259288
},
@@ -312,6 +341,10 @@ export const QUERY_PRESETS = [
312341
name: `${WORKFLOW_TYPE.MULTIMODAL_SEARCH} (neural)`,
313342
query: customStringify(MULTIMODAL_SEARCH_QUERY_NEURAL),
314343
},
344+
{
345+
name: `Hybrid search (match & k-NN queries)`,
346+
query: customStringify(HYBRID_SEARCH_QUERY_MATCH_KNN),
347+
},
315348
{
316349
name: `Hybrid search (match & term queries)`,
317350
query: customStringify(HYBRID_SEARCH_QUERY_MATCH_TERM),

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

+7-3
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,18 @@ export function SelectWithCustomOptions(props: SelectWithCustomOptionsProps) {
2626
// selected option state
2727
const [selectedOption, setSelectedOption] = useState<any[]>([]);
2828

29-
// update the selected option when the form is updated. set to empty if the form value is undefined
30-
// or an empty string ('')
29+
// update the selected option when the form is updated. if the form is empty,
30+
// default to the top option. by default, this will re-trigger this hook with a populated
31+
// value, to then finally update the displayed option.
3132
useEffect(() => {
3233
const formValue = getIn(values, props.fieldPath);
3334
if (!isEmpty(formValue)) {
3435
setSelectedOption([{ label: getIn(values, props.fieldPath) }]);
3536
} else {
36-
setSelectedOption([]);
37+
if (props.options.length > 0) {
38+
setFieldTouched(props.fieldPath, true);
39+
setFieldValue(props.fieldPath, props.options[0].label);
40+
}
3741
}
3842
}, [getIn(values, props.fieldPath)]);
3943

public/pages/workflow_detail/workflow_inputs/search_inputs/configure_search_request.tsx

+42-35
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,12 @@ import {
1212
EuiFlexGroup,
1313
EuiFlexItem,
1414
EuiFormRow,
15+
EuiSmallButton,
1516
EuiSuperSelect,
1617
EuiSuperSelectOption,
1718
EuiText,
1819
EuiTitle,
20+
EuiSpacer,
1921
} from '@elastic/eui';
2022
import { SearchHit, WorkflowFormValues } from '../../../../../common';
2123
import { JsonField } from '../input_fields';
@@ -142,41 +144,46 @@ export function ConfigureSearchRequest(props: ConfigureSearchRequestProps) {
142144
/>
143145
</EuiFlexItem>
144146
<EuiFlexItem grow={false}>
145-
<EuiButton
146-
fill={false}
147-
style={{ width: '100px' }}
148-
size="s"
149-
onClick={() => {
150-
// for this test query, we don't want to involve any configured search pipelines, if any exist
151-
// see https://opensearch.org/docs/latest/search-plugins/search-pipelines/using-search-pipeline/#disabling-the-default-pipeline-for-a-request
152-
dispatch(
153-
searchIndex({
154-
apiBody: {
155-
index: values.search.index.name,
156-
body: values.search.request,
157-
searchPipeline: '_none',
158-
},
159-
dataSourceId,
160-
})
161-
)
162-
.unwrap()
163-
.then(async (resp) => {
164-
props.setQueryResponse(
165-
JSON.stringify(
166-
resp.hits.hits.map((hit: SearchHit) => hit._source),
167-
undefined,
168-
2
169-
)
170-
);
171-
})
172-
.catch((error: any) => {
173-
props.setQueryResponse('');
174-
console.error('Error running query: ', error);
175-
});
176-
}}
177-
>
178-
Test
179-
</EuiButton>
147+
<>
148+
<EuiSmallButton
149+
fill={false}
150+
style={{ width: '100px' }}
151+
onClick={() => {
152+
// for this test query, we don't want to involve any configured search pipelines, if any exist
153+
// see https://opensearch.org/docs/latest/search-plugins/search-pipelines/using-search-pipeline/#disabling-the-default-pipeline-for-a-request
154+
dispatch(
155+
searchIndex({
156+
apiBody: {
157+
index: values.search.index.name,
158+
body: values.search.request,
159+
searchPipeline: '_none',
160+
},
161+
dataSourceId,
162+
})
163+
)
164+
.unwrap()
165+
.then(async (resp) => {
166+
props.setQueryResponse(
167+
JSON.stringify(
168+
resp.hits.hits.map((hit: SearchHit) => hit._source),
169+
undefined,
170+
2
171+
)
172+
);
173+
})
174+
.catch((error: any) => {
175+
props.setQueryResponse('');
176+
console.error('Error running query: ', error);
177+
});
178+
}}
179+
>
180+
Test
181+
</EuiSmallButton>
182+
<EuiSpacer size="s" />
183+
<EuiText size="s" color="subdued">
184+
Run query without any search pipeline configuration.
185+
</EuiText>
186+
</>
180187
</EuiFlexItem>
181188
</EuiFlexGroup>
182189
</>

public/pages/workflows/new_workflow/quick_configure_modal.tsx

+33-16
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import {
2424
MapFormValue,
2525
QuickConfigureFields,
2626
TEXT_FIELD_PATTERN,
27+
VECTOR,
2728
VECTOR_FIELD_PATTERN,
2829
WORKFLOW_NAME_REGEXP,
2930
WORKFLOW_TYPE,
@@ -172,8 +173,8 @@ function injectQuickConfigureFields(
172173
workflow.ui_metadata.config,
173174
quickConfigureFields
174175
);
175-
workflow.ui_metadata.config = updateSearchRequestConfig(
176-
workflow.ui_metadata.config,
176+
workflow.ui_metadata.config.search.request.value = injectPlaceholderValues(
177+
(workflow.ui_metadata.config.search.request.value || '') as string,
177178
quickConfigureFields
178179
);
179180
workflow.ui_metadata.config = updateSearchRequestProcessorConfig(
@@ -228,6 +229,7 @@ function updateIngestProcessorConfig(
228229
}
229230

230231
// prefill ML search request processor config, if applicable
232+
// including populating placeholders in any pre-configured query_template
231233
function updateSearchRequestProcessorConfig(
232234
config: WorkflowConfig,
233235
fields: QuickConfigureFields
@@ -236,10 +238,28 @@ function updateSearchRequestProcessorConfig(
236238
if (field.id === 'model' && fields.embeddingModelId) {
237239
field.value = { id: fields.embeddingModelId };
238240
}
239-
if (field.id === 'input_map' || field.id === 'output_map') {
241+
if (field.id === 'input_map') {
242+
// TODO: pre-populate more if the query becomes standard
240243
field.value = [[EMPTY_MAP_ENTRY]] as MapArrayFormValue;
241244
}
245+
if (field.id === 'output_map') {
246+
// prepopulate 'vector' constant as the model output transformed field,
247+
// so it is consistent and used in the downstream query_template, if configured.
248+
field.value = [[{ key: VECTOR, value: '' }]] as MapArrayFormValue;
249+
}
242250
});
251+
config.search.enrichRequest.processors[0].optionalFields = config.search.enrichRequest.processors[0].optionalFields?.map(
252+
(optionalField) => {
253+
let updatedOptionalField = optionalField;
254+
if (optionalField.id === 'query_template') {
255+
optionalField.value = injectPlaceholderValues(
256+
(optionalField.value || '') as string,
257+
fields
258+
);
259+
}
260+
return updatedOptionalField;
261+
}
262+
);
243263

244264
return config;
245265
}
@@ -295,39 +315,36 @@ function updateIndexConfig(
295315
return config;
296316
}
297317

298-
// pre-populate placeholders in the query, if applicable
299-
function updateSearchRequestConfig(
300-
config: WorkflowConfig,
318+
// pre-populate placeholders for a query request string
319+
function injectPlaceholderValues(
320+
requestString: string,
301321
fields: QuickConfigureFields
302-
): WorkflowConfig {
322+
): string {
323+
let finalRequestString = requestString;
303324
if (fields.embeddingModelId) {
304-
config.search.request.value = ((config.search.request.value ||
305-
'') as string).replace(
325+
finalRequestString = finalRequestString.replace(
306326
new RegExp(MODEL_ID_PATTERN, 'g'),
307327
fields.embeddingModelId
308328
);
309329
}
310330
if (fields.textField) {
311-
config.search.request.value = ((config.search.request.value ||
312-
'') as string).replace(
331+
finalRequestString = finalRequestString.replace(
313332
new RegExp(TEXT_FIELD_PATTERN, 'g'),
314333
fields.textField
315334
);
316335
}
317336
if (fields.vectorField) {
318-
config.search.request.value = ((config.search.request.value ||
319-
'') as string).replace(
337+
finalRequestString = finalRequestString.replace(
320338
new RegExp(VECTOR_FIELD_PATTERN, 'g'),
321339
fields.vectorField
322340
);
323341
}
324342
if (fields.imageField) {
325-
config.search.request.value = ((config.search.request.value ||
326-
'') as string).replace(
343+
finalRequestString = finalRequestString.replace(
327344
new RegExp(IMAGE_FIELD_PATTERN, 'g'),
328345
fields.imageField
329346
);
330347
}
331348

332-
return config;
349+
return finalRequestString;
333350
}

0 commit comments

Comments
 (0)