Extract review logic into bot_actions module
Move `exec_review`, `download_git_diff`, and review formatting to a new `bot_actions::review` module. Update the review flow to post inline review comments via the Gitea API and simplify the comment markdown to a summary. Add diff formatting that preprocesses added lines with line numbers for the LLM prompt.
This commit is contained in:
+8
-107
@@ -1,14 +1,9 @@
|
||||
use futures_util::stream::TryStreamExt;
|
||||
use serde::Deserialize;
|
||||
use std::time::Duration;
|
||||
use tokio::io::AsyncReadExt;
|
||||
use tokio_util::io::StreamReader;
|
||||
|
||||
use crate::{
|
||||
consts::{BOT_PROCESS_MSG, MAX_DIFF_SIZE, REVIEW_PROMPT},
|
||||
env::EnvConfig,
|
||||
errors::AppError,
|
||||
gitea::{GiteaAPI, ReviewPayload, WebhookType},
|
||||
gitea::{GiteaAPI, WebhookType},
|
||||
open_router::OpenRouterClient,
|
||||
};
|
||||
|
||||
@@ -104,7 +99,13 @@ impl Bot {
|
||||
|
||||
pub async fn exec(&self, webhook: WebhookType) {
|
||||
let exec_result = match webhook {
|
||||
WebhookType::Review(review_payload) => self.exec_review(review_payload),
|
||||
WebhookType::Review(review_payload) => crate::bot_actions::review::exec_review(
|
||||
&self.gitea_api,
|
||||
&self.open_router_client,
|
||||
&self.http_client,
|
||||
&self.config.open_router_model,
|
||||
review_payload,
|
||||
),
|
||||
}
|
||||
.await;
|
||||
|
||||
@@ -113,104 +114,4 @@ impl Bot {
|
||||
Err(err) => println!("{}", err),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn exec_review(&self, review_payload: ReviewPayload) -> Result<(), AppError> {
|
||||
let new_comment = self
|
||||
.gitea_api
|
||||
.comment(
|
||||
&BOT_PROCESS_MSG.replace("{model}", &self.config.open_router_model),
|
||||
&review_payload.repository.full_name,
|
||||
review_payload.pull_request.number,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let bot_result: Result<ReviewResult, anyhow::Error> = async {
|
||||
let git_diff = self
|
||||
.download_git_diff(&review_payload.pull_request.diff_url)
|
||||
.await?;
|
||||
|
||||
let bot_request = REVIEW_PROMPT
|
||||
.replace("{subject}", &review_payload.pull_request.title)
|
||||
.replace("{comment}", &review_payload.comment.body)
|
||||
.replace("{diff}", &git_diff);
|
||||
|
||||
let chat_result = self.open_router_client.chat(&bot_request).await?;
|
||||
let mut review_result = serde_json::from_str::<ReviewResult>(&chat_result.message)?;
|
||||
|
||||
review_result.cost = chat_result.cost;
|
||||
|
||||
Ok(review_result)
|
||||
}
|
||||
.await;
|
||||
|
||||
let edit_msg = match bot_result {
|
||||
Ok(bot_result) => self.review_result_to_markdown(&bot_result),
|
||||
Err(e) => format!("Error while reviewing: {}", e),
|
||||
};
|
||||
|
||||
self.gitea_api
|
||||
.edit_comment(
|
||||
&edit_msg,
|
||||
&review_payload.repository.full_name,
|
||||
new_comment.id,
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn review_result_to_markdown(&self, review_result: &ReviewResult) -> String {
|
||||
if review_result.reviews.is_empty() {
|
||||
return String::from("No issues found. ✅");
|
||||
}
|
||||
|
||||
let mut md = String::from("## Review Feedback\n\n");
|
||||
for (i, item) in review_result.reviews.iter().enumerate() {
|
||||
if i > 0 {
|
||||
md.push_str("\n---\n\n");
|
||||
}
|
||||
md.push_str(&format!("### `{}`\n\n", item.filename));
|
||||
if let Some(line) = item.line {
|
||||
md.push_str(&format!("> **Line {}**\n\n", line));
|
||||
}
|
||||
if !item.code.is_empty() {
|
||||
let lang = lang_from_filename(&item.filename);
|
||||
md.push_str(&format!("```{}\n{}\n```\n\n", lang, item.code));
|
||||
}
|
||||
md.push_str(&item.message);
|
||||
md.push('\n');
|
||||
}
|
||||
|
||||
if !review_result.comment.is_empty() {
|
||||
md.push_str("\n---\n\n");
|
||||
md.push_str("### Summary\n\n");
|
||||
md.push_str(&review_result.comment);
|
||||
md.push('\n');
|
||||
}
|
||||
|
||||
if let Some(cost) = review_result.cost {
|
||||
md.push_str("\n---\n\n");
|
||||
md.push_str(&format!("### Cost: ${}", cost));
|
||||
md.push('\n');
|
||||
}
|
||||
|
||||
md
|
||||
}
|
||||
|
||||
async fn download_git_diff(&self, url: &str) -> anyhow::Result<String> {
|
||||
let response = self.http_client.get(url).send().await?;
|
||||
let stream = response.bytes_stream().map_err(std::io::Error::other);
|
||||
|
||||
let mut buf = Vec::with_capacity(MAX_DIFF_SIZE);
|
||||
StreamReader::new(stream)
|
||||
.take((MAX_DIFF_SIZE + 1) as u64)
|
||||
.read_to_end(&mut buf)
|
||||
.await?;
|
||||
|
||||
if buf.len() > MAX_DIFF_SIZE {
|
||||
anyhow::bail!("Git diff exceeds the maximum allowed size of 1 Mo");
|
||||
}
|
||||
|
||||
Ok(String::from_utf8_lossy(&buf).into_owned())
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user