LLM Council persists every conversation to your local filesystem as a plain JSON file. There is no database, no cloud sync, and no account required. When you restart the app, your conversation history reappears exactly as you left it — with one important exception around ranking metadata that this guide explains in detail.Documentation Index
Fetch the complete documentation index at: https://mintlify.com/karpathy/llm-council/llms.txt
Use this file to discover all available pages before exploring further.
Storage location
Conversations are written todata/conversations/ relative to the project root. This path is controlled by DATA_DIR in backend/config.py:
backend/config.py
Conversation file structure
Every file follows this shape, matching whatstorage.py writes:
data/conversations/<uuid>.json
messages entry is either a user object (with a content string) or an assistant object (with stage1, stage2, and stage3 fields). The stage2 entries include both the raw evaluation text and the parsed_ranking list that was extracted from the FINAL RANKING: section.
What is and is not persisted
Persisted to disk:- User message content
- Every council member’s Stage 1 response (model identifier + full text)
- Every council member’s Stage 2 evaluation (model identifier, raw ranking text, and parsed ranking list)
- The Chairman’s Stage 3 synthesized response
- Conversation title and creation timestamp
label_to_model mapping and aggregate_rankings that are returned in the API response metadata field are not written to the JSON file. They are computed fresh during each council run and exist only in the API response payload and in the frontend’s in-memory state for that session.
Auto-generated titles
When you send the first message in a new conversation, LLM Council firesgenerate_conversation_title() in backend/council.py. This function calls google/gemini-2.5-flash with a prompt that asks for a 3–5 word summary of your question. The resulting title is saved immediately to the conversation file via update_conversation_title(), so it survives restarts. In the streaming endpoint, title generation runs as a background asyncio task in parallel with Stage 1 to avoid adding latency.
Storage functions reference
backend/storage.py exposes these functions, each of which reads or writes the JSON file for the relevant conversation:
| Function | Description |
|---|---|
create_conversation(id) | Creates a new JSON file with empty messages and a placeholder title |
get_conversation(id) | Loads and returns a conversation dict, or None if not found |
save_conversation(conversation) | Overwrites the JSON file with the current conversation dict |
list_conversations() | Scans DATA_DIR, returns metadata for all conversations sorted by created_at descending |
add_user_message(id, content) | Appends a user message and saves |
add_assistant_message(id, stage1, stage2, stage3) | Appends the full three-stage assistant message and saves |
update_conversation_title(id, title) | Updates the title field and saves |
Sidebar ordering
The conversation sidebar lists all conversations sorted bycreated_at in descending order — newest conversations appear at the top. This ordering is applied in list_conversations() before the list is returned to the frontend.
There is no built-in search, filtering, or export feature. Because conversations are standard JSON files, you can process them with any JSON-aware tool —
jq, Python, or a spreadsheet that imports JSON — to extract, analyze, or transform your conversation history however you like.