"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.createNewConversation = createNewConversation; exports.useTimeline = useTimeline; var _common = require("@kbn/kibana-utils-plugin/common"); var _lodash = require("lodash"); var _react = require("react"); var _usePrevious = _interopRequireDefault(require("react-use/lib/usePrevious")); var _i18n = require("@kbn/i18n"); var _types = require("../../common/types"); var _i18n2 = require("../i18n"); var _get_assistant_setup_message = require("../service/get_assistant_setup_message"); var _get_timeline_items_from_conversation = require("../utils/get_timeline_items_from_conversation"); var _use_kibana = require("./use_kibana"); /* * 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. */ function createNewConversation({ contexts }) { return { '@timestamp': new Date().toISOString(), messages: [(0, _get_assistant_setup_message.getAssistantSetupMessage)({ contexts })], conversation: { title: _i18n2.EMPTY_CONVERSATION_TITLE }, labels: {}, numeric_labels: {}, public: false }; } function useTimeline({ messages, connectors, conversationId, currentUser, chatService, startedFrom, onChatUpdate, onChatComplete }) { const connectorId = connectors.selectedConnector; const hasConnector = !!connectorId; const { services: { notifications } } = (0, _use_kibana.useKibana)(); const conversationItems = (0, _react.useMemo)(() => { const items = (0, _get_timeline_items_from_conversation.getTimelineItemsfromConversation)({ currentUser, chatService, hasConnector, messages, startedFrom }); return items; }, [currentUser, chatService, hasConnector, messages, startedFrom]); const [subscription, setSubscription] = (0, _react.useState)(); const controllerRef = (0, _react.useRef)(new AbortController()); const [pendingMessage, setPendingMessage] = (0, _react.useState)(); const prevConversationId = (0, _usePrevious.default)(conversationId); (0, _react.useEffect)(() => { if (prevConversationId !== conversationId && pendingMessage !== null && pendingMessage !== void 0 && pendingMessage.error) { setPendingMessage(undefined); } }, [conversationId, pendingMessage === null || pendingMessage === void 0 ? void 0 : pendingMessage.error, prevConversationId]); function chat(nextMessages) { const controller = new AbortController(); return new Promise((resolve, reject) => { var _lastMessage$message$; if (!connectorId) { reject(new Error('Can not add a message without a connector')); return; } onChatUpdate(nextMessages); const lastMessage = (0, _lodash.last)(nextMessages); if (lastMessage !== null && lastMessage !== void 0 && (_lastMessage$message$ = lastMessage.message.function_call) !== null && _lastMessage$message$ !== void 0 && _lastMessage$message$.name) { // the user has edited a function suggestion, no need to talk to resolve(undefined); return; } const response$ = chatService.chat({ messages: nextMessages, connectorId }); let pendingMessageLocal = pendingMessage; const nextSubscription = response$.subscribe({ next: nextPendingMessage => { pendingMessageLocal = nextPendingMessage; setPendingMessage(() => nextPendingMessage); }, error: reject, complete: () => { var _pendingMessageLocal; const error = (_pendingMessageLocal = pendingMessageLocal) === null || _pendingMessageLocal === void 0 ? void 0 : _pendingMessageLocal.error; if (error) { notifications.toasts.addError(error, { title: _i18n.i18n.translate('xpack.observabilityAiAssistant.failedToLoadResponse', { defaultMessage: 'Failed to load response from the AI Assistant' }) }); } resolve(pendingMessageLocal); } }); setSubscription(() => { controllerRef.current = controller; return nextSubscription; }); }).then(async reply => { var _lastMessage$message$2; if (reply !== null && reply !== void 0 && reply.error) { return nextMessages; } if (reply !== null && reply !== void 0 && reply.aborted) { return nextMessages; } setPendingMessage(undefined); const messagesAfterChat = reply ? nextMessages.concat({ '@timestamp': new Date().toISOString(), message: { ...reply.message } }) : nextMessages; onChatUpdate(messagesAfterChat); const lastMessage = (0, _lodash.last)(messagesAfterChat); if (lastMessage !== null && lastMessage !== void 0 && (_lastMessage$message$2 = lastMessage.message.function_call) !== null && _lastMessage$message$2 !== void 0 && _lastMessage$message$2.name) { const name = lastMessage.message.function_call.name; try { const message = await chatService.executeFunction({ name, args: lastMessage.message.function_call.arguments, messages: messagesAfterChat.slice(0, -1), signal: controller.signal }); return await chat(messagesAfterChat.concat({ '@timestamp': new Date().toISOString(), message: { name, role: _types.MessageRole.User, content: JSON.stringify(message.content), data: JSON.stringify(message.data) } })); } catch (error) { return await chat(messagesAfterChat.concat({ '@timestamp': new Date().toISOString(), message: { role: _types.MessageRole.User, name, content: JSON.stringify({ message: error.toString(), error }) } })); } } return messagesAfterChat; }); } const items = (0, _react.useMemo)(() => { if (pendingMessage) { const nextItems = conversationItems.concat({ id: '', actions: { canCopy: true, canEdit: false, canGiveFeedback: false, canRegenerate: pendingMessage.aborted || !!pendingMessage.error }, display: { collapsed: false, hide: pendingMessage.message.role === _types.MessageRole.System }, content: pendingMessage.message.content, currentUser, error: pendingMessage.error, function_call: pendingMessage.message.function_call, loading: !pendingMessage.aborted && !pendingMessage.error, role: pendingMessage.message.role, title: '' }); return nextItems; } return conversationItems; }, [conversationItems, pendingMessage, currentUser]); (0, _react.useEffect)(() => { return () => { subscription === null || subscription === void 0 ? void 0 : subscription.unsubscribe(); }; }, [subscription]); return { items, onEdit: async (item, newMessage) => { const indexOf = items.indexOf(item); const sliced = messages.slice(0, indexOf - 1); const nextMessages = await chat(sliced.concat(newMessage)); onChatComplete(nextMessages); }, onFeedback: (item, feedback) => {}, onRegenerate: item => { const indexOf = items.indexOf(item); chat(messages.slice(0, indexOf - 1)).then(nextMessages => onChatComplete(nextMessages)); }, onStopGenerating: () => { subscription === null || subscription === void 0 ? void 0 : subscription.unsubscribe(); setPendingMessage(prevPendingMessage => ({ message: { role: _types.MessageRole.Assistant, ...(prevPendingMessage === null || prevPendingMessage === void 0 ? void 0 : prevPendingMessage.message) }, aborted: true, error: new _common.AbortError() })); setSubscription(undefined); }, onSubmit: async message => { const nextMessages = await chat(messages.concat(message)); onChatComplete(nextMessages); } }; }