Spaces:
Sleeping
Per-User Session Isolation Implementation Guide
Overview
This guide explains how to implement per-user session isolation in the ID Agents app so that each authenticated user has their own isolated workspace (chat histories, agents, patient data, etc.).
Problem
Currently, all users share the same gr.State() objects, meaning:
- User A sees User B's chat messages
- User A's agents appear in User B's dropdown
- Everyone works in the same shared container
Solution
Use Gradio's gr.Request object to identify users and store their data separately in a UserSessionManager.
Files Created
- user_session_manager.py - Core session storage with thread-safe operations
- session_helpers.py - Helper functions for getting/setting user data
Implementation Steps
Step 1: Update Function Signatures
All functions that currently accept gr.State parameters need to accept request: gr.Request instead:
Before:
def simple_chat_response(user_message, history):
# Uses history parameter
...
After:
def simple_chat_response(user_message, request: gr.Request):
# Gets history from session manager
history = get_user_simple_chat_history(request)
...
# Saves history back to session manager
set_user_simple_chat_history(request, updated_history)
Step 2: Update Gradio Event Bindings
When binding functions to Gradio components, remove gr.State from inputs/outputs and add request:
Before:
simple_input.submit(
simple_chat_response,
inputs=[simple_input, simple_chat_history],
outputs=[simple_chatbot, simple_input]
)
After:
simple_input.submit(
simple_chat_response,
inputs=[simple_input], # request is added automatically by Gradio
outputs=[simple_chatbot, simple_input]
)
Step 3: Remove gr.State() Declarations
In build_ui(), remove these lines:
simple_chat_history = gr.State([])
builder_chat_histories = gr.State({})
deployed_chat_histories = gr.State({})
These are now managed by the session manager per-user.
Functions That Need Updates
Critical Functions (High Priority)
- ✅
simple_chat_response- Already updated - ✅
chatpanel_handle- Already updated load_history- Needs updatereset_chat- May need update if it affects per-user statesave_deployed_agent- If it stores to chat historiespopulate_from_preset- If it affects builder state
UI Event Bindings That Need Updates
All .click(), .submit(), .change() handlers that currently use:
simple_chat_historybuilder_chat_historiesdeployed_chat_histories- Any other
gr.State()objects
Testing Checklist
After implementation, test with 2 different user accounts simultaneously:
- User1 and User2 have separate simple chat histories
- User1's agents don't appear in User2's dropdown
- User1 and User2 can build different agents without interference
- Patient data is separate between users
- Reset/clear functions only affect the current user
- Logout/re-login maintains session (or clears if desired)
Key Benefits
- True Multi-Tenancy: Each user gets isolated workspace
- No Data Leakage: User A cannot see User B's data
- Thread-Safe: Concurrent users don't interfere with each other
- Scalable: Can handle multiple simultaneous users
- Auditable: Can log per-user actions for debugging
Important Notes
gr.Requestonly works when authentication is enabled- Username comes from
request.username(set by Gradio's auth system) - Session data is stored in memory (lost on restart - could be persisted to database if needed)
- The session manager is thread-safe for concurrent access
Migration Strategy
Phase 1: Core Chat Functions (DONE)
- ✅ simple_chat_response
- ✅ chatpanel_handle
Phase 2: Agent Management
- load_history
- save_deployed_agent
- remove_selected_agent
Phase 3: UI Bindings
- Update all .click() and .submit() bindings
- Remove gr.State declarations
Phase 4: Testing & Verification
- Multi-user testing
- Session isolation verification
- Performance testing
Quick Reference
# Import at top of app.py (DONE)
from user_session_manager import session_manager, get_username_from_request, SessionKeys
from session_helpers import (
get_user_simple_chat_history, set_user_simple_chat_history,
get_user_builder_chat_histories, set_user_builder_chat_histories,
get_user_deployed_chat_histories, set_user_deployed_chat_histories,
get_current_username, log_user_access
)
# In any function:
def my_function(user_input, request: gr.Request):
username = get_current_username(request)
log_user_access(request, "my_function")
# Get user-specific data
data = session_manager.get_user_data(username, "my_key", default=[])
# Process...
# Save user-specific data
session_manager.set_user_data(username, "my_key", new_data)
Next Steps
To complete the implementation:
- Search for all
gr.State(declarations and remove them - Search for all functions that have
historiesorhistoryparameters - Update each function to use session helpers
- Update all Gradio event bindings
- Test with multiple users
- Deploy and verify
For assistance, see:
- user_session_manager.py - Core session storage
- session_helpers.py - Helper functions and examples