feat(calls): optional LLM transcript summaries (LC-396) #410
Loading…
Reference in a new issue
No description provided.
Delete branch "feat/lc-396-transcript-llm-summary"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
The optional final piece of the LC-393 transcription epic: an operator-selectable AI summary / action-items for saved transcripts. Filed as LC-396.
What it does
When
LETS_CHAT_LLM_URLis set, the transcript page gains a "Summarize" action: it sends the transcript text to an OpenAI-compatible/v1/chat/completionsendpoint (Ollama, llama.cpp server, vLLM, LocalAI, ...) and stores the returned markdown summary + action items. Cached summaries render on the page; a Regenerate button re-runs it. When no LLM is configured the UI is hidden entirely.Architecture (mirrors the Phase 3 STT pattern)
llmmodule:LlmConfig::from_env(LETS_CHAT_LLM_URL/_API_KEY/_MODEL, default modelgpt-4o-mini), anLlmClienttrait withReqwestLlmClient(chat-completions POST) +MockLlmClient.AppStategainsllm_client: Option<...>+llm_available().call_transcripts.summarycolumn (migration0067), cached so it's computed once. Summary is markdown, rendered through the same markdown pipeline as messages, and length-capped on the write path (LC-153).POST /transcripts/{id}/summary: gated like the transcript page, builds the transcript text (bounded prompt), calls the LLM, stores + returns the rendered fragment (htmx swap). 400 when no LLM is configured.http_client::outbound_trusted_postpath as STT, so a localhost model works. System prompt asks for a concise## Summary+## Action itemslist.Testing
tests/transcripts.rs-> 12 green:summary_generates_stores_and_gates(mock LLM -> fragment + persisted + shown on the page + non-member 403) andsummary_rejected_when_llm_disabled(400).just check,just test,just test-saasall exit 0 (migration 0067 picked up by the common test pools; all 87 plainAppStateliterals updated, the transcripts harness injects a mock).just build-cssregenerated.Operator-action
Optional: set
LETS_CHAT_LLM_URL(+ optional_API_KEY/_MODEL) to an OpenAI-compatible chat-completions endpoint to enable AI transcript summaries; leave unset to hide the feature. Documented in CLAUDE.md.With this, the LC-393 transcription epic (Phases 1-4) is complete.
Note
Branches off
main; #408 (archive) and #409 (export/search) are still open and also touchtranscripts/show.htmlin different sections - if they land first this rebases cleanly (the summary block is a separate part of the page).The optional final piece of the LC-393 transcription epic: an operator-selectable AI summary / action-items for a saved transcript. When LETS_CHAT_LLM_URL is set, the transcript page gains a "Summarize" action that sends the transcript text to an OpenAI-compatible /v1/chat/completions endpoint (Ollama, llama.cpp server, vLLM, LocalAI, ...) and stores the returned markdown summary + action items, cached in a new call_transcripts.summary column (migration 0067). Cached summaries render on the page; a Regenerate button re-runs it. - New `llm` module mirroring the Phase 3 `stt` pattern: LlmConfig::from_env (LETS_CHAT_LLM_URL/_API_KEY/_MODEL, default model gpt-4o-mini), an LlmClient trait with ReqwestLlmClient + MockLlmClient. AppState gains llm_client: Option<...> + llm_available(). - POST /transcripts/{id}/summary: gated like the transcript page, builds the transcript text (bounded), calls the LLM, stores the summary (length-capped for the LC-153 markdown render), returns the rendered fragment (htmx swap). 400 when no LLM is configured. - The endpoint is operator-trusted and reached through the same blessed, NOT-SSRF-filtered http_client::outbound_trusted_post path as STT, so a localhost model works. - The summary markdown is rendered through the existing message markdown pipeline; the UI is hidden entirely when no LLM is configured. All 87 plain test AppState literals get llm_client: None; the transcripts harness injects a mock. Tests cover generate+store+render, the disabled-400, and non-member gating. Operator-Action: optional - set LETS_CHAT_LLM_URL (+ optional _API_KEY / _MODEL) to an OpenAI-compatible chat-completions endpoint to enable AI transcript summaries; leave unset to hide the feature. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>