Merge branch 'big-nate/bus-939-create-new-structure-for-chats' into nate/create-component-library

This commit is contained in:
Nate Kelley 2025-02-28 11:53:23 -07:00
commit e407b10df8
No known key found for this signature in database
GPG Key ID: FD90372AB8D98B4F
33 changed files with 59 additions and 58 deletions

View File

@ -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
}[];
};

View File

@ -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';

View File

@ -1,4 +1,4 @@
import { AppMaterialIcons } from '@/components/ui';
import { AppMaterialIcons } from '@/components/ui/icons';
import { Button } from 'antd';
import React from 'react';

View File

@ -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[];

View File

@ -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';

View File

@ -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 = {

View File

@ -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 {

View File

@ -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;
}

View File

@ -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

View 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}
/>
))}

View File

@ -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>

View File

@ -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;

View File

@ -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}
/>

View File

@ -110,6 +110,7 @@ const fallbackToFileChatMessage = ({
return {
request_message: null,
reasoning: [],
final_reasoning_message: null,
response_messages: [
{
id: 'init',

View File

@ -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);
};