use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MemorySnapshot {
pub iteration: u32,
pub execution_history_len: usize,
pub execution_history_heap_bytes: usize,
pub checkpoint_count: u32,
pub timestamp: String,
}
impl MemorySnapshot {
#[must_use]
pub fn from_pipeline_state(state: &crate::reducer::PipelineState) -> Self {
let execution_history_heap_bytes = estimate_execution_history_heap_size(state);
Self {
iteration: state.iteration,
execution_history_len: state.execution_history_len(),
execution_history_heap_bytes,
checkpoint_count: state.checkpoint_saved_count,
timestamp: chrono::Utc::now().to_rfc3339(),
}
}
}
pub(super) fn estimate_execution_history_heap_size(state: &crate::reducer::PipelineState) -> usize {
use crate::checkpoint::execution_history::StepOutcome;
state
.execution_history()
.iter()
.map(|step| {
let modified_files_detail_size = step.modified_files_detail.as_ref().map_or(0, |d| {
let sum_list = |xs: &Option<Box<[String]>>| {
xs.as_ref()
.map_or(0, |v| v.iter().map(std::string::String::len).sum::<usize>())
};
sum_list(&d.added) + sum_list(&d.modified) + sum_list(&d.deleted)
});
let issues_summary_size = step
.issues_summary
.as_ref()
.and_then(|s| s.description.as_ref())
.map_or(0, std::string::String::len);
let base_size = step
.phase
.len()
.saturating_add(step.step_type.len())
.saturating_add(step.timestamp.len())
.saturating_add(step.agent.as_ref().map_or(0, |s| s.len()))
.saturating_add(
step.checkpoint_saved_at
.as_ref()
.map_or(0, std::string::String::len),
)
.saturating_add(
step.git_commit_oid
.as_ref()
.map_or(0, std::string::String::len),
)
.saturating_add(
step.prompt_used
.as_ref()
.map_or(0, std::string::String::len),
)
.saturating_add(modified_files_detail_size)
.saturating_add(issues_summary_size);
let outcome_size = match &step.outcome {
StepOutcome::Success {
output,
files_modified,
..
} => output.as_ref().map_or(0, |s| s.len()).saturating_add(
files_modified.as_ref().map_or(0, |files| {
files.iter().map(std::string::String::len).sum::<usize>()
}),
),
StepOutcome::Failure { error, signals, .. } => {
error
.len()
.saturating_add(signals.as_ref().map_or(0, |sigs| {
sigs.iter().map(std::string::String::len).sum::<usize>()
}))
}
StepOutcome::Partial {
completed,
remaining,
..
} => completed.len().saturating_add(remaining.len()),
StepOutcome::Skipped { reason } => reason.len(),
};
base_size.saturating_add(outcome_size)
})
.sum()
}