fn check_ci_thresholds(report: &HeadlessReport, config: &CbtopConfig) -> bool {
let mut passed = true;
if let Some(threshold) = config.throughput_threshold {
if report.throughput.tokens_per_sec < threshold {
eprintln!(
"cbtop: FAIL - Throughput {:.1} tok/s < threshold {:.1} tok/s",
report.throughput.tokens_per_sec, threshold
);
passed = false;
} else {
eprintln!(
"cbtop: PASS - Throughput {:.1} tok/s >= threshold {:.1} tok/s",
report.throughput.tokens_per_sec, threshold
);
}
}
if let Some(threshold) = config.brick_score_threshold {
let avg_score = if report.brick_scores.is_empty() {
0
} else {
report.brick_scores.iter().map(|b| b.score).sum::<u32>()
/ report.brick_scores.len() as u32
};
if avg_score < threshold {
eprintln!(
"cbtop: FAIL - Brick score {} < threshold {}",
avg_score, threshold
);
passed = false;
} else {
eprintln!(
"cbtop: PASS - Brick score {} >= threshold {}",
avg_score, threshold
);
}
}
passed
}
fn format_report_as_json(report: &HeadlessReport) -> String {
let brick_scores_json: String = report
.brick_scores
.iter()
.map(|b| {
format!(
r#" {{
"name": "{}",
"score": {},
"grade": "{}",
"budget_us": {:.2},
"actual_us": {:.2},
"gap_factor": {:.3}
}}"#,
b.name, b.score, b.grade, b.budget_us, b.actual_us, b.gap_factor
)
})
.collect::<Vec<_>>()
.join(",\n");
format!(
r#"{{
"model": "{}",
"timestamp": "{}",
"hardware": {{
"gpu": "{}",
"cpu": "{}",
"memory_gb": {}
}},
"throughput": {{
"tokens_per_sec": {:.2},
"ttft_ms": {:.2},
"cv_percent": {:.2},
"p50_us": {:.2},
"p99_us": {:.2}
}},
"brick_scores": [
{}
],
"pmat_scores": {{
"rust_project_score": {:.1},
"tdg_score": {:.1},
"cuda_tdg_score": {:.1},
"brick_score": {},
"grade": "{}"
}},
"falsification": {{
"total_points": {},
"passed": {},
"failed": {},
"blocked": {}
}},
"status": "{}",
"ci_result": "{}"
}}"#,
report.model,
report.timestamp,
report.hardware.gpu,
report.hardware.cpu,
report.hardware.memory_gb,
report.throughput.tokens_per_sec,
report.throughput.ttft_ms,
report.throughput.cv_percent,
report.throughput.p50_us,
report.throughput.p99_us,
brick_scores_json,
report.pmat_scores.rust_project_score,
report.pmat_scores.tdg_score,
report.pmat_scores.cuda_tdg_score,
report.pmat_scores.brick_score,
report.pmat_scores.grade,
report.falsification.total_points,
report.falsification.passed,
report.falsification.failed,
report.falsification.blocked,
report.status,
report.ci_result,
)
}
fn print_report_text(report: &HeadlessReport) {
println!("═══════════════════════════════════════════════════════════════");
println!(" cbtop Headless Benchmark Report");
println!("═══════════════════════════════════════════════════════════════");
println!(" Model: {}", report.model);
println!(" Timestamp: {}", report.timestamp);
println!();
println!(
" Throughput: {:.1} tok/s",
report.throughput.tokens_per_sec
);
println!(" TTFT: {:.2} ms", report.throughput.ttft_ms);
println!(" CV: {:.2}%", report.throughput.cv_percent);
println!();
println!(" Brick Scores:");
for brick in &report.brick_scores {
let status = if brick.gap_factor <= 1.0 {
"✅"
} else {
"❌"
};
println!(
" {} {:12} {:>3} ({}) - {:.1}µs / {:.1}µs ({:.2}x)",
status,
brick.name,
brick.score,
brick.grade,
brick.actual_us,
brick.budget_us,
brick.gap_factor
);
}
println!();
println!(
" Falsification: {}/{} passed",
report.falsification.passed, report.falsification.total_points
);
println!(" Status: {} | CI: {}", report.status, report.ci_result);
println!("═══════════════════════════════════════════════════════════════");
}
fn run_tui(model: Option<&str>, attach: Option<&str>) -> Result<()> {
if attach.is_some() {
eprintln!("Warning: --attach is not yet implemented for TUI mode. Flag ignored.");
}
enable_raw_mode()
.map_err(|e| CliError::ValidationFailed(format!("Failed to enable raw mode: {e}")))?;
let mut stdout = io::stdout();
execute!(stdout, EnterAlternateScreen, EnableMouseCapture)
.map_err(|e| CliError::ValidationFailed(format!("Failed to setup terminal: {e}")))?;
let backend = CrosstermBackend::new(stdout);
let mut terminal = Terminal::new(backend)
.map_err(|e| CliError::ValidationFailed(format!("Failed to create terminal: {e}")))?;
let mut app = App::new(model);
let res = run_app(&mut terminal, &mut app);
disable_raw_mode().ok();
execute!(
terminal.backend_mut(),
LeaveAlternateScreen,
DisableMouseCapture
)
.ok();
terminal.show_cursor().ok();
res
}
fn run_app<B: ratatui::backend::Backend>(terminal: &mut Terminal<B>, app: &mut App) -> Result<()> {
loop {
app.tick();
terminal
.draw(|f| ui(f, app))
.map_err(|e| CliError::ValidationFailed(format!("Failed to draw: {e}")))?;
if event::poll(std::time::Duration::from_millis(100))
.map_err(|e| CliError::ValidationFailed(format!("Event poll error: {e}")))?
{
if let Event::Key(key) = event::read()
.map_err(|e| CliError::ValidationFailed(format!("Event read error: {e}")))?
{
handle_cbtop_key(key, app);
}
}
if app.should_quit {
return Ok(());
}
}
}
fn handle_cbtop_key(key: crossterm::event::KeyEvent, app: &mut App) {
if key.kind != KeyEventKind::Press {
return;
}
match key.code {
KeyCode::Char('q') | KeyCode::Esc => app.should_quit = true,
KeyCode::Char('p') => app.current_view = View::Pipeline,
KeyCode::Char('b') => app.current_view = View::Budget,
KeyCode::Char('h') => app.current_view = View::Histogram,
KeyCode::Char('g') => app.current_view = View::Gpu,
KeyCode::Char('m') => app.current_view = View::Memory,
KeyCode::Down | KeyCode::Char('j') => app.next_brick(),
KeyCode::Up | KeyCode::Char('k') => app.prev_brick(),
_ => {}
}
}