diff --git a/lib/core/services/api_service.dart b/lib/core/services/api_service.dart index 45c59f0..014f51e 100644 --- a/lib/core/services/api_service.dart +++ b/lib/core/services/api_service.dart @@ -14,9 +14,7 @@ import '../models/chat_message.dart'; import '../auth/api_auth_interceptor.dart'; import '../validation/validation_interceptor.dart'; import '../error/api_error_interceptor.dart'; -import 'sse_parser.dart'; // Tool-call details are parsed in the UI layer to render collapsible blocks -import 'stream_recovery_service.dart'; import 'persistent_streaming_service.dart'; import '../utils/debug_logger.dart'; @@ -2784,12 +2782,6 @@ class ApiService { if (!streamController.isClosed) streamController.close(); } }(); - } else { - // Ensure we do not leak background-only identifiers into SSE path - data.remove('session_id'); - data.remove('id'); - // Use SSE streaming with proper parser (chat_id allowed) - _streamSSE(data, streamController, messageId); } return ( @@ -3032,9 +3024,8 @@ class ApiService { } } catch (_) {} } - - // SSE streaming with persistent background support - Main Implementation - void _streamSSE( + // SSE helpers removed: background task flow is the only path now. + /* void _streamSSE( Map data, StreamController streamController, String messageId, @@ -3690,6 +3681,7 @@ class ApiService { } } + */ // Legacy Socket.IO and older SSE methods removed // File upload for RAG diff --git a/lib/core/services/share_receiver_service.dart b/lib/core/services/share_receiver_service.dart index 93838ba..d7d0375 100644 --- a/lib/core/services/share_receiver_service.dart +++ b/lib/core/services/share_receiver_service.dart @@ -50,9 +50,9 @@ final shareReceiverInitializerProvider = Provider((ref) { // React when auth/model changes to process a queued share ref.listen( authNavigationStateProvider, - (_, __) => maybeProcessPending(), + (prev, next) => maybeProcessPending(), ); - ref.listen(selectedModelProvider, (_, __) => maybeProcessPending()); + ref.listen(selectedModelProvider, (prev, next) => maybeProcessPending()); // Also poll once shortly after navigation settles to ensure ChatPage is ready Future.delayed(const Duration(milliseconds: 150), () => maybeProcessPending()); diff --git a/lib/core/utils/reasoning_parser.dart b/lib/core/utils/reasoning_parser.dart index e670f57..17fa311 100644 --- a/lib/core/utils/reasoning_parser.dart +++ b/lib/core/utils/reasoning_parser.dart @@ -1,7 +1,7 @@ -/// Utility class for parsing and extracting reasoning/thinking content from messages +/// Utility class for parsing and extracting reasoning/thinking content from messages. class ReasoningParser { - /// Default tag pairs to detect raw reasoning blocks when providers don't emit
- /// This mirrors Open WebUI defaults: ..., ... + /// Default tag pairs to detect raw reasoning blocks when providers don't emit `
`. + /// This mirrors Open WebUI defaults: `...`, `...`. static const List> defaultReasoningTagPairs = >[ ['', ''], ['', ''], @@ -9,8 +9,8 @@ class ReasoningParser { /// Parses a message and extracts reasoning content /// Supports: - /// -
blocks (server-emitted) - /// - Raw tag pairs like ... or ... + /// - `
` blocks (server-emitted) + /// - Raw tag pairs like `...` or `...` /// - Optional custom tag pair override static ReasoningContent? parseReasoningContent( String content, { @@ -19,7 +19,7 @@ class ReasoningParser { }) { if (content.isEmpty) return null; - // 1) Prefer server-emitted
blocks + // 1) Prefer server-emitted `
` blocks final detailsRegex = RegExp( r']*>\s*([^<]*)<\/summary>\s*([\s\S]*?)<\/details>', multiLine: true, @@ -44,7 +44,7 @@ class ReasoningParser { ); } - // 2) Handle partially streamed
(opening present, no closing yet) + // 2) Handle partially streamed `
` (opening present, no closing yet) final openingIdx = content.indexOf('
= 0 && !content.contains('
')) { final after = content.substring(openingIdx); @@ -80,7 +80,7 @@ class ReasoningParser { for (final pair in tagPairs) { final start = RegExp.escape(pair[0]); final end = RegExp.escape(pair[1]); - final tagRegex = RegExp('($start)([\n\r\s\S]*?)($end)', multiLine: true, dotAll: true); + final tagRegex = RegExp('($start)([\s\S]*?)($end)', multiLine: true, dotAll: true); final match = tagRegex.firstMatch(content); if (match != null) { final reasoning = (match.group(2) ?? '').trim(); diff --git a/lib/core/utils/tool_calls_parser.dart b/lib/core/utils/tool_calls_parser.dart index f000733..d6e7bee 100644 --- a/lib/core/utils/tool_calls_parser.dart +++ b/lib/core/utils/tool_calls_parser.dart @@ -32,7 +32,7 @@ class ToolCallsContent { }); } -/// Utility to parse
blocks from content +/// Utility to parse `
` blocks from content. class ToolCallsParser { static String _unescapeHtml(String s) { return s @@ -135,7 +135,7 @@ class ToolCallsParser { done: done, arguments: args, result: result, - files: (files is List) ? files as List : null, + files: (files is List) ? files : null, ), ), ); @@ -230,10 +230,10 @@ class ToolCallsParser { if (value == null) return ''; try { final pretty = const JsonEncoder.withIndent(' ').convert(value); - return pretty.length > max ? pretty.substring(0, max) + '\n…' : pretty; + return pretty.length > max ? '${pretty.substring(0, max)}\n…' : pretty; } catch (_) { final raw = value.toString(); - return raw.length > max ? raw.substring(0, max) + '…' : raw; + return raw.length > max ? '${raw.substring(0, max)}…' : raw; } } } diff --git a/lib/features/chat/providers/chat_providers.dart b/lib/features/chat/providers/chat_providers.dart index 8a8fcba..c498031 100644 --- a/lib/features/chat/providers/chat_providers.dart +++ b/lib/features/chat/providers/chat_providers.dart @@ -1219,7 +1219,7 @@ Future _sendMessageInternal( if (apiSvc != null && chatId != null && chatId.isNotEmpty) { Future.microtask(() async { try { - final resp = await apiSvc.dio.get('/api/v1/chats/' + chatId); + final resp = await apiSvc.dio.get('/api/v1/chats/$chatId'); final data = resp.data as Map; String content = ''; final chatObj = data['chat'] as Map?; @@ -1305,7 +1305,7 @@ Future _sendMessageInternal( try { if (line is String) { final s = line.trim(); - DebugLogger.stream('Socket [' + channel + '] line=' + (s.length > 160 ? s.substring(0, 160) + '…' : s)); + DebugLogger.stream('Socket [$channel] line=${s.length > 160 ? '${s.substring(0, 160)}…' : s}'); if (s == '[DONE]' || s == 'DONE') { socketService.offEvent(channel); // Channel completed @@ -1350,13 +1350,13 @@ Future _sendMessageInternal( if (delta.containsKey('content')) { final c = delta['content']?.toString() ?? ''; if (c.isNotEmpty) { - DebugLogger.stream('Socket [' + channel + '] delta.content len=' + c.length.toString()); + DebugLogger.stream('Socket [$channel] delta.content len=${c.length}'); } } // Surface tool_calls status if (delta.containsKey('tool_calls')) { if (kSocketVerboseLogging) { - DebugLogger.stream('Socket [' + channel + '] delta.tool_calls detected'); + DebugLogger.stream('Socket [$channel] delta.tool_calls detected'); } final tc = delta['tool_calls']; if (tc is List) { @@ -1431,7 +1431,7 @@ Future _sendMessageInternal( // Show an executing tile immediately using provided tool info try { final name = payload['name']?.toString() ?? 'tool'; - DebugLogger.stream('Socket execute:tool name=' + name); + DebugLogger.stream('Socket execute:tool name=$name'); final status = '\n
Executing...\n
\n'; ref.read(chatMessagesProvider.notifier).appendToLastMessage(status); } catch (_) {} @@ -1447,7 +1447,7 @@ Future _sendMessageInternal( if (data == null) return; final type = data['type']; final payload = data['data']; - DebugLogger.stream('Socket channel-events: type=' + type.toString()); + DebugLogger.stream('Socket channel-events: type=$type'); // Handle generic channel progress messages if needed if (type == 'message' && payload is Map) { final content = payload['content']?.toString() ?? ''; diff --git a/lib/features/chat/widgets/assistant_message_widget.dart b/lib/features/chat/widgets/assistant_message_widget.dart index e90a0e7..72372f2 100644 --- a/lib/features/chat/widgets/assistant_message_widget.dart +++ b/lib/features/chat/widgets/assistant_message_widget.dart @@ -198,10 +198,10 @@ class _AssistantMessageWidgetState extends ConsumerState String _pretty(dynamic v, {int max = 1200}) { try { final pretty = const JsonEncoder.withIndent(' ').convert(v); - return pretty.length > max ? pretty.substring(0, max) + '\n…' : pretty; + return pretty.length > max ? '${pretty.substring(0, max)}\n…' : pretty; } catch (_) { final s = v?.toString() ?? ''; - return s.length > max ? s.substring(0, max) + '…' : s; + return s.length > max ? '${s.substring(0, max)}…' : s; } } diff --git a/lib/features/chat/widgets/modern_chat_input.dart b/lib/features/chat/widgets/modern_chat_input.dart index b2ac0a9..d6782e1 100644 --- a/lib/features/chat/widgets/modern_chat_input.dart +++ b/lib/features/chat/widgets/modern_chat_input.dart @@ -1164,11 +1164,9 @@ class _ModernChatInputState extends ConsumerState _textSub?.cancel(); _textSub = stream.listen( (text) async { - final updated = - (_baseTextAtStart.isEmpty - ? '' - : (_baseTextAtStart.trimRight() + ' ')) + - text; + final updated = _baseTextAtStart.isEmpty + ? text + : '${_baseTextAtStart.trimRight()} $text'; _controller.value = TextEditingValue( text: updated, selection: TextSelection.collapsed(offset: updated.length), diff --git a/lib/shared/services/tasks/task_queue.dart b/lib/shared/services/tasks/task_queue.dart index e740458..8da9cbc 100644 --- a/lib/shared/services/tasks/task_queue.dart +++ b/lib/shared/services/tasks/task_queue.dart @@ -25,7 +25,7 @@ class TaskQueueNotifier extends StateNotifier> { bool _processing = false; final Set _activeThreads = {}; - int _maxParallel = 2; // bounded parallelism across conversations + final int _maxParallel = 2; // bounded parallelism across conversations Future _load() async { try {