refactor: cleanup

This commit is contained in:
cogwheel0
2025-09-02 00:04:21 +05:30
parent 1b65743b06
commit 66935d1b0f
8 changed files with 29 additions and 39 deletions

View File

@@ -14,9 +14,7 @@ import '../models/chat_message.dart';
import '../auth/api_auth_interceptor.dart'; import '../auth/api_auth_interceptor.dart';
import '../validation/validation_interceptor.dart'; import '../validation/validation_interceptor.dart';
import '../error/api_error_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 // Tool-call details are parsed in the UI layer to render collapsible blocks
import 'stream_recovery_service.dart';
import 'persistent_streaming_service.dart'; import 'persistent_streaming_service.dart';
import '../utils/debug_logger.dart'; import '../utils/debug_logger.dart';
@@ -2784,12 +2782,6 @@ class ApiService {
if (!streamController.isClosed) streamController.close(); 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 ( return (
@@ -3032,9 +3024,8 @@ class ApiService {
} }
} catch (_) {} } catch (_) {}
} }
// SSE helpers removed: background task flow is the only path now.
// SSE streaming with persistent background support - Main Implementation /* void _streamSSE(
void _streamSSE(
Map<String, dynamic> data, Map<String, dynamic> data,
StreamController<String> streamController, StreamController<String> streamController,
String messageId, String messageId,
@@ -3690,6 +3681,7 @@ class ApiService {
} }
} }
*/
// Legacy Socket.IO and older SSE methods removed // Legacy Socket.IO and older SSE methods removed
// File upload for RAG // File upload for RAG

View File

@@ -50,9 +50,9 @@ final shareReceiverInitializerProvider = Provider<void>((ref) {
// React when auth/model changes to process a queued share // React when auth/model changes to process a queued share
ref.listen<AuthNavigationState>( ref.listen<AuthNavigationState>(
authNavigationStateProvider, 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 // Also poll once shortly after navigation settles to ensure ChatPage is ready
Future.delayed(const Duration(milliseconds: 150), () => maybeProcessPending()); Future.delayed(const Duration(milliseconds: 150), () => maybeProcessPending());

View File

@@ -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 { class ReasoningParser {
/// Default tag pairs to detect raw reasoning blocks when providers don't emit <details> /// Default tag pairs to detect raw reasoning blocks when providers don't emit `<details>`.
/// This mirrors Open WebUI defaults: <think>...</think>, <reasoning>...</reasoning> /// This mirrors Open WebUI defaults: `<think>...</think>`, `<reasoning>...</reasoning>`.
static const List<List<String>> defaultReasoningTagPairs = <List<String>>[ static const List<List<String>> defaultReasoningTagPairs = <List<String>>[
['<think>', '</think>'], ['<think>', '</think>'],
['<reasoning>', '</reasoning>'], ['<reasoning>', '</reasoning>'],
@@ -9,8 +9,8 @@ class ReasoningParser {
/// Parses a message and extracts reasoning content /// Parses a message and extracts reasoning content
/// Supports: /// Supports:
/// - <details type="reasoning" ...> blocks (server-emitted) /// - `<details type="reasoning" ...>` blocks (server-emitted)
/// - Raw tag pairs like <think>...</think> or <reasoning>...</reasoning> /// - Raw tag pairs like `<think>...</think>` or `<reasoning>...</reasoning>`
/// - Optional custom tag pair override /// - Optional custom tag pair override
static ReasoningContent? parseReasoningContent( static ReasoningContent? parseReasoningContent(
String content, { String content, {
@@ -19,7 +19,7 @@ class ReasoningParser {
}) { }) {
if (content.isEmpty) return null; if (content.isEmpty) return null;
// 1) Prefer server-emitted <details type="reasoning"> blocks // 1) Prefer server-emitted `<details type="reasoning">` blocks
final detailsRegex = RegExp( final detailsRegex = RegExp(
r'<details\s+type="reasoning"(?:\s+done="(true|false)")?(?:\s+duration="(\d+)")?[^>]*>\s*<summary>([^<]*)<\/summary>\s*([\s\S]*?)<\/details>', r'<details\s+type="reasoning"(?:\s+done="(true|false)")?(?:\s+duration="(\d+)")?[^>]*>\s*<summary>([^<]*)<\/summary>\s*([\s\S]*?)<\/details>',
multiLine: true, multiLine: true,
@@ -44,7 +44,7 @@ class ReasoningParser {
); );
} }
// 2) Handle partially streamed <details> (opening present, no closing yet) // 2) Handle partially streamed `<details>` (opening present, no closing yet)
final openingIdx = content.indexOf('<details type="reasoning"'); final openingIdx = content.indexOf('<details type="reasoning"');
if (openingIdx >= 0 && !content.contains('</details>')) { if (openingIdx >= 0 && !content.contains('</details>')) {
final after = content.substring(openingIdx); final after = content.substring(openingIdx);
@@ -80,7 +80,7 @@ class ReasoningParser {
for (final pair in tagPairs) { for (final pair in tagPairs) {
final start = RegExp.escape(pair[0]); final start = RegExp.escape(pair[0]);
final end = RegExp.escape(pair[1]); 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); final match = tagRegex.firstMatch(content);
if (match != null) { if (match != null) {
final reasoning = (match.group(2) ?? '').trim(); final reasoning = (match.group(2) ?? '').trim();

View File

@@ -32,7 +32,7 @@ class ToolCallsContent {
}); });
} }
/// Utility to parse <details type="tool_calls"> blocks from content /// Utility to parse `<details type="tool_calls">` blocks from content.
class ToolCallsParser { class ToolCallsParser {
static String _unescapeHtml(String s) { static String _unescapeHtml(String s) {
return s return s
@@ -135,7 +135,7 @@ class ToolCallsParser {
done: done, done: done,
arguments: args, arguments: args,
result: result, 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 ''; if (value == null) return '';
try { try {
final pretty = const JsonEncoder.withIndent(' ').convert(value); 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 (_) { } catch (_) {
final raw = value.toString(); final raw = value.toString();
return raw.length > max ? raw.substring(0, max) + '' : raw; return raw.length > max ? '${raw.substring(0, max)}' : raw;
} }
} }
} }

View File

@@ -1219,7 +1219,7 @@ Future<void> _sendMessageInternal(
if (apiSvc != null && chatId != null && chatId.isNotEmpty) { if (apiSvc != null && chatId != null && chatId.isNotEmpty) {
Future.microtask(() async { Future.microtask(() async {
try { 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, dynamic>; final data = resp.data as Map<String, dynamic>;
String content = ''; String content = '';
final chatObj = data['chat'] as Map<String, dynamic>?; final chatObj = data['chat'] as Map<String, dynamic>?;
@@ -1305,7 +1305,7 @@ Future<void> _sendMessageInternal(
try { try {
if (line is String) { if (line is String) {
final s = line.trim(); 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') { if (s == '[DONE]' || s == 'DONE') {
socketService.offEvent(channel); socketService.offEvent(channel);
// Channel completed // Channel completed
@@ -1350,13 +1350,13 @@ Future<void> _sendMessageInternal(
if (delta.containsKey('content')) { if (delta.containsKey('content')) {
final c = delta['content']?.toString() ?? ''; final c = delta['content']?.toString() ?? '';
if (c.isNotEmpty) { 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 // Surface tool_calls status
if (delta.containsKey('tool_calls')) { if (delta.containsKey('tool_calls')) {
if (kSocketVerboseLogging) { if (kSocketVerboseLogging) {
DebugLogger.stream('Socket [' + channel + '] delta.tool_calls detected'); DebugLogger.stream('Socket [$channel] delta.tool_calls detected');
} }
final tc = delta['tool_calls']; final tc = delta['tool_calls'];
if (tc is List) { if (tc is List) {
@@ -1431,7 +1431,7 @@ Future<void> _sendMessageInternal(
// Show an executing tile immediately using provided tool info // Show an executing tile immediately using provided tool info
try { try {
final name = payload['name']?.toString() ?? 'tool'; final name = payload['name']?.toString() ?? 'tool';
DebugLogger.stream('Socket execute:tool name=' + name); DebugLogger.stream('Socket execute:tool name=$name');
final status = '\n<details type="tool_calls" done="false" name="$name"><summary>Executing...</summary>\n</details>\n'; final status = '\n<details type="tool_calls" done="false" name="$name"><summary>Executing...</summary>\n</details>\n';
ref.read(chatMessagesProvider.notifier).appendToLastMessage(status); ref.read(chatMessagesProvider.notifier).appendToLastMessage(status);
} catch (_) {} } catch (_) {}
@@ -1447,7 +1447,7 @@ Future<void> _sendMessageInternal(
if (data == null) return; if (data == null) return;
final type = data['type']; final type = data['type'];
final payload = data['data']; 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 // Handle generic channel progress messages if needed
if (type == 'message' && payload is Map) { if (type == 'message' && payload is Map) {
final content = payload['content']?.toString() ?? ''; final content = payload['content']?.toString() ?? '';

View File

@@ -198,10 +198,10 @@ class _AssistantMessageWidgetState extends ConsumerState<AssistantMessageWidget>
String _pretty(dynamic v, {int max = 1200}) { String _pretty(dynamic v, {int max = 1200}) {
try { try {
final pretty = const JsonEncoder.withIndent(' ').convert(v); 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 (_) { } catch (_) {
final s = v?.toString() ?? ''; final s = v?.toString() ?? '';
return s.length > max ? s.substring(0, max) + '' : s; return s.length > max ? '${s.substring(0, max)}' : s;
} }
} }

View File

@@ -1164,11 +1164,9 @@ class _ModernChatInputState extends ConsumerState<ModernChatInput>
_textSub?.cancel(); _textSub?.cancel();
_textSub = stream.listen( _textSub = stream.listen(
(text) async { (text) async {
final updated = final updated = _baseTextAtStart.isEmpty
(_baseTextAtStart.isEmpty ? text
? '' : '${_baseTextAtStart.trimRight()} $text';
: (_baseTextAtStart.trimRight() + ' ')) +
text;
_controller.value = TextEditingValue( _controller.value = TextEditingValue(
text: updated, text: updated,
selection: TextSelection.collapsed(offset: updated.length), selection: TextSelection.collapsed(offset: updated.length),

View File

@@ -25,7 +25,7 @@ class TaskQueueNotifier extends StateNotifier<List<OutboundTask>> {
bool _processing = false; bool _processing = false;
final Set<String> _activeThreads = <String>{}; final Set<String> _activeThreads = <String>{};
int _maxParallel = 2; // bounded parallelism across conversations final int _maxParallel = 2; // bounded parallelism across conversations
Future<void> _load() async { Future<void> _load() async {
try { try {