refactor(apps): 重构数据层目录结构并新增启动预热编排器
This commit is contained in:
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user