'use client'; import React from 'react'; import { createContext, useContextSelector } from 'use-context-selector'; import { useMemoizedFn } from '@/hooks'; import type { BusterSearchResult, FileType } from '@/api/asset_interfaces'; import { useBusterWebSocket } from '@/context/BusterWebSocket'; import { useChatStreamMessage } from './useChatStreamMessage'; import { useGetChatMemoized, useGetChatMessageMemoized } from '@/api/buster_rest/chats'; import { useChatUpdate } from './useChatUpdate'; import { create } from 'mutative'; export const useBusterNewChat = () => { const busterSocket = useBusterWebSocket(); const getChatMessageMemoized = useGetChatMessageMemoized(); const getChatMemoized = useGetChatMemoized(); const { onUpdateChat, onUpdateChatMessage } = useChatUpdate(); const { completeChatCallback, stopChatCallback, initializeNewChatCallback } = useChatStreamMessage(); const onSelectSearchAsset = useMemoizedFn(async (asset: BusterSearchResult) => { await new Promise((resolve) => setTimeout(resolve, 1000)); }); const onStartNewChat = useMemoizedFn( async ({ prompt, datasetId, metricId, dashboardId }: { prompt: string; datasetId?: string; metricId?: string; dashboardId?: string; }) => { const res = await busterSocket.emitAndOnce({ emitEvent: { route: '/chats/post', payload: { dataset_id: datasetId, //TODO: add selected dataset id prompt, metric_id: metricId, dashboard_id: dashboardId } }, responseEvent: { route: '/chats/post:initializeChat', callback: initializeNewChatCallback } }); busterSocket.once({ route: '/chats/post:complete', callback: completeChatCallback }); } ); const onStartChatFromFile = useMemoizedFn( async ({ prompt, fileId, fileType }: { prompt: string; fileId: string; fileType: FileType; }) => { onStartNewChat({ prompt, metricId: fileType === 'metric' ? fileId : undefined, dashboardId: fileType === 'dashboard' ? fileId : undefined }); } ); const onReplaceMessageInChat = useMemoizedFn( async ({ prompt, messageId, chatId }: { prompt: string; messageId: string; chatId: string; }) => { const currentChat = getChatMemoized(chatId); const currentMessage = getChatMessageMemoized(messageId); const currentRequestMessage = currentMessage?.request_message!; onUpdateChatMessage({ id: messageId, request_message: create(currentRequestMessage, (draft) => { draft.request = prompt; }), reasoning_message_ids: [], response_message_ids: [], reasoning_messages: {}, final_reasoning_message: null, isCompletedStream: false }); const messageIndex = currentChat?.message_ids.findIndex( (messageId) => messageId === messageId ); if (messageIndex && messageIndex !== -1) { console.log('oldMessageIds', currentChat?.message_ids); const updatedMessageIds = currentChat?.message_ids.slice(0, messageIndex + 1); console.log('stripping message ids', updatedMessageIds); onUpdateChat({ id: chatId, message_ids: updatedMessageIds }); } return; await busterSocket.emitAndOnce({ emitEvent: { route: '/chats/post', payload: { prompt, message_id: messageId, chat_id: chatId } }, responseEvent: { route: '/chats/post:complete', callback: completeChatCallback } }); } ); const onFollowUpChat = useMemoizedFn( async ({ prompt, chatId }: { prompt: string; chatId: string }) => { busterSocket.once({ route: '/chats/post:initializeChat', callback: initializeNewChatCallback }); await busterSocket.emitAndOnce({ emitEvent: { route: '/chats/post', payload: { prompt, chat_id: chatId } }, responseEvent: { route: '/chats/post:complete', callback: completeChatCallback } }); } ); const onStopChat = useMemoizedFn( ({ chatId, messageId }: { chatId: string; messageId: string }) => { busterSocket.emit({ route: '/chats/stop', payload: { id: chatId, message_id: messageId } }); stopChatCallback(chatId); } ); return { onStartNewChat, onSelectSearchAsset, onFollowUpChat, onStartChatFromFile, onReplaceMessageInChat, onStopChat }; }; export const BusterNewChatContext = createContext>( {} as ReturnType ); export const BusterNewChatProvider: React.FC<{ children: React.ReactNode; }> = ({ children }) => { const value = useBusterNewChat(); return {children}; }; export const useBusterNewChatContextSelector = ( selector: (state: ReturnType) => T ) => useContextSelector(BusterNewChatContext, selector);