Skip to content

Commit 5064a18

Browse files
opensearch-trigger-bot[bot]github-actions[bot]Kama Huang
authored
filter presets and processors based on backend version (opensearch-project#537) (opensearch-project#556)
(cherry picked from commit ea08b7d) Signed-off-by: Kama Huang <kamahuan@amazon.com> Signed-off-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: Kama Huang <kamahuan@amazon.com>
1 parent d61a05a commit 5064a18

16 files changed

+594
-246
lines changed

common/constants.ts

+6
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,10 @@ export enum WORKFLOW_TYPE {
169169
CUSTOM = 'Custom',
170170
UNKNOWN = 'Unknown',
171171
}
172+
// If no datasource version is found, we default to 2.17.0
173+
export const MIN_SUPPORTED_VERSION = '2.17.0';
174+
// Min version to support ML processors
175+
export const MINIMUM_FULL_SUPPORTED_VERSION = '2.19.0';
172176

173177
// the names should be consistent with the underlying implementation. used when generating the
174178
// final ingest/search pipeline configurations.
@@ -180,6 +184,8 @@ export enum PROCESSOR_TYPE {
180184
NORMALIZATION = 'normalization-processor',
181185
COLLAPSE = 'collapse',
182186
RERANK = 'rerank',
187+
TEXT_EMBEDDING = 'text_embedding',
188+
TEXT_IMAGE_EMBEDDING = 'text_image_embedding',
183189
}
184190

185191
export enum MODEL_TYPE {

opensearch_dashboards.json

+5-13
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,13 @@
55
"server": true,
66
"ui": true,
77
"requiredBundles": [],
8-
"requiredPlugins": [
9-
"navigation",
10-
"opensearchDashboardsUtils"
11-
],
8+
"requiredPlugins": ["navigation", "opensearchDashboardsUtils"],
129
"optionalPlugins": [
1310
"dataSource",
1411
"dataSourceManagement",
1512
"contentManagement"
1613
],
17-
"supportedOSDataSourceVersions": ">=2.18.0 <4.0.0",
18-
"requiredOSDataSourcePlugins": [
19-
"opensearch-ml",
20-
"opensearch-flow-framework"
21-
],
22-
"configPath": [
23-
"flowFrameworkDashboards"
24-
]
25-
}
14+
"supportedOSDataSourceVersions": ">=2.17.0 <4.0.0",
15+
"requiredOSDataSourcePlugins": ["opensearch-ml", "opensearch-flow-framework"],
16+
"configPath": ["flowFrameworkDashboards"]
17+
}

public/configs/ingest_processors/index.ts

+2
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,5 @@ export * from './ml_ingest_processor';
77
export * from './split_ingest_processor';
88
export * from './sort_ingest_processor';
99
export * from './text_chunking_ingest_processor';
10+
export * from './text_embedding_ingest_processor';
11+
export * from './text_image_embedding_ingest_processor';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { PROCESSOR_TYPE } from '../../../common';
2+
import { generateId } from '../../utils';
3+
import { Processor } from '../processor';
4+
5+
export class TextEmbeddingIngestProcessor extends Processor {
6+
constructor() {
7+
super();
8+
this.name = 'Text Embedding Processor';
9+
this.type = PROCESSOR_TYPE.TEXT_EMBEDDING;
10+
this.id = generateId('text_embedding_processor_ingest');
11+
this.fields = [
12+
{
13+
id: 'model_id',
14+
type: 'string',
15+
},
16+
{
17+
id: 'field_map',
18+
type: 'map',
19+
},
20+
];
21+
this.optionalFields = [
22+
{
23+
id: 'description',
24+
type: 'string',
25+
},
26+
{
27+
id: 'tag',
28+
type: 'string',
29+
},
30+
{
31+
id: 'batch_size',
32+
type: 'number',
33+
value: 1,
34+
},
35+
];
36+
}
37+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import { PROCESSOR_TYPE } from '../../../common';
2+
import { generateId } from '../../utils';
3+
import { Processor } from '../processor';
4+
5+
export class TextImageEmbeddingIngestProcessor extends Processor {
6+
constructor() {
7+
super();
8+
this.name = 'Text Image Embedding Processor';
9+
this.type = PROCESSOR_TYPE.TEXT_IMAGE_EMBEDDING;
10+
this.id = generateId('text_image_embedding_processor_ingest');
11+
this.fields = [
12+
{
13+
id: 'model_id',
14+
type: 'string',
15+
},
16+
{
17+
id: 'embedding',
18+
type: 'string',
19+
},
20+
{
21+
id: 'field_map',
22+
type: 'map',
23+
},
24+
];
25+
this.optionalFields = [
26+
{
27+
id: 'description',
28+
type: 'string',
29+
},
30+
{
31+
id: 'tag',
32+
type: 'string',
33+
},
34+
];
35+
}
36+
}

public/pages/workflow_detail/components/header.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ export function WorkflowDetailHeader(props: WorkflowDetailHeaderProps) {
115115

116116
// get & render the data source component, if applicable
117117
let DataSourceComponent: ReactElement | null = null;
118-
if (dataSourceEnabled && getDataSourceManagementPlugin()) {
118+
if (dataSourceEnabled && getDataSourceManagementPlugin() && dataSourceId) {
119119
const DataSourceMenu = getDataSourceManagementPlugin().ui.getDataSourceMenu<
120120
DataSourceViewConfig
121121
>();

public/pages/workflow_detail/workflow_detail.test.tsx

+26-25
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import { WorkflowDetailRouterProps } from '../../pages';
1313
import '@testing-library/jest-dom';
1414
import { mockStore, resizeObserverMock } from '../../../test/utils';
1515
import { createMemoryHistory } from 'history';
16-
import { WORKFLOW_TYPE } from '../../../common';
16+
import { MINIMUM_FULL_SUPPORTED_VERSION, WORKFLOW_TYPE } from '../../../common';
1717

1818
jest.mock('../../services', () => {
1919
const { mockCoreServices } = require('../../../test');
@@ -39,15 +39,22 @@ const renderWithRouter = (
3939
initialEntries: [`/workflow/${workflowId}`],
4040
});
4141

42+
const mockInput = {
43+
id: workflowId,
44+
name: workflowName,
45+
type: workflowType,
46+
version: [
47+
WORKFLOW_TYPE.SEMANTIC_SEARCH,
48+
WORKFLOW_TYPE.MULTIMODAL_SEARCH,
49+
WORKFLOW_TYPE.HYBRID_SEARCH,
50+
].includes(workflowType)
51+
? MINIMUM_FULL_SUPPORTED_VERSION
52+
: undefined,
53+
};
54+
4255
return {
4356
...render(
44-
<Provider
45-
store={mockStore({
46-
id: workflowId,
47-
name: workflowName,
48-
type: workflowType,
49-
})}
50-
>
57+
<Provider store={mockStore(mockInput)}>
5158
<Router history={history}>
5259
<Switch>
5360
<Route
@@ -68,6 +75,7 @@ describe('WorkflowDetail Page with create ingestion option', () => {
6875
beforeEach(() => {
6976
jest.clearAllMocks();
7077
});
78+
7179
Object.values(WORKFLOW_TYPE).forEach((type) => {
7280
test(`renders the WorkflowDetail page with ${type} type`, async () => {
7381
const {
@@ -110,33 +118,27 @@ describe('WorkflowDetail Page Functionality (Custom Workflow)', () => {
110118
workflowName,
111119
WORKFLOW_TYPE.CUSTOM
112120
);
113-
114121
// Export button opens the export component
115122
userEvent.click(getByTestId('exportButton'));
116123
await waitFor(() => {
117124
expect(getByText(`Export '${workflowName}'`)).toBeInTheDocument();
118125
});
119-
120126
// Close the export component
121127
userEvent.click(getByTestId('exportCloseButton'));
122-
123128
// Check workspace button group exists (Visual and JSON)
124129
getByTestId('visualJSONToggleButtonGroup');
125-
126-
// Tools panel should collapse and expand on toggle
130+
// Tools panel should collapse and expand the toggle
127131
const toolsPanel = container.querySelector('#tools_panel_id');
128132
expect(toolsPanel).toBeVisible();
129133

130134
const toggleButton = toolsPanel?.querySelector('button[type="button"]');
131135
expect(toggleButton).toBeInTheDocument();
132136
userEvent.click(toggleButton!);
133-
134137
// Tools panel after collapsing
135138
const collapsedToolsPanel = container.querySelector('#tools_panel_id');
136139
await waitFor(() => {
137140
expect(collapsedToolsPanel).toHaveClass('euiResizablePanel-isCollapsed');
138141
});
139-
140142
// Tools panel after expanding
141143
userEvent.click(toggleButton!);
142144
const expandedToolsPanel = container.querySelector('#tools_panel_id');
@@ -153,7 +155,6 @@ describe('WorkflowDetail Page Functionality (Custom Workflow)', () => {
153155
workflowName,
154156
WORKFLOW_TYPE.CUSTOM
155157
);
156-
157158
// The WorkflowDetail Page Close button should navigate back to the workflows list
158159
userEvent.click(getByTestId('closeButton'));
159160
await waitFor(() => {
@@ -166,57 +167,57 @@ describe('WorkflowDetail Page with skip ingestion option (Hybrid Search Workflow
166167
beforeEach(() => {
167168
jest.clearAllMocks();
168169
});
170+
169171
test(`renders the WorkflowDetail page with skip ingestion option`, async () => {
170172
const { getByTestId, getAllByText, getAllByTestId } = renderWithRouter(
171173
workflowId,
172174
workflowName,
173175
WORKFLOW_TYPE.HYBRID_SEARCH
174176
);
175-
176177
// Defining a new ingest pipeline & index is enabled by default
177178
const enabledCheckbox = getByTestId('switch-ingest.enabled');
178-
179179
// Skipping ingest pipeline and navigating to search
180180
userEvent.click(enabledCheckbox);
181181
await waitFor(() => {});
182+
182183
const searchPipelineButton = getByTestId('searchPipelineButton');
183184
userEvent.click(searchPipelineButton);
184-
185185
// Search pipeline
186186
await waitFor(() => {
187187
expect(getAllByText('Define search flow').length).toBeGreaterThan(0);
188188
});
189189
expect(getAllByText('Configure query').length).toBeGreaterThan(0);
190-
191190
// Edit Search Query
192191
const queryEditButton = getByTestId('queryEditButton');
193192
expect(queryEditButton).toBeInTheDocument();
194193
userEvent.click(queryEditButton);
194+
195195
await waitFor(() => {
196196
expect(getAllByText('Edit query definition').length).toBeGreaterThan(0);
197197
});
198+
198199
const searchQueryPresetButton = getByTestId('searchQueryPresetButton');
199200
expect(searchQueryPresetButton).toBeInTheDocument();
200201
const updateSearchQueryButton = getByTestId('updateSearchQueryButton');
201202
expect(updateSearchQueryButton).toBeInTheDocument();
202203
userEvent.click(updateSearchQueryButton);
203-
204204
// Add request processor
205205
const addRequestProcessorButton = await waitFor(
206206
() => getAllByTestId('addProcessorButton')[0]
207207
);
208208
userEvent.click(addRequestProcessorButton);
209+
209210
await waitFor(() => {
210-
expect(getAllByText('PROCESSORS').length).toBeGreaterThan(0);
211+
const popoverPanel = document.querySelector('.euiPopover__panel');
212+
expect(popoverPanel).toBeTruthy();
211213
});
212-
213214
// Add response processor
214215
const addResponseProcessorButton = getAllByTestId('addProcessorButton')[1];
215216
userEvent.click(addResponseProcessorButton);
216217
await waitFor(() => {
217-
expect(getAllByText('PROCESSORS').length).toBeGreaterThan(0);
218+
const popoverPanel = document.querySelector('.euiPopover__panel');
219+
expect(popoverPanel).toBeTruthy();
218220
});
219-
220221
// Build and Run query, Back buttons are present
221222
const searchPipelineBackButton = getByTestId('searchPipelineBackButton');
222223
userEvent.click(searchPipelineBackButton);

public/pages/workflow_detail/workflow_detail.tsx

+3-1
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,9 @@ export function WorkflowDetail(props: WorkflowDetailProps) {
126126
}, [USE_NEW_HOME_PAGE, dataSourceEnabled, dataSourceId, workflowName]);
127127

128128
// form state
129-
const [formValues, setFormValues] = useState<WorkflowFormValues>({});
129+
const [formValues, setFormValues] = useState<WorkflowFormValues>(
130+
{} as WorkflowFormValues
131+
);
130132
const [formSchema, setFormSchema] = useState<WorkflowSchema>(yup.object({}));
131133

132134
// ingest docs state. we need to persist here to update the form values.

public/pages/workflow_detail/workflow_inputs/ingest_inputs/enrich_data.tsx

-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
88
import { ProcessorsList } from '../processors_list';
99
import { PROCESSOR_CONTEXT, WorkflowConfig } from '../../../../../common';
1010
import { ProcessorsTitle } from '../../../../general_components';
11-
1211
interface EnrichDataProps {
1312
uiConfig: WorkflowConfig;
1413
setUiConfig: (uiConfig: WorkflowConfig) => void;

0 commit comments

Comments
 (0)