fix: preserve uploaded file attachments after subsequent assistant messages#13993
fix: preserve uploaded file attachments after subsequent assistant messages#13993octo-patch wants to merge 3 commits intoinfiniflow:mainfrom
Conversation
…ssages When a user uploads a file in Q1 and then sends a KB-only Q2, the server response resets derivedMessages from conversation?.messages which lacks the local File objects attached to Q1. The attachment icon therefore disappeared from the earlier message. Fix: when syncing server messages to local state, build a map of message-id to files from the previous local state and restore those files onto the matching server messages, so uploaded attachments remain visible throughout the conversation. Fixes infiniflow#13959
📝 WalkthroughWalkthroughThe PR modifies message state synchronization in the chat UI to preserve locally cached file attachments when syncing with server messages, preventing attachments from disappearing during subsequent message updates. Additionally, three query-related modules add single-quote character stripping as a preprocessing step, and the OCR detection method removes timing metadata from its return value. Changes
Estimated code review effort🎯 2 (Simple) | ⏱️ ~12 minutes Poem
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@web/src/pages/next-chats/chat/chat-box/single-chat-box.tsx`:
- Around line 65-76: The files restoration logic builds a Map keyed by m.id
which can collide across messages with the same id; change the map to use
buildMessageUuidWithRole(m.id, m.role) when creating filesMap from prevMessages
and likewise use buildMessageUuidWithRole(m.id, m.role) when reading from
filesMap in the return messages.map callback so that files are matched by
id+role (refer to filesMap, prevMessages, messages, and
buildMessageUuidWithRole).
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 9a66ed4d-75ff-4feb-ac86-eb88dfa5c536
📒 Files selected for processing (5)
common/query_base.pydeepdoc/vision/ocr.pymemory/services/query.pyrag/nlp/query.pyweb/src/pages/next-chats/chat/chat-box/single-chat-box.tsx
| const filesMap = new Map( | ||
| prevMessages | ||
| .filter((m) => m.files?.length) | ||
| .map((m) => [m.id, m.files]), | ||
| ); | ||
| if (filesMap.size === 0) { | ||
| return messages; | ||
| } | ||
| return messages.map((m) => ({ | ||
| ...m, | ||
| files: filesMap.get(m.id) ?? m.files, | ||
| })); |
There was a problem hiding this comment.
Use a role-qualified key when restoring files to avoid cross-message collisions.
On Line 65 and Line 75, keying by m.id alone can mis-attach files if multiple messages share the same id (the component already treats id+role as identity at Line 102). Use buildMessageUuidWithRole for both map write/read.
🔧 Proposed fix
- const filesMap = new Map(
+ const filesMap = new Map(
prevMessages
.filter((m) => m.files?.length)
- .map((m) => [m.id, m.files]),
+ .map((m) => [buildMessageUuidWithRole(m), m.files]),
);
@@
return messages.map((m) => ({
...m,
- files: filesMap.get(m.id) ?? m.files,
+ files: filesMap.get(buildMessageUuidWithRole(m)) ?? m.files,
}));📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const filesMap = new Map( | |
| prevMessages | |
| .filter((m) => m.files?.length) | |
| .map((m) => [m.id, m.files]), | |
| ); | |
| if (filesMap.size === 0) { | |
| return messages; | |
| } | |
| return messages.map((m) => ({ | |
| ...m, | |
| files: filesMap.get(m.id) ?? m.files, | |
| })); | |
| const filesMap = new Map( | |
| prevMessages | |
| .filter((m) => m.files?.length) | |
| .map((m) => [buildMessageUuidWithRole(m), m.files]), | |
| ); | |
| if (filesMap.size === 0) { | |
| return messages; | |
| } | |
| return messages.map((m) => ({ | |
| ...m, | |
| files: filesMap.get(buildMessageUuidWithRole(m)) ?? m.files, | |
| })); |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@web/src/pages/next-chats/chat/chat-box/single-chat-box.tsx` around lines 65 -
76, The files restoration logic builds a Map keyed by m.id which can collide
across messages with the same id; change the map to use
buildMessageUuidWithRole(m.id, m.role) when creating filesMap from prevMessages
and likewise use buildMessageUuidWithRole(m.id, m.role) when reading from
filesMap in the return messages.map callback so that files are matched by
id+role (refer to filesMap, prevMessages, messages, and
buildMessageUuidWithRole).
Problem
When a user uploads a file attachment in their first message (Q1) and then sends a follow-up message (Q2) that triggers a backend response, the uploaded file attachment disappears from Q1 in the chat UI.
Fixes #13959
Root Cause
In
single-chat-box.tsx, auseEffecthook syncsderivedMessagesfromconversation?.messageswhenever the conversation data changes (e.g., after a new assistant reply arrives):The problem is that
conversation.messagescomes from the server, which stores messages as plain JSON. BrowserFileobjects (uploaded by the user) cannot be serialized to JSON, so they are never stored on the server. Each time the server data is applied to local state, thefilesarray on the user's first message is lost.Fix
Instead of replacing the local messages wholesale, preserve any
filesentries from the previous local state by ID before applying the server data:This is a minimal, targeted fix: when there are no local files to preserve the behavior is identical to before (early return with plain assignment). When local file objects exist they are re-attached to the corresponding server messages by ID.
Summary by CodeRabbit
Bug Fixes
Refactor