优化编辑工具调用的行号解析

This commit is contained in:
xintaofei
2026-03-28 15:46:52 +08:00
parent cb1c7211ef
commit c0b3e9aff9
8 changed files with 314 additions and 5 deletions

View File

@@ -53,6 +53,46 @@ fn strip_system_tags(text: &str) -> Option<String> {
}
/// Check if a JSONL entry is a system meta message (isMeta: true).
/// Rebuild a standard unified diff from `toolUseResult.structuredPatch`.
///
/// Each hunk in `structuredPatch` has `oldStart`, `oldLines`, `newStart`,
/// `newLines`, and `lines` (prefixed with ` `, `+`, or `-`).
fn rebuild_diff_from_structured_patch(
file_path: &str,
structured_patch: &serde_json::Value,
) -> Option<String> {
let hunks = structured_patch.as_array()?;
if hunks.is_empty() {
return None;
}
let mut output = String::new();
output.push_str(&format!("--- a/{}\n+++ b/{}\n", file_path, file_path));
for hunk in hunks {
let old_start = hunk.get("oldStart").and_then(|v| v.as_u64()).unwrap_or(1);
let old_lines = hunk.get("oldLines").and_then(|v| v.as_u64()).unwrap_or(0);
let new_start = hunk.get("newStart").and_then(|v| v.as_u64()).unwrap_or(1);
let new_lines = hunk.get("newLines").and_then(|v| v.as_u64()).unwrap_or(0);
output.push_str(&format!(
"@@ -{},{} +{},{} @@\n",
old_start, old_lines, new_start, new_lines
));
if let Some(lines) = hunk.get("lines").and_then(|v| v.as_array()) {
for line in lines {
if let Some(text) = line.as_str() {
output.push_str(text);
output.push('\n');
}
}
}
}
Some(output)
}
fn is_meta_message(value: &serde_json::Value) -> bool {
value
.get("isMeta")
@@ -489,7 +529,7 @@ impl ClaudeParser {
continue;
}
"user" => {
let content = extract_user_content(&value);
let mut content = extract_user_content(&value);
// Skip user messages that are empty after system tag stripping
if content.is_empty() {
@@ -518,6 +558,31 @@ impl ClaudeParser {
MessageRole::User
};
// Check toolUseResult.structuredPatch for real line numbers
if let Some(tur) = value.get("toolUseResult") {
if let Some(sp) = tur.get("structuredPatch") {
let fp = tur
.get("filePath")
.and_then(|v| v.as_str())
.unwrap_or("file");
if let Some(diff) = rebuild_diff_from_structured_patch(fp, sp) {
// Find the matching ToolResult in this user message's content
// and replace its output_preview with the real diff
for block in content.iter_mut() {
if let ContentBlock::ToolResult {
ref mut output_preview,
is_error: false,
..
} = block
{
*output_preview = Some(diff.clone());
break;
}
}
}
}
}
messages.push(UnifiedMessage {
id: uuid,
role,
@@ -738,6 +803,7 @@ impl ClaudeParser {
let mut turns = group_into_turns(messages);
super::relocate_orphaned_tool_results(&mut turns);
super::structurize_read_tool_output(&mut turns);
super::resolve_patch_line_numbers(&mut turns, cwd.as_deref());
let context_window_used_tokens = latest_claude_context_window_used_tokens(&turns);
let context_window_max_tokens =
claude_context_window_max_tokens_for_model(model.as_deref());