Skip to content

Commit

Permalink
More preset automation & usability improvements (#324)
Browse files Browse the repository at this point in the history
Signed-off-by: Tyler Ohlsen <[email protected]>
  • Loading branch information
ohltyler authored Aug 29, 2024
1 parent ee56015 commit 831f71e
Show file tree
Hide file tree
Showing 6 changed files with 171 additions and 71 deletions.
45 changes: 39 additions & 6 deletions common/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,10 @@ export const IMAGE_FIELD_PATTERN = `{{image_field}}`;
export const QUERY_TEXT_PATTERN = `{{query_text}}`;
export const QUERY_IMAGE_PATTERN = `{{query_image}}`;
export const MODEL_ID_PATTERN = `{{model_id}}`;
export const VECTOR = 'vector';
export const VECTOR_PATTERN = `{{${VECTOR}}}`;
export const VECTOR_TEMPLATE_PLACEHOLDER = `\$\{${VECTOR}\}`;
export const DEFAULT_K = 10;

export const FETCH_ALL_QUERY = {
query: {
Expand All @@ -180,10 +184,9 @@ export const KNN_QUERY = {
query: {
knn: {
[VECTOR_FIELD_PATTERN]: {
vector: `{{vector}}`,
vector: VECTOR_PATTERN,
k: DEFAULT_K,
},
k: 10,
model_id: MODEL_ID_PATTERN,
},
},
};
Expand All @@ -196,7 +199,7 @@ export const SEMANTIC_SEARCH_QUERY_NEURAL = {
[VECTOR_FIELD_PATTERN]: {
query_text: QUERY_TEXT_PATTERN,
model_id: MODEL_ID_PATTERN,
k: 100,
k: DEFAULT_K,
},
},
},
Expand All @@ -211,7 +214,7 @@ export const MULTIMODAL_SEARCH_QUERY_NEURAL = {
query_text: QUERY_TEXT_PATTERN,
query_image: QUERY_IMAGE_PATTERN,
model_id: MODEL_ID_PATTERN,
k: 100,
k: DEFAULT_K,
},
},
},
Expand All @@ -234,6 +237,32 @@ export const MULTIMODAL_SEARCH_QUERY_BOOL = {
},
},
};
export const HYBRID_SEARCH_QUERY_MATCH_KNN = {
_source: {
excludes: [VECTOR_FIELD_PATTERN],
},
query: {
hybrid: {
queries: [
{
match: {
[TEXT_FIELD_PATTERN]: {
query: QUERY_TEXT_PATTERN,
},
},
},
{
knn: {
[VECTOR_FIELD_PATTERN]: {
vector: VECTOR_PATTERN,
k: DEFAULT_K,
},
},
},
],
},
},
};
export const HYBRID_SEARCH_QUERY_MATCH_NEURAL = {
_source: {
excludes: [VECTOR_FIELD_PATTERN],
Expand All @@ -253,7 +282,7 @@ export const HYBRID_SEARCH_QUERY_MATCH_NEURAL = {
[VECTOR_FIELD_PATTERN]: {
query_text: QUERY_TEXT_PATTERN,
model_id: MODEL_ID_PATTERN,
k: 5,
k: DEFAULT_K,
},
},
},
Expand Down Expand Up @@ -312,6 +341,10 @@ export const QUERY_PRESETS = [
name: `${WORKFLOW_TYPE.MULTIMODAL_SEARCH} (neural)`,
query: customStringify(MULTIMODAL_SEARCH_QUERY_NEURAL),
},
{
name: `Hybrid search (match & k-NN queries)`,
query: customStringify(HYBRID_SEARCH_QUERY_MATCH_KNN),
},
{
name: `Hybrid search (match & term queries)`,
query: customStringify(HYBRID_SEARCH_QUERY_MATCH_TERM),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,18 @@ export function SelectWithCustomOptions(props: SelectWithCustomOptionsProps) {
// selected option state
const [selectedOption, setSelectedOption] = useState<any[]>([]);

// update the selected option when the form is updated. set to empty if the form value is undefined
// or an empty string ('')
// 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.
useEffect(() => {
const formValue = getIn(values, props.fieldPath);
if (!isEmpty(formValue)) {
setSelectedOption([{ label: getIn(values, props.fieldPath) }]);
} else {
setSelectedOption([]);
if (props.options.length > 0) {
setFieldTouched(props.fieldPath, true);
setFieldValue(props.fieldPath, props.options[0].label);
}
}
}, [getIn(values, props.fieldPath)]);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,12 @@ import {
EuiFlexGroup,
EuiFlexItem,
EuiFormRow,
EuiSmallButton,
EuiSuperSelect,
EuiSuperSelectOption,
EuiText,
EuiTitle,
EuiSpacer,
} from '@elastic/eui';
import { SearchHit, WorkflowFormValues } from '../../../../../common';
import { JsonField } from '../input_fields';
Expand Down Expand Up @@ -142,41 +144,46 @@ export function ConfigureSearchRequest(props: ConfigureSearchRequestProps) {
/>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButton
fill={false}
style={{ width: '100px' }}
size="s"
onClick={() => {
// for this test query, we don't want to involve any configured search pipelines, if any exist
// see https://opensearch.org/docs/latest/search-plugins/search-pipelines/using-search-pipeline/#disabling-the-default-pipeline-for-a-request
dispatch(
searchIndex({
apiBody: {
index: values.search.index.name,
body: values.search.request,
searchPipeline: '_none',
},
dataSourceId,
})
)
.unwrap()
.then(async (resp) => {
props.setQueryResponse(
JSON.stringify(
resp.hits.hits.map((hit: SearchHit) => hit._source),
undefined,
2
)
);
})
.catch((error: any) => {
props.setQueryResponse('');
console.error('Error running query: ', error);
});
}}
>
Test
</EuiButton>
<>
<EuiSmallButton
fill={false}
style={{ width: '100px' }}
onClick={() => {
// for this test query, we don't want to involve any configured search pipelines, if any exist
// see https://opensearch.org/docs/latest/search-plugins/search-pipelines/using-search-pipeline/#disabling-the-default-pipeline-for-a-request
dispatch(
searchIndex({
apiBody: {
index: values.search.index.name,
body: values.search.request,
searchPipeline: '_none',
},
dataSourceId,
})
)
.unwrap()
.then(async (resp) => {
props.setQueryResponse(
JSON.stringify(
resp.hits.hits.map((hit: SearchHit) => hit._source),
undefined,
2
)
);
})
.catch((error: any) => {
props.setQueryResponse('');
console.error('Error running query: ', error);
});
}}
>
Test
</EuiSmallButton>
<EuiSpacer size="s" />
<EuiText size="s" color="subdued">
Run query without any search pipeline configuration.
</EuiText>
</>
</EuiFlexItem>
</EuiFlexGroup>
</>
Expand Down
49 changes: 33 additions & 16 deletions public/pages/workflows/new_workflow/quick_configure_modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {
MapFormValue,
QuickConfigureFields,
TEXT_FIELD_PATTERN,
VECTOR,
VECTOR_FIELD_PATTERN,
WORKFLOW_NAME_REGEXP,
WORKFLOW_TYPE,
Expand Down Expand Up @@ -172,8 +173,8 @@ function injectQuickConfigureFields(
workflow.ui_metadata.config,
quickConfigureFields
);
workflow.ui_metadata.config = updateSearchRequestConfig(
workflow.ui_metadata.config,
workflow.ui_metadata.config.search.request.value = injectPlaceholderValues(
(workflow.ui_metadata.config.search.request.value || '') as string,
quickConfigureFields
);
workflow.ui_metadata.config = updateSearchRequestProcessorConfig(
Expand Down Expand Up @@ -228,6 +229,7 @@ function updateIngestProcessorConfig(
}

// prefill ML search request processor config, if applicable
// including populating placeholders in any pre-configured query_template
function updateSearchRequestProcessorConfig(
config: WorkflowConfig,
fields: QuickConfigureFields
Expand All @@ -236,10 +238,28 @@ function updateSearchRequestProcessorConfig(
if (field.id === 'model' && fields.embeddingModelId) {
field.value = { id: fields.embeddingModelId };
}
if (field.id === 'input_map' || field.id === 'output_map') {
if (field.id === 'input_map') {
// TODO: pre-populate more if the query becomes standard
field.value = [[EMPTY_MAP_ENTRY]] as MapArrayFormValue;
}
if (field.id === 'output_map') {
// prepopulate 'vector' constant as the model output transformed field,
// so it is consistent and used in the downstream query_template, if configured.
field.value = [[{ key: VECTOR, value: '' }]] as MapArrayFormValue;
}
});
config.search.enrichRequest.processors[0].optionalFields = config.search.enrichRequest.processors[0].optionalFields?.map(
(optionalField) => {
let updatedOptionalField = optionalField;
if (optionalField.id === 'query_template') {
optionalField.value = injectPlaceholderValues(
(optionalField.value || '') as string,
fields
);
}
return updatedOptionalField;
}
);

return config;
}
Expand Down Expand Up @@ -295,39 +315,36 @@ function updateIndexConfig(
return config;
}

// pre-populate placeholders in the query, if applicable
function updateSearchRequestConfig(
config: WorkflowConfig,
// pre-populate placeholders for a query request string
function injectPlaceholderValues(
requestString: string,
fields: QuickConfigureFields
): WorkflowConfig {
): string {
let finalRequestString = requestString;
if (fields.embeddingModelId) {
config.search.request.value = ((config.search.request.value ||
'') as string).replace(
finalRequestString = finalRequestString.replace(
new RegExp(MODEL_ID_PATTERN, 'g'),
fields.embeddingModelId
);
}
if (fields.textField) {
config.search.request.value = ((config.search.request.value ||
'') as string).replace(
finalRequestString = finalRequestString.replace(
new RegExp(TEXT_FIELD_PATTERN, 'g'),
fields.textField
);
}
if (fields.vectorField) {
config.search.request.value = ((config.search.request.value ||
'') as string).replace(
finalRequestString = finalRequestString.replace(
new RegExp(VECTOR_FIELD_PATTERN, 'g'),
fields.vectorField
);
}
if (fields.imageField) {
config.search.request.value = ((config.search.request.value ||
'') as string).replace(
finalRequestString = finalRequestString.replace(
new RegExp(IMAGE_FIELD_PATTERN, 'g'),
fields.imageField
);
}

return config;
return finalRequestString;
}
Loading

0 comments on commit 831f71e

Please sign in to comment.