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

[Backport 2.x] Add initial component interfaces & basic node rendering #35

Merged
merged 1 commit into from
Sep 21, 2023
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
86 changes: 86 additions & 0 deletions public/component_types/base_interfaces.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

import { COMPONENT_CATEGORY } from '../utils';

/**
* ************ Types **************************
*/

// TODO: may change some/all of these to enums later
export type BaseClass = string;
export type UIFlow = string;
export type FieldType = 'string' | 'json' | 'select';

/**
* ************ Base interfaces ****************
*/

/**
* Represents a single base class as an input handle for a component.
* It may be optional. It may also accept multiples of that class.
*/
export interface IComponentInput {
id: string;
label: string;
baseClass: string;
optional: boolean;
acceptMultiple: boolean;
}

/**
* An input field for a component. Specifies enough configuration for the
* UI node to render it properly within the component (show it as optional,
* put it in advanced settings, placeholder values, etc.)
*/
export interface IComponentField {
label: string;
type: FieldType;
placeholder?: string;
optional?: boolean;
advanced?: boolean;
}

/**
* Represents the list of base classes as a single output handle for
* a component.
*/
export interface IComponentOutput {
id: string;
label: string;
baseClasses: BaseClass[];
}

/**
* The base interface the components will implement.
*/
export interface IComponent {
id: string;
type: BaseClass;
label: string;
description: string;
// will be used for grouping together in the drag-and-drop component library
category: COMPONENT_CATEGORY;
// determines if this component allows for new creation. this means to
// allow a "create" option on the UI component, as well as potentially
// include in the use case template construction ('provisioning' flow)
allowsCreation: boolean;
// determines if this is something that will be included in the use
// case template construction (query or ingest flows). provisioning flow
// is handled by the allowsCreation flag above.
isApplicationStep: boolean;
// the set of allowed flows this component can be drug into the workspace
allowedFlows: UIFlow[];
// the list of base classes that will be used in the component output
baseClasses?: BaseClass[];
inputs?: IComponentInput[];
fields?: IComponentField[];
// if the component supports creation, we will have a different set of input fields
// the user needs to fill out
createFields?: IComponentField[];
outputs?: IComponentOutput[];
// we will need some init function when the component is drug into the workspace
init?(): Promise<any>;
}
8 changes: 8 additions & 0 deletions public/component_types/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

export * from './base_interfaces';
export * from './processors';
export * from './indices';
6 changes: 6 additions & 0 deletions public/component_types/indices/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

export * from './knn_index';
87 changes: 87 additions & 0 deletions public/component_types/indices/knn_index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

import { COMPONENT_CATEGORY } from '../../utils';
import {
IComponent,
IComponentField,
IComponentInput,
IComponentOutput,
UIFlow,
BaseClass,
} from '../base_interfaces';

/**
* A k-NN index UI component
*/
export class KnnIndex implements IComponent {
id: string;
type: BaseClass;
label: string;
description: string;
category: COMPONENT_CATEGORY;
allowsCreation: boolean;
isApplicationStep: boolean;
allowedFlows: UIFlow[];
baseClasses: BaseClass[];
inputs: IComponentInput[];
fields: IComponentField[];
createFields: IComponentField[];
outputs: IComponentOutput[];

constructor() {
this.id = 'knn_index';
this.type = 'knn_index';
this.label = 'k-NN Index';
this.description = 'A k-NN Index to be used as a vector store';
this.category = COMPONENT_CATEGORY.INDICES;
this.allowsCreation = true;
this.isApplicationStep = false;
// TODO: 'other' may not be how this is stored. the idea is 'other' allows
// for placement outside of the ingest or query flows- typically something
// that will be referenced/used as input across multiple flows
this.allowedFlows = ['Ingest', 'Query', 'Other'];
this.baseClasses = [this.type];
this.inputs = [];
this.fields = [
{
label: 'Index Name',
type: 'select',
optional: false,
advanced: false,
},
];
this.createFields = [
{
label: 'Index Name',
type: 'string',
optional: false,
advanced: false,
},
// we don't need to expose "settings" here since it will be index.knn by default
// just let users customize the mappings
// TODO: figure out how to handle defaults for all of these values. maybe toggle between
// simple form inputs vs. complex JSON editor
{
label: 'Mappings',
type: 'json',
placeholder: 'Enter an index mappings JSON blob...',
optional: false,
advanced: false,
},
];
this.outputs = [
{
id: this.id,
label: this.label,
baseClasses: this.baseClasses,
},
];
}

async init(): Promise<any> {
return new KnnIndex();
}
}
6 changes: 6 additions & 0 deletions public/component_types/processors/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

export * from './text_embedding_processor';
77 changes: 77 additions & 0 deletions public/component_types/processors/text_embedding_processor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

import { COMPONENT_CATEGORY } from '../../utils';
import {
IComponent,
IComponentField,
IComponentInput,
IComponentOutput,
UIFlow,
BaseClass,
} from '../base_interfaces';

/**
* A text embedding processor UI component
*/
export class TextEmbeddingProcessor implements IComponent {
id: string;
type: BaseClass;
label: string;
description: string;
category: COMPONENT_CATEGORY;
allowsCreation: boolean;
isApplicationStep: boolean;
allowedFlows: UIFlow[];
baseClasses: BaseClass[];
inputs: IComponentInput[];
fields: IComponentField[];
outputs: IComponentOutput[];

constructor() {
this.id = 'text_embedding_processor';
this.type = 'text_embedding_processor';
this.label = 'Text Embedding Processor';
this.description =
'A text embedding ingest processor to be used in an ingest pipeline';
this.category = COMPONENT_CATEGORY.INGEST_PROCESSORS;
this.allowsCreation = false;
this.isApplicationStep = false;
this.allowedFlows = ['Ingest'];
this.baseClasses = [this.type];
this.inputs = [];
this.fields = [
{
label: 'Model ID',
type: 'string',
optional: false,
advanced: false,
},
{
label: 'Input Field',
type: 'string',
optional: false,
advanced: false,
},
{
label: 'Output Field',
type: 'string',
optional: false,
advanced: false,
},
];
this.outputs = [
{
id: this.id,
label: this.label,
baseClasses: this.baseClasses,
},
];
}

async init(): Promise<any> {
return new TextEmbeddingProcessor();
}
}
8 changes: 8 additions & 0 deletions public/pages/workflow_builder/components/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

export * from './input_fields';
export { NewOrExistingTabs } from './new_or_existing_tabs';
export { InputFieldList } from './input_field_list';
62 changes: 62 additions & 0 deletions public/pages/workflow_builder/components/input_field_list.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

import React from 'react';
import { EuiFlexItem, EuiSpacer } from '@elastic/eui';
import { IComponentField } from '../../../component_types';
import { TextField, JsonField, SelectField } from './input_fields';

/**
* A helper component to format all of the input fields for a component. Dynamically
* render based on the input type.
*/

interface InputFieldListProps {
inputFields?: IComponentField[];
}

export function InputFieldList(props: InputFieldListProps) {
return (
<EuiFlexItem>
{props.inputFields?.map((field, idx) => {
let el;
switch (field.type) {
case 'string': {
el = (
<EuiFlexItem key={idx}>
<TextField
label={field.label}
placeholder={field.placeholder || ''}
/>
<EuiSpacer size="s" />
</EuiFlexItem>
);
break;
}
case 'json': {
el = (
<EuiFlexItem key={idx}>
<JsonField
label={field.label}
placeholder={field.placeholder || ''}
/>
</EuiFlexItem>
);
break;
}
case 'select': {
el = (
<EuiFlexItem key={idx}>
<SelectField />
</EuiFlexItem>
);
break;
}
}
return el;
})}
</EuiFlexItem>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

export { TextField } from './text_field';
export { JsonField } from './json_field';
export { SelectField } from './select_field';
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

import React from 'react';
import { EuiText, EuiTextArea } from '@elastic/eui';

interface JsonFieldProps {
label: string;
placeholder: string;
}

/**
* An input field for a component where users manually enter
* in some custom JSON
*/
export function JsonField(props: JsonFieldProps) {
return (
<>
<EuiText size="s" className="eui-textLeft">
{props.label}
</EuiText>
<EuiTextArea placeholder={props.placeholder} />
</>
);
}
Loading
Loading