Skip to content

Commit 37788e7

Browse files
Simplify RAG presets; add bulk API details (#610) (#611)
* Add new ml response tab * Add bulk API content --------- (cherry picked from commit ee7ec89) Signed-off-by: Tyler Ohlsen <ohltyler@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>
1 parent 5d13c93 commit 37788e7

File tree

7 files changed

+178
-10
lines changed

7 files changed

+178
-10
lines changed

common/constants.ts

+2
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,8 @@ export const BULK_API_DOCS_LINK =
250250
'https://opensearch.org/docs/latest/api-reference/document-apis/bulk/';
251251
export const SEARCH_PIPELINE_DOCS_LINK =
252252
'https://opensearch.org/docs/latest/search-plugins/search-pipelines/using-search-pipeline/';
253+
export const ML_RESPONSE_PROCESSOR_EXAMPLE_DOCS_LINK =
254+
'https://opensearch.org/docs/latest/search-plugins/search-pipelines/ml-inference-search-response/#example-externally-hosted-text-embedding-model';
253255

254256
/**
255257
* Text chunking algorithm constants
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/*
2+
* Copyright OpenSearch Contributors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
import React from 'react';
7+
import { isEmpty } from 'lodash';
8+
import {
9+
EuiCode,
10+
EuiCodeEditor,
11+
EuiEmptyPrompt,
12+
EuiLink,
13+
EuiSpacer,
14+
EuiText,
15+
} from '@elastic/eui';
16+
import {
17+
customStringify,
18+
ML_RESPONSE_PROCESSOR_EXAMPLE_DOCS_LINK,
19+
} from '../../../common';
20+
21+
interface MLResponseProps {
22+
mlResponse: {};
23+
}
24+
25+
/**
26+
* Small component to render the ML response within a raw search response.
27+
*/
28+
export function MLResponse(props: MLResponseProps) {
29+
return (
30+
<>
31+
<EuiSpacer size="s" />
32+
<EuiText size="s">
33+
Showing results stored in <EuiCode>ext.ml_inference</EuiCode> from the
34+
search response.{' '}
35+
<EuiLink href={ML_RESPONSE_PROCESSOR_EXAMPLE_DOCS_LINK} target="_blank">
36+
See an example
37+
</EuiLink>
38+
</EuiText>
39+
<EuiSpacer size="m" />
40+
{isEmpty(props.mlResponse) ? (
41+
<EuiEmptyPrompt title={<h2>No response found</h2>} titleSize="s" />
42+
) : (
43+
<EuiCodeEditor
44+
mode="json"
45+
theme="textmate"
46+
width="100%"
47+
height="100%"
48+
value={customStringify(props.mlResponse)}
49+
readOnly={true}
50+
setOptions={{
51+
fontSize: '12px',
52+
autoScrollEditorIntoView: true,
53+
wrap: true,
54+
}}
55+
tabSize={2}
56+
/>
57+
)}
58+
</>
59+
);
60+
}

public/general_components/results/results.tsx

+16
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
*/
55

66
import React, { useState } from 'react';
7+
import { get } from 'lodash';
78
import {
89
EuiPanel,
910
EuiFlexGroup,
@@ -13,6 +14,7 @@ import {
1314
import { SearchResponse } from '../../../common';
1415
import { ResultsTable } from './results_table';
1516
import { ResultsJSON } from './results_json';
17+
import { MLResponse } from './ml_response';
1618

1719
interface ResultsProps {
1820
response: SearchResponse;
@@ -21,6 +23,7 @@ interface ResultsProps {
2123
enum VIEW {
2224
HITS_TABLE = 'hits_table',
2325
RAW_JSON = 'raw_json',
26+
ML_RESPONSE = 'ml_response',
2427
}
2528

2629
/**
@@ -55,6 +58,10 @@ export function Results(props: ResultsProps) {
5558
id: VIEW.RAW_JSON,
5659
label: 'Raw JSON',
5760
},
61+
{
62+
id: VIEW.ML_RESPONSE,
63+
label: 'ML response',
64+
},
5865
]}
5966
idSelected={selectedView}
6067
onChange={(id) => setSelectedView(id as VIEW)}
@@ -69,9 +76,18 @@ export function Results(props: ResultsProps) {
6976
{selectedView === VIEW.RAW_JSON && (
7077
<ResultsJSON response={props.response} />
7178
)}
79+
{selectedView === VIEW.ML_RESPONSE && (
80+
<MLResponse
81+
mlResponse={getMLResponseFromSearchResponse(props.response)}
82+
/>
83+
)}
7284
</>
7385
</EuiFlexItem>
7486
</EuiFlexGroup>
7587
</EuiPanel>
7688
);
7789
}
90+
91+
function getMLResponseFromSearchResponse(searchResponse: SearchResponse): {} {
92+
return get(searchResponse, 'ext.ml_inference', {});
93+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/*
2+
* Copyright OpenSearch Contributors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
import React from 'react';
7+
import {
8+
EuiText,
9+
EuiFlexGroup,
10+
EuiFlexItem,
11+
EuiLink,
12+
EuiCodeBlock,
13+
EuiPopoverTitle,
14+
} from '@elastic/eui';
15+
import { BULK_API_DOCS_LINK } from '../../../../../common';
16+
17+
interface BulkPopoverContentProps {
18+
indexName: string;
19+
}
20+
21+
/**
22+
* A basic component containing details about the bulk API and link to documentation.
23+
* Provides a partially-complete example, dynamically populated based on an index name.
24+
*/
25+
export function BulkPopoverContent(props: BulkPopoverContentProps) {
26+
return (
27+
<EuiFlexItem style={{ width: '40vw' }}>
28+
<EuiPopoverTitle>Ingest additional data</EuiPopoverTitle>
29+
<EuiFlexGroup direction="column">
30+
<EuiFlexItem grow={false}>
31+
<EuiText>
32+
You can ingest additional bulk data into the same index using the
33+
Bulk API.{' '}
34+
<EuiLink href={BULK_API_DOCS_LINK} target="_blank">
35+
Learn more
36+
</EuiLink>
37+
</EuiText>
38+
</EuiFlexItem>
39+
<EuiFlexItem grow={false}>
40+
<EuiCodeBlock fontSize="m" isCopyable={true}>
41+
{`POST ${props.indexName}/_bulk
42+
{ "index": { "_index": "${props.indexName}", "_id": //YOUR DOC ID// } }
43+
{ //INSERT YOUR DOCUMENTS// }`}
44+
</EuiCodeBlock>
45+
</EuiFlexItem>
46+
</EuiFlexGroup>
47+
</EuiFlexItem>
48+
);
49+
}

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

+48-5
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import {
1414
EuiCodeBlock,
1515
EuiSmallButtonEmpty,
1616
EuiEmptyPrompt,
17+
EuiPopover,
1718
} from '@elastic/eui';
1819
import {
1920
MapEntry,
@@ -25,6 +26,7 @@ import {
2526
toFormattedDate,
2627
} from '../../../../../common';
2728
import { SourceDataModal } from './source_data_modal';
29+
import { BulkPopoverContent } from './bulk_popover_content';
2830

2931
interface SourceDataProps {
3032
workflow: Workflow | undefined;
@@ -54,6 +56,9 @@ export function SourceData(props: SourceDataProps) {
5456
// edit modal state
5557
const [isEditModalOpen, setIsEditModalOpen] = useState<boolean>(false);
5658

59+
// bulk API popover state
60+
const [bulkPopoverOpen, setBulkPopoverOpen] = useState<boolean>(false);
61+
5762
// hook to listen when the docs form value changes.
5863
useEffect(() => {
5964
if (values?.ingest?.docs) {
@@ -133,11 +138,49 @@ export function SourceData(props: SourceDataProps) {
133138
</EuiFlexGroup>
134139
</EuiFlexItem>
135140
{props.lastIngested !== undefined && (
136-
<EuiFlexItem grow={false}>
137-
<EuiText size="s" color="subdued">
138-
{`Last ingested: ${toFormattedDate(props.lastIngested)}`}
139-
</EuiText>
140-
</EuiFlexItem>
141+
<>
142+
<EuiFlexItem grow={false}>
143+
<EuiText size="s">
144+
{`Last ingested: ${toFormattedDate(props.lastIngested)}`}
145+
</EuiText>
146+
</EuiFlexItem>
147+
<EuiFlexItem>
148+
<EuiFlexGroup
149+
direction="row"
150+
gutterSize="none"
151+
justifyContent="flexStart"
152+
style={{ marginTop: '-8px' }}
153+
>
154+
<EuiFlexItem grow={false}>
155+
<EuiText size="s">
156+
Ingest additional data with the bulk API.
157+
</EuiText>
158+
</EuiFlexItem>
159+
<EuiFlexItem grow={false}>
160+
<EuiPopover
161+
isOpen={bulkPopoverOpen}
162+
initialFocus={false}
163+
anchorPosition="downCenter"
164+
closePopover={() => setBulkPopoverOpen(false)}
165+
button={
166+
<EuiSmallButtonEmpty
167+
style={{ marginTop: '-4px' }}
168+
onClick={() => {
169+
setBulkPopoverOpen(!bulkPopoverOpen);
170+
}}
171+
>
172+
Learn more
173+
</EuiSmallButtonEmpty>
174+
}
175+
>
176+
<BulkPopoverContent
177+
indexName={getIn(values, 'ingest.index.name')}
178+
/>
179+
</EuiPopover>
180+
</EuiFlexItem>
181+
</EuiFlexGroup>
182+
</EuiFlexItem>
183+
</>
141184
)}
142185
{docsPopulated ? (
143186
<>

public/pages/workflows/new_workflow/quick_configure_modal.tsx

+3-2
Original file line numberDiff line numberDiff line change
@@ -741,7 +741,8 @@ function updateRAGSearchResponseProcessors(
741741
llmInterface: ModelInterface | undefined
742742
): WorkflowConfig {
743743
config.search.enrichResponse.processors.forEach((processor, idx) => {
744-
// prefill ML inference
744+
// prefill ML inference. By default, store the inference results
745+
// under the `ext.ml_inference` response body.
745746
if (processor.type === PROCESSOR_TYPE.ML) {
746747
config.search.enrichResponse.processors[idx].fields.forEach((field) => {
747748
if (field.id === 'model' && fields.llmId) {
@@ -784,7 +785,7 @@ function updateRAGSearchResponseProcessors(
784785
...outputMap[0],
785786
value: {
786787
transformType: TRANSFORM_TYPE.FIELD,
787-
value: fields.llmResponseField,
788+
value: `ext.ml_inference.${fields.llmResponseField}`,
788789
},
789790
};
790791
} else {

public/pages/workflows/new_workflow/utils.ts

-3
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
*/
55

66
import {
7-
CollapseProcessor,
87
MLIngestProcessor,
98
MLSearchRequestProcessor,
109
MLSearchResponseProcessor,
@@ -253,7 +252,6 @@ export function fetchRAGMetadata(version: string): UIState {
253252
baseState.config.search.request.value = customStringify(FETCH_ALL_QUERY);
254253
baseState.config.search.enrichResponse.processors = [
255254
new MLSearchResponseProcessor().toObj(),
256-
new CollapseProcessor().toObj(),
257255
];
258256
return baseState;
259257
}
@@ -278,7 +276,6 @@ export function fetchVectorSearchWithRAGMetadata(version: string): UIState {
278276
];
279277
baseState.config.search.enrichResponse.processors = [
280278
new MLSearchResponseProcessor().toObj(),
281-
new CollapseProcessor().toObj(),
282279
];
283280
return baseState;
284281
}

0 commit comments

Comments
 (0)