Skip to content

Commit b886a51

Browse files
committed
refactor: split feedback example helpers
Separate example ranking from example selection so the feedback report can tune filtering and ordering without mixing the two concerns. Made-with: Cursor
1 parent db4a2c5 commit b886a51

4 files changed

Lines changed: 90 additions & 77 deletions

File tree

TODO.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@
7272
- [x] `src/commands/doctor/command/run.rs`: separate endpoint discovery, recommendation flow, and test helpers.
7373
- [x] `src/commands/eval/runner/matching.rs`: split required-match search, unexpected-match detection, and rule metric assembly.
7474
- [x] `src/commands/eval/runner/execute/loading.rs`: separate diff resolution from repo-path resolution if it grows again.
75-
- [ ] `src/commands/feedback_eval/report/examples.rs`: split ranking helpers from example builders.
75+
- [x] `src/commands/feedback_eval/report/examples.rs`: split ranking helpers from example builders.
7676
- [ ] `src/commands/doctor/system.rs`: carve environment probes vs output helpers.
7777

7878
### Commands backlog
Lines changed: 5 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -1,77 +1,6 @@
1-
use std::cmp::Ordering;
1+
#[path = "examples/build.rs"]
2+
mod build;
3+
#[path = "examples/ranking.rs"]
4+
mod ranking;
25

3-
use crate::review;
4-
5-
use super::super::{FeedbackEvalComment, FeedbackEvalExample};
6-
7-
const MAX_EXAMPLES: usize = 10;
8-
9-
pub(super) fn build_showcase_candidates(
10-
comments: &[FeedbackEvalComment],
11-
confidence_threshold: f32,
12-
) -> Vec<FeedbackEvalExample> {
13-
let mut examples = comments
14-
.iter()
15-
.filter(|comment| {
16-
comment.accepted
17-
&& !review::is_vague_comment_text(&comment.content)
18-
&& comment
19-
.confidence
20-
.map(|confidence| confidence >= confidence_threshold)
21-
.unwrap_or(true)
22-
})
23-
.map(FeedbackEvalExample::from)
24-
.collect::<Vec<_>>();
25-
examples.sort_by(compare_feedback_examples);
26-
examples.truncate(MAX_EXAMPLES);
27-
examples
28-
}
29-
30-
pub(super) fn build_vague_rejections(comments: &[FeedbackEvalComment]) -> Vec<FeedbackEvalExample> {
31-
let mut examples = comments
32-
.iter()
33-
.filter(|comment| !comment.accepted && review::is_vague_comment_text(&comment.content))
34-
.map(FeedbackEvalExample::from)
35-
.collect::<Vec<_>>();
36-
examples.sort_by(compare_feedback_examples);
37-
examples.truncate(MAX_EXAMPLES);
38-
examples
39-
}
40-
41-
fn compare_feedback_examples(left: &FeedbackEvalExample, right: &FeedbackEvalExample) -> Ordering {
42-
right
43-
.confidence
44-
.partial_cmp(&left.confidence)
45-
.unwrap_or(Ordering::Equal)
46-
.then_with(|| {
47-
severity_rank(right.severity.as_deref()).cmp(&severity_rank(left.severity.as_deref()))
48-
})
49-
.then_with(|| left.content.cmp(&right.content))
50-
}
51-
52-
fn severity_rank(severity: Option<&str>) -> usize {
53-
match severity.map(|value| value.to_ascii_lowercase()) {
54-
Some(value) if value == "error" => 3,
55-
Some(value) if value == "warning" => 2,
56-
Some(value) if value == "info" => 1,
57-
_ => 0,
58-
}
59-
}
60-
61-
impl From<&FeedbackEvalComment> for FeedbackEvalExample {
62-
fn from(comment: &FeedbackEvalComment) -> Self {
63-
Self {
64-
source_kind: comment.source_kind.clone(),
65-
review_id: comment.review_id.clone(),
66-
repo: comment.repo.clone(),
67-
pr_number: comment.pr_number,
68-
title: comment.title.clone(),
69-
file_path: comment.file_path.clone(),
70-
line_number: comment.line_number,
71-
category: comment.category.clone(),
72-
severity: comment.severity.clone(),
73-
confidence: comment.confidence,
74-
content: comment.content.clone(),
75-
}
76-
}
77-
}
6+
pub(super) use build::{build_showcase_candidates, build_vague_rejections};
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
use crate::review;
2+
3+
use super::super::super::{FeedbackEvalComment, FeedbackEvalExample};
4+
use super::ranking::rank_feedback_examples;
5+
6+
pub(in super::super) fn build_showcase_candidates(
7+
comments: &[FeedbackEvalComment],
8+
confidence_threshold: f32,
9+
) -> Vec<FeedbackEvalExample> {
10+
let mut examples = comments
11+
.iter()
12+
.filter(|comment| {
13+
comment.accepted
14+
&& !review::is_vague_comment_text(&comment.content)
15+
&& comment
16+
.confidence
17+
.map(|confidence| confidence >= confidence_threshold)
18+
.unwrap_or(true)
19+
})
20+
.map(FeedbackEvalExample::from)
21+
.collect::<Vec<_>>();
22+
rank_feedback_examples(&mut examples);
23+
examples
24+
}
25+
26+
pub(in super::super) fn build_vague_rejections(
27+
comments: &[FeedbackEvalComment],
28+
) -> Vec<FeedbackEvalExample> {
29+
let mut examples = comments
30+
.iter()
31+
.filter(|comment| !comment.accepted && review::is_vague_comment_text(&comment.content))
32+
.map(FeedbackEvalExample::from)
33+
.collect::<Vec<_>>();
34+
rank_feedback_examples(&mut examples);
35+
examples
36+
}
37+
38+
impl From<&FeedbackEvalComment> for FeedbackEvalExample {
39+
fn from(comment: &FeedbackEvalComment) -> Self {
40+
Self {
41+
source_kind: comment.source_kind.clone(),
42+
review_id: comment.review_id.clone(),
43+
repo: comment.repo.clone(),
44+
pr_number: comment.pr_number,
45+
title: comment.title.clone(),
46+
file_path: comment.file_path.clone(),
47+
line_number: comment.line_number,
48+
category: comment.category.clone(),
49+
severity: comment.severity.clone(),
50+
confidence: comment.confidence,
51+
content: comment.content.clone(),
52+
}
53+
}
54+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
use std::cmp::Ordering;
2+
3+
use super::super::super::FeedbackEvalExample;
4+
5+
const MAX_EXAMPLES: usize = 10;
6+
7+
pub(super) fn rank_feedback_examples(examples: &mut Vec<FeedbackEvalExample>) {
8+
examples.sort_by(compare_feedback_examples);
9+
examples.truncate(MAX_EXAMPLES);
10+
}
11+
12+
fn compare_feedback_examples(left: &FeedbackEvalExample, right: &FeedbackEvalExample) -> Ordering {
13+
right
14+
.confidence
15+
.partial_cmp(&left.confidence)
16+
.unwrap_or(Ordering::Equal)
17+
.then_with(|| {
18+
severity_rank(right.severity.as_deref()).cmp(&severity_rank(left.severity.as_deref()))
19+
})
20+
.then_with(|| left.content.cmp(&right.content))
21+
}
22+
23+
fn severity_rank(severity: Option<&str>) -> usize {
24+
match severity.map(|value| value.to_ascii_lowercase()) {
25+
Some(value) if value == "error" => 3,
26+
Some(value) if value == "warning" => 2,
27+
Some(value) if value == "info" => 1,
28+
_ => 0,
29+
}
30+
}

0 commit comments

Comments
 (0)