Librechat-Mobile/DISCOVERY.md
Garfie abde86ca4e chore(sync): sync to upstream LibreChat v0.8.5
Targets upstream tag v0.8.5 (commit 9ccc8d9be). Bumps the supported
backend version constant, advances the upstream submodule pin, and
adds wire-protocol parity for new server features.

Wire protocol
- Add ContentType.SUMMARY enum value and flat sibling fields on
  MessageContentPart (content as JsonElement to absorb array/string/
  legacy-text variants per BaseClient.getSummaryText).
- Add Anthropic effort.xhigh dropdown value, ThinkingDisplay (Opus 4.7
  reasoning visibility), and Message.contextMeta pass-through.
- Add allowAccountDeletion to StartupConfig (defaults to true to
  preserve older-server behavior).
- Drop unused instanceProjectId from StartupConfig and from
  ConfigRepositoryImpl.isValidLibreChatConfig.

New endpoints / features
- Favorites: UserFavorite DTO + FavoritesApi + FavoritesRepository,
  Settings -> Favorites sub-screen (list/unpin), pin icons in chat
  model selector. Repository owns the canonical list as a StateFlow
  guarded by Mutex.withLock so chat-side toggles and settings-side
  bulk-unpin can't drift apart and concurrent writes can't lose each
  other.
- Prompt-group usage telemetry (POST /api/prompts/groups/:id/use,
  fire-and-forget on prompt insertion).
- Summarize SSE events (on_summarize_complete -> StreamEvent.ContextSummary)
  and a collapsible "Summarized earlier messages" card rendered from
  the persisted SUMMARY content part. Forward-compat else-arm preserved
  on both LangGraph and legacy SSE mappers.

Backward compatibility
- BackendVersion.isCompatibleOrNewer helper added; documented fail-open
  contract for callers that have already null-checked
  detectedBackendVersion.
- New VERSION_GATES.md catalog at repo root tracks every code path that
  branches on server version. Initial entries:
  - isCollaborative agent toggle (hidden on v0.8.5+; full-ACL UX deferred)
  - xhigh effort dropdown value (filtered out on <v0.8.5 to avoid
    server-side enum rejection)

Defensive hardening
- ChatApi.startChat asserts Content-Type: application/json before
  decoding ChatStartResponse and catches NoTransformationFoundException
  as a backstop, translating both to a friendly ApiException. Observed
  during testing on v0.8.5-rc1 with summarization enabled, where the
  agents chat-start endpoint returned 200 OK with no Content-Type and
  Ktor leaked a NoTransformationFoundException stack trace into the
  chat surface. Belt-and-suspenders against future backend regressions
  or unsupported-server pointing.

Documentation
- DISCOVERY.md updated for the v0.8.5 surface: /api/endpoints now
  requires JWT, /api/config pre-auth/post-auth split, allowAccountDeletion,
  instanceProjectId removal, favorites endpoints (with all three XOR
  variants: agentId | model+endpoint | spec), prompt-usage endpoint,
  /api/admin/** marked out-of-scope. Plus a new MessageContentPart +
  SUMMARY wire-shape section.

iOS
- Swift SharedFrameworkTest.swift updated for the new
  StreamEvent.ContextSummary variant and Message.contextMeta
  constructor argument.

Out of scope (by design)
- /api/admin/** route family - web-only feature.
- Full ACL migration for agent sharing - replaced by version-gating
  the legacy isCollaborative toggle.
- Spec-pinning UI on mobile - UserFavorite.spec round-trips for
  fidelity with web-authored pins, but mobile does not yet surface
  a spec picker.
2026-04-26 23:48:14 -06:00

364 lines
13 KiB
Markdown

# LibreChat Mobile - Discovery & Context Document
## Project Goal
Build a native mobile app (LibreChat-Mobile) with full feature parity to the LibreChat web application. The app must connect to an existing LibreChat backend server (no backend changes). Users specify the server URL during onboarding.
## Tech Stack (Mandated)
- **UI**: Jetpack Compose + Compose Navigation
- **DI**: Koin
- **Network**: Ktor Client
- **Serialization**: Kotlinx Serialization
- **Min SDK**: TBD (recommend 26+)
---
## LibreChat Web Application Overview
### Architecture
- Monorepo: `/api` (Express/Node backend), `/client` (React/Vite frontend), `/packages` (shared libs)
- MongoDB database, optional Redis, optional Meilisearch
- JWT-based auth with refresh tokens
### Core Features (Full Parity Required)
#### 1. Authentication & Onboarding
- **Server URL entry** (Android-only: user specifies their LibreChat server)
- Local login (email/password)
- OAuth2 social logins (Google, GitHub, Discord, Facebook, Apple, OpenID, SAML)
- Registration with email verification
- Password reset flow
- Two-factor authentication (TOTP + backup codes)
- Terms of service acceptance
#### 2. Chat (Primary Experience)
- Conversation list in sidebar (cursor-paginated, sorted by updatedAt)
- Create new conversations
- Send messages, receive streaming AI responses via SSE
- Message tree with parent/child relationships and branching
- Sibling message navigation (e.g., "1/3" switcher for regenerated responses)
- Edit user messages (creates new branch)
- Regenerate AI responses
- Continue incomplete responses
- Stop generation mid-stream
- Fork conversations from any message
- Duplicate conversations
- Markdown rendering with syntax highlighting
- LaTeX/math rendering
- Code blocks with copy button and language badge
- Image display (inline + expandable)
- File attachments in messages
- Tool call display (expandable cards showing input/output)
- Message feedback (thumbs up/down with optional comment)
- Typing/streaming indicator
- Time-based greeting on landing ("Good morning", etc.)
- Custom welcome messages from server config
#### 3. Model/Endpoint Selection
- Multiple AI providers: OpenAI, Anthropic, Azure, Google, Groq, Mistral, Ollama, custom
- Model selector dropdown with search
- Endpoint icons (branded)
- Model parameters (temperature, top_p, frequency_penalty, etc.)
- Model specs from server config
#### 4. Agents & Assistants
- Agent marketplace (grid of cards with avatar, name, description, category)
- Agent chat with tool calling
- OpenAI Assistants integration
- Agent/assistant selection UI
#### 5. File Management
- Upload files (images, PDFs, documents, code)
- Drag-and-drop (not applicable on Android, use file picker)
- File preview in messages
- File download
- Image generation display
- Audio recording for voice input (STT)
- Text-to-speech playback (TTS)
#### 6. Conversation Management
- Rename conversations (inline)
- Archive/unarchive
- Delete (single + bulk)
- Share (generate public link)
- Export (JSON, markdown)
- Import conversations
- Tags for organization
- Bookmarks/favorites
- Search conversations (full-text via Meilisearch)
#### 7. Presets
- Save chat configurations as presets
- Load presets
- Delete presets
#### 8. Prompts Library
- Create/edit/delete prompts
- Share prompts
- Prompt versioning
- @mentions for prompts in chat input
- Prompt variables
#### 9. User Settings
- Theme (dark/light)
- Language selection (46+ languages)
- Font size
- Speech settings
- Data export
- Account management (profile, delete account)
- Balance/credits display
#### 10. Artifacts
- Split-pane code preview/editor
- Tabs: Preview, Code, Info
- Live editing
---
## API Endpoints (Key Routes)
### Authentication
```
POST /api/auth/login → { token, user } or { twoFAPending, tempToken }
POST /api/auth/register → { status, message }
POST /api/auth/logout → { status }
POST /api/auth/refresh → { token, user } (refresh token in cookies)
POST /api/auth/requestPasswordReset
POST /api/auth/resetPassword
GET /api/auth/2fa/enable → { secret, qrCode }
POST /api/auth/2fa/verify
POST /api/auth/2fa/confirm → { confirmed, backupCodes }
POST /api/auth/2fa/disable
POST /api/auth/2fa/verify-temp → { token, user }
```
### Configuration
```
GET /api/config → startup config (models, features, auth methods, interface config)
GET /api/endpoints → available AI providers and models (JWT required as of v0.8.5)
POST /api/user/settings/favorites → update user favorites (agent/model pins, v0.8.5+)
GET /api/user/settings/favorites → list user favorites (v0.8.5+)
POST /api/prompts/groups/:id/use → record prompt-group usage for analytics (v0.8.5+)
```
**v0.8.5 notes**
- `GET /api/config` response payload is now split into a pre-auth and post-auth variant.
Pre-auth fields (what `validateServerUrl` / `fetchStartupConfig` rely on) are unchanged
from v0.8.4; post-auth adds fields driven by the logged-in user (not consumed by mobile).
- `GET /api/config` removed `instanceProjectId`. Mobile previously used it as an OR fallback
in `ConfigRepositoryImpl.isValidLibreChatConfig`; cleanup landed in v0.8.5 sync.
- `GET /api/config` added `allowAccountDeletion: Boolean`. Mobile honors this and hides
the Delete Account button when `false`. Defaults to `true` for older servers that omit the field.
- `GET /api/endpoints` now requires JWT (was public in v0.8.4). Mobile already called it
post-auth, so this is non-breaking.
- Favorites schema: each entry has exactly one of `{ agentId }`, `{ model, endpoint }`,
or `{ spec }` — three mutually exclusive variants enforced by the server
(`FavoritesController.js`: "Each favorite must have either agentId, model+endpoint, or spec";
`model` and `endpoint` must be supplied together; combining `spec` with any of
`agentId`/`model`/`endpoint` is rejected). Server also enforces 50 entries max /
256-character max per string; mobile short-circuits oversize writes. The `spec`
variant is round-tripped unchanged (mobile does not yet render a spec picker).
`POST` replaces the entire list (upsert-by-overwrite), and the response echoes the stored list.
### Out of scope (admin panel)
```
/api/admin/auth/** → admin-only SAML + Social OAuth callbacks (v0.8.5, web-only)
/api/admin/config/** → admin YAML config endpoints
/api/admin/grants/**, /api/admin/groups/**, /api/admin/roles/**, /api/admin/users/**
```
Admin panel is a web-only surface in upstream; mobile intentionally does not implement it.
### Conversations
```
GET /api/convos → { conversations, nextCursor }
GET /api/convos/:conversationId → single conversation
POST /api/convos/update → update title
POST /api/convos/archive → archive/unarchive
DELETE /api/convos → delete conversation(s)
POST /api/convos/fork → fork from message
POST /api/convos/duplicate → duplicate conversation
POST /api/convos/import → import (multipart)
GET /api/convos/gen_title/:conversationId → generated title
```
### Messages
```
GET /api/messages/:conversationId → messages for conversation
GET /api/messages/:conversationId/:messageId
POST /api/messages/:conversationId → save message
PUT /api/messages/:conversationId/:messageId → update message
DELETE /api/messages/:conversationId/:messageId
PUT /api/messages/:conversationId/:messageId/feedback
```
### Chat (Streaming)
```
POST /api/agents/chat → { streamId } (start generation)
GET /api/agents/chat/stream/:streamId?resume=true → SSE stream
POST /api/agents/chat/abort → abort generation
GET /api/agents/chat/active → { activeJobIds }
GET /api/agents/chat/status/:conversationId → job status
```
### Files
```
GET /api/files → user's files
POST /api/files → upload (multipart)
GET /api/files/download/:userId/:file_id
DELETE /api/files → delete file(s)
POST /api/files/speech/stt → speech-to-text
POST /api/files/speech/tts → text-to-speech
```
### Other
```
GET/POST/DELETE /api/presets
GET/POST/PUT/DELETE /api/prompts
GET/POST/DELETE /api/tags
GET/POST/PATCH/DELETE /api/share
GET /api/balance
GET /api/search
GET /api/user
GET /api/banner
```
---
## Data Models
### User
- id, name, email, username, avatar, role, provider
- emailVerified, twoFactorEnabled
- favorites, termsAccepted
### Conversation
- conversationId (UUID), title, user, endpoint, model
- agent_id, assistant_id, tags, isArchived
- Model parameters (temperature, top_p, etc.)
### Message
- messageId (UUID), conversationId, parentMessageId
- user, sender, text, content (structured parts)
- isCreatedByUser, model, endpoint
- files, attachments, feedback
- error, unfinished, finish_reason, tokenCount
### MessageContentPart
Discriminated by `type`: `text`, `think`, `text_delta`, `tool_call`, `image_file`,
`image_url`, `video_url`, `input_audio`, `agent_update`, `summary`, `error`.
**SUMMARY part wire shape (v0.8.5+)** — context-compaction emits a content part with
fields at the top level (not nested under a `summary` key):
```
{
"type": "summary",
"content": [{"type":"text","text":"..."}], // array OR string (two variants)
"tokenCount": 42,
"summarizing": false,
"summaryVersion": 1,
"model": "gpt-4o",
"provider": "openai",
"createdAt": "2026-04-22T...",
"boundary": {"messageId": "...", "contentIndex": 0}
}
```
Variants for the body text (mirrors upstream `BaseClient.getSummaryText`, last-wins):
1. `content: Array<{type:"text", text}>` — new default since v0.8.5.
2. `content: string` — intermediate variant; rare but emitted by some code paths.
3. No `content`; `text: "..."` at the top level — legacy fallback from pre-v0.8.5
summarization or test fixtures.
Mobile's `MessageContentPart.content` is typed `JsonElement?` to absorb variants 1/2,
and falls back to the existing `text: String?` field for variant 3.
### File
- file_id, filename, filepath, type, bytes, source
- user, conversationId, messageId
---
## SSE Streaming Protocol
### Flow
1. POST to `/api/agents/chat` with message payload → returns `{ streamId }`
2. Connect SSE to `GET /api/agents/chat/stream/:streamId`
3. Receive events: message, step, created, attachment, final, sync, error
4. On disconnect: reconnect with `?resume=true` for sync event
### Agent-library event names (v0.8.5)
`on_message_delta`, `on_reasoning_delta`, `on_run_step`, `on_run_step_delta`,
`on_run_step_completed`, `on_chat_model_end`, `on_agent_update`, `attachment`, and —
added in v0.8.5 — `on_summarize_start`, `on_summarize_delta`, `on_summarize_complete`.
`on_summarize_complete` payload nests the finished summary block under a `summary` key
(distinct from the message-persistence SUMMARY content part described in
`MessageContentPart`):
```
{"id":"...","agentId":"...","summary":{"type":"summary","content":[{"type":"text","text":"..."}],...}}
```
Mobile only renders the compacted summary once it is persisted to the final message as
a SUMMARY content part; the delta/lifecycle events are surfaced as status only.
### SSE Event Format
```
event: message
data: {"type":"content","chunk":"...","status":"streaming"}
event: message
data: {"type":"tool_call","toolName":"...","input":{...}}
event: message
data: {"sync":true,"resumeState":{"runSteps":[...],"aggregatedContent":[...]}}
```
### Reconnection
- Exponential backoff: 1s, 2s, 4s, 8s... max 30s
- Max 5 retries
- Resume preserves state via sync event
---
## Authentication Details
### Token Management
- JWT access token in `Authorization: Bearer <token>` header
- Refresh token stored as HTTP-only cookie (for web; Android should store securely)
- Access token expiry: ~15 minutes
- Refresh token expiry: ~24 hours
- Auto-refresh on 401 response
### OAuth Flow (Android)
- Open browser/Custom Chrome Tab for OAuth provider
- Callback redirect to app via deep link
- Exchange code for token
---
## UI/UX Reference (from Web App)
### Theme
- Light: White bg (#fff), text #212121, surface hover #e3e3e3
- Dark: #0d0d0d bg, text #ececf1, surface hover #424242
- Material 3 equivalents should be used on Android
### Key Screens
1. **Server URL Entry** (Android-only onboarding)
2. **Login/Register** with social login buttons
3. **Chat List** (sidebar on web → drawer or dedicated screen on Android)
4. **Chat View** (messages + input)
5. **Landing/New Chat** (greeting + model icon)
6. **Model Selector** (dropdown → bottom sheet on Android)
7. **Settings** (tabbed → Material 3 navigation)
8. **Agent Marketplace** (grid of cards)
9. **Search** (full-text search)
10. **File Viewer/Picker**
### Navigation Patterns (Android Adaptation)
- Web sidebar → Navigation drawer or bottom navigation
- Web modals → Bottom sheets or new screens
- Web dropdowns → Material 3 menus or bottom sheets
- Web hover actions → Long-press menus or always-visible icons
### Responsive Behavior
- Single-pane on phones (chat list or chat view, not both)
- Potential dual-pane on tablets
- Bottom navigation for primary actions