feat(web): enhance mention handling to support connectors and improve document key management

This commit is contained in:
Anish Sarkar 2026-05-26 21:52:04 +05:30
parent a41b16b73e
commit 2d134439ec
11 changed files with 160 additions and 164 deletions

View file

@ -203,13 +203,11 @@ class NewChatUserImagePart(BaseModel):
class MentionedDocumentInfo(BaseModel):
"""Display metadata for a single ``@``-mention chip.
Carries either a knowledge-base document or a knowledge-base folder
(discriminated by ``kind``). The full triple
``{id, title, document_type}`` is forwarded by the frontend mention
chip so the server can embed it in the persisted user message
``ContentPart[]`` (single ``mentioned-documents`` part). The
history loader then renders the chips on reload without an extra
fetch mirrors the pre-refactor frontend ``persistUserTurn`` shape.
Carries a knowledge-base document, knowledge-base folder, or
connected account (discriminated by ``kind``). Each kind uses its
real identity fields: docs carry ``document_type``, folders carry
only their folder id/title, and connectors carry ``connector_type``
plus account metadata.
``kind`` defaults to ``"doc"`` so legacy clients and persisted rows
that predate folder mentions deserialise unchanged.
@ -217,17 +215,14 @@ class MentionedDocumentInfo(BaseModel):
id: int
title: str = Field(..., min_length=1, max_length=500)
document_type: str = Field(..., min_length=1, max_length=100)
document_type: str | None = Field(default=None, min_length=1, max_length=100)
kind: Literal["doc", "folder", "connector"] = Field(
default="doc",
description=(
"Discriminator for the chip's referent: ``doc`` is a "
"knowledge-base ``Document`` row, ``folder`` is a "
"knowledge-base ``Folder`` row, and ``connector`` is a "
"concrete connected account. Folders carry the sentinel "
"``document_type='FOLDER'`` to keep the frontend dedup key "
"``(kind:document_type:id)`` from colliding doc and folder "
"ids that happen to share an integer value."
"concrete connected account."
),
)
connector_type: str | None = Field(default=None, max_length=100)

View file

@ -109,7 +109,7 @@ def _build_user_content(
[{"type": "text", "text": "..."},
{"type": "image", "image": "data:..."},
{"type": "mentioned-documents", "documents": [{"id": int,
"title": str, "document_type": str, "kind": "doc" | "folder"},
"title": str, "kind": "doc" | "folder" | "connector", ...},
...]}]
The companion reader is
@ -117,8 +117,8 @@ def _build_user_content(
which expects exactly this shape keep them in sync.
``mentioned_documents``: optional list of mention chip dicts. Each
dict may include a ``kind`` discriminator (``"doc"`` or ``"folder"``)
so the persisted ContentPart round-trips folder chips on reload.
dict may include a ``kind`` discriminator so the persisted
ContentPart round-trips folder and connector chips on reload.
When ``kind`` is missing we default to ``"doc"`` so legacy clients
that haven't migrated to the union schema still persist correctly.
"""
@ -134,18 +134,23 @@ def _build_user_content(
doc_id = doc.get("id")
title = doc.get("title")
document_type = doc.get("document_type")
if doc_id is None or title is None or document_type is None:
continue
kind_raw = doc.get("kind", "doc")
kind = kind_raw if kind_raw in ("doc", "folder", "connector") else "doc"
if doc_id is None or title is None:
continue
if kind == "doc" and document_type is None:
continue
item = {
"id": doc_id,
"title": str(title),
"document_type": str(document_type),
"kind": kind,
}
if document_type is not None:
item["document_type"] = str(document_type)
if kind == "connector":
connector_type = doc.get("connector_type") or document_type
connector_type = doc.get("connector_type")
if connector_type is None:
continue
account_name = doc.get("account_name") or title
item["connector_type"] = str(connector_type)
item["account_name"] = str(account_name)