mirror of
https://github.com/open-webui/open-webui.git
synced 2026-08-23 22:14:58 -06:00
perf: filter workspace models by access in SQL instead of loading every model (#28795)
Exporting workspace models loaded every model row, built a full response object with its owner for each, and only then dropped the ones the caller may not see. On a large model table that made the export endpoint slow in proportion to models the user cannot even access. The owner-or-grant check now happens in the query itself, reusing the permission filter this file already applies to the paginated list endpoint, so only visible rows are ever hydrated. The by-user wrapper had one caller left and is gone with it. Measured with 500 workspace models of which 3 are visible to the caller: 5 queries and ~12.7 ms before, 4 queries and ~2.8 ms after. The resulting set is unchanged for owner, public, direct-user, group and multi-grant entries, and base model entries stay excluded as before.
This commit is contained in:
@@ -244,9 +244,21 @@ class ModelsTable:
|
||||
log.error('Skipping model %r during get_all_models due to error: %s', model.id, exc)
|
||||
return models
|
||||
|
||||
async def get_models(self, db: AsyncSession | None = None) -> list[ModelUserResponse]:
|
||||
async def get_models(
|
||||
self, writable_by_user_id: str | None = None, db: AsyncSession | None = None
|
||||
) -> list[ModelUserResponse]:
|
||||
async with get_async_db_context(db) as db:
|
||||
result = await db.execute(select(Model).filter(Model.base_model_id != None))
|
||||
stmt = select(Model).filter(Model.base_model_id != None)
|
||||
|
||||
if writable_by_user_id:
|
||||
user_group_ids = {
|
||||
group.id for group in await Groups.get_groups_by_member_id(writable_by_user_id, db=db)
|
||||
}
|
||||
stmt = self._has_permission(
|
||||
db, stmt, {'user_id': writable_by_user_id, 'group_ids': user_group_ids}, permission='write'
|
||||
)
|
||||
|
||||
result = await db.execute(stmt)
|
||||
all_models = result.scalars().all()
|
||||
|
||||
user_ids = list(set(model.user_id for model in all_models))
|
||||
@@ -319,21 +331,6 @@ class ModelsTable:
|
||||
for model in all_models
|
||||
]
|
||||
|
||||
async def get_models_by_user_id(self, user_id: str, db: AsyncSession | None = None) -> list[ModelUserResponse]:
|
||||
models = await self.get_models(db=db)
|
||||
user_group_ids = {group.id for group in await Groups.get_groups_by_member_id(user_id, db=db)}
|
||||
|
||||
# One grants query for all non-owned models instead of one per model
|
||||
accessible_ids = await AccessGrants.get_accessible_resource_ids(
|
||||
user_id=user_id,
|
||||
resource_type='model',
|
||||
resource_ids=[model.id for model in models if model.user_id != user_id],
|
||||
permission='write',
|
||||
user_group_ids=user_group_ids,
|
||||
db=db,
|
||||
)
|
||||
return [model for model in models if model.user_id == user_id or model.id in accessible_ids]
|
||||
|
||||
def _has_permission(self, db, query, filter: dict, permission: str = 'read'):
|
||||
return AccessGrants.has_permission_filter(
|
||||
db=db,
|
||||
|
||||
@@ -353,7 +353,7 @@ async def export_models(
|
||||
if user.role == 'admin' and BYPASS_ADMIN_ACCESS_CONTROL:
|
||||
return await Models.get_models(db=db)
|
||||
else:
|
||||
return await Models.get_models_by_user_id(user.id, db=db)
|
||||
return await Models.get_models(writable_by_user_id=user.id, db=db)
|
||||
|
||||
|
||||
############################
|
||||
|
||||
Reference in New Issue
Block a user