refactor: create preseed helper
This commit is contained in:
@@ -293,6 +293,62 @@ class ChatMessagesNotifier extends StateNotifier<List<ChatMessage>> {
|
||||
}
|
||||
}
|
||||
|
||||
// Pre-seed an assistant skeleton message (with a given id or a new one),
|
||||
// persist it to the server to keep the chain correct, and return the id.
|
||||
Future<String> _preseedAssistantAndPersist(
|
||||
dynamic ref, {
|
||||
String? existingAssistantId,
|
||||
required String modelId,
|
||||
}) async {
|
||||
final api = ref.read(apiServiceProvider);
|
||||
final activeConv = ref.read(activeConversationProvider);
|
||||
|
||||
// Choose id: reuse existing if provided, else create new
|
||||
final String assistantMessageId =
|
||||
(existingAssistantId != null && existingAssistantId.isNotEmpty)
|
||||
? existingAssistantId
|
||||
: const Uuid().v4();
|
||||
|
||||
// If the message with this id doesn't exist locally, add a placeholder
|
||||
final msgs = ref.read(chatMessagesProvider);
|
||||
final exists = msgs.any((m) => m.id == assistantMessageId);
|
||||
if (!exists) {
|
||||
final placeholder = ChatMessage(
|
||||
id: assistantMessageId,
|
||||
role: 'assistant',
|
||||
content: '',
|
||||
timestamp: DateTime.now(),
|
||||
model: modelId,
|
||||
isStreaming: true,
|
||||
);
|
||||
ref.read(chatMessagesProvider.notifier).addMessage(placeholder);
|
||||
} else {
|
||||
// If it exists and is the last assistant, ensure we mark it streaming
|
||||
try {
|
||||
final last = msgs.isNotEmpty ? msgs.last : null;
|
||||
if (last != null && last.id == assistantMessageId && last.role == 'assistant' && !last.isStreaming) {
|
||||
ref.read(chatMessagesProvider.notifier).updateLastMessageWithFunction(
|
||||
(m) => m.copyWith(isStreaming: true),
|
||||
);
|
||||
}
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
// Persist the skeleton to the server so the web client sees a correct chain
|
||||
try {
|
||||
if (api != null && activeConv != null) {
|
||||
final current = ref.read(chatMessagesProvider);
|
||||
await api.updateConversationWithMessages(
|
||||
activeConv.id,
|
||||
current,
|
||||
model: modelId,
|
||||
);
|
||||
}
|
||||
} catch (_) {}
|
||||
|
||||
return assistantMessageId;
|
||||
}
|
||||
|
||||
// Start a new chat (unified function for both "New Chat" button and home screen)
|
||||
void startNewChat(dynamic ref) {
|
||||
// Clear active conversation
|
||||
@@ -473,25 +529,11 @@ Future<void> regenerateMessage(
|
||||
}
|
||||
}
|
||||
|
||||
// Pre-seed assistant skeleton
|
||||
final String assistantMessageId = const Uuid().v4();
|
||||
final assistantMessage = ChatMessage(
|
||||
id: assistantMessageId,
|
||||
role: 'assistant',
|
||||
content: '',
|
||||
timestamp: DateTime.now(),
|
||||
model: selectedModel.id,
|
||||
isStreaming: true,
|
||||
// Pre-seed assistant skeleton and persist chain
|
||||
final String assistantMessageId = await _preseedAssistantAndPersist(
|
||||
ref,
|
||||
modelId: selectedModel.id,
|
||||
);
|
||||
ref.read(chatMessagesProvider.notifier).addMessage(assistantMessage);
|
||||
try {
|
||||
final msgsForSeed = ref.read(chatMessagesProvider);
|
||||
await api!.updateConversationWithMessages(
|
||||
activeConversation.id,
|
||||
msgsForSeed,
|
||||
model: selectedModel.id,
|
||||
);
|
||||
} catch (_) {}
|
||||
|
||||
// Stream response via background task (socket/dynamic channel or polling)
|
||||
final response = api!.sendMessage(
|
||||
|
||||
@@ -1544,42 +1544,7 @@ class _ChatPageState extends ConsumerState<ChatPage> {
|
||||
); // ErrorBoundary
|
||||
}
|
||||
|
||||
Future<void> _saveConversationBeforeLeaving(WidgetRef ref) async {
|
||||
try {
|
||||
final api = ref.read(apiServiceProvider);
|
||||
final messages = ref.read(chatMessagesProvider);
|
||||
final activeConversation = ref.read(activeConversationProvider);
|
||||
final selectedModel = ref.read(selectedModelProvider);
|
||||
|
||||
if (api == null || messages.isEmpty || activeConversation == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove trailing assistant message only if it has no text and no files
|
||||
final lastMessage = messages.isNotEmpty ? messages.last : null;
|
||||
if (lastMessage != null &&
|
||||
lastMessage.role == 'assistant' &&
|
||||
lastMessage.content.trim().isEmpty &&
|
||||
(lastMessage.files == null || lastMessage.files!.isEmpty) &&
|
||||
(lastMessage.attachmentIds == null ||
|
||||
lastMessage.attachmentIds!.isEmpty)) {
|
||||
messages.removeLast();
|
||||
if (messages.isEmpty) return;
|
||||
}
|
||||
|
||||
// Update the existing conversation with all messages
|
||||
await api.updateConversationWithMessages(
|
||||
activeConversation.id,
|
||||
messages,
|
||||
model: selectedModel?.id,
|
||||
);
|
||||
|
||||
debugPrint('DEBUG: Conversation saved before leaving');
|
||||
} catch (e) {
|
||||
debugPrint('DEBUG: Failed to save conversation before leaving: $e');
|
||||
// Don't block navigation even if save fails
|
||||
}
|
||||
}
|
||||
// Removed legacy save-before-leave hook; server manages chat state via background pipeline.
|
||||
|
||||
void _showModelDropdown(
|
||||
BuildContext context,
|
||||
|
||||
Reference in New Issue
Block a user