mergeCacheWithNewStats method
PersistedStatsCache
mergeCacheWithNewStats({
- required PersistedStatsCache existingCache,
- required ProcessedStats newStats,
- required String newLastComputedDate,
Merge new stats into an existing cache. Used when incrementally adding new days to the cache.
Implementation
PersistedStatsCache mergeCacheWithNewStats({
required PersistedStatsCache existingCache,
required ProcessedStats newStats,
required String newLastComputedDate,
}) {
// Merge daily activity — combine by date.
final dailyActivityMap = <String, DailyActivity>{};
for (final day in existingCache.dailyActivity) {
dailyActivityMap[day.date] = day.copyWith();
}
for (final day in newStats.dailyActivity) {
final existing = dailyActivityMap[day.date];
if (existing != null) {
existing.messageCount += day.messageCount;
existing.sessionCount += day.sessionCount;
existing.toolCallCount += day.toolCallCount;
} else {
dailyActivityMap[day.date] = day.copyWith();
}
}
// Merge daily model tokens — combine by date.
final dailyModelTokensMap = <String, Map<String, int>>{};
for (final day in existingCache.dailyModelTokens) {
dailyModelTokensMap[day.date] = Map<String, int>.from(day.tokensByModel);
}
for (final day in newStats.dailyModelTokens) {
final existing = dailyModelTokensMap[day.date];
if (existing != null) {
for (final entry in day.tokensByModel.entries) {
existing[entry.key] = (existing[entry.key] ?? 0) + entry.value;
}
} else {
dailyModelTokensMap[day.date] = Map<String, int>.from(
day.tokensByModel,
);
}
}
// Merge model usage.
final modelUsage = Map<String, ModelUsage>.from(existingCache.modelUsage);
for (final entry in newStats.modelUsage.entries) {
final existing = modelUsage[entry.key];
if (existing != null) {
modelUsage[entry.key] = existing.merge(entry.value);
} else {
modelUsage[entry.key] = entry.value;
}
}
// Merge hour counts.
final hourCounts = Map<int, int>.from(existingCache.hourCounts);
for (final entry in newStats.hourCounts.entries) {
hourCounts[entry.key] = (hourCounts[entry.key] ?? 0) + entry.value;
}
// Update session aggregates.
final totalSessions =
existingCache.totalSessions + newStats.sessionStats.length;
final totalMessages =
existingCache.totalMessages +
newStats.sessionStats.fold<int>(0, (sum, s) => sum + s.messageCount);
// Find longest session.
SessionStats? longestSession = existingCache.longestSession;
for (final session in newStats.sessionStats) {
if (longestSession == null ||
session.duration > longestSession.duration) {
longestSession = session;
}
}
// Find first session date.
String? firstSessionDate = existingCache.firstSessionDate;
for (final session in newStats.sessionStats) {
if (firstSessionDate == null ||
session.timestamp.compareTo(firstSessionDate) < 0) {
firstSessionDate = session.timestamp;
}
}
final sortedDailyActivity = dailyActivityMap.values.toList()
..sort((a, b) => a.date.compareTo(b.date));
final sortedDailyModelTokens =
dailyModelTokensMap.entries
.map((e) => DailyModelTokens(date: e.key, tokensByModel: e.value))
.toList()
..sort((a, b) => a.date.compareTo(b.date));
var result = PersistedStatsCache(
version: statsCacheVersion,
lastComputedDate: newLastComputedDate,
dailyActivity: sortedDailyActivity,
dailyModelTokens: sortedDailyModelTokens,
modelUsage: modelUsage,
totalSessions: totalSessions,
totalMessages: totalMessages,
longestSession: longestSession,
firstSessionDate: firstSessionDate,
hourCounts: hourCounts,
totalSpeculationTimeSavedMs:
existingCache.totalSpeculationTimeSavedMs +
newStats.totalSpeculationTimeSavedMs,
);
if (shotStatsEnabled.value) {
final shotDistribution = Map<int, int>.from(
existingCache.shotDistribution ?? {},
);
for (final entry in (newStats.shotDistribution ?? {}).entries) {
shotDistribution[entry.key] =
(shotDistribution[entry.key] ?? 0) + entry.value;
}
result = result.copyWith(shotDistribution: shotDistribution);
}
return result;
}