Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Set default outputs and display schema based on full_response_path #382

Merged
merged 1 commit into from
Sep 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 8 additions & 6 deletions common/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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';
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -228,6 +232,7 @@ export function MLProcessorInputs(props: MLProcessorInputsProps) {
<OutputTransformModal
uiConfig={props.uiConfig}
config={props.config}
baseConfigPath={props.baseConfigPath}
context={props.context}
outputMapField={outputMapField}
outputMapFieldPath={outputMapFieldPath}
Expand Down Expand Up @@ -345,7 +350,11 @@ export function MLProcessorInputs(props: MLProcessorInputsProps) {
: 'New document field'
}
valuePlaceholder="Model output field"
valueOptions={parseModelOutputs(modelInterface)}
valueOptions={
fullResponsePath
? undefined
: parseModelOutputs(modelInterface, false)
}
/>
<EuiSpacer size="s" />
{inputMapValue.length !== outputMapValue.length &&
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ import {
EuiSmallButton,
EuiSpacer,
EuiText,
EuiPopover,
EuiSmallButtonEmpty,
EuiPopoverTitle,
EuiCodeBlock,
} from '@elastic/eui';
import {
IConfigField,
Expand Down Expand Up @@ -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;
Expand All @@ -77,8 +86,15 @@ export function OutputTransformModal(props: OutputTransformModalProps) {
const [sourceOutput, setSourceOutput] = useState<string>('[]');
const [transformedOutput, setTransformedOutput] = useState<string>('{}');

// 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<boolean>(false);

// selected transform state
const transformOptions = map.map((_, idx) => ({
Expand Down Expand Up @@ -123,7 +139,44 @@ export function OutputTransformModal(props: OutputTransformModalProps) {
Fetch some sample output data and see how it is transformed.
</EuiText>
<EuiSpacer size="s" />
<EuiText>Source output</EuiText>
<EuiFlexGroup direction="row" justifyContent="spaceBetween">
<EuiFlexItem>
<EuiText>Source output</EuiText>
</EuiFlexItem>
{!isEmpty(
parseModelOutputsObj(props.modelInterface, fullResponsePath)
) && (
<EuiFlexItem grow={false}>
<EuiPopover
isOpen={popoverOpen}
closePopover={() => setPopoverOpen(false)}
button={
<EuiSmallButtonEmpty
onClick={() => setPopoverOpen(!popoverOpen)}
>
View output schema
</EuiSmallButtonEmpty>
}
>
<EuiPopoverTitle>
The JSON Schema defining the model's expected output
</EuiPopoverTitle>
<EuiCodeBlock
language="json"
fontSize="m"
isCopyable={false}
>
{customStringify(
parseModelOutputsObj(
props.modelInterface,
fullResponsePath
)
)}
</EuiCodeBlock>
</EuiPopover>
</EuiFlexItem>
)}
</EuiFlexGroup>
<EuiSmallButton
style={{ width: '100px' }}
isLoading={isFetching}
Expand Down Expand Up @@ -277,7 +330,11 @@ export function OutputTransformModal(props: OutputTransformModalProps) {
helpLink={ML_INFERENCE_DOCS_LINK}
keyPlaceholder="Document field"
valuePlaceholder="Model output field"
valueOptions={parseModelOutputs(props.modelInterface)}
valueOptions={
fullResponsePath
? undefined
: parseModelOutputs(props.modelInterface, false)
}
// If the map we are adding is the first one, populate the selected option to index 0
onMapAdd={(curArray) => {
if (isEmpty(curArray)) {
Expand Down
37 changes: 32 additions & 5 deletions public/utils/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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';
Expand Down Expand Up @@ -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(
Expand All @@ -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 => {
Expand Down
Loading