Skip to content

Commit 743b9fd

Browse files
committed
refac
1 parent a2000df commit 743b9fd

12 files changed

Lines changed: 98 additions & 91 deletions

File tree

backend/open_webui/models/chat_messages.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,21 @@ async def get_message_by_id(self, id: str, db: Optional[AsyncSession] = None) ->
248248
message = await db.get(ChatMessage, id)
249249
return ChatMessageModel.model_validate(message) if message else None
250250

251+
async def has_unfinished_assistant_by_chat_id(
252+
self,
253+
chat_id: str,
254+
db: Optional[AsyncSession] = None,
255+
) -> bool:
256+
async with get_async_db_context(db) as db:
257+
result = await db.execute(
258+
select(ChatMessage.id)
259+
.where(ChatMessage.chat_id == chat_id)
260+
.where(ChatMessage.role == 'assistant')
261+
.where(ChatMessage.done.is_(False))
262+
.limit(1)
263+
)
264+
return result.scalar_one_or_none() is not None
265+
251266
async def get_messages_by_chat_id(self, chat_id: str, db: Optional[AsyncSession] = None) -> list[ChatMessageModel]:
252267
async with get_async_db_context(db) as db:
253268
result = await db.execute(

backend/open_webui/models/chats.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,7 @@ class ChatTitleIdResponse(BaseModel):
186186
created_at: int
187187
last_read_at: int | None = None
188188
snippet: str | None = None
189+
active: bool = False
189190

190191

191192
class SharedChatResponse(BaseModel):

backend/open_webui/routers/chats.py

Lines changed: 36 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
from open_webui.internal.db import get_async_session
1515
from open_webui.models.access_grants import AccessGrants
1616
from open_webui.models.config import Config
17+
from open_webui.models.chat_messages import ChatMessages
1718
from open_webui.models.chats import (
1819
AggregateChatStats,
1920
ChatBody,
@@ -57,6 +58,19 @@
5758
}
5859

5960

61+
async def add_active_state_to_chat_list(
62+
request: Request, chat_list: list[ChatTitleIdResponse]
63+
) -> list[ChatTitleIdResponse]:
64+
for chat in chat_list:
65+
chat.active = False
66+
if not await has_active_tasks(request.app.state.redis, chat.id):
67+
continue
68+
69+
chat.active = await ChatMessages.has_unfinished_assistant_by_chat_id(chat.id)
70+
71+
return chat_list
72+
73+
6074
class ChatConfigForm(BaseModel):
6175
ENABLE_CONTEXT_COMPACTION: bool
6276
CONTEXT_COMPACTION_TOKEN_THRESHOLD: int
@@ -137,6 +151,7 @@ async def require_chat_import_permission(request: Request, user, db: AsyncSessio
137151
@router.get('/', response_model=list[ChatTitleIdResponse])
138152
@router.get('/list', response_model=list[ChatTitleIdResponse])
139153
async def get_session_user_chat_list(
154+
request: Request,
140155
user=Depends(get_verified_user),
141156
page: int | None = None,
142157
include_pinned: bool | None = False,
@@ -148,7 +163,7 @@ async def get_session_user_chat_list(
148163
limit = 60
149164
skip = (page - 1) * limit
150165

151-
return await Chats.get_chat_title_id_list_by_user_id(
166+
chats = await Chats.get_chat_title_id_list_by_user_id(
152167
user.id,
153168
include_folders=include_folders,
154169
include_pinned=include_pinned,
@@ -157,12 +172,13 @@ async def get_session_user_chat_list(
157172
db=db,
158173
)
159174
else:
160-
return await Chats.get_chat_title_id_list_by_user_id(
175+
chats = await Chats.get_chat_title_id_list_by_user_id(
161176
user.id,
162177
include_folders=include_folders,
163178
include_pinned=include_pinned,
164179
db=db,
165180
)
181+
return await add_active_state_to_chat_list(request, chats)
166182
except Exception as e:
167183
log.exception(e)
168184
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=ERROR_MESSAGES.DEFAULT())
@@ -605,6 +621,7 @@ async def delete_all_user_chats(
605621

606622
@router.get('/list/user/{user_id}', response_model=list[ChatTitleIdResponse])
607623
async def get_user_chat_list_by_user_id(
624+
request: Request,
608625
user_id: str,
609626
page: int | None = None,
610627
query: str | None = None,
@@ -629,9 +646,10 @@ async def get_user_chat_list_by_user_id(
629646
if direction:
630647
filter['direction'] = direction
631648

632-
return await Chats.get_chat_list_by_user_id(
649+
chats = await Chats.get_chat_list_by_user_id(
633650
user_id, include_archived=True, filter=filter, skip=skip, limit=limit, db=db
634651
)
652+
return await add_active_state_to_chat_list(request, chats)
635653

636654

637655
############################
@@ -738,6 +756,7 @@ async def set_chat_config(form_data: ChatConfigForm, user=Depends(get_admin_user
738756

739757
@router.get('/search', response_model=list[ChatTitleIdResponse])
740758
async def search_user_chats(
759+
request: Request,
741760
text: str,
742761
page: int | None = None,
743762
user=Depends(get_verified_user),
@@ -763,7 +782,7 @@ async def search_user_chats(
763782
log.debug(f'deleting tag: {tag_id}')
764783
await Tags.delete_tag_by_name_and_user_id(tag_id, user.id, db=db)
765784

766-
return chat_list
785+
return await add_active_state_to_chat_list(request, chat_list)
767786

768787

769788
############################
@@ -786,8 +805,9 @@ async def get_chats_by_folder_id(
786805
]
787806

788807

789-
@router.get('/folder/{folder_id}/list')
808+
@router.get('/folder/{folder_id}/list', response_model=list[ChatTitleIdResponse])
790809
async def get_chat_list_by_folder_id(
810+
request: Request,
791811
folder_id: str,
792812
page: int | None = 1,
793813
user=Depends(get_verified_user),
@@ -798,10 +818,7 @@ async def get_chat_list_by_folder_id(
798818
skip = (page - 1) * limit
799819

800820
chats = await Chats.get_chats_by_folder_id_and_user_id(folder_id, user.id, skip=skip, limit=limit, db=db)
801-
return [
802-
{'title': chat.title, 'id': chat.id, 'updated_at': chat.updated_at, 'last_read_at': chat.last_read_at}
803-
for chat in chats
804-
]
821+
return await add_active_state_to_chat_list(request, chats)
805822

806823
except Exception as e:
807824
log.exception(e)
@@ -814,8 +831,11 @@ async def get_chat_list_by_folder_id(
814831

815832

816833
@router.get('/pinned', response_model=list[ChatTitleIdResponse])
817-
async def get_user_pinned_chats(user=Depends(get_verified_user), db: AsyncSession = Depends(get_async_session)):
818-
return await Chats.get_pinned_chats_by_user_id(user.id, db=db)
834+
async def get_user_pinned_chats(
835+
request: Request, user=Depends(get_verified_user), db: AsyncSession = Depends(get_async_session)
836+
):
837+
chats = await Chats.get_pinned_chats_by_user_id(user.id, db=db)
838+
return await add_active_state_to_chat_list(request, chats)
819839

820840

821841
############################
@@ -908,6 +928,7 @@ async def get_all_user_chats_in_db(user=Depends(get_admin_user), db: AsyncSessio
908928

909929
@router.get('/archived', response_model=list[ChatTitleIdResponse])
910930
async def get_archived_session_user_chat_list(
931+
request: Request,
911932
page: int | None = None,
912933
query: str | None = None,
913934
order_by: str | None = None,
@@ -929,13 +950,14 @@ async def get_archived_session_user_chat_list(
929950
if direction:
930951
filter['direction'] = direction
931952

932-
return await Chats.get_archived_chat_list_by_user_id(
953+
chats = await Chats.get_archived_chat_list_by_user_id(
933954
user.id,
934955
filter=filter,
935956
skip=skip,
936957
limit=limit,
937958
db=db,
938959
)
960+
return await add_active_state_to_chat_list(request, chats)
939961

940962

941963
############################
@@ -1103,6 +1125,7 @@ class TagFilterForm(TagForm):
11031125

11041126
@router.post('/tags', response_model=list[ChatTitleIdResponse])
11051127
async def get_user_chat_list_by_tag_name(
1128+
request: Request,
11061129
form_data: TagFilterForm,
11071130
user=Depends(get_verified_user),
11081131
db: AsyncSession = Depends(get_async_session),
@@ -1113,7 +1136,7 @@ async def get_user_chat_list_by_tag_name(
11131136
if len(chats) == 0:
11141137
await Tags.delete_tag_by_name_and_user_id(form_data.name, user.id, db=db)
11151138

1116-
return chats
1139+
return await add_active_state_to_chat_list(request, chats)
11171140

11181141

11191142
############################

backend/open_webui/routers/tasks.py

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -75,19 +75,6 @@ def config_updates(data: dict, key_map: dict[str, str]) -> dict:
7575
##################################
7676

7777

78-
class ActiveChatsForm(BaseModel):
79-
chat_ids: list[str]
80-
81-
82-
@router.post('/active/chats')
83-
async def check_active_chats(request: Request, form_data: ActiveChatsForm, user=Depends(get_verified_user)):
84-
"""Check which chat IDs have active tasks."""
85-
from open_webui.tasks import get_active_chat_ids
86-
87-
active = await get_active_chat_ids(request.app.state.redis, form_data.chat_ids)
88-
return {'active_chat_ids': active}
89-
90-
9178
@router.get('/config')
9279
async def get_task_config(request: Request, user=Depends(get_verified_user)):
9380
return await get_config_values(TASK_CONFIG_KEYS)

backend/open_webui/tasks.py

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -199,12 +199,3 @@ async def has_active_tasks(redis, chat_id: str) -> bool:
199199
"""Check if a chat has any active tasks."""
200200
task_ids = await list_task_ids_by_item_id(redis, chat_id)
201201
return len(task_ids) > 0
202-
203-
204-
async def get_active_chat_ids(redis, chat_ids: List[str]) -> List[str]:
205-
"""Filter a list of chat_ids to only those with active tasks."""
206-
active = []
207-
for chat_id in chat_ids:
208-
if await has_active_tasks(redis, chat_id):
209-
active.append(chat_id)
210-
return active

src/lib/apis/tasks/index.ts

Lines changed: 0 additions & 14 deletions
This file was deleted.

src/lib/components/layout/Sidebar.svelte

Lines changed: 25 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,9 @@
2525
models,
2626
selectedFolder,
2727
WEBUI_NAME,
28-
sidebarWidth,
29-
activeChatIds
28+
sidebarWidth
3029
} from '$lib/stores';
31-
import { loadNextChatListPage, refreshChatList } from '$lib/stores/chatList';
30+
import { loadNextChatListPage, refreshChatList, setChatActive } from '$lib/stores/chatList';
3231
import { onMount, getContext, tick, onDestroy } from 'svelte';
3332
3433
const i18n = getContext('i18n');
@@ -52,7 +51,6 @@
5251
} from '$lib/apis/folders';
5352
import { createNewNote, getPinnedNoteList, toggleNotePinnedStatusById } from '$lib/apis/notes';
5453
import { updateUserSettings } from '$lib/apis/users';
55-
import { checkActiveChats } from '$lib/apis/tasks';
5654
import { createNoteHandler } from '$lib/components/notes/utils';
5755
import { WEBUI_API_BASE_URL, WEBUI_BASE_URL } from '$lib/constants';
5856
@@ -110,7 +108,7 @@
110108
let showSharedFolders = false;
111109
112110
let folders = {};
113-
let folderRegistry = {};
111+
let folderRegistry: Record<string, { setFolderItems?: () => unknown }> = {};
114112
115113
let newFolderId = null;
116114
@@ -366,18 +364,20 @@
366364
})(),
367365
(async () => {
368366
console.log('Init chat list');
369-
const result = await refreshChatList(localStorage.token, { refreshPinned: true });
370-
if (result.accepted) {
371-
await Promise.all(
372-
Object.values(folderRegistry).map((folder: any) => folder?.setFolderItems?.())
373-
);
374-
allChatsLoaded = result.allLoaded;
375-
chatListReady = true;
376-
}
367+
await refreshChatRows();
377368
})()
378369
]);
379370
};
380371
372+
const refreshChatRows = async () => {
373+
const result = await refreshChatList(localStorage.token, { refreshPinned: true });
374+
if (result.accepted) {
375+
await Promise.all(Object.values(folderRegistry).map((folder) => folder?.setFolderItems?.()));
376+
allChatsLoaded = result.allLoaded;
377+
chatListReady = true;
378+
}
379+
};
380+
381381
const loadMoreChats = async () => {
382382
chatListLoading = true;
383383
@@ -610,17 +610,6 @@
610610
await initChannels();
611611
}
612612
await initChatList();
613-
614-
// Check which chats have active tasks
615-
const allChatIds = [...$chats.map((c) => c.id), ...$pinnedChats.map((c) => c.id)];
616-
if (allChatIds.length > 0) {
617-
try {
618-
const res = await checkActiveChats(localStorage.token, allChatIds);
619-
activeChatIds.set(new Set(res.active_chat_ids || []));
620-
} catch (e) {
621-
console.debug('Failed to check active chats:', e);
622-
}
623-
}
624613
}
625614
}),
626615
settings.subscribe((value) => {
@@ -649,6 +638,7 @@
649638
650639
const socketInstance = $socket;
651640
socketInstance?.on('events', chatActiveEventHandler);
641+
socketInstance?.on('connect', refreshChatRows);
652642
653643
await tick();
654644
initPinnedMenuSortable();
@@ -672,28 +662,24 @@
672662
}
673663
674664
socketInstance?.off('events', chatActiveEventHandler);
665+
socketInstance?.off('connect', refreshChatRows);
675666
};
676667
});
677668
678669
// Handler for chat events (defined outside onMount for proper cleanup)
679-
const chatActiveEventHandler = (event: {
670+
const chatActiveEventHandler = async (event: {
680671
chat_id: string;
681672
message_id: string;
682-
data: { type: string; data: any };
673+
data: { type: string; data: { active?: boolean } };
683674
}) => {
684675
if (event.data?.type === 'chat:active') {
685-
const { active } = event.data.data;
686-
activeChatIds.update((ids) => {
687-
const newSet = new Set(ids);
688-
if (active) {
689-
newSet.add(event.chat_id);
690-
} else {
691-
newSet.delete(event.chat_id);
692-
}
693-
return newSet;
694-
});
676+
const active = event.data.data.active ?? false;
677+
const found = setChatActive(event.chat_id, active);
678+
if (!found && active) {
679+
await refreshChatRows();
680+
}
695681
} else if (event.data?.type === 'chat:list') {
696-
initChatList();
682+
refreshChatRows();
697683
}
698684
};
699685
@@ -1416,6 +1402,7 @@
14161402
createdAt={chat.created_at}
14171403
updatedAt={chat.updated_at}
14181404
lastReadAt={chat.last_read_at}
1405+
active={chat.active ?? false}
14191406
{shiftKey}
14201407
selected={selectedChatId === chat.id}
14211408
on:select={() => {
@@ -1479,6 +1466,7 @@
14791466
createdAt={chat.created_at}
14801467
updatedAt={chat.updated_at}
14811468
lastReadAt={chat.last_read_at}
1469+
active={chat.active ?? false}
14821470
{shiftKey}
14831471
selected={selectedChatId === chat.id}
14841472
on:select={() => {

0 commit comments

Comments
 (0)