feat(assistant): in-channel RAG AI assistant /ask (LC-492) #489

Merged
longjacksonle merged 5 commits from feat/LC-492-assistant into main 2026-06-29 02:18:14 +02:00

LC-492 - In-channel AI assistant with RAG (/ask)

A first vertical slice of the "big bet" assistant: /ask <question> in a room retrieves context and answers with the operator LLM, posting the reply as an assistant bot. Reuses the existing FTS, LLM client, bot-user, and broadcast plumbing; the new work is retrieval -> prompt orchestration, per-room enablement, and guardrails.

Trigger + orchestration (routes::assistant)

  • New built-in slash command /ask (shows in autocomplete/help). It runs after the normal access + posting gates in try_dispatch.
  • Retrieval (RAG): db::chat::fts_room_context pulls the top FTS-ranked messages in the room for the question, plus the room wiki + description, into a length-capped context block (authors resolved to display labels).
  • Prompt: a system prompt instructing the model to answer only from the provided context (say "not found" otherwise), then context + question, sent via the existing crate::llm::LlmClient::complete.
  • Answer: posted as the lazily get-or-created assistant bot user via finalize_message_send (so it broadcasts + carries the bot badge). The asker's question is quoted into the message (the /ask itself is not echoed). On any failure nothing is posted and the error surfaces to the asker's composer.

Per-room enablement + guardrails

  • Migration 0077 adds rooms.assistant_enabled (off by default, opt-in). A switch on the room Manage page (require_can_manage, mod-log audited) toggles it, with a hint when no LLM is configured.
  • /ask is gated three ways: operator LLM configured, room assistant enabled, and a per-user rate limit (RateLimitKind::AssistantAsk, 10/min). Question length is capped (1000 chars); context is capped (8000 chars).

Tests

routes_assistant (mock LLM): a disabled room refuses /ask and posts nothing (and does not create the bot); an enabled room posts the answer as the assistant bot, quoting the question. db_room_assistant: toggle round-trip + room-scoped FTS retrieval. just check, just test, just test-saas all green; i18n parity covers the new room-assistant-* keys (en+es); Tailwind rebuilt.

Operator note

Documented in the LETS_CHAT_LLM_* env row (CLAUDE.md): /ask sends retrieved room context + the question to the LLM endpoint. It is off by default and per-room opt-in and requires the LLM configured, so enabling the LLM does not by itself send anything new until a manager turns the assistant on. No new env var; not an operator action on upgrade.

Follow-ups (not in this slice)

@assistant mention-triggering (alias for /ask), threaded replies, and citing the retrieved sources in the answer.

🤖 Generated with Claude Code

## LC-492 - In-channel AI assistant with RAG (`/ask`) A first vertical slice of the "big bet" assistant: `/ask <question>` in a room retrieves context and answers with the operator LLM, posting the reply as an `assistant` bot. Reuses the existing FTS, LLM client, bot-user, and broadcast plumbing; the new work is retrieval -> prompt orchestration, per-room enablement, and guardrails. ### Trigger + orchestration (`routes::assistant`) - New built-in slash command `/ask` (shows in autocomplete/help). It runs after the normal access + posting gates in `try_dispatch`. - **Retrieval (RAG)**: `db::chat::fts_room_context` pulls the top FTS-ranked messages in the room for the question, plus the room wiki + description, into a length-capped context block (authors resolved to display labels). - **Prompt**: a system prompt instructing the model to answer only from the provided context (say "not found" otherwise), then `context + question`, sent via the existing `crate::llm::LlmClient::complete`. - **Answer**: posted as the lazily get-or-created `assistant` bot user via `finalize_message_send` (so it broadcasts + carries the bot badge). The asker's question is quoted into the message (the `/ask` itself is not echoed). On any failure nothing is posted and the error surfaces to the asker's composer. ### Per-room enablement + guardrails - Migration `0077` adds `rooms.assistant_enabled` (off by default, opt-in). A switch on the room **Manage** page (`require_can_manage`, mod-log audited) toggles it, with a hint when no LLM is configured. - `/ask` is gated three ways: operator LLM configured, room assistant enabled, and a per-user rate limit (`RateLimitKind::AssistantAsk`, 10/min). Question length is capped (1000 chars); context is capped (8000 chars). ### Tests `routes_assistant` (mock LLM): a disabled room refuses `/ask` and posts nothing (and does not create the bot); an enabled room posts the answer as the `assistant` bot, quoting the question. `db_room_assistant`: toggle round-trip + room-scoped FTS retrieval. `just check`, `just test`, `just test-saas` all green; i18n parity covers the new `room-assistant-*` keys (en+es); Tailwind rebuilt. ### Operator note Documented in the `LETS_CHAT_LLM_*` env row (CLAUDE.md): `/ask` sends retrieved room context + the question to the LLM endpoint. It is off by default and per-room opt-in and requires the LLM configured, so enabling the LLM does not by itself send anything new until a manager turns the assistant on. No new env var; not an operator action on upgrade. ### Follow-ups (not in this slice) `@assistant` mention-triggering (alias for `/ask`), threaded replies, and citing the retrieved sources in the answer. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Migration 0077 adds rooms.assistant_enabled (opt-in, default off). db::chat gains get/set_room_assistant_enabled and fts_room_context (lightweight room-scoped FTS retrieval for RAG). New RateLimitKind::AssistantAsk caps per-user asks.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
New /ask built-in command and routes::assistant: retrieves room context (FTS over messages + the room wiki/description), answers with the operator LLM (crate::llm), and posts the reply as the lazily-created `assistant` bot. Gated on LLM configured + per-room enablement, with a question length cap and per-user rate limit; the asker's question is quoted into the bot message (the /ask is not echoed). Errors surface to the asker's composer; nothing posts on failure.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
POST /room/{id}/assistant (require_can_manage + mod-log audit + dual-mode response) flips rooms.assistant_enabled. The manage page shows a switch with a hint when no LLM is configured. New i18n room-assistant-* (en+es).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Integration (mock LLM): a disabled room refuses /ask and posts nothing; an enabled room posts the answer as the assistant bot, quoting the question. db: toggle round-trip + room-scoped FTS retrieval.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
docs(assistant): note /ask LLM data flow in the LETS_CHAT_LLM env row (LC-492)
All checks were successful
check-secrets / TruffleHog (push) Successful in 5s
check-secrets / Nosey parker (push) Successful in 3s
check-secrets / Kingfisher (push) Successful in 5s
check-secrets / Kingfisher (pull_request) Successful in 8s
check-secrets / Nosey parker (pull_request) Successful in 8s
check-secrets / TruffleHog (pull_request) Successful in 9s
Create release / Create release from merged PR (pull_request) Has been skipped
Check / clippy + fmt + tests (pull_request) Successful in 5m43s
a922121bc6
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-29 02:14:17 +02:00
longjacksonle deleted branch feat/LC-492-assistant 2026-06-29 02:18:15 +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!489
No description provided.