Coverage for src/lilbee/server/routes/sessions.py: 100%
40 statements
« prev ^ index » next coverage.py v7.15.2, created at 2026-09-28 17:20 +0000
« prev ^ index » next coverage.py v7.15.2, created at 2026-09-28 17:20 +0000
1"""Session routes: list, get, markdown, create, append, fork, summary, rename, delete.
3Every route requires the token, reads included: a transcript is at least as
4personal as the memory store next door.
5"""
7from __future__ import annotations
9from litestar import Response, delete, get, patch, post, put
10from litestar.datastructures import ResponseHeader
11from litestar.exceptions import NotFoundException
12from litestar.params import FromPath
14from lilbee.data.types import MARKDOWN_MIME
15from lilbee.server.content_disposition import CONTENT_DISPOSITION, attachment_disposition
16from lilbee.server.handlers.sessions import (
17 add_session_message,
18 claim_session,
19 create_session,
20 delete_session,
21 fork_session,
22 get_session,
23 get_session_markdown,
24 list_sessions,
25 rename_session,
26 set_session_summary,
27)
28from lilbee.server.models import (
29 SessionCreateRequest,
30 SessionDeleteResponse,
31 SessionDetailResponse,
32 SessionForkRequest,
33 SessionListResponse,
34 SessionMessageCreateRequest,
35 SessionRenameRequest,
36 SessionRenameResponse,
37 SessionSummaryRequest,
38)
41@get("/api/sessions")
42async def sessions_list_route() -> SessionListResponse:
43 """List saved conversations, newest first."""
44 return await list_sessions()
47@get("/api/sessions/{session_id:str}")
48async def session_get_route(session_id: FromPath[str]) -> SessionDetailResponse:
49 """Return a conversation's metadata and full transcript."""
50 return await get_session(session_id)
53@get(
54 "/api/sessions/{session_id:str}/markdown",
55 media_type=MARKDOWN_MIME,
56 raises=[NotFoundException],
57 response_headers=[
58 ResponseHeader(
59 name=CONTENT_DISPOSITION,
60 description="The export's file name: the title slug and the session id prefix.",
61 documentation_only=True,
62 )
63 ],
64)
65async def session_markdown_route(session_id: FromPath[str]) -> Response[str]:
66 """Return a conversation as a markdown document, named for download."""
67 export = await get_session_markdown(session_id)
68 return Response(
69 export.markdown,
70 media_type=MARKDOWN_MIME,
71 headers={CONTENT_DISPOSITION: attachment_disposition(export.filename)},
72 )
75@post("/api/sessions")
76async def session_create_route(data: SessionCreateRequest) -> SessionDetailResponse:
77 """Start a new conversation."""
78 return await create_session(data)
81@post("/api/sessions/{session_id:str}/messages")
82async def session_add_message_route(
83 session_id: FromPath[str], data: SessionMessageCreateRequest
84) -> SessionDetailResponse:
85 """Append a turn to a conversation."""
86 return await add_session_message(session_id, data)
89@post("/api/sessions/{session_id:str}/fork")
90async def session_fork_route(
91 session_id: FromPath[str], data: SessionForkRequest | None = None
92) -> SessionDetailResponse:
93 """Start a new conversation from a copy of this one's leading messages."""
94 return await fork_session(session_id, data)
97@post("/api/sessions/{session_id:str}/claim")
98async def session_claim_route(session_id: FromPath[str]) -> SessionDetailResponse:
99 """Claim a conversation for this surface so it can append."""
100 return await claim_session(session_id)
103@put("/api/sessions/{session_id:str}/summary")
104async def session_set_summary_route(
105 session_id: FromPath[str], data: SessionSummaryRequest
106) -> SessionDetailResponse:
107 """Replace a conversation's compaction summary."""
108 return await set_session_summary(session_id, data)
111@patch("/api/sessions/{session_id:str}")
112async def session_rename_route(
113 session_id: FromPath[str], data: SessionRenameRequest
114) -> SessionRenameResponse:
115 """Rename a conversation."""
116 return await rename_session(session_id, data.title)
119@delete("/api/sessions/{session_id:str}", status_code=200)
120async def session_delete_route(session_id: FromPath[str]) -> SessionDeleteResponse:
121 """Delete a conversation."""
122 return await delete_session(session_id)