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
44 lines
1.5 KiB
Python
44 lines
1.5 KiB
Python
"""Rows to API shapes.
|
|
|
|
A conversation has no title column: the first message is the title, derived
|
|
here so the list and the detail can never disagree about what a conversation
|
|
is called.
|
|
"""
|
|
|
|
from app.api.conversations.schemas import MessageOut, MessageSource
|
|
from app.models import Message
|
|
from app.modes.query import excerpt as clean_excerpt
|
|
|
|
TITLE_LENGTH = 80
|
|
|
|
|
|
def title(first_message: str | None) -> str | None:
|
|
if not first_message:
|
|
return None
|
|
flattened = " ".join(first_message.split())
|
|
if len(flattened) <= TITLE_LENGTH:
|
|
return flattened
|
|
return flattened[: TITLE_LENGTH - 1] + "…"
|
|
|
|
|
|
def message_out(message: Message) -> MessageOut:
|
|
"""Message + its citation snapshot from `meta` (assistant turns only).
|
|
|
|
The excerpt is re-cleaned on the way out, not just on the way in. It is
|
|
a presentation detail frozen at answer time, so an improvement to the
|
|
cleaning would otherwise only reach conversations created afterwards,
|
|
and every existing citation would keep showing raw Markdown forever.
|
|
Cleaning is idempotent, so text stored by a newer backend passes
|
|
through untouched.
|
|
"""
|
|
meta = message.meta or {}
|
|
sources = [
|
|
MessageSource.model_validate(item).model_copy(
|
|
update={"excerpt": clean_excerpt(item.get("excerpt", ""))}
|
|
)
|
|
for item in meta.get("sources", [])
|
|
]
|
|
return MessageOut.model_validate(message).model_copy(
|
|
update={"sources": sources, "fallback": meta.get("fallback")}
|
|
)
|