feat: task based send flow
This commit is contained in:
@@ -13,6 +13,7 @@ import '../../../core/utils/stream_chunker.dart';
|
||||
import '../../../core/services/persistent_streaming_service.dart';
|
||||
import '../../../core/utils/debug_logger.dart';
|
||||
import '../services/reviewer_mode_service.dart';
|
||||
import '../../../shared/services/tasks/task_queue.dart';
|
||||
|
||||
const bool kSocketVerboseLogging = false;
|
||||
|
||||
@@ -1044,6 +1045,11 @@ Future<void> _sendMessageInternal(
|
||||
'follow_up_generation': true,
|
||||
};
|
||||
|
||||
// Determine if we need background task flow (tools/tool servers)
|
||||
final bool isBackgroundToolsFlowPre =
|
||||
(toolIdsForApi != null && toolIdsForApi.isNotEmpty) ||
|
||||
(toolServers != null && toolServers.isNotEmpty);
|
||||
|
||||
final response = await api.sendMessage(
|
||||
messages: conversationMessages,
|
||||
model: selectedModel.id,
|
||||
@@ -1054,7 +1060,9 @@ Future<void> _sendMessageInternal(
|
||||
// handled via pre-stream client-side request above
|
||||
enableImageGeneration: false,
|
||||
modelItem: modelItem,
|
||||
sessionIdOverride: socketSessionId,
|
||||
// Only pass a session when we truly want task-based dynamic-channel
|
||||
// behavior; for pure text flows prefer polling (if background mode).
|
||||
sessionIdOverride: isBackgroundToolsFlowPre ? socketSessionId : null,
|
||||
toolServers: toolServers,
|
||||
backgroundTasks: bgTasks,
|
||||
);
|
||||
@@ -1078,9 +1086,7 @@ Future<void> _sendMessageInternal(
|
||||
// Background-tools flow (tools/tool servers) relies on socket/dynamic channel for
|
||||
// streaming content. Allow socket TEXT in that mode. For pure SSE flows, suppress
|
||||
// socket TEXT to avoid duplicates (still surface tool_call status).
|
||||
final bool isBackgroundToolsFlow =
|
||||
(toolIdsForApi != null && toolIdsForApi.isNotEmpty) ||
|
||||
(toolServers != null && toolServers.isNotEmpty);
|
||||
final bool isBackgroundToolsFlow = isBackgroundToolsFlowPre;
|
||||
bool suppressSocketContent = !isBackgroundToolsFlow; // allow socket text for tools
|
||||
bool usingDynamicChannel = false; // set true when server provides a channel
|
||||
if (socketService != null) {
|
||||
@@ -2555,6 +2561,15 @@ final stopGenerationProvider = Provider<void Function()>((ref) {
|
||||
}
|
||||
} catch (_) {}
|
||||
}());
|
||||
|
||||
// Also cancel local queue tasks for this conversation
|
||||
try {
|
||||
// Fire-and-forget local queue cancellation
|
||||
// ignore: unawaited_futures
|
||||
ref
|
||||
.read(taskQueueProvider.notifier)
|
||||
.cancelByConversation(activeConv.id);
|
||||
} catch (_) {}
|
||||
}
|
||||
} catch (_) {}
|
||||
|
||||
|
||||
@@ -33,6 +33,7 @@ import '../../onboarding/views/onboarding_sheet.dart';
|
||||
import '../../../shared/widgets/sheet_handle.dart';
|
||||
import '../../../shared/widgets/conduit_components.dart';
|
||||
import '../../../core/services/settings_service.dart';
|
||||
import '../../../shared/services/tasks/task_queue.dart';
|
||||
// Removed unused PlatformUtils import
|
||||
import '../../../core/services/platform_service.dart' as ps;
|
||||
import 'package:flutter/gestures.dart' show DragStartBehavior;
|
||||
@@ -280,33 +281,30 @@ class _ChatPageState extends ConsumerState<ChatPage> {
|
||||
}
|
||||
|
||||
try {
|
||||
// Get attached files and use uploadedFileIds when sendMessage is updated to accept file IDs
|
||||
// Get attached files and collect uploaded file IDs (including data URLs for images)
|
||||
final attachedFiles = ref.read(attachedFilesProvider);
|
||||
|
||||
final uploadedFileIds = attachedFiles
|
||||
.where(
|
||||
(file) =>
|
||||
file.status == FileUploadStatus.completed &&
|
||||
file.fileId != null,
|
||||
)
|
||||
.where((file) =>
|
||||
file.status == FileUploadStatus.completed && file.fileId != null)
|
||||
.map((file) => file.fileId!)
|
||||
.toList();
|
||||
|
||||
// Get selected tools
|
||||
final toolIds = ref.read(selectedToolIdsProvider);
|
||||
|
||||
// Send message with file attachments and tools using existing provider logic
|
||||
await sendMessage(
|
||||
ref,
|
||||
text,
|
||||
uploadedFileIds.isNotEmpty ? uploadedFileIds : null,
|
||||
toolIds.isNotEmpty ? toolIds : null,
|
||||
);
|
||||
// Enqueue task-based send to unify flow across text, images, and tools
|
||||
final activeConv = ref.read(activeConversationProvider);
|
||||
await ref.read(taskQueueProvider.notifier).enqueueSendText(
|
||||
conversationId: activeConv?.id,
|
||||
text: text,
|
||||
attachments: uploadedFileIds.isNotEmpty ? uploadedFileIds : null,
|
||||
toolIds: toolIds.isNotEmpty ? toolIds : null,
|
||||
);
|
||||
|
||||
// Clear attachments after successful send
|
||||
ref.read(attachedFilesProvider.notifier).clearAll();
|
||||
|
||||
// Scroll to bottom after sending message (only if user was near bottom)
|
||||
// Scroll to bottom after enqueuing (only if user was near bottom)
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
if (_scrollController.hasClients) {
|
||||
final maxScroll = _scrollController.position.maxScrollExtent;
|
||||
@@ -879,7 +877,11 @@ class _ChatPageState extends ConsumerState<ChatPage> {
|
||||
// Send the edited message
|
||||
final selectedModel = ref.read(selectedModelProvider);
|
||||
if (selectedModel != null) {
|
||||
await sendMessage(ref, result, null);
|
||||
final activeConv = ref.read(activeConversationProvider);
|
||||
await ref.read(taskQueueProvider.notifier).enqueueSendText(
|
||||
conversationId: activeConv?.id,
|
||||
text: result,
|
||||
);
|
||||
|
||||
if (mounted) {}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user