feat: 添加 Agent 步骤事件与图片附件功能
- 新增 stepStarted/stepFinished 事件类型支持 - 前端实现图片附件上传和预览功能 - 后端增强工具结果存储和事件处理 - 完善相关单元测试和集成测试
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:bloc_test/bloc_test.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:image_picker/image_picker.dart';
|
||||
@@ -9,8 +11,17 @@ import 'package:social_app/features/chat/presentation/bloc/chat_bloc.dart';
|
||||
class MockAgUiService extends AgUiService {
|
||||
MockAgUiService() : super(onEvent: (_) {});
|
||||
|
||||
int previewCalls = 0;
|
||||
|
||||
@override
|
||||
Future<void> sendMessage(String content, {List<XFile>? images}) async {}
|
||||
|
||||
@override
|
||||
Future<Uint8List> fetchAttachmentPreview(String previewPath) async {
|
||||
previewCalls += 1;
|
||||
await Future<void>.delayed(const Duration(milliseconds: 10));
|
||||
return Uint8List.fromList(<int>[1, 2, 3]);
|
||||
}
|
||||
}
|
||||
|
||||
class _ThrowingAgUiService extends AgUiService {
|
||||
@@ -182,6 +193,23 @@ void main() {
|
||||
],
|
||||
);
|
||||
|
||||
blocTest<ChatBloc, ChatState>(
|
||||
'step events update currentStage',
|
||||
build: () => chatBloc,
|
||||
act: (bloc) {
|
||||
service.onEvent(StepStartedEvent(stepName: 'execution'));
|
||||
service.onEvent(StepFinishedEvent(stepName: 'execution'));
|
||||
},
|
||||
expect: () => [
|
||||
isA<ChatState>().having(
|
||||
(s) => s.currentStage,
|
||||
'currentStage',
|
||||
AgentStage.execution,
|
||||
),
|
||||
isA<ChatState>().having((s) => s.currentStage, 'currentStage', isNull),
|
||||
],
|
||||
);
|
||||
|
||||
blocTest<ChatBloc, ChatState>(
|
||||
'runError sets error message',
|
||||
build: () => chatBloc,
|
||||
@@ -325,5 +353,58 @@ void main() {
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
blocTest<ChatBloc, ChatState>(
|
||||
'state snapshot user message keeps attachments',
|
||||
build: () => chatBloc,
|
||||
act: (bloc) {
|
||||
service.onEvent(
|
||||
StateSnapshotEvent(
|
||||
snapshot: {
|
||||
'scope': 'history_day',
|
||||
'messages': [
|
||||
{
|
||||
'id': 'u1',
|
||||
'role': 'user',
|
||||
'content': '请分析这张图',
|
||||
'attachments': [
|
||||
{'bucket': 'b', 'path': 'p', 'mimeType': 'image/png'},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
expect: () => [
|
||||
isA<ChatState>().having(
|
||||
(s) {
|
||||
final item = s.items.first;
|
||||
return item is TextMessageItem && item.attachments.length == 1;
|
||||
},
|
||||
'user attachment count',
|
||||
true,
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
test(
|
||||
'loadAttachmentPreview deduplicates in-flight and caches result',
|
||||
() async {
|
||||
final mock = service as MockAgUiService;
|
||||
final results = await Future.wait<Uint8List?>([
|
||||
chatBloc.loadAttachmentPreview('/api/preview/1'),
|
||||
chatBloc.loadAttachmentPreview('/api/preview/1'),
|
||||
]);
|
||||
final secondRound = await chatBloc.loadAttachmentPreview(
|
||||
'/api/preview/1',
|
||||
);
|
||||
|
||||
expect(results.first, isNotNull);
|
||||
expect(results.last, isNotNull);
|
||||
expect(secondRound, isNotNull);
|
||||
expect(mock.previewCalls, 1);
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user