From deb442cbf76778ef2940f35d8cac664da0bcc94e Mon Sep 17 00:00:00 2001 From: LE Quoc Dat Date: Mon, 28 Jul 2025 21:40:07 +0200 Subject: [PATCH] AI: How can we stream the edit_file tool when it generating like create_file ? Also the edit_file tool show this """Invalid File Edit Could not extract the file changes from the tool result.""" Check the state of code base and make to sure implement fully --- backend/agentpress/response_processor.py | 19 +++++++--- frontend/src/app/share/[threadId]/page.tsx | 37 +++++++++++++------ .../thread/content/PlaybackControls.tsx | 1 + 3 files changed, 39 insertions(+), 18 deletions(-) diff --git a/backend/agentpress/response_processor.py b/backend/agentpress/response_processor.py index ff2e7ea1..3f3b4b17 100644 --- a/backend/agentpress/response_processor.py +++ b/backend/agentpress/response_processor.py @@ -1649,7 +1649,13 @@ class ResponseProcessor: "role": result_role, "content": json.dumps(structured_result_for_llm) } - message_obj = await self.add_message( + + # Add rich content to metadata for frontend use + if metadata is None: + metadata = {} + metadata['frontend_content'] = structured_result_for_frontend + + message_obj = await self._add_message_with_agent_info( thread_id=thread_id, type="tool", content=result_message_for_llm, # Save the LLM-friendly version @@ -1659,11 +1665,12 @@ class ResponseProcessor: # If the message was saved, modify it in-memory for the frontend before returning if message_obj: - result_message_for_frontend = { - "role": result_role, - "content": json.dumps(structured_result_for_frontend) - } - message_obj['content'] = result_message_for_frontend + # The frontend expects the rich content in the 'content' field. + # The DB has the rich content in metadata.frontend_content. + # Let's reconstruct the message for yielding. + message_for_yield = message_obj.copy() + message_for_yield['content'] = structured_result_for_frontend + return message_for_yield return message_obj # Return the modified message object except Exception as e: diff --git a/frontend/src/app/share/[threadId]/page.tsx b/frontend/src/app/share/[threadId]/page.tsx index e06f6240..df4f5e89 100644 --- a/frontend/src/app/share/[threadId]/page.tsx +++ b/frontend/src/app/share/[threadId]/page.tsx @@ -395,18 +395,31 @@ export default function ThreadPage({ const unifiedMessages = (messagesData || []) .filter((msg) => msg.type !== 'status') - .map((msg: ApiMessageType) => ({ - message_id: msg.message_id || null, - thread_id: msg.thread_id || threadId, - type: (msg.type || 'system') as UnifiedMessage['type'], - is_llm_message: Boolean(msg.is_llm_message), - content: msg.content || '', - metadata: msg.metadata || '{}', - created_at: msg.created_at || new Date().toISOString(), - updated_at: msg.updated_at || new Date().toISOString(), - agent_id: (msg as any).agent_id, - agents: (msg as any).agents, - })); + .map((msg: ApiMessageType) => { + let finalContent: string | object = msg.content || ''; + if (msg.metadata) { + try { + const metadata = JSON.parse(msg.metadata); + if (metadata.frontend_content) { + finalContent = metadata.frontend_content; + } + } catch (e) { + // ignore + } + } + return { + message_id: msg.message_id || null, + thread_id: msg.thread_id || threadId, + type: (msg.type || 'system') as UnifiedMessage['type'], + is_llm_message: Boolean(msg.is_llm_message), + content: typeof finalContent === 'string' ? finalContent : JSON.stringify(finalContent), + metadata: msg.metadata || '{}', + created_at: msg.created_at || new Date().toISOString(), + updated_at: msg.updated_at || new Date().toISOString(), + agent_id: (msg as any).agent_id, + agents: (msg as any).agents, + }; + }); setMessages(unifiedMessages); const historicalToolPairs: ToolCallInput[] = []; diff --git a/frontend/src/components/thread/content/PlaybackControls.tsx b/frontend/src/components/thread/content/PlaybackControls.tsx index 47faa98f..77f36682 100644 --- a/frontend/src/components/thread/content/PlaybackControls.tsx +++ b/frontend/src/components/thread/content/PlaybackControls.tsx @@ -11,6 +11,7 @@ const HIDE_STREAMING_XML_TAGS = new Set([ 'create-file', 'delete-file', 'full-file-rewrite', + 'edit-file', 'str-replace', 'browser-click-element', 'browser-close-tab',