Pablan, as it stands
Self-hosted knowledge management for SMEs: a split-screen Markdown editor whose sections an LLM refines while you write, and RAG question answering over the documents that result. FastAPI + Postgres/pgvector on the back, SvelteKit on the front, everything OpenAI-compatible and self-hostable. Squashed into a single commit; the development history stays local. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CA43ZJda8Rbp2hKXNy8f6b
This commit is contained in:
@@ -0,0 +1,82 @@
|
||||
"""The citation popover shows prose, not Markdown source.
|
||||
|
||||
The popover is a small hover surface showing a fragment, so the excerpt is
|
||||
reduced to prose rather than rendered: a cited table would otherwise become
|
||||
a real table squeezed into ~320px, and a cited heading would render at h2
|
||||
size. Clicking the badge opens the document in the side panel, which does
|
||||
render Markdown properly.
|
||||
|
||||
These cases are all real shapes from the fixture corpus.
|
||||
"""
|
||||
|
||||
from app.modes.query import EXCERPT_CHARS
|
||||
from app.modes.query import excerpt as make_excerpt
|
||||
|
||||
|
||||
def test_a_cited_table_reads_as_prose() -> None:
|
||||
"""The worst case, and the one that sent this back for a second pass:
|
||||
the divider row is pure punctuation and survives naive stripping."""
|
||||
excerpt = make_excerpt(
|
||||
"| Code | Bedeutung | Sofortmaßnahme |\n"
|
||||
"|-------|------------|----------------|\n"
|
||||
"| E-101 | Not-Aus-Kreis unterbrochen | Alle Not-Aus-Taster prüfen |"
|
||||
)
|
||||
assert "|" not in excerpt
|
||||
assert "---" not in excerpt
|
||||
assert excerpt.startswith("Code · Bedeutung · Sofortmaßnahme · E-101")
|
||||
|
||||
|
||||
def test_inline_emphasis_loses_its_markers_not_its_words() -> None:
|
||||
for source, expected in [
|
||||
("**Eingang erfassen**: im ERP anlegen.", "Eingang erfassen: im ERP anlegen."),
|
||||
("die Presse muss *drucklos* sein", "die Presse muss drucklos sein"),
|
||||
("__Achtung__ beim Anfahren", "Achtung beim Anfahren"),
|
||||
("setze `max_turns` hoch", "setze max_turns hoch"),
|
||||
("**fett _und_ kursiv**", "fett und kursiv"),
|
||||
]:
|
||||
assert make_excerpt(source) == expected
|
||||
|
||||
|
||||
def test_links_keep_their_text_and_drop_their_target() -> None:
|
||||
excerpt = make_excerpt(
|
||||
"Siehe [das Handbuch](https://intranet.example/doc.pdf) dazu."
|
||||
)
|
||||
assert excerpt == "Siehe das Handbuch dazu."
|
||||
# A URL in a hover preview is noise, and it is also the part a reader
|
||||
# cannot click here anyway.
|
||||
assert "http" not in excerpt
|
||||
|
||||
|
||||
def test_block_markers_and_fences_are_dropped() -> None:
|
||||
excerpt = make_excerpt(
|
||||
"## Wartung\n\n"
|
||||
"- **Getriebeöl GX-220**: monatlich\n"
|
||||
"1. Bettbahnöl wöchentlich\n"
|
||||
"> Wichtig: erst entlüften\n"
|
||||
"```yaml\n"
|
||||
"interval: 30\n"
|
||||
"```"
|
||||
)
|
||||
assert excerpt.startswith("Wartung Getriebeöl GX-220: monatlich")
|
||||
for marker in ("##", "```", "> ", "**"):
|
||||
assert marker not in excerpt
|
||||
|
||||
|
||||
def test_plain_prose_is_left_alone() -> None:
|
||||
source = "Die Kaffeemaschine wird freitags entkalkt."
|
||||
assert make_excerpt(source) == source
|
||||
|
||||
|
||||
def test_underscores_inside_identifiers_survive() -> None:
|
||||
"""`result_document_id` is not italics. Emphasis needs a non-space
|
||||
character on both sides, which is what keeps snake_case intact."""
|
||||
assert make_excerpt("das Feld result_document_id bleibt leer") == (
|
||||
"das Feld result_document_id bleibt leer"
|
||||
)
|
||||
|
||||
|
||||
def test_long_content_is_cut_on_a_word_boundary() -> None:
|
||||
excerpt = make_excerpt("Wartung " * 200)
|
||||
assert len(excerpt) <= EXCERPT_CHARS + 1 # the ellipsis
|
||||
assert excerpt.endswith("…")
|
||||
assert not excerpt.rstrip("…").endswith(" ")
|
||||
Reference in New Issue
Block a user