- Added `markdown` dependency version `^7.2.1` in `pubspec.yaml`. - Updated `pubspec.lock` to reflect the direct dependency change. - Refactored `streaming_helper.dart` to utilize `StreamingResponseController` for better stream management. - Enhanced `ChatMessagesNotifier` to handle message streams with improved formatting and error handling. - Updated `StreamingMarkdownWidget` to streamline markdown rendering and support new configurations.
96 lines
2.6 KiB
Dart
96 lines
2.6 KiB
Dart
import 'dart:async';
|
|
|
|
import '../utils/debug_logger.dart';
|
|
|
|
/// Signature for callbacks that receive streaming text updates.
|
|
typedef StreamingChunkCallback = void Function(String chunk);
|
|
|
|
/// Signature for callbacks invoked when a streaming session finishes.
|
|
typedef StreamingCompletionCallback = void Function();
|
|
|
|
/// Signature for callbacks invoked when a streaming session encounters an
|
|
/// error.
|
|
typedef StreamingErrorCallback =
|
|
void Function(Object error, StackTrace stackTrace);
|
|
|
|
/// A lightweight controller that manages the lifecycle of a streamed response.
|
|
///
|
|
/// This wraps a [StreamSubscription], normalises error handling, and exposes
|
|
/// a unified cancel method so UI layers can stop streaming without having to
|
|
/// know the underlying transport (SSE, polling, etc.).
|
|
class StreamingResponseController {
|
|
StreamingResponseController({
|
|
required Stream<String> stream,
|
|
required StreamingChunkCallback onChunk,
|
|
required StreamingCompletionCallback onComplete,
|
|
required StreamingErrorCallback onError,
|
|
bool cancelOnError = true,
|
|
}) : _onChunk = onChunk,
|
|
_onComplete = onComplete,
|
|
_onError = onError {
|
|
_subscription = stream.listen(
|
|
_handleChunk,
|
|
cancelOnError: cancelOnError,
|
|
onDone: _handleCompleted,
|
|
onError: _handleError,
|
|
);
|
|
}
|
|
|
|
final StreamingChunkCallback _onChunk;
|
|
final StreamingCompletionCallback _onComplete;
|
|
final StreamingErrorCallback _onError;
|
|
|
|
StreamSubscription<String>? _subscription;
|
|
bool _isCancelled = false;
|
|
|
|
/// Whether the underlying stream subscription is still active.
|
|
bool get isActive => _subscription != null && !_isCancelled;
|
|
|
|
void _handleChunk(String chunk) {
|
|
if (_isCancelled) {
|
|
return;
|
|
}
|
|
try {
|
|
_onChunk(chunk);
|
|
} catch (err, stackTrace) {
|
|
DebugLogger.error(
|
|
'streaming-chunk-handler-failed',
|
|
scope: 'streaming/controller',
|
|
error: err,
|
|
);
|
|
_handleError(err, stackTrace);
|
|
}
|
|
}
|
|
|
|
void _handleCompleted() {
|
|
if (_isCancelled) {
|
|
return;
|
|
}
|
|
_subscription = null;
|
|
try {
|
|
_onComplete();
|
|
} catch (err, stackTrace) {
|
|
_handleError(err, stackTrace);
|
|
}
|
|
}
|
|
|
|
void _handleError(Object error, StackTrace stackTrace) {
|
|
if (_isCancelled) {
|
|
return;
|
|
}
|
|
_subscription = null;
|
|
_onError(error, stackTrace);
|
|
}
|
|
|
|
/// Cancels the underlying stream subscription.
|
|
Future<void> cancel() async {
|
|
if (_isCancelled) {
|
|
return;
|
|
}
|
|
_isCancelled = true;
|
|
final subscription = _subscription;
|
|
_subscription = null;
|
|
await subscription?.cancel();
|
|
}
|
|
}
|