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

FTR for playground #186246

Merged
2 changes: 2 additions & 0 deletions x-pack/plugins/search_playground/public/components/chat.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ export const Chat = () => {
iconType="sparkles"
disabled={isToolBarActionsDisabled}
onClick={regenerateMessages}
data-test-subj="regenerateActionButton"
>
<FormattedMessage
id="xpack.searchPlayground.chat.regenerateBtn"
Expand All @@ -169,6 +170,7 @@ export const Chat = () => {
iconType="refresh"
disabled={isToolBarActionsDisabled}
onClick={handleClearChat}
data-test-subj="clearChatActionButton"
>
<FormattedMessage
id="xpack.searchPlayground.chat.clearChatBtn"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ export const ChatSidebar: React.FC<ChatSidebarProps> = ({ selectedIndicesCount }
defaultMessage: 'Model settings',
}),
children: <SummarizationPanel />,
dataTestId: 'summarizationAccordion',
},
{
id: useGeneratedHtmlId({ prefix: 'sourcesAccordion' }),
Expand All @@ -50,13 +51,14 @@ export const ChatSidebar: React.FC<ChatSidebarProps> = ({ selectedIndicesCount }
</EuiText>
),
children: <SourcesPanelSidebar />,
dataTestId: 'sourcesAccordion',
},
];
const [openAccordionId, setOpenAccordionId] = useState(accordions[0].id);

return (
<EuiFlexGroup direction="column" className="eui-yScroll" gutterSize="none">
{accordions.map(({ id, title, extraAction, children }, index) => (
{accordions.map(({ id, title, extraAction, children, dataTestId }, index) => (
<EuiFlexItem
key={id}
css={{
Expand All @@ -77,6 +79,7 @@ export const ChatSidebar: React.FC<ChatSidebarProps> = ({ selectedIndicesCount }
buttonProps={{ paddingSize: 'l' }}
forceState={openAccordionId === id ? 'open' : 'closed'}
onToggle={() => setOpenAccordionId(openAccordionId === id ? '' : id)}
data-test-subj={dataTestId}
>
{children}
<EuiSpacer size="l" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ export const IndicesList: React.FC<IndicesListProps> = ({ indices, onRemoveClick
color="primary"
label={index}
size="s"
data-test-subj="indicesInAccordian"
extraAction={{
alwaysShow: true,
'aria-label': i18n.translate('xpack.searchPlayground.sources.indices.removeIndex', {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,16 @@
* 2.0.
*/

import type OpenAI from 'openai';
import { FtrProviderContext } from '../../ftr_provider_context';
import { createOpenAIConnector } from './utils/create_openai_connector';
import { MachineLearningCommonAPIProvider } from '../../services/ml/common_api';

import {
createLlmProxy,
LlmProxy,
} from '../../../observability_ai_assistant_api_integration/common/create_llm_proxy';

const indexName = 'basic_index';
const esArchiveIndex = 'test/api_integration/fixtures/es_archiver/index_patterns/basic_index';

Expand All @@ -18,76 +24,166 @@ export default function (ftrContext: FtrProviderContext) {
const commonAPI = MachineLearningCommonAPIProvider(ftrContext);
const supertest = getService('supertest');
const esArchiver = getService('esArchiver');

const log = getService('log');
const browser = getService('browser');

const createIndex = async () => await esArchiver.load(esArchiveIndex);

let proxy: LlmProxy;
let removeOpenAIConnector: () => Promise<void>;
const createConnector = async () => {
removeOpenAIConnector = await createOpenAIConnector({
supertest,
requestHeader: commonAPI.getCommonRequestHeader(),
proxy,
});
};

describe('Playground Overview', () => {
describe('Playground', () => {
before(async () => {
proxy = await createLlmProxy(log);
await pageObjects.common.navigateToApp('enterpriseSearchApplications/playground');
});

after(async () => {
await esArchiver.unload(esArchiveIndex);
await removeOpenAIConnector?.();
proxy.close();
});

describe('start chat page', () => {
it('playground app is loaded', async () => {
await pageObjects.searchPlayground.PlaygroundStartChatPage.expectPlaygroundStartChatPageComponentsToExist();
describe('setup Page', () => {
it('is loaded successfully', async () => {
await pageObjects.searchPlayground.PlaygroundStartChatPage.expectPlaygroundHeaderComponentsToExist();
await pageObjects.searchPlayground.PlaygroundStartChatPage.expectPlaygroundHeaderComponentsToDisabled();
await pageObjects.searchPlayground.PlaygroundStartChatPage.expectPlaygroundStartChatPageComponentsToExist();
});

it('show no index callout', async () => {
await pageObjects.searchPlayground.PlaygroundStartChatPage.expectNoIndexCalloutExists();
await pageObjects.searchPlayground.PlaygroundStartChatPage.expectCreateIndexButtonToExists();
});

it('hide no index callout when index added', async () => {
await createIndex();
await pageObjects.searchPlayground.PlaygroundStartChatPage.expectSelectIndex(indexName);
});

it('show add connector button', async () => {
await pageObjects.searchPlayground.PlaygroundStartChatPage.expectAddConnectorButtonExists();
describe('with gen ai connectors', () => {
before(async () => {
await createConnector();
await browser.refresh();
});

after(async () => {
await removeOpenAIConnector?.();
await browser.refresh();
});
it('hide gen ai panel', async () => {
await pageObjects.searchPlayground.PlaygroundStartChatPage.expectHideGenAIPanelConnector();
});
});

it('click add connector button opens connector flyout', async () => {
await pageObjects.searchPlayground.PlaygroundStartChatPage.expectOpenConnectorPagePlayground();
describe('without gen ai connectors', () => {
it('should display the set up connectors button', async () => {
await pageObjects.searchPlayground.PlaygroundStartChatPage.expectAddConnectorButtonExists();
});

it('creates a connector successfully', async () => {
await pageObjects.searchPlayground.PlaygroundStartChatPage.expectOpenConnectorPagePlayground();
await pageObjects.searchPlayground.PlaygroundStartChatPage.expectHideGenAIPanelConnectorAfterCreatingConnector(
createConnector
);
});

after(async () => {
await removeOpenAIConnector?.();
await browser.refresh();
});
});

it('hide gen ai panel when connector exists', async () => {
await pageObjects.searchPlayground.PlaygroundStartChatPage.expectHideGenAIPanelConnector(
createConnector
);
describe('without any indices', () => {
it('show no index callout', async () => {
await pageObjects.searchPlayground.PlaygroundStartChatPage.expectNoIndexCalloutExists();
await pageObjects.searchPlayground.PlaygroundStartChatPage.expectCreateIndexButtonToExists();
});

it('hide no index callout when index added', async () => {
await createIndex();
await pageObjects.searchPlayground.PlaygroundStartChatPage.expectSelectIndex(indexName);
});

after(async () => {
await pageObjects.searchPlayground.PlaygroundStartChatPage.removeIndexFromComboBox();
await esArchiver.unload(esArchiveIndex);
await browser.refresh();
});
});

it('show chat page', async () => {
await pageObjects.searchPlayground.PlaygroundStartChatPage.expectSelectIndex(indexName);
await pageObjects.searchPlayground.PlaygroundStartChatPage.expectToStartChatPage();
describe('with existing indices', () => {
before(async () => {
await createConnector();
await createIndex();
await browser.refresh();
});

it('dropdown shows up', async () => {
await pageObjects.searchPlayground.PlaygroundStartChatPage.expectIndicesInDropdown();
});

it('can select index from dropdown and navigate to chat window', async () => {
await pageObjects.searchPlayground.PlaygroundStartChatPage.expectToSelectIndicesAndStartButtonEnabled(
indexName
);
});

after(async () => {
await removeOpenAIConnector?.();
await esArchiver.unload(esArchiveIndex);
await browser.refresh();
});
});
});

describe('chat page', () => {
it('chat works', async () => {
await pageObjects.searchPlayground.PlaygroundChatPage.expectChatWorks();
before(async () => {
await createConnector();
await createIndex();
await browser.refresh();
await pageObjects.searchPlayground.PlaygroundChatPage.navigateToChatPage(indexName);
});

it('open view code', async () => {
await pageObjects.searchPlayground.PlaygroundChatPage.expectOpenViewCode();
it('loads successfully', async () => {
await pageObjects.searchPlayground.PlaygroundChatPage.expectChatWindowLoaded();
});

it('show fields and code in view query', async () => {
await pageObjects.searchPlayground.PlaygroundChatPage.expectViewQueryHasFields();
describe('chat', () => {
it('works', async () => {
const conversationInterceptor = proxy.intercept(
'conversation',
(body) =>
(JSON.parse(body) as OpenAI.Chat.ChatCompletionCreateParamsNonStreaming).tools?.find(
(fn) => fn.function.name === 'title_conversation'
) === undefined
);

await pageObjects.searchPlayground.PlaygroundChatPage.sendQuestion();

const conversationSimulator = await conversationInterceptor.waitForIntercept();

await conversationSimulator.next('My response');

await conversationSimulator.complete();

await pageObjects.searchPlayground.PlaygroundChatPage.expectChatWorks();
await pageObjects.searchPlayground.PlaygroundChatPage.expectTokenTooltipExists();
});

it('open view code', async () => {
await pageObjects.searchPlayground.PlaygroundChatPage.expectOpenViewCode();
});

it('show fields and code in view query', async () => {
await pageObjects.searchPlayground.PlaygroundChatPage.expectViewQueryHasFields();
});

it('show edit context', async () => {
await pageObjects.searchPlayground.PlaygroundChatPage.expectEditContextOpens();
});
});

it('show edit context', async () => {
await pageObjects.searchPlayground.PlaygroundChatPage.expectEditContextOpens();
after(async () => {
await removeOpenAIConnector?.();
await esArchiver.unload(esArchiveIndex);
await browser.refresh();
});
});
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,23 @@
*/

import type SuperTest from 'supertest';
import { LlmProxy } from '../../../../observability_ai_assistant_api_integration/common/create_llm_proxy';

export async function createOpenAIConnector({
supertest,
requestHeader = {},
apiKeyHeader = {},
proxy,
}: {
supertest: SuperTest.Agent;
requestHeader?: Record<string, string>;
apiKeyHeader?: Record<string, string>;
proxy: LlmProxy;
}): Promise<() => Promise<void>> {
const config = {
apiProvider: 'OpenAI',
defaultModel: 'gpt-4',
apiUrl: 'http://localhost:3002',
apiUrl: `http://localhost:${proxy.getPort()}`,
};

const connector: { id: string } | undefined = (
Expand All @@ -28,7 +31,7 @@ export async function createOpenAIConnector({
.set(requestHeader)
.set(apiKeyHeader)
.send({
name: 'test Open AI',
name: 'myConnector',
connector_type_id: '.gen-ai',
config,
secrets: {
Expand Down
Loading