ChatAgents persists every conversation to disk so that your history survives server restarts and container redeploys. Each conversation is assigned a unique UUID as itsDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/EllisYuan/ChatAgents/llms.txt
Use this file to discover all available pages before exploring further.
thread_id. That ID is used both by LangGraph’s in-memory checkpointer (for multi-turn context) and by SessionManager (for durable JSON storage). All session files live under data/sessions/ and are indexed by a single lightweight index.json file, making it fast to list sessions without reading every individual file.
Session data structure
Each conversation is stored as a JSON file atdata/sessions/<thread_id>.json. The structure mirrors what SessionManager.create_session() and SessionManager.save_session() write to disk:
tool_calls is only present on assistant messages where the agent invoked a Tavily tool during that turn.
Title auto-generation
When a new conversation is started and no explicit title is provided,SessionManager.auto_generate_title() derives the title from the very first user message:
- Whitespace is stripped and newlines are collapsed to spaces.
- If the message is longer than 30 characters, it is truncated and
...is appended. - If the message is empty, the fallback title
"新对话"(New Conversation) is used.
app.py’s stream_agent endpoint, immediately after the first streaming response completes.
Index file
data/sessions/index.json is a lightweight catalogue of all sessions. It is updated on every create_session, save_session, delete_session, and rename_session call so that get_sessions_list() never needs to open individual session files:
get_sessions_list() sorts the array by updated_at in descending order before returning it, so the most recently active conversation always appears first in the sidebar.
SessionManager API
TheSessionManager class (located in backend/session_manager.py) exposes a clean CRUD interface. A global singleton is accessed via get_session_manager().
get_sessions_list() → List[Dict]
Returns the metadata list from index.json, sorted by updated_at descending. Does not load individual session files.
get_session(session_id) → Optional[Dict]
Reads and returns the full session file including all messages. Returns None if the file does not exist; logs a warning in that case.
create_session(session_id, title=None) → Dict
Creates a new empty session file and adds it to index.json. If title is omitted, the session_id string is used as the title placeholder — in practice app.py always passes the auto-generated title.
save_session(session_data)
Upserts the session file on disk and refreshes the corresponding entry in index.json (updating updated_at and message_count). If the session does not yet exist in the index, a new entry is appended. Raises ValueError if session_data does not contain a session_id key.
delete_session(session_id) → bool
Removes the session JSON file from disk and deletes its entry from index.json. Always returns True (even if the file was already absent) after cleaning up the index.
rename_session(session_id, new_title) → bool
Updates the title and updated_at fields in both the session file and the index. Returns False (and logs a warning) if the session file does not exist.
Concurrency safety
SessionManager uses filelock.FileLock to serialize all reads and writes:
- Lock file:
data/sessions/.lock - Timeout: 10 seconds — raises
filelock.Timeoutif another process holds the lock longer - Backup-on-write: before overwriting any JSON file,
_write_json()copies the original to a.json.bakbackup. If the write fails, the backup is restored. The backup is deleted after a successful write.
Data persistence with Docker
Thedocker-compose.yml mounts the data/ directory from the host into both containers:
SessionManager creates data/sessions/ automatically (via DATA_DIR.mkdir(parents=True, exist_ok=True)). Subsequent container restarts find the existing files on the host and session history is preserved.
In-memory LangGraph state vs. durable JSON
ChatAgents uses two complementary persistence layers:| Layer | Implementation | Purpose |
|---|---|---|
| In-memory checkpointer | MemorySaver (LangGraph) | Holds the LangGraph message graph for multi-turn LLM context within a running process |
| Durable JSON storage | SessionManager | Persists conversation history to disk so it survives restarts |
MemorySaver is initialized once in app.py’s lifespan handler and is keyed by thread_id. It enables the agent to recall earlier turns in the same conversation when constructing the next LLM prompt. SessionManager stores the finalized messages after each streaming response completes, providing the data shown in the sidebar and loaded when a user reopens a past session.
When the backend process restarts, MemorySaver is reset (in-process memory is gone), but SessionManager can reload messages from disk, so displayed history is never lost.
Managing sessions in the UI
The Streamlit sidebar exposes all session management actions:- Session list: displays all sessions from
GET /api/sessions, sorted most-recent-first. Clicking a session loads its full message history viaGET /api/sessions/{session_id}. - New Session button: generates a fresh UUID
thread_id, creates a blank session, and clears the chat window. - Rename: double-click a session title in the sidebar to edit it inline. The change is sent to
PUT /api/sessions/{session_id}. - Delete: click the delete icon next to a session to call
DELETE /api/sessions/{session_id}and remove it from the list.