diff --git a/lib/core/providers/app_providers.dart b/lib/core/providers/app_providers.dart index 8c34f40..8ecd2d3 100644 --- a/lib/core/providers/app_providers.dart +++ b/lib/core/providers/app_providers.dart @@ -275,7 +275,7 @@ final conversationsProvider = FutureProvider>((ref) async { try { foundation.debugPrint('DEBUG: Fetching conversations from OpenWebUI API...'); - final conversations = await api.getConversations(limit: 50); + final conversations = await api.getConversations(); // Fetch all conversations foundation.debugPrint( 'DEBUG: Successfully fetched ${conversations.length} conversations', ); @@ -328,9 +328,17 @@ final conversationsProvider = FutureProvider>((ref) async { } foundation.debugPrint('DEBUG: Final conversation count: ${updatedConversations.length}'); + + // Sort conversations by updatedAt in descending order (most recent first) + updatedConversations.sort((a, b) => b.updatedAt.compareTo(a.updatedAt)); + foundation.debugPrint('DEBUG: Sorted conversations by updatedAt (most recent first)'); + return updatedConversations; } catch (e) { foundation.debugPrint('DEBUG: Failed to fetch folder information: $e'); + // Sort conversations even when folder fetch fails + conversations.sort((a, b) => b.updatedAt.compareTo(a.updatedAt)); + foundation.debugPrint('DEBUG: Sorted conversations by updatedAt (fallback case)'); return conversations; // Return original conversations if folder fetch fails } } catch (e, stackTrace) { diff --git a/lib/core/services/api_service.dart b/lib/core/services/api_service.dart index d944c93..dd3d9b1 100644 --- a/lib/core/services/api_service.dart +++ b/lib/core/services/api_service.dart @@ -325,30 +325,65 @@ class ApiService { debugPrint('DEBUG: Making request to ${serverConfig.url}/api/v1/chats/'); debugPrint('DEBUG: Auth token present: ${authToken != null}'); - // Fetch regular, pinned, and archived conversations - final regularResponse = await _dio.get( - '/api/v1/chats/', - queryParameters: { - if (limit != null && limit > 0) - 'page': ((skip ?? 0) / limit) - .floor(), // OpenWebUI uses page-based pagination with proper bounds checking - }, - ); + List allRegularChats = []; + + if (limit == null) { + // Fetch all conversations using pagination + debugPrint('DEBUG: Fetching ALL conversations using pagination'); + int currentPage = 0; + + while (true) { + debugPrint('DEBUG: Fetching page $currentPage'); + final response = await _dio.get( + '/api/v1/chats/', + queryParameters: {'page': currentPage}, + ); + + if (response.data is! List) { + throw Exception('Expected array of chats, got ${response.data.runtimeType}'); + } + + final pageChats = response.data as List; + debugPrint('DEBUG: Page $currentPage returned ${pageChats.length} conversations'); + + if (pageChats.isEmpty) { + debugPrint('DEBUG: No more conversations, stopping pagination'); + break; + } + + allRegularChats.addAll(pageChats); + currentPage++; + + // Safety break to avoid infinite loops (adjust as needed) + if (currentPage > 100) { + debugPrint('WARNING: Reached maximum page limit (100), stopping pagination'); + break; + } + } + + debugPrint('DEBUG: Fetched total of ${allRegularChats.length} conversations across $currentPage pages'); + } else { + // Original single page fetch + final regularResponse = await _dio.get( + '/api/v1/chats/', + queryParameters: { + if (limit > 0) + 'page': ((skip ?? 0) / limit).floor(), + }, + ); + + if (regularResponse.data is! List) { + throw Exception('Expected array of chats, got ${regularResponse.data.runtimeType}'); + } + + allRegularChats = regularResponse.data as List; + } final pinnedResponse = await _dio.get('/api/v1/chats/pinned'); final archivedResponse = await _dio.get('/api/v1/chats/all/archived'); - debugPrint('DEBUG: Regular response status: ${regularResponse.statusCode}'); debugPrint('DEBUG: Pinned response status: ${pinnedResponse.statusCode}'); - debugPrint( - 'DEBUG: Archived response status: ${archivedResponse.statusCode}', - ); - - if (regularResponse.data is! List) { - throw Exception( - 'Expected array of chats, got ${regularResponse.data.runtimeType}', - ); - } + debugPrint('DEBUG: Archived response status: ${archivedResponse.statusCode}'); if (pinnedResponse.data is! List) { throw Exception( @@ -362,7 +397,7 @@ class ApiService { ); } - final regularChatList = regularResponse.data as List; + final regularChatList = allRegularChats; final pinnedChatList = pinnedResponse.data as List; final archivedChatList = archivedResponse.data as List; @@ -548,6 +583,16 @@ class ApiService { // Parse messages from the 'chat' object or top-level messages final chatObject = chatData['chat'] as Map?; final messages = []; + + // Extract model from chat.models array + String? model; + if (chatObject != null && chatObject['models'] != null) { + final models = chatObject['models'] as List?; + if (models != null && models.isNotEmpty) { + model = models.first as String; + debugPrint('DEBUG: Extracted model from chat.models: $model'); + } + } // Try multiple locations for messages - prefer list format to avoid duplication List? messagesList; @@ -610,6 +655,7 @@ class ApiService { title: title, createdAt: createdAt, updatedAt: updatedAt, + model: model, pinned: pinned, archived: archived, shareId: shareId, diff --git a/lib/features/chat/providers/chat_providers.dart b/lib/features/chat/providers/chat_providers.dart index a32027d..a0cfb24 100644 --- a/lib/features/chat/providers/chat_providers.dart +++ b/lib/features/chat/providers/chat_providers.dart @@ -53,6 +53,9 @@ class ChatMessagesNotifier extends StateNotifier> { 'DEBUG: Loading ${next.messages.length} messages for conversation ${next.id}', ); state = next.messages; + + // Update selected model if conversation has a different model + _updateModelForConversation(next); } else { debugPrint('DEBUG: Clearing messages - no active conversation'); state = []; @@ -71,6 +74,57 @@ class ChatMessagesNotifier extends StateNotifier> { _messageStream = null; } + Future _updateModelForConversation(Conversation conversation) async { + debugPrint('DEBUG: _updateModelForConversation called for conversation ${conversation.id}'); + + // Check if conversation has a model specified + if (conversation.model == null || conversation.model!.isEmpty) { + debugPrint('DEBUG: Conversation has no model specified'); + return; + } + + debugPrint('DEBUG: Conversation model: ${conversation.model}'); + + final currentSelectedModel = _ref.read(selectedModelProvider); + + // If the conversation's model is different from the currently selected one + if (currentSelectedModel?.id != conversation.model) { + debugPrint( + 'DEBUG: Conversation model (${conversation.model}) differs from selected model (${currentSelectedModel?.id})', + ); + + // Get available models to find the matching one + try { + final models = await _ref.read(modelsProvider.future); + debugPrint('DEBUG: Available models count: ${models.length}'); + + if (models.isEmpty) { + debugPrint('DEBUG: No models available, cannot update selected model'); + return; + } + + // Look for exact match first + final conversationModel = models.where( + (model) => model.id == conversation.model, + ).firstOrNull; + + if (conversationModel != null) { + // Update the selected model + _ref.read(selectedModelProvider.notifier).state = conversationModel; + debugPrint( + 'DEBUG: Updated selected model to ${conversationModel.name} (${conversationModel.id}) for conversation ${conversation.id}', + ); + } else { + debugPrint( + 'DEBUG: Conversation model ${conversation.model} not found in available models: ${models.map((m) => m.id).join(', ')}', + ); + } + } catch (e) { + debugPrint('DEBUG: Failed to update model for conversation: $e'); + } + } + } + void setMessageStream(StreamSubscription stream) { _cancelMessageStream(); _messageStream = stream; @@ -526,6 +580,9 @@ Future _sendMessageInternal( debugPrint( 'DEBUG: Server conversation ID: ${serverConversation.id}, Title: ${serverConversation.title}', ); + + // Invalidate conversations provider to refresh the list + ref.invalidate(conversationsProvider); } catch (e) { debugPrint( 'DEBUG: Failed to create conversation on server, using local: $e',