)}
- {/* Summary Section */}
- {completeData.summary && (
+ {/* Text/Summary Section */}
+ {(text || completeData.summary) && (
)}
{/* Attachments Section */}
- {completeData.attachments && completeData.attachments.length > 0 && (
+ {attachments && attachments.length > 0 ? (
+
+ ) : completeData.attachments && completeData.attachments.length > 0 ? (
- )}
+ ) : null}
{/* Tasks Completed Section */}
{completeData.tasksCompleted && completeData.tasksCompleted.length > 0 && (
@@ -288,7 +392,7 @@ export function CompleteToolView({
)}
{/* Empty State */}
- {!completeData.summary && !completeData.result && !completeData.attachments && !completeData.tasksCompleted && !isStreaming && (
+ {!text && !attachments && !completeData.summary && !completeData.result && !completeData.attachments && !completeData.tasksCompleted && !isStreaming && (
diff --git a/frontend/src/components/thread/tool-views/complete-tool/_utils.ts b/frontend/src/components/thread/tool-views/complete-tool/_utils.ts
new file mode 100644
index 00000000..00e5d230
--- /dev/null
+++ b/frontend/src/components/thread/tool-views/complete-tool/_utils.ts
@@ -0,0 +1,234 @@
+import { extractToolData, normalizeContentToString } from '../utils';
+
+export interface CompleteData {
+ text: string | null;
+ attachments: string[] | null;
+ status: string | null;
+ success?: boolean;
+ timestamp?: string;
+}
+
+const parseContent = (content: any): any => {
+ if (typeof content === 'string') {
+ try {
+ return JSON.parse(content);
+ } catch (e) {
+ return content;
+ }
+ }
+ return content;
+};
+
+const extractFromNewFormat = (content: any): {
+ text: string | null;
+ attachments: string[] | null;
+ status: string | null;
+ success?: boolean;
+ timestamp?: string;
+} => {
+ const parsedContent = parseContent(content);
+
+ if (!parsedContent || typeof parsedContent !== 'object') {
+ return { text: null, attachments: null, status: null, success: undefined, timestamp: undefined };
+ }
+
+ if ('tool_execution' in parsedContent && typeof parsedContent.tool_execution === 'object') {
+ const toolExecution = parsedContent.tool_execution;
+ const args = toolExecution.arguments || {};
+
+ let parsedOutput = toolExecution.result?.output;
+ if (typeof parsedOutput === 'string') {
+ try {
+ parsedOutput = JSON.parse(parsedOutput);
+ } catch (e) {
+ }
+ }
+
+ let attachments: string[] | null = null;
+ if (args.attachments) {
+ if (typeof args.attachments === 'string') {
+ attachments = args.attachments.split(',').map((a: string) => a.trim()).filter((a: string) => a.length > 0);
+ } else if (Array.isArray(args.attachments)) {
+ attachments = args.attachments;
+ }
+ }
+
+ let status: string | null = null;
+ if (parsedOutput && typeof parsedOutput === 'object' && parsedOutput.status) {
+ status = parsedOutput.status;
+ }
+
+ const extractedData = {
+ text: args.text || null,
+ attachments,
+ status: status || parsedContent.summary || null,
+ success: toolExecution.result?.success,
+ timestamp: toolExecution.execution_details?.timestamp
+ };
+
+ console.log('CompleteToolView: Extracted from new format:', {
+ hasText: !!extractedData.text,
+ attachmentCount: extractedData.attachments?.length || 0,
+ hasStatus: !!extractedData.status,
+ success: extractedData.success
+ });
+
+ return extractedData;
+ }
+
+ if ('role' in parsedContent && 'content' in parsedContent) {
+ return extractFromNewFormat(parsedContent.content);
+ }
+
+ return { text: null, attachments: null, status: null, success: undefined, timestamp: undefined };
+};
+
+const extractFromLegacyFormat = (content: any): {
+ text: string | null;
+ attachments: string[] | null;
+ status: string | null;
+} => {
+ const toolData = extractToolData(content);
+
+ if (toolData.toolResult && toolData.arguments) {
+ console.log('CompleteToolView: Extracted from legacy format (extractToolData):', {
+ hasText: !!toolData.arguments.text,
+ attachmentCount: toolData.arguments.attachments ?
+ (Array.isArray(toolData.arguments.attachments) ? toolData.arguments.attachments.length : 1) : 0
+ });
+
+ let attachments: string[] | null = null;
+ if (toolData.arguments.attachments) {
+ if (Array.isArray(toolData.arguments.attachments)) {
+ attachments = toolData.arguments.attachments;
+ } else if (typeof toolData.arguments.attachments === 'string') {
+ attachments = toolData.arguments.attachments.split(',').map(a => a.trim()).filter(a => a.length > 0);
+ }
+ }
+
+ return {
+ text: toolData.arguments.text || null,
+ attachments,
+ status: null
+ };
+ }
+
+ const contentStr = normalizeContentToString(content);
+ if (!contentStr) {
+ return { text: null, attachments: null, status: null };
+ }
+
+ let attachments: string[] | null = null;
+ const attachmentsMatch = contentStr.match(/attachments=["']([^"']*)["']/i);
+ if (attachmentsMatch) {
+ attachments = attachmentsMatch[1].split(',').map(a => a.trim()).filter(a => a.length > 0);
+ }
+
+ let text: string | null = null;
+ const textMatch = contentStr.match(/]*>([^<]*)<\/complete>/i);
+ if (textMatch) {
+ text = textMatch[1].trim();
+ }
+
+ console.log('CompleteToolView: Extracted from legacy format (manual parsing):', {
+ hasText: !!text,
+ attachmentCount: attachments?.length || 0
+ });
+
+ return {
+ text,
+ attachments,
+ status: null
+ };
+};
+
+export function extractCompleteData(
+ assistantContent: any,
+ toolContent: any,
+ isSuccess: boolean,
+ toolTimestamp?: string,
+ assistantTimestamp?: string
+): {
+ text: string | null;
+ attachments: string[] | null;
+ status: string | null;
+ actualIsSuccess: boolean;
+ actualToolTimestamp?: string;
+ actualAssistantTimestamp?: string;
+} {
+ let text: string | null = null;
+ let attachments: string[] | null = null;
+ let status: string | null = null;
+ let actualIsSuccess = isSuccess;
+ let actualToolTimestamp = toolTimestamp;
+ let actualAssistantTimestamp = assistantTimestamp;
+
+ const assistantNewFormat = extractFromNewFormat(assistantContent);
+ const toolNewFormat = extractFromNewFormat(toolContent);
+
+ console.log('CompleteToolView: Format detection results:', {
+ assistantNewFormat: {
+ hasText: !!assistantNewFormat.text,
+ attachmentCount: assistantNewFormat.attachments?.length || 0,
+ hasStatus: !!assistantNewFormat.status
+ },
+ toolNewFormat: {
+ hasText: !!toolNewFormat.text,
+ attachmentCount: toolNewFormat.attachments?.length || 0,
+ hasStatus: !!toolNewFormat.status
+ }
+ });
+
+ if (assistantNewFormat.text || assistantNewFormat.attachments || assistantNewFormat.status) {
+ text = assistantNewFormat.text;
+ attachments = assistantNewFormat.attachments;
+ status = assistantNewFormat.status;
+ if (assistantNewFormat.success !== undefined) {
+ actualIsSuccess = assistantNewFormat.success;
+ }
+ if (assistantNewFormat.timestamp) {
+ actualAssistantTimestamp = assistantNewFormat.timestamp;
+ }
+ console.log('CompleteToolView: Using assistant new format data');
+ } else if (toolNewFormat.text || toolNewFormat.attachments || toolNewFormat.status) {
+ text = toolNewFormat.text;
+ attachments = toolNewFormat.attachments;
+ status = toolNewFormat.status;
+ if (toolNewFormat.success !== undefined) {
+ actualIsSuccess = toolNewFormat.success;
+ }
+ if (toolNewFormat.timestamp) {
+ actualToolTimestamp = toolNewFormat.timestamp;
+ }
+ console.log('CompleteToolView: Using tool new format data');
+ } else {
+ const assistantLegacy = extractFromLegacyFormat(assistantContent);
+ const toolLegacy = extractFromLegacyFormat(toolContent);
+
+ text = assistantLegacy.text || toolLegacy.text;
+ attachments = assistantLegacy.attachments || toolLegacy.attachments;
+ status = assistantLegacy.status || toolLegacy.status;
+
+ console.log('CompleteToolView: Using legacy format data:', {
+ hasText: !!text,
+ attachmentCount: attachments?.length || 0,
+ hasStatus: !!status
+ });
+ }
+
+ console.log('CompleteToolView: Final extracted data:', {
+ hasText: !!text,
+ attachmentCount: attachments?.length || 0,
+ hasStatus: !!status,
+ actualIsSuccess
+ });
+
+ return {
+ text,
+ attachments,
+ status,
+ actualIsSuccess,
+ actualToolTimestamp,
+ actualAssistantTimestamp
+ };
+}
\ No newline at end of file