mirror of https://github.com/buster-so/buster.git
start chat from asset 🦚
This commit is contained in:
parent
fec77bda4b
commit
6c994bb9e8
|
@ -8,7 +8,8 @@ import {
|
||||||
updateChat,
|
updateChat,
|
||||||
deleteChat,
|
deleteChat,
|
||||||
getListLogs,
|
getListLogs,
|
||||||
duplicateChat
|
duplicateChat,
|
||||||
|
startChatFromAsset
|
||||||
} from './requests';
|
} from './requests';
|
||||||
import type { IBusterChat, IBusterChatMessage } from '@/api/asset_interfaces/chat';
|
import type { IBusterChat, IBusterChatMessage } from '@/api/asset_interfaces/chat';
|
||||||
import { queryKeys } from '@/api/query_keys';
|
import { queryKeys } from '@/api/query_keys';
|
||||||
|
@ -78,7 +79,6 @@ export const useGetChat = <TData = IBusterChat>(
|
||||||
const queryFn = useMemoizedFn(() => {
|
const queryFn = useMemoizedFn(() => {
|
||||||
return getChat(params).then((chat) => {
|
return getChat(params).then((chat) => {
|
||||||
const { iChat, iChatMessages } = updateChatToIChat(chat, false);
|
const { iChat, iChatMessages } = updateChatToIChat(chat, false);
|
||||||
|
|
||||||
const lastMessageId = last(iChat.message_ids);
|
const lastMessageId = last(iChat.message_ids);
|
||||||
const lastMessage = iChatMessages[lastMessageId!];
|
const lastMessage = iChatMessages[lastMessageId!];
|
||||||
if (lastMessage) {
|
if (lastMessage) {
|
||||||
|
@ -108,6 +108,32 @@ export const useGetChat = <TData = IBusterChat>(
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const useStartChatFromAsset = () => {
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
|
const mutationFn = useMemoizedFn(async (params: Parameters<typeof startChatFromAsset>[0]) => {
|
||||||
|
const chat = await startChatFromAsset(params);
|
||||||
|
const { iChat, iChatMessages } = updateChatToIChat(chat, false);
|
||||||
|
iChat.message_ids.forEach((messageId) => {
|
||||||
|
queryClient.setQueryData(
|
||||||
|
queryKeys.chatsMessages(messageId).queryKey,
|
||||||
|
iChatMessages[messageId]
|
||||||
|
);
|
||||||
|
});
|
||||||
|
queryClient.setQueryData(queryKeys.chatsGetChat(chat.id).queryKey, iChat);
|
||||||
|
return iChat;
|
||||||
|
});
|
||||||
|
|
||||||
|
return useMutation({
|
||||||
|
mutationFn,
|
||||||
|
onSuccess: (chat) => {
|
||||||
|
queryClient.invalidateQueries({
|
||||||
|
queryKey: queryKeys.chatsGetList().queryKey
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
export const prefetchGetChat = async (
|
export const prefetchGetChat = async (
|
||||||
params: Parameters<typeof getChat>[0],
|
params: Parameters<typeof getChat>[0],
|
||||||
queryClientProp?: QueryClient
|
queryClientProp?: QueryClient
|
||||||
|
|
|
@ -75,3 +75,18 @@ export const duplicateChat = async ({
|
||||||
}): Promise<BusterChat> => {
|
}): Promise<BusterChat> => {
|
||||||
return mainApi.post(`${CHATS_BASE}/duplicate`, { id, message_id }).then((res) => res.data);
|
return mainApi.post(`${CHATS_BASE}/duplicate`, { id, message_id }).then((res) => res.data);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const startChatFromAsset = async ({
|
||||||
|
asset_id,
|
||||||
|
asset_type
|
||||||
|
}: {
|
||||||
|
asset_id: string;
|
||||||
|
asset_type: 'metric' | 'dashboard';
|
||||||
|
}): Promise<BusterChat> => {
|
||||||
|
return mainApi
|
||||||
|
.post(`${CHATS_BASE}`, {
|
||||||
|
asset_id,
|
||||||
|
asset_type
|
||||||
|
})
|
||||||
|
.then((res) => res.data);
|
||||||
|
};
|
||||||
|
|
|
@ -171,7 +171,7 @@ export const useUpdateDashboardConfig = (params?: {
|
||||||
saveToServer?: boolean;
|
saveToServer?: boolean;
|
||||||
updateVersion?: boolean;
|
updateVersion?: boolean;
|
||||||
}) => {
|
}) => {
|
||||||
const { saveToServer = false, updateVersion = true } = params || {};
|
const { saveToServer = false, updateVersion = false } = params || {};
|
||||||
const { mutateAsync } = useUpdateDashboard({
|
const { mutateAsync } = useUpdateDashboard({
|
||||||
saveToServer,
|
saveToServer,
|
||||||
updateVersion
|
updateVersion
|
||||||
|
|
|
@ -296,7 +296,11 @@ export const DropdownContent = <T,>({
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{footerContent && <div className={cn('border-t p-1', footerClassName)}>{footerContent}</div>}
|
{footerContent && (
|
||||||
|
<div className={cn(hasShownItem && 'border-t', 'p-1', footerClassName)}>
|
||||||
|
{footerContent}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -23,7 +23,6 @@ export const useUpdateMetricChart = (props?: { metricId?: string }) => {
|
||||||
saveToServer: false
|
saveToServer: false
|
||||||
});
|
});
|
||||||
const { mutateAsync: saveMetricToServer } = useUpdateMetric({
|
const { mutateAsync: saveMetricToServer } = useUpdateMetric({
|
||||||
updateVersion: true,
|
|
||||||
updateOnSave: true,
|
updateOnSave: true,
|
||||||
saveToServer: true
|
saveToServer: true
|
||||||
});
|
});
|
||||||
|
|
|
@ -52,9 +52,7 @@ export const MetricViewChart: React.FC<{
|
||||||
} = useGetMetricData({ id: metricId });
|
} = useGetMetricData({ id: metricId });
|
||||||
|
|
||||||
const { mutate: updateMetric } = useUpdateMetric({
|
const { mutate: updateMetric } = useUpdateMetric({
|
||||||
updateOnSave: false,
|
saveToServer: false
|
||||||
updateVersion: true,
|
|
||||||
saveToServer: true
|
|
||||||
});
|
});
|
||||||
const { name, description, time_frame, evaluation_score, evaluation_summary } = metric || {};
|
const { name, description, time_frame, evaluation_score, evaluation_summary } = metric || {};
|
||||||
|
|
||||||
|
|
|
@ -25,8 +25,7 @@ export const MetricViewFile: React.FC<{ metricId: string }> = React.memo(({ metr
|
||||||
} = useUpdateMetric({
|
} = useUpdateMetric({
|
||||||
updateOnSave: true,
|
updateOnSave: true,
|
||||||
saveToServer: true,
|
saveToServer: true,
|
||||||
updateVersion: true,
|
updateVersion: false
|
||||||
wait: 0
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const { isReadOnly } = useIsMetricReadOnly({
|
const { isReadOnly } = useIsMetricReadOnly({
|
||||||
|
|
|
@ -16,8 +16,7 @@ export const useMetricRunSQL = () => {
|
||||||
const { mutateAsync: stageMetric } = useUpdateMetric({
|
const { mutateAsync: stageMetric } = useUpdateMetric({
|
||||||
updateVersion: false,
|
updateVersion: false,
|
||||||
saveToServer: false,
|
saveToServer: false,
|
||||||
updateOnSave: false,
|
updateOnSave: false
|
||||||
wait: 0
|
|
||||||
});
|
});
|
||||||
const {
|
const {
|
||||||
mutateAsync: saveMetric,
|
mutateAsync: saveMetric,
|
||||||
|
@ -26,8 +25,7 @@ export const useMetricRunSQL = () => {
|
||||||
} = useUpdateMetric({
|
} = useUpdateMetric({
|
||||||
updateOnSave: true,
|
updateOnSave: true,
|
||||||
updateVersion: true,
|
updateVersion: true,
|
||||||
saveToServer: true,
|
saveToServer: true
|
||||||
wait: 0
|
|
||||||
});
|
});
|
||||||
const {
|
const {
|
||||||
mutateAsync: runSQLMutation,
|
mutateAsync: runSQLMutation,
|
||||||
|
|
|
@ -19,16 +19,19 @@ export const ChatResponseMessages: React.FC<ChatResponseMessagesProps> = React.m
|
||||||
(x) => x?.reasoning_message_ids?.[x.reasoning_message_ids.length - 1]
|
(x) => x?.reasoning_message_ids?.[x.reasoning_message_ids.length - 1]
|
||||||
);
|
);
|
||||||
const finalReasoningMessage = useGetChatMessage(messageId, (x) => x?.final_reasoning_message);
|
const finalReasoningMessage = useGetChatMessage(messageId, (x) => x?.final_reasoning_message);
|
||||||
|
const showReasoningMessage = !!lastReasoningMessageId || !isCompletedStream;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MessageContainer className="flex w-full flex-col space-y-3 overflow-hidden">
|
<MessageContainer className="flex w-full flex-col space-y-3 overflow-hidden">
|
||||||
<ChatResponseReasoning
|
{showReasoningMessage && (
|
||||||
reasoningMessageId={lastReasoningMessageId}
|
<ChatResponseReasoning
|
||||||
finalReasoningMessage={finalReasoningMessage}
|
reasoningMessageId={lastReasoningMessageId}
|
||||||
isCompletedStream={isCompletedStream}
|
finalReasoningMessage={finalReasoningMessage}
|
||||||
messageId={messageId}
|
isCompletedStream={isCompletedStream}
|
||||||
chatId={chatId}
|
messageId={messageId}
|
||||||
/>
|
chatId={chatId}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
{responseMessageIds.map((responseMessageId, index) => (
|
{responseMessageIds.map((responseMessageId, index) => (
|
||||||
<React.Fragment key={responseMessageId}>
|
<React.Fragment key={responseMessageId}>
|
||||||
|
|
|
@ -163,7 +163,7 @@ export const useLayoutConfig = ({
|
||||||
return 'chat';
|
return 'chat';
|
||||||
}
|
}
|
||||||
return 'file';
|
return 'file';
|
||||||
}, [selectedFileId]);
|
}, [selectedFileId, chatId]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (
|
if (
|
||||||
|
|
|
@ -1,29 +1,67 @@
|
||||||
import { Stars } from '@/components/ui/icons';
|
import { Stars } from '@/components/ui/icons';
|
||||||
import { Button } from '@/components/ui/buttons';
|
import { Button } from '@/components/ui/buttons';
|
||||||
import React from 'react';
|
import React, { useState } from 'react';
|
||||||
import { useChatLayoutContextSelector } from '../../ChatLayoutContext';
|
|
||||||
import { useHotkeys } from 'react-hotkeys-hook';
|
import { useHotkeys } from 'react-hotkeys-hook';
|
||||||
import { useMemoizedFn } from '@/hooks';
|
import { useMemoizedFn } from '@/hooks';
|
||||||
import { AppTooltip } from '@/components/ui/tooltip';
|
import { AppTooltip } from '@/components/ui/tooltip';
|
||||||
|
import { useStartChatFromAsset } from '@/api/buster_rest/chats/queryRequests';
|
||||||
|
import { useAppLayoutContextSelector } from '@/context/BusterAppLayout';
|
||||||
|
import { BusterRoutes } from '@/routes';
|
||||||
|
import { useChatLayoutContextSelector } from '../../ChatLayoutContext';
|
||||||
|
import { timeout } from '@/lib';
|
||||||
|
|
||||||
export const CreateChatButton = React.memo(() => {
|
export const CreateChatButton = React.memo(
|
||||||
const onCollapseFileClickPreflight = useMemoizedFn(() => {
|
({ assetId, assetType }: { assetId: string; assetType: 'metric' | 'dashboard' }) => {
|
||||||
// onCollapseFileClick(false);
|
const [loading, setLoading] = useState(false);
|
||||||
alert('TODO');
|
const { mutateAsync: startChatFromAsset, isPending } = useStartChatFromAsset();
|
||||||
});
|
const onChangePage = useAppLayoutContextSelector((x) => x.onChangePage);
|
||||||
|
const onSetFileView = useChatLayoutContextSelector((x) => x.onSetFileView);
|
||||||
|
|
||||||
useHotkeys('e', onCollapseFileClickPreflight, { preventDefault: true });
|
const onCreateFileClick = useMemoizedFn(async () => {
|
||||||
|
setLoading(true);
|
||||||
|
try {
|
||||||
|
const result = await startChatFromAsset({ asset_id: assetId, asset_type: assetType });
|
||||||
|
|
||||||
return (
|
if (assetType === 'metric') {
|
||||||
<AppTooltip title={'Start chat'} shortcuts={['e']}>
|
await onChangePage({
|
||||||
<Button
|
route: BusterRoutes.APP_CHAT_ID_METRIC_ID_CHART,
|
||||||
onClick={onCollapseFileClickPreflight}
|
metricId: assetId,
|
||||||
variant="black"
|
chatId: result.id
|
||||||
className="ml-1.5"
|
});
|
||||||
prefix={<Stars />}>
|
} else if (assetType === 'dashboard') {
|
||||||
Start chat
|
await onChangePage({
|
||||||
</Button>
|
route: BusterRoutes.APP_CHAT_ID_DASHBOARD_ID,
|
||||||
</AppTooltip>
|
dashboardId: assetId,
|
||||||
);
|
chatId: result.id
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
await timeout(250); //wait for the chat to load and the file to be selected
|
||||||
|
onSetFileView({
|
||||||
|
fileId: assetId,
|
||||||
|
fileView: 'chart'
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
//
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
useHotkeys('e', onCreateFileClick, { preventDefault: true });
|
||||||
|
|
||||||
|
return (
|
||||||
|
<AppTooltip title={'Start chat'} shortcuts={['e']} delayDuration={650}>
|
||||||
|
<Button
|
||||||
|
loading={isPending || loading}
|
||||||
|
onClick={onCreateFileClick}
|
||||||
|
variant="black"
|
||||||
|
className="ml-1.5"
|
||||||
|
prefix={<Stars />}>
|
||||||
|
Start chat
|
||||||
|
</Button>
|
||||||
|
</AppTooltip>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
CreateChatButton.displayName = 'CreateChatButton';
|
CreateChatButton.displayName = 'CreateChatButton';
|
||||||
|
|
|
@ -31,7 +31,7 @@ export const DashboardHeaderButtons: React.FC<{
|
||||||
{isEditor && <AddContentToDashboardButton />}
|
{isEditor && <AddContentToDashboardButton />}
|
||||||
<DashboardThreeDotMenu dashboardId={dashboardId} />
|
<DashboardThreeDotMenu dashboardId={dashboardId} />
|
||||||
<HideButtonContainer show>
|
<HideButtonContainer show>
|
||||||
<CreateChatButton />
|
<CreateChatButton assetId={dashboardId} assetType="dashboard" />
|
||||||
</HideButtonContainer>
|
</HideButtonContainer>
|
||||||
</FileButtonContainer>
|
</FileButtonContainer>
|
||||||
);
|
);
|
||||||
|
|
|
@ -50,7 +50,7 @@ export const DashboardThreeDotMenu = React.memo(({ dashboardId }: { dashboardId:
|
||||||
{ id: dashboardId },
|
{ id: dashboardId },
|
||||||
{ select: (x) => x.permission }
|
{ select: (x) => x.permission }
|
||||||
);
|
);
|
||||||
const isOwner = getIsEffectiveOwner(permission);
|
const isEffectiveOwner = getIsEffectiveOwner(permission);
|
||||||
const isFilter = canFilter(permission);
|
const isFilter = canFilter(permission);
|
||||||
const isEditor = canEdit(permission);
|
const isEditor = canEdit(permission);
|
||||||
|
|
||||||
|
@ -60,13 +60,13 @@ export const DashboardThreeDotMenu = React.memo(({ dashboardId }: { dashboardId:
|
||||||
isFilter && filterDashboardMenu,
|
isFilter && filterDashboardMenu,
|
||||||
isEditor && addContentToDashboardMenu,
|
isEditor && addContentToDashboardMenu,
|
||||||
{ type: 'divider' },
|
{ type: 'divider' },
|
||||||
isOwner && shareMenu,
|
isEffectiveOwner && shareMenu,
|
||||||
collectionSelectMenu,
|
collectionSelectMenu,
|
||||||
favoriteDashboard,
|
favoriteDashboard,
|
||||||
versionHistoryItems,
|
versionHistoryItems,
|
||||||
{ type: 'divider' },
|
{ type: 'divider' },
|
||||||
isEditor && renameDashboardMenu,
|
isEditor && renameDashboardMenu,
|
||||||
isOwner && deleteDashboardMenu
|
isEffectiveOwner && deleteDashboardMenu
|
||||||
].filter(Boolean) as DropdownItems,
|
].filter(Boolean) as DropdownItems,
|
||||||
[
|
[
|
||||||
filterDashboardMenu,
|
filterDashboardMenu,
|
||||||
|
|
|
@ -41,7 +41,7 @@ export const MetricContainerHeaderButtons: React.FC<FileContainerButtonsProps> =
|
||||||
{isEffectiveOwner && <ShareMetricButton metricId={metricId} />}
|
{isEffectiveOwner && <ShareMetricButton metricId={metricId} />}
|
||||||
<ThreeDotMenuButton metricId={metricId} />
|
<ThreeDotMenuButton metricId={metricId} />
|
||||||
<HideButtonContainer show={selectedLayout === 'file'}>
|
<HideButtonContainer show={selectedLayout === 'file'}>
|
||||||
<CreateChatButton />
|
<CreateChatButton assetId={metricId} assetType="metric" />
|
||||||
</HideButtonContainer>
|
</HideButtonContainer>
|
||||||
</FileButtonContainer>
|
</FileButtonContainer>
|
||||||
);
|
);
|
||||||
|
|
|
@ -104,7 +104,6 @@ export const ThreeDotMenuButton = React.memo(({ metricId }: { metricId: string }
|
||||||
deleteMetricMenu,
|
deleteMetricMenu,
|
||||||
downloadCSVMenu,
|
downloadCSVMenu,
|
||||||
downloadPNGMenu,
|
downloadPNGMenu,
|
||||||
metricId,
|
|
||||||
openSuccessMessage,
|
openSuccessMessage,
|
||||||
onSetSelectedFile,
|
onSetSelectedFile,
|
||||||
versionHistoryItems,
|
versionHistoryItems,
|
||||||
|
|
Loading…
Reference in New Issue