mirror of
https://github.com/bytedance/deer-flow.git
synced 2026-10-26 13:16:24 +00:00
fix(projects): label untitled threads in the conversation-files view (#5879)
* fix(projects): label untitled threads in the conversation-files view
GET /api/projects/{id}/threads/files returns each member thread's
display_name as `str | None`: the thread meta row is created with NULL and
only filled once title generation has run after the first reply, or never
if that step fails. The frontend's ProjectThreadFileGroup declared the field
as a required string and ConversationFileGroup rendered it verbatim, so a
thread whose files were uploaded before its first reply showed them under
an empty heading. The sibling ProjectThread type already models the field
as nullable and every consumer of it falls back to t.projects.untitled.
Make the group type nullable to match the wire, and render the heading
through a shared threadTitle() helper that both the thread-name map and the
file group now use, so the "Untitled" rule lives in one place.
The DOM test covers null, missing and whitespace-only titles.
* docs(changelog): reference #5879 in the conversation-files entries
---------
Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
This commit is contained in:
parent
9c53ebb06f
commit
59aa91d622
@ -323,6 +323,12 @@ This release closes that milestone with **181 merged pull requests**.
|
||||
|
||||
### Fixed
|
||||
|
||||
- **projects:** The conversation-files view no longer shows an empty heading
|
||||
for a member thread that has no title yet. A thread's `display_name` is
|
||||
`null` on the wire until title generation has run (or if it never does), but
|
||||
the file-group type declared it as a required string and rendered it as-is,
|
||||
so files uploaded before the first reply sat under a blank line. Such groups
|
||||
now read "Untitled", matching the project's thread list. ([#5879])
|
||||
- **skills:** Deleting a custom skill whose package directory is an
|
||||
operator-managed symlink now succeeds. The storage contract accepts a
|
||||
one-level link under `custom/`, but `delete_custom_skill` removed the
|
||||
@ -6102,5 +6108,6 @@ with **180 merged pull requests** since the first 2.0 milestone tag.
|
||||
[#5855]: https://github.com/bytedance/deer-flow/pull/5855
|
||||
[#5856]: https://github.com/bytedance/deer-flow/pull/5856
|
||||
[#5859]: https://github.com/bytedance/deer-flow/pull/5859
|
||||
[#5879]: https://github.com/bytedance/deer-flow/pull/5879
|
||||
[#5881]: https://github.com/bytedance/deer-flow/pull/5881
|
||||
|
||||
|
||||
@ -328,6 +328,10 @@
|
||||
|
||||
### 修复
|
||||
|
||||
- **项目:** 会话文件视图不再为尚无标题的成员会话显示空标题。会话的 `display_name` 在标题
|
||||
生成运行之前(或从未运行时)在接口上为 `null`,但文件分组类型将其声明为必填字符串并原样
|
||||
渲染,因此在首次回复之前上传的文件会挂在一行空白之下。这类分组现在显示为“未命名”,与
|
||||
项目的会话列表一致。([#5879])
|
||||
- **技能:** 删除包目录为运维方管理的符号链接的自定义技能现在可以成功。存储契约允许
|
||||
`custom/` 下的一级链接,但 `delete_custom_skill` 用 `shutil.rmtree` 删除包目录,而它拒绝
|
||||
符号链接:删除在写入历史记录之后以 `OSError` 失败,退出时清空了用户的技能投影视图,
|
||||
@ -5192,4 +5196,5 @@ DeerFlow 2.0 是围绕"超级智能体"框架的彻底重写,核心包含子
|
||||
[#5855]: https://github.com/bytedance/deer-flow/pull/5855
|
||||
[#5856]: https://github.com/bytedance/deer-flow/pull/5856
|
||||
[#5859]: https://github.com/bytedance/deer-flow/pull/5859
|
||||
[#5879]: https://github.com/bytedance/deer-flow/pull/5879
|
||||
[#5881]: https://github.com/bytedance/deer-flow/pull/5881
|
||||
|
||||
@ -73,6 +73,14 @@ import { getFileIcon } from "@/core/utils/files";
|
||||
import { env } from "@/env";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
/** A thread's wire title, or the localized "Untitled" while it has none. */
|
||||
function threadTitle(
|
||||
displayName: string | null | undefined,
|
||||
untitled: string,
|
||||
): string {
|
||||
return displayName?.trim() ? displayName : untitled;
|
||||
}
|
||||
|
||||
function errorToastMessage(error: unknown, fallback: string): string {
|
||||
return error instanceof Error && error.message ? error.message : fallback;
|
||||
}
|
||||
@ -143,7 +151,7 @@ function ProjectDocumentShelf({
|
||||
const threadNameById = new Map(
|
||||
threads.map((thread) => [
|
||||
thread.thread_id,
|
||||
thread.display_name?.trim() ? thread.display_name : t.projects.untitled,
|
||||
threadTitle(thread.display_name, t.projects.untitled),
|
||||
]),
|
||||
);
|
||||
|
||||
@ -854,7 +862,9 @@ function ConversationFileGroup({
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-1">
|
||||
<div className="text-sm font-medium">{group.display_name}</div>
|
||||
<div className="text-sm font-medium">
|
||||
{threadTitle(group.display_name, t.projects.untitled)}
|
||||
</div>
|
||||
<ul className="flex w-full flex-col gap-1">
|
||||
{group.files.map((file) => {
|
||||
const previewUrl = resolveArtifactOpenURL({
|
||||
|
||||
@ -100,7 +100,11 @@ export type ProjectThreadFile = {
|
||||
/** One member-thread group of the conversation-files view (spec §7.4). */
|
||||
export type ProjectThreadFileGroup = {
|
||||
thread_id: string;
|
||||
display_name: string;
|
||||
/**
|
||||
* The member thread's title. Nullable on the wire like ``ProjectThread``:
|
||||
* the thread meta row starts as ``null`` and is filled by title generation.
|
||||
*/
|
||||
display_name?: string | null;
|
||||
updated_at: string;
|
||||
files: ProjectThreadFile[];
|
||||
/** True when the thread's files were cut at the request's file_limit. */
|
||||
|
||||
@ -679,4 +679,39 @@ describe("ProjectDocumentsSection", () => {
|
||||
expect(screen.getByText("chart.png")).toBeDefined();
|
||||
expect(screen.queryByTestId("thread-files-truncated")).toBeNull();
|
||||
});
|
||||
|
||||
it.each([
|
||||
["null", null],
|
||||
["missing", undefined],
|
||||
["blank", " "],
|
||||
])(
|
||||
"labels a thread group whose title is %s as Untitled instead of rendering an empty heading",
|
||||
(_label, displayName) => {
|
||||
// ``display_name`` is nullable on the wire: the thread meta row starts
|
||||
// as ``null`` and is only filled once title generation has run.
|
||||
mocks.threadFileGroups = [
|
||||
{
|
||||
thread_id: "thread-9",
|
||||
display_name: displayName,
|
||||
updated_at: "2026-09-09T00:00:00Z",
|
||||
truncated: false,
|
||||
files: [
|
||||
{
|
||||
kind: "output",
|
||||
name: "chart.png",
|
||||
size_bytes: 1024,
|
||||
modified_at: "2026-09-09T00:00:00Z",
|
||||
artifact_url:
|
||||
"/api/threads/thread-9/artifacts/mnt/user-data/outputs/chart.png",
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
render(
|
||||
<ProjectDocumentsSection project={makeProject()} threads={THREADS} />,
|
||||
{ wrapper: Wrapper },
|
||||
);
|
||||
expect(screen.getByText("Untitled")).toBeDefined();
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user