feat: 优化 Agent 运行时与聊天设置体验
This commit is contained in:
@@ -7,8 +7,7 @@ import 'package:social_app/core/api/i_api_client.dart';
|
||||
import '../../data/models/ag_ui_event.dart';
|
||||
import '../../data/models/chat_list_item.dart';
|
||||
import '../../data/services/ag_ui_service.dart';
|
||||
|
||||
enum AgentStage { intent, execution, report }
|
||||
import 'agent_stage.dart';
|
||||
|
||||
class ChatState {
|
||||
final List<ChatListItem> items;
|
||||
@@ -93,6 +92,19 @@ class ChatBloc extends Cubit<ChatState> {
|
||||
final Map<String, Future<Uint8List?>> _attachmentPreviewInflight =
|
||||
<String, Future<Uint8List?>>{};
|
||||
|
||||
/// Common state reset for run completion (success/error/cancel)
|
||||
ChatState _resetRunState({String? error, String? currentMessageId}) {
|
||||
return state.copyWith(
|
||||
isSending: false,
|
||||
isWaitingFirstToken: false,
|
||||
isStreaming: false,
|
||||
isCancelling: false,
|
||||
currentMessageId: currentMessageId,
|
||||
error: error,
|
||||
currentStage: null,
|
||||
);
|
||||
}
|
||||
|
||||
void _handleEvent(AgUiEvent event) {
|
||||
switch (event.type) {
|
||||
case AgUiEventType.runStarted:
|
||||
@@ -106,29 +118,10 @@ class ChatBloc extends Cubit<ChatState> {
|
||||
),
|
||||
);
|
||||
case AgUiEventType.runFinished:
|
||||
emit(
|
||||
state.copyWith(
|
||||
isSending: false,
|
||||
isWaitingFirstToken: false,
|
||||
isStreaming: false,
|
||||
isCancelling: false,
|
||||
currentMessageId: null,
|
||||
currentStage: null,
|
||||
),
|
||||
);
|
||||
emit(_resetRunState());
|
||||
case AgUiEventType.runError:
|
||||
final errorEvent = event as RunErrorEvent;
|
||||
emit(
|
||||
state.copyWith(
|
||||
isSending: false,
|
||||
isWaitingFirstToken: false,
|
||||
isStreaming: false,
|
||||
isCancelling: false,
|
||||
currentMessageId: null,
|
||||
error: errorEvent.message,
|
||||
currentStage: null,
|
||||
),
|
||||
);
|
||||
emit(_resetRunState(error: errorEvent.message));
|
||||
case AgUiEventType.stepStarted:
|
||||
_handleStepStarted(event as StepStartedEvent);
|
||||
case AgUiEventType.stepFinished:
|
||||
@@ -151,57 +144,27 @@ class ChatBloc extends Cubit<ChatState> {
|
||||
}
|
||||
|
||||
void _handleStepStarted(StepStartedEvent event) {
|
||||
emit(state.copyWith(currentStage: _stageFromName(event.stepName)));
|
||||
emit(state.copyWith(currentStage: stageFromStepName(event.stepName)));
|
||||
}
|
||||
|
||||
void _handleStepFinished(StepFinishedEvent event) {
|
||||
if (state.currentStage == _stageFromName(event.stepName)) {
|
||||
if (state.currentStage == stageFromStepName(event.stepName)) {
|
||||
emit(state.copyWith(currentStage: null));
|
||||
}
|
||||
}
|
||||
|
||||
void _handleTextMessageEnd(TextMessageEndEvent event) {
|
||||
final timestamp = DateTime.now();
|
||||
final items = List<ChatListItem>.from(state.items);
|
||||
|
||||
final messageIndex = items.indexWhere(
|
||||
(item) => item.id == event.messageId && item is TextMessageItem,
|
||||
final items = _updateOrAddMessage(
|
||||
state.items,
|
||||
event.messageId,
|
||||
event.answer,
|
||||
timestamp,
|
||||
);
|
||||
|
||||
if (messageIndex >= 0) {
|
||||
final existing = items[messageIndex] as TextMessageItem;
|
||||
items[messageIndex] = existing.copyWith(
|
||||
content: event.answer,
|
||||
isStreaming: false,
|
||||
);
|
||||
} else {
|
||||
items.add(
|
||||
TextMessageItem(
|
||||
id: event.messageId,
|
||||
content: event.answer,
|
||||
timestamp: timestamp,
|
||||
sender: MessageSender.ai,
|
||||
isStreaming: false,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
final uiSchema = event.uiSchema;
|
||||
if (uiSchema != null) {
|
||||
final uiItemId = '${event.messageId}-ui';
|
||||
final existingUiIndex = items.indexWhere((item) => item.id == uiItemId);
|
||||
final uiItem = ToolResultItem(
|
||||
id: uiItemId,
|
||||
callId: event.messageId,
|
||||
uiSchema: uiSchema,
|
||||
timestamp: timestamp,
|
||||
sender: MessageSender.ai,
|
||||
);
|
||||
if (existingUiIndex >= 0) {
|
||||
items[existingUiIndex] = uiItem;
|
||||
} else {
|
||||
items.add(uiItem);
|
||||
}
|
||||
_upsertUiSchema(items, event.messageId, uiSchema, timestamp);
|
||||
}
|
||||
|
||||
emit(
|
||||
@@ -214,6 +177,56 @@ class ChatBloc extends Cubit<ChatState> {
|
||||
);
|
||||
}
|
||||
|
||||
List<ChatListItem> _updateOrAddMessage(
|
||||
List<ChatListItem> items,
|
||||
String messageId,
|
||||
String content,
|
||||
DateTime timestamp,
|
||||
) {
|
||||
final result = List<ChatListItem>.from(items);
|
||||
final index = result.indexWhere(
|
||||
(item) => item.id == messageId && item is TextMessageItem,
|
||||
);
|
||||
|
||||
if (index >= 0) {
|
||||
final existing = result[index] as TextMessageItem;
|
||||
result[index] = existing.copyWith(content: content, isStreaming: false);
|
||||
} else {
|
||||
result.add(
|
||||
TextMessageItem(
|
||||
id: messageId,
|
||||
content: content,
|
||||
timestamp: timestamp,
|
||||
sender: MessageSender.ai,
|
||||
isStreaming: false,
|
||||
),
|
||||
);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void _upsertUiSchema(
|
||||
List<ChatListItem> items,
|
||||
String messageId,
|
||||
Map<String, dynamic> uiSchema,
|
||||
DateTime timestamp,
|
||||
) {
|
||||
final uiItemId = '$messageId-ui';
|
||||
final existingIndex = items.indexWhere((item) => item.id == uiItemId);
|
||||
final uiItem = ToolResultItem(
|
||||
id: uiItemId,
|
||||
callId: messageId,
|
||||
uiSchema: uiSchema,
|
||||
timestamp: timestamp,
|
||||
sender: MessageSender.ai,
|
||||
);
|
||||
if (existingIndex >= 0) {
|
||||
items[existingIndex] = uiItem;
|
||||
} else {
|
||||
items.add(uiItem);
|
||||
}
|
||||
}
|
||||
|
||||
void _handleToolCallStart(ToolCallStartEvent event) {
|
||||
final items = List<ChatListItem>.from(state.items)
|
||||
..add(
|
||||
@@ -299,10 +312,14 @@ class ChatBloc extends Cubit<ChatState> {
|
||||
final converted = <ChatListItem>[];
|
||||
for (final msg in messages) {
|
||||
final sender = msg.role == 'user' ? MessageSender.user : MessageSender.ai;
|
||||
final attachments = <Map<String, dynamic>>[];
|
||||
if (msg.url != null && msg.url!.isNotEmpty) {
|
||||
attachments.add({'url': msg.url!, 'mimeType': 'image/*'});
|
||||
}
|
||||
final attachments = msg.attachments
|
||||
.map(
|
||||
(attachment) => <String, dynamic>{
|
||||
'url': attachment.url,
|
||||
'mimeType': attachment.mimeType,
|
||||
},
|
||||
)
|
||||
.toList();
|
||||
|
||||
if (msg.content.isNotEmpty || sender == MessageSender.user) {
|
||||
converted.add(
|
||||
@@ -500,16 +517,3 @@ class ChatBloc extends Cubit<ChatState> {
|
||||
emit(state.copyWith(error: null));
|
||||
}
|
||||
}
|
||||
|
||||
AgentStage? _stageFromName(String value) {
|
||||
switch (value) {
|
||||
case 'intent':
|
||||
return AgentStage.intent;
|
||||
case 'execution':
|
||||
return AgentStage.execution;
|
||||
case 'report':
|
||||
return AgentStage.report;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user