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:
ProfessorNova
2026-09-04 09:21:37 +02:00
co-authored by Claude Opus 5
parent 68d3a43191
commit 784b76baf7
346 changed files with 43430 additions and 0 deletions
+107
View File
@@ -0,0 +1,107 @@
"""Markdown chunking along the heading hierarchy.
Chunk size uses a character heuristic (~4 chars/token, target ~400 tokens);
no tokenizer dependency — precision is not required for chunk sizing, and a
real tokenizer would not match local model tokenizers anyway. Sections that
exceed the cap are split at paragraph boundaries, never inside code fences.
"""
import re
from dataclasses import dataclass
# ~400 tokens at the ~4 chars/token heuristic.
TARGET_CHUNK_CHARS = 1600
HEADING_RE = re.compile(r"^(#{1,6})\s+(.*?)\s*#*\s*$")
HEADING_PATH_SEPARATOR = " "
@dataclass
class ChunkData:
content: str
heading_path: str
@dataclass
class _Section:
path: list[str]
lines: list[str]
@property
def text(self) -> str:
return "\n".join(self.lines).strip()
def _split_sections(content_md: str, title: str) -> list[_Section]:
sections: list[_Section] = [_Section(path=[title], lines=[])]
heading_stack: list[tuple[int, str]] = [] # (level, text)
in_fence = False
for line in content_md.splitlines():
if line.lstrip().startswith("```"):
in_fence = not in_fence
match = None if in_fence else HEADING_RE.match(line)
if match:
level = len(match.group(1))
text = match.group(2).strip()
while heading_stack and heading_stack[-1][0] >= level:
heading_stack.pop()
heading_stack.append((level, text))
path = [title, *(heading for _, heading in heading_stack)]
# Drop a leading H1 that just repeats the document title.
if len(path) > 1 and path[1] == title:
path = [title, *path[2:]]
sections.append(_Section(path=path, lines=[line]))
else:
sections[-1].lines.append(line)
return [section for section in sections if section.text]
def _split_paragraphs(text: str) -> list[str]:
"""Split at blank lines, but never inside a ``` fence."""
paragraphs: list[str] = []
current: list[str] = []
in_fence = False
for line in text.splitlines():
if line.lstrip().startswith("```"):
in_fence = not in_fence
if not line.strip() and not in_fence:
if current:
paragraphs.append("\n".join(current))
current = []
else:
current.append(line)
if current:
paragraphs.append("\n".join(current))
return paragraphs
def _split_oversized(text: str) -> list[str]:
pieces: list[str] = []
current = ""
for paragraph in _split_paragraphs(text):
candidate = f"{current}\n\n{paragraph}" if current else paragraph
if current and len(candidate) > TARGET_CHUNK_CHARS:
pieces.append(current)
current = paragraph
else:
current = candidate
if current:
pieces.append(current)
return pieces
def chunk_markdown(content_md: str, title: str) -> list[ChunkData]:
"""Split a document into chunks; each chunk has exactly one heading path."""
chunks: list[ChunkData] = []
for section in _split_sections(content_md, title):
heading_path = HEADING_PATH_SEPARATOR.join(section.path)
text = section.text
if len(text) <= TARGET_CHUNK_CHARS:
chunks.append(ChunkData(content=text, heading_path=heading_path))
else:
for piece in _split_oversized(text):
chunks.append(ChunkData(content=piece, heading_path=heading_path))
return chunks