Skip to content

Per-user chat conversation history (persistent, isolated, multiple threads) #10

Description

@gangtao

Problem

/chat is currently stateless: the client sends the full history array on every request (ChatRequest in src/tpk/server.py = message + history), and nothing is persisted server-side. Reloading the page or switching devices loses the conversation. Now that we have authenticated users (issue #8, merged in #9), chat history should be stored per user.

Requirements

  1. Persistent chat history — conversations are stored server-side (a Timeplus stream), so a user's past turns survive page reloads and are retrievable across devices.
  2. Per-user isolation — a user can only see their own conversations. No user can read, list, or resume another user's history. Enforced server-side against the authenticated identity (require_user), not just hidden in the UI.
  3. Multiple conversations per user — a user can hold several independent conversation threads (like ChatGPT's conversation list): create a new one, list their own, open/resume one, and delete one.

Naming caveat (important)

The word "session" is already taken: kg_sessions holds auth login tokens (issue #8). To avoid collision, this feature should use a distinct term — e.g. conversation / thread — with streams like kg_conversations (metadata: id, owner username, title, created/updated) and kg_conversation_messages (conversation_id, role, content, timestamp), following the existing corpus.py/auth.py mutable-stream store pattern.

Touchpoints

  • src/tpk/server.py/chat handler: associate each turn with a conversation id owned by user.username; persist the user message and the assistant's final answer. ChatRequest gains an optional conversation_id (server creates one when absent). The client can stop sending full history once the server rehydrates it from the store.
  • New store module (e.g. src/tpk/conversations.py) + schema in src/tpk/db.py (ensure_schema/drop_schema), mirroring auth.py.
  • New API routes under require_user (NOT require_admin — every user manages their own): GET /api/conversations (list mine), POST /api/conversations (new), GET /api/conversations/{id} (messages; 404 if not owned), POST /api/conversations/{id}/rename, POST /api/conversations/{id}/delete. Every handler must filter by user.username and treat another user's id as 404 (no existence leak).
  • Web UI: a conversation sidebar/list, new-chat button, resume-on-click, rename/delete; App.tsx chat state becomes per-conversation instead of the current single in-memory turns array.

Open questions (for design/brainstorming, not blocking the issue)

  • Auto-title conversations from the first message, or leave untitled until renamed?
  • Retention/limits (max conversations or messages per user)?
  • Does role-scoped chat (ROLE_SCOPE, issue Authentication & authorization: customer/admin roles, user store, credential encryption, timeplusd DB user #8) interact — e.g. should a stored conversation record which entry scope produced it? Probably out of scope for v1.
  • Admin visibility: per the requirement, admins do NOT get to see other users' histories through this feature (isolation is absolute); a separate audit capability would be its own issue if ever wanted.

Related: #8 (auth/users/roles, merged in #9) provides the require_user identity this builds on.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions