first cut of horizontal partitioning
# Conflicts: # Cargo.lock # Conflicts: # Cargo.lock # crates/g3-cli/src/lib.rs
This commit is contained in:
240
crates/g3-ensembles/src/status.rs
Normal file
240
crates/g3-ensembles/src/status.rs
Normal file
@@ -0,0 +1,240 @@
|
||||
//! Status tracking for flock mode
|
||||
|
||||
use chrono::{DateTime, Utc};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
use std::path::PathBuf;
|
||||
|
||||
/// Status of an individual segment worker
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct SegmentStatus {
|
||||
/// Segment number
|
||||
pub segment_id: usize,
|
||||
|
||||
/// Segment workspace directory
|
||||
pub workspace: PathBuf,
|
||||
|
||||
/// Current state of the segment
|
||||
pub state: SegmentState,
|
||||
|
||||
/// Start time
|
||||
pub started_at: DateTime<Utc>,
|
||||
|
||||
/// Completion time (if finished)
|
||||
pub completed_at: Option<DateTime<Utc>>,
|
||||
|
||||
/// Total tokens used
|
||||
pub tokens_used: u64,
|
||||
|
||||
/// Number of tool calls made
|
||||
pub tool_calls: u64,
|
||||
|
||||
/// Number of errors encountered
|
||||
pub errors: u64,
|
||||
|
||||
/// Current turn number (for autonomous mode)
|
||||
pub current_turn: usize,
|
||||
|
||||
/// Maximum turns allowed
|
||||
pub max_turns: usize,
|
||||
|
||||
/// Last status message
|
||||
pub last_message: Option<String>,
|
||||
|
||||
/// Error message (if failed)
|
||||
pub error_message: Option<String>,
|
||||
}
|
||||
|
||||
/// State of a segment worker
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub enum SegmentState {
|
||||
/// Waiting to start
|
||||
Pending,
|
||||
|
||||
/// Currently running
|
||||
Running,
|
||||
|
||||
/// Completed successfully
|
||||
Completed,
|
||||
|
||||
/// Failed with error
|
||||
Failed,
|
||||
|
||||
/// Cancelled by user
|
||||
Cancelled,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for SegmentState {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
SegmentState::Pending => write!(f, "⏳ Pending"),
|
||||
SegmentState::Running => write!(f, "🔄 Running"),
|
||||
SegmentState::Completed => write!(f, "✅ Completed"),
|
||||
SegmentState::Failed => write!(f, "❌ Failed"),
|
||||
SegmentState::Cancelled => write!(f, "⚠️ Cancelled"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Overall flock status
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct FlockStatus {
|
||||
/// Flock session ID
|
||||
pub session_id: String,
|
||||
|
||||
/// Project directory
|
||||
pub project_dir: PathBuf,
|
||||
|
||||
/// Flock workspace directory
|
||||
pub flock_workspace: PathBuf,
|
||||
|
||||
/// Number of segments
|
||||
pub num_segments: usize,
|
||||
|
||||
/// Start time
|
||||
pub started_at: DateTime<Utc>,
|
||||
|
||||
/// Completion time (if finished)
|
||||
pub completed_at: Option<DateTime<Utc>>,
|
||||
|
||||
/// Status of each segment
|
||||
pub segments: HashMap<usize, SegmentStatus>,
|
||||
|
||||
/// Total tokens used across all segments
|
||||
pub total_tokens: u64,
|
||||
|
||||
/// Total tool calls across all segments
|
||||
pub total_tool_calls: u64,
|
||||
|
||||
/// Total errors across all segments
|
||||
pub total_errors: u64,
|
||||
}
|
||||
|
||||
impl FlockStatus {
|
||||
/// Create a new flock status
|
||||
pub fn new(
|
||||
session_id: String,
|
||||
project_dir: PathBuf,
|
||||
flock_workspace: PathBuf,
|
||||
num_segments: usize,
|
||||
) -> Self {
|
||||
Self {
|
||||
session_id,
|
||||
project_dir,
|
||||
flock_workspace,
|
||||
num_segments,
|
||||
started_at: Utc::now(),
|
||||
completed_at: None,
|
||||
segments: HashMap::new(),
|
||||
total_tokens: 0,
|
||||
total_tool_calls: 0,
|
||||
total_errors: 0,
|
||||
}
|
||||
}
|
||||
|
||||
/// Update segment status
|
||||
pub fn update_segment(&mut self, segment_id: usize, status: SegmentStatus) {
|
||||
self.segments.insert(segment_id, status);
|
||||
self.recalculate_totals();
|
||||
}
|
||||
|
||||
/// Recalculate total metrics
|
||||
fn recalculate_totals(&mut self) {
|
||||
self.total_tokens = self.segments.values().map(|s| s.tokens_used).sum();
|
||||
self.total_tool_calls = self.segments.values().map(|s| s.tool_calls).sum();
|
||||
self.total_errors = self.segments.values().map(|s| s.errors).sum();
|
||||
}
|
||||
|
||||
/// Check if all segments are complete
|
||||
pub fn is_complete(&self) -> bool {
|
||||
self.segments.len() == self.num_segments
|
||||
&& self.segments.values().all(|s| {
|
||||
matches!(
|
||||
s.state,
|
||||
SegmentState::Completed | SegmentState::Failed | SegmentState::Cancelled
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
/// Get count of segments by state
|
||||
pub fn count_by_state(&self, state: SegmentState) -> usize {
|
||||
self.segments.values().filter(|s| s.state == state).count()
|
||||
}
|
||||
|
||||
/// Save status to file
|
||||
pub fn save_to_file(&self, path: &PathBuf) -> anyhow::Result<()> {
|
||||
let json = serde_json::to_string_pretty(self)?;
|
||||
std::fs::write(path, json)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Load status from file
|
||||
pub fn load_from_file(path: &PathBuf) -> anyhow::Result<Self> {
|
||||
let json = std::fs::read_to_string(path)?;
|
||||
let status = serde_json::from_str(&json)?;
|
||||
Ok(status)
|
||||
}
|
||||
|
||||
/// Generate a summary report
|
||||
pub fn generate_report(&self) -> String {
|
||||
let mut report = String::new();
|
||||
|
||||
report.push_str(&format!("\n{}", "=".repeat(80)));
|
||||
report.push_str(&format!("\n📊 FLOCK MODE SESSION REPORT"));
|
||||
report.push_str(&format!("\n{}", "=".repeat(80)));
|
||||
|
||||
report.push_str(&format!("\n\n🆔 Session ID: {}", self.session_id));
|
||||
report.push_str(&format!("\n📁 Project: {}", self.project_dir.display()));
|
||||
report.push_str(&format!("\n🗂️ Workspace: {}", self.flock_workspace.display()));
|
||||
report.push_str(&format!("\n🔢 Segments: {}", self.num_segments));
|
||||
|
||||
let duration = if let Some(completed) = self.completed_at {
|
||||
completed.signed_duration_since(self.started_at)
|
||||
} else {
|
||||
Utc::now().signed_duration_since(self.started_at)
|
||||
};
|
||||
|
||||
report.push_str(&format!("\n⏱️ Duration: {:.2}s", duration.num_milliseconds() as f64 / 1000.0));
|
||||
|
||||
// Segment status summary
|
||||
report.push_str(&format!("\n\n📈 Segment Status:"));
|
||||
report.push_str(&format!("\n • Completed: {}", self.count_by_state(SegmentState::Completed)));
|
||||
report.push_str(&format!("\n • Running: {}", self.count_by_state(SegmentState::Running)));
|
||||
report.push_str(&format!("\n • Failed: {}", self.count_by_state(SegmentState::Failed)));
|
||||
report.push_str(&format!("\n • Pending: {}", self.count_by_state(SegmentState::Pending)));
|
||||
report.push_str(&format!("\n • Cancelled: {}", self.count_by_state(SegmentState::Cancelled)));
|
||||
|
||||
// Metrics
|
||||
report.push_str(&format!("\n\n📊 Aggregate Metrics:"));
|
||||
report.push_str(&format!("\n • Total Tokens: {}", self.total_tokens));
|
||||
report.push_str(&format!("\n • Total Tool Calls: {}", self.total_tool_calls));
|
||||
report.push_str(&format!("\n • Total Errors: {}", self.total_errors));
|
||||
|
||||
// Per-segment details
|
||||
report.push_str(&format!("\n\n🔍 Segment Details:"));
|
||||
let mut segments: Vec<_> = self.segments.iter().collect();
|
||||
segments.sort_by_key(|(id, _)| *id);
|
||||
|
||||
for (id, segment) in segments {
|
||||
report.push_str(&format!("\n\n Segment {}:", id));
|
||||
report.push_str(&format!("\n Status: {}", segment.state));
|
||||
report.push_str(&format!("\n Workspace: {}", segment.workspace.display()));
|
||||
report.push_str(&format!("\n Tokens: {}", segment.tokens_used));
|
||||
report.push_str(&format!("\n Tool Calls: {}", segment.tool_calls));
|
||||
report.push_str(&format!("\n Errors: {}", segment.errors));
|
||||
report.push_str(&format!("\n Turn: {}/{}", segment.current_turn, segment.max_turns));
|
||||
|
||||
if let Some(ref msg) = segment.last_message {
|
||||
report.push_str(&format!("\n Last Message: {}", msg));
|
||||
}
|
||||
|
||||
if let Some(ref err) = segment.error_message {
|
||||
report.push_str(&format!("\n Error: {}", err));
|
||||
}
|
||||
}
|
||||
|
||||
report.push_str(&format!("\n\n{}", "=".repeat(80)));
|
||||
|
||||
report
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user