Skip to content

feat: persist chat history per user with sidebar navigation#298

Open
aizaz68 wants to merge 1 commit into
JoshuaC215:mainfrom
aizaz68:feat/293-chat-history-sidebar
Open

feat: persist chat history per user with sidebar navigation#298
aizaz68 wants to merge 1 commit into
JoshuaC215:mainfrom
aizaz68:feat/293-chat-history-sidebar

Conversation

@aizaz68

@aizaz68 aizaz68 commented Apr 27, 2026

Copy link
Copy Markdown

Summary

Closes #293 — adds a Chat History section to the Streamlit sidebar so users can see and resume past conversations.

How it works

The app already generates a persistent user_id (stored in a browser cookie/URL param) but had no way to map it to previous thread IDs — they were lost the moment a new chat started.

This PR introduces a lightweight thread_registry SQLite table that records every (user_id, thread_id) pair as soon as the user sends their first message in a thread. The first message becomes the conversation title. The registry is separate from LangGraph's checkpointer and requires no schema changes to existing tables.

Flow:

User sends first message
      ↓
Backend registers (user_id, thread_id, title="first message") in thread_registry
      ↓
Streamlit sidebar calls GET /threads?user_id=... on each render
      ↓
Past conversations appear as clickable buttons, newest first
      ↓
Clicking a thread loads its full history into the chat view

Files changed

File Change
src/memory/thread_store.py New — SQLite thread registry with setup/register/touch/list
src/memory/__init__.py Export thread store helpers; call setup_thread_registry() on startup
src/schema/schema.py Add ThreadInfo and ThreadListResponse Pydantic models
src/schema/__init__.py Export new schema types
src/service/service.py Register/touch thread on every request; add GET /threads endpoint
src/client/client.py Add get_threads(user_id) method
src/streamlit_app.py Render chat history section in sidebar

Test plan

  • Start fresh — send a few messages across multiple new chats, confirm each appears in the sidebar
  • Refresh the page — confirm history persists (it's in SQLite, not session state)
  • Click a past thread — confirm the full conversation loads correctly
  • Open in a different browser tab with the same user_id URL param — confirm history is shared
  • Different user_id sees only their own threads (user isolation)

🤖 Generated with Claude Code

Fixes JoshuaC215#293 — adds a sidebar in the Streamlit UI that lists all past
conversations for the current user, allowing them to resume any previous
thread by clicking it.

## How it works

A new `thread_registry` SQLite table maps every `user_id` (the persistent
browser cookie that already existed in the app) to its thread IDs, along
with a display title (the first user message) and timestamps.

- On every new message, the service registers the thread_id + user_id in
  the registry so the record survives page refreshes and browser restarts.
- A new `GET /threads?user_id=...` endpoint returns all threads for a user,
  newest first.
- The Streamlit sidebar fetches this list on each render and displays
  clickable buttons — clicking one loads that conversation into the main chat
  view without losing the current thread.

## Files changed

- `src/memory/thread_store.py` — new module: SQLite thread registry (CRUD)
- `src/memory/__init__.py` — export thread store helpers; setup on startup
- `src/schema/schema.py` — add `ThreadInfo` and `ThreadListResponse` models
- `src/schema/__init__.py` — export new schema types
- `src/service/service.py` — setup registry on startup; register/touch thread
  on every request; add `GET /threads` endpoint
- `src/client/client.py` — add `get_threads()` method
- `src/streamlit_app.py` — render chat history section in sidebar
JoshuaC215 pushed a commit that referenced this pull request Jun 30, 2026
- #261: the contributor's real question in #259 (how to supply IndexConfig
  at the initialize_store() call site when it needs an embedding function,
  not an .env string) was never answered. Rewrote the draft to answer it:
  put model name + dims in settings, build IndexConfig inside
  initialize_store() (config-driven, off by default), with a snippet.
- SKILL.md: add "answer the contributor's actual question first" pattern —
  check linked issues for an unanswered ask before jumping to test/lint.
- #298, #303 marked Approved per Joshua.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ALYJSRixXCfLsmb2Koc1Hi
@JoshuaC215

Copy link
Copy Markdown
Owner

Hey @aizaz68 — thanks for tackling #293. Before going further on this one: it adds a separate thread_registry table that duplicates state the checkpointer already persists, plus a fair amount of surface (new endpoint, client method, sidebar). I'd rather find a leaner design that derives the thread list from existing persistence instead of maintaining a second source of truth.

Can we hash out the approach in #293 first? @surajvariar was also looking at this. I want to get the design right before merging ~200 lines across 7 files — happy to give feedback early. Thanks for the effort here!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature: Add chat history

2 participants