@@ -111,7 +111,7 @@ class ChatMessageModel(BaseModel):
111111 embeds : Optional [list ] = None
112112 done : bool = True
113113 status_history : Optional [list ] = None
114- error : Optional [dict ] = None
114+ error : Optional [dict | str ] = None
115115 usage : Optional [dict ] = None
116116 created_at : int
117117 updated_at : int
@@ -269,6 +269,36 @@ def get_messages_by_model_id(
269269 )
270270 return [ChatMessageModel .model_validate (message ) for message in messages ]
271271
272+ def get_chat_ids_by_model_id (
273+ self ,
274+ model_id : str ,
275+ start_date : Optional [int ] = None ,
276+ end_date : Optional [int ] = None ,
277+ skip : int = 0 ,
278+ limit : int = 50 ,
279+ db : Optional [Session ] = None ,
280+ ) -> list [str ]:
281+ """Get distinct chat_ids that used a specific model."""
282+ from sqlalchemy import distinct
283+
284+ with get_db_context (db ) as db :
285+ query = db .query (distinct (ChatMessage .chat_id )).filter (
286+ ChatMessage .model_id == model_id
287+ )
288+ if start_date :
289+ query = query .filter (ChatMessage .created_at >= start_date )
290+ if end_date :
291+ query = query .filter (ChatMessage .created_at <= end_date )
292+
293+ # Order by most recent message in each chat
294+ chat_ids = (
295+ query .order_by (ChatMessage .created_at .desc ())
296+ .offset (skip )
297+ .limit (limit )
298+ .all ()
299+ )
300+ return [chat_id for (chat_id ,) in chat_ids ]
301+
272302 def delete_messages_by_chat_id (
273303 self , chat_id : str , db : Optional [Session ] = None
274304 ) -> bool :
@@ -282,19 +312,28 @@ def get_message_count_by_model(
282312 self ,
283313 start_date : Optional [int ] = None ,
284314 end_date : Optional [int ] = None ,
315+ group_id : Optional [str ] = None ,
285316 db : Optional [Session ] = None ,
286317 ) -> dict [str , int ]:
287318 with get_db_context (db ) as db :
288319 from sqlalchemy import func
320+ from open_webui .models .groups import GroupMember
289321
290322 query = db .query (
291323 ChatMessage .model_id , func .count (ChatMessage .id ).label ("count" )
292- ).filter (ChatMessage .role == "assistant" , ChatMessage .model_id .isnot (None ))
324+ ).filter (
325+ ChatMessage .role == "assistant" ,
326+ ChatMessage .model_id .isnot (None ),
327+ ~ ChatMessage .user_id .like ("shared-%" ),
328+ )
293329
294330 if start_date :
295331 query = query .filter (ChatMessage .created_at >= start_date )
296332 if end_date :
297333 query = query .filter (ChatMessage .created_at <= end_date )
334+ if group_id :
335+ group_users = db .query (GroupMember .user_id ).filter (GroupMember .group_id == group_id ).subquery ()
336+ query = query .filter (ChatMessage .user_id .in_ (group_users ))
298337
299338 results = query .group_by (ChatMessage .model_id ).all ()
300339 return {row .model_id : row .count for row in results }
@@ -303,11 +342,13 @@ def get_token_usage_by_model(
303342 self ,
304343 start_date : Optional [int ] = None ,
305344 end_date : Optional [int ] = None ,
345+ group_id : Optional [str ] = None ,
306346 db : Optional [Session ] = None ,
307347 ) -> dict [str , dict ]:
308348 """Aggregate token usage by model using database-level aggregation."""
309349 with get_db_context (db ) as db :
310350 from sqlalchemy import func , cast , Integer
351+ from open_webui .models .groups import GroupMember
311352
312353 dialect = db .bind .dialect .name
313354
@@ -338,12 +379,16 @@ def get_token_usage_by_model(
338379 ChatMessage .role == "assistant" ,
339380 ChatMessage .model_id .isnot (None ),
340381 ChatMessage .usage .isnot (None ),
382+ ~ ChatMessage .user_id .like ("shared-%" ),
341383 )
342384
343385 if start_date :
344386 query = query .filter (ChatMessage .created_at >= start_date )
345387 if end_date :
346388 query = query .filter (ChatMessage .created_at <= end_date )
389+ if group_id :
390+ group_users = db .query (GroupMember .user_id ).filter (GroupMember .group_id == group_id ).subquery ()
391+ query = query .filter (ChatMessage .user_id .in_ (group_users ))
347392
348393 results = query .group_by (ChatMessage .model_id ).all ()
349394
@@ -396,6 +441,7 @@ def get_token_usage_by_user(
396441 ChatMessage .role == "assistant" ,
397442 ChatMessage .user_id .isnot (None ),
398443 ChatMessage .usage .isnot (None ),
444+ ~ ChatMessage .user_id .like ("shared-%" ),
399445 )
400446
401447 if start_date :
@@ -419,19 +465,24 @@ def get_message_count_by_user(
419465 self ,
420466 start_date : Optional [int ] = None ,
421467 end_date : Optional [int ] = None ,
468+ group_id : Optional [str ] = None ,
422469 db : Optional [Session ] = None ,
423470 ) -> dict [str , int ]:
424471 with get_db_context (db ) as db :
425472 from sqlalchemy import func
473+ from open_webui .models .groups import GroupMember
426474
427475 query = db .query (
428476 ChatMessage .user_id , func .count (ChatMessage .id ).label ("count" )
429- )
477+ ). filter ( ~ ChatMessage . user_id . like ( "shared-%" ))
430478
431479 if start_date :
432480 query = query .filter (ChatMessage .created_at >= start_date )
433481 if end_date :
434482 query = query .filter (ChatMessage .created_at <= end_date )
483+ if group_id :
484+ group_users = db .query (GroupMember .user_id ).filter (GroupMember .group_id == group_id ).subquery ()
485+ query = query .filter (ChatMessage .user_id .in_ (group_users ))
435486
436487 results = query .group_by (ChatMessage .user_id ).all ()
437488 return {row .user_id : row .count for row in results }
@@ -440,19 +491,24 @@ def get_message_count_by_chat(
440491 self ,
441492 start_date : Optional [int ] = None ,
442493 end_date : Optional [int ] = None ,
494+ group_id : Optional [str ] = None ,
443495 db : Optional [Session ] = None ,
444496 ) -> dict [str , int ]:
445497 with get_db_context (db ) as db :
446498 from sqlalchemy import func
499+ from open_webui .models .groups import GroupMember
447500
448501 query = db .query (
449502 ChatMessage .chat_id , func .count (ChatMessage .id ).label ("count" )
450- )
503+ ). filter ( ~ ChatMessage . user_id . like ( "shared-%" ))
451504
452505 if start_date :
453506 query = query .filter (ChatMessage .created_at >= start_date )
454507 if end_date :
455508 query = query .filter (ChatMessage .created_at <= end_date )
509+ if group_id :
510+ group_users = db .query (GroupMember .user_id ).filter (GroupMember .group_id == group_id ).subquery ()
511+ query = query .filter (ChatMessage .user_id .in_ (group_users ))
456512
457513 results = query .group_by (ChatMessage .chat_id ).all ()
458514 return {row .chat_id : row .count for row in results }
@@ -461,21 +517,27 @@ def get_daily_message_counts_by_model(
461517 self ,
462518 start_date : Optional [int ] = None ,
463519 end_date : Optional [int ] = None ,
520+ group_id : Optional [str ] = None ,
464521 db : Optional [Session ] = None ,
465522 ) -> dict [str , dict [str , int ]]:
466523 """Get message counts grouped by day and model."""
467524 with get_db_context (db ) as db :
468525 from datetime import datetime , timedelta
526+ from open_webui .models .groups import GroupMember
469527
470528 query = db .query (ChatMessage .created_at , ChatMessage .model_id ).filter (
471529 ChatMessage .role == "assistant" ,
472- ChatMessage .model_id .isnot (None )
530+ ChatMessage .model_id .isnot (None ),
531+ ~ ChatMessage .user_id .like ("shared-%" ),
473532 )
474533
475534 if start_date :
476535 query = query .filter (ChatMessage .created_at >= start_date )
477536 if end_date :
478537 query = query .filter (ChatMessage .created_at <= end_date )
538+ if group_id :
539+ group_users = db .query (GroupMember .user_id ).filter (GroupMember .group_id == group_id ).subquery ()
540+ query = query .filter (ChatMessage .user_id .in_ (group_users ))
479541
480542 results = query .all ()
481543
@@ -511,7 +573,8 @@ def get_hourly_message_counts_by_model(
511573
512574 query = db .query (ChatMessage .created_at , ChatMessage .model_id ).filter (
513575 ChatMessage .role == "assistant" ,
514- ChatMessage .model_id .isnot (None )
576+ ChatMessage .model_id .isnot (None ),
577+ ~ ChatMessage .user_id .like ("shared-%" ),
515578 )
516579
517580 if start_date :
0 commit comments