Dashboard (chat) #5

Open
opened 2026-10-23 03:36:30 -07:00 by westfarn · 0 comments
Owner

Part of #1.

Web route: / when authenticated (HomeRoute renders AsyncDashboard2). Logged-out / is the public landing page and stays out of the iOS app.

Source: llm-fe/src/llm-fe/pages/AsyncDashboard2/AsyncDashboard2.tsx, plus ConversationContext, MessageContext, WebSocketContext.js.

This is the screen a stored session must open.

Screenshot

Not attached. Production / without a session is the public landing page, which this app does not ship. Capture after sign-in: header, conversation drawer, empty state or a thread, composer.

Behavior

  • Header (Header2): conversations button, logo (stays on Dashboard), nav to Account / Documents (RAG only) / Analytics / Feedback, Sign out.
  • Drawer lists conversations. Empty copy: “No conversations yet. Start with + New Chat.”
    • New Chat clears the selection (no REST create). The first send creates the conversation over the socket.
  • Selecting a row loads that thread and closes the drawer.
  • Delete (trash on the selected row) removes it and clears the selection if it was open.
  • Empty thread copy: “Select a conversation or start a new one.”
  • Messages render user vs assistant, markdown, citations, image attachments, thumbs up/down on assistant prompts.
  • Live assistant bubble shows streaming tokens plus activity stage/label/detail from status frames.
  • Export (PDF, CSV, Excel, plain text) is client-side from the messages already loaded. No export API.
  • Composer: attach file, multiline text, send. Enter sends, Shift+Enter newline. Send stays disabled until the socket is open and the text is non-empty.
  • Connection banner: “Connecting to chat…”, “Reconnecting…”, or “Connection to the LLM is not active”.
  • If the socket drops mid-stream: keep the partial text, show “Last response was interrupted. You can re-send your message.” There is no resume protocol.

API

Call Purpose
GET conversations List { id, title }. Refetch when auth, selection, or conversation preferences change.
DELETE conversation_details Body { conversation_id }.
GET conversation_details?conversation_id= Thread: id, message, user_created, timestamps, tokens, citations, feedback, file_type, image.
WebSocket wss://chatbackend.aimloperations.com/ws/chat_again/ Prod URL from REACT_APP_BACKEND_WS_API_BASE_URL.
POST prompt_feedback Body { prompt_id, rating: "up"|"down", reason?, comment? }.
DELETE prompt_feedback?prompt_id= Clear a rating.

Socket send (JSON text frame):

{
  "message": "trimmed text",
  "conversation_id": 123,
  "email": "user@example.com",
  "file": null,
  "fileType": null,
  "modelName": "<default model>"
}

conversation_id is omitted/null for a new chat. A file is base64 (no data-URL prefix) plus fileType.

Socket receive (text frames, in order for one turn):

  1. CONVERSATION_ID, then the next text frame is the numeric id (only applied when no conversation is selected).
  2. START_OF_THE_STREAM_ENDER_GAME_42
  3. Token chunks appended to the assistant bubble.
  4. END_OF_THE_STREAM_ENDER_GAME_42, then refetch conversation_details after ~800ms so ids, citations, and feedback hydrate.

Versioned JSON frames (ignore unknown types): type: "status" (stage, label, detail) and type: "citations". A non-stream text frame may be a chat error payload.

Heartbeat runs on the open socket so proxies do not idle it out (websocketReconnect.js).

iOS

New screen. Authenticated start destination. Use URLSessionWebSocketTask (or equivalent) and keep the app-socket alive across brief backgrounding the same way the web client heartbeats.

Part of #1. Web route: `/` when authenticated (`HomeRoute` renders `AsyncDashboard2`). Logged-out `/` is the public landing page and stays out of the iOS app. Source: `llm-fe/src/llm-fe/pages/AsyncDashboard2/AsyncDashboard2.tsx`, plus `ConversationContext`, `MessageContext`, `WebSocketContext.js`. This is the screen a stored session must open. ## Screenshot Not attached. Production `/` without a session is the public landing page, which this app does not ship. Capture after sign-in: header, conversation drawer, empty state or a thread, composer. ## Behavior - Header (`Header2`): conversations button, logo (stays on Dashboard), nav to Account / Documents (RAG only) / Analytics / Feedback, Sign out. - Drawer lists conversations. Empty copy: “No conversations yet. Start with + New Chat.” - + New Chat clears the selection (no REST create). The first send creates the conversation over the socket. - Selecting a row loads that thread and closes the drawer. - Delete (trash on the selected row) removes it and clears the selection if it was open. - Empty thread copy: “Select a conversation or start a new one.” - Messages render user vs assistant, markdown, citations, image attachments, thumbs up/down on assistant prompts. - Live assistant bubble shows streaming tokens plus activity stage/label/detail from status frames. - Export (PDF, CSV, Excel, plain text) is client-side from the messages already loaded. No export API. - Composer: attach file, multiline text, send. Enter sends, Shift+Enter newline. Send stays disabled until the socket is open and the text is non-empty. - Connection banner: “Connecting to chat…”, “Reconnecting…”, or “Connection to the LLM is not active”. - If the socket drops mid-stream: keep the partial text, show “Last response was interrupted. You can re-send your message.” There is no resume protocol. ## API | Call | Purpose | | --- | --- | | `GET conversations` | List `{ id, title }`. Refetch when auth, selection, or conversation preferences change. | | `DELETE conversation_details` | Body `{ conversation_id }`. | | `GET conversation_details?conversation_id=` | Thread: id, message, `user_created`, timestamps, tokens, citations, feedback, `file_type`, image. | | WebSocket `wss://chatbackend.aimloperations.com/ws/chat_again/` | Prod URL from `REACT_APP_BACKEND_WS_API_BASE_URL`. | | `POST prompt_feedback` | Body `{ prompt_id, rating: "up"\|"down", reason?, comment? }`. | | `DELETE prompt_feedback?prompt_id=` | Clear a rating. | **Socket send** (JSON text frame): ```json { "message": "trimmed text", "conversation_id": 123, "email": "user@example.com", "file": null, "fileType": null, "modelName": "<default model>" } ``` `conversation_id` is omitted/null for a new chat. A file is base64 (no data-URL prefix) plus `fileType`. **Socket receive** (text frames, in order for one turn): 1. `CONVERSATION_ID`, then the next text frame is the numeric id (only applied when no conversation is selected). 2. `START_OF_THE_STREAM_ENDER_GAME_42` 3. Token chunks appended to the assistant bubble. 4. `END_OF_THE_STREAM_ENDER_GAME_42`, then refetch `conversation_details` after ~800ms so ids, citations, and feedback hydrate. Versioned JSON frames (ignore unknown types): `type: "status"` (stage, label, detail) and `type: "citations"`. A non-stream text frame may be a chat error payload. Heartbeat runs on the open socket so proxies do not idle it out (`websocketReconnect.js`). ## iOS New screen. Authenticated start destination. Use `URLSessionWebSocketTask` (or equivalent) and keep the app-socket alive across brief backgrounding the same way the web client heartbeats.
Sign in to join this conversation.
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: ai_ml_operations/chat_ios#5