马良AI写作初始化仓库

This commit is contained in:
邓滨杰
2025-09-10 00:07:52 +08:00
parent 3c06bb1a03
commit 39c0f8840f
1309 changed files with 318528 additions and 0 deletions

View File

@@ -0,0 +1,53 @@
import 'package:ainoval/models/admin/billing_models.dart';
import 'package:ainoval/services/api_service/base/api_client.dart';
class BillingRepositoryImpl {
final ApiClient apiClient;
BillingRepositoryImpl({required this.apiClient});
Future<List<CreditTransactionModel>> listTransactions({int page = 0, int size = 20, String? status, String? userId}) async {
final params = <String, dynamic>{
'page': page,
'size': size,
if (status != null && status.isNotEmpty) 'status': status,
if (userId != null && userId.isNotEmpty) 'userId': userId,
};
final data = await apiClient.getWithParams('/admin/billing/transactions', queryParameters: params);
if (data is List) {
return data.map((e) => CreditTransactionModel.fromJson(Map<String, dynamic>.from(e))).toList();
}
return [];
}
Future<int> countTransactions({String? status, String? userId}) async {
final params = <String, dynamic>{
if (status != null && status.isNotEmpty) 'status': status,
if (userId != null && userId.isNotEmpty) 'userId': userId,
};
final data = await apiClient.getWithParams('/admin/billing/transactions/count', queryParameters: params);
if (data is int) return data;
if (data is String) return int.tryParse(data) ?? 0;
if (data is Map<String, dynamic> && data['count'] is int) return data['count'] as int;
return 0;
}
Future<CreditTransactionModel?> getTransaction(String traceId) async {
final data = await apiClient.get('/admin/billing/transactions/$traceId');
if (data is Map<String, dynamic>) {
return CreditTransactionModel.fromJson(data);
}
return null;
}
Future<CreditTransactionModel?> reverse(String traceId, {required String operatorUserId, required String reason}) async {
final payload = {'operatorUserId': operatorUserId, 'reason': reason};
final data = await apiClient.post('/admin/billing/transactions/$traceId/reverse', data: payload);
if (data is Map<String, dynamic>) {
return CreditTransactionModel.fromJson(data);
}
return null;
}
}

View File

@@ -0,0 +1,744 @@
import '../../../../../models/admin/llm_observability_models.dart';
import '../../admin/llm_observability_repository.dart';
import '../../../base/api_client.dart';
import '../../../base/api_exception.dart';
import '../../../../../utils/logger.dart';
/// LLM可观测性仓库实现
class LLMObservabilityRepositoryImpl implements LLMObservabilityRepository {
final ApiClient _apiClient;
final String _tag = 'LLMObservabilityRepository';
LLMObservabilityRepositoryImpl({ApiClient? apiClient}) : _apiClient = apiClient ?? ApiClient();
// ==================== 日志查询 ====================
/// 获取所有LLM调用日志
Future<PagedResponse<LLMTrace>> getAllTraces({
int page = 0,
int size = 20,
String sortBy = 'timestamp',
String sortDir = 'desc',
}) async {
try {
AppLogger.d(_tag, '获取LLM调用日志: page=$page, size=$size, sortBy=$sortBy, sortDir=$sortDir');
final response = await _apiClient.getWithParams('/admin/llm-observability/traces', queryParameters: {
'page': page,
'size': size,
'sortBy': sortBy,
'sortDir': sortDir,
});
if (response is Map<String, dynamic> && response.containsKey('data')) {
return PagedResponse.fromJson(response['data'], (json) => LLMTrace.fromJson(json as Map<String, dynamic>));
} else if (response is Map<String, dynamic>) {
return PagedResponse.fromJson(response, (json) => LLMTrace.fromJson(json as Map<String, dynamic>));
} else {
throw ApiException(-1, 'LLM日志响应格式错误');
}
} catch (e) {
AppLogger.e(_tag, '获取LLM调用日志失败', e);
rethrow;
}
}
/// 游标分页获取LLM调用日志
@override
Future<CursorPageResponse<LLMTrace>> getTracesByCursor({
String? cursor,
int limit = 50,
String? userId,
String? provider,
String? model,
String? sessionId,
bool? hasError,
String? businessType,
String? correlationId,
String? traceId,
String? type,
String? tag,
DateTime? startTime,
DateTime? endTime,
}) async {
try {
final params = <String, dynamic>{
'limit': limit,
};
if (cursor != null && cursor.isNotEmpty) params['cursor'] = cursor;
if (userId != null) params['userId'] = userId;
if (provider != null) params['provider'] = provider;
if (model != null) params['model'] = model;
if (sessionId != null) params['sessionId'] = sessionId;
if (hasError != null) params['hasError'] = hasError;
if (businessType != null) params['businessType'] = businessType;
if (correlationId != null) params['correlationId'] = correlationId;
if (traceId != null) params['traceId'] = traceId;
if (type != null) params['type'] = type;
if (tag != null) params['tag'] = tag;
if (startTime != null) params['startTime'] = startTime.toIso8601String();
if (endTime != null) params['endTime'] = endTime.toIso8601String();
final response = await _apiClient.getWithParams('/admin/llm-observability/traces/cursor', queryParameters: params);
if (response is Map<String, dynamic> && response.containsKey('data')) {
return CursorPageResponse.fromJson(response['data'], (json) => LLMTrace.fromJson(json as Map<String, dynamic>));
} else if (response is Map<String, dynamic>) {
return CursorPageResponse.fromJson(response, (json) => LLMTrace.fromJson(json as Map<String, dynamic>));
} else {
throw ApiException(-1, '游标分页响应格式错误');
}
} catch (e) {
AppLogger.e(_tag, '游标分页获取LLM调用日志失败', e);
rethrow;
}
}
/// 根据用户ID获取LLM调用日志
Future<PagedResponse<LLMTrace>> getTracesByUserId(
String userId, {
int page = 0,
int size = 20,
}) async {
try {
AppLogger.d(_tag, '获取用户LLM调用日志: userId=$userId, page=$page, size=$size');
final response = await _apiClient.getWithParams('/admin/llm-observability/traces/user/$userId', queryParameters: {
'page': page,
'size': size,
});
if (response is Map<String, dynamic> && response.containsKey('data')) {
return PagedResponse.fromJson(response['data'], (json) => LLMTrace.fromJson(json as Map<String, dynamic>));
} else if (response is Map<String, dynamic>) {
return PagedResponse.fromJson(response, (json) => LLMTrace.fromJson(json as Map<String, dynamic>));
} else {
throw ApiException(-1, '用户LLM日志响应格式错误');
}
} catch (e) {
AppLogger.e(_tag, '获取用户LLM调用日志失败', e);
rethrow;
}
}
/// 根据提供商获取LLM调用日志
Future<PagedResponse<LLMTrace>> getTracesByProvider(
String provider, {
int page = 0,
int size = 20,
}) async {
try {
AppLogger.d(_tag, '获取提供商LLM调用日志: provider=$provider, page=$page, size=$size');
final response = await _apiClient.getWithParams('/admin/llm-observability/traces/provider/$provider', queryParameters: {
'page': page,
'size': size,
});
if (response is Map<String, dynamic> && response.containsKey('data')) {
return PagedResponse.fromJson(response['data'], (json) => LLMTrace.fromJson(json as Map<String, dynamic>));
} else if (response is Map<String, dynamic>) {
return PagedResponse.fromJson(response, (json) => LLMTrace.fromJson(json as Map<String, dynamic>));
} else {
throw ApiException(-1, '提供商LLM日志响应格式错误');
}
} catch (e) {
AppLogger.e(_tag, '获取提供商LLM调用日志失败', e);
rethrow;
}
}
/// 根据模型名称获取LLM调用日志
Future<PagedResponse<LLMTrace>> getTracesByModel(
String modelName, {
int page = 0,
int size = 20,
}) async {
try {
AppLogger.d(_tag, '获取模型LLM调用日志: modelName=$modelName, page=$page, size=$size');
final response = await _apiClient.getWithParams('/admin/llm-observability/traces/model/$modelName', queryParameters: {
'page': page,
'size': size,
});
if (response is Map<String, dynamic> && response.containsKey('data')) {
return PagedResponse.fromJson(response['data'], (json) => LLMTrace.fromJson(json as Map<String, dynamic>));
} else if (response is Map<String, dynamic>) {
return PagedResponse.fromJson(response, (json) => LLMTrace.fromJson(json as Map<String, dynamic>));
} else {
throw ApiException(-1, '模型LLM日志响应格式错误');
}
} catch (e) {
AppLogger.e(_tag, '获取模型LLM调用日志失败', e);
rethrow;
}
}
/// 根据时间范围获取LLM调用日志
Future<PagedResponse<LLMTrace>> getTracesByTimeRange(
DateTime startTime,
DateTime endTime, {
int page = 0,
int size = 20,
}) async {
try {
AppLogger.d(_tag, '按时间范围获取LLM调用日志: startTime=$startTime, endTime=$endTime, page=$page, size=$size');
final response = await _apiClient.getWithParams('/admin/llm-observability/traces/timerange', queryParameters: {
'startTime': startTime.toIso8601String(),
'endTime': endTime.toIso8601String(),
'page': page,
'size': size,
});
if (response is Map<String, dynamic> && response.containsKey('data')) {
return PagedResponse.fromJson(response['data'], (json) => LLMTrace.fromJson(json as Map<String, dynamic>));
} else if (response is Map<String, dynamic>) {
return PagedResponse.fromJson(response, (json) => LLMTrace.fromJson(json as Map<String, dynamic>));
} else {
throw ApiException(-1, '时间范围LLM日志响应格式错误');
}
} catch (e) {
AppLogger.e(_tag, '按时间范围获取LLM调用日志失败', e);
rethrow;
}
}
/// 搜索LLM调用日志
Future<PagedResponse<LLMTrace>> searchTraces(
LLMTraceSearchCriteria criteria, {
String? businessType,
String? correlationId,
String? traceId,
String? type,
String? tag,
}) async {
try {
AppLogger.d(_tag, '搜索LLM调用日志: criteria=$criteria');
final queryParams = <String, dynamic>{
'page': criteria.page,
'size': criteria.size,
};
if (criteria.userId != null) queryParams['userId'] = criteria.userId;
if (criteria.provider != null) queryParams['provider'] = criteria.provider;
if (criteria.model != null) queryParams['model'] = criteria.model;
if (criteria.sessionId != null) queryParams['sessionId'] = criteria.sessionId;
if (criteria.hasError != null) queryParams['hasError'] = criteria.hasError;
if (criteria.startTime != null) queryParams['startTime'] = criteria.startTime!.toIso8601String();
if (criteria.endTime != null) queryParams['endTime'] = criteria.endTime!.toIso8601String();
if (businessType != null && businessType.isNotEmpty) queryParams['businessType'] = businessType;
if (correlationId != null && correlationId.isNotEmpty) queryParams['correlationId'] = correlationId;
if (traceId != null && traceId.isNotEmpty) queryParams['traceId'] = traceId;
if (type != null && type.isNotEmpty) queryParams['type'] = type;
if (tag != null && tag.isNotEmpty) queryParams['tag'] = tag;
final response = await _apiClient.getWithParams('/admin/llm-observability/traces/search', queryParameters: queryParams);
if (response is Map<String, dynamic> && response.containsKey('data')) {
return PagedResponse.fromJson(response['data'], (json) => LLMTrace.fromJson(json as Map<String, dynamic>));
} else if (response is Map<String, dynamic>) {
return PagedResponse.fromJson(response, (json) => LLMTrace.fromJson(json as Map<String, dynamic>));
} else {
throw ApiException(-1, '搜索LLM日志响应格式错误');
}
} catch (e) {
AppLogger.e(_tag, '搜索LLM调用日志失败', e);
rethrow;
}
}
/// 获取单个LLM调用日志详情
Future<LLMTrace?> getTraceById(String traceId) async {
try {
AppLogger.d(_tag, '获取LLM调用日志详情: traceId=$traceId');
final response = await _apiClient.getWithParams('/admin/llm-observability/traces/$traceId');
if (response is Map<String, dynamic> && response.containsKey('data')) {
return LLMTrace.fromJson(response['data']);
} else if (response is Map<String, dynamic>) {
return LLMTrace.fromJson(response);
} else {
return null;
}
} catch (e) {
AppLogger.e(_tag, '获取LLM调用日志详情失败', e);
if (e is ApiException && e.statusCode == 404) {
return null;
}
rethrow;
}
}
// ==================== 统计分析 ====================
/// 获取LLM调用统计概览
Future<Map<String, dynamic>> getOverviewStatistics({
DateTime? startTime,
DateTime? endTime,
}) async {
try {
AppLogger.d(_tag, '获取LLM调用统计概览: startTime=$startTime, endTime=$endTime');
final queryParams = <String, dynamic>{};
if (startTime != null) queryParams['startTime'] = startTime.toIso8601String();
if (endTime != null) queryParams['endTime'] = endTime.toIso8601String();
final response = await _apiClient.getWithParams('/admin/llm-observability/statistics/overview', queryParameters: queryParams);
if (response is Map<String, dynamic> && response.containsKey('data')) {
return response['data'];
} else if (response is Map<String, dynamic>) {
return response;
} else {
throw ApiException(-1, '统计概览响应格式错误');
}
} catch (e) {
AppLogger.e(_tag, '获取LLM调用统计概览失败', e);
rethrow;
}
}
/// 获取提供商统计信息
@override
Future<List<ProviderStatistics>> getProviderStatistics({
DateTime? startTime,
DateTime? endTime,
}) async {
try {
AppLogger.d(_tag, '获取提供商统计信息: startTime=$startTime, endTime=$endTime');
final queryParams = <String, dynamic>{};
if (startTime != null) queryParams['startTime'] = startTime.toIso8601String();
if (endTime != null) queryParams['endTime'] = endTime.toIso8601String();
final response = await _apiClient.getWithParams('/admin/llm-observability/statistics/providers', queryParameters: queryParams);
Map<String, dynamic> dataMap;
if (response is Map<String, dynamic> && response.containsKey('data')) {
final d = response['data'];
if (d is Map<String, dynamic>) {
dataMap = d;
} else {
AppLogger.w(_tag, '提供商统计 data 非 Map实际为: ${d.runtimeType}');
return [];
}
} else if (response is Map<String, dynamic>) {
dataMap = response;
} else {
AppLogger.w(_tag, '提供商统计响应不是 Map实际为: ${response.runtimeType}');
return [];
}
final Map<String, num> callsByProvider = Map<String, num>.from(dataMap['callsByProvider'] ?? {});
final Map<String, num> errorsByProvider = Map<String, num>.from(dataMap['errorsByProvider'] ?? {});
final Map<String, num> avgDurationByProvider = Map<String, num>.from(dataMap['avgDurationByProvider'] ?? {});
final List<ProviderStatistics> result = [];
for (final entry in callsByProvider.entries) {
final String provider = entry.key;
final int totalCalls = entry.value.toInt();
final int failed = (errorsByProvider[provider] ?? 0).toInt();
final int successful = totalCalls - failed;
final double successRate = totalCalls == 0 ? 0.0 : successful / totalCalls * 100.0;
final double avgLatency = (avgDurationByProvider[provider] ?? 0).toDouble();
final stats = LLMStatistics(
totalCalls: totalCalls,
successfulCalls: successful,
failedCalls: failed,
successRate: successRate,
averageLatency: avgLatency,
totalTokens: 0,
);
result.add(ProviderStatistics(provider: provider, statistics: stats, models: const []));
}
// 排序:按调用次数降序
result.sort((a, b) => b.statistics.totalCalls.compareTo(a.statistics.totalCalls));
return result;
} catch (e) {
AppLogger.e(_tag, '获取提供商统计信息失败', e);
// 出错时返回空列表而不是抛出异常,避免崩溃
return [];
}
}
/// 获取模型统计信息
@override
Future<List<ModelStatistics>> getModelStatistics({
DateTime? startTime,
DateTime? endTime,
}) async {
try {
AppLogger.d(_tag, '获取模型统计信息: startTime=$startTime, endTime=$endTime');
final queryParams = <String, dynamic>{};
if (startTime != null) queryParams['startTime'] = startTime.toIso8601String();
if (endTime != null) queryParams['endTime'] = endTime.toIso8601String();
final response = await _apiClient.getWithParams('/admin/llm-observability/statistics/models', queryParameters: queryParams);
Map<String, dynamic> dataMap;
if (response is Map<String, dynamic> && response.containsKey('data')) {
final d = response['data'];
if (d is Map<String, dynamic>) {
dataMap = d;
} else {
AppLogger.w(_tag, '模型统计 data 非 Map实际为: ${d.runtimeType}');
return [];
}
} else if (response is Map<String, dynamic>) {
dataMap = response;
} else {
AppLogger.w(_tag, '模型统计响应不是 Map实际为: ${response.runtimeType}');
return [];
}
final Map<String, num> callsByModel = Map<String, num>.from(dataMap['callsByModel'] ?? {});
final Map<String, num> errorsByModel = Map<String, num>.from(dataMap['errorsByModel'] ?? {});
final Map<String, num> tokensByModel = Map<String, num>.from(dataMap['tokensByModel'] ?? {});
final List<ModelStatistics> result = [];
for (final entry in callsByModel.entries) {
final String modelName = entry.key;
final int totalCalls = entry.value.toInt();
final int failed = (errorsByModel[modelName] ?? 0).toInt();
final int successful = totalCalls - failed;
final double successRate = totalCalls == 0 ? 0.0 : successful / totalCalls * 100.0;
final int totalTokens = (tokensByModel[modelName] ?? 0).toInt();
final stats = LLMStatistics(
totalCalls: totalCalls,
successfulCalls: successful,
failedCalls: failed,
successRate: successRate,
averageLatency: 0.0,
totalTokens: totalTokens,
);
// 后端未提供 provider 归属,这里尝试从模型名前缀简单推断,否则留空
final provider = _inferProviderFromModel(modelName);
result.add(ModelStatistics(modelName: modelName, provider: provider, statistics: stats));
}
// 排序:按调用次数降序
result.sort((a, b) => b.statistics.totalCalls.compareTo(a.statistics.totalCalls));
return result;
} catch (e) {
AppLogger.e(_tag, '获取模型统计信息失败', e);
// 出错时返回空列表而不是抛出异常,避免崩溃
return [];
}
}
String _inferProviderFromModel(String modelName) {
final lower = modelName.toLowerCase();
if (lower.contains('gpt') || lower.contains('o1') || lower.contains('openai')) return 'OpenAI';
if (lower.contains('claude') || lower.contains('anthropic')) return 'Anthropic';
if (lower.contains('gemini') || lower.contains('google') || lower.contains('palm')) return 'Google';
if (lower.contains('glm') || lower.contains('zhipu')) return 'ZhipuAI';
if (lower.contains('qwen') || lower.contains('dashscope') || lower.contains('ali')) return 'AliCloud';
return '';
}
/// 获取用户统计信息
@override
Future<List<UserStatistics>> getUserStatistics({
DateTime? startTime,
DateTime? endTime,
}) async {
try {
AppLogger.d(_tag, '获取用户统计信息: startTime=$startTime, endTime=$endTime');
final queryParams = <String, dynamic>{};
if (startTime != null) queryParams['startTime'] = startTime.toIso8601String();
if (endTime != null) queryParams['endTime'] = endTime.toIso8601String();
final response = await _apiClient.getWithParams('/admin/llm-observability/statistics/users', queryParameters: queryParams);
Map<String, dynamic> dataMap;
if (response is Map<String, dynamic> && response.containsKey('data')) {
final d = response['data'];
if (d is Map<String, dynamic>) {
dataMap = d;
} else {
AppLogger.w(_tag, '用户统计 data 非 Map实际为: ${d.runtimeType}');
return [];
}
} else if (response is Map<String, dynamic>) {
dataMap = response;
} else {
AppLogger.w(_tag, '用户统计响应不是 Map实际为: ${response.runtimeType}');
return [];
}
final Map<String, num> callsByUser = Map<String, num>.from(dataMap['callsByUser'] ?? {});
final Map<String, num> tokensByUser = Map<String, num>.from(dataMap['tokensByUser'] ?? {});
final Map<String, num> errorsByUser = Map<String, num>.from(dataMap['errorsByUser'] ?? {});
final List<UserStatistics> result = [];
for (final entry in callsByUser.entries) {
final String userId = entry.key;
final int totalCalls = entry.value.toInt();
final int failed = (errorsByUser[userId] ?? 0).toInt();
final int successful = totalCalls - failed;
final double successRate = totalCalls == 0 ? 0.0 : successful / totalCalls * 100.0;
final int totalTokens = (tokensByUser[userId] ?? 0).toInt();
final stats = LLMStatistics(
totalCalls: totalCalls,
successfulCalls: successful,
failedCalls: failed,
successRate: successRate,
averageLatency: 0.0,
totalTokens: totalTokens,
);
result.add(UserStatistics(
userId: userId,
username: null,
statistics: stats,
topModels: const [],
topProviders: const [],
));
}
// 排序:按调用次数降序
result.sort((a, b) => b.statistics.totalCalls.compareTo(a.statistics.totalCalls));
return result;
} catch (e) {
AppLogger.e(_tag, '获取用户统计信息失败', e);
// 出错时返回空列表而不是抛出异常,避免崩溃
return [];
}
}
/// 获取错误统计信息
@override
Future<List<ErrorStatistics>> getErrorStatistics({
DateTime? startTime,
DateTime? endTime,
}) async {
try {
AppLogger.d(_tag, '获取错误统计信息: startTime=$startTime, endTime=$endTime');
final queryParams = <String, dynamic>{};
if (startTime != null) queryParams['startTime'] = startTime.toIso8601String();
if (endTime != null) queryParams['endTime'] = endTime.toIso8601String();
final response = await _apiClient.getWithParams('/admin/llm-observability/statistics/errors', queryParameters: queryParams);
List<dynamic> dataList;
if (response is Map<String, dynamic> && response.containsKey('data')) {
final data = response['data'];
if (data is List) {
dataList = data;
} else {
AppLogger.w(_tag, '错误统计数据不是List格式: ${data.runtimeType}');
return [];
}
} else if (response is List) {
dataList = response;
} else {
AppLogger.w(_tag, '错误统计响应格式错误,返回空列表: ${response.runtimeType}');
return [];
}
return dataList
.map((item) => ErrorStatistics.fromJson(item as Map<String, dynamic>))
.toList();
} catch (e) {
AppLogger.e(_tag, '获取错误统计信息失败', e);
return [];
}
}
/// 获取性能统计信息
@override
Future<PerformanceStatistics> getPerformanceStatistics({
DateTime? startTime,
DateTime? endTime,
}) async {
try {
AppLogger.d(_tag, '获取性能统计信息: startTime=$startTime, endTime=$endTime');
final queryParams = <String, dynamic>{};
if (startTime != null) queryParams['startTime'] = startTime.toIso8601String();
if (endTime != null) queryParams['endTime'] = endTime.toIso8601String();
final response = await _apiClient.getWithParams('/admin/llm-observability/statistics/performance', queryParameters: queryParams);
Map<String, dynamic> data;
if (response is Map<String, dynamic> && response.containsKey('data')) {
data = response['data'];
} else if (response is Map<String, dynamic>) {
data = response;
} else {
throw ApiException(-1, '性能统计响应格式错误');
}
return PerformanceStatistics.fromJson(data);
} catch (e) {
AppLogger.e(_tag, '获取性能统计信息失败', e);
// 返回空的性能统计对象
return const PerformanceStatistics(
averageLatency: 0.0,
medianLatency: 0.0,
p95Latency: 0.0,
p99Latency: 0.0,
averageThroughput: 0.0,
latencyTrends: [],
throughputTrends: [],
);
}
}
/// 获取趋势数据
@override
Future<Map<String, dynamic>> getTrends({
String? metric,
String? groupBy,
String? businessType,
String? model,
String? provider,
String interval = 'hour',
DateTime? startTime,
DateTime? endTime,
}) async {
try {
final queryParams = <String, dynamic>{
'interval': interval,
};
if (metric != null) queryParams['metric'] = metric;
if (groupBy != null) queryParams['groupBy'] = groupBy;
if (businessType != null) queryParams['businessType'] = businessType;
if (model != null) queryParams['model'] = model;
if (provider != null) queryParams['provider'] = provider;
if (startTime != null) queryParams['startTime'] = startTime.toIso8601String();
if (endTime != null) queryParams['endTime'] = endTime.toIso8601String();
final response = await _apiClient.getWithParams('/admin/llm-observability/statistics/trends', queryParameters: queryParams);
if (response is Map<String, dynamic> && response.containsKey('data')) {
return response['data'];
} else if (response is Map<String, dynamic>) {
return response;
} else {
throw ApiException(-1, '趋势数据响应格式错误');
}
} catch (e) {
AppLogger.e(_tag, '获取趋势数据失败', e);
rethrow;
}
}
// ==================== 系统管理 ====================
/// 导出LLM调用日志
@override
Future<List<LLMTrace>> exportTraces({
Map<String, dynamic>? filterCriteria,
}) async {
try {
AppLogger.d(_tag, '导出LLM调用日志: filterCriteria=$filterCriteria');
dynamic response;
try {
// 优先使用带过滤的高级导出端点
response = await _apiClient.post('/admin/llm-observability/export2', data: filterCriteria ?? {});
} catch (e) {
AppLogger.w(_tag, 'export2 不可用,回退到 export', e);
response = await _apiClient.post('/admin/llm-observability/export', data: filterCriteria ?? {});
}
if (response is Map<String, dynamic> && response.containsKey('data')) {
final List<dynamic> traces = response['data'];
return traces.map((trace) => LLMTrace.fromJson(trace)).toList();
} else if (response is List<dynamic>) {
return response.map((trace) => LLMTrace.fromJson(trace)).toList();
} else {
throw ApiException(-1, '导出日志响应格式错误');
}
} catch (e) {
AppLogger.e(_tag, '导出LLM调用日志失败', e);
rethrow;
}
}
/// 清理旧日志
@override
Future<Map<String, dynamic>> cleanupOldTraces(DateTime beforeTime) async {
try {
AppLogger.d(_tag, '清理旧日志: beforeTime=$beforeTime');
final response = await _apiClient.deleteWithParams('/admin/llm-observability/cleanup', queryParameters: {
'beforeTime': beforeTime.toIso8601String(),
});
if (response is Map<String, dynamic> && response.containsKey('data')) {
return response['data'];
} else if (response is Map<String, dynamic>) {
return response;
} else {
return {'deletedCount': 0};
}
} catch (e) {
AppLogger.e(_tag, '清理旧日志失败', e);
rethrow;
}
}
/// 获取系统健康状态
@override
Future<SystemHealthStatus> getSystemHealth() async {
try {
AppLogger.d(_tag, '获取系统健康状态');
final response = await _apiClient.getWithParams('/admin/llm-observability/health');
Map<String, dynamic> data;
if (response is Map<String, dynamic> && response.containsKey('data')) {
data = response['data'];
} else if (response is Map<String, dynamic>) {
data = response;
} else {
throw ApiException(-1, '系统健康状态响应格式错误');
}
return SystemHealthStatus.fromJson(data);
} catch (e) {
AppLogger.e(_tag, '获取系统健康状态失败', e);
// 返回默认的系统健康状态
return const SystemHealthStatus(
components: {},
status: HealthStatus.unknown,
);
}
}
/// 获取数据库状态
@override
Future<Map<String, dynamic>> getDatabaseStatus() async {
try {
AppLogger.d(_tag, '获取数据库状态');
final response = await _apiClient.getWithParams('/admin/llm-observability/database/status');
if (response is Map<String, dynamic> && response.containsKey('data')) {
return response['data'];
} else if (response is Map<String, dynamic>) {
return response;
} else {
throw ApiException(-1, '数据库状态响应格式错误');
}
} catch (e) {
AppLogger.e(_tag, '获取数据库状态失败', e);
rethrow;
}
}
}