import 'package:flutter/foundation.dart'; import 'debug_logger.dart'; /// Utility class for parsing and extracting reasoning/thinking content from messages class ReasoningParser { /// Parses a message and extracts reasoning content static ReasoningContent? parseReasoningContent(String content) { if (content.isEmpty) return null; if (kDebugMode) { DebugLogger.log( 'Parsing content: ${content.substring(0, content.length > 200 ? 200 : content.length)}...', ); } // Check if content contains reasoning if (!content.contains('
tag with type="reasoning" final reasoningRegex = RegExp( r']*>\s*([^<]*)\s*(.*?)\s*
', multiLine: true, dotAll: true, ); final match = reasoningRegex.firstMatch(content); if (match == null) { if (kDebugMode) { debugPrint('DEBUG: Regex did not match - checking pattern'); } // Try a more flexible regex to debug final flexRegex = RegExp( r']*type="reasoning"[^>]*>.*?', multiLine: true, dotAll: true, ); final flexMatch = flexRegex.firstMatch(content); if (flexMatch != null) { if (kDebugMode) { DebugLogger.log('Found flexible match: ${flexMatch.group(0)}'); } } else { if (kDebugMode) { DebugLogger.log('No flexible match found either'); } } return null; } if (kDebugMode) { DebugLogger.log('Regex matched successfully'); } final isDone = match.group(1) == 'true'; final duration = int.tryParse(match.group(2) ?? '0') ?? 0; final summary = match.group(3)?.trim() ?? ''; final reasoning = match.group(4)?.trim() ?? ''; if (kDebugMode) { DebugLogger.log( 'Parsed values - isDone: $isDone, duration: $duration, summary: $summary', ); DebugLogger.log('Reasoning content length: ${reasoning.length}'); } // Remove the reasoning section from the main content final mainContent = content.replaceAll(reasoningRegex, '').trim(); return ReasoningContent( reasoning: reasoning, summary: summary, duration: duration, isDone: isDone, mainContent: mainContent, originalContent: content, ); } /// Checks if a message contains reasoning content static bool hasReasoningContent(String content) { return content.contains('
identical(this, other) || other is ReasoningContent && runtimeType == other.runtimeType && reasoning == other.reasoning && summary == other.summary && duration == other.duration && isDone == other.isDone && mainContent == other.mainContent && originalContent == other.originalContent; @override int get hashCode => reasoning.hashCode ^ summary.hashCode ^ duration.hashCode ^ isDone.hashCode ^ mainContent.hashCode ^ originalContent.hashCode; String get formattedDuration => ReasoningParser.formatDuration(duration); /// Gets the cleaned reasoning text (removes leading '>') String get cleanedReasoning { // Split by lines and clean each line return reasoning .split('\n') .map((line) => line.startsWith('>') ? line.substring(1).trim() : line) .join('\n') .trim(); } }