From 21a2d3f2d7ce4ea7ea53c25a8fd46f301939801e Mon Sep 17 00:00:00 2001 From: dal Date: Fri, 22 Aug 2025 10:05:07 -0600 Subject: [PATCH] Refactor report generation handling and UI updates - Simplified the `GeneratingContent` component to display a static loading message. - Updated `ReportPageController` to determine if a report is being generated based on the current message context. - Adjusted report status handling in the delta creation process to maintain 'loading' status during streaming. - Enhanced logic in the modify reports helper to ensure file status reflects loading during streaming and only updates to completed or failed when all edits are finalized. - Modified database query to preserve input order for new messages during updates. --- .../GeneratingContent.tsx | 15 +---- .../ReportPageController.tsx | 22 ++++++- .../create-reports-delta.ts | 64 +++---------------- .../modify-reports-transform-helper.ts | 36 +++++++---- .../messages/update-message-entries.ts | 7 +- 5 files changed, 62 insertions(+), 82 deletions(-) diff --git a/apps/web/src/controllers/ReportPageControllers/GeneratingContent.tsx b/apps/web/src/controllers/ReportPageControllers/GeneratingContent.tsx index b533f73dd..8d94dc176 100644 --- a/apps/web/src/controllers/ReportPageControllers/GeneratingContent.tsx +++ b/apps/web/src/controllers/ReportPageControllers/GeneratingContent.tsx @@ -1,7 +1,5 @@ -import { queryKeys } from '@/api/query_keys'; import { ShimmerText } from '@/components/ui/typography/ShimmerText'; import { cn } from '@/lib/classMerge'; -import { useQuery } from '@tanstack/react-query'; export const GeneratingContent = ({ messageId, @@ -10,18 +8,9 @@ export const GeneratingContent = ({ messageId: string; className?: string; }) => { - const { data: text } = useQuery({ - ...queryKeys.chatsBlackBoxMessages(messageId), - notifyOnChangeProps: ['data'], - select: (data) => data - }); - return ( -
-
- -
+
+
); }; diff --git a/apps/web/src/controllers/ReportPageControllers/ReportPageController.tsx b/apps/web/src/controllers/ReportPageControllers/ReportPageController.tsx index e5badb834..bf4ba7220 100644 --- a/apps/web/src/controllers/ReportPageControllers/ReportPageController.tsx +++ b/apps/web/src/controllers/ReportPageControllers/ReportPageController.tsx @@ -11,6 +11,9 @@ import { ReportEditorSkeleton } from '@/components/ui/report/ReportEditorSkeleto import { useChatIndividualContextSelector } from '@/layouts/ChatLayout/ChatContext'; import { useTrackAndUpdateReportChanges } from '@/api/buster-electric/reports/hooks'; import { GeneratingContent } from './GeneratingContent'; +import { useQuery } from '@tanstack/react-query'; +import { queryKeys } from '@/api/query_keys'; +import type { BusterChatMessage } from '@/api/asset_interfaces/chat'; export const ReportPageController: React.FC<{ reportId: string; @@ -24,8 +27,25 @@ export const ReportPageController: React.FC<{ const isStreamingMessage = useChatIndividualContextSelector((x) => x.isStreamingMessage); const messageId = useChatIndividualContextSelector((x) => x.currentMessageId); + // Fetch the current message to check which files are being generated + const { data: currentMessage } = useQuery({ + ...queryKeys.chatsMessages(messageId || ''), + enabled: !!messageId && isStreamingMessage + }); + + // Check if this specific report is being generated in the current message + const isThisReportBeingGenerated = React.useMemo(() => { + if (!currentMessage || !isStreamingMessage || !messageId) return false; + + // Check if the current report ID matches any file being generated + const responseMessages = Object.values(currentMessage.response_messages || {}); + return responseMessages.some( + (msg) => msg.type === 'file' && msg.file_type === 'report' && msg.id === reportId + ); + }, [currentMessage, isStreamingMessage, messageId, reportId]); + const content = report?.content || ''; - const showGeneratingContent = messageId && isStreamingMessage; + const showGeneratingContent = isThisReportBeingGenerated; const commonClassName = 'sm:px-[max(64px,calc(50%-350px))]'; const { mutate: updateReport } = useUpdateReport(); diff --git a/packages/ai/src/tools/visualization-tools/reports/create-reports-tool/create-reports-delta.ts b/packages/ai/src/tools/visualization-tools/reports/create-reports-tool/create-reports-delta.ts index 1af46b50f..3615cfea6 100644 --- a/packages/ai/src/tools/visualization-tools/reports/create-reports-tool/create-reports-delta.ts +++ b/packages/ai/src/tools/visualization-tools/reports/create-reports-tool/create-reports-delta.ts @@ -219,9 +219,6 @@ export function createCreateReportsDelta(context: CreateReportsContext, state: C // Update report content for all reports that have content if (contentUpdates.length > 0) { - // Track response messages to create in batch - const responseMessagesToCreate: ChatMessageResponseMessage[] = []; - for (const update of contentUpdates) { try { await updateReportContent({ @@ -229,37 +226,16 @@ export function createCreateReportsDelta(context: CreateReportsContext, state: C content: update.content, }); - // Mark the file as completed in state + // Keep the file status as 'loading' during streaming + // Status will be updated to 'completed' in the execute phase const stateFile = state.files?.find((f) => f.id === update.reportId); if (stateFile) { - stateFile.status = 'completed'; - - // Create response message for this report if not already created - if (!state.responseMessagesCreated?.has(update.reportId)) { - // Create response message for this report - responseMessagesToCreate.push({ - id: update.reportId, - type: 'file' as const, - file_type: 'report' as const, - file_name: stateFile.file_name || '', - version_number: stateFile.version_number || 1, - filter_version_id: null, - metadata: [ - { - status: 'completed' as const, - message: 'Report created successfully', - timestamp: Date.now(), - }, - ], - }); - - // Track that we've created a response message for this report - if (!state.responseMessagesCreated) { - state.responseMessagesCreated = new Set(); - } - state.responseMessagesCreated.add(update.reportId); - } + // Ensure status remains 'loading' during delta phase + stateFile.status = 'loading'; } + + // Note: Response messages should only be created in execute phase + // after all processing is complete } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Content update failed'; console.error('[create-reports] Error updating report content:', { @@ -268,35 +244,15 @@ export function createCreateReportsDelta(context: CreateReportsContext, state: C stack: error instanceof Error ? error.stack : undefined, }); - // Mark the file as failed in state with error message + // Keep file as loading during delta phase even on error + // The execute phase will handle final status const stateFile = state.files?.find((f) => f.id === update.reportId); if (stateFile) { - stateFile.status = 'failed'; + stateFile.status = 'loading'; stateFile.error = `Failed to update report content: ${errorMessage}`; } } } - - // Update database with response messages if we have any - if (responseMessagesToCreate.length > 0 && context.messageId) { - try { - await updateMessageEntries({ - messageId: context.messageId, - responseMessages: responseMessagesToCreate, - }); - - console.info('[create-reports] Created response messages during delta', { - count: responseMessagesToCreate.length, - reportIds: responseMessagesToCreate.map((m) => m.id), - }); - } catch (error) { - console.error( - '[create-reports] Error creating response messages during delta:', - error - ); - // Don't throw - continue processing - } - } } } } diff --git a/packages/ai/src/tools/visualization-tools/reports/modify-reports-tool/helpers/modify-reports-transform-helper.ts b/packages/ai/src/tools/visualization-tools/reports/modify-reports-tool/helpers/modify-reports-transform-helper.ts index 5e1841da4..d0dcb1402 100644 --- a/packages/ai/src/tools/visualization-tools/reports/modify-reports-tool/helpers/modify-reports-transform-helper.ts +++ b/packages/ai/src/tools/visualization-tools/reports/modify-reports-tool/helpers/modify-reports-transform-helper.ts @@ -46,15 +46,28 @@ export function createModifyReportsReasoningEntry( let status: 'loading' | 'completed' | 'failed' = 'loading'; let secondaryTitle: string | undefined; - // Check if modification is complete based on state - if (state.finalContent !== undefined) { + // Check if all edits have a final status (completed or failed), not just 'loading' + const allEditsComplete = + state.edits && state.edits.length > 0 + ? state.edits.every((edit) => edit.status === 'completed' || edit.status === 'failed') + : false; + + // Only mark as complete when all edits are actually done, not during streaming + if (allEditsComplete) { // Check if any edits failed const hasFailedEdits = state.edits?.some((edit) => edit.status === 'failed') ?? false; if (hasFailedEdits) { title = 'Failed to modify report'; status = 'failed'; - } else if (state.finalContent) { + // Update the file status in filesRecord + if (state.reportId) { + const file = filesRecord[state.reportId]; + if (file) { + file.status = 'failed'; + } + } + } else { title = 'Modified 1 report'; status = 'completed'; // Update the file status in filesRecord @@ -66,14 +79,15 @@ export function createModifyReportsReasoningEntry( } } - // Only show elapsed time when all edits are complete (not during streaming) - // Check if all edits have a final status (completed or failed), not just 'loading' - const allEditsComplete = - state.edits?.every((edit) => edit.status === 'completed' || edit.status === 'failed') ?? - false; - - if (allEditsComplete) { - secondaryTitle = formatElapsedTime(state.startTime); + // Show elapsed time when complete + secondaryTitle = formatElapsedTime(state.startTime); + } else { + // Keep file status as loading during streaming + if (state.reportId) { + const file = filesRecord[state.reportId]; + if (file) { + file.status = 'loading'; + } } } diff --git a/packages/database/src/queries/messages/update-message-entries.ts b/packages/database/src/queries/messages/update-message-entries.ts index 33fc746b2..0d09526c0 100644 --- a/packages/database/src/queries/messages/update-message-entries.ts +++ b/packages/database/src/queries/messages/update-message-entries.ts @@ -127,6 +127,7 @@ export async function updateMessageEntries({ WITH new_messages AS ( SELECT value, + ordinality as input_order, value->>'role' AS role, COALESCE( CASE @@ -138,7 +139,7 @@ export async function updateMessageEntries({ END, '' ) AS tool_calls - FROM jsonb_array_elements(${newData}::jsonb) AS value + FROM jsonb_array_elements(${newData}::jsonb) WITH ORDINALITY AS t(value, ordinality) ), existing_messages AS ( SELECT @@ -170,8 +171,8 @@ export async function updateMessageEntries({ WHERE n.role = e.role AND n.tool_calls = e.tool_calls ) UNION ALL - -- Add all new messages - SELECT n.value, 1000000 + row_number() OVER () AS ord + -- Add all new messages, preserving their input order + SELECT n.value, 1000000 + n.input_order AS ord FROM new_messages n ) combined )