This commit is contained in:
Timothy Jaeryang Baek
2026-07-26 23:49:03 -04:00
parent 2196b4e1ff
commit 6f93ecd4fd
16 changed files with 550 additions and 62 deletions
+57 -1
View File
@@ -115,6 +115,24 @@ async def add_active_state_to_chat_list(
return chat_list
async def get_folder_unread_counts(user_id: str, db: AsyncSession | None = None) -> dict[str, int]:
user_folders = await Folders.get_folders_by_user_id(user_id, db=db)
parent_by_id = {folder.id: folder.parent_id for folder in user_folders}
unread_counts = dict.fromkeys(parent_by_id.keys(), 0)
direct_unread_counts = await Chats.count_unread_by_folder_ids(user_id, list(parent_by_id.keys()), db=db)
for unread_folder_id, unread_count in direct_unread_counts.items():
current_id = unread_folder_id
seen = set()
while current_id and current_id not in seen:
seen.add(current_id)
if current_id in unread_counts:
unread_counts[current_id] += unread_count
current_id = parent_by_id.get(current_id)
return unread_counts
class ChatConfigForm(BaseModel):
ENABLE_CONTEXT_COMPACTION: bool
CONTEXT_COMPACTION_TOKEN_THRESHOLD: int
@@ -203,6 +221,8 @@ async def get_session_user_chat_list(
page: int | None = None,
include_pinned: bool | None = False,
include_folders: bool | None = False,
sort_by: str = 'updated_at',
sort_dir: str = 'desc',
db: AsyncSession = Depends(get_async_session),
):
try:
@@ -214,6 +234,8 @@ async def get_session_user_chat_list(
user.id,
include_folders=include_folders,
include_pinned=include_pinned,
sort_by=sort_by,
sort_dir=sort_dir,
skip=skip,
limit=limit,
db=db,
@@ -223,6 +245,8 @@ async def get_session_user_chat_list(
user.id,
include_folders=include_folders,
include_pinned=include_pinned,
sort_by=sort_by,
sort_dir=sort_dir,
db=db,
)
return await add_active_state_to_chat_list(request, chats)
@@ -869,6 +893,8 @@ async def get_chat_list_by_folder_id(
request: Request,
folder_id: str,
page: int | None = 1,
sort_by: str = 'unread_updated_at',
sort_dir: str = 'desc',
user=Depends(get_verified_user),
db: AsyncSession = Depends(get_async_session),
):
@@ -876,7 +902,15 @@ async def get_chat_list_by_folder_id(
limit = 10
skip = (page - 1) * limit
chats = await Chats.get_chats_by_folder_id_and_user_id(folder_id, user.id, skip=skip, limit=limit, db=db)
chats = await Chats.get_chats_by_folder_id_and_user_id(
folder_id,
user.id,
skip=skip,
limit=limit,
sort_by=sort_by,
sort_dir=sort_dir,
db=db,
)
return await add_active_state_to_chat_list(request, chats)
except Exception as e:
@@ -2033,6 +2067,28 @@ class ChatFolderIdForm(BaseModel):
folder_id: str | None = None
@router.post('/{id}/unread')
async def mark_chat_unread_by_id(
id: str,
user=Depends(get_verified_user),
db: AsyncSession = Depends(get_async_session),
):
chat = await Chats.mark_chat_unread_by_id(id, user.id, db=db)
if not chat:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=ERROR_MESSAGES.NOT_FOUND,
)
folder_id = await Chats.get_chat_folder_id(id, user.id, db=db)
return {
'chat_id': id,
'last_read_at': chat.last_read_at,
'folder_id': folder_id,
'folder_unread_counts': await get_folder_unread_counts(user.id, db=db),
}
@router.post('/{id}/folder', response_model=ChatResponse | None)
async def update_chat_folder_id_by_id(
request: Request,
+59 -14
View File
@@ -45,6 +45,24 @@ router = APIRouter()
from open_webui.utils.access_control.folders import has_folder_access as _has_folder_access
async def get_folder_unread_counts(user_id: str, db: AsyncSession | None = None) -> dict[str, int]:
folders = await Folders.get_folders_by_user_id(user_id, db=db)
parent_by_id = {folder.id: folder.parent_id for folder in folders}
unread_counts = dict.fromkeys(parent_by_id.keys(), 0)
direct_unread_counts = await Chats.count_unread_by_folder_ids(user_id, list(parent_by_id.keys()), db=db)
for unread_folder_id, unread_count in direct_unread_counts.items():
current_id = unread_folder_id
seen = set()
while current_id and current_id not in seen:
seen.add(current_id)
if current_id in unread_counts:
unread_counts[current_id] += unread_count
current_id = parent_by_id.get(current_id)
return unread_counts
async def check_folders_permission(request: Request, user, db=None):
"""Verify the folders feature is enabled and the user has permission."""
config = await Config.get_many('folders.enable', 'user.permissions')
@@ -97,19 +115,7 @@ async def get_folders(
folder_list.append(folder)
direct_unread_counts = await Chats.count_unread_by_folder_ids(
user.id, [folder.id for folder in folder_list], db=db
)
parent_by_id = {folder.id: folder.parent_id for folder in folder_list}
unread_counts = dict.fromkeys(parent_by_id.keys(), 0)
for unread_folder_id, unread_count in direct_unread_counts.items():
current_id = unread_folder_id
seen = set()
while current_id and current_id not in seen:
seen.add(current_id)
if current_id in unread_counts:
unread_counts[current_id] += unread_count
current_id = parent_by_id.get(current_id)
unread_counts = await get_folder_unread_counts(user.id, db=db)
return [
FolderNameIdResponse(**folder.model_dump(), unread_count=unread_counts.get(folder.id, 0))
@@ -504,7 +510,7 @@ async def get_shared_folder_chats(
request: Request,
id: str,
page: int | None = Query(None, ge=1),
sort_by: str = Query('updated_at'),
sort_by: str = Query('unread_updated_at'),
sort_dir: str = Query('desc'),
user=Depends(get_verified_user),
db: AsyncSession = Depends(get_async_session),
@@ -537,6 +543,7 @@ async def get_shared_folder_chats(
limit=limit if page is not None else 60,
sort_by=sort_by,
sort_dir=sort_dir,
unread_for_user_id=user.id,
db=db,
)
total = await Chats.count_all_chats_by_folder_id(id, db=db) if page is not None else len(chats)
@@ -564,6 +571,44 @@ async def get_shared_folder_chats(
return response
@router.post('/{id}/read')
async def mark_folder_chats_read_by_id(
request: Request,
id: str,
user=Depends(get_verified_user),
db: AsyncSession = Depends(get_async_session),
):
await check_folders_permission(request, user, db=db)
folder = await Folders.get_folder_by_id(id, db=db)
if not folder:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=ERROR_MESSAGES.NOT_FOUND,
)
is_owner = user.id == folder.user_id
is_admin = user.role == 'admin'
if not (is_owner or is_admin or await _has_folder_access(user.id, folder, 'read', db)):
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail=ERROR_MESSAGES.ACCESS_PROHIBITED,
)
folder_ids = (
await Folders.get_folder_ids_by_id_and_user_id_in_subtree(id, folder.user_id, db=db)
if is_owner or is_admin
else [id]
)
updated_count = await Chats.mark_chats_read_by_folder_ids(user.id, folder_ids, db=db)
return {
'folder_id': id,
'folder_ids': folder_ids,
'updated_count': updated_count,
'folder_unread_counts': await get_folder_unread_counts(user.id, db=db),
}
############################
# Delete Folder By Id
############################