Web route: / when authenticated (HomeRoute renders AsyncDashboard2). Logged-out / is the public landing page and stays out of the Android 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.
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):
CONVERSATION_ID, then the next text frame is the numeric id (only applied when no conversation is selected).
START_OF_THE_STREAM_ENDER_GAME_42
Token chunks appended to the assistant bubble.
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).
Android
Replace the ChatScreen placeholder. This is Routes.Chat and the authenticated start destination.
Part of #1.
Web route: `/` when authenticated (`HomeRoute` renders `AsyncDashboard2`). Logged-out `/` is the public landing page and stays out of the Android 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`).
## Android
Replace the `ChatScreen` placeholder. This is `Routes.Chat` and the authenticated start destination.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Part of #1.
Web route:
/when authenticated (HomeRouterendersAsyncDashboard2). Logged-out/is the public landing page and stays out of the Android app.Source:
llm-fe/src/llm-fe/pages/AsyncDashboard2/AsyncDashboard2.tsx, plusConversationContext,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
Header2): conversations button, logo (stays on Dashboard), nav to Account / Documents (RAG only) / Analytics / Feedback, Sign out.API
GET conversations{ id, title }. Refetch when auth, selection, or conversation preferences change.DELETE conversation_details{ conversation_id }.GET conversation_details?conversation_id=user_created, timestamps, tokens, citations, feedback,file_type, image.wss://chatbackend.aimloperations.com/ws/chat_again/REACT_APP_BACKEND_WS_API_BASE_URL.POST prompt_feedback{ prompt_id, rating: "up"|"down", reason?, comment? }.DELETE prompt_feedback?prompt_id=Socket send (JSON text frame):
conversation_idis omitted/null for a new chat. A file is base64 (no data-URL prefix) plusfileType.Socket receive (text frames, in order for one turn):
CONVERSATION_ID, then the next text frame is the numeric id (only applied when no conversation is selected).START_OF_THE_STREAM_ENDER_GAME_42END_OF_THE_STREAM_ENDER_GAME_42, then refetchconversation_detailsafter ~800ms so ids, citations, and feedback hydrate.Versioned JSON frames (ignore unknown types):
type: "status"(stage, label, detail) andtype: "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).Android
Replace the
ChatScreenplaceholder. This isRoutes.Chatand the authenticated start destination.