mirror of https://github.com/buster-so/buster.git
Merge branch 'big-nate/bus-939-create-new-structure-for-chats' into nate/create-component-library
This commit is contained in:
commit
e407b10df8
|
@ -6,6 +6,7 @@ export type BusterChatMessage = {
|
|||
response_messages: BusterChatMessageResponse[];
|
||||
reasoning: BusterChatMessageReasoning[];
|
||||
created_at: string;
|
||||
final_reasoning_message: string | null;
|
||||
};
|
||||
|
||||
export type BusterChatMessageRequest = null | {
|
||||
|
@ -43,36 +44,37 @@ export type BusterChatMessage_file = {
|
|||
};
|
||||
|
||||
export type BusterChatMessageReasoning =
|
||||
| BusterChatMessageReasoning_thought
|
||||
| BusterChatMessageReasoning_pills
|
||||
| BusterChatMessageReasoning_text
|
||||
| BusterChatMessageReasoning_file;
|
||||
|
||||
export type BusterChatMessageReasoning_thoughtPill = {
|
||||
export type BusterChatMessageReasoning_pillsPill = {
|
||||
text: string;
|
||||
type: ThoughtFileType;
|
||||
type: ThoughtFileType | null; //if null then the pill will not link anywhere
|
||||
id: string;
|
||||
};
|
||||
|
||||
export type BusterChatMessageReasoning_thoughtPillContainer = {
|
||||
export type BusterChatMessageReasoning_pillsPillContainer = {
|
||||
title: string;
|
||||
thought_pills: BusterChatMessageReasoning_thoughtPill[];
|
||||
thought_pills: BusterChatMessageReasoning_pillsPill[];
|
||||
};
|
||||
|
||||
export type BusterChatMessageReasoning_status = 'loading' | 'completed' | 'failed';
|
||||
|
||||
export type BusterChatMessageReasoning_thought = {
|
||||
export type BusterChatMessageReasoning_pills = {
|
||||
id: string;
|
||||
type: 'thought';
|
||||
thought_title: string;
|
||||
thought_secondary_title: string;
|
||||
thoughts?: BusterChatMessageReasoning_thoughtPillContainer[];
|
||||
type: 'pills';
|
||||
title: string;
|
||||
secondary_title: string;
|
||||
pill_containers?: BusterChatMessageReasoning_pillsPillContainer[];
|
||||
status?: BusterChatMessageReasoning_status; //if left undefined, will automatically be set to 'loading' if the chat stream is in progress AND there is no message after it
|
||||
};
|
||||
|
||||
export type BusterChatMessageReasoning_text = {
|
||||
id: string;
|
||||
type: 'text';
|
||||
message: string;
|
||||
title: string;
|
||||
message?: string;
|
||||
message_chunk?: string;
|
||||
status?: BusterChatMessageReasoning_status;
|
||||
};
|
||||
|
@ -85,10 +87,9 @@ export type BusterChatMessageReasoning_file = {
|
|||
version_number: number;
|
||||
version_id: string;
|
||||
status?: BusterChatMessageReasoning_status;
|
||||
//when we are streaming, the whole file will always be streamed back, not chunks
|
||||
file?: {
|
||||
text: string;
|
||||
line_number: number;
|
||||
modified?: boolean; //defaults to true
|
||||
}[]; //will be defined if the file has been completed OR on a page refresh
|
||||
modified?: boolean; //only toggle to true if we want to hide previous lines
|
||||
}[];
|
||||
};
|
||||
|
|
|
@ -6,10 +6,7 @@ import { StatusBadgeButton } from '../../../../components/features/lists';
|
|||
import { VerificationStatus } from '@/api/asset_interfaces';
|
||||
import { useBusterMetricsIndividualContextSelector } from '@/context/Metrics';
|
||||
import { useUserConfigContextSelector } from '@/context/Users';
|
||||
import {
|
||||
useBusterCollectionIndividualContextSelector,
|
||||
useBusterCollectionListContextSelector
|
||||
} from '@/context/Collections';
|
||||
import { useBusterCollectionListContextSelector } from '@/context/Collections';
|
||||
import { useMemoizedFn, useMount } from 'ahooks';
|
||||
import { SaveToCollectionsDropdown } from '@/components/features/dropdowns/SaveToCollectionsDropdown';
|
||||
import { useBusterNotifications } from '@/context/BusterNotifications';
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { AppMaterialIcons } from '@/components/ui';
|
||||
import { AppMaterialIcons } from '@/components/ui/icons';
|
||||
import { Button } from 'antd';
|
||||
import React from 'react';
|
||||
|
|
@ -1,10 +1,10 @@
|
|||
import React, { useState } from 'react';
|
||||
import { SaveToCollectionsDropdown } from '../dropdowns/SaveToCollectionsDropdown';
|
||||
import { useMemoizedFn, useMount } from 'ahooks';
|
||||
import { useMemoizedFn } from 'ahooks';
|
||||
import { useBusterNotifications } from '@/context/BusterNotifications';
|
||||
import { useBusterCollectionListContextSelector } from '@/context/Collections';
|
||||
import { useBusterDashboardContextSelector } from '@/context/Dashboards';
|
||||
import { CollectionButton } from './CollectionButton';
|
||||
import { CollectionButton } from './CollectionsButton';
|
||||
|
||||
export const SaveDashboardToCollectionButton: React.FC<{
|
||||
dashboardIds: string[];
|
|
@ -1,6 +1,5 @@
|
|||
import { useBusterDashboardListByFilter } from '@/context/Dashboards';
|
||||
import { useBusterMetricsIndividualContextSelector } from '@/context/Metrics';
|
||||
import { useMemoizedFn, useMount } from 'ahooks';
|
||||
import { useMemoizedFn } from 'ahooks';
|
||||
import React from 'react';
|
||||
import { SaveToDashboardDropdown } from '../dropdowns/SaveToDashboardDropdown';
|
||||
import { Button } from 'antd';
|
|
@ -13,7 +13,6 @@ import { NoDatasets } from './NoDatasets';
|
|||
import { useAppLayoutContextSelector } from '@/context/BusterAppLayout';
|
||||
import { useGetDatasets } from '@/api/buster_rest/datasets';
|
||||
import { NewDatasetModal } from '../NewDatasetModal';
|
||||
|
||||
const { TextArea } = Input;
|
||||
|
||||
const themeConfig: ThemeConfig = {
|
||||
|
|
|
@ -5,8 +5,8 @@ import {
|
|||
type BusterChatMessageRequest,
|
||||
BusterChatMessage_fileMetadata,
|
||||
BusterChatMessageReasoning_pills,
|
||||
BusterChatMessageReasoning_thoughtPill,
|
||||
BusterChatMessageReasoning_file
|
||||
BusterChatMessageReasoning_file,
|
||||
BusterChatMessageReasoning_pillsPill
|
||||
} from '@/api/asset_interfaces';
|
||||
import { faker } from '@faker-js/faker';
|
||||
|
||||
|
@ -31,7 +31,7 @@ export const createMockResponseMessageText = (): BusterChatMessage_text => ({
|
|||
|
||||
export const createMockResponseMessageThought = (): BusterChatMessageReasoning_pills => {
|
||||
const randomPillCount = faker.number.int({ min: 0, max: 10 });
|
||||
const fourRandomPills: BusterChatMessageReasoning_thoughtPill[] = Array.from(
|
||||
const fourRandomPills: BusterChatMessageReasoning_pillsPill[] = Array.from(
|
||||
{ length: randomPillCount },
|
||||
() => {
|
||||
return {
|
||||
|
|
|
@ -14,7 +14,7 @@ export const ReasoningMessageContainer: React.FC<{
|
|||
const typeClassRecord: Record<BusterChatMessageReasoning['type'], string> = useMemo(() => {
|
||||
return {
|
||||
text: cx(styles.textCard, 'text-card'),
|
||||
pills: cx(styles.thoughtCard, 'pills-card'),
|
||||
pills: cx(styles.pillsCard, 'pills-card'),
|
||||
file: cx(styles.fileCard, 'file-card')
|
||||
};
|
||||
}, []);
|
||||
|
@ -60,7 +60,7 @@ const useStyles = createStyles(({ token, css }) => ({
|
|||
display: none;
|
||||
}
|
||||
`,
|
||||
thoughtCard: css`
|
||||
pillsCard: css`
|
||||
.vertical-divider {
|
||||
display: none;
|
||||
}
|
||||
|
|
|
@ -3,8 +3,8 @@ import type {
|
|||
BusterChatMessageReasoning,
|
||||
BusterChatMessageReasoning_text
|
||||
} from '@/api/asset_interfaces';
|
||||
import { ReasoningMessage_Thought } from './ReasoningMessage_Thought';
|
||||
import { StreamingMessage_Text } from '@/components/ui/streaming/StreamingMessage_Text';
|
||||
import { ReasoningMessage_Thought } from './ReasoningMessage_Thought';
|
||||
import { ReasoningMessage_File } from './ReasoningMessage_File';
|
||||
|
||||
export interface ReasoningMessageProps {
|
||||
|
@ -18,11 +18,11 @@ const ReasoningMessageRecord: Record<
|
|||
BusterChatMessageReasoning['type'],
|
||||
React.FC<ReasoningMessageProps>
|
||||
> = {
|
||||
thought: ReasoningMessage_Thought,
|
||||
pills: ReasoningMessage_Thought,
|
||||
text: (props) => (
|
||||
<StreamingMessage_Text
|
||||
{...props}
|
||||
message={(props.reasoningMessage as BusterChatMessageReasoning_text).message}
|
||||
message={(props.reasoningMessage as BusterChatMessageReasoning_text).message ?? ''}
|
||||
/>
|
||||
),
|
||||
file: ReasoningMessage_File
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
import type { BusterChatMessageReasoning_thought } from '@/api/asset_interfaces';
|
||||
import React from 'react';
|
||||
import type { BusterChatMessageReasoning_pills } from '@/api/asset_interfaces';
|
||||
import { ReasoningMessageProps } from '../ReasoningMessageSelector';
|
||||
import { ReasoningMessage_ThoughtContainer } from './ReasoningMessage_ThoughtContainer';
|
||||
import { BarContainer } from '../BarContainer';
|
||||
|
||||
export const ReasoningMessage_Thought: React.FC<ReasoningMessageProps> = React.memo(
|
||||
({ reasoningMessage, isCompletedStream, isLastMessageItem }) => {
|
||||
const { thought_title, thought_secondary_title, thoughts, status, id } =
|
||||
reasoningMessage as BusterChatMessageReasoning_thought;
|
||||
const { title, secondary_title, pill_containers, status, id } =
|
||||
reasoningMessage as BusterChatMessageReasoning_pills;
|
||||
|
||||
const hasThoughts = !!thoughts && thoughts.length > 0;
|
||||
const loadingStatus: NonNullable<BusterChatMessageReasoning_thought['status']> =
|
||||
const hasThoughts = !!pill_containers && pill_containers.length > 0;
|
||||
const loadingStatus: NonNullable<BusterChatMessageReasoning_pills['status']> =
|
||||
(status ?? (isLastMessageItem && !isCompletedStream)) ? status || 'loading' : 'completed';
|
||||
|
||||
return (
|
||||
|
@ -18,14 +18,14 @@ export const ReasoningMessage_Thought: React.FC<ReasoningMessageProps> = React.m
|
|||
showBar={hasThoughts || !isLastMessageItem}
|
||||
status={loadingStatus}
|
||||
isCompletedStream={isCompletedStream}
|
||||
title={thought_title}
|
||||
secondaryTitle={thought_secondary_title}
|
||||
title={title}
|
||||
secondaryTitle={secondary_title}
|
||||
contentClassName="mb-3">
|
||||
{hasThoughts &&
|
||||
thoughts.map((thought, index) => (
|
||||
pill_containers.map((pill_container, index) => (
|
||||
<ReasoningMessage_ThoughtContainer
|
||||
key={index}
|
||||
thought={thought}
|
||||
pillContainer={pill_container}
|
||||
isCompletedStream={isCompletedStream}
|
||||
/>
|
||||
))}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { BusterChatMessageReasoning_thoughtPillContainer } from '@/api/asset_interfaces';
|
||||
import { BusterChatMessageReasoning_pillsPillContainer } from '@/api/asset_interfaces';
|
||||
import React from 'react';
|
||||
import { Text } from '@/components/ui';
|
||||
import { PillContainer } from './ReasoningMessage_ThoughtPills';
|
||||
|
@ -6,17 +6,20 @@ import { itemAnimationConfig } from '../animationConfig';
|
|||
import { AnimatePresence, motion } from 'framer-motion';
|
||||
|
||||
export const ReasoningMessage_ThoughtContainer: React.FC<{
|
||||
thought: BusterChatMessageReasoning_thoughtPillContainer;
|
||||
pillContainer: BusterChatMessageReasoning_pillsPillContainer;
|
||||
isCompletedStream: boolean;
|
||||
}> = React.memo(({ thought, isCompletedStream }) => {
|
||||
}> = React.memo(({ pillContainer, isCompletedStream }) => {
|
||||
return (
|
||||
<AnimatePresence initial={!isCompletedStream}>
|
||||
<motion.div {...itemAnimationConfig}>
|
||||
<div className="flex flex-col space-y-1">
|
||||
<Text size="xs" type="tertiary">
|
||||
{thought.title}
|
||||
{pillContainer.title}
|
||||
</Text>
|
||||
<PillContainer pills={thought.thought_pills} isCompletedStream={isCompletedStream} />
|
||||
<PillContainer
|
||||
pills={pillContainer.thought_pills}
|
||||
isCompletedStream={isCompletedStream}
|
||||
/>
|
||||
</div>
|
||||
</motion.div>
|
||||
</AnimatePresence>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import type { BusterChatMessageReasoning_thoughtPill } from '@/api/asset_interfaces';
|
||||
import type { BusterChatMessageReasoning_pillsPill } from '@/api/asset_interfaces';
|
||||
import { createStyles } from 'antd-style';
|
||||
import React, { useMemo } from 'react';
|
||||
import { AnimatePresence, motion } from 'framer-motion';
|
||||
|
@ -36,7 +36,7 @@ const pillVariants = {
|
|||
};
|
||||
|
||||
export const PillContainer: React.FC<{
|
||||
pills: BusterChatMessageReasoning_thoughtPill[];
|
||||
pills: BusterChatMessageReasoning_pillsPill[];
|
||||
isCompletedStream: boolean;
|
||||
}> = React.memo(({ pills = [], isCompletedStream }) => {
|
||||
const { cx } = useStyles();
|
||||
|
@ -45,7 +45,7 @@ export const PillContainer: React.FC<{
|
|||
const useAnimation = !isCompletedStream;
|
||||
|
||||
const handlePillClick = useMemoizedFn(
|
||||
(pill: Pick<BusterChatMessageReasoning_thoughtPill, 'id' | 'type'>) => {
|
||||
(pill: Pick<BusterChatMessageReasoning_pillsPill, 'id' | 'type'>) => {
|
||||
if (isOpenableFile(pill.type)) {
|
||||
onSetSelectedFile(pill as SelectedFile);
|
||||
}
|
||||
|
@ -78,10 +78,10 @@ PillContainer.displayName = 'PillContainer';
|
|||
const Pill: React.FC<{
|
||||
text: string;
|
||||
id?: string;
|
||||
type?: BusterChatMessageReasoning_thoughtPill['type'];
|
||||
type?: BusterChatMessageReasoning_pillsPill['type'];
|
||||
useAnimation: boolean;
|
||||
className?: string;
|
||||
onClick?: (pill: Pick<BusterChatMessageReasoning_thoughtPill, 'id' | 'type'>) => void;
|
||||
onClick?: (pill: Pick<BusterChatMessageReasoning_pillsPill, 'id' | 'type'>) => void;
|
||||
}> = React.memo(({ text, type, id, useAnimation, className = '', onClick }) => {
|
||||
const { styles, cx } = useStyles();
|
||||
return (
|
||||
|
@ -110,9 +110,9 @@ const OverflowPill = React.memo(
|
|||
useAnimation,
|
||||
onClickPill
|
||||
}: {
|
||||
hiddenPills: BusterChatMessageReasoning_thoughtPill[];
|
||||
hiddenPills: BusterChatMessageReasoning_pillsPill[];
|
||||
useAnimation: boolean;
|
||||
onClickPill: (pill: Pick<BusterChatMessageReasoning_thoughtPill, 'id' | 'type'>) => void;
|
||||
onClickPill: (pill: Pick<BusterChatMessageReasoning_pillsPill, 'id' | 'type'>) => void;
|
||||
}) => {
|
||||
const count = hiddenPills.length;
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { BusterChatMessageReasoning } from '@/api/asset_interfaces';
|
||||
import type { BusterChatMessageReasoning } from '@/api/asset_interfaces';
|
||||
import React, { useMemo } from 'react';
|
||||
import last from 'lodash/last';
|
||||
import { ShimmerText } from '@/components/ui';
|
||||
|
@ -31,8 +31,8 @@ export const ChatResponseReasoning: React.FC<{
|
|||
switch (lastMessage.type) {
|
||||
case 'text':
|
||||
return lastMessage.message;
|
||||
case 'thought':
|
||||
return lastMessage.thought_title;
|
||||
case 'pills':
|
||||
return lastMessage.title;
|
||||
case 'file':
|
||||
return lastMessage.file_name;
|
||||
default:
|
||||
|
@ -52,7 +52,7 @@ export const ChatResponseReasoning: React.FC<{
|
|||
<AnimatePresence initial={!isCompletedStream} mode="wait">
|
||||
<motion.div {...animations} key={text} className="mb-3.5 w-fit" onClick={onClickReasoning}>
|
||||
<ShimmerTextWithIcon
|
||||
text={text}
|
||||
text={text ?? ''}
|
||||
isCompletedStream={isCompletedStream}
|
||||
isSelected={isReasonginFileSelected}
|
||||
/>
|
||||
|
|
|
@ -110,6 +110,7 @@ const fallbackToFileChatMessage = ({
|
|||
return {
|
||||
request_message: null,
|
||||
reasoning: [],
|
||||
final_reasoning_message: null,
|
||||
response_messages: [
|
||||
{
|
||||
id: 'init',
|
||||
|
|
|
@ -2,6 +2,7 @@ import type { ThoughtFileType } from '@/api/asset_interfaces';
|
|||
|
||||
const OPENABLE_FILES = new Set<string>(['metric', 'dashboard', 'reasoning']);
|
||||
|
||||
export const isOpenableFile = (type: ThoughtFileType): boolean => {
|
||||
export const isOpenableFile = (type: ThoughtFileType | null): boolean => {
|
||||
if (!type) return false;
|
||||
return OPENABLE_FILES.has(type);
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue