| 
					
				 | 
			
			
				@@ -1,6 +1,7 @@ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+from datetime import datetime, timezone 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 from typing import Optional, Union 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-from sqlalchemy import or_ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+from sqlalchemy import asc, desc, or_ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 from core.app.entities.app_invoke_entities import InvokeFrom 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 from core.llm_generator.llm_generator import LLMGenerator 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -18,7 +19,8 @@ class ConversationService: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                               last_id: Optional[str], limit: int, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                               invoke_from: InvokeFrom, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                               include_ids: Optional[list] = None, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                              exclude_ids: Optional[list] = None) -> InfiniteScrollPagination: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                              exclude_ids: Optional[list] = None, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                              sort_by: str = '-updated_at') -> InfiniteScrollPagination: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         if not user: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             return InfiniteScrollPagination(data=[], limit=limit, has_more=False) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -37,28 +39,28 @@ class ConversationService: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         if exclude_ids is not None: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             base_query = base_query.filter(~Conversation.id.in_(exclude_ids)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        if last_id: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            last_conversation = base_query.filter( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                Conversation.id == last_id, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            ).first() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        # define sort fields and directions 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        sort_field, sort_direction = cls._get_sort_params(sort_by) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if last_id: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            last_conversation = base_query.filter(Conversation.id == last_id).first() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             if not last_conversation: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 raise LastConversationNotExistsError() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            conversations = base_query.filter( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                Conversation.created_at < last_conversation.created_at, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                Conversation.id != last_conversation.id 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            ).order_by(Conversation.created_at.desc()).limit(limit).all() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        else: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            conversations = base_query.order_by(Conversation.created_at.desc()).limit(limit).all() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            # build filters based on sorting 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            filter_condition = cls._build_filter_condition(sort_field, sort_direction, last_conversation) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            base_query = base_query.filter(filter_condition) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        base_query = base_query.order_by(sort_direction(getattr(Conversation, sort_field))) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        conversations = base_query.limit(limit).all() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         has_more = False 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         if len(conversations) == limit: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            current_page_first_conversation = conversations[-1] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            rest_count = base_query.filter( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                Conversation.created_at < current_page_first_conversation.created_at, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                Conversation.id != current_page_first_conversation.id 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            ).count() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            current_page_last_conversation = conversations[-1] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            rest_filter_condition = cls._build_filter_condition(sort_field, sort_direction, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                                                current_page_last_conversation, is_next_page=True) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            rest_count = base_query.filter(rest_filter_condition).count() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             if rest_count > 0: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 has_more = True 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -70,6 +72,21 @@ class ConversationService: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     @classmethod 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    def _get_sort_params(cls, sort_by: str) -> tuple[str, callable]: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if sort_by.startswith('-'): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            return sort_by[1:], desc 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return sort_by, asc 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    @classmethod 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    def _build_filter_condition(cls, sort_field: str, sort_direction: callable, reference_conversation: Conversation, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                is_next_page: bool = False): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        field_value = getattr(reference_conversation, sort_field) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if (sort_direction == desc and not is_next_page) or (sort_direction == asc and is_next_page): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            return getattr(Conversation, sort_field) < field_value 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        else: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            return getattr(Conversation, sort_field) > field_value 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    @classmethod 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     def rename(cls, app_model: App, conversation_id: str, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                user: Optional[Union[Account, EndUser]], name: str, auto_generate: bool): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         conversation = cls.get_conversation(app_model, conversation_id, user) 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -78,6 +95,7 @@ class ConversationService: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             return cls.auto_generate_name(app_model, conversation) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         else: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             conversation.name = name 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            conversation.updated_at = datetime.now(timezone.utc).replace(tzinfo=None) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             db.session.commit() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         return conversation 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -87,9 +105,9 @@ class ConversationService: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         # get conversation first message 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         message = db.session.query(Message) \ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             .filter( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                Message.app_id == app_model.id, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                Message.conversation_id == conversation.id 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            ).order_by(Message.created_at.asc()).first() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            Message.app_id == app_model.id, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            Message.conversation_id == conversation.id 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ).order_by(Message.created_at.asc()).first() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         if not message: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             raise MessageNotExistsError() 
			 |