Files
MaliangAINovalWriter/AINoval/lib/blocs/universal_ai/universal_ai_bloc.dart
2025-09-10 00:07:52 +08:00

250 lines
8.3 KiB
Dart
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import 'dart:async';
import 'package:bloc/bloc.dart';
import 'package:ainoval/services/api_service/repositories/universal_ai_repository.dart';
import 'package:ainoval/models/ai_request_models.dart';
import 'package:ainoval/utils/logger.dart';
import 'universal_ai_event.dart';
import 'universal_ai_state.dart';
/// 通用AI请求BLoC
class UniversalAIBloc extends Bloc<UniversalAIEvent, UniversalAIState> {
final UniversalAIRepository _repository;
StreamSubscription? _streamSubscription;
UniversalAIBloc({
required UniversalAIRepository repository,
}) : _repository = repository,
super(const UniversalAIInitial()) {
on<SendAIRequestEvent>(_onSendAIRequest);
on<SendAIStreamRequestEvent>(_onSendAIStreamRequest);
on<PreviewAIRequestEvent>(_onPreviewAIRequest);
on<EstimateCostEvent>(_onEstimateCost);
on<StopStreamRequestEvent>(_onStopStreamRequest);
on<ClearResponseEvent>(_onClearResponse);
on<ResetStateEvent>(_onResetState);
}
/// 处理发送AI请求事件非流式
Future<void> _onSendAIRequest(
SendAIRequestEvent event,
Emitter<UniversalAIState> emit,
) async {
try {
emit(const UniversalAILoading(message: '正在发送请求...'));
AppLogger.d('UniversalAIBloc', '发送非流式AI请求: ${event.request.requestType}');
final response = await _repository.sendRequest(event.request);
emit(UniversalAISuccess(response: response));
AppLogger.d('UniversalAIBloc', '非流式AI请求完成');
} catch (e, stackTrace) {
AppLogger.e('UniversalAIBloc', '发送AI请求失败', e, stackTrace);
emit(UniversalAIError(
message: '请求失败: ${e.toString()}',
details: stackTrace.toString(),
));
}
}
/// 处理发送流式AI请求事件
Future<void> _onSendAIStreamRequest(
SendAIStreamRequestEvent event,
Emitter<UniversalAIState> emit,
) async {
try {
// 先取消之前的流式请求
await _streamSubscription?.cancel();
emit(const UniversalAILoading(message: '正在连接AI服务...'));
AppLogger.d('UniversalAIBloc', '开始流式AI请求: ${event.request.requestType}');
StringBuffer buffer = StringBuffer();
int tokenCount = 0;
bool isStreamCompleted = false;
final stream = _repository.streamRequest(event.request);
// 🚀 使用 emit.forEach 确保在事件处理器内部处理完整个流
await emit.forEach<UniversalAIResponse>(
stream,
onData: (response) {
// 🚀 检查是否收到结束信号
if (response.finishReason != null) {
AppLogger.i('UniversalAIBloc', '收到流式生成结束信号: ${response.finishReason}');
isStreamCompleted = true;
// 🚀 立即返回成功状态,不再发送流式状态
return UniversalAISuccess(
response: UniversalAIResponse(
id: response.id,
requestType: event.request.requestType,
content: buffer.toString(),
finishReason: response.finishReason,
model: response.model,
createdAt: response.createdAt,
metadata: response.metadata,
),
isStreaming: false, // 标记为非流式状态
);
}
// 🚀 只有在未完成时才累积内容
if (!isStreamCompleted && response.content.isNotEmpty) {
buffer.write(response.content);
tokenCount += response.tokenUsage?.completionTokens ?? 1;
//AppLogger.v('UniversalAIBloc', '收到流式响应片段,长度: ${response.content.length}');
return UniversalAIStreaming(
partialResponse: buffer.toString(),
tokenCount: tokenCount,
);
}
// 🚀 如果已完成或内容为空,保持当前状态
return emit.isDone ? const UniversalAIInitial() : const UniversalAIStreaming(partialResponse: '');
},
onError: (error, stackTrace) {
AppLogger.e('UniversalAIBloc', '流式AI请求错误', error, stackTrace);
return UniversalAIError(
message: '流式请求失败: ${error.toString()}',
details: stackTrace.toString(),
);
},
);
// 🚀 如果流正常结束但没有收到结束信号,手动发出成功状态
if (!isStreamCompleted && !emit.isDone) {
AppLogger.d('UniversalAIBloc', '流式AI请求完成无结束信号');
emit(UniversalAISuccess(
response: UniversalAIResponse(
id: DateTime.now().millisecondsSinceEpoch.toString(),
requestType: event.request.requestType,
content: buffer.toString(),
finishReason: 'stop',
),
isStreaming: false,
));
}
} catch (e, stackTrace) {
AppLogger.e('UniversalAIBloc', '流式AI请求失败', e, stackTrace);
emit(UniversalAIError(
message: '流式请求失败: ${e.toString()}',
details: stackTrace.toString(),
));
}
}
/// 处理预览AI请求事件
Future<void> _onPreviewAIRequest(
PreviewAIRequestEvent event,
Emitter<UniversalAIState> emit,
) async {
try {
emit(const UniversalAILoading(message: '正在生成预览...'));
AppLogger.d('UniversalAIBloc', '预览AI请求: ${event.request.requestType}');
final previewResponse = await _repository.previewRequest(event.request);
emit(UniversalAIPreviewSuccess(
previewResponse: previewResponse,
request: event.request,
));
AppLogger.d('UniversalAIBloc', '预览生成完成');
} catch (e, stackTrace) {
AppLogger.e('UniversalAIBloc', '预览AI请求失败', e, stackTrace);
emit(UniversalAIError(
message: '预览失败: ${e.toString()}',
details: stackTrace.toString(),
));
}
}
/// 🚀 新增:处理积分预估事件
Future<void> _onEstimateCost(
EstimateCostEvent event,
Emitter<UniversalAIState> emit,
) async {
try {
emit(const UniversalAILoading(message: '正在预估积分成本...'));
AppLogger.d('UniversalAIBloc', '预估AI请求积分成本: ${event.request.requestType}');
final costEstimation = await _repository.estimateCost(event.request);
if (costEstimation.success) {
emit(UniversalAICostEstimationSuccess(
costEstimation: costEstimation,
request: event.request,
));
AppLogger.d('UniversalAIBloc', '积分预估完成: ${costEstimation.estimatedCost}积分');
} else {
emit(UniversalAIError(
message: costEstimation.errorMessage ?? '积分预估失败',
canRetry: true,
));
}
} catch (e, stackTrace) {
AppLogger.e('UniversalAIBloc', '积分预估失败', e, stackTrace);
emit(UniversalAIError(
message: '积分预估失败: ${e.toString()}',
details: stackTrace.toString(),
canRetry: true,
));
}
}
/// 处理停止流式请求事件
Future<void> _onStopStreamRequest(
StopStreamRequestEvent event,
Emitter<UniversalAIState> emit,
) async {
AppLogger.d('UniversalAIBloc', '停止流式请求');
await _streamSubscription?.cancel();
_streamSubscription = null;
// 保留当前的部分响应
String? partialResponse;
if (state is UniversalAIStreaming) {
partialResponse = (state as UniversalAIStreaming).partialResponse;
}
emit(UniversalAICancelled(partialResponse: partialResponse));
}
/// 处理清除响应事件
Future<void> _onClearResponse(
ClearResponseEvent event,
Emitter<UniversalAIState> emit,
) async {
AppLogger.d('UniversalAIBloc', '清除响应');
emit(const UniversalAIInitial());
}
/// 处理重置状态事件
Future<void> _onResetState(
ResetStateEvent event,
Emitter<UniversalAIState> emit,
) async {
AppLogger.d('UniversalAIBloc', '重置状态');
await _streamSubscription?.cancel();
_streamSubscription = null;
emit(const UniversalAIInitial());
}
@override
Future<void> close() {
_streamSubscription?.cancel();
return super.close();
}
}