Conversation Management
Conversation Management
Section titled “Conversation Management”Arbitex organises chat sessions into conversations. Each conversation has a title, an operating mode (Single / Compare / Summarize), a snapshot of the models it was created with, and optional organisational attributes (folder, pin, archive, project).
Auto-Title Generation
Section titled “Auto-Title Generation”When a new conversation’s first message is sent, Arbitex automatically generates a concise title in a background task. The auto-title service runs independently of the response stream so it never adds latency to the chat.
How Titles Are Generated
Section titled “How Titles Are Generated”- The first
userrole message is sent to a lightweight LLM (default:claude-haiku-3-20240307via the Anthropic provider). - The model receives the system prompt: “Generate a concise 3-5 word title for this conversation. Respond with only the title, no quotes or punctuation.”
- Only the first 500 characters of the message are forwarded to the title model (token cost optimisation).
max_tokens=30,temperature=0.3. - The raw response is sanitised: surrounding quotes stripped, trailing punctuation removed, truncated to 100 characters.
Fallback Title
Section titled “Fallback Title”If the LLM is disabled, unavailable, or returns an empty response, a fallback title is derived from the first 50 characters of the message text, truncated at the nearest word boundary with "..." appended.
Examples:
| First message (excerpt) | Generated title |
|---|---|
"How do I configure SCIM in Okta?" | Configure SCIM Okta |
"Write a Python function that..." | Python Function Writing |
(LLM unavailable) "Explain the difference between..." | Explain the difference... |
Configuration
Section titled “Configuration”Auto-titling is controlled by two SystemConfig keys:
| Key | Description | Default |
|---|---|---|
auto_title_enabled | Enable/disable auto-titling globally | true |
auto_title_model | Model to use for title generation, format "provider/model_id" | "anthropic/claude-haiku-3-20240307" |
Admins can change these via PUT /api/admin/config/{key}.
When Titles Are Not Auto-Generated
Section titled “When Titles Are Not Auto-Generated”- If a conversation already has a manually-set title (even an empty string set intentionally), the auto-title service returns immediately without overwriting it.
- If
auto_title_enabledisfalseinSystemConfig, the first-N-chars fallback title is used instead.
Manual Rename
Section titled “Manual Rename”Users can rename any conversation they own at any time.
PATCH /api/conversations/{conversation_id}Authorization: Bearer <token>Content-Type: application/json
{ "title": "SCIM Okta Integration Questions"}Response 200 OK — the full conversation object with the updated title.
The new title replaces the existing one and persists. After a manual rename, the auto-title service will not overwrite it (it only acts when title is null or empty at the time of the first message).
Setting title to an empty string "" is allowed and results in the conversation appearing as “Untitled Conversation” in the UI.
Conversation Object Fields
Section titled “Conversation Object Fields”| Field | Type | Description |
|---|---|---|
id | UUID | Unique conversation identifier |
title | string | null | Conversation title (null until auto-titled) |
mode | "single" | "compare" | "summarize" | Operating mode |
model_config_snapshot | JSONB | Snapshot of models selected at creation |
model_instructions | string | null | Custom system prompt for this conversation |
is_pinned | bool | Pinned to top of sidebar |
is_archived | bool | Soft-archived (hidden from default view) |
folder_id | UUID | null | Folder assignment |
project_id | UUID | null | Project cost-tagging |
share_token | string | null | Token for public share link |
parent_conversation_id | UUID | null | Fork source (null if not a fork) |
created_at | ISO-8601 | Creation timestamp |
updated_at | ISO-8601 | Last modification timestamp |
Archive and Unarchive
Section titled “Archive and Unarchive”Archived conversations are hidden from the default sidebar list but remain accessible via the archive view or direct URL.
PATCH /api/conversations/{conversation_id}Authorization: Bearer <token>Content-Type: application/json
{ "is_archived": true}To unarchive, send "is_archived": false.
The PATCH endpoint accepts both title and is_archived in the same request.
Pin / Unpin
Section titled “Pin / Unpin”Pinned conversations float to the top of the sidebar regardless of recency. Pin state is toggled via the same PATCH endpoint:
PATCH /api/conversations/{conversation_id}Content-Type: application/json
{ "is_pinned": true}Model Badges
Section titled “Model Badges”Each message in a conversation carries a model_id field. The frontend renders a model badge alongside assistant messages to identify which model produced each response. In Compare and Summarize modes (multiple models active), each response column displays the appropriate model badge.
Badge Display Logic
Section titled “Badge Display Logic”- Single mode: one badge, matching the model selected for the conversation.
- Compare mode: N badges (one per model column), rendered side-by-side.
- Summarize mode: individual model responses are labelled with their
model_id; the synthesis message carries the synthesizer model badge.
The model_config_snapshot on the conversation records which models were active at the time the conversation was created. If models are later renamed or deprecated, the badge still shows the original model_id string from the snapshot.
Message Model Fields
Section titled “Message Model Fields”Each Message in the API response includes:
{ "role": "assistant", "content": "Here is your answer...", "model_id": "gpt-4o", "created_at": "2026-03-12T14:22:00Z"}For summarizer synthesis messages, role is "summary" and model_id is the configured synthesizer model.
Sidebar Navigation
Section titled “Sidebar Navigation”The conversation list endpoint returns conversations ordered by most recently updated, with pagination support.
GET /api/conversations?page=1&page_size=20&search=SCIMAuthorization: Bearer <token>Query parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
page | int | 1 | Page number (1-indexed) |
page_size | int | 20 | Results per page |
search | string | — | Full-text search against title and message content (case-insensitive ILIKE) |
is_archived | bool | false | Include/exclude archived conversations |
folder_id | UUID | — | Filter to a specific folder |
Search behaviour: The search parameter performs a PostgreSQL ILIKE match against the conversation title column and any message content in the conversation (CAST(content AS text)). This is a substring match — no tokenisation or stemming.
Forking Conversations
Section titled “Forking Conversations”A fork creates an independent copy of a conversation from a chosen message point. The fork title is automatically prefixed with (Fork).
POST /api/conversations/{conversation_id}/forkAuthorization: Bearer <token>Content-Type: application/json
{ "fork_message_id": "msg-uuid-here"}The forked conversation has parent_conversation_id set to the source conversation’s ID and fork_message_id set to the branch point. Messages up to and including the fork point are copied.
Deleting Conversations
Section titled “Deleting Conversations”Conversations are soft-deleted: the deleted_at timestamp is set. Soft-deleted conversations do not appear in list or search results.
DELETE /api/conversations/{conversation_id}Authorization: Bearer <token>Response 204 No Content
Permanent deletion is not available via the user API. Admins can perform hard deletes via GDPR erasure endpoints (see Conversation Export).