refactor: cleanup
This commit is contained in:
@@ -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
|
||||||
|
|||||||
@@ -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());
|
||||||
|
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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() ?? '';
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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),
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
Reference in New Issue
Block a user