Remove Depends(get_session) from POST /query endpoint to prevent database connections from being held during embedding API calls (1-5+ seconds).
The Memories.get_memories_by_user_id() function manages its own short-lived session internally, releasing the connection before the slow EMBEDDING_FUNCTION() call begins.
Remove Depends(get_session) from POST /create endpoint to prevent database connections from being held during embedding API calls (1-5+ seconds).
The has_permission() and Knowledges.insert_new_knowledge() functions manage their own short-lived sessions internally, releasing connections before the slow embed_knowledge_base_metadata() call begins.
Remove Depends(get_session) from POST /{id}/update endpoint to prevent database connections from being held during embedding API calls (1-5+ seconds).
All database operations (get_knowledge_by_id, has_access, has_permission, update_knowledge_by_id, get_file_metadatas_by_id) manage their own short-lived sessions internally, releasing connections before and after the slow embed_knowledge_base_metadata() call.
Remove Depends(get_session) from the /v1/completions endpoint to prevent database connections from being held during the entire duration of LLM calls.
Previously, the database session was acquired at request start and held until the response completed. Under concurrent load, this exhausted the connection pool, causing QueuePool timeout errors.
The fix allows Models.get_model_by_id() and has_access() to manage their own short-lived sessions internally, releasing the connection immediately after authorization checks complete.
Remove Depends(get_session) from the /v1/chat/completions endpoint to prevent database connections from being held during the entire duration of LLM calls.
Previously, the database session was acquired at request start and held until the streaming response completed. Under concurrent load, this exhausted the connection pool, causing QueuePool timeout errors.
The fix allows Models.get_model_by_id() and has_access() to manage their own short-lived sessions internally, releasing the connection immediately after authorization checks complete.
Remove Depends(get_session) from the /api/chat endpoint to prevent database connections from being held during the entire duration of LLM calls (30-60+ seconds for streaming responses).
Previously, the database session was acquired at request start and held until the streaming response completed. Under concurrent load, this exhausted the connection pool, causing QueuePool timeout errors for other database operations.
The fix allows Models.get_model_by_id() and has_access() to manage their own short-lived sessions internally, releasing the connection immediately after the quick authorization checks complete - before the slow external LLM API call begins.
Remove Depends(get_session) from the /chat/completions endpoint to prevent database connections from being held during the entire duration of LLM calls (30-60+ seconds for streaming responses).
Previously, the database session was acquired at request start and held until the streaming response completed. Under concurrent load, this exhausted the connection pool, causing QueuePool timeout errors for other database operations.
The fix allows Models.get_model_by_id() and has_access() to manage their own short-lived sessions internally, releasing the connection immediately after the quick authorization checks complete - before the slow external LLM API call begins.
Remove Depends(get_session) from POST /add endpoint to prevent database connections from being held during embedding API calls (1-5+ seconds).
The Memories.insert_new_memory() function manages its own short-lived session internally, releasing the connection before the slow EMBEDDING_FUNCTION() call begins.
Remove Depends(get_session) from POST /metadata/reindex endpoint to prevent database connections from being held during N embedding API calls.
This endpoint is CRITICAL as it loops through ALL knowledge bases and calls embed_knowledge_base_metadata() for each one. With the original code, a single connection would be held for the entire duration (potentially minutes for large deployments), completely exhausting the pool.
The Knowledges.get_knowledge_bases() function manages its own short-lived session, releasing the connection before the embedding loop begins.
Remove Depends(get_session) from POST /process/files/batch endpoint to prevent database connections from being held during batch embedding API calls (5-60+ seconds for large batches).
The save_docs_to_vector_db() function makes external embedding API calls. Post-embedding file updates (Files.update_file_by_id) manage their own short-lived sessions internally, releasing connections promptly.
Remove Depends(get_session) from POST /reset to prevent catastrophic connection pool exhaustion.
This endpoint was holding a SINGLE database connection while executing N PARALLEL embedding API calls via asyncio.gather(). For a user with 100 memories, this meant one connection blocked for potentially MINUTES (100 calls * 1-5 seconds each, even in parallel due to rate limits).
A single user triggering /reset could completely starve the connection pool, causing QueuePool timeout errors across the entire application.
The Memories.get_memories_by_user_id() function now manages its own short-lived session, releasing the connection immediately before the massive parallel embedding operation begins.
Refactored the file processing status streaming endpoint to avoid holding
a database connection for the entire stream duration (up to 2 hours).
Changes:
- Each status poll now creates its own short-lived database session instead
of capturing the request's session in the generator closure
- Increased poll interval from 0.5s to 1s, halving database queries with
negligible UX impact
This prevents a single file status stream from blocking a connection pool
slot for hours, which could contribute to pool exhaustion under load.
* feat: add ENABLE_USER_STATUS toggle for admin-controlled user status visibility
feat: add ENABLE_USER_STATUS toggle for admin-controlled user status visibility
Add a new admin panel toggle (Admin > Settings > General) called "User Status" that allows administrators to globally enable or disable user status functionality.
When disabled:
- User status API endpoints return 403 Forbidden
- Status emoji, message, and "Update your status" button are hidden from the user menu
The setting:
- Defaults to True (enabled)
- Can be overridden via ENABLE_USER_STATUS environment variable
- Persists across restarts using PersistentConfig
Files modified:
- backend/open_webui/config.py - Added ENABLE_USER_STATUS PersistentConfig
- backend/open_webui/main.py - App state init and features dict
- backend/open_webui/routers/auths.py - AdminConfig model and endpoints
- backend/open_webui/routers/users.py - 403 guards on status endpoints
- src/lib/components/admin/Settings/General.svelte - Toggle UI
- src/lib/components/layout/Sidebar/UserMenu.svelte - Conditional status display
* Update UserMenu.svelte
feat: add ENABLE_USER_STATUS toggle for admin-controlled user status visibility
Add a new admin panel toggle (Admin > Settings > General) called "User Status" that allows administrators to globally enable or disable user status functionality.
When disabled:
- User status API endpoints return 403 Forbidden
- Active/Away indicator with blinking dot is hidden from the user menu
- Status emoji, message, and "Update your status" button are hidden from the user menu
The setting:
- Defaults to True (enabled)
- Can be overridden via ENABLE_USER_STATUS environment variable
- Persists across restarts using PersistentConfig
Files modified:
- backend/open_webui/config.py - Added ENABLE_USER_STATUS PersistentConfig
- backend/open_webui/main.py - App state init and features dict
- backend/open_webui/routers/auths.py - AdminConfig model and endpoints
- backend/open_webui/routers/users.py - 403 guards on status endpoints
- src/lib/components/admin/Settings/General.svelte - Toggle UI
- src/lib/components/layout/Sidebar/UserMenu.svelte - Conditional status display
* nuke the indicator
* fix
Replaced per-message user lookup with batch fetch using SQL IN clause.
Changes:
- Fetch all message user_ids in a single pass
- Use Users.get_users_by_user_ids() for batch lookup
- Build user mapping to avoid DB calls in loop
- Add early return for empty message lists
Performance: Reduces N+1 queries to 2 queries (messages + users)
Replaced per-message user lookup with batch fetch using SQL IN clause.
Changes:
- Fetch all message user_ids in a single pass
- Use Users.get_users_by_user_ids() for batch lookup
- Build user mapping to avoid DB calls in loop
- Add early return for empty message lists
Performance: Reduces N+1 queries to 2 queries (messages + users)
## Summary
Fixed N+1 query pattern in the `/api/v1/users` endpoint where groups were being fetched for each user individually.
### Problem
The `GET /api/v1/users` endpoint called `Groups.get_groups_by_member_id()` for each user, resulting in:
- 1 query for users
- N queries for groups (one per user)
### Solution
Added a new `Groups.get_groups_by_member_ids()` method that fetches groups for multiple users in a single query using SQL `IN` clause and `JOIN`.
### Changes
- **[groups.py](open_webui/models/groups.py)**: Added `get_groups_by_member_ids()` method
- **[users.py](open_webui/routers/users.py)**: Updated endpoint to use bulk method
### Result
- Before: 1 + N queries
- After: 2 queries total (1 for users, 1 for all groups)
* feat: Add read-only access support for Tools
- Backend: Add write_access field to ToolAccessResponse
- Backend: Update /tools/list to return tools with write_access
- Frontend: Display Read Only badge in Tools list
- Frontend: Disable inputs and save button when no write access
- Frontend: Add readOnly prop to CodeEditor component
* Update Tools.svelte
* fix: Return write_access from getToolById endpoint
fix: Return write_access from getToolById endpoint
- Use ToolAccessResponse instead of raw dict
- Remove inefficient getToolList call in edit page
* refactor: Rename write_access to disabled in ToolkitEditor
- Rename prop from write_access to disabled
- Invert logic where needed
- Update edit page to pass disabled instead of write_access
* rem
* Update +page.svelte
* fix
* Update ToolkitEditor.svelte
* Update CodeEditor.svelte
* Update ToolkitEditor.svelte
* feat: Add read-only access support for Models
- Backend: Add write_access field to ModelAccessResponse
- Backend: Update /models/list to return ModelAccessListResponse
- Frontend: Display Read Only badge in Models list
- Frontend: Disable inputs and save button when no write access
- Frontend: Hide action buttons for read-only models
* fix: Handle ModelAccessListResponse format in getModels API
- Backend returns {items, total} instead of {data}
- Update getModels API to handle both formats for backward compatibility
* fix: Show read-only shared models in workspace list
- Backend: Change search_models permission from 'write' to 'read' to include shared models
- Backend: Keep user_id filter to only show owned/shared models (not all public)
- Frontend: Handle ModelAccessListResponse format in getModels API
* fix: Align Read Only badge inline with model name
* fix: Correct badge placement and fix syntax error
* fix: Resolve badge truncation in Models list
- Add w-full to flex container for proper spacing
- Wrap Badge in div to prevent truncation
- Match Knowledge.svelte badge pattern
* fix: Align Read Only badge with Knowledge.svelte pattern
- Match Knowledge.svelte structure for badge placement
- Actions only show when write_access or admin
- Remove w-full from container to prevent right-overflow
* fix: Return write_access from getModelById endpoint
fix: Return write_access from getModelById endpoint
- Use ModelAccessResponse instead of raw dict
- Remove inefficient getModels call in edit page
* revert
* fix
* fix
* fix