mirror of https://github.com/buster-so/buster.git
Merge remote-tracking branch 'origin/staging' into add-report-to-collection-functionality
This commit is contained in:
commit
13a1d38437
|
@ -1,7 +1,5 @@
|
||||||
import { queryKeys } from '@/api/query_keys';
|
|
||||||
import { ShimmerText } from '@/components/ui/typography/ShimmerText';
|
import { ShimmerText } from '@/components/ui/typography/ShimmerText';
|
||||||
import { cn } from '@/lib/classMerge';
|
import { cn } from '@/lib/classMerge';
|
||||||
import { useQuery } from '@tanstack/react-query';
|
|
||||||
|
|
||||||
export const GeneratingContent = ({
|
export const GeneratingContent = ({
|
||||||
messageId,
|
messageId,
|
||||||
|
@ -10,18 +8,9 @@ export const GeneratingContent = ({
|
||||||
messageId: string;
|
messageId: string;
|
||||||
className?: string;
|
className?: string;
|
||||||
}) => {
|
}) => {
|
||||||
const { data: text } = useQuery({
|
|
||||||
...queryKeys.chatsBlackBoxMessages(messageId),
|
|
||||||
notifyOnChangeProps: ['data'],
|
|
||||||
select: (data) => data
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div className={cn('right-0 bottom-0 left-0 -mt-68', className)}>
|
||||||
className={cn('right-0 bottom-0 left-0 -mt-68 flex items-center justify-center', className)}>
|
<ShimmerText text="Generating..." className="text-lg" />
|
||||||
<div className="border-border item-center flex w-full justify-center rounded border px-8 py-1.5 shadow">
|
|
||||||
<ShimmerText text={text || 'Generating content...'} className="text-lg" />
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -11,6 +11,9 @@ import { ReportEditorSkeleton } from '@/components/ui/report/ReportEditorSkeleto
|
||||||
import { useChatIndividualContextSelector } from '@/layouts/ChatLayout/ChatContext';
|
import { useChatIndividualContextSelector } from '@/layouts/ChatLayout/ChatContext';
|
||||||
import { useTrackAndUpdateReportChanges } from '@/api/buster-electric/reports/hooks';
|
import { useTrackAndUpdateReportChanges } from '@/api/buster-electric/reports/hooks';
|
||||||
import { GeneratingContent } from './GeneratingContent';
|
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<{
|
export const ReportPageController: React.FC<{
|
||||||
reportId: string;
|
reportId: string;
|
||||||
|
@ -24,8 +27,25 @@ export const ReportPageController: React.FC<{
|
||||||
const isStreamingMessage = useChatIndividualContextSelector((x) => x.isStreamingMessage);
|
const isStreamingMessage = useChatIndividualContextSelector((x) => x.isStreamingMessage);
|
||||||
const messageId = useChatIndividualContextSelector((x) => x.currentMessageId);
|
const messageId = useChatIndividualContextSelector((x) => x.currentMessageId);
|
||||||
|
|
||||||
|
// Fetch the current message to check which files are being generated
|
||||||
|
const { data: currentMessage } = useQuery<BusterChatMessage>({
|
||||||
|
...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 content = report?.content || '';
|
||||||
const showGeneratingContent = messageId && isStreamingMessage;
|
const showGeneratingContent = isThisReportBeingGenerated;
|
||||||
const commonClassName = 'sm:px-[max(64px,calc(50%-350px))]';
|
const commonClassName = 'sm:px-[max(64px,calc(50%-350px))]';
|
||||||
|
|
||||||
const { mutate: updateReport } = useUpdateReport();
|
const { mutate: updateReport } = useUpdateReport();
|
||||||
|
|
|
@ -219,9 +219,6 @@ export function createCreateReportsDelta(context: CreateReportsContext, state: C
|
||||||
|
|
||||||
// Update report content for all reports that have content
|
// Update report content for all reports that have content
|
||||||
if (contentUpdates.length > 0) {
|
if (contentUpdates.length > 0) {
|
||||||
// Track response messages to create in batch
|
|
||||||
const responseMessagesToCreate: ChatMessageResponseMessage[] = [];
|
|
||||||
|
|
||||||
for (const update of contentUpdates) {
|
for (const update of contentUpdates) {
|
||||||
try {
|
try {
|
||||||
await updateReportContent({
|
await updateReportContent({
|
||||||
|
@ -229,37 +226,16 @@ export function createCreateReportsDelta(context: CreateReportsContext, state: C
|
||||||
content: update.content,
|
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);
|
const stateFile = state.files?.find((f) => f.id === update.reportId);
|
||||||
if (stateFile) {
|
if (stateFile) {
|
||||||
stateFile.status = 'completed';
|
// Ensure status remains 'loading' during delta phase
|
||||||
|
stateFile.status = 'loading';
|
||||||
|
}
|
||||||
|
|
||||||
// Create response message for this report if not already created
|
// Note: Response messages should only be created in execute phase
|
||||||
if (!state.responseMessagesCreated?.has(update.reportId)) {
|
// after all processing is complete
|
||||||
// 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<string>();
|
|
||||||
}
|
|
||||||
state.responseMessagesCreated.add(update.reportId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const errorMessage = error instanceof Error ? error.message : 'Content update failed';
|
const errorMessage = error instanceof Error ? error.message : 'Content update failed';
|
||||||
console.error('[create-reports] Error updating report content:', {
|
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,
|
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);
|
const stateFile = state.files?.find((f) => f.id === update.reportId);
|
||||||
if (stateFile) {
|
if (stateFile) {
|
||||||
stateFile.status = 'failed';
|
stateFile.status = 'loading';
|
||||||
stateFile.error = `Failed to update report content: ${errorMessage}`;
|
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,15 +46,28 @@ export function createModifyReportsReasoningEntry(
|
||||||
let status: 'loading' | 'completed' | 'failed' = 'loading';
|
let status: 'loading' | 'completed' | 'failed' = 'loading';
|
||||||
let secondaryTitle: string | undefined;
|
let secondaryTitle: string | undefined;
|
||||||
|
|
||||||
// Check if modification is complete based on state
|
// Check if all edits have a final status (completed or failed), not just 'loading'
|
||||||
if (state.finalContent !== undefined) {
|
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
|
// Check if any edits failed
|
||||||
const hasFailedEdits = state.edits?.some((edit) => edit.status === 'failed') ?? false;
|
const hasFailedEdits = state.edits?.some((edit) => edit.status === 'failed') ?? false;
|
||||||
|
|
||||||
if (hasFailedEdits) {
|
if (hasFailedEdits) {
|
||||||
title = 'Failed to modify report';
|
title = 'Failed to modify report';
|
||||||
status = 'failed';
|
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';
|
title = 'Modified 1 report';
|
||||||
status = 'completed';
|
status = 'completed';
|
||||||
// Update the file status in filesRecord
|
// 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)
|
// Show elapsed time when complete
|
||||||
// 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);
|
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';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -127,6 +127,7 @@ export async function updateMessageEntries({
|
||||||
WITH new_messages AS (
|
WITH new_messages AS (
|
||||||
SELECT
|
SELECT
|
||||||
value,
|
value,
|
||||||
|
ordinality as input_order,
|
||||||
value->>'role' AS role,
|
value->>'role' AS role,
|
||||||
COALESCE(
|
COALESCE(
|
||||||
CASE
|
CASE
|
||||||
|
@ -138,7 +139,7 @@ export async function updateMessageEntries({
|
||||||
END,
|
END,
|
||||||
''
|
''
|
||||||
) AS tool_calls
|
) 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 (
|
existing_messages AS (
|
||||||
SELECT
|
SELECT
|
||||||
|
@ -170,8 +171,8 @@ export async function updateMessageEntries({
|
||||||
WHERE n.role = e.role AND n.tool_calls = e.tool_calls
|
WHERE n.role = e.role AND n.tool_calls = e.tool_calls
|
||||||
)
|
)
|
||||||
UNION ALL
|
UNION ALL
|
||||||
-- Add all new messages
|
-- Add all new messages, preserving their input order
|
||||||
SELECT n.value, 1000000 + row_number() OVER () AS ord
|
SELECT n.value, 1000000 + n.input_order AS ord
|
||||||
FROM new_messages n
|
FROM new_messages n
|
||||||
) combined
|
) combined
|
||||||
)
|
)
|
||||||
|
|
Loading…
Reference in New Issue