Skip to content

Feat/conversation artifacts panel - #4311

Merged
jeffwu-1999 merged 28 commits into
developfrom
feat/conversation-artifacts-panel
Nov 13, 2026
Merged

jeffwu-1999 merged 28 commits into
developfrom
feat/conversation-artifacts-panel

Conversation

@lijiayang619

@lijiayang619 lijiayang619 commented Nov 9, 2026 •

Copy link
Copy Markdown
Contributor

变更

  • 新增“分支到新对话”,复制截至所选回答的历史消息、会话配置及关联产物,分支名称使用数字后缀区分。
  • 修复重新生成回答后分支定位错误的问题,支持准确选择 1/2、2/2 中的回答版本,避免复制其他版本或后续轮次。
  • 新增“添加批注”,支持将选中文字和可选评论加入输入框,并随下一条消息发送给模型;移除选中文字前后的额外上下文。
  • 调整当前会话的问题导航,使用侧边刻度和悬停预览,支持快速定位历史提问。
  • 会话产物入口统一放在右上角,有产物后持续显示,支持查看、下载及添加到输入框。
  • 新生成的可预览文档自动在右侧打开,复用现有文件预览能力;优化 Word 预览的页面背景,移除深色边框和阴影。
  • 移除书签持久化功能及相关 SQL 迁移,不修改数据库结构。清理本次分支新增的过程文档,保留功能代码、翻译及回归测试。
    验证
  • 本次分支定位修复:14 项前端测试、49 项后端隔离回归测试通过,覆盖回答版本选择、复制范围、非法定位参数及关联数据查询范围。
  • 分支接口参数传递、服务调用及请求校验的定向隔离检查通过。
  • 修改文件的定向 TypeScript 检查、Python 语法检查、版权头及 git diff --check 通过。
  • 本地依赖不齐,未完成全量类型检查、完整应用测试及部署后的浏览器验证;最终结果以 PR CI 和实际部署验证为准。

图片:
1.当前对话历史记录
9ed7c008-a3c9-49ea-ad4e-d68641173eb0

2.查看所有历史产物
8dcc1cd3-144b-4868-83fd-7c08320397ef

3.分支到新对话

image

4.添加批注
image

ljy added 18 commits October 31, 2026 12:13
…files

Collect every agent-generated file across a conversation and expose a
persistent "view all artifacts (N)" entry above the composer. The entry
opens a right-hand panel listing each artifact with icon, size and
generation time, plus preview (reusing FilePreviewDrawer) and download.

Aggregation covers both attachment paths: streamed marker parts carrying
skillFileAttachments and the history-restore skillFileUploadsRegistry.
Keys stay unique across streaming chunks whose attachment ids restart.

Unit proof lives in conversationArtifacts.test.tsx (aggregation, panel,
preview, download fallbacks, entry button). The change reuses the proven
SSE files-chunk, preview-drawer and storage contracts without adding a
new cross-component boundary, so no formal D1 case is added; the
assessment is recorded in CHAT-ARTIFACTS-PANEL-20261031.
Artifacts listed in the conversation artifacts panel can now be
re-attached into the composer (WorkBuddy "添加到对话框" parity):

- attachment-adapter: files marked via markFileWithExistingUpload carry
  their MinIO metadata through add(); send() reuses the stored object
  instead of uploading the same bytes again, keeping minio_files intact
- conversation-artifacts: fetchArtifactFile resolves artifact bytes via
  the backend stream endpoint for object_name with URL fallbacks
- artifacts-panel: optional onAddToComposer prop renders a per-entry
  "add to chat" action with an in-flight guard; omitted in read-only
  and shared conversations
- thread: wires the handler to the main thread composer with success
  and failure feedback; artifacts without object_name fall back to the
  regular upload path
- i18n: chat.artifacts.addToChat / addedToChat / addToChatFailed (zh/en)
- tests: 8 new UTs (22 total) covering the panel action, the fetch
  fallback chain, and stored-object reuse; adds the missing copyright
  header to attachment-adapter.ts

Change record updated: CHAT-ARTIFACTS-PANEL-20261031.
List every user question asked in the current agent conversation and
jump the message viewport to the selected turn ("问题目录"):

- question-directory utils: collectQuestionDirectory extracts one entry
  per user message (first text line truncated for the summary, full text
  kept for the tooltip, first attachment name for attachment-only
  turns); scrollThreadToMessage locates the message root via
  data-message-id (assistant-ui exposes no scroll API), smooth-scrolls
  it into view, and briefly highlights the target
- question-directory ui: QuestionDirectoryButton renders a floating chip
  on surfaces without a persistent header (main newchat flow) and a
  ghost header icon button elsewhere (workbench, share mode, read-only
  page); the shared popover lists numbered entries and closes on jump
- thread: directory computed next to the artifacts aggregation and
  wired into all four surfaces; navigation stays available while a run
  streams and in read-only/share contexts
- i18n: chat.questions.viewAll/title/total/jump (zh/en)
- tests: 8 new UTs (30 with the artifacts regression) covering
  collection rules, scroll/highlight behavior, and popover interaction

Change record added: QUESTION-DIRECTORY-JUMP-20261102.
The question directory popover now filters entries as you type:

- a search box at the top of the popover list, matching the full
  question text (not just the truncated summary) case-insensitively so
  keywords from any line still find the entry; attachment turns match
  by their file name
- unmatched keywords show an explicit empty state
- the keyword resets when the popover closes so reopening starts from
  the full directory again
- i18n: chat.questions.search / noMatches (zh/en)
- tests: 3 new UTs (11 total in the file) covering keyword filtering,
  the empty state, and the close-reset behaviour

Change record updated: QUESTION-DIRECTORY-JUMP-20261102.
Add a "branch to new chat" action on the assistant message "..." menu.
Branching creates a new conversation that copies the configuration
(agent, chat mode, knowledge scope, runtime metadata, workbench
config) plus every message, unit, search source, and image source up
to and including the selected Q&A pair, then switches to the branched
thread. The original conversation and its MinIO file references stay
untouched; user feedback is not carried over.

The turn is addressed by a 1-based index that both sides compute the
same way: the frontend counts user messages up to the selected answer,
the backend counts its active (deduplicated) user rows in
message_index order, so no message-id mapping round trip is needed.

Backend: POST /conversation/branch with conversation_id and turn_index;
the repository clones rows in one transaction behind the user-quota
advisory lock and collapses refresh duplicates / regenerated answers
to the newest row per message_index. The branched title reuses the
source title with a single "(分支)" suffix within the 100-char column.
Selecting text inside one assistant reply shows a floating "Add to
chat" bubble above the selection. Confirming it (optionally after
typing a comment) stores a pending annotation and renders a count
chip above the composer input; hovering the chip opens the annotation
list where each entry can be edited or deleted, and the trailing
button clears all.

Sending folds every pending annotation into the outgoing message
text as a numbered quote block ("> "-prefixed quoted reply lines, an
optional comment line, and the first-40-char excerpt of the source
reply), followed by a "---" separator and the user-typed text, so the
model resolves the reference from the full conversation history
without any backend or protocol change. Annotation-only sends (empty
composer text) are allowed. The fold is idempotent for the
send-button/form-submit double invocation; a rejected run keeps the
annotations and restores the original draft, and the annotations are
consumed only once the run accepts. Switching threads resets them.

Quotable selections are restricted to the content area of a single
assistant message: reasoning, tool, execution-code, and subagent
containers are excluded, and the bubble is disabled in read-only
threads, share mode, skill-file chats, and the preview layouts.
Upgrade the "add to chat" flow from frontend-only text folding to a
LibreChat-style structured channel:

- AgentRequest/MessageRequest gain an `annotations` field (snake_case wire
  items quoted_text/comment/source_excerpt, capped at 10 entries and
  1500/500/120 chars, truncated rather than rejected) via the new
  contracts/annotations.py module.
- The user message persists annotations as a `message_annotations` unit
  (mirroring clarification_response) while the stored message_content stays
  clean; history reads return the structured field.
- The backend merges the numbered quote blocks into the model-facing text
  at request time: the current turn in prepare_agent_run and past turns in
  get_historical_context.
- The composer takes over the append when annotations are pending (no
  in-flight uploads) so the optimistic user message carries them as
  metadata; the user bubble renders read-only reference cards, restored
  from history after reload. In-flight uploads fall back to the runConfig
  channel; stale payloads are cleared so they cannot leak into
  clarification sends or edit resends. Ordinary sends are untouched.
- The fold function, its localized labels and the double-invocation guard
  are removed; formatting now happens in the backend.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Unresolved defects can expose internal database diagnostics, disrupt keyboard popover access, allow duplicate artifact operations, and disable bookmarks after annotation-only turns.

4 open issues
What changed in this PR

Adds conversation-wide artifact browsing and document previews, alongside question navigation, persistent bookmarks, conversation branching, and structured reply annotations.

Changes:

  • Adds artifact aggregation, preview, download, reattachment, and automatic document opening.
  • Introduces question rails, saved bookmarks, numbered branches, and selected-text annotations.
  • Extends backend APIs, contracts, persistence, localization, tests, and requirement records.

This pull request requires changes. Unresolved moderate findings cover database-error disclosure, annotation-only history loss, annotation-popover keyboard focus, keyboard clear-all handling, TSV preview routing and automatic-preview eligibility, duplicate artifact additions, bookmark identity mapping, and branching session-expiration handling.

File Description
test/​backend/​services/​test_question_bookmarks.py Tests bookmark validation, ownership, and endpoint behavior.
test/​backend/​services/​test_conversation_management_service.py Tests conversation branching service behavior.
test/​backend/​database/​test_conversation_db.py Updates historical-context query mocks.
test/​backend/​database/​test_branch_titles.py Tests branch-title numbering and collisions.
test/​backend/​app/​test_conversation_management_app.py Tests branching endpoint responses.
test-e2e/​changes/​requirements/​REMOVE-QUESTION-DIRECTORY-POPOVER-20261109.yaml Records directory-popover removal requirements.
test-e2e/​changes/​requirements/​QUESTION-DIRECTORY-RAIL-20261105.yaml Defines question-rail navigation requirements.
test-e2e/​changes/​requirements/​QUESTION-DIRECTORY-RAIL-20261105-verification.md Documents rail checks and verification limits.
test-e2e/​changes/​requirements/​QUESTION-DIRECTORY-JUMP-20261102.yaml Records initial question-directory requirements.
test-e2e/​changes/​requirements/​QUESTION-BOOKMARKS-20261109.yaml Defines persistent bookmark behavior.
test-e2e/​changes/​requirements/​QUESTION-BOOKMARKS-20261109.verification.md Documents bookmark checks and migration prerequisites.
test-e2e/​changes/​requirements/​CHAT-ARTIFACTS-TOP-ENTRY-20261106.yaml Defines persistent artifact-toolbar placement.
test-e2e/​changes/​requirements/​CHAT-ARTIFACTS-TOP-ENTRY-20261106-verification.md Documents toolbar lifecycle checks.
test-e2e/​changes/​requirements/​CHAT-ARTIFACTS-PANEL-20261031.yaml Defines artifact aggregation and file actions.
test-e2e/​changes/​requirements/​BRANCH-NUMBERED-TITLES-20261106.yaml Defines numbered branch-title requirements.
test-e2e/​changes/​requirements/​BRANCH-NUMBERED-TITLES-20261106-verification.md Documents title-allocation checks and limits.
test-e2e/​changes/​requirements/​BRANCH-CONVERSATION-TO-NEW-CHAT-20261102.yaml Defines conversation branching requirements.
test-e2e/​changes/​requirements/​AUTO-OPEN-DOCUMENT-20261109.yaml Defines automatic document-preview behavior.
test-e2e/​changes/​requirements/​AUTO-OPEN-DOCUMENT-20261109.verification.md Documents preview checks and limitations.
test-e2e/​changes/​requirements/​ANNOTATION-SELECTED-TEXT-ONLY-20261109.yaml Defines selected-text-only annotation behavior.
test-e2e/​changes/​requirements/​ANNOTATION-SELECTED-TEXT-ONLY-20261109.verification.md Documents selected-text annotation checks.
test-e2e/​changes/​requirements/​ADD-TO-CHAT-MESSAGE-ANNOTATIONS-20261102.yaml Records initial annotation interaction requirements.
test-e2e/​changes/​requirements/​ADD-TO-CHAT-LOCAL-CONTEXT-20261105.yaml Records earlier context-capture requirements.
test-e2e/​changes/​requirements/​ADD-TO-CHAT-LOCAL-CONTEXT-20261105-verification.md Documents context-capture verification.
test-e2e/​changes/​requirements/​ADD-TO-CHAT-ANNOTATIONS-STRUCTURED-20261102.yaml Defines structured annotation requirements.
test-e2e/​changes/​bugs/​QUESTION-RAIL-STALE-PREVIEW-20261105.yaml Records the adjacent-preview regression.
test-e2e/​changes/​bugs/​QUESTION-RAIL-STALE-PREVIEW-20261105-verification.md Documents preview-switching regression evidence.
frontend/​types/​conversation.ts Adds annotation response types.
frontend/​types/​chat.ts Extends preview presentation types.
frontend/​tests/​component/​viewportScrollRescue.test.tsx Tests scrolling and streaming-follow behavior.
frontend/​tests/​component/​rawStreamPreviewViewer.test.tsx Checks DOCX wrapper configuration.
frontend/​tests/​component/​questionBookmarks.test.tsx Tests bookmark state and identity mapping.
frontend/​tests/​component/​inlineFilePreview.test.tsx Tests inline preview compatibility.
frontend/​tests/​component/​docxPreviewBackground.test.tsx Tests rendered DOCX paper styling.
frontend/​tests/​component/​conversationBranch.test.tsx Tests branch turn calculation and menu wiring.
frontend/​tests/​component/​autoArtifactPreview.test.tsx Tests automatic-preview lifecycle.
frontend/​tests/​component/​artifactDocumentPanel.test.tsx Tests document sources and panel controls.
frontend/​services/​conversationService.ts Adds bookmark, branch, and annotation requests.
frontend/​services/​api.ts Defines bookmark and branch endpoints.
frontend/​public/​locales/​zh/​common.json Adds Chinese interaction labels.
frontend/​public/​locales/​en/​common.json Adds English interaction labels.
frontend/​hooks/​chat/​useQuestionBookmarks.ts Manages bookmark queries and mutations.
frontend/​components/​common/​rawStreamPreviewViewer.tsx Enables DOCX page-wrapper rendering.
frontend/​components/​common/​rawStreamPreviewViewer.module.css Scopes DOCX page styling.
frontend/​components/​common/​filePreviewDrawer.tsx Adds inline preview presentation.
frontend/​app/​[locale]/​newchat/​utils/​question-directory.ts Collects questions and tracks navigation.
frontend/​app/​[locale]/​newchat/​utils/​message-annotations.ts Provides selection and annotation-format helpers.
frontend/​app/​[locale]/​newchat/​utils/​conversation-branch.ts Computes branch turn boundaries.
frontend/​app/​[locale]/​newchat/​utils/​conversation-artifacts.ts Aggregates generated files and retrieves attachment data.
frontend/​app/​[locale]/​newchat/​ui/​question-directory-rail.tsx Adds question-navigation ticks and previews.
frontend/​app/​[locale]/​newchat/​ui/​question-bookmark-rail.tsx Connects question identities to saved bookmarks.
frontend/​app/​[locale]/​newchat/​ui/​conversation-artifacts-context.tsx Shares artifact toolbar state.
frontend/​app/​[locale]/​newchat/​ui/​attachment.tsx Exports shared artifact-action helpers.
frontend/​app/​[locale]/​newchat/​ui/​artifacts-panel.tsx Lists artifacts and exposes file actions.
frontend/​app/​[locale]/​newchat/​components/​use-auto-artifact-preview.ts Selects newly generated documents for automatic preview.
frontend/​app/​[locale]/​newchat/​components/​clarification-message-card.tsx Excludes pending annotations from clarification sends.
frontend/​app/​[locale]/​newchat/​components/​artifact-document-panel.tsx Hosts inline artifact document previews.
frontend/​app/​[locale]/​newchat/​assistant-ui/​viewport-scroll-rescue.tsx Restores history and streaming scroll behavior.
frontend/​app/​[locale]/​newchat/​assistant-ui/​message-annotation-cards.tsx Displays message annotation cards.
frontend/​app/​[locale]/​newchat/​assistant-ui/​message-annotation-bubble.tsx Captures selected reply text and comments.
frontend/​app/​[locale]/​newchat/​assistant-ui/​conversation-branch-menu-item.tsx Adds the conversation branch action.
frontend/​app/​[locale]/​newchat/​assistant-ui/​conversation-branch-context.ts Shares branching availability and controls.
frontend/​app/​[locale]/​newchat/​assistant-ui/​composer.tsx Integrates annotation-aware message submission.
frontend/​app/​[locale]/​newchat/​assistant-ui/​annotation-chip.tsx Displays and manages pending annotations.
frontend/​app/​[locale]/​newchat/​adapter/​remote-chat-model-adapter.ts Sends structured annotations to the backend.
frontend/​app/​[locale]/​newchat/​adapter/​conversation-thread-list-adapter.tsx Restores annotation metadata from history.
frontend/​app/​[locale]/​newchat/​adapter/​attachment-adapter.ts Reuses existing uploaded artifacts.
deploy/​sql/​migrations/​v2.9.0_009_question_bookmarks.sql Adds persistent bookmark storage.
backend/​runtime/​api/​application.py Registers bookmark routes.
backend/​management/​services/​conversation/​service.py Adds branching and annotation-history handling.
backend/​management/​services/​conversation/​bookmarks.py Validates bookmark operations.
backend/​management/​services/​agent/​run.py Incorporates annotations into model input.
backend/​management/​db/​conversation/​models.py Maps the bookmark storage column.
backend/​management/​db/​conversation/​branch_titles.py Allocates numbered branch titles.
backend/​management/​db/​conversation/​bookmarks.py Implements owner-scoped bookmark persistence.
backend/​management/​api/​conversation/​question_bookmarks.py Adds authenticated bookmark endpoints.
backend/​management/​api/​conversation/​conversation_management.py Exposes conversation branching.
backend/​contracts/​conversation.py Adds annotation and branching contracts.
backend/​contracts/​annotations.py Defines annotation normalization and formatting.
backend/​contracts/​agent.py Adds structured request annotations.

Review effort: Balanced

@codecov

codecov Bot commented Nov 9, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 94.02390% with 15 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
backend/management/db/conversation/repository.py 89.83% 2 Missing and 10 partials ⚠️
...gement/api/conversation/conversation_management.py 90.90% 2 Missing ⚠️
...ackend/management/services/conversation/service.py 96.29% 0 Missing and 1 partial ⚠️

📢 Thoughts on this report? Let us know!

ljy added 5 commits November 9, 2026 17:04
Merge develop at 3de8384 while retaining conversation branching, reply annotations and artifact preview changes.

Resolve overlapping repository imports by retaining the dependencies from both branches and removing duplicates. No conversation function definitions are removed. Existing SQL files match develop.

Validation: 464 targeted backend tests passed in isolated temporary directories, covering branch selection/copying, annotations, history, multi-role permissions, database, services and API contracts. The API suite required pytest-asyncio in the temporary test path. Copyright checks passed for all 55 changed source files relative to develop; git diff --check passed. Full application and live integration suites were not run.
@jeffwu-1999
jeffwu-1999 merged commit 652d1c1 into develop Nov 13, 2026
16 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants