feat(compose): AI writing assistant in the composer (LC-532) #506

Merged
longjacksonle merged 3 commits from feat/LC-532-composer-ai-assist into main 2026-07-03 05:49:49 +02:00

First build from the Round 3 shortlist (LC-531): a composer AI writing assistant.

What it does: a sparkle button in the composer sends your current draft to the operator's LLM and returns a rewritten suggestion in five modes - rephrase, fix grammar, concise, friendly, formal. "Use this" drops it into the composer; the mode chips regenerate on the current draft; "Dismiss" clears it.

Stack fit: reuses the already-wired llm::complete (mirrors routes::translate), the composer, and the [data-lc-llm] CSS gate. The button is hidden entirely unless an LLM is configured. No new DB, no new dependency - just a new endpoint + a composer affordance.

Three small commits: endpoint + panel template, composer wiring (button/slot/apply-dismiss JS), tests.

Privacy (the one call-out from the brainstorm): unlike the existing AI features which act on already-posted text, this sends an unsent draft to the LLM. It is therefore explicit opt-in per click, gated on an LLM being configured, never auto-run, and the draft is length-capped before sending. The integration test asserts that with no LLM configured the endpoint 400s so the draft never leaves the device.

Verification: end-to-end HTTP test with a mock LLM (happy path returns the panel with the suggestion + chips; empty draft 400s; no-LLM 400s) + inline unit tests. just test + just test-saas + just check green.

Not operator-visible (no new env/config; it rides the existing LETS_CHAT_LLM_*).

🤖 Generated with Claude Code

First build from the Round 3 shortlist (LC-531): a composer AI writing assistant. **What it does:** a sparkle button in the composer sends your current draft to the operator's LLM and returns a rewritten suggestion in five modes - rephrase, fix grammar, concise, friendly, formal. "Use this" drops it into the composer; the mode chips regenerate on the current draft; "Dismiss" clears it. **Stack fit:** reuses the already-wired `llm::complete` (mirrors `routes::translate`), the composer, and the `[data-lc-llm]` CSS gate. The button is hidden entirely unless an LLM is configured. No new DB, no new dependency - just a new endpoint + a composer affordance. Three small commits: endpoint + panel template, composer wiring (button/slot/apply-dismiss JS), tests. **Privacy (the one call-out from the brainstorm):** unlike the existing AI features which act on already-posted text, this sends an *unsent draft* to the LLM. It is therefore explicit opt-in per click, gated on an LLM being configured, never auto-run, and the draft is length-capped before sending. The integration test asserts that with no LLM configured the endpoint 400s so the draft never leaves the device. **Verification:** end-to-end HTTP test with a mock LLM (happy path returns the panel with the suggestion + chips; empty draft 400s; no-LLM 400s) + inline unit tests. `just test` + `just test-saas` + `just check` green. Not operator-visible (no new env/config; it rides the existing `LETS_CHAT_LLM_*`). 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Adds POST /room/{id}/compose-assist: gate on an LLM being configured + room access, then rewrite the draft via llm::complete in one of five modes (rephrase / grammar / concise / friendly / formal) and return a panel with the suggestion + mode chips. Mirrors routes::translate. The draft is length-capped (4000 chars) before it is sent; empty drafts and unknown actions are handled. i18n en + es; inline unit tests cover action resolution and prompt shape.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NTRhc4n7A2uB2Y5PHV2ewL
Adds the composer sparkle button (CSS-gated on [data-lc-llm], like the translate item) that posts the current draft to /compose-assist, an empty #composer-ai-panel slot, and the live.js delegated handler: "Use this" writes the suggestion into the composer textarea and fires an input event so autosize/highlight/draft-autosave pick it up; "Dismiss" clears the panel. The mode chips re-run on the current draft (hx-include the composer form).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NTRhc4n7A2uB2Y5PHV2ewL
test(compose): end-to-end AI-assist happy path + gates (LC-532)
All checks were successful
check-secrets / Nosey parker (push) Successful in 6s
check-secrets / Kingfisher (push) Successful in 8s
check-secrets / TruffleHog (push) Successful in 9s
check-secrets / Nosey parker (pull_request) Successful in 7s
check-secrets / Kingfisher (pull_request) Successful in 10s
check-secrets / TruffleHog (pull_request) Successful in 11s
Check / clippy + fmt + tests (pull_request) Successful in 6m11s
Create release / Create release from merged PR (pull_request) Has been skipped
07089ce933
HTTP integration test with a mock LLM: rephrase returns the panel with the suggestion + mode chips; an empty draft 400s; and with no LLM configured the endpoint 400s so the draft never leaves the device.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NTRhc4n7A2uB2Y5PHV2ewL
longjacksonle deleted branch feat/LC-532-composer-ai-assist 2026-07-03 05:49:49 +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!506
No description provided.