From 73571f69c38889240a04aa3c63b291f1e1e2a72b Mon Sep 17 00:00:00 2001 From: Tyler Ohlsen Date: Mon, 16 Sep 2024 14:49:53 -0700 Subject: [PATCH] set defaults and display output schema based on fullresponsepath Signed-off-by: Tyler Ohlsen --- common/constants.ts | 14 ++-- .../processor_inputs/ml_processor_inputs.tsx | 11 +++- .../output_transform_modal.tsx | 65 +++++++++++++++++-- public/utils/utils.ts | 37 +++++++++-- 4 files changed, 111 insertions(+), 16 deletions(-) diff --git a/common/constants.ts b/common/constants.ts index 05d911a8..873e733a 100644 --- a/common/constants.ts +++ b/common/constants.ts @@ -410,6 +410,11 @@ export const QUERY_PRESETS = [ /** * MISCELLANEOUS */ +export enum PROCESSOR_CONTEXT { + INGEST = 'ingest', + SEARCH_REQUEST = 'search_request', + SEARCH_RESPONSE = 'search_response', +} export const START_FROM_SCRATCH_WORKFLOW_NAME = 'Start From Scratch'; export const DEFAULT_NEW_WORKFLOW_NAME = 'new_workflow'; export const DEFAULT_NEW_WORKFLOW_DESCRIPTION = 'My new workflow'; @@ -434,9 +439,6 @@ export const MAX_JSON_STRING_LENGTH = 10000; export const MAX_WORKFLOW_NAME_TO_DISPLAY = 40; export const WORKFLOW_NAME_REGEXP = RegExp('^[a-zA-Z0-9_-]*$'); export const EMPTY_MAP_ENTRY = { key: '', value: '' } as MapEntry; - -export enum PROCESSOR_CONTEXT { - INGEST = 'ingest', - SEARCH_REQUEST = 'search_request', - SEARCH_RESPONSE = 'search_response', -} +export const MODEL_OUTPUT_SCHEMA_NESTED_PATH = + 'output.properties.inference_results.items.properties.output.items.properties.dataAsMap.properties'; +export const MODEL_OUTPUT_SCHEMA_FULL_PATH = 'output.properties'; diff --git a/public/pages/workflow_detail/workflow_inputs/processor_inputs/ml_processor_inputs.tsx b/public/pages/workflow_detail/workflow_inputs/processor_inputs/ml_processor_inputs.tsx index 6f1b53ad..f6d3be5e 100644 --- a/public/pages/workflow_detail/workflow_inputs/processor_inputs/ml_processor_inputs.tsx +++ b/public/pages/workflow_detail/workflow_inputs/processor_inputs/ml_processor_inputs.tsx @@ -79,6 +79,10 @@ export function MLProcessorInputs(props: MLProcessorInputsProps) { ) as IConfigField; const outputMapFieldPath = `${props.baseConfigPath}.${props.config.id}.${outputMapField.id}`; const outputMapValue = getIn(values, outputMapFieldPath); + const fullResponsePath = getIn( + values, + `${props.baseConfigPath}.${props.config.id}.full_response_path` + ); // preview availability states // if there are preceding search request processors, we cannot fetch and display the interim transformed query. @@ -228,6 +232,7 @@ export function MLProcessorInputs(props: MLProcessorInputsProps) { {inputMapValue.length !== outputMapValue.length && diff --git a/public/pages/workflow_detail/workflow_inputs/processor_inputs/output_transform_modal.tsx b/public/pages/workflow_detail/workflow_inputs/processor_inputs/output_transform_modal.tsx index 785e5f16..11b9d922 100644 --- a/public/pages/workflow_detail/workflow_inputs/processor_inputs/output_transform_modal.tsx +++ b/public/pages/workflow_detail/workflow_inputs/processor_inputs/output_transform_modal.tsx @@ -20,6 +20,10 @@ import { EuiSmallButton, EuiSpacer, EuiText, + EuiPopover, + EuiSmallButtonEmpty, + EuiPopoverTitle, + EuiCodeBlock, } from '@elastic/eui'; import { IConfigField, @@ -50,11 +54,16 @@ import { } from '../../../../store'; import { getCore } from '../../../../services'; import { MapArrayField } from '../input_fields'; -import { getDataSourceId, parseModelOutputs } from '../../../../utils/utils'; +import { + getDataSourceId, + parseModelOutputs, + parseModelOutputsObj, +} from '../../../../utils/utils'; interface OutputTransformModalProps { uiConfig: WorkflowConfig; config: IProcessorConfig; + baseConfigPath: string; context: PROCESSOR_CONTEXT; outputMapField: IConfigField; outputMapFieldPath: string; @@ -77,8 +86,15 @@ export function OutputTransformModal(props: OutputTransformModalProps) { const [sourceOutput, setSourceOutput] = useState('[]'); const [transformedOutput, setTransformedOutput] = useState('{}'); - // get the current output map + // get some current values const map = getIn(values, props.outputMapFieldPath) as MapArrayFormValue; + const fullResponsePath = getIn( + values, + `${props.baseConfigPath}.${props.config.id}.full_response_path` + ); + + // popover state containing the model interface details, if applicable + const [popoverOpen, setPopoverOpen] = useState(false); // selected transform state const transformOptions = map.map((_, idx) => ({ @@ -123,7 +139,44 @@ export function OutputTransformModal(props: OutputTransformModalProps) { Fetch some sample output data and see how it is transformed. - Source output + + + Source output + + {!isEmpty( + parseModelOutputsObj(props.modelInterface, fullResponsePath) + ) && ( + + setPopoverOpen(false)} + button={ + setPopoverOpen(!popoverOpen)} + > + View output schema + + } + > + + The JSON Schema defining the model's expected output + + + {customStringify( + parseModelOutputsObj( + props.modelInterface, + fullResponsePath + ) + )} + + + + )} + { if (isEmpty(curArray)) { diff --git a/public/utils/utils.ts b/public/utils/utils.ts index b9684929..d6ce3d22 100644 --- a/public/utils/utils.ts +++ b/public/utils/utils.ts @@ -8,6 +8,8 @@ import jsonpath from 'jsonpath'; import { escape, get } from 'lodash'; import { JSONPATH_ROOT_SELECTOR, + MODEL_OUTPUT_SCHEMA_FULL_PATH, + MODEL_OUTPUT_SCHEMA_NESTED_PATH, MapFormValue, ModelInputFormField, ModelInterface, @@ -18,10 +20,13 @@ import { WORKFLOW_RESOURCE_TYPE, WORKFLOW_STEP_TYPE, Workflow, - customStringify, } from '../../common'; import { getCore, getDataSourceEnabled } from '../services'; -import { MDSQueryParams, ModelInputMap } from '../../common/interfaces'; +import { + MDSQueryParams, + ModelInputMap, + ModelOutputMap, +} from '../../common/interfaces'; import queryString from 'query-string'; import { useLocation } from 'react-router-dom'; import * as pluginManifest from '../../opensearch_dashboards.json'; @@ -221,11 +226,19 @@ export function parseModelInputsObj( ) as ModelInputMap; } -// Derive the collection of model outputs from the model interface JSONSchema into a form-ready list +// Derive the collection of model outputs from the model interface JSONSchema into a form-ready list. +// Expose the full path or nested path depending on fullResponsePath export function parseModelOutputs( - modelInterface: ModelInterface | undefined + modelInterface: ModelInterface | undefined, + fullResponsePath: boolean = false ): ModelOutputFormField[] { - const modelOutputsObj = get(modelInterface, 'output.properties', {}) as { + const modelOutputsObj = get( + modelInterface, + fullResponsePath + ? MODEL_OUTPUT_SCHEMA_FULL_PATH + : MODEL_OUTPUT_SCHEMA_NESTED_PATH, + {} + ) as { [key: string]: ModelOutput; }; return Object.keys(modelOutputsObj).map( @@ -237,6 +250,20 @@ export function parseModelOutputs( ); } +// Derive the collection of model outputs as an obj. +// Expose the full path or nested path depending on fullResponsePath +export function parseModelOutputsObj( + modelInterface: ModelInterface | undefined, + fullResponsePath: boolean = false +): ModelOutputMap { + return get( + modelInterface, + fullResponsePath + ? MODEL_OUTPUT_SCHEMA_FULL_PATH + : MODEL_OUTPUT_SCHEMA_NESTED_PATH, + {} + ) as ModelOutputMap; +} export const getDataSourceFromURL = (location: { search: string; }): MDSQueryParams => {