Skip to content

Commit 11638ac

Browse files
authored
[Agentic Search] Improve export / next steps UX (#805)
Signed-off-by: Tyler Ohlsen <[email protected]>
1 parent 998e0fc commit 11638ac

File tree

10 files changed

+531
-114
lines changed

10 files changed

+531
-114
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.1.0/)
1010
- Add agent summary ([#801](https://github.com/opensearch-project/dashboards-flow-framework/pull/801))
1111
- Clean up agent summary formatting ([#803](https://github.com/opensearch-project/dashboards-flow-framework/pull/803))
1212
- [Agentic Search] Add MCP server support ([#802](https://github.com/opensearch-project/dashboards-flow-framework/pull/802))
13+
- [Agentic Search] Improve export / next steps UX ([#805](https://github.com/opensearch-project/dashboards-flow-framework/pull/805))
1314
### Bug Fixes
1415
### Infrastructure
1516
### Documentation

common/constants.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,12 @@ export const AGENT_MAIN_DOCS_LINK =
329329
'https://docs.opensearch.org/latest/ml-commons-plugin/agents-tools/agents/index/';
330330
export const AGENTIC_SEARCH_DOCS_LINK =
331331
'https://docs.opensearch.org/latest/vector-search/ai-search/agentic-search/';
332+
export const AGENTIC_SEARCH_MODELS_DOCS_LINK =
333+
'https://docs.opensearch.org/latest/vector-search/ai-search/agentic-search/agent-customization/#model-configuration';
334+
export const AGENTIC_SEARCH_AGENTS_DOCS_LINK =
335+
'https://docs.opensearch.org/latest/vector-search/ai-search/agentic-search/agent-customization/';
336+
export const AGENTIC_SEARCH_MCP_DOCS_LINK =
337+
'https://docs.opensearch.org/latest/vector-search/ai-search/agentic-search/mcp-server/';
332338
export const MCP_CONNECTOR_DOCS_LINK =
333339
'https://docs.opensearch.org/latest/ml-commons-plugin/agents-tools/mcp/mcp-connector';
334340
export const MCP_AGENT_CONFIG_DOCS_LINK =
@@ -1033,6 +1039,10 @@ export enum COMPONENT_ID {
10331039
// not override the default styles from the EuiCard component.
10341040
export const LEFT_NAV_SELECTED_STYLE = '2px solid rgba(128, 128, 128, 0.8)';
10351041

1042+
/**
1043+
* Agents / tools constants
1044+
*/
1045+
10361046
// Derived from https://docs.opensearch.org/latest/ml-commons-plugin/agents-tools/agents/index/
10371047
export enum AGENT_TYPE {
10381048
FLOW = 'flow',
@@ -1104,3 +1114,31 @@ export const DEFAULT_MCP_SERVER = {
11041114
mcp_connector_id: '',
11051115
tool_filters: [],
11061116
} as MCPConnector;
1117+
1118+
export const EXAMPLE_PUT_AGENTIC_SEARCH_PIPELINE = `PUT _search/pipeline/agentic-pipeline
1119+
{
1120+
"request_processors": [
1121+
{
1122+
"agentic_query_translator": {
1123+
"agent_id": "${AGENT_ID_PATTERN}"
1124+
}
1125+
}
1126+
],
1127+
"response_processors": [
1128+
{
1129+
"agentic_context": {
1130+
"agent_steps_summary": true,
1131+
"dsl_query": true
1132+
}
1133+
}
1134+
]
1135+
}`;
1136+
1137+
export const EXAMPLE_AGENTIC_SEARCH_QUERY = `GET <your-index>/_search?search_pipeline=agentic-pipeline
1138+
{
1139+
"query": {
1140+
"agentic": {
1141+
"query_text": "<your-search-query>",
1142+
}
1143+
}
1144+
}`;

public/pages/workflow_detail/agentic_search/configure_flow/agent_configuration.tsx

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,14 +30,14 @@ import {
3030
import {
3131
Agent,
3232
AGENT_ID_PATH,
33-
AGENT_MAIN_DOCS_LINK,
3433
AGENT_TYPE,
34+
AGENTIC_SEARCH_AGENTS_DOCS_LINK,
35+
AGENTIC_SEARCH_MCP_DOCS_LINK,
3536
customStringify,
3637
DEFAULT_AGENT,
3738
EMPTY_AGENT,
3839
MAX_DESCRIPTION_LENGTH,
3940
MAX_STRING_LENGTH,
40-
MCP_CONNECTOR_DOCS_LINK,
4141
NEW_AGENT_ID_PLACEHOLDER,
4242
NEW_AGENT_PLACEHOLDER,
4343
WorkflowConfig,
@@ -233,7 +233,10 @@ export function AgentConfiguration(props: AgentConfigurationProps) {
233233
<EuiFlexGroup direction="row" gutterSize="s" alignItems="center">
234234
<EuiFlexItem grow={false}>
235235
<EuiText size="s" color="subdued">
236-
<EuiLink target="_blank" href={AGENT_MAIN_DOCS_LINK}>
236+
<EuiLink
237+
target="_blank"
238+
href={AGENTIC_SEARCH_AGENTS_DOCS_LINK}
239+
>
237240
Documentation
238241
</EuiLink>
239242
</EuiText>
@@ -434,7 +437,7 @@ export function AgentConfiguration(props: AgentConfigurationProps) {
434437
labelAppend={
435438
<EuiText size="xs">
436439
<EuiLink
437-
href={MCP_CONNECTOR_DOCS_LINK}
440+
href={AGENTIC_SEARCH_MCP_DOCS_LINK}
438441
target="_blank"
439442
>
440443
Learn more

public/pages/workflow_detail/agentic_search/configure_flow/agent_llm_fields.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import { EuiFormRow, EuiLink, EuiSelect, EuiText } from '@elastic/eui';
1111
import {
1212
Agent,
1313
AGENT_TYPE,
14-
AGENTIC_SEARCH_DOCS_LINK,
14+
AGENTIC_SEARCH_MODELS_DOCS_LINK,
1515
AgentLLM,
1616
Model,
1717
MODEL_STATE,
@@ -76,7 +76,7 @@ export function AgentLLMFields({
7676
fullWidth
7777
labelAppend={
7878
<EuiText size="xs">
79-
<EuiLink href={AGENTIC_SEARCH_DOCS_LINK} target="_blank">
79+
<EuiLink href={AGENTIC_SEARCH_MODELS_DOCS_LINK} target="_blank">
8080
Learn more
8181
</EuiLink>
8282
</EuiText>
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/*
2+
* Copyright OpenSearch Contributors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
import React from 'react';
7+
import { render, screen } from '@testing-library/react';
8+
import '@testing-library/jest-dom';
9+
import { AgenticSearchApplicationContent } from './agentic_search_application_content';
10+
import { Workflow } from '../../../../common';
11+
12+
const TEST_AGENT_ID = 'test-agent-id';
13+
const TEST_WORKFLOW = {
14+
ui_metadata: {
15+
config: {
16+
search: {
17+
requestAgentId: {
18+
value: TEST_AGENT_ID,
19+
},
20+
},
21+
},
22+
},
23+
} as Workflow;
24+
const TEST_WORKFLOW_NO_AGENT = {
25+
ui_metadata: {
26+
config: {
27+
search: {
28+
requestAgentId: {
29+
value: '',
30+
},
31+
},
32+
},
33+
},
34+
} as Workflow;
35+
36+
describe('AgenticSearchApplicationContent', () => {
37+
test('renders the component with agent', () => {
38+
render(<AgenticSearchApplicationContent workflow={TEST_WORKFLOW} />);
39+
40+
expect(screen.queryByTestId('noAgentFoundCallout')).not.toBeInTheDocument();
41+
expect(screen.getByTestId('searchPipelineCodeBlock')).toBeInTheDocument();
42+
expect(screen.getByTestId('agenticSearchCodeBlock')).toBeInTheDocument();
43+
});
44+
test('renders the component with no agent', () => {
45+
render(
46+
<AgenticSearchApplicationContent workflow={TEST_WORKFLOW_NO_AGENT} />
47+
);
48+
49+
expect(screen.getByTestId('noAgentFoundCallout')).toBeInTheDocument();
50+
expect(
51+
screen.queryByTestId('searchPipelineCodeBlock')
52+
).not.toBeInTheDocument();
53+
expect(
54+
screen.queryByTestId('agenticSearchCodeBlock')
55+
).not.toBeInTheDocument();
56+
});
57+
});
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
/*
2+
* Copyright OpenSearch Contributors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
import React from 'react';
7+
import {
8+
EuiAccordion,
9+
EuiCallOut,
10+
EuiCodeBlock,
11+
EuiFlexGroup,
12+
EuiFlexItem,
13+
EuiLink,
14+
EuiText,
15+
} from '@elastic/eui';
16+
import {
17+
AGENT_ID_PATTERN,
18+
AGENTIC_SEARCH_DOCS_LINK,
19+
EXAMPLE_AGENTIC_SEARCH_QUERY,
20+
EXAMPLE_PUT_AGENTIC_SEARCH_PIPELINE,
21+
Workflow,
22+
} from '../../../../common';
23+
24+
interface AgenticSearchApplicationContentProps {
25+
workflow?: Workflow;
26+
}
27+
28+
const NO_AGENT_FOUND_TEXT =
29+
'No agent found. Make sure to select an agent before trying to use agentic search in your application.';
30+
31+
/**
32+
* Static content for next steps & how to use agentic search in your application
33+
*/
34+
export function AgenticSearchApplicationContent(
35+
props: AgenticSearchApplicationContentProps
36+
) {
37+
const selectedAgentId = (props.workflow?.ui_metadata?.config?.search
38+
?.requestAgentId?.value ?? '') as string;
39+
40+
return (
41+
<EuiFlexGroup direction="column" gutterSize="s">
42+
{!selectedAgentId ? (
43+
<EuiFlexItem>
44+
<EuiCallOut
45+
size="s"
46+
color="warning"
47+
data-testid="noAgentFoundCallout"
48+
>
49+
{NO_AGENT_FOUND_TEXT}
50+
</EuiCallOut>
51+
</EuiFlexItem>
52+
) : (
53+
<>
54+
<EuiFlexItem grow={false}>
55+
<EuiText size="s">
56+
To use agentic search in your application, create a search
57+
pipeline and run <code>agentic</code>
58+
{' '}
59+
search queries. Use the following examples as a starting point.
60+
</EuiText>
61+
</EuiFlexItem>
62+
<EuiFlexItem grow={false}>
63+
<EuiAccordion
64+
id="agenticSearchPipeline"
65+
paddingSize="s"
66+
buttonContent={
67+
<EuiText size="s">1. Create a search pipeline</EuiText>
68+
}
69+
initialIsOpen={true}
70+
>
71+
<EuiCodeBlock
72+
data-testid="searchPipelineCodeBlock"
73+
language="json"
74+
fontSize="s"
75+
paddingSize="s"
76+
overflowHeight={400}
77+
whiteSpace="pre"
78+
isCopyable={true}
79+
>
80+
{injectAgentId(
81+
EXAMPLE_PUT_AGENTIC_SEARCH_PIPELINE,
82+
selectedAgentId
83+
)}
84+
</EuiCodeBlock>
85+
</EuiAccordion>
86+
</EuiFlexItem>
87+
<EuiFlexItem grow={false}>
88+
<EuiAccordion
89+
id="agenticSearchQuery"
90+
paddingSize="s"
91+
buttonContent={<EuiText size="s">2. Run a search query</EuiText>}
92+
initialIsOpen={true}
93+
>
94+
<EuiCodeBlock
95+
data-testid="agenticSearchCodeBlock"
96+
language="json"
97+
fontSize="s"
98+
paddingSize="s"
99+
overflowHeight={300}
100+
whiteSpace="pre"
101+
isCopyable={true}
102+
>
103+
{EXAMPLE_AGENTIC_SEARCH_QUERY}
104+
</EuiCodeBlock>
105+
</EuiAccordion>
106+
</EuiFlexItem>
107+
<EuiFlexItem grow={false}>
108+
<EuiText size="s">
109+
For more details and examples, check out the{' '}
110+
<EuiLink target="_blank" href={AGENTIC_SEARCH_DOCS_LINK}>
111+
full documentation
112+
</EuiLink>
113+
</EuiText>
114+
</EuiFlexItem>
115+
</>
116+
)}
117+
</EuiFlexGroup>
118+
);
119+
}
120+
121+
function injectAgentId(template: string, agentId: string) {
122+
return template.replaceAll(AGENT_ID_PATTERN, agentId);
123+
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
/*
2+
* Copyright OpenSearch Contributors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
import React from 'react';
7+
import { render, screen } from '@testing-library/react';
8+
import '@testing-library/jest-dom';
9+
import { Workflow, WORKFLOW_TYPE } from '../../../../common';
10+
import { ExportModal } from './export_modal';
11+
12+
const TEST_WORKFLOW_NAME = 'test-workflow-name';
13+
const TEST_WORKFLOW_CUSTOM = {
14+
name: TEST_WORKFLOW_NAME,
15+
ui_metadata: {
16+
type: WORKFLOW_TYPE.CUSTOM,
17+
config: {},
18+
},
19+
} as Workflow;
20+
const TEST_WORKFLOW_AGENTIC = {
21+
name: TEST_WORKFLOW_NAME,
22+
ui_metadata: {
23+
type: WORKFLOW_TYPE.AGENTIC_SEARCH,
24+
config: {
25+
search: {
26+
requestAgentId: {
27+
value: 'test-agent-id',
28+
},
29+
},
30+
},
31+
},
32+
} as Workflow;
33+
34+
jest.mock('../../../services', () => {
35+
const { mockCoreServices } = require('../../../../test');
36+
return {
37+
...jest.requireActual('../../../services'),
38+
...mockCoreServices,
39+
};
40+
});
41+
42+
describe('ExportTemplateContent', () => {
43+
global.URL.createObjectURL = jest.fn();
44+
45+
test('renders the component for non-agentic-search usecases', () => {
46+
render(
47+
<ExportModal
48+
workflow={TEST_WORKFLOW_CUSTOM}
49+
setIsExportModalOpen={jest.fn()}
50+
/>
51+
);
52+
53+
expect(
54+
screen.getByTestId('exportDataToggleButtonGroup')
55+
).toBeInTheDocument();
56+
expect(screen.queryByTestId('agenticSearchTabs')).not.toBeInTheDocument();
57+
});
58+
59+
test('renders the component for agentic-search usecase', () => {
60+
render(
61+
<ExportModal
62+
workflow={TEST_WORKFLOW_AGENTIC}
63+
setIsExportModalOpen={jest.fn()}
64+
/>
65+
);
66+
expect(
67+
screen.queryByTestId('exportDataToggleButtonGroup')
68+
).not.toBeInTheDocument();
69+
expect(screen.getByTestId('agenticSearchTabs')).toBeInTheDocument();
70+
expect(screen.getByTestId('searchPipelineCodeBlock')).toBeInTheDocument();
71+
expect(screen.getByTestId('agenticSearchCodeBlock')).toBeInTheDocument();
72+
});
73+
});

0 commit comments

Comments
 (0)