chatStream method
Sends a streaming chat request to the provider
messages
- The conversation history as a list of chat messages
tools
- Optional list of tools to use in the chat
Returns a stream of chat events
Implementation
@override
Stream<ChatStreamEvent> chatStream(
List<ChatMessage> messages, {
List<Tool>? tools,
}) async* {
try {
final requestBody = buildRequestBody(messages, tools, true);
// Optimized trace logging with condition check
if (_logger.isLoggable(Level.FINEST)) {
_logger.finest(
'$providerName stream request payload: ${jsonEncode(requestBody)}');
}
// Log request headers and body for debugging
if (_logger.isLoggable(Level.FINE)) {
_logger.fine('$providerName stream request: POST $chatEndpoint');
_logger.fine(
'$providerName stream request headers: ${_dio.options.headers}');
}
if (_logger.isLoggable(Level.FINE)) {
_logger.fine(
'$providerName stream request body: ${jsonEncode(requestBody)}');
}
final response = await _dio.post(
chatEndpoint,
data: requestBody,
options: Options(responseType: ResponseType.stream),
);
_logger.fine('$providerName stream HTTP status: ${response.statusCode}');
if (response.statusCode != 200) {
yield ErrorEvent(
ProviderError(
'$providerName API returned status ${response.statusCode}'),
);
return;
}
final stream = response.data as ResponseBody;
await for (final chunk in stream.stream.map(utf8.decode)) {
try {
// Debug logging for Google provider
if (providerName == 'Google') {
_logger.fine('$providerName raw stream chunk: $chunk');
}
final events = parseStreamEvents(chunk);
for (final event in events) {
yield event;
}
} catch (e) {
// Skip malformed chunks but log them
_logger.warning('Failed to parse stream chunk: $e');
_logger.warning('Raw chunk content: $chunk');
continue;
}
}
} on DioException catch (e) {
yield ErrorEvent(handleDioError(e));
} catch (e) {
yield ErrorEvent(GenericError('Unexpected error: $e'));
}
}