feat(summary): AI catch-me-up summaries for rooms + threads [operator-action] (LC-484) #460

Merged
longjacksonle merged 5 commits from feat/LC-484-catch-me-up-summaries into main 2026-06-27 20:15:02 +02:00

What

AI "Catch me up" summaries for rooms and threads (LC-484, shortlist item from epic LC-475). Reuses the existing operator LLM client (LC-396) with a chat-specific prompt, so this is mostly plumbing.

  • Channel "Catch me up": a room-header action opens a drawer (shared #thread-panel slot) that summarizes the viewer's unread range, falling back to the recent tail when nothing is unread.
  • Thread "Summarize": a thread-panel action summarizes the thread root + replies in place.

Both render the model's markdown through the same views::markdown pipeline as messages, and both are gated on an operator-configured LLM (the actions are hidden when unset).

How (small commits)

  1. refactor(llm) - generalize LlmClient to a core complete(system, user); summarize() stays as a default wrapper, so transcript behavior is unchanged.
  2. feat(summary) - routes/summary.rs + views/summary.rs + two templates + routes; chat prompt; no persistent cache (thread grows / unread is per-viewer, so a cached summary would be stale - generate on demand with a regenerate button, mirroring the transcript action).
  3. feat(summary) - wire the Summarize + Catch me up buttons; thread llm_available flag through RoomPage, ThreadPanelFragment, RoomHeaderFragment.
  4. test(summary) - 4 HTTP tests via the transcripts/MockLlmClient harness.
  5. docs(summary) - operator note.

Tests

just check, just test, just test-saas green (standalone + saas, clippy -D warnings). New tests: channel summary renders model output, thread summary renders it, 400 when LLM disabled, drawer opens.

Operator note

[operator-action] marker: no action required, but enabling LETS_CHAT_LLM_URL now sends room/thread message text to the endpoint for on-demand summaries, in addition to call transcripts (load/cost/content consideration on metered or third-party engines). Documented in the CLAUDE.md LLM row + llm module header.

Verify

just verify builds the release binary clean; the boot step fails only on the missing mandatory LETS_CHAT_BUNYIP_SSO_* env (same environmental block as prior PRs), not a regression.

## What AI "Catch me up" summaries for rooms and threads (LC-484, shortlist item from epic LC-475). Reuses the existing operator LLM client (LC-396) with a chat-specific prompt, so this is mostly plumbing. - **Channel "Catch me up"**: a room-header action opens a drawer (shared `#thread-panel` slot) that summarizes the viewer's unread range, falling back to the recent tail when nothing is unread. - **Thread "Summarize"**: a thread-panel action summarizes the thread root + replies in place. Both render the model's markdown through the same `views::markdown` pipeline as messages, and both are gated on an operator-configured LLM (the actions are hidden when unset). ## How (small commits) 1. `refactor(llm)` - generalize `LlmClient` to a core `complete(system, user)`; `summarize()` stays as a default wrapper, so transcript behavior is unchanged. 2. `feat(summary)` - `routes/summary.rs` + `views/summary.rs` + two templates + routes; chat prompt; no persistent cache (thread grows / unread is per-viewer, so a cached summary would be stale - generate on demand with a regenerate button, mirroring the transcript action). 3. `feat(summary)` - wire the Summarize + Catch me up buttons; thread `llm_available` flag through `RoomPage`, `ThreadPanelFragment`, `RoomHeaderFragment`. 4. `test(summary)` - 4 HTTP tests via the transcripts/MockLlmClient harness. 5. `docs(summary)` - operator note. ## Tests `just check`, `just test`, `just test-saas` green (standalone + saas, clippy `-D warnings`). New tests: channel summary renders model output, thread summary renders it, 400 when LLM disabled, drawer opens. ## Operator note `[operator-action]` marker: no action required, but enabling `LETS_CHAT_LLM_URL` now sends room/thread message text to the endpoint for on-demand summaries, in addition to call transcripts (load/cost/content consideration on metered or third-party engines). Documented in the CLAUDE.md LLM row + `llm` module header. ## Verify `just verify` builds the release binary clean; the boot step fails only on the missing mandatory `LETS_CHAT_BUNYIP_SSO_*` env (same environmental block as prior PRs), not a regression.
Generalize LlmClient so the chat-completions call can take an arbitrary system + user prompt. complete() is now the core method; summarize() defaults to complete() with the transcript SYSTEM_PROMPT, so transcript behavior is unchanged. Lets LC-484 reuse the same client + plumbing with a chat-specific "catch me up" prompt.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
New routes/summary.rs reuses the operator LLM client with a chat-specific prompt to summarize a thread (root + replies) or a channel's unread range (falling back to the recent tail when nothing is unread). GET /room/{id}/summary opens a drawer in the shared #thread-panel slot; POST /room/{id}/summary and POST /room/{id}/thread/{parent}/summary generate the markdown and render it through the message markdown pipeline. No persistent cache: a thread keeps growing and an unread range is per-viewer, so each click generates fresh (regenerate button), mirroring the transcript action. i18n keys added in en + es.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Thread panel header gets a "Summarize" action that swaps the AI summary into a new #thread-summary-{parent} slot; the room header gets a "Catch me up" action that opens the summary drawer. Both gated on an operator-configured LLM via a new llm_available flag threaded through RoomPage, ThreadPanelFragment, and RoomHeaderFragment (so the action survives the notify-prefs header swap).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Four HTTP-level tests via the transcripts harness (MockLlmClient): channel summary renders the model output, thread summary renders it, the action 400s when no LLM is configured, and the catch-me-up drawer opens. Also applies rustfmt to the new llm/summary code.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
docs(summary): note LLM now also receives chat text [operator-action] (LC-484)
Some checks failed
check-secrets / Nosey parker (push) Successful in 5s
check-secrets / TruffleHog (push) Successful in 7s
check-secrets / Kingfisher (push) Failing after 9s
check-secrets / Nosey parker (pull_request) Successful in 5s
check-secrets / Kingfisher (pull_request) Failing after 9s
check-secrets / TruffleHog (pull_request) Successful in 11s
Check / clippy + fmt + tests (pull_request) Successful in 5m39s
Create release / Create release from merged PR (pull_request) Has been skipped
68e07f45af
The "Catch me up" / "Summarize" actions reuse the existing LETS_CHAT_LLM_* endpoint, so enabling the LLM now sends recent/unread room and thread message text in addition to call transcripts. Documented in the CLAUDE.md LLM env row and the llm module header. No new config.

Operator-Action: No action required. Be aware that with LETS_CHAT_LLM_URL set, room/thread message text is now sent to that endpoint for on-demand summaries (load/cost/content consideration on metered or third-party engines).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
longjacksonle scheduled this pull request to auto merge when all checks succeed 2026-06-27 20:11:48 +02:00
longjacksonle deleted branch feat/LC-484-catch-me-up-summaries 2026-06-27 20:15:02 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
psa-systems/lets-chat!460
No description provided.