mirror of
https://github.com/TencentCloud/Octop.git
synced 2026-10-15 05:30:33 +08:00
* feat(knowledge): include document path in citation markers * feat(chat): in-chat preview for knowledge citations (#634) Open Sources chips in a chat modal (rich with text fallback), show KB/path in the tooltip, and deep-link View in knowledge base to the right folder. * polish(chat): citation preview UX — tooltip, nav, download, path Only show tooltip when nested path or truncation; match KB modal width for markdown; hide download without original; prev/next + arrow keys; scroll-to-top for long MD; resolve path/has_original from document list. * polish(dashboard): open citation preview and expert file edit as right drawers Align chat knowledge citation preview and expert workspace file edit with the knowledge-base text editor: placement=right, shared width, footer actions. * polish(chat): put knowledge base name inline in citation drawer title Show filename · kbName on one line; keep nested folder path as the only subtitle. * refactor(chat): open knowledge citations in the shared dock panel Match “edited files” behavior: Sources chips open knowledge tabs in the chat dock instead of a separate Ant Design drawer. * fix(dashboard): keep nearby PDF pages mounted to avoid blank scroll canvases Windowed react-pdf unmount/remount left blank pages while scrolling; keep pages sticky around the current page and tighten citation dock flex height.
96 lines
2.4 KiB
Python
96 lines
2.4 KiB
Python
"""Unit tests for knowledge citation markers."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from types import SimpleNamespace
|
|
|
|
from octop.infra.knowledge.citations import (
|
|
CITATIONS_MARKER_PREFIX,
|
|
append_citations_marker,
|
|
citations_from_ranked,
|
|
strip_citations_marker,
|
|
)
|
|
|
|
|
|
def test_citations_from_ranked_dedupes_documents() -> None:
|
|
base = SimpleNamespace(id="kb1", name="Policies")
|
|
doc_a = SimpleNamespace(id="d1", filename="a.md", path="notes/a.md")
|
|
doc_b = SimpleNamespace(id="d2", filename="b.md", path="b.md")
|
|
hit = SimpleNamespace()
|
|
ranked = [
|
|
(base, hit, doc_a),
|
|
(base, hit, doc_a),
|
|
(base, hit, doc_b),
|
|
]
|
|
assert citations_from_ranked(ranked) == [
|
|
{
|
|
"kb_id": "kb1",
|
|
"kb_name": "Policies",
|
|
"doc_id": "d1",
|
|
"filename": "a.md",
|
|
"path": "notes/a.md",
|
|
},
|
|
{
|
|
"kb_id": "kb1",
|
|
"kb_name": "Policies",
|
|
"doc_id": "d2",
|
|
"filename": "b.md",
|
|
"path": "b.md",
|
|
},
|
|
]
|
|
|
|
|
|
def test_append_citations_marker() -> None:
|
|
text = append_citations_marker(
|
|
"passages here",
|
|
[
|
|
{
|
|
"kb_id": "kb",
|
|
"kb_name": "KB",
|
|
"doc_id": "d1",
|
|
"filename": "doc.md",
|
|
"path": "doc.md",
|
|
}
|
|
],
|
|
)
|
|
assert text.startswith("passages here\n\n" + CITATIONS_MARKER_PREFIX)
|
|
assert '"filename":"doc.md"' in text
|
|
assert '"path":"doc.md"' in text
|
|
assert text.endswith("-->")
|
|
|
|
|
|
def test_append_citations_marker_skips_empty() -> None:
|
|
assert (
|
|
append_citations_marker(
|
|
"",
|
|
[
|
|
{
|
|
"kb_id": "k",
|
|
"kb_name": "n",
|
|
"doc_id": "d",
|
|
"filename": "f",
|
|
"path": "f",
|
|
}
|
|
],
|
|
)
|
|
== ""
|
|
)
|
|
assert append_citations_marker("text", []) == "text"
|
|
|
|
|
|
def test_strip_citations_marker() -> None:
|
|
raw = append_citations_marker(
|
|
"passages here",
|
|
[
|
|
{
|
|
"kb_id": "kb",
|
|
"kb_name": "KB",
|
|
"doc_id": "d1",
|
|
"filename": "doc.md",
|
|
"path": "notes/doc.md",
|
|
}
|
|
],
|
|
)
|
|
assert strip_citations_marker(raw) == "passages here"
|
|
assert strip_citations_marker("plain") == "plain"
|