Skip to content

Commit 14175d3

Browse files
committed
Improve context limits and PR comment fallbacks
1 parent 7e56cf2 commit 14175d3

File tree

5 files changed

+105
-13
lines changed

5 files changed

+105
-13
lines changed

.github/workflows/diffscope.yml

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -41,19 +41,38 @@ jobs:
4141
const fs = require('fs');
4242
const comments = JSON.parse(fs.readFileSync('comments.json', 'utf8'));
4343
const headSha = context.payload.pull_request.head.sha;
44+
const fallback = [];
4445
4546
for (const comment of comments) {
4647
if (!comment.file_path || !comment.line_number || comment.line_number < 1) {
4748
continue;
4849
}
49-
await github.rest.pulls.createReviewComment({
50+
try {
51+
await github.rest.pulls.createReviewComment({
52+
owner: context.repo.owner,
53+
repo: context.repo.repo,
54+
pull_number: context.issue.number,
55+
body: `**${comment.severity}**: ${comment.content}`,
56+
commit_id: headSha,
57+
path: comment.file_path,
58+
line: comment.line_number,
59+
side: "RIGHT"
60+
});
61+
} catch (error) {
62+
fallback.push(`- **${comment.severity}** ${comment.file_path}:${comment.line_number} ${comment.content}`);
63+
}
64+
}
65+
66+
if (fallback.length > 0) {
67+
const body = [
68+
"Some review comments could not be placed inline and are listed below:",
69+
"",
70+
...fallback.slice(0, 100)
71+
].join("\\n");
72+
await github.rest.issues.createComment({
5073
owner: context.repo.owner,
5174
repo: context.repo.repo,
52-
pull_number: context.issue.number,
53-
body: `**${comment.severity}**: ${comment.content}`,
54-
commit_id: headSha,
55-
path: comment.file_path,
56-
line: comment.line_number,
57-
side: "RIGHT"
75+
issue_number: context.issue.number,
76+
body
5877
});
5978
}

src/core/context.rs

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,10 @@ impl ContextFetcher {
5353
let end_idx = end.min(file_lines.len());
5454

5555
if start_idx < file_lines.len() {
56-
let chunk_content = file_lines[start_idx..end_idx].join("\n");
56+
let chunk_content = truncate_with_notice(
57+
file_lines[start_idx..end_idx].join("\n"),
58+
MAX_CONTEXT_CHARS,
59+
);
5760
chunks.push(LLMContextChunk {
5861
file_path: file_path.clone(),
5962
content: chunk_content,
@@ -106,6 +109,7 @@ impl ContextFetcher {
106109
.take(max_lines)
107110
.collect::<Vec<_>>()
108111
.join("\n");
112+
let snippet = truncate_with_notice(snippet, MAX_CONTEXT_CHARS);
109113
if snippet.trim().is_empty() {
110114
continue;
111115
}
@@ -153,7 +157,10 @@ impl ContextFetcher {
153157
// Extract a few lines around the definition for context
154158
let start_line = line_num.saturating_sub(2);
155159
let end_line = (line_num + 5).min(lines.len());
156-
let definition_content = lines[start_line..end_line].join("\n");
160+
let definition_content = truncate_with_notice(
161+
lines[start_line..end_line].join("\n"),
162+
MAX_CONTEXT_CHARS,
163+
);
157164

158165
chunks.push(LLMContextChunk {
159166
file_path: file_path.clone(),
@@ -194,6 +201,17 @@ fn merge_ranges(lines: &[(usize, usize)]) -> Vec<(usize, usize)> {
194201
merged
195202
}
196203

204+
const MAX_CONTEXT_CHARS: usize = 8000;
205+
206+
fn truncate_with_notice(mut content: String, max_chars: usize) -> String {
207+
if max_chars == 0 || content.len() <= max_chars {
208+
return content;
209+
}
210+
content.truncate(max_chars.saturating_sub(20));
211+
content.push_str("\n[Truncated]\n");
212+
content
213+
}
214+
197215
async fn read_file_lossy(path: &Path) -> Result<String> {
198216
match tokio::fs::read_to_string(path).await {
199217
Ok(content) => Ok(content),

src/core/prompt.rs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ pub struct PromptConfig {
88
pub user_prompt_template: String,
99
pub max_tokens: usize,
1010
pub include_context: bool,
11+
pub max_context_chars: usize,
1112
}
1213

1314
impl Default for PromptConfig {
@@ -50,6 +51,7 @@ Line 28: Performance - O(n²) algorithm for large dataset. Will be slow with man
5051
</instructions>"#.to_string(),
5152
max_tokens: 2000,
5253
include_context: true,
54+
max_context_chars: 20000,
5355
}
5456
}
5557
}
@@ -108,7 +110,7 @@ impl PromptBuilder {
108110
let mut output = String::new();
109111

110112
for chunk in chunks {
111-
output.push_str(&format!(
113+
let block = format!(
112114
"\n[{:?} - {}{}]\n{}\n",
113115
chunk.context_type,
114116
chunk.file_path.display(),
@@ -117,7 +119,14 @@ impl PromptBuilder {
117119
.map(|(s, e)| format!(":{}-{}", s, e))
118120
.unwrap_or_default(),
119121
chunk.content
120-
));
122+
);
123+
if self.config.max_context_chars > 0
124+
&& output.len().saturating_add(block.len()) > self.config.max_context_chars
125+
{
126+
output.push_str("\n[Context truncated]\n");
127+
break;
128+
}
129+
output.push_str(&block);
121130
}
122131

123132
Ok(output)

src/core/smart_review_prompt.rs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,8 @@ TAGS: [comma-separated relevant tags]
7474
context_chunks: &[LLMContextChunk],
7575
) -> Result<String> {
7676
let mut prompt = String::new();
77+
let mut context_chars = 0usize;
78+
const MAX_CONTEXT_CHARS: usize = 20000;
7779

7880
prompt.push_str(&format!(
7981
"Please review the following code changes in file: {}\n\n",
@@ -90,7 +92,7 @@ TAGS: [comma-separated relevant tags]
9092
chunk.file_path.display(),
9193
format!("{:?}", chunk.context_type)
9294
);
93-
prompt.push_str(&format!(
95+
let block = format!(
9496
"**{}** (lines {}-{}):\n```\n{}\n```\n\n",
9597
description,
9698
start_line,
@@ -101,7 +103,13 @@ TAGS: [comma-separated relevant tags]
101103
.take(20)
102104
.collect::<Vec<_>>()
103105
.join("\n")
104-
));
106+
);
107+
if context_chars.saturating_add(block.len()) > MAX_CONTEXT_CHARS {
108+
prompt.push_str("[Context truncated]\n\n");
109+
break;
110+
}
111+
prompt.push_str(&block);
112+
context_chars = context_chars.saturating_add(block.len());
105113
}
106114
}
107115

src/main.rs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -375,6 +375,7 @@ async fn review_command(
375375
}
376376
}
377377

378+
let comments = filter_comments_for_diff(&diff, comments);
378379
all_comments.extend(comments);
379380
}
380381
}
@@ -888,6 +889,7 @@ async fn review_diff_content_raw(
888889
}
889890
}
890891

892+
let comments = filter_comments_for_diff(&diff, comments);
891893
all_comments.extend(comments);
892894
}
893895
}
@@ -1308,6 +1310,7 @@ async fn smart_review_command(
13081310
}
13091311
}
13101312

1313+
let comments = filter_comments_for_diff(&diff, comments);
13111314
all_comments.extend(comments);
13121315
}
13131316
}
@@ -1777,6 +1780,41 @@ fn extract_symbols_from_diff(diff: &core::UnifiedDiff) -> Vec<String> {
17771780
symbols
17781781
}
17791782

1783+
fn filter_comments_for_diff(
1784+
diff: &core::UnifiedDiff,
1785+
comments: Vec<core::Comment>,
1786+
) -> Vec<core::Comment> {
1787+
let mut filtered = Vec::new();
1788+
let total = comments.len();
1789+
for comment in comments {
1790+
if is_line_in_diff(diff, comment.line_number) {
1791+
filtered.push(comment);
1792+
}
1793+
}
1794+
1795+
if filtered.len() != total {
1796+
let dropped = total.saturating_sub(filtered.len());
1797+
info!(
1798+
"Dropped {} comment(s) for {} due to unmatched line numbers",
1799+
dropped,
1800+
diff.file_path.display()
1801+
);
1802+
}
1803+
1804+
filtered
1805+
}
1806+
1807+
fn is_line_in_diff(diff: &core::UnifiedDiff, line_number: usize) -> bool {
1808+
if line_number == 0 {
1809+
return false;
1810+
}
1811+
diff.hunks.iter().any(|hunk| {
1812+
hunk.changes
1813+
.iter()
1814+
.any(|line| line.new_line_no == Some(line_number))
1815+
})
1816+
}
1817+
17801818
#[cfg(test)]
17811819
mod tests {
17821820
use super::*;

0 commit comments

Comments
 (0)