diff --git a/oas_docs/output/kibana.serverless.yaml b/oas_docs/output/kibana.serverless.yaml index 81668b68e07db..a014a66f798d4 100644 --- a/oas_docs/output/kibana.serverless.yaml +++ b/oas_docs/output/kibana.serverless.yaml @@ -40577,9 +40577,6 @@ components: id: description: The conversation id. type: string - isDefault: - description: Is default conversation. - type: boolean messages: description: The conversation messages. items: @@ -40602,16 +40599,13 @@ components: $ref: '#/components/schemas/Security_AI_Assistant_API_ConversationCategory' description: The conversation category. createdAt: - description: The last time conversation was updated. + description: The time conversation was created. type: string excludeFromLastConversationStorage: description: excludeFromLastConversationStorage. type: boolean id: $ref: '#/components/schemas/Security_AI_Assistant_API_NonEmptyString' - isDefault: - description: Is default conversation. - type: boolean messages: description: The conversation messages. items: @@ -40838,7 +40832,6 @@ components: Security_AI_Assistant_API_FindConversationsSortField: enum: - created_at - - is_default - title - updated_at type: string diff --git a/oas_docs/output/kibana.yaml b/oas_docs/output/kibana.yaml index b9b03fbdf359d..12fd2d78c0b1b 100644 --- a/oas_docs/output/kibana.yaml +++ b/oas_docs/output/kibana.yaml @@ -28763,9 +28763,6 @@ components: id: description: The conversation id. type: string - isDefault: - description: Is default conversation. - type: boolean messages: description: The conversation messages. items: @@ -28788,16 +28785,13 @@ components: $ref: '#/components/schemas/Security_AI_Assistant_API_ConversationCategory' description: The conversation category. createdAt: - description: The last time conversation was updated. + description: The time conversation was created. type: string excludeFromLastConversationStorage: description: excludeFromLastConversationStorage. type: boolean id: $ref: '#/components/schemas/Security_AI_Assistant_API_NonEmptyString' - isDefault: - description: Is default conversation. - type: boolean messages: description: The conversation messages. items: @@ -29024,7 +29018,6 @@ components: Security_AI_Assistant_API_FindConversationsSortField: enum: - created_at - - is_default - title - updated_at type: string diff --git a/x-pack/platform/packages/shared/kbn-elastic-assistant-common/docs/openapi/ess/elastic_assistant_api_2023_10_31.bundled.schema.yaml b/x-pack/platform/packages/shared/kbn-elastic-assistant-common/docs/openapi/ess/elastic_assistant_api_2023_10_31.bundled.schema.yaml index 13c15e5ca58d2..ab675c8d9dedf 100644 --- a/x-pack/platform/packages/shared/kbn-elastic-assistant-common/docs/openapi/ess/elastic_assistant_api_2023_10_31.bundled.schema.yaml +++ b/x-pack/platform/packages/shared/kbn-elastic-assistant-common/docs/openapi/ess/elastic_assistant_api_2023_10_31.bundled.schema.yaml @@ -1179,9 +1179,6 @@ components: id: description: The conversation id. type: string - isDefault: - description: Is default conversation. - type: boolean messages: description: The conversation messages. items: @@ -1204,16 +1201,13 @@ components: $ref: '#/components/schemas/ConversationCategory' description: The conversation category. createdAt: - description: The last time conversation was updated. + description: The time conversation was created. type: string excludeFromLastConversationStorage: description: excludeFromLastConversationStorage. type: boolean id: $ref: '#/components/schemas/NonEmptyString' - isDefault: - description: Is default conversation. - type: boolean messages: description: The conversation messages. items: @@ -1448,7 +1442,6 @@ components: FindConversationsSortField: enum: - created_at - - is_default - title - updated_at type: string diff --git a/x-pack/platform/packages/shared/kbn-elastic-assistant-common/docs/openapi/serverless/elastic_assistant_api_2023_10_31.bundled.schema.yaml b/x-pack/platform/packages/shared/kbn-elastic-assistant-common/docs/openapi/serverless/elastic_assistant_api_2023_10_31.bundled.schema.yaml index 650fc048d0948..239686e34950c 100644 --- a/x-pack/platform/packages/shared/kbn-elastic-assistant-common/docs/openapi/serverless/elastic_assistant_api_2023_10_31.bundled.schema.yaml +++ b/x-pack/platform/packages/shared/kbn-elastic-assistant-common/docs/openapi/serverless/elastic_assistant_api_2023_10_31.bundled.schema.yaml @@ -1179,9 +1179,6 @@ components: id: description: The conversation id. type: string - isDefault: - description: Is default conversation. - type: boolean messages: description: The conversation messages. items: @@ -1204,16 +1201,13 @@ components: $ref: '#/components/schemas/ConversationCategory' description: The conversation category. createdAt: - description: The last time conversation was updated. + description: The time conversation was created. type: string excludeFromLastConversationStorage: description: excludeFromLastConversationStorage. type: boolean id: $ref: '#/components/schemas/NonEmptyString' - isDefault: - description: Is default conversation. - type: boolean messages: description: The conversation messages. items: @@ -1448,7 +1442,6 @@ components: FindConversationsSortField: enum: - created_at - - is_default - title - updated_at type: string diff --git a/x-pack/platform/packages/shared/kbn-elastic-assistant-common/impl/schemas/conversations/common_attributes.gen.ts b/x-pack/platform/packages/shared/kbn-elastic-assistant-common/impl/schemas/conversations/common_attributes.gen.ts index b41211aca8eb2..5ab44916e0204 100644 --- a/x-pack/platform/packages/shared/kbn-elastic-assistant-common/impl/schemas/conversations/common_attributes.gen.ts +++ b/x-pack/platform/packages/shared/kbn-elastic-assistant-common/impl/schemas/conversations/common_attributes.gen.ts @@ -327,7 +327,7 @@ export const ConversationResponse = z.object({ */ updatedAt: z.string().optional(), /** - * The last time conversation was updated. + * The time conversation was created. */ createdAt: z.string(), replacements: Replacements.optional(), @@ -340,10 +340,6 @@ export const ConversationResponse = z.object({ * LLM API configuration. */ apiConfig: ApiConfig.optional(), - /** - * Is default conversation. - */ - isDefault: z.boolean().optional(), /** * excludeFromLastConversationStorage. */ @@ -403,10 +399,6 @@ export const ConversationCreateProps = z.object({ * LLM API configuration. */ apiConfig: ApiConfig.optional(), - /** - * Is default conversation. - */ - isDefault: z.boolean().optional(), /** * excludeFromLastConversationStorage. */ diff --git a/x-pack/platform/packages/shared/kbn-elastic-assistant-common/impl/schemas/conversations/common_attributes.schema.yaml b/x-pack/platform/packages/shared/kbn-elastic-assistant-common/impl/schemas/conversations/common_attributes.schema.yaml index 6df8ee8a19f3a..c0be31b3d48ec 100644 --- a/x-pack/platform/packages/shared/kbn-elastic-assistant-common/impl/schemas/conversations/common_attributes.schema.yaml +++ b/x-pack/platform/packages/shared/kbn-elastic-assistant-common/impl/schemas/conversations/common_attributes.schema.yaml @@ -316,7 +316,7 @@ components: description: The last time conversation was updated. type: string createdAt: - description: The last time conversation was updated. + description: The time conversation was created. type: string replacements: $ref: '#/components/schemas/Replacements' @@ -332,9 +332,6 @@ components: apiConfig: $ref: '#/components/schemas/ApiConfig' description: LLM API configuration. - isDefault: - description: Is default conversation. - type: boolean excludeFromLastConversationStorage: description: excludeFromLastConversationStorage. type: boolean @@ -393,9 +390,6 @@ components: apiConfig: $ref: '#/components/schemas/ApiConfig' description: LLM API configuration. - isDefault: - description: Is default conversation. - type: boolean excludeFromLastConversationStorage: description: excludeFromLastConversationStorage. type: boolean diff --git a/x-pack/platform/packages/shared/kbn-elastic-assistant-common/impl/schemas/conversations/find_conversations_route.gen.ts b/x-pack/platform/packages/shared/kbn-elastic-assistant-common/impl/schemas/conversations/find_conversations_route.gen.ts index 244a88fa1daff..7354722ba6e38 100644 --- a/x-pack/platform/packages/shared/kbn-elastic-assistant-common/impl/schemas/conversations/find_conversations_route.gen.ts +++ b/x-pack/platform/packages/shared/kbn-elastic-assistant-common/impl/schemas/conversations/find_conversations_route.gen.ts @@ -21,12 +21,7 @@ import { SortOrder } from '../common_attributes.gen'; import { ConversationResponse } from './common_attributes.gen'; export type FindConversationsSortField = z.infer; -export const FindConversationsSortField = z.enum([ - 'created_at', - 'is_default', - 'title', - 'updated_at', -]); +export const FindConversationsSortField = z.enum(['created_at', 'title', 'updated_at']); export type FindConversationsSortFieldEnum = typeof FindConversationsSortField.enum; export const FindConversationsSortFieldEnum = FindConversationsSortField.enum; diff --git a/x-pack/platform/packages/shared/kbn-elastic-assistant-common/impl/schemas/conversations/find_conversations_route.schema.yaml b/x-pack/platform/packages/shared/kbn-elastic-assistant-common/impl/schemas/conversations/find_conversations_route.schema.yaml index 4b532c2cc7303..ccf2bd8e9cafe 100644 --- a/x-pack/platform/packages/shared/kbn-elastic-assistant-common/impl/schemas/conversations/find_conversations_route.schema.yaml +++ b/x-pack/platform/packages/shared/kbn-elastic-assistant-common/impl/schemas/conversations/find_conversations_route.schema.yaml @@ -98,6 +98,5 @@ components: type: string enum: - 'created_at' - - 'is_default' - 'title' - 'updated_at' diff --git a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/api/conversations/conversations.ts b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/api/conversations/conversations.ts index 54bac7e563acc..d2a5c9d265cb1 100644 --- a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/api/conversations/conversations.ts +++ b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/api/conversations/conversations.ts @@ -61,16 +61,15 @@ export const getConversationById = async ({ }; /** - * API call for getting all user conversations. + * API call for determining whether any user conversations exist * - * @param {Object} options - The options object. * @param {HttpSetup} options.http - HttpSetup * @param {IToasts} [options.toasts] - IToasts * @param {AbortSignal} [options.signal] - AbortSignal * - * @returns {Promise} + * @returns {Promise} */ -export const getUserConversations = async ({ +export const getUserConversationsExist = async ({ http, signal, toasts, @@ -78,16 +77,24 @@ export const getUserConversations = async ({ http: HttpSetup; toasts?: IToasts; signal?: AbortSignal | undefined; -}) => { +}): Promise => { try { - return await http.fetch( + const conversation = await http.fetch( ELASTIC_AI_ASSISTANT_CONVERSATIONS_URL_FIND, { method: 'GET', version: API_VERSIONS.public.v1, signal, + query: { + per_page: 1, + page: 1, + // one field to keep request as small as possible + fields: ['title'], + }, } ); + + return conversation.total > 0; } catch (error) { toasts?.addError(error.body && error.body.message ? new Error(error.body.message) : error, { title: i18n.translate('xpack.elasticAssistant.conversations.getUserConversationsError', { diff --git a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/api/conversations/use_fetch_current_user_conversations.test.tsx b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/api/conversations/use_fetch_current_user_conversations.test.tsx index cfe67c60324cc..4927d5256db71 100644 --- a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/api/conversations/use_fetch_current_user_conversations.test.tsx +++ b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/api/conversations/use_fetch_current_user_conversations.test.tsx @@ -19,11 +19,8 @@ import { defaultAssistantFeatures } from '@kbn/elastic-assistant-common'; const http = { fetch: jest.fn().mockResolvedValue(defaultAssistantFeatures), }; -const onFetch = jest.fn(); - const defaultProps = { http, - onFetch, isAssistantEnabled: true, } as unknown as UseFetchCurrentUserConversationsParams; @@ -48,14 +45,16 @@ describe('useFetchCurrentUserConversations', () => { method: 'GET', query: { page: 1, - per_page: 99, + fields: ['id', 'title', 'apiConfig', 'updatedAt'], + filter: undefined, + per_page: 28, + sort_field: 'updated_at', + sort_order: 'desc', }, version: '2023-10-31', signal: undefined, } ); - - expect(onFetch).toHaveBeenCalled(); }); }); }); diff --git a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/api/conversations/use_fetch_current_user_conversations.ts b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/api/conversations/use_fetch_current_user_conversations.ts index 9006ca387e086..1b0e0f9bcc028 100644 --- a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/api/conversations/use_fetch_current_user_conversations.ts +++ b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/api/conversations/use_fetch_current_user_conversations.ts @@ -6,71 +6,182 @@ */ import { HttpSetup } from '@kbn/core/public'; -import { useQuery } from '@tanstack/react-query'; +import { + InfiniteData, + QueryObserverResult, + RefetchOptions, + RefetchQueryFilters, + useInfiniteQuery, +} from '@tanstack/react-query'; import { API_VERSIONS, ELASTIC_AI_ASSISTANT_CONVERSATIONS_URL_FIND, } from '@kbn/elastic-assistant-common'; +import { useCallback, useEffect, useMemo, useRef } from 'react'; import { Conversation } from '../../../assistant_context/types'; export interface FetchConversationsResponse { page: number; - per_page: number; + perPage: number; total: number; data: Conversation[]; } export interface UseFetchCurrentUserConversationsParams { http: HttpSetup; - onFetch: (result: FetchConversationsResponse) => Record; + fields?: string[]; + filter?: string; + page?: number; + perPage?: number; signal?: AbortSignal | undefined; + sortField?: string; + sortOrder?: string; refetchOnWindowFocus?: boolean; isAssistantEnabled: boolean; + setTotalItemCount?: (total: number) => void; +} + +export interface FetchCurrentUserConversations { + data: Record; + isLoading: boolean; + refetch: ( + options?: RefetchOptions & RefetchQueryFilters + ) => Promise, unknown>>; + isFetched: boolean; + isFetching: boolean; + setPaginationObserver: (ref: HTMLDivElement) => void; } -/** - * API call for fetching assistant conversations for the current user - * - * @param {Object} options - The options object. - * @param {HttpSetup} options.http - HttpSetup - * @param {Function} [options.onFetch] - transformation function for conversations fetch result - * @param {AbortSignal} [options.signal] - AbortSignal - * - * @returns {useQuery} hook for getting the status of the conversations - */ const query = { page: 1, - per_page: 99, + perPage: 28, + // keep request small returning a subset of fields + // un-requested required fields like the users array will now appear empty + // something to consider if/when we add support for global/shared conversations. + fields: ['id', 'title', 'apiConfig', 'updatedAt'], }; -export const CONVERSATIONS_QUERY_KEYS = [ - ELASTIC_AI_ASSISTANT_CONVERSATIONS_URL_FIND, - query.page, - query.per_page, - API_VERSIONS.public.v1, -]; - +const formatFetchedData = (data: InfiniteData | undefined) => + data?.pages.reduce((acc, curr) => { + return { + ...acc, + ...curr.data.reduce( + (conversationsArr, conversation) => ({ + ...conversationsArr, + [conversation.id]: conversation, + }), + {} + ), + }; + }, {}); +/** + * API call for fetching assistant conversations for the current user + */ export const useFetchCurrentUserConversations = ({ http, - onFetch, + fields = query.fields, + filter, + page = query.page, + perPage = query.perPage, signal, + sortField = 'updated_at', + sortOrder = 'desc', refetchOnWindowFocus = true, isAssistantEnabled, -}: UseFetchCurrentUserConversationsParams) => - useQuery( - CONVERSATIONS_QUERY_KEYS, - async () => - http.fetch(ELASTIC_AI_ASSISTANT_CONVERSATIONS_URL_FIND, { + setTotalItemCount, +}: UseFetchCurrentUserConversationsParams): FetchCurrentUserConversations => { + const queryFn = useCallback( + async ({ pageParam }: { pageParam?: UseFetchCurrentUserConversationsParams }) => { + return http.fetch(ELASTIC_AI_ASSISTANT_CONVERSATIONS_URL_FIND, { method: 'GET', version: API_VERSIONS.public.v1, - query, + query: { + fields, + filter, + page: pageParam?.page ?? page, + per_page: pageParam?.perPage ?? perPage, + sort_field: sortField, + sort_order: sortOrder, + }, signal, - }), - { - select: (data) => onFetch(data), - keepPreviousData: true, - initialData: { ...query, total: 0, data: [] }, - refetchOnWindowFocus, - enabled: isAssistantEnabled, + }); + }, + [fields, filter, http, page, perPage, signal, sortField, sortOrder] + ); + + const getNextPageParam = useCallback((lastPage: FetchConversationsResponse) => { + const totalPages = Math.max(1, Math.ceil(lastPage.total / lastPage.perPage)); + if (totalPages === lastPage.page) { + return; + } + return { + ...lastPage, + page: lastPage.page + 1, + }; + }, []); + + const { data, fetchNextPage, hasNextPage, isFetched, isFetching, isLoading, refetch } = + useInfiniteQuery( + [ + ELASTIC_AI_ASSISTANT_CONVERSATIONS_URL_FIND, + page, + perPage, + API_VERSIONS.public.v1, + filter, + sortField, + sortOrder, + ], + queryFn, + { + enabled: isAssistantEnabled, + getNextPageParam, + refetchOnWindowFocus, + } + ); + useEffect(() => { + if (setTotalItemCount && data?.pages?.length) { + setTotalItemCount(data?.pages[0].total ?? 0); } + }, [data?.pages, setTotalItemCount]); + + const formatted = useMemo(() => formatFetchedData(data), [data]); + + const observerRef = useRef(); + const fetchNext = useCallback( + async ([{ isIntersecting }]: IntersectionObserverEntry[]) => { + if (isIntersecting && hasNextPage && !isLoading && !isFetching) { + await fetchNextPage(); + observerRef.current?.disconnect(); + } + }, + [fetchNextPage, hasNextPage, isFetching, isLoading] ); + + useEffect(() => { + return () => observerRef.current?.disconnect(); + }, []); + + // Attaches an intersection observer to the last element + // to trigger a callback to paginate when the user scrolls to it + const setPaginationObserver = useCallback( + (ref: HTMLDivElement) => { + observerRef.current?.disconnect(); + observerRef.current = new IntersectionObserver(fetchNext, { + root: null, + rootMargin: '0px', + threshold: 0.1, + }); + observerRef.current?.observe(ref); + }, + [fetchNext] + ); + + return { + data: formatted ?? {}, + isLoading, + refetch, + isFetched, + isFetching, + setPaginationObserver, + }; +}; diff --git a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/api/index.tsx b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/api/index.tsx index f522c22549cdc..ac912d3570746 100644 --- a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/api/index.tsx +++ b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/api/index.tsx @@ -57,7 +57,6 @@ export const fetchConnectorExecuteAction = async ({ traceOptions, screenContext, }: FetchConnectorExecuteAction): Promise => { - // TODO add streaming support for gemini with langchain on const isStream = assistantStreamingEnabled; const optionalRequestParams = getOptionalRequestParams({ diff --git a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/assistant_body/index.tsx b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/assistant_body/index.tsx index 8ce728f339db9..856ac9c9af97c 100644 --- a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/assistant_body/index.tsx +++ b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/assistant_body/index.tsx @@ -29,7 +29,7 @@ interface Props { comments: JSX.Element; currentConversation: Conversation | undefined; currentSystemPromptId: string | undefined; - handleOnConversationSelected: ({ cId, cTitle }: { cId: string; cTitle: string }) => Promise; + handleOnConversationSelected: ({ cId }: { cId: string }) => Promise; isAssistantEnabled: boolean; isSettingsModalVisible: boolean; isWelcomeSetup: boolean; diff --git a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/assistant_body/welcome_setup.tsx b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/assistant_body/welcome_setup.tsx index 9446454ad94cd..3ba0dc1783afe 100644 --- a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/assistant_body/welcome_setup.tsx +++ b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/assistant_body/welcome_setup.tsx @@ -15,7 +15,7 @@ import * as i18n from '../translations'; interface Props { currentConversation: Conversation | undefined; - handleOnConversationSelected: ({ cId, cTitle }: { cId: string; cTitle: string }) => Promise; + handleOnConversationSelected: ({ cId }: { cId: string }) => Promise; } export const WelcomeSetup: React.FC = ({ diff --git a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/assistant_header/index.test.tsx b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/assistant_header/index.test.tsx index fe4b0de4d2149..1b47ea546e3cb 100644 --- a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/assistant_header/index.test.tsx +++ b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/assistant_header/index.test.tsx @@ -38,6 +38,7 @@ const testProps = { onChatCleared: jest.fn(), showAnonymizedValues: false, conversations: mockConversations, + refetchCurrentConversation: jest.fn(), refetchCurrentUserConversations: jest.fn(), isAssistantEnabled: true, anonymizationFields: { total: 0, page: 1, perPage: 1000, data: [] }, @@ -45,6 +46,7 @@ const testProps = { allPrompts: [], contentReferencesVisible: true, setContentReferencesVisible: jest.fn(), + setPaginationObserver: jest.fn(), }; jest.mock('../../connectorland/use_load_connectors', () => ({ diff --git a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/assistant_header/index.tsx b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/assistant_header/index.tsx index 669c0c6f327e1..37832504392a7 100644 --- a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/assistant_header/index.tsx +++ b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/assistant_header/index.tsx @@ -10,7 +10,8 @@ import { QueryObserverResult, RefetchOptions, RefetchQueryFilters } from '@tanst import { EuiFlexGroup, EuiFlexItem, EuiButtonIcon, EuiPanel, EuiSkeletonTitle } from '@elastic/eui'; import { css } from '@emotion/react'; import { euiThemeVars } from '@kbn/ui-theme'; -import { isEmpty } from 'lodash'; +import { ApiConfig } from '@kbn/elastic-assistant-common'; +import { NEW_CHAT } from '../conversations/conversation_sidepanel/translations'; import { DataStreamApis } from '../use_data_stream_apis'; import { Conversation } from '../../..'; import { AssistantTitle } from '../assistant_title'; @@ -32,15 +33,25 @@ interface OwnProps { onCloseFlyout?: () => void; chatHistoryVisible?: boolean; setChatHistoryVisible?: React.Dispatch>; - onConversationSelected: ({ cId, cTitle }: { cId: string; cTitle: string }) => void; + onConversationSelected: ({ + cId, + cTitle, + apiConfig, + }: { + apiConfig?: ApiConfig; + cId: string; + cTitle?: string; + }) => void; conversations: Record; conversationsLoaded: boolean; + refetchCurrentConversation: ({ isStreamRefetch }: { isStreamRefetch?: boolean }) => void; refetchCurrentUserConversations: DataStreamApis['refetchCurrentUserConversations']; onConversationCreate: () => Promise; isAssistantEnabled: boolean; refetchPrompts?: ( options?: RefetchOptions & RefetchQueryFilters ) => Promise>; + setPaginationObserver: (ref: HTMLDivElement) => void; } type Props = OwnProps; @@ -53,23 +64,25 @@ export const AI_ASSISTANT_SETTINGS_MENU_CONTAINER_ID = 'aiAssistantSettingsMenuC * toggling the display of anonymized values, and accessing the assistant settings. */ export const AssistantHeader: React.FC = ({ - selectedConversation, + chatHistoryVisible, + conversations, + conversationsLoaded, defaultConnector, + isAssistantEnabled, isDisabled, isLoading, isSettingsModalVisible, - setIsSettingsModalVisible, onChatCleared, - chatHistoryVisible, - setChatHistoryVisible, onCloseFlyout, + onConversationCreate, onConversationSelected, - conversations, - conversationsLoaded, + refetchCurrentConversation, refetchCurrentUserConversations, - onConversationCreate, - isAssistantEnabled, refetchPrompts, + selectedConversation, + setChatHistoryVisible, + setIsSettingsModalVisible, + setPaginationObserver, }) => { const selectedConnectorId = useMemo( () => selectedConversation?.apiConfig?.connectorId, @@ -77,10 +90,11 @@ export const AssistantHeader: React.FC = ({ ); const onConversationChange = useCallback( - (updatedConversation: Conversation) => { + (updatedConversation: Conversation, apiConfig?: ApiConfig) => { onConversationSelected({ cId: updatedConversation.id, cTitle: updatedConversation.title, + ...(apiConfig ? { apiConfig } : {}), }); }, [onConversationSelected] @@ -101,17 +115,14 @@ export const AssistantHeader: React.FC = ({ defaultConnector={defaultConnector} isDisabled={isDisabled} isSettingsModalVisible={isSettingsModalVisible} - selectedConversationId={ - !isEmpty(selectedConversation?.id) - ? selectedConversation?.id - : selectedConversation?.title - } setIsSettingsModalVisible={setIsSettingsModalVisible} onConversationSelected={onConversationSelected} conversations={conversations} conversationsLoaded={conversationsLoaded} + refetchCurrentConversation={refetchCurrentConversation} refetchCurrentUserConversations={refetchCurrentUserConversations} refetchPrompts={refetchPrompts} + setPaginationObserver={setPaginationObserver} /> @@ -147,8 +158,8 @@ export const AssistantHeader: React.FC = ({ ) : ( diff --git a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/assistant_header/translations.ts b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/assistant_header/translations.ts index 38d439848ee38..020afe039b52c 100644 --- a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/assistant_header/translations.ts +++ b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/assistant_header/translations.ts @@ -49,20 +49,6 @@ export const RESET_CONVERSATION = i18n.translate( } ); -export const SHOW_ANONYMIZED = i18n.translate( - 'xpack.elasticAssistant.assistant.settings.showAnonymizedToggleLabel', - { - defaultMessage: 'Show anonymized', - } -); - -export const SHOW_REAL_VALUES = i18n.translate( - 'xpack.elasticAssistant.assistant.settings.showAnonymizedToggleRealValuesLabel', - { - defaultMessage: 'Show real values', - } -); - export const ANONYMIZE_VALUES = i18n.translate( 'xpack.elasticAssistant.assistant.settings.anonymizeValues', { diff --git a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/assistant_overlay/index.test.tsx b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/assistant_overlay/index.test.tsx index 9e6a9164607a3..4cd7b0fce58cc 100644 --- a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/assistant_overlay/index.test.tsx +++ b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/assistant_overlay/index.test.tsx @@ -57,7 +57,6 @@ describe('AssistantOverlay', () => { expect(reportAssistantInvoked).toHaveBeenCalledTimes(1); expect(reportAssistantInvoked).toHaveBeenCalledWith({ invokedBy: 'shortcut', - conversationId: 'Welcome', }); fireEvent.keyDown(document, { key: ';', ctrlKey: true }); expect(reportAssistantInvoked).toHaveBeenCalledTimes(1); diff --git a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/assistant_overlay/index.tsx b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/assistant_overlay/index.tsx index c80145dcef5b9..5f8f9fa4c3809 100644 --- a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/assistant_overlay/index.tsx +++ b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/assistant_overlay/index.tsx @@ -12,7 +12,11 @@ import useEvent from 'react-use/lib/useEvent'; import { css } from '@emotion/react'; import { createGlobalStyle } from 'styled-components'; -import { ShowAssistantOverlayProps, useAssistantContext } from '../../assistant_context'; +import { + LastConversation, + ShowAssistantOverlayProps, + useAssistantContext, +} from '../../assistant_context'; import { Assistant, CONVERSATION_SIDE_PANEL_WIDTH } from '..'; const isMac = navigator.platform.toLowerCase().indexOf('mac') >= 0; @@ -30,10 +34,12 @@ export const UnifiedTimelineGlobalStyles = createGlobalStyle` export const AssistantOverlay = React.memo(() => { const [isModalVisible, setIsModalVisible] = useState(false); - // Why is this named Title and not Id? - const [conversationTitle, setConversationTitle] = useState(undefined); + // id if the conversation exists in the data stream, title if it's a new conversation + const [lastConversation, setSelectedConversation] = useState( + undefined + ); const [promptContextId, setPromptContextId] = useState(); - const { assistantTelemetry, setShowAssistantOverlay, getLastConversationId } = + const { assistantTelemetry, setShowAssistantOverlay, getLastConversation } = useAssistantContext(); const [chatHistoryVisible, setChatHistoryVisible] = useState(false); @@ -44,16 +50,16 @@ export const AssistantOverlay = React.memo(() => { ({ showOverlay: so, promptContextId: pid, - conversationTitle: cTitle, + selectedConversation, }: ShowAssistantOverlayProps) => { - const conversationId = getLastConversationId(cTitle); - if (so) assistantTelemetry?.reportAssistantInvoked({ conversationId, invokedBy: 'click' }); + const nextConversation = getLastConversation(selectedConversation); + if (so) assistantTelemetry?.reportAssistantInvoked({ invokedBy: 'click' }); setIsModalVisible(so); setPromptContextId(pid); - setConversationTitle(conversationId); + setSelectedConversation(nextConversation); }, - [assistantTelemetry, getLastConversationId] + [assistantTelemetry, getLastConversation] ); useEffect(() => { setShowAssistantOverlay(showOverlay); @@ -63,15 +69,14 @@ export const AssistantOverlay = React.memo(() => { const handleShortcutPress = useCallback(() => { // Try to restore the last conversation on shortcut pressed if (!isModalVisible) { - setConversationTitle(getLastConversationId()); + setSelectedConversation(getLastConversation()); assistantTelemetry?.reportAssistantInvoked({ invokedBy: 'shortcut', - conversationId: getLastConversationId(), }); } setIsModalVisible(!isModalVisible); - }, [isModalVisible, getLastConversationId, assistantTelemetry]); + }, [isModalVisible, getLastConversation, assistantTelemetry]); // Register keyboard listener to show the modal when cmd + ; is pressed const onKeyDown = useCallback( @@ -89,8 +94,8 @@ export const AssistantOverlay = React.memo(() => { const cleanupAndCloseModal = useCallback(() => { setIsModalVisible(false); setPromptContextId(undefined); - setConversationTitle(conversationTitle); - }, [conversationTitle]); + setSelectedConversation(lastConversation); + }, [lastConversation]); const handleCloseModal = useCallback(() => { cleanupAndCloseModal(); @@ -132,7 +137,7 @@ export const AssistantOverlay = React.memo(() => { hideCloseButton > = ({ title, selectedConversation, refetchCurrentUserConversations, isDisabled = false }) => { +}> = ({ + title = NEW_CHAT, + selectedConversation, + refetchCurrentUserConversations, + isDisabled = false, +}) => { const [newTitle, setNewTitle] = useState(title); const [newTitleError, setNewTitleError] = useState(false); const { updateConversationTitle } = useConversation(); @@ -62,10 +67,10 @@ export const AssistantTitle: React.FC<{ data-test-subj="conversationTitle" heading="h2" inputAriaLabel="Edit text inline" - value={newTitle ?? NEW_CHAT} + value={newTitle} size="xs" isInvalid={!!newTitleError} - isReadOnly={isDisabled || selectedConversation?.isDefault} + isReadOnly={isDisabled} onChange={(e) => setNewTitle(e.currentTarget.nodeValue || '')} onCancel={() => setNewTitle(title)} onSave={handleUpdateTitle} diff --git a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/chat_send/index.tsx b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/chat_send/index.tsx index cdfa12e3507f0..6351b496422e4 100644 --- a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/chat_send/index.tsx +++ b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/chat_send/index.tsx @@ -48,10 +48,6 @@ export const ChatSend: React.FC = ({ useAutosizeTextArea(promptTextAreaRef?.current, promptValue); - useEffect(() => { - setUserPrompt(promptValue); - }, [setUserPrompt, promptValue]); - return ( { await waitFor(() => { expect(reportAssistantMessageSent).toHaveBeenNthCalledWith(1, { - conversationId: testProps.currentConversation?.title, role: 'user', actionTypeId: '.gen-ai', model: undefined, @@ -138,7 +137,6 @@ describe('use chat send', () => { isEnabledKnowledgeBase: false, }); expect(reportAssistantMessageSent).toHaveBeenNthCalledWith(2, { - conversationId: testProps.currentConversation?.title, role: 'assistant', actionTypeId: '.gen-ai', model: undefined, diff --git a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/chat_send/use_chat_send.tsx b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/chat_send/use_chat_send.tsx index c240d5ac6b60b..f2d9adc6bf81b 100644 --- a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/chat_send/use_chat_send.tsx +++ b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/chat_send/use_chat_send.tsx @@ -11,7 +11,6 @@ import { i18n } from '@kbn/i18n'; import { Replacements } from '@kbn/elastic-assistant-common'; import { useKnowledgeBaseStatus } from '../api/knowledge_base/use_knowledge_base_status'; import { DataStreamApis } from '../use_data_stream_apis'; -import { NEW_CHAT } from '../conversations/conversation_sidepanel/translations'; import type { ClientMessage } from '../../assistant_context/types'; import { SelectedPromptContext } from '../prompt_context/types'; import { useSendMessage } from '../use_send_message'; @@ -56,11 +55,13 @@ export const useChatSend = ({ assistantTelemetry, toasts, assistantAvailability: { isAssistantEnabled }, + setLastConversation, } = useAssistantContext(); const [userPrompt, setUserPrompt] = useState(null); const { isLoading, sendMessage, abortStream } = useSendMessage(); - const { clearConversation, removeLastMessage } = useConversation(); + const { clearConversation, createConversation, getConversation, removeLastMessage } = + useConversation(); const { data: kbStatus } = useKnowledgeBaseStatus({ http, enabled: isAssistantEnabled }); const isSetupComplete = kbStatus?.elser_exists && @@ -82,15 +83,25 @@ export const useChatSend = ({ ); return; } - + const apiConfig = currentConversation.apiConfig; + let newConvo; + if (currentConversation.id === '') { + // create conversation with empty title, GENERATE_CHAT_TITLE graph step will properly title + newConvo = await createConversation(currentConversation); + if (newConvo?.id) { + setLastConversation({ + id: newConvo.id, + }); + } + } + const convo: Conversation = { ...currentConversation, ...(newConvo ?? {}) }; const userMessage = getCombinedMessage({ - currentReplacements: currentConversation.replacements, + currentReplacements: convo.replacements, promptText, selectedPromptContexts, }); - const baseReplacements: Replacements = - userMessage.replacements ?? currentConversation.replacements; + const baseReplacements: Replacements = userMessage.replacements ?? convo.replacements; const selectedPromptContextsReplacements = Object.values( selectedPromptContexts @@ -100,12 +111,12 @@ export const useChatSend = ({ ...baseReplacements, ...selectedPromptContextsReplacements, }; - const updatedMessages = [...currentConversation.messages, userMessage].map((m) => ({ + const updatedMessages = [...convo.messages, userMessage].map((m) => ({ ...m, content: m.content ?? '', })); setCurrentConversation({ - ...currentConversation, + ...convo, replacements, messages: updatedMessages, }); @@ -114,46 +125,49 @@ export const useChatSend = ({ setSelectedPromptContexts({}); const rawResponse = await sendMessage({ - apiConfig: currentConversation.apiConfig, + apiConfig, http, message: userMessage.content ?? '', - conversationId: currentConversation.id, + conversationId: convo.id, replacements, }); assistantTelemetry?.reportAssistantMessageSent({ - conversationId: currentConversation.title, role: userMessage.role, - actionTypeId: currentConversation.apiConfig.actionTypeId, - model: currentConversation.apiConfig.model, - provider: currentConversation.apiConfig.provider, + actionTypeId: apiConfig.actionTypeId, + model: apiConfig.model, + provider: apiConfig.provider, isEnabledKnowledgeBase: isSetupComplete ?? false, }); const responseMessage: ClientMessage = getMessageFromRawResponse(rawResponse); - + if (convo.title === '') { + convo.title = (await getConversation(convo.id))?.title ?? ''; + } setCurrentConversation({ - ...currentConversation, + ...convo, replacements, messages: [...updatedMessages, responseMessage], }); assistantTelemetry?.reportAssistantMessageSent({ - conversationId: currentConversation.title, role: responseMessage.role, - actionTypeId: currentConversation.apiConfig.actionTypeId, - model: currentConversation.apiConfig.model, - provider: currentConversation.apiConfig.provider, + actionTypeId: apiConfig.actionTypeId, + model: apiConfig.model, + provider: apiConfig.provider, isEnabledKnowledgeBase: isSetupComplete ?? false, }); }, [ assistantTelemetry, + createConversation, currentConversation, + getConversation, http, isSetupComplete, selectedPromptContexts, sendMessage, setCurrentConversation, + setLastConversation, setSelectedPromptContexts, toasts, ] @@ -218,11 +232,10 @@ export const useChatSend = ({ const handleChatSend = useCallback( async (promptText: string) => { await handleSendMessage(promptText); - if (currentConversation?.title === NEW_CHAT) { - await refetchCurrentUserConversations(); - } + // refetch to update the conversation list + await refetchCurrentUserConversations(); }, - [currentConversation, handleSendMessage, refetchCurrentUserConversations] + [handleSendMessage, refetchCurrentUserConversations] ); return { diff --git a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/common/components/assistant_settings_management/badges/index.tsx b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/common/components/assistant_settings_management/badges/index.tsx index 0a44664bd5d34..b84e5225c4d6f 100644 --- a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/common/components/assistant_settings_management/badges/index.tsx +++ b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/common/components/assistant_settings_management/badges/index.tsx @@ -15,9 +15,9 @@ export const BadgesColumn: React.FC<{ }> = React.memo(({ items, prefix, color = 'hollow' }) => items && items.length > 0 ? (
- {items.map((c, idx) => ( + {items.map((title, idx) => ( - {c} + {title} ))}
diff --git a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/common/components/assistant_settings_management/inline_actions/index.tsx b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/common/components/assistant_settings_management/inline_actions/index.tsx index 7a2da0d22fc3e..8748d1bbe8e7d 100644 --- a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/common/components/assistant_settings_management/inline_actions/index.tsx +++ b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/common/components/assistant_settings_management/inline_actions/index.tsx @@ -16,7 +16,7 @@ interface Props { onEdit?: (rowItem: T) => void; } -export const useInlineActions = () => { +export const useInlineActions = () => { const getInlineActions = useCallback( ({ isEditEnabled = () => false, diff --git a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/common/components/assistant_settings_management/pagination/use_session_pagination.ts b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/common/components/assistant_settings_management/pagination/use_session_pagination.ts index 3bf0a5e792089..0386156197945 100644 --- a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/common/components/assistant_settings_management/pagination/use_session_pagination.ts +++ b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/common/components/assistant_settings_management/pagination/use_session_pagination.ts @@ -5,57 +5,89 @@ * 2.0. */ -import { Direction } from '@elastic/eui'; +import { CriteriaWithPagination, EuiInMemoryTableProps, EuiTableSortingType } from '@elastic/eui'; import { useCallback, useMemo } from 'react'; import useSessionStorage from 'react-use/lib/useSessionStorage'; import { DEFAULT_ASSISTANT_NAMESPACE } from '../../../../../assistant_context/constants'; import { DEFAULT_PAGE_SIZE } from '../../../../settings/const'; -export const DEFAULT_TABLE_OPTIONS = { - page: { size: DEFAULT_PAGE_SIZE, index: 0 }, - sort: { field: '', direction: 'asc' as const }, -}; +export const getDefaultTableOptions = ({ + pageSize, + sortDirection, + sortField, +}: { + sortField: keyof T; + sortDirection?: 'asc' | 'desc'; + pageSize?: number; +}) => ({ + page: { size: pageSize ?? DEFAULT_PAGE_SIZE, index: 0 }, + sort: { field: sortField, direction: sortDirection ?? ('desc' as const) }, +}); + +interface InMemoryPagination { + initialPageSize: number; + pageSizeOptions: number[]; + pageIndex: number; +} + +interface ServerSidePagination { + totalItemCount: number; + pageSize: number; + pageSizeOptions: number[]; + pageIndex: number; +} -export const useSessionPagination = ({ +interface UseSessionPaginationReturn { + onTableChange: (criteria: CriteriaWithPagination) => void; + pagination: B extends true ? InMemoryPagination : ServerSidePagination; + sorting: B extends true ? EuiInMemoryTableProps['sorting'] : EuiTableSortingType; +} + +export const useSessionPagination = ({ defaultTableOptions, nameSpace = DEFAULT_ASSISTANT_NAMESPACE, + inMemory, storageKey, + totalItemCount = 0, }: { - defaultTableOptions: { - page: { size: number; index: number }; - sort: { field: string; direction: Direction }; - }; + defaultTableOptions: CriteriaWithPagination; + inMemory?: B; nameSpace?: string; storageKey: string; -}) => { + totalItemCount?: number; +}): UseSessionPaginationReturn => { const [sessionStorageTableOptions = defaultTableOptions, setSessionStorageTableOptions] = - useSessionStorage(`${nameSpace}.${storageKey}`, defaultTableOptions); + useSessionStorage>(`${nameSpace}.${storageKey}`, defaultTableOptions); const pagination = useMemo( - () => ({ - initialPageSize: sessionStorageTableOptions.page.size, - pageSizeOptions: [5, 10, DEFAULT_PAGE_SIZE, 50], - pageIndex: sessionStorageTableOptions.page.index, - }), - [sessionStorageTableOptions] + () => + (inMemory + ? { + initialPageSize: sessionStorageTableOptions.page.size, + pageSizeOptions: [5, 10, DEFAULT_PAGE_SIZE, 50], + pageIndex: sessionStorageTableOptions.page.index, + } + : { + totalItemCount, + pageSize: sessionStorageTableOptions.page.size ?? DEFAULT_PAGE_SIZE, + pageSizeOptions: [5, 10, DEFAULT_PAGE_SIZE, 50], + pageIndex: sessionStorageTableOptions.page.index, + }) as UseSessionPaginationReturn['pagination'], + [inMemory, sessionStorageTableOptions, totalItemCount] ); - const sorting = useMemo( - () => ({ - sort: sessionStorageTableOptions.sort, - }), - [sessionStorageTableOptions.sort] - ); + const sorting = useMemo(() => { + return { + sort: sessionStorageTableOptions.sort ?? defaultTableOptions.sort, + } as UseSessionPaginationReturn['sorting']; + }, [defaultTableOptions.sort, sessionStorageTableOptions.sort]); - const onTableChange = useCallback( - ({ - page, - sort, - }: // eslint-disable-next-line @typescript-eslint/no-explicit-any - any) => { + const onTableChange: UseSessionPaginationReturn['onTableChange'] = useCallback( + (args: CriteriaWithPagination) => { + const { page, sort } = args; setSessionStorageTableOptions({ page, - sort, + ...(sort ? { sort } : {}), }); }, [setSessionStorageTableOptions] diff --git a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/conversations/conversation_selector_settings/index.test.tsx b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/conversations/conversation_selector_settings/index.test.tsx index e2c630bbedd14..1e9e5d1c9c3ca 100644 --- a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/conversations/conversation_selector_settings/index.test.tsx +++ b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/conversations/conversation_selector_settings/index.test.tsx @@ -35,13 +35,6 @@ describe('ConversationSelectorSettings', () => { fireEvent.click(getByTestId(alertConvo.title)); expect(onConversationSelectionChange).toHaveBeenCalledWith(alertConvo); }); - it('Only custom option can be deleted', () => { - const { getByTestId } = render(); - fireEvent.click(getByTestId('comboBoxToggleListButton')); - // there is only one delete conversation because there is only one custom convo - fireEvent.click(getByTestId('delete-conversation')); - expect(onConversationDeleted).toHaveBeenCalledWith(customConvo.title); - }); it('Selects existing conversation from the search input', () => { const { getByTestId } = render(); fireEvent.change(getByTestId('comboBoxSearchInput'), { target: { value: alertConvo.title } }); diff --git a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/conversations/conversation_selector_settings/index.tsx b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/conversations/conversation_selector_settings/index.tsx index cf30f5ea935e9..dacbc079e6691 100644 --- a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/conversations/conversation_selector_settings/index.tsx +++ b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/conversations/conversation_selector_settings/index.tsx @@ -8,6 +8,7 @@ import { EuiButtonIcon, EuiComboBox, + EuiComboBoxOptionOption, EuiFlexGroup, EuiFlexItem, EuiFormRow, @@ -20,7 +21,6 @@ import { css } from '@emotion/react'; import { Conversation } from '../../../..'; import * as i18n from './translations'; import { SystemPromptSelectorOption } from '../../prompt_editor/system_prompt/system_prompt_modal/system_prompt_selector/system_prompt_selector'; -import { ConversationSelectorSettingsOption } from './types'; interface Props { conversations: Record; @@ -67,25 +67,24 @@ export const ConversationSelectorSettings: React.FC = React.memo( [conversations] ); - const [conversationOptions, setConversationOptions] = useState< - ConversationSelectorSettingsOption[] - >(() => { - return Object.values(conversations).map((conversation) => ({ - value: { isDefault: conversation.isDefault ?? false }, - label: conversation.title, - id: conversation.id, - 'data-test-subj': conversation.title, - })); - }); + const [conversationOptions, setConversationOptions] = useState( + () => { + return Object.values(conversations).map((conversation) => ({ + label: conversation.title, + id: conversation.id, + 'data-test-subj': conversation.title, + })); + } + ); - const selectedOptions = useMemo(() => { + const selectedOptions = useMemo(() => { return selectedConversationTitle ? conversationOptions.filter((c) => c.label === selectedConversationTitle) ?? [] : []; }, [conversationOptions, selectedConversationTitle]); const handleSelectionChange = useCallback( - (conversationSelectorSettingsOption: ConversationSelectorSettingsOption[]) => { + (conversationSelectorSettingsOption: EuiComboBoxOptionOption[]) => { const newConversation = conversationSelectorSettingsOption.length === 0 ? undefined @@ -129,7 +128,7 @@ export const ConversationSelectorSettings: React.FC = React.memo( // Callback for when a user selects a conversation const onChange = useCallback( - (newOptions: ConversationSelectorSettingsOption[]) => { + (newOptions: EuiComboBoxOptionOption[]) => { if (newOptions.length === 0) { handleSelectionChange([]); } else if (conversationOptions.findIndex((o) => o.label === newOptions?.[0].label) !== -1) { @@ -163,11 +162,11 @@ export const ConversationSelectorSettings: React.FC = React.memo( }, [conversationTitles, selectedConversationTitle, conversationOptions, handleSelectionChange]); const renderOption: ( - option: ConversationSelectorSettingsOption, + option: EuiComboBoxOptionOption, searchValue: string, OPTION_CONTENT_CLASSNAME: string ) => React.ReactNode = (option, searchValue) => { - const { label, value } = option; + const { label } = option; return ( = React.memo( {label} - {!value?.isDefault && ( - - - { - e.stopPropagation(); - onDelete(label); - }} - css={css` - visibility: hidden; - .parentFlexGroup:hover & { - visibility: visible; - } - `} - /> - - - )} + + + { + e.stopPropagation(); + onDelete(label); + }} + css={css` + visibility: hidden; + .parentFlexGroup:hover & { + visibility: visible; + } + `} + /> + + ); }; diff --git a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/conversations/conversation_selector_settings/types.ts b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/conversations/conversation_selector_settings/types.ts deleted file mode 100644 index 548149ffe0c7b..0000000000000 --- a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/conversations/conversation_selector_settings/types.ts +++ /dev/null @@ -1,12 +0,0 @@ -/* - * 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 { EuiComboBoxOptionOption } from '@elastic/eui'; - -export type ConversationSelectorSettingsOption = EuiComboBoxOptionOption<{ - isDefault: boolean; -}>; diff --git a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/conversations/conversation_settings/conversation_settings.test.tsx b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/conversations/conversation_settings/conversation_settings.test.tsx deleted file mode 100644 index 9897fc9dc3d97..0000000000000 --- a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/conversations/conversation_settings/conversation_settings.test.tsx +++ /dev/null @@ -1,343 +0,0 @@ -/* - * 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 from 'react'; -import { fireEvent, render } from '@testing-library/react'; -import { ConversationSettings, ConversationSettingsProps } from './conversation_settings'; -import { TestProviders } from '../../../mock/test_providers/test_providers'; -import { alertConvo, customConvo, welcomeConvo } from '../../../mock/conversation'; -import { mockSystemPrompts } from '../../../mock/system_prompt'; -import { mockConnectors } from '../../../mock/connectors'; -import { HttpSetup } from '@kbn/core/public'; - -const mockConvos = { - '1234': { ...welcomeConvo, id: '1234' }, - '12345': { ...alertConvo, id: '12345' }, - '123': { ...customConvo, id: '123' }, -}; -const onSelectedConversationChange = jest.fn(); - -const setConversationSettings = jest.fn(); -const setConversationsSettingsBulkActions = jest.fn(); - -const testProps: ConversationSettingsProps = { - allSystemPrompts: mockSystemPrompts, - assistantStreamingEnabled: false, - connectors: mockConnectors, - conversationSettings: mockConvos, - conversationsSettingsBulkActions: {}, - http: { basePath: { get: jest.fn() } } as unknown as HttpSetup, - onSelectedConversationChange, - selectedConversation: mockConvos['1234'], - setAssistantStreamingEnabled: jest.fn(), - setConversationSettings, - setConversationsSettingsBulkActions, -}; - -jest.mock('../../../connectorland/use_load_connectors', () => ({ - useLoadConnectors: () => ({ - data: mockConnectors, - error: null, - isSuccess: true, - }), -})); - -const mockConvo = mockConvos['12345']; -jest.mock('../conversation_selector_settings', () => ({ - // @ts-ignore - ConversationSelectorSettings: ({ onConversationDeleted, onConversationSelectionChange }) => ( - <> -