9.3 KiB
AI Message Flow + React Dashboard Redesign
Goal
Rebuild the moderation watcher flow so message capture is fast, AI analysis is contextual and reliable, APIs are split by concern, and the dashboard is maintainable. The implementation may fully replace the current static frontend and reorganize backend modules, while preserving existing voice functionality.
Current Problems
- Message capture, AI queuing, DB access, and WebSocket broadcasting are tightly coupled.
- AI analysis batches depend on array ordering, which can mismatch results when the model returns malformed or partial JSON.
- Pending analysis is polled globally, not grouped by conversation, so context is weak and request efficiency is inconsistent.
/api/messagesmixes text/image concerns and uses offset pagination, which gets slower and less stable as rows grow.- Frontend is a large static HTML file with inline state, API, WebSocket, rendering, and audio code.
- WebSocket broadcast uses untyped
globalThishooks across modules.
Backend Architecture
Message ingestion
messageCapture becomes a narrow ingestion layer:
- Filter Discord events by guild and author.
- Normalize message payload and metadata.
- Upsert message/attachment records.
- Set or reset
ai_statustopendingfor new or edited text. - Emit typed domain events for WebSocket broadcasting and analysis queueing.
It should not build prompts, manage AI batches, or query unrelated DB state.
Store/repository layer
messageStore becomes the single message/attachment query boundary. It should expose focused functions:
upsertMessagemarkMessageEditedmarkMessageDeletedlistMessageslistReviewMessagesgetConversationContextclaimPendingMessagesForChannelsaveAnalysisResultsinsertAttachments
Queries should use cursor pagination based on (created_at, id) instead of offset pagination. Common filters: guildId, channelId, threadId, status, userId, q, limit, cursor.
Analysis queue
Add an analysisQueue module. It owns async AI processing and keeps capture fast.
- Queue key:
thread_id ?? channel_id. - Debounce: 1–3 seconds per key to group nearby messages.
- Batch pending messages by conversation key and token budget.
- Only one or a small fixed number of active LLM requests.
- Backlog worker feeds the same queue; no separate analysis path.
- Edits reset a message to
pendingand enqueue its conversation key.
If the process restarts, pending rows are recovered by a periodic lightweight scanner grouped by conversation key.
Conversation context builder
Add conversationContext module:
- Input: conversation key + target pending messages.
- Fetch context before the first target message, normally 20 prior messages.
- Include target messages and close neighboring messages when within budget.
- Mark target messages explicitly in the prompt.
- Keep context scoped to one channel/thread to avoid irrelevant noise.
LLM moderation client
Add llmModerationClient module:
- Own request shape, timeout, retry, JSON extraction, and validation.
- Prompt returns JSON keyed by
message_id, not positional arrays. - Expected response shape:
{ "results": [ { "message_id": "string", "status": "clean|warn|flagged", "flags": ["string"], "score": 0.0, "analysis": "Bahasa Indonesia summary + reason + suggested action" } ] } - Reject unknown IDs, invalid statuses, invalid scores, and missing target IDs.
- On partial model failure, retry once with smaller batch. If still invalid, mark only affected target messages as
error. - Store raw batch request/response in one run record if the DB migration is included; otherwise store compact raw metadata per message.
API Design
Split API by use case so reads remain fast and obvious.
Message read APIs
-
GET /api/messages- Query:
guildId,channelId,threadId,cursor,limit,status,userId,q. - Returns:
{ data, nextCursor }. - Uses indexed cursor pagination.
- Query:
-
GET /api/messages/:id- Returns one message with attachments and AI analysis.
-
GET /api/review- Query:
guildId, optionalchannelId,status=warn,flagged,error,cursor,limit. - Optimized for moderator review panel.
- Query:
-
GET /api/attachments- Query:
channelId,threadId,cursor,limit,type. - Replaces image mode inside
/api/messages.
- Query:
Analysis APIs
-
POST /api/messages/:id/reanalyze- Sets message to
pendingand queues its conversation. - Returns
202 Acceptedwith current message status.
- Sets message to
-
POST /api/analysis/requeue-pending- Admin/manual recovery endpoint for pending/error rows.
- Returns count queued.
-
GET /api/analysis/status- Returns queue depth, active requests, last error, and pending counts.
Discord sync APIs
POST /api/backlog-sync- Stays async-friendly: starts sync for guild/channel/thread and returns
202with a job id or immediate summary if small. - Sync inserts messages, then queues analysis through the same
analysisQueue.
- Stays async-friendly: starts sync for guild/channel/thread and returns
Voice/control APIs
Keep existing voice APIs working, but move route registration into route modules:
routes/voiceRoutes.tsroutes/messageRoutes.tsroutes/analysisRoutes.tsroutes/syncRoutes.tsroutes/uiStateRoutes.ts
webserver.ts should only create Express/WS server, install middleware, register routes, and start listening.
WebSocket Design
Replace ad-hoc globals with a typed broadcaster module.
Events:
ui_stateuser_statemessage_createdmessage_updatedmessage_deletedmessage_analyzedattachment_createdanalysis_queue_status
Backend modules call broadcaster functions; they do not touch WebSocket clients directly.
Database Changes
Add or verify indexes:
- messages
(channel_id, created_at, id) - messages
(thread_id, created_at, id) - messages
(ai_status, created_at, id) - messages
(guild_id, ai_status, created_at, id) - attachments
(channel_id, created_at, id) - attachments
(thread_id, created_at, id)
Optional but preferred:
ai_analysis_runstable:idconversation_keytarget_message_idsmodelrequest_tokens_estimateresponse_rawstatuserrorcreated_atcompleted_at
This avoids duplicating large raw LLM responses into every message row.
React/Vite Frontend
Replace static inline dashboard code with a TypeScript React app.
Suggested structure:
frontend/src/api/— typed REST clientsfrontend/src/ws/— WebSocket client and event typesfrontend/src/state/— small hooks for selected guild/channel, messages, review queue, voice statefrontend/src/components/voice/— existing voice control/audio componentsfrontend/src/components/messages/— feed, message card, filters, detail drawerfrontend/src/components/review/— needs-review list and analysis statusfrontend/src/components/layout/— shell/sidebar/status cards
UI layout:
- Left sidebar: guild, voice channel, text channel/thread, connection state.
- Main area: message feed with filters and load-more cursor pagination.
- Right panel: review queue for
warn,flagged, anderrormessages. - Detail drawer/modal: message metadata, attachments, AI rationale, raw flags, reanalyze action.
Voice features stay functionally equivalent. Audio capture/playback code can be moved into React hooks but should not be behaviorally rewritten unless needed.
Build integration:
- Add Vite dev/build scripts.
- Express serves the built app from a stable public directory in production.
- During development, either run Vite separately or proxy API/WS to Express.
Performance Rules
- Message capture must not wait on AI.
- Read APIs use cursor pagination and indexes.
- AI batches are bounded by token estimate and message count.
- UI fetches initial pages, then patches via WebSocket.
- Backlog sync should not block dashboard interactions.
- Avoid storing full raw LLM response per message when a batch table is available.
Error Handling
- Capture errors log and do not crash Discord client event handlers.
- AI request failures mark target messages
errorwith a short reason. - Invalid LLM JSON triggers retry/split before marking errors.
- API validation returns 400 with structured error code.
- WebSocket reconnect logic stays client-side.
- Manual reanalysis provides recovery for bad AI results.
Testing
Backend:
- Unit tests for conversation context selection.
- Unit tests for LLM response parser and validation.
- Unit tests for queue batching/debounce behavior.
- Integration tests for message cursor pagination and review filters.
- Existing voice tests remain unchanged.
Frontend:
- Typecheck and Vite build.
- Component-level smoke tests may be added if test tooling is already practical.
- Manual browser verification: channel select, message feed, review panel, WebSocket updates, reanalyze action, voice controls.
Implementation Scope
This is a full redesign of the message/AI/dashboard path. Voice recording and live audio behavior should be preserved unless a change is required to integrate the React dashboard.
Implementation should proceed incrementally:
- Backend boundaries and typed broadcaster.
- Store/query improvements and indexes.
- Analysis queue/context/client rewrite.
- Split API routes.
- React/Vite dashboard.
- Verification and cleanup of old static dashboard code.