Query builder base page (#190091)

## Summary

Adds the base Search Playground behind a feature flag. This will be used
to create new Search Playground page mode that is used for queries
rather than LLMs
<img width="1533" alt="Screenshot 2024-08-13 at 16 00 32"
src="https://github.com/user-attachments/assets/d1c78ef2-88c0-4c67-bbdd-2704142b95df">
<img width="1523" alt="Screenshot 2024-08-13 at 16 00 41"
src="https://github.com/user-attachments/assets/01b3b3cb-95e4-4a09-a07d-069d810cf42c">



### Checklist

Delete any items that are not applicable to this PR.

- [x] Any text added follows [EUI's writing
guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses
sentence case text and includes [i18n
support](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md)
- [ ]
[Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html)
was added for features that require explanation or tutorials
- [ ] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios
- [ ] [Flaky Test
Runner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was
used on any tests changed
- [x] Any UI touched in this PR is usable by keyboard only (learn more
about [keyboard accessibility](https://webaim.org/techniques/keyboard/))
- [ ] Any UI touched in this PR does not create any new axe failures
(run axe in browser:
[FF](https://addons.mozilla.org/en-US/firefox/addon/axe-devtools/),
[Chrome](https://chrome.google.com/webstore/detail/axe-web-accessibility-tes/lhdoppojpmngadmnindnejefpokejbdd?hl=en-US))
- [ ] This was checked for [cross-browser
compatibility](https://www.elastic.co/support/matrix#matrix_browsers)
This commit is contained in:
Efe Gürkan YALAMAN 2024-08-21 18:33:50 +02:00 committed by GitHub
parent 8f39bdfe25
commit 1949de076a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
19 changed files with 414 additions and 38 deletions

View file

@ -152,7 +152,12 @@ export const renderApp = (
ReactDOM.render(
<I18nProvider>
<KibanaThemeProvider theme={{ theme$: params.theme$ }}>
<KibanaContextProvider services={{ ...core, ...plugins }}>
<KibanaContextProvider
services={{
...core,
...plugins,
}}
>
<CloudContext>
<Provider store={store}>
<Router history={params.history}>

View file

@ -68,8 +68,8 @@ export interface KibanaLogicProps {
productFeatures: ProductFeatures;
renderHeaderActions(HeaderActions?: FC): void;
searchHomepage?: SearchHomepagePluginStart;
searchPlayground?: SearchPlaygroundPluginStart;
searchInferenceEndpoints?: SearchInferenceEndpointsPluginStart;
searchPlayground?: SearchPlaygroundPluginStart;
security?: SecurityPluginStart;
setBreadcrumbs(crumbs: ChromeBreadcrumb[]): void;
setChromeIsVisible(isVisible: boolean): void;
@ -103,8 +103,8 @@ export interface KibanaValues {
productFeatures: ProductFeatures;
renderHeaderActions(HeaderActions?: FC): void;
searchHomepage: SearchHomepagePluginStart | null;
searchPlayground: SearchPlaygroundPluginStart | null;
searchInferenceEndpoints: SearchInferenceEndpointsPluginStart | null;
searchPlayground: SearchPlaygroundPluginStart | null;
security: SecurityPluginStart | null;
setBreadcrumbs(crumbs: ChromeBreadcrumb[]): void;
setChromeIsVisible(isVisible: boolean): void;
@ -150,8 +150,8 @@ export const KibanaLogic = kea<MakeLogicType<KibanaValues>>({
productFeatures: [props.productFeatures, {}],
renderHeaderActions: [props.renderHeaderActions, {}],
searchHomepage: [props.searchHomepage || null, {}],
searchPlayground: [props.searchPlayground || null, {}],
searchInferenceEndpoints: [props.searchInferenceEndpoints || null, {}],
searchPlayground: [props.searchPlayground || null, {}],
security: [props.security || null, {}],
setBreadcrumbs: [props.setBreadcrumbs, {}],
setChromeIsVisible: [props.setChromeIsVisible, {}],

View file

@ -7,3 +7,6 @@
export const PLUGIN_ID = 'searchPlayground';
export const PLUGIN_NAME = 'Playground';
export const PLUGIN_PATH = '/app/search_playground';
export const SEARCH_MODE_FEATURE_FLAG_ID = 'searchPlayground:searchModeEnabled';

View file

@ -22,12 +22,15 @@ export enum AnalyticsEvents {
includeCitations = 'include_citations',
instructionsFieldChanged = 'instructions_field_changed',
queryFieldsUpdated = 'view_query_fields_updated',
queryBuilderFieldsUpdated = 'view_search_fields_updated',
queryModeLoaded = 'query_mode_loaded',
queryBuilderModeLoaded = 'search_builder_mode_loaded',
modelSelected = 'model_selected',
retrievalDocsFlyoutOpened = 'retrieval_docs_flyout_opened',
sourceFieldsLoaded = 'source_fields_loaded',
sourceIndexUpdated = 'source_index_updated',
setupChatPageLoaded = 'start_new_chat_page_loaded',
setupSearchPageLoaded = 'search_setup_page_loaded',
viewCodeFlyoutOpened = 'view_code_flyout_opened',
viewCodeLanguageChange = 'view_code_language_change',
}

View file

@ -5,19 +5,23 @@
* 2.0.
*/
import React, { useEffect, useState } from 'react';
import React, { useState } from 'react';
import { KibanaPageTemplate } from '@kbn/shared-ux-page-kibana-template';
import { useWatch } from 'react-hook-form';
import { QueryMode } from './query_mode/query_mode';
import { SetupPage } from './setup_page/setup_page';
import { ChatSetupPage } from './setup_page/chat_setup_page';
import { Header } from './header';
import { useLoadConnectors } from '../hooks/use_load_connectors';
import { ChatForm, ChatFormFields } from '../types';
import { ChatForm, ChatFormFields, PlaygroundPageMode } from '../types';
import { Chat } from './chat';
import { SearchMode } from './search_mode/search_mode';
import { SearchPlaygroundSetupPage } from './setup_page/search_playground_setup_page';
import { usePageMode } from '../hooks/use_page_mode';
export interface AppProps {
showDocs?: boolean;
pageMode?: PlaygroundPageMode;
}
export enum ViewMode {
@ -25,22 +29,65 @@ export enum ViewMode {
query = 'query',
}
export const App: React.FC<AppProps> = ({ showDocs = false }) => {
const [showSetupPage, setShowSetupPage] = useState(true);
export const App: React.FC<AppProps> = ({
showDocs = false,
pageMode = PlaygroundPageMode.chat,
}) => {
const [selectedMode, setSelectedMode] = useState<ViewMode>(ViewMode.chat);
const { data: connectors } = useLoadConnectors();
const hasSelectedIndices = useWatch<ChatForm, ChatFormFields.indices>({
name: ChatFormFields.indices,
}).length;
const handleModeChange = (id: string) => setSelectedMode(id as ViewMode);
const hasSelectedIndices = Boolean(
useWatch<ChatForm, ChatFormFields.indices>({
name: ChatFormFields.indices,
}).length
);
const handleModeChange = (id: ViewMode) => setSelectedMode(id);
const handlePageModeChange = (mode: PlaygroundPageMode) => setSelectedPageMode(mode);
const {
showSetupPage,
pageMode: selectedPageMode,
setPageMode: setSelectedPageMode,
} = usePageMode({
hasSelectedIndices,
hasConnectors: Boolean(connectors?.length),
initialPageMode: pageMode,
});
useEffect(() => {
if (showSetupPage && connectors?.length && hasSelectedIndices) {
setShowSetupPage(false);
} else if (!showSetupPage && (!connectors?.length || !hasSelectedIndices)) {
setShowSetupPage(true);
}
}, [connectors, hasSelectedIndices, showSetupPage]);
const restrictedWidth = selectedPageMode === PlaygroundPageMode.search && selectedMode === 'chat';
const paddingSize =
selectedPageMode === PlaygroundPageMode.search && selectedMode === 'chat' ? 'xl' : 'none';
const getSetupPage = () => {
return (
showSetupPage && (
<>
{selectedPageMode === PlaygroundPageMode.chat && <ChatSetupPage />}
{selectedPageMode === PlaygroundPageMode.search && <SearchPlaygroundSetupPage />}
</>
)
);
};
const getQueryBuilderPage = () => {
return (
!showSetupPage &&
selectedPageMode === PlaygroundPageMode.search && (
<>
{selectedMode === ViewMode.chat && <SearchMode />}
{selectedMode === ViewMode.query && <QueryMode />}
</>
)
);
};
const getChatPage = () => {
return (
!showSetupPage &&
selectedPageMode === PlaygroundPageMode.chat && (
<>
{selectedMode === ViewMode.chat && <Chat />}
{selectedMode === ViewMode.query && <QueryMode />}
</>
)
);
};
return (
<>
@ -49,19 +96,23 @@ export const App: React.FC<AppProps> = ({ showDocs = false }) => {
onModeChange={handleModeChange}
selectedMode={selectedMode}
isActionsDisabled={showSetupPage}
selectedPageMode={selectedPageMode}
onSelectPageModeChange={handlePageModeChange}
/>
<KibanaPageTemplate.Section
alignment="top"
restrictWidth={false}
restrictWidth={restrictedWidth}
grow
css={{
position: 'relative',
}}
contentProps={{ css: { display: 'flex', flexGrow: 1, position: 'absolute', inset: 0 } }}
paddingSize="none"
paddingSize={paddingSize}
className="eui-fullHeight"
>
{showSetupPage ? <SetupPage /> : selectedMode === ViewMode.chat ? <Chat /> : <QueryMode />}
{getSetupPage()}
{getChatPage()}
{getQueryBuilderPage()}
</KibanaPageTemplate.Section>
</>
);

View file

@ -11,6 +11,7 @@ import {
EuiFlexGroup,
EuiPageHeaderSection,
EuiPageTemplate,
EuiSelect,
EuiTitle,
useEuiTheme,
} from '@elastic/eui';
@ -20,11 +21,15 @@ import React from 'react';
import { PlaygroundHeaderDocs } from './playground_header_docs';
import { Toolbar } from './toolbar';
import { ViewMode } from './app';
import { PlaygroundPageMode } from '../types';
import { useSearchPlaygroundFeatureFlag } from '../hooks/use_search_playground_feature_flag';
interface HeaderProps {
showDocs?: boolean;
selectedMode: string;
onModeChange: (mode: string) => void;
onModeChange: (mode: ViewMode) => void;
selectedPageMode: PlaygroundPageMode;
onSelectPageModeChange: (mode: PlaygroundPageMode) => void;
isActionsDisabled?: boolean;
}
@ -33,7 +38,10 @@ export const Header: React.FC<HeaderProps> = ({
onModeChange,
showDocs = false,
isActionsDisabled = false,
selectedPageMode,
onSelectPageModeChange,
}) => {
const isSearchModeEnabled = useSearchPlaygroundFeatureFlag();
const { euiTheme } = useEuiTheme();
const options = [
{
@ -72,6 +80,18 @@ export const Header: React.FC<HeaderProps> = ({
<FormattedMessage id="xpack.searchPlayground.pageTitle" defaultMessage="Playground" />
</h2>
</EuiTitle>
{isSearchModeEnabled && (
<EuiSelect
data-test-subj="page-mode-select"
options={[
{ value: 'chat', text: 'Chat' },
{ value: 'search', text: 'Search' },
]}
value={selectedPageMode}
onChange={(e) => onSelectPageModeChange(e.target.value as PlaygroundPageMode)}
/>
)}
<EuiBetaBadge
label={i18n.translate('xpack.searchPlayground.pageTitle.techPreview', {
defaultMessage: 'TECH PREVIEW',
@ -86,7 +106,7 @@ export const Header: React.FC<HeaderProps> = ({
legend="viewMode"
options={options}
idSelected={selectedMode}
onChange={onModeChange}
onChange={(id: string) => onModeChange(id as ViewMode)}
buttonSize="compressed"
isDisabled={isActionsDisabled}
data-test-subj="viewModeSelector"

View file

@ -0,0 +1,59 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import {
EuiFlexGroup,
EuiFlexItem,
EuiHorizontalRule,
EuiPanel,
EuiText,
EuiTitle,
} from '@elastic/eui';
import React from 'react';
const DEMO_DATA = [
{ id: '123321', name: 'John Doe', age: 25 },
{ id: '123321', name: 'John Doe', age: 25 },
{ id: '123321', name: 'John Doe', age: 25 },
{ id: '123321', name: 'John Doe', age: 25 },
{ id: '123321', name: 'John Doe', age: 25 },
{ id: '123321', name: 'John Doe', age: 25 },
{ id: '123321', name: 'John Doe', age: 25 },
{ id: '123321', name: 'John Doe', age: 25 },
{ id: '123321', name: 'John Doe', age: 25 },
{ id: '123321', name: 'John Doe', age: 25 },
];
export const ResultList: React.FC = () => {
return (
<EuiPanel grow={false}>
<EuiFlexGroup direction="column" gutterSize="none">
{DEMO_DATA.map((item, index) => {
return (
<>
<EuiFlexItem key={item.id + '-' + index} grow>
<EuiFlexGroup direction="column" gutterSize="xs">
<EuiFlexItem grow>
<EuiTitle size="xs">
<h2>{item.id}</h2>
</EuiTitle>
</EuiFlexItem>
<EuiFlexItem grow>
<EuiText size="s">
<p>{item.name}</p>
</EuiText>
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlexItem>
{index !== DEMO_DATA.length - 1 && <EuiHorizontalRule margin="m" />}
</>
);
})}
</EuiFlexGroup>
</EuiPanel>
);
};

View file

@ -0,0 +1,62 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import {
EuiButton,
EuiEmptyPrompt,
EuiFlexGroup,
EuiFlexItem,
EuiSearchBar,
useEuiTheme,
} from '@elastic/eui';
import React from 'react';
import { css } from '@emotion/react';
import { ResultList } from './result_list';
export const SearchMode: React.FC = () => {
const { euiTheme } = useEuiTheme();
const showResults = true; // TODO demo
return (
<EuiFlexGroup direction="row" justifyContent="center">
<EuiFlexItem
grow
css={css`
max-width: ${euiTheme.base * 48}px;
`}
>
<EuiFlexGroup direction="column">
<EuiFlexItem grow={false}>
<EuiSearchBar />
</EuiFlexItem>
<EuiFlexItem className="eui-yScroll">
<EuiFlexGroup direction="column">
<EuiFlexItem>
{showResults ? (
<ResultList />
) : (
<EuiEmptyPrompt
iconType={'checkInCircleFilled'}
iconColor="success"
title={<h2>Ready to search</h2>}
body={
<p>
Type in a query in the search bar above or view the query we automatically
created for you.
</p>
}
actions={<EuiButton>View the query</EuiButton>}
/>
)}
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlexItem>
</EuiFlexGroup>
);
};

View file

@ -5,6 +5,8 @@
* 2.0.
*/
import React, { useEffect } from 'react';
import {
EuiEmptyPrompt,
EuiFlexGroup,
@ -13,17 +15,16 @@ import {
EuiLoadingSpinner,
EuiTitle,
} from '@elastic/eui';
import React, { useEffect } from 'react';
import { FormattedMessage } from '@kbn/i18n-react';
import { CreateIndexButton } from './create_index_button';
import { useQueryIndices } from '../../hooks/use_query_indices';
import { docLinks } from '../../../common/doc_links';
import { useUsageTracker } from '../../hooks/use_usage_tracker';
import { AnalyticsEvents } from '../../analytics/constants';
import { ConnectLLMButton } from './connect_llm_button';
import { AddDataSources } from './add_data_sources';
import { ConnectLLMButton } from './connect_llm_button';
import { CreateIndexButton } from './create_index_button';
export const SetupPage: React.FC = () => {
export const ChatSetupPage: React.FC = () => {
const usageTracker = useUsageTracker();
const { indices, isLoading: isIndicesLoading } = useQueryIndices();

View file

@ -0,0 +1,75 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import React, { useEffect } from 'react';
import {
EuiEmptyPrompt,
EuiFlexGroup,
EuiFlexItem,
EuiLink,
EuiLoadingSpinner,
EuiTitle,
} from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n-react';
import { useQueryIndices } from '../../hooks/use_query_indices';
import { useUsageTracker } from '../../hooks/use_usage_tracker';
import { AnalyticsEvents } from '../../analytics/constants';
import { AddDataSources } from './add_data_sources';
export const SearchPlaygroundSetupPage: React.FC = () => {
const usageTracker = useUsageTracker();
const { isLoading: isIndicesLoading } = useQueryIndices();
useEffect(() => {
usageTracker?.load(AnalyticsEvents.setupSearchPageLoaded);
}, [usageTracker]);
return (
<EuiEmptyPrompt
iconType="indexOpen"
data-test-subj="setupPage"
title={
<h2>
<FormattedMessage
id="xpack.searchPlayground.setupPage.queryBuilder.title"
defaultMessage="Add data to query"
/>
</h2>
}
actions={
<EuiFlexGroup justifyContent="center">
{isIndicesLoading ? (
<EuiLoadingSpinner />
) : (
<EuiFlexItem grow={false}>
<AddDataSources />
</EuiFlexItem>
)}
</EuiFlexGroup>
}
footer={
<>
<EuiTitle size="xxs">
<span>
<FormattedMessage
id="xpack.searchPlayground.setupPage.learnMore"
defaultMessage="Want to learn more?"
/>
</span>
</EuiTitle>{' '}
<EuiLink href="todo" target="_blank" external>
<FormattedMessage
id="xpack.searchPlayground.setupPage.documentationLink"
defaultMessage="Read documentation"
/>
</EuiLink>
</>
}
/>
);
};

View file

@ -0,0 +1,44 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { useEffect, useState } from 'react';
import { PlaygroundPageMode } from '../types';
export const usePageMode = ({
hasSelectedIndices,
hasConnectors,
initialPageMode = PlaygroundPageMode.chat,
}: {
hasSelectedIndices: boolean;
hasConnectors: boolean;
initialPageMode?: PlaygroundPageMode;
}) => {
const [showSetupPage, setShowSetupPage] = useState(true);
const [pageMode, setPageMode] = useState<PlaygroundPageMode>(initialPageMode);
useEffect(() => {
if (pageMode === PlaygroundPageMode.chat) {
if (showSetupPage && hasConnectors && hasSelectedIndices) {
setShowSetupPage(false);
} else if (!showSetupPage && (!hasConnectors || !hasSelectedIndices)) {
setShowSetupPage(true);
}
} else {
if (showSetupPage && hasSelectedIndices) {
setShowSetupPage(false);
} else if (!showSetupPage && !hasSelectedIndices) {
setShowSetupPage(true);
}
}
}, [hasSelectedIndices, showSetupPage, pageMode, hasConnectors]);
return {
showSetupPage,
pageMode,
setPageMode,
};
};

View file

@ -0,0 +1,15 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { isSearchModeEnabled } from '../utils/feature_flags';
import { useKibana } from './use_kibana';
export const useSearchPlaygroundFeatureFlag = (): boolean => {
const { uiSettings } = useKibana().services;
return uiSettings ? isSearchModeEnabled(uiSettings) : false;
};

View file

@ -9,10 +9,16 @@ import React, { useMemo } from 'react';
import { EuiPageTemplate } from '@elastic/eui';
import { PlaygroundProvider } from './providers/playground_provider';
import { App } from './components/app';
import { useKibana } from './hooks/use_kibana';
import { PlaygroundPageMode } from './types';
import { App } from './components/app';
export const ChatPlaygroundOverview: React.FC = () => {
interface PlaygroundOverviewProps {
pageMode?: PlaygroundPageMode;
}
export const PlaygroundOverview: React.FC<PlaygroundOverviewProps> = ({
pageMode = PlaygroundPageMode.chat,
}) => {
const {
services: { console: consolePlugin },
} = useKibana();
@ -31,7 +37,7 @@ export const ChatPlaygroundOverview: React.FC = () => {
grow={false}
panelled={false}
>
<App showDocs />
<App showDocs pageMode={pageMode} />
{embeddableConsole}
</EuiPageTemplate>
</PlaygroundProvider>

View file

@ -8,17 +8,26 @@
import { Route, Routes } from '@kbn/shared-ux-router';
import React from 'react';
import { Redirect } from 'react-router-dom';
import { ChatPlaygroundOverview } from './chat_playground_overview';
import { PlaygroundOverview } from './playground_overview';
import { ROOT_PATH, SEARCH_PLAYGROUND_CHAT_PATH } from './routes';
import { ROOT_PATH, SEARCH_PLAYGROUND_CHAT_PATH, SEARCH_PLAYGROUND_SEARCH_PATH } from './routes';
import { PlaygroundPageMode } from './types';
import { useSearchPlaygroundFeatureFlag } from './hooks/use_search_playground_feature_flag';
export const PlaygroundRouter: React.FC = () => {
const isSearchModeEnabled = useSearchPlaygroundFeatureFlag();
return (
<Routes>
<Redirect exact from={ROOT_PATH} to={SEARCH_PLAYGROUND_CHAT_PATH} />
<Route path={SEARCH_PLAYGROUND_CHAT_PATH}>
<ChatPlaygroundOverview />
<PlaygroundOverview pageMode={PlaygroundPageMode.chat} />
</Route>
{isSearchModeEnabled && (
<Route path={SEARCH_PLAYGROUND_SEARCH_PATH}>
<PlaygroundOverview pageMode={PlaygroundPageMode.search} />
</Route>
)}
</Routes>
);
};

View file

@ -12,7 +12,7 @@ import type {
AppMountParameters,
PluginInitializerContext,
} from '@kbn/core/public';
import { PLUGIN_ID, PLUGIN_NAME } from '../common';
import { PLUGIN_ID, PLUGIN_NAME, PLUGIN_PATH } from '../common';
import { docLinks } from '../common/doc_links';
import { PlaygroundHeaderDocs } from './components/playground_header_docs';
import { Playground, getPlaygroundProvider } from './embeddable';
@ -42,7 +42,7 @@ export class SearchPlaygroundPlugin
core.application.register({
id: PLUGIN_ID,
appRoute: '/app/search_playground',
appRoute: PLUGIN_PATH,
title: PLUGIN_NAME,
async mount({ element, history }: AppMountParameters) {
const { renderApp } = await import('./application');

View file

@ -5,5 +5,8 @@
* 2.0.
*/
export const SEARCH_PLAYGROUND_APP_ID = `search_playground`;
export const ROOT_PATH = '/';
export const SEARCH_PLAYGROUND_CHAT_PATH = `${ROOT_PATH}chat`;
export const SEARCH_PLAYGROUND_SEARCH_PATH = `${ROOT_PATH}search`;

View file

@ -28,6 +28,11 @@ import { PlaygroundHeaderDocs } from './components/playground_header_docs';
export * from '../common/types';
export enum PlaygroundPageMode {
chat = 'chat',
search = 'search',
}
// eslint-disable-next-line @typescript-eslint/no-empty-interface
export interface SearchPlaygroundPluginSetup {}
export interface SearchPlaygroundPluginStart {
@ -57,6 +62,9 @@ export interface AppServicesContext {
triggersActionsUi: TriggersAndActionsUIPublicPluginStart;
usageCollection?: UsageCollectionStart;
console?: ConsolePluginStart;
featureFlags: {
searchPlaygroundEnabled: boolean;
};
}
export enum ChatFormFields {

View file

@ -0,0 +1,13 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { IUiSettingsClient } from '@kbn/core/public';
import { SEARCH_MODE_FEATURE_FLAG_ID } from '../../common';
export function isSearchModeEnabled(uiSettings: IUiSettingsClient): boolean {
return uiSettings.get<boolean>(SEARCH_MODE_FEATURE_FLAG_ID, false);
}

View file

@ -59,7 +59,6 @@ export function defineRoutes({
},
errorHandler(logger)(async (context, request, response) => {
const { client } = (await context.core).elasticsearch;
const { indices } = request.body;
const fields = await fetchFields(client, indices);