Files
foxhunt/testing/integration/standalone/watch_tuning_progress_updated.rs
jgrusewski 9c3d741a08 refactor: restructure repo — crates/, bin/, testing/ layout
Move 17 library crates into crates/, CLI binary into bin/fxt,
consolidate 10 test crates into testing/, split config crate
from deployment config files.

Root directory reduced from 38+ to ~17 directories.
All Cargo.toml paths and build.rs proto refs updated.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 11:56:00 +01:00

159 lines
6.0 KiB
Rust

/// Watch tuning progress with live updates (poll every 5 seconds)
async fn watch_tuning_progress(
api_gateway_url: &str,
jwt_token: &str,
job_id: &str,
) -> AnyhowResult<()> {
use tokio::time::{sleep, Duration};
let mut last_trial: u32 = 0;
let mut iteration: u32 = 0;
// Validate job ID format once at the start
let job_id_uuid = Uuid::parse_str(job_id)
.context("❌ Invalid job ID format (expected UUID)")?;
// Create gRPC client once (reuse connection)
let mut client = MlTrainingServiceClient::connect(api_gateway_url.to_string())
.await
.context("Failed to connect to API Gateway")?;
loop {
iteration += 1;
// Create gRPC request with JWT metadata
let mut request = tonic::Request::new(GetTuningJobStatusRequest {
job_id: job_id_uuid.to_string(),
});
request.metadata_mut().insert(
"authorization",
format!("Bearer {}", jwt_token)
.parse()
.context("Failed to parse JWT token")?
);
// Execute gRPC call to get live status
let response = client
.get_tuning_job_status(request)
.await
.context("Failed to get tuning job status")?;
let status_response = response.into_inner();
// Calculate progress percentage
let progress_percent = if status_response.total_trials > 0 {
(status_response.current_trial as f32 / status_response.total_trials as f32) * 100.0
} else {
0.0
};
// Calculate elapsed time
let elapsed_seconds = if status_response.started_at > 0 {
chrono::Utc::now().timestamp() - status_response.started_at
} else {
0
};
// Extract best Sharpe ratio from metrics
let best_sharpe_ratio = status_response
.best_metrics
.get("sharpe_ratio")
.copied()
.unwrap_or(0.0);
// Format status string
let status_str = format_tuning_status(status_response.status());
// Clear previous output (move cursor up and clear lines)
if iteration > 1 {
// Clear the previous display (9 lines)
print!("\x1B[9A\x1B[J");
}
// Display rich progress UI
println!("┌─────────────────────────────────────────────────────────┐");
println!("{} Tuning Job: {}", "🎯".bright_cyan(), job_id.chars().take(8).collect::<String>());
println!("├─────────────────────────────────────────────────────────┤");
println!("│ Trials: {}/{} ({:.1}%) │",
status_response.current_trial,
status_response.total_trials,
progress_percent
);
println!("{} Best Sharpe Ratio: {}",
"🏆".bright_yellow(),
format!("{:.4}", best_sharpe_ratio).bright_green()
);
// Show trial progress indicator if trial changed
if status_response.current_trial > last_trial {
println!("{} Current Trial #{}: Running... │",
"🔄".bright_blue(),
status_response.current_trial
);
last_trial = status_response.current_trial;
} else {
println!("{} Status: {}",
"📊".bright_white(),
format_status_colored(&status_str)
);
}
// Progress bar
let progress_bar = create_progress_bar(progress_percent);
println!("{}", progress_bar);
// Elapsed time
let elapsed_minutes = elapsed_seconds / 60;
let elapsed_seconds_remainder = elapsed_seconds % 60;
println!("│ ⏱️ Elapsed: {}m {}s │",
elapsed_minutes, elapsed_seconds_remainder
);
println!("└─────────────────────────────────────────────────────────┘");
// Check if job is complete
match status_response.status() {
TuningJobStatus::TuningCompleted => {
println!("\n✅ Tuning job completed successfully!");
println!(" Best Sharpe Ratio: {}", format!("{:.4}", best_sharpe_ratio).bright_green());
println!("\n💡 Get best parameters with:");
println!(" tli tune best --job-id {}", job_id);
break;
}
TuningJobStatus::TuningFailed => {
println!("\n❌ Tuning job failed!");
if !status_response.message.is_empty() {
println!(" Error: {}", status_response.message);
}
break;
}
TuningJobStatus::TuningStopped => {
println!("\n🛑 Tuning job stopped by user");
println!("\n💡 Get partial results with:");
println!(" tli tune best --job-id {}", job_id);
break;
}
_ => {
// Still running, continue polling
}
}
// Wait 5 seconds before next poll
sleep(Duration::from_secs(5)).await;
}
Ok(())
}
/// Format tuning job status as string
fn format_tuning_status(status: TuningJobStatus) -> String {
match status {
TuningJobStatus::TuningUnknown => "UNKNOWN",
TuningJobStatus::TuningPending => "PENDING",
TuningJobStatus::TuningRunning => "RUNNING",
TuningJobStatus::TuningCompleted => "COMPLETED",
TuningJobStatus::TuningFailed => "FAILED",
TuningJobStatus::TuningStopped => "STOPPED",
}.to_string()
}