diff --git a/public/component_types/base_interfaces.ts b/public/component_types/base_interfaces.ts new file mode 100644 index 00000000..5f761c42 --- /dev/null +++ b/public/component_types/base_interfaces.ts @@ -0,0 +1,87 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { COMPONENT_CATEGORY } from '../utils'; + +/** + * ************ Types ************************** + */ + +// TODO: may change to enums later +export type BaseClass = string; +export type UIFlow = string; + +// will expand later on +export type FieldType = 'string' | 'json'; + +/** + * ************ 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; + 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; +} diff --git a/public/component_types/index.ts b/public/component_types/index.ts index 77b7b4d5..b732283b 100644 --- a/public/component_types/index.ts +++ b/public/component_types/index.ts @@ -3,4 +3,6 @@ * SPDX-License-Identifier: Apache-2.0 */ -export * from './interfaces'; +export * from './base_interfaces'; +export * from './processors'; +export * from './indices'; diff --git a/public/component_types/indices/index.ts b/public/component_types/indices/index.ts new file mode 100644 index 00000000..cc5778c9 --- /dev/null +++ b/public/component_types/indices/index.ts @@ -0,0 +1,6 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +export * from './knn_index'; diff --git a/public/component_types/indices/knn_index.ts b/public/component_types/indices/knn_index.ts new file mode 100644 index 00000000..8b239180 --- /dev/null +++ b/public/component_types/indices/knn_index.ts @@ -0,0 +1,83 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { COMPONENT_CATEGORY } from '../../utils'; +import { + IComponent, + IComponentField, + IComponentInput, + IComponentOutput, + UIFlow, + BaseClass, +} from '../base_interfaces'; + +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[]; + inputFields: 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: 'Name', + type: 'string', + optional: false, + advanced: false, + }, + ]; + this.inputFields = [ + { + label: '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', + optional: false, + advanced: false, + }, + ]; + this.outputs = [ + { + id: this.id, + label: this.label, + baseClasses: this.baseClasses, + }, + ]; + } + + async init(): Promise { + return new KnnIndex(); + } +} diff --git a/public/component_types/interfaces.ts b/public/component_types/interfaces.ts deleted file mode 100644 index d5cc139b..00000000 --- a/public/component_types/interfaces.ts +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -export interface IComponentInput { - id: string; - label: string; - optional: boolean; -} - -export interface IComponentField { - id: string; - label: string; - optional: boolean; -} - -export interface IComponentOutput { - id: string; - label: string; -} - -export interface IComponent { - id: string; - label: string; - description: string; - inputs?: IComponentInput[]; - fields?: IComponentField[]; - outputs?: IComponentOutput[]; - init?(): Promise; -} diff --git a/public/component_types/model_component.tsx b/public/component_types/model_component.tsx deleted file mode 100644 index 7efa0e4d..00000000 --- a/public/component_types/model_component.tsx +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -import { - IComponent, - IComponentField, - IComponentInput, - IComponentOutput, -} from './interfaces'; - -export class ModelComponent implements IComponent { - id: string; - label: string; - description: string; - inputs: IComponentInput[]; - fields: IComponentField[]; - outputs: IComponentOutput[]; - - constructor() { - this.id = 'model_component'; - this.label = 'Model'; - this.description = 'A deployed ML model'; - this.inputs = []; - this.fields = [ - { - id: 'model_id', - label: 'Model ID', - optional: false, - }, - ]; - this.outputs = [ - { - id: 'model_id', - label: 'Model ID', - }, - ]; - } - - async init(): Promise { - return new ModelComponent(); - } -} diff --git a/public/component_types/processors/index.ts b/public/component_types/processors/index.ts new file mode 100644 index 00000000..364e51bc --- /dev/null +++ b/public/component_types/processors/index.ts @@ -0,0 +1,6 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +export * from './text_embedding_processor'; diff --git a/public/component_types/processors/text_embedding_processor.ts b/public/component_types/processors/text_embedding_processor.ts new file mode 100644 index 00000000..939b0efa --- /dev/null +++ b/public/component_types/processors/text_embedding_processor.ts @@ -0,0 +1,74 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { COMPONENT_CATEGORY } from '../../utils'; +import { + IComponent, + IComponentField, + IComponentInput, + IComponentOutput, + UIFlow, + BaseClass, +} from '../base_interfaces'; + +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 { + return new TextEmbeddingProcessor(); + } +} diff --git a/public/pages/workflow_builder/index.ts b/public/pages/workflow_builder/index.ts index a84bee82..0e54fa8c 100644 --- a/public/pages/workflow_builder/index.ts +++ b/public/pages/workflow_builder/index.ts @@ -4,3 +4,4 @@ */ export { WorkflowBuilder } from './workflow_builder'; +export { WorkflowComponent } from './workflow_component'; diff --git a/public/pages/workflow_builder/workflow_builder.tsx b/public/pages/workflow_builder/workflow_builder.tsx index d50759ac..22f97225 100644 --- a/public/pages/workflow_builder/workflow_builder.tsx +++ b/public/pages/workflow_builder/workflow_builder.tsx @@ -14,6 +14,12 @@ import { } from '@elastic/eui'; import { BREADCRUMBS } from '../../utils'; import { getCore } from '../../services'; +import { + TextEmbeddingProcessor, + IComponent, + KnnIndex, +} from '../../component_types'; +import { WorkflowComponent } from './workflow_component'; export function WorkflowBuilder() { useEffect(() => { @@ -23,6 +29,11 @@ export function WorkflowBuilder() { ]); }); + const curComponents = [ + new TextEmbeddingProcessor(), + new KnnIndex(), + ] as IComponent[]; + return (
@@ -35,9 +46,15 @@ export function WorkflowBuilder() { - - Placeholder for workflow builder page... - + + {curComponents.map((component, idx) => { + return ( + + + + ); + })} +
); } diff --git a/public/pages/workflow_builder/workflow_component.tsx b/public/pages/workflow_builder/workflow_component.tsx index 83106e50..cdffe13b 100644 --- a/public/pages/workflow_builder/workflow_component.tsx +++ b/public/pages/workflow_builder/workflow_component.tsx @@ -4,7 +4,15 @@ */ import React from 'react'; -import { EuiText } from '@elastic/eui'; +import { + EuiFlexGroup, + EuiFlexItem, + EuiFieldText, + EuiText, + EuiSpacer, + EuiCard, +} from '@elastic/eui'; +import { IComponent } from '../../component_types'; /** * This will be the ReactFlow node in the drag-and-drop workspace. It will take in a component @@ -13,13 +21,51 @@ import { EuiText } from '@elastic/eui'; * https://github.com/FlowiseAI/Flowise/blob/main/packages/ui/src/views/canvas/CanvasNode.js */ +interface WorkflowComponentProps { + component: IComponent; +} + // TODO: convert this to a ReactFlow node -export function WorkflowComponent() { +export const WorkflowComponent = (props: WorkflowComponentProps) => { + const { component } = props; + return ( -
- - A placeholder ReactFlow node to render a workflow component - -
+ + + + {component.fields?.map((field, idx) => { + if (field.type === 'string') { + return ( + + + + + ); + } + })} + + + <> + + Inputs: + + {component.inputs?.map((input, idx) => { + return {input.label}; + })} + + + Outputs: + + {component.outputs?.map((output, idx) => { + return {output.label}; + })} + + + + ); -} +}; diff --git a/public/utils/constants.ts b/public/utils/constants.ts index 8db2f6dd..b20b36d9 100644 --- a/public/utils/constants.ts +++ b/public/utils/constants.ts @@ -28,3 +28,8 @@ export const BREADCRUMBS = Object.freeze({ href: `#${APP_PATH.WORKFLOW_BUILDER}`, }, }); + +export enum COMPONENT_CATEGORY { + INGEST_PROCESSORS = 'Ingest Processors', + INDICES = 'Indices', +}