mirror of
https://gitee.com/wanwujie/deer-flow
synced 2026-04-05 15:10:20 +08:00
fix(chat): handle empty uploaded files case and improve artifact selection logic (#979)
* fix(chat): handle empty uploaded files case and improve artifact selection logic * Update frontend/src/components/workspace/chats/chat-box.tsx Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * fix: address code review suggestions from PR #979 (#3) * Initial plan * fix: address PR #979 review suggestions - utils.ts: scope (empty) check inside <uploaded_files> tag content - chat-box.tsx: remove stale `artifacts` from useEffect deps - context.tsx: wrap select/deselect with useCallback for stable refs - test: add test_empty_new_files_produces_empty_marker Co-authored-by: foreleven <4785594+foreleven@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: foreleven <4785594+foreleven@users.noreply.github.com> --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com> Co-authored-by: foreleven <4785594+foreleven@users.noreply.github.com>
This commit is contained in:
@@ -53,12 +53,15 @@ class UploadsMiddleware(AgentMiddleware[UploadsMiddlewareState]):
|
||||
|
||||
lines.append("The following files were uploaded in this message:")
|
||||
lines.append("")
|
||||
for file in new_files:
|
||||
size_kb = file["size"] / 1024
|
||||
size_str = f"{size_kb:.1f} KB" if size_kb < 1024 else f"{size_kb / 1024:.1f} MB"
|
||||
lines.append(f"- {file['filename']} ({size_str})")
|
||||
lines.append(f" Path: {file['path']}")
|
||||
lines.append("")
|
||||
if new_files:
|
||||
for file in new_files:
|
||||
size_kb = file["size"] / 1024
|
||||
size_str = f"{size_kb:.1f} KB" if size_kb < 1024 else f"{size_kb / 1024:.1f} MB"
|
||||
lines.append(f"- {file['filename']} ({size_str})")
|
||||
lines.append(f" Path: {file['path']}")
|
||||
lines.append("")
|
||||
else:
|
||||
lines.append("(empty)")
|
||||
|
||||
if historical_files:
|
||||
lines.append("The following files were uploaded in previous messages and are still available:")
|
||||
|
||||
@@ -188,6 +188,13 @@ class TestCreateFilesMessage:
|
||||
msg = mw._create_files_message([self._new_file()], [])
|
||||
assert "read_file" in msg
|
||||
|
||||
def test_empty_new_files_produces_empty_marker(self, tmp_path):
|
||||
mw = _middleware(tmp_path)
|
||||
msg = mw._create_files_message([], [])
|
||||
assert "(empty)" in msg
|
||||
assert "<uploaded_files>" in msg
|
||||
assert "</uploaded_files>" in msg
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# before_agent
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
import { createContext, useContext, useState, type ReactNode } from "react";
|
||||
import {
|
||||
createContext,
|
||||
useCallback,
|
||||
useContext,
|
||||
useState,
|
||||
type ReactNode,
|
||||
} from "react";
|
||||
|
||||
import { useSidebar } from "@/components/ui/sidebar";
|
||||
import { env } from "@/env";
|
||||
@@ -35,20 +41,23 @@ export function ArtifactsProvider({ children }: ArtifactsProviderProps) {
|
||||
const [autoOpen, setAutoOpen] = useState(true);
|
||||
const { setOpen: setSidebarOpen } = useSidebar();
|
||||
|
||||
const select = (artifact: string, autoSelect = false) => {
|
||||
setSelectedArtifact(artifact);
|
||||
if (env.NEXT_PUBLIC_STATIC_WEBSITE_ONLY !== "true") {
|
||||
setSidebarOpen(false);
|
||||
}
|
||||
if (!autoSelect) {
|
||||
setAutoSelect(false);
|
||||
}
|
||||
};
|
||||
const select = useCallback(
|
||||
(artifact: string, autoSelect = false) => {
|
||||
setSelectedArtifact(artifact);
|
||||
if (env.NEXT_PUBLIC_STATIC_WEBSITE_ONLY !== "true") {
|
||||
setSidebarOpen(false);
|
||||
}
|
||||
if (!autoSelect) {
|
||||
setAutoSelect(false);
|
||||
}
|
||||
},
|
||||
[setSidebarOpen, setSelectedArtifact, setAutoSelect],
|
||||
);
|
||||
|
||||
const deselect = () => {
|
||||
const deselect = useCallback(() => {
|
||||
setSelectedArtifact(null);
|
||||
setAutoSelect(true);
|
||||
};
|
||||
}, []);
|
||||
|
||||
const value: ArtifactsContextType = {
|
||||
artifacts,
|
||||
|
||||
@@ -34,12 +34,19 @@ const ChatBox: React.FC<{ children: React.ReactNode; threadId: string }> = ({
|
||||
setOpen: setArtifactsOpen,
|
||||
setArtifacts,
|
||||
select: selectArtifact,
|
||||
deselect,
|
||||
selectedArtifact,
|
||||
} = useArtifacts();
|
||||
|
||||
const [autoSelectFirstArtifact, setAutoSelectFirstArtifact] = useState(true);
|
||||
useEffect(() => {
|
||||
setArtifacts(thread.values.artifacts);
|
||||
if (
|
||||
thread.values.artifacts?.length === 0 ||
|
||||
(selectedArtifact && !thread.values.artifacts?.includes(selectedArtifact))
|
||||
) {
|
||||
deselect();
|
||||
}
|
||||
if (
|
||||
env.NEXT_PUBLIC_STATIC_WEBSITE_ONLY === "true" &&
|
||||
autoSelectFirstArtifact
|
||||
@@ -51,7 +58,9 @@ const ChatBox: React.FC<{ children: React.ReactNode; threadId: string }> = ({
|
||||
}
|
||||
}, [
|
||||
autoSelectFirstArtifact,
|
||||
deselect,
|
||||
selectArtifact,
|
||||
selectedArtifact,
|
||||
setArtifacts,
|
||||
thread.values.artifacts,
|
||||
]);
|
||||
|
||||
@@ -300,6 +300,11 @@ export function parseUploadedFiles(content: string): FileInMessage[] {
|
||||
return [];
|
||||
}
|
||||
|
||||
// Check if the backend reported no new files were uploaded in this message
|
||||
if (uploadedFilesContent?.includes("(empty)")) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// Parse file list
|
||||
// Format: - filename (size)\n Path: /path/to/file
|
||||
const fileRegex = /- ([^\n(]+)\s*\(([^)]+)\)\s*\n\s*Path:\s*([^\n]+)/g;
|
||||
|
||||
Reference in New Issue
Block a user