speccoder_runtime/pipeline/
samples.rs1use crate::worktree;
4
5pub(crate) fn sample_summary(
7 report: &speccoder_core::RunReport,
8 status: &str,
9 patch: &str,
10) -> serde_json::Value {
11 use speccoder_core::verify::Verdict;
12 let rank = match (status, &report.verdict) {
13 ("completed", Some(Verdict::Verified)) => 2,
14 ("completed", Some(Verdict::RegressionOnly)) => 1,
15 _ => 0,
16 };
17 let changed = patch
18 .lines()
19 .filter(|l| {
20 (l.starts_with('+') && !l.starts_with("+++"))
21 || (l.starts_with('-') && !l.starts_with("---"))
22 })
23 .count();
24 let tokens = report.usage.as_ref().map_or(0, |u| {
25 u["input_tokens"].as_u64().unwrap_or(0) + u["output_tokens"].as_u64().unwrap_or(0)
26 });
27 serde_json::json!({
28 "run_id": report.run_id,
29 "attempt_status": status,
30 "verdict": report.verdict,
31 "rank": rank,
32 "survivors": report.mutation.as_ref().and_then(|m| m["missed"].as_u64()),
33 "changed_lines": changed,
34 "tokens": tokens,
35 })
36}
37
38pub(crate) fn best_sample(
41 samples: &[(
42 speccoder_core::RunReport,
43 String,
44 worktree::Checkpoint,
45 serde_json::Value,
46 )],
47) -> usize {
48 let key = |s: &serde_json::Value| {
49 (
50 std::cmp::Reverse(s["rank"].as_u64().unwrap_or(0)),
51 s["survivors"].as_u64().unwrap_or(0),
52 s["changed_lines"].as_u64().unwrap_or(0),
53 s["tokens"].as_u64().unwrap_or(0),
54 )
55 };
56 (0..samples.len())
57 .min_by_key(|&i| (key(&samples[i].3), i))
58 .unwrap_or(0)
59}