Add LLM digest setup and share timestamp context
This commit is contained in:
@@ -0,0 +1,118 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
const String _llmDiagnosticsLogKey = 'llm_diagnostics_log_v1';
|
||||
const int _maxLlmDiagnosticsEvents = 40;
|
||||
const int _maxLlmDiagnosticsDetailChars = 8000;
|
||||
|
||||
class LlmDiagnosticsEvent {
|
||||
const LlmDiagnosticsEvent({
|
||||
required this.at,
|
||||
required this.stage,
|
||||
required this.message,
|
||||
this.details,
|
||||
});
|
||||
|
||||
factory LlmDiagnosticsEvent.fromJson(Map<String, dynamic> json) {
|
||||
return LlmDiagnosticsEvent(
|
||||
at: DateTime.parse(
|
||||
json['at'] as String? ?? DateTime.now().toUtc().toIso8601String(),
|
||||
).toLocal(),
|
||||
stage: json['stage'] as String? ?? 'unknown',
|
||||
message: json['message'] as String? ?? '',
|
||||
details: json['details'] as String?,
|
||||
);
|
||||
}
|
||||
|
||||
final DateTime at;
|
||||
final String stage;
|
||||
final String message;
|
||||
final String? details;
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return <String, dynamic>{
|
||||
'at': at.toUtc().toIso8601String(),
|
||||
'stage': stage,
|
||||
'message': message,
|
||||
if (details != null) 'details': details,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
class LlmDiagnosticsLog {
|
||||
const LlmDiagnosticsLog();
|
||||
|
||||
Future<List<LlmDiagnosticsEvent>> read() async {
|
||||
final SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||
final String? raw = prefs.getString(_llmDiagnosticsLogKey);
|
||||
if (raw == null || raw.isEmpty) {
|
||||
return const <LlmDiagnosticsEvent>[];
|
||||
}
|
||||
try {
|
||||
final List<dynamic> items = jsonDecode(raw) as List<dynamic>;
|
||||
return items
|
||||
.whereType<Map<String, dynamic>>()
|
||||
.map(LlmDiagnosticsEvent.fromJson)
|
||||
.toList(growable: false);
|
||||
} on FormatException {
|
||||
return const <LlmDiagnosticsEvent>[];
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> record(String stage, String message, {String? details}) async {
|
||||
final SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||
final List<LlmDiagnosticsEvent> current = await read();
|
||||
final List<LlmDiagnosticsEvent> next = <LlmDiagnosticsEvent>[
|
||||
LlmDiagnosticsEvent(
|
||||
at: DateTime.now(),
|
||||
stage: stage,
|
||||
message: message,
|
||||
details: _compactDetails(details),
|
||||
),
|
||||
...current,
|
||||
].take(_maxLlmDiagnosticsEvents).toList(growable: false);
|
||||
await prefs.setString(
|
||||
_llmDiagnosticsLogKey,
|
||||
jsonEncode(
|
||||
next
|
||||
.map((LlmDiagnosticsEvent event) => event.toJson())
|
||||
.toList(growable: false),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> clear() async {
|
||||
final SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||
await prefs.remove(_llmDiagnosticsLogKey);
|
||||
}
|
||||
|
||||
String exportText(List<LlmDiagnosticsEvent> events) {
|
||||
if (events.isEmpty) {
|
||||
return 'No LLM diagnostics recorded.';
|
||||
}
|
||||
return events
|
||||
.map((LlmDiagnosticsEvent event) {
|
||||
final String details = event.details == null
|
||||
? ''
|
||||
: '\n${event.details}';
|
||||
return '[${event.at.toIso8601String()}] ${event.stage}: ${event.message}$details';
|
||||
})
|
||||
.join('\n\n');
|
||||
}
|
||||
}
|
||||
|
||||
final Provider<LlmDiagnosticsLog> llmDiagnosticsLogProvider =
|
||||
Provider<LlmDiagnosticsLog>((Ref ref) => const LlmDiagnosticsLog());
|
||||
|
||||
String? _compactDetails(String? value) {
|
||||
final String? trimmed = value?.trim();
|
||||
if (trimmed == null || trimmed.isEmpty) {
|
||||
return null;
|
||||
}
|
||||
if (trimmed.length <= _maxLlmDiagnosticsDetailChars) {
|
||||
return trimmed;
|
||||
}
|
||||
return '${trimmed.substring(0, _maxLlmDiagnosticsDetailChars)}\n[truncated]';
|
||||
}
|
||||
Reference in New Issue
Block a user