refactor(apps): 重构数据层目录结构并新增启动预热编排器

This commit is contained in:
zl-q
2026-03-29 20:26:30 +08:00
parent 33340de8f9
commit 4db9a13bfe
108 changed files with 1653 additions and 1320 deletions
@@ -0,0 +1,114 @@
import 'package:social_app/data/network/i_api_client.dart';
import 'package:social_app/data/cache/cache_policy.dart';
import 'package:social_app/data/cache/cached_repository.dart';
import '../models/ag_ui_event.dart';
class ChatHistoryRepository extends CachedRepository<HistorySnapshot> {
ChatHistoryRepository({required IApiClient apiClient, required super.store})
: _apiClient = apiClient,
super(
policy: const CachePolicy(
softTtl: Duration(seconds: 30),
hardTtl: Duration(minutes: 5),
minRefreshInterval: Duration(seconds: 15),
),
encodeValue: _encodeSnapshot,
decodeValue: _decodeSnapshot,
);
final IApiClient _apiClient;
Future<HistorySnapshot> loadHistory({
String? threadId,
DateTime? beforeDate,
bool forceRefresh = false,
}) {
final key = _keyFor(threadId: threadId, beforeDate: beforeDate);
return getOrLoad(
key: key,
forceRefresh: forceRefresh,
loadFromRemote: () =>
_loadHistoryRemote(threadId: threadId, beforeDate: beforeDate),
);
}
Future<HistorySnapshot> _loadHistoryRemote({
String? threadId,
DateTime? beforeDate,
}) async {
final path = _buildHistoryPath(threadId: threadId, beforeDate: beforeDate);
final response = await _apiClient.get<Map<String, dynamic>>(path);
final payload = response.data;
if (payload is! Map<String, dynamic>) {
throw StateError('Invalid /agent/history response');
}
return HistorySnapshot.fromJson(payload);
}
static String _buildHistoryPath({String? threadId, DateTime? beforeDate}) {
final query = <String>[];
if (threadId != null && threadId.isNotEmpty) {
query.add('threadId=$threadId');
}
if (beforeDate != null) {
final day = DateTime(beforeDate.year, beforeDate.month, beforeDate.day);
query.add('before=${day.toIso8601String().substring(0, 10)}');
}
if (query.isEmpty) {
return '/api/v1/agent/history';
}
return '/api/v1/agent/history?${query.join('&')}';
}
static String _keyFor({String? threadId, DateTime? beforeDate}) {
final threadPart = (threadId == null || threadId.isEmpty)
? 'default'
: threadId;
if (beforeDate == null) {
return 'chat:history:first:$threadPart';
}
final day = DateTime(
beforeDate.year,
beforeDate.month,
beforeDate.day,
).toIso8601String().substring(0, 10);
return 'chat:history:before:$threadPart:$day';
}
static Object? _encodeSnapshot(HistorySnapshot snapshot) {
return <String, Object?>{
'scope': snapshot.scope,
'threadId': snapshot.threadId,
'day': snapshot.day,
'hasMore': snapshot.hasMore,
'messages': snapshot.messages.map(_encodeMessage).toList(growable: false),
};
}
static HistorySnapshot _decodeSnapshot(Object? raw) {
if (raw is! Map) {
throw const FormatException('Invalid cached history snapshot payload');
}
return HistorySnapshot.fromJson(Map<String, dynamic>.from(raw));
}
static Map<String, Object?> _encodeMessage(HistoryMessage message) {
return <String, Object?>{
'id': message.id,
'seq': message.seq,
'role': message.role,
'content': message.content,
'timestamp': message.timestamp.toIso8601String(),
'attachments': message.attachments
.map(
(attachment) => <String, Object?>{
'url': attachment.url,
'mimeType': attachment.mimeType,
},
)
.toList(growable: false),
'ui_schema': message.uiSchema,
};
}
}
@@ -5,9 +5,10 @@ import 'dart:typed_data';
import 'package:dio/dio.dart';
import 'package:image_picker/image_picker.dart';
import 'package:social_app/core/network/i_api_client.dart';
import 'package:social_app/data/network/i_api_client.dart';
import '../models/ag_ui_event.dart';
import '../repositories/chat_history_repository.dart';
typedef EventCallback = void Function(AgUiEvent event);
@@ -43,6 +44,7 @@ class _RunInputPayload {
class AgUiService {
final IApiClient _apiClient;
final ChatHistoryRepository? _historyRepository;
EventCallback onEvent;
final Map<String, String> _lastEventIdByThread = {};
int _activeStreamToken = 0;
@@ -54,9 +56,13 @@ class AgUiService {
String? _activeRunId;
bool _hasMoreHistory = false;
AgUiService({EventCallback? onEvent, required IApiClient apiClient})
: onEvent = onEvent ?? ((_) {}),
_apiClient = apiClient;
AgUiService({
EventCallback? onEvent,
required IApiClient apiClient,
ChatHistoryRepository? historyRepository,
}) : onEvent = onEvent ?? ((_) {}),
_apiClient = apiClient,
_historyRepository = historyRepository;
Future<SendMessageResult> sendMessage(
String content, {
@@ -105,18 +111,28 @@ class AgUiService {
}
Future<HistorySnapshot> loadHistory({DateTime? beforeDate}) async {
final repository = _historyRepository;
final snapshot = repository != null
? await repository.loadHistory(
threadId: _threadId,
beforeDate: beforeDate,
)
: await _loadHistoryFromApi(beforeDate: beforeDate);
if (snapshot.threadId != null && snapshot.threadId!.isNotEmpty) {
_threadId = snapshot.threadId;
}
_hasMoreHistory = snapshot.hasMore;
return snapshot;
}
Future<HistorySnapshot> _loadHistoryFromApi({DateTime? beforeDate}) async {
final path = _buildHistoryPath(beforeDate: beforeDate);
final response = await _apiClient.get<Map<String, dynamic>>(path);
final payload = response.data;
if (payload is! Map<String, dynamic>) {
throw StateError('Invalid /agent/history response');
}
final snapshot = HistorySnapshot.fromJson(payload);
if (snapshot.threadId != null && snapshot.threadId!.isNotEmpty) {
_threadId = snapshot.threadId;
}
_hasMoreHistory = snapshot.hasMore;
return snapshot;
return HistorySnapshot.fromJson(payload);
}
Future<Uint8List> fetchAttachmentPreview(String previewPath) async {
@@ -5,10 +5,11 @@ import 'package:image_picker/image_picker.dart';
import 'package:social_app/core/chat/agent_stage.dart';
import 'package:social_app/core/chat/chat_list_item.dart';
import 'package:social_app/core/chat/chat_orchestrator.dart';
import 'package:social_app/core/network/i_api_client.dart';
import 'package:social_app/data/network/i_api_client.dart';
import 'package:social_app/core/l10n/l10n.dart';
import '../../data/models/ag_ui_event.dart';
import '../../data/repositories/chat_history_repository.dart';
import '../../data/services/ag_ui_service.dart';
class ChatState implements ChatOrchestratorState {
@@ -95,9 +96,17 @@ class ChatState implements ChatOrchestratorState {
}
class ChatBloc extends Cubit<ChatState> implements ChatOrchestrator {
ChatBloc({AgUiService? service, required IApiClient apiClient})
: _service = service ?? AgUiService(apiClient: apiClient),
super(const ChatState()) {
ChatBloc({
AgUiService? service,
required IApiClient apiClient,
ChatHistoryRepository? historyRepository,
}) : _service =
service ??
AgUiService(
apiClient: apiClient,
historyRepository: historyRepository,
),
super(const ChatState()) {
_service.onEvent = _handleEvent;
}