From 5984a26cdb6f539b24c71abc2f74e70c8fcb4b27 Mon Sep 17 00:00:00 2001 From: Michael Yuan Date: Tue, 25 Apr 2023 20:35:25 +0000 Subject: [PATCH 01/10] create playground Signed-off-by: Michael Yuan --- src/github-pr-summary.rs | 40 +++++++++++++++++----------------------- 1 file changed, 17 insertions(+), 23 deletions(-) diff --git a/src/github-pr-summary.rs b/src/github-pr-summary.rs index a304478..6b78ad1 100644 --- a/src/github-pr-summary.rs +++ b/src/github-pr-summary.rs @@ -5,15 +5,15 @@ use github_flows::{ octocrab::models::events::payload::{IssueCommentEventAction, PullRequestEventAction}, EventPayload, }; -use openai_flows::{chat_completion_default_key, ChatModel, ChatOptions}; +use openai_flows::{chat_completion, ChatModel, ChatOptions}; use std::env; // The soft character limit of the input context size // the max token size or word count for GPT4 is 8192 // the max token size or word count for GPT35Turbo is 4096 -static CHAR_SOFT_LIMIT : usize = 9000; -static MODEL : ChatModel = ChatModel::GPT35Turbo; -// static MODEL : ChatModel = ChatModel::GPT4; +static CHAR_SOFT_LIMIT : usize = 18000; +// static MODEL : ChatModel = ChatModel::GPT35Turbo; +static MODEL : ChatModel = ChatModel::GPT4; #[no_mangle] #[tokio::main(flavor = "current_thread")] @@ -47,19 +47,7 @@ async fn handler( trigger_phrase: &str, payload: EventPayload, ) { - let (title, pull_number, _contributor) = match payload { - EventPayload::PullRequestEvent(e) => { - if e.action != PullRequestEventAction::Opened { - write_error_log!("Not a Opened pull event"); - return; - } - let p = e.pull_request; - ( - p.title.unwrap_or("".to_string()), - p.number, - p.user.unwrap().login, - ) - } + let (pull_url, issue_number, _contributor) = match payload { EventPayload::IssueCommentEvent(e) => { if e.action == IssueCommentEventAction::Deleted { write_error_log!("Deleted issue event"); @@ -87,8 +75,14 @@ async fn handler( _ => return, }; + let pull_url_components : Vec<&str> = pull_url.split("/").collect(); + if pull_url_components.len() < 5 { return; } + let pull_number = pull_url_components[pull_url_components.len() - 1].parse::().unwrap(); + let pull_repo = pull_url_components[pull_url_components.len() - 3]; + let pull_owner = pull_url_components[pull_url_components.len() - 4]; + let octo = get_octo(Some(String::from(login))); - let pulls = octo.pulls(owner, repo); + let pulls = octo.pulls(pull_owner, pull_repo); let patch_as_text = pulls.get_patch(pull_number).await.unwrap(); let mut current_commit = String::new(); @@ -120,7 +114,7 @@ async fn handler( } let chat_id = format!("PR#{pull_number}"); - let system = &format!("You are an experienced software developer. You will act as a reviewer for a GitHub Pull Request titled \"{}\".", title); + let system = "You are an experienced software developer. You will act as a reviewer for a GitHub Pull Request."; let mut reviews: Vec = Vec::new(); let mut reviews_text = String::new(); for (_i, commit) in commits.iter().enumerate() { @@ -132,7 +126,7 @@ async fn handler( retry_times: 3, }; let question = "The following is a GitHub patch. Please summarize the key changes and identify potential problems. Start with the most important findings.\n\n".to_string() + truncate(commit, CHAR_SOFT_LIMIT); - if let Some(r) = chat_completion_default_key(&chat_id, &question, &co) { + if let Some(r) = chat_completion("gpt4", &chat_id, &question, &co) { if reviews_text.len() < CHAR_SOFT_LIMIT { reviews_text.push_str("------\n"); reviews_text.push_str(&r.choice); @@ -156,7 +150,7 @@ async fn handler( retry_times: 3, }; let question = "Here is a set of summaries for software source code patches. Each summary starts with a ------ line. Please write an overall summary considering all the individual summary. Please present the potential issues and errors first, following by the most important findings, in your summary.\n\n".to_string() + &reviews_text; - if let Some(r) = chat_completion_default_key(&chat_id, &question, &co) { + if let Some(r) = chat_completion("gpt4", &chat_id, &question, &co) { write_error_log!("Got the overall summary"); resp.push_str(&r.choice); resp.push_str("\n\n## Details\n\n"); @@ -168,8 +162,8 @@ async fn handler( // Send the entire response to GitHub PR let issues = octo.issues(owner, repo); - // issues.create_comment(pull_number, resp).await.unwrap(); - match issues.create_comment(pull_number, resp).await { + // issues.create_comment(issue_number, resp).await.unwrap(); + match issues.create_comment(issue_number, resp).await { Err(error) => { write_error_log!(format!("Error posting resp: {}", error)); } From 56d605d0895c4d97378c938bebcc9f407ab5f3b0 Mon Sep 17 00:00:00 2001 From: Michael Yuan Date: Tue, 25 Apr 2023 20:50:35 +0000 Subject: [PATCH 02/10] Add cc Signed-off-by: Michael Yuan --- src/github-pr-summary.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/github-pr-summary.rs b/src/github-pr-summary.rs index 6b78ad1..69f49aa 100644 --- a/src/github-pr-summary.rs +++ b/src/github-pr-summary.rs @@ -160,6 +160,10 @@ async fn handler( resp.push_str(review); } + resp.push_str("cc "); + resp.push_str(&pull_url); + resp.push_str("\n"); + // Send the entire response to GitHub PR let issues = octo.issues(owner, repo); // issues.create_comment(issue_number, resp).await.unwrap(); From 7004ee90067fa94970c74081ff3256bcdde762ac Mon Sep 17 00:00:00 2001 From: Michael Yuan Date: Tue, 25 Apr 2023 22:03:07 +0000 Subject: [PATCH 03/10] Add wait message Signed-off-by: Michael Yuan --- src/github-pr-summary.rs | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/github-pr-summary.rs b/src/github-pr-summary.rs index 69f49aa..4ca10a1 100644 --- a/src/github-pr-summary.rs +++ b/src/github-pr-summary.rs @@ -2,7 +2,8 @@ use dotenv::dotenv; use flowsnet_platform_sdk::write_error_log; use github_flows::{ get_octo, listen_to_event, - octocrab::models::events::payload::{IssueCommentEventAction, PullRequestEventAction}, + octocrab::models::events::payload::IssueCommentEventAction, + octocrab::models::CommentId, EventPayload, }; use openai_flows::{chat_completion, ChatModel, ChatOptions}; @@ -82,8 +83,19 @@ async fn handler( let pull_owner = pull_url_components[pull_url_components.len() - 4]; let octo = get_octo(Some(String::from(login))); + let issues = octo.issues(owner, repo); + let comment_id: CommentId; + match issues.create_comment(issue_number, "Hello, I am a [serverless review bot](https://github.com/flows-network/github-pr-summary/) on [flows.network](https://flows.network/).\n\nIt could take a few minutes for me to analyze this PR. Relax, grab a cup of coffee and check back later. Thanks!").await { + Ok(comment) => { + comment_id = comment.id; + } + Err(error) => { + write_error_log!(format!("Error posting comment: {}", error)); + return; + } + } + let pulls = octo.pulls(pull_owner, pull_repo); - let patch_as_text = pulls.get_patch(pull_number).await.unwrap(); let mut current_commit = String::new(); let mut commits: Vec = Vec::new(); @@ -165,9 +177,7 @@ async fn handler( resp.push_str("\n"); // Send the entire response to GitHub PR - let issues = octo.issues(owner, repo); - // issues.create_comment(issue_number, resp).await.unwrap(); - match issues.create_comment(issue_number, resp).await { + match issues.update_comment(comment_id, resp).await { Err(error) => { write_error_log!(format!("Error posting resp: {}", error)); } From 828e0d540befcb5d54509597b222fe0f0163575d Mon Sep 17 00:00:00 2001 From: Michael Yuan Date: Tue, 25 Apr 2023 22:12:29 +0000 Subject: [PATCH 04/10] Improve wording Signed-off-by: Michael Yuan --- src/github-pr-summary.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/github-pr-summary.rs b/src/github-pr-summary.rs index 4ca10a1..a348244 100644 --- a/src/github-pr-summary.rs +++ b/src/github-pr-summary.rs @@ -61,7 +61,7 @@ async fn handler( // return; // } // TODO: Makeshift but operational - if body.starts_with("Hello, I am a [serverless review bot]") { + if body.starts_with("Hello, I am a [code review bot]") { write_error_log!("Ignore comment via bot"); return; }; @@ -85,7 +85,7 @@ async fn handler( let octo = get_octo(Some(String::from(login))); let issues = octo.issues(owner, repo); let comment_id: CommentId; - match issues.create_comment(issue_number, "Hello, I am a [serverless review bot](https://github.com/flows-network/github-pr-summary/) on [flows.network](https://flows.network/).\n\nIt could take a few minutes for me to analyze this PR. Relax, grab a cup of coffee and check back later. Thanks!").await { + match issues.create_comment(issue_number, "Hello, I am a [code review bot](https://github.com/flows-network/github-pr-summary/) on [flows.network](https://flows.network/).\n\nIt could take a few minutes for me to analyze this PR. Relax, grab a cup of coffee and check back later. Thanks!").await { Ok(comment) => { comment_id = comment.id; } @@ -153,7 +153,7 @@ async fn handler( } let mut resp = String::new(); - resp.push_str("Hello, I am a [serverless review bot](https://github.com/flows-network/github-pr-summary/) on [flows.network](https://flows.network/). Here are my reviews of code commits in this PR.\n\n------\n\n"); + resp.push_str("Hello, I am a [code review bot](https://github.com/flows-network/github-pr-summary/) on [flows.network](https://flows.network/). Here are my reviews of code commits in this PR.\n\n------\n\n"); if reviews.len() > 1 { let co = ChatOptions { model: MODEL, From 3d22c0f2831cf3c8b4f7ec1a6ae11ef19f59257e Mon Sep 17 00:00:00 2001 From: Michael Yuan Date: Mon, 1 May 2023 06:46:23 +0000 Subject: [PATCH 05/10] Use new logger Signed-off-by: Michael Yuan --- Cargo.toml | 3 ++- src/github-pr-summary.rs | 20 ++++++++++++-------- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 8c88141..7d1e452 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,8 +14,9 @@ serde = { version = "1.0", features = ["derive"] } serde_json = "1.0.93" tokio_wasi = { version = "1.25.1", features = ["macros", "rt"] } anyhow = "1" -flowsnet-platform-sdk = "0.1.2" +flowsnet-platform-sdk = "0.1" lazy_static = "1.4.0" regex = "1.7.1" openai-flows = "0.4" words-count = "0.1.4" +log = "0.4" diff --git a/src/github-pr-summary.rs b/src/github-pr-summary.rs index a348244..1333173 100644 --- a/src/github-pr-summary.rs +++ b/src/github-pr-summary.rs @@ -1,5 +1,5 @@ use dotenv::dotenv; -use flowsnet_platform_sdk::write_error_log; +use flowsnet_platform_sdk::logger; use github_flows::{ get_octo, listen_to_event, octocrab::models::events::payload::IssueCommentEventAction, @@ -20,6 +20,7 @@ static MODEL : ChatModel = ChatModel::GPT4; #[tokio::main(flavor = "current_thread")] pub async fn run() -> anyhow::Result<()> { dotenv().ok(); + logger::init(); let login = env::var("login").unwrap_or("juntao".to_string()); let owner = env::var("owner").unwrap_or("juntao".to_string()); @@ -51,7 +52,7 @@ async fn handler( let (pull_url, issue_number, _contributor) = match payload { EventPayload::IssueCommentEvent(e) => { if e.action == IssueCommentEventAction::Deleted { - write_error_log!("Deleted issue event"); + log::info!("Deleted issue event"); return; } @@ -62,12 +63,12 @@ async fn handler( // } // TODO: Makeshift but operational if body.starts_with("Hello, I am a [code review bot]") { - write_error_log!("Ignore comment via bot"); + log::info!("Ignore comment via bot"); return; }; if !body.to_lowercase().contains(&trigger_phrase.to_lowercase()) { - write_error_log!(format!("Ignore the comment, raw: {}", body)); + log::info!("Ignore the comment without the magic words"); return; } @@ -90,7 +91,7 @@ async fn handler( comment_id = comment.id; } Err(error) => { - write_error_log!(format!("Error posting comment: {}", error)); + log::error!("Error posting comment: {}", error); return; } } @@ -121,7 +122,7 @@ async fn handler( } if commits.is_empty() { - write_error_log!("Cannot parse any commit from the patch file"); + log::error!("Cannot parse any commit from the patch file"); return; } @@ -149,6 +150,8 @@ async fn handler( review.push_str(&r.choice); review.push_str("\n\n"); reviews.push(review); + } else { + log::error!("OpenAI returned an error for commit {commit_hash}"); } } @@ -163,9 +166,10 @@ async fn handler( }; let question = "Here is a set of summaries for software source code patches. Each summary starts with a ------ line. Please write an overall summary considering all the individual summary. Please present the potential issues and errors first, following by the most important findings, in your summary.\n\n".to_string() + &reviews_text; if let Some(r) = chat_completion("gpt4", &chat_id, &question, &co) { - write_error_log!("Got the overall summary"); resp.push_str(&r.choice); resp.push_str("\n\n## Details\n\n"); + } else { + log::error!("OpenAI returned an error for the overall summary"); } } for (_i, review) in reviews.iter().enumerate() { @@ -179,7 +183,7 @@ async fn handler( // Send the entire response to GitHub PR match issues.update_comment(comment_id, resp).await { Err(error) => { - write_error_log!(format!("Error posting resp: {}", error)); + log::error!("Error posting resp: {}", error); } _ => {} } From 380e2ad6a2202337d112518971fbef8e4eb1d8a8 Mon Sep 17 00:00:00 2001 From: Michael Yuan Date: Mon, 1 May 2023 17:48:30 +0000 Subject: [PATCH 06/10] Use async openAI Signed-off-by: Michael Yuan --- Cargo.toml | 2 +- src/github-pr-summary.rs | 40 +++++++++++++++++++++++----------------- 2 files changed, 24 insertions(+), 18 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 7d1e452..f596811 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,6 +17,6 @@ anyhow = "1" flowsnet-platform-sdk = "0.1" lazy_static = "1.4.0" regex = "1.7.1" -openai-flows = "0.4" +openai-flows = "0.5" words-count = "0.1.4" log = "0.4" diff --git a/src/github-pr-summary.rs b/src/github-pr-summary.rs index 1333173..caf057d 100644 --- a/src/github-pr-summary.rs +++ b/src/github-pr-summary.rs @@ -139,19 +139,22 @@ async fn handler( retry_times: 3, }; let question = "The following is a GitHub patch. Please summarize the key changes and identify potential problems. Start with the most important findings.\n\n".to_string() + truncate(commit, CHAR_SOFT_LIMIT); - if let Some(r) = chat_completion("gpt4", &chat_id, &question, &co) { - if reviews_text.len() < CHAR_SOFT_LIMIT { - reviews_text.push_str("------\n"); - reviews_text.push_str(&r.choice); - reviews_text.push_str("\n"); + match chat_completion("gpt4", &chat_id, &question, &co).await { + Ok(r) => { + if reviews_text.len() < CHAR_SOFT_LIMIT { + reviews_text.push_str("------\n"); + reviews_text.push_str(&r.choice); + reviews_text.push_str("\n"); + } + let mut review = String::new(); + review.push_str(&format!("### [Commit {commit_hash}](https://github.com/WasmEdge/WasmEdge/pull/{pull_number}/commits/{commit_hash})\n")); + review.push_str(&r.choice); + review.push_str("\n\n"); + reviews.push(review); + } + Err(e) => { + log::error!("OpenAI returned an error for commit {commit_hash}: {}", e); } - let mut review = String::new(); - review.push_str(&format!("### [Commit {commit_hash}](https://github.com/WasmEdge/WasmEdge/pull/{pull_number}/commits/{commit_hash})\n")); - review.push_str(&r.choice); - review.push_str("\n\n"); - reviews.push(review); - } else { - log::error!("OpenAI returned an error for commit {commit_hash}"); } } @@ -165,11 +168,14 @@ async fn handler( retry_times: 3, }; let question = "Here is a set of summaries for software source code patches. Each summary starts with a ------ line. Please write an overall summary considering all the individual summary. Please present the potential issues and errors first, following by the most important findings, in your summary.\n\n".to_string() + &reviews_text; - if let Some(r) = chat_completion("gpt4", &chat_id, &question, &co) { - resp.push_str(&r.choice); - resp.push_str("\n\n## Details\n\n"); - } else { - log::error!("OpenAI returned an error for the overall summary"); + match chat_completion("gpt4", &chat_id, &question, &co).await { + Ok(r) => { + resp.push_str(&r.choice); + resp.push_str("\n\n## Details\n\n"); + } + Err(e) => { + log::error!("OpenAI returned an error for the overall summary: {}", e); + } } } for (_i, review) in reviews.iter().enumerate() { From a4fd8f8e494024615370551e75366255379ccd39 Mon Sep 17 00:00:00 2001 From: Michael Yuan Date: Sat, 6 May 2023 22:57:26 +0000 Subject: [PATCH 07/10] Use longer config var names Signed-off-by: Michael Yuan --- src/github-pr-summary.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/github-pr-summary.rs b/src/github-pr-summary.rs index caf057d..791c415 100644 --- a/src/github-pr-summary.rs +++ b/src/github-pr-summary.rs @@ -22,9 +22,9 @@ pub async fn run() -> anyhow::Result<()> { dotenv().ok(); logger::init(); - let login = env::var("login").unwrap_or("juntao".to_string()); - let owner = env::var("owner").unwrap_or("juntao".to_string()); - let repo = env::var("repo").unwrap_or("test".to_string()); + let login = env::var("github_login").unwrap_or("juntao".to_string()); + let owner = env::var("github_owner").unwrap_or("juntao".to_string()); + let repo = env::var("github_repo").unwrap_or("test".to_string()); let trigger_phrase = env::var("trigger_phrase").unwrap_or("flows summarize".to_string()); let events = vec!["pull_request", "issue_comment"]; From 442fa899c14b32ecffb1f9e13881993c1e79bac7 Mon Sep 17 00:00:00 2001 From: Michael Yuan Date: Mon, 8 May 2023 02:21:08 +0000 Subject: [PATCH 08/10] Optimize logging Signed-off-by: Michael Yuan --- src/github-pr-summary.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/github-pr-summary.rs b/src/github-pr-summary.rs index 791c415..bcbbe63 100644 --- a/src/github-pr-summary.rs +++ b/src/github-pr-summary.rs @@ -21,6 +21,7 @@ static MODEL : ChatModel = ChatModel::GPT4; pub async fn run() -> anyhow::Result<()> { dotenv().ok(); logger::init(); + log::debug!("Running github-pr-summary/playground"); let login = env::var("github_login").unwrap_or("juntao".to_string()); let owner = env::var("github_owner").unwrap_or("juntao".to_string()); @@ -52,9 +53,10 @@ async fn handler( let (pull_url, issue_number, _contributor) = match payload { EventPayload::IssueCommentEvent(e) => { if e.action == IssueCommentEventAction::Deleted { - log::info!("Deleted issue event"); + log::debug!("Deleted issue comment"); return; } + log::debug!("Other event for issue comment"); let body = e.comment.body.unwrap_or_default(); @@ -132,6 +134,7 @@ async fn handler( let mut reviews_text = String::new(); for (_i, commit) in commits.iter().enumerate() { let commit_hash = &commit[5..45]; + log::debug!("Sending patch to OpenAI: {}", commit_hash); let co = ChatOptions { model: MODEL, restart: true, @@ -151,6 +154,7 @@ async fn handler( review.push_str(&r.choice); review.push_str("\n\n"); reviews.push(review); + log::debug!("Received OpenAI resp for patch: {}", commit_hash); } Err(e) => { log::error!("OpenAI returned an error for commit {commit_hash}: {}", e); @@ -161,6 +165,7 @@ async fn handler( let mut resp = String::new(); resp.push_str("Hello, I am a [code review bot](https://github.com/flows-network/github-pr-summary/) on [flows.network](https://flows.network/). Here are my reviews of code commits in this PR.\n\n------\n\n"); if reviews.len() > 1 { + log::debug!("Sending all reviews to OpenAI for summarization"); let co = ChatOptions { model: MODEL, restart: true, @@ -172,6 +177,7 @@ async fn handler( Ok(r) => { resp.push_str(&r.choice); resp.push_str("\n\n## Details\n\n"); + log::debug!("Received the overall summary"); } Err(e) => { log::error!("OpenAI returned an error for the overall summary: {}", e); From aff5fd5fbbc7f0bcf401d16f948fc75c2bd62bd2 Mon Sep 17 00:00:00 2001 From: Michael Yuan Date: Mon, 8 May 2023 06:15:24 +0000 Subject: [PATCH 09/10] Redirect README Signed-off-by: Michael Yuan --- README.md | 105 +----------------------------------------------------- 1 file changed, 1 insertion(+), 104 deletions(-) diff --git a/README.md b/README.md index b74e4f2..6f2fb23 100644 --- a/README.md +++ b/README.md @@ -1,104 +1 @@ -#

ChatGPT/4 code reviewer for Github PR

- -

- - flows.network Discord - - - flows.network Twitter - - - Create a flow - -

- -[Deploy this function on flows.network](#deploy-the-pr-summary-app-for-your-github-repo), and you will get a GitHub 🤖 to review and summarize Pull Requests. It helps busy open source contributors understand and make decisions on PRs faster! A few examples below! - -* [[Rust] Improve support for host functions in the WasmEdge Rust SDK](https://github.com/WasmEdge/WasmEdge/pull/2394#issuecomment-1497819842) -* [[bash] Support ARM architecture in the WasmEdge installer](https://github.com/WasmEdge/WasmEdge/pull/1084#issuecomment-1497830324) -* [[C++] Add an eBPF plugin for WasmEdge](https://github.com/WasmEdge/WasmEdge/pull/2314#issuecomment-1497861516) -* [[Haskell] Improve the CLI utility for WasmEdge Component Model tooling](https://github.com/second-state/witc/pull/73#issuecomment-1507539260) - -> Still not convinced? [See "potential problems 1" in this review](https://github.com/second-state/wasmedge-quickjs/pull/82#issuecomment-1498299630), it identified an inefficient Rust implementation of an algorithm. 🤯 - -## How it works - -This flow function (or 🤖) will be triggered and executed when a new PR is raised in the designated GitHub repo. It can also be triggered again when someone says a magic "trigger phrase" in the PR's comments section. Once triggered, the flow function collects the content in the PR, and asks ChatGPT/4 to review and summarize it. The result is then posted back to the PR as a comment. The flow functions are written in Rust and runs in hosted [WasmEdge Runtimes](https://github.com/wasmedge) on [flows.network](https://flows.network/). - -The GitHub repo is connected to the flow function via the [flows.network](https://flows.network/) platform. The "trigger phrase" can also be configured in [flows.network](https://flows.network/). - -### Deploy your own code review bot in 3 simple steps - -1. Fork this repo to your own GitHub account. It contains the source code for the GitHub bot. -2. Import the forked repo on [flows.network](https://flows.network/) as a flow function. -3. Connect the flow function to the GitHub repo you want to deploy the bot on (using the [flows.network](https://flows.network/) UI). - -

-
- Click on the picture above to watch a 3-min tutorial video -

- -## Prerequisites - -You will need to bring your own [OpenAI API key](https://openai.com/blog/openai-api). If you do not already have one, [sign up here](https://platform.openai.com/signup). - -You will also need to sign into [flows.network](https://flows.network/) from your GitHub account. It is free. - -## Deploy the PR review 🤖 onto your GitHub repos - -The 🤖 is designed to run on [flows.network](https://flows.network/), a serverless platform for SaaS and AI automation. - -### 1 Fork this repo - -Fork [this repo](https://github.com/flows-network/github-pr-summary/) into your own GitHub account. - -> If your OpenAI API key has GPT4 access, you can change `GPT35Turbo` to `GPT4` in your fork of the source code. GPT4 provides substantially better code reviews, but it is also 10x more expensive. - -### 2 Deploy the bot's source code on flow.network - -Go to [flows.network](https://flows.network/) to deploy your own flow function (🤖) from the forked source code. - -1. Click on the "Create a Flow" button to start. -2. Authenticate the [flows.network](https://flows.network/) to access the `github-pr-summary` repo you just forked. **NOTE: This is NOT the repo you want to install the bot on.** -image - -3. Click on the "Advanced" link to see more settings. Fill in the following environment variables. - -> The 4 variables below are defined in the flow function's Rust source code. You can assign their values in the source code in your fork directly and skip the steps below. - -* `login`: Your personal GitHub id. The GitHub app will act as you when posting reviews. -* `owner`: GitHub org for the repo you want to deploy the 🤖 on. -* `repo` : GitHub repo you want to deploy the 🤖 on. -* `trigger_phrase`: The magic phrase to trigger a review from a PR comment. - -> Let's see an example. You forked the flow function source code to `my-name/github-pr-summary` and would like to deploy the bot to summarize PRs on `my-company/work-project` repo. Here `login = my-name`, `owner = my-company` and `repo = work-project`. - -image - -4. Click on the Deploy button. - -### 3 Configure integrations - -After that, [flows.network](https://flows.network/) will direct you to configure the external services required by your flow function 🤖. - -image - -For this flow function, we need to configue two integrations. - -1. Click on the "Connect" or "+ Add new authentication" button to add your OpenAI API key. - -image - -2. Click on the "Connect" or "+ Add new authentication" button to give the function access to the GitHub repo to deploy the 🤖. That is to give access to the `owner/repo` in the environment variables. You'll be redirected to a new page where you must grant [flows.network](https://flows.network/) permission to the repo. - -After that, click on the "Check" button to go to the flow details page. As soon as the flow's status became `running`, the PR summary GitHub bot is ready to give code reviews! The bot is summoned by every new PR or magic words (i.e., `trigger_phrase`) in PR comments. - -image - -## Credits - -This flow function is originally created by [Jay Chen](https://github.com/jaykchen), and [jinser](https://github.com/jetjinser) made significant contributions to optimize the event triggers from GitHub. - -

-GPT Nitro for Github PR - A ChatGPT-based reviewer for your GitHub pull requests | Product Hunt -

+# Please refer to README on the `main` branch From 44ac70d4165fce9161b4601065e23065b5c954d3 Mon Sep 17 00:00:00 2001 From: Michael Yuan Date: Tue, 9 May 2023 23:30:54 +0000 Subject: [PATCH 10/10] Update to the new SDKs Signed-off-by: Michael Yuan --- Cargo.toml | 4 ++-- src/github-pr-summary.rs | 24 +++++++++++++----------- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index f596811..66d5f1c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,7 @@ crate-type = ["cdylib"] [dependencies] dotenv = "0.15.0" -github-flows = "0.4.1" +github-flows = "0.5" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0.93" tokio_wasi = { version = "1.25.1", features = ["macros", "rt"] } @@ -17,6 +17,6 @@ anyhow = "1" flowsnet-platform-sdk = "0.1" lazy_static = "1.4.0" regex = "1.7.1" -openai-flows = "0.5" +openai-flows = "0.7" words-count = "0.1.4" log = "0.4" diff --git a/src/github-pr-summary.rs b/src/github-pr-summary.rs index bcbbe63..0e12ba2 100644 --- a/src/github-pr-summary.rs +++ b/src/github-pr-summary.rs @@ -4,9 +4,12 @@ use github_flows::{ get_octo, listen_to_event, octocrab::models::events::payload::IssueCommentEventAction, octocrab::models::CommentId, - EventPayload, + EventPayload, GithubLogin +}; +use openai_flows::{ + chat::{ChatModel, ChatOptions}, + OpenAIFlows, FlowsAccount, }; -use openai_flows::{chat_completion, ChatModel, ChatOptions}; use std::env; // The soft character limit of the input context size @@ -23,15 +26,13 @@ pub async fn run() -> anyhow::Result<()> { logger::init(); log::debug!("Running github-pr-summary/playground"); - let login = env::var("github_login").unwrap_or("juntao".to_string()); let owner = env::var("github_owner").unwrap_or("juntao".to_string()); let repo = env::var("github_repo").unwrap_or("test".to_string()); let trigger_phrase = env::var("trigger_phrase").unwrap_or("flows summarize".to_string()); let events = vec!["pull_request", "issue_comment"]; - listen_to_event(&login, &owner, &repo, events, |payload| { + listen_to_event(&GithubLogin::Default, &owner, &repo, events, |payload| { handler( - &login, &owner, &repo, &trigger_phrase, @@ -44,7 +45,6 @@ pub async fn run() -> anyhow::Result<()> { } async fn handler( - login: &str, owner: &str, repo: &str, trigger_phrase: &str, @@ -85,7 +85,7 @@ async fn handler( let pull_repo = pull_url_components[pull_url_components.len() - 3]; let pull_owner = pull_url_components[pull_url_components.len() - 4]; - let octo = get_octo(Some(String::from(login))); + let octo = get_octo(&GithubLogin::Default); let issues = octo.issues(owner, repo); let comment_id: CommentId; match issues.create_comment(issue_number, "Hello, I am a [code review bot](https://github.com/flows-network/github-pr-summary/) on [flows.network](https://flows.network/).\n\nIt could take a few minutes for me to analyze this PR. Relax, grab a cup of coffee and check back later. Thanks!").await { @@ -130,6 +130,10 @@ async fn handler( let chat_id = format!("PR#{pull_number}"); let system = "You are an experienced software developer. You will act as a reviewer for a GitHub Pull Request."; + let mut openai = OpenAIFlows::new(); + openai.set_flows_account(FlowsAccount::Provided("gpt4".to_string())); + openai.set_retry_times(3); + let mut reviews: Vec = Vec::new(); let mut reviews_text = String::new(); for (_i, commit) in commits.iter().enumerate() { @@ -139,10 +143,9 @@ async fn handler( model: MODEL, restart: true, system_prompt: Some(system), - retry_times: 3, }; let question = "The following is a GitHub patch. Please summarize the key changes and identify potential problems. Start with the most important findings.\n\n".to_string() + truncate(commit, CHAR_SOFT_LIMIT); - match chat_completion("gpt4", &chat_id, &question, &co).await { + match openai.chat_completion(&chat_id, &question, &co).await { Ok(r) => { if reviews_text.len() < CHAR_SOFT_LIMIT { reviews_text.push_str("------\n"); @@ -170,10 +173,9 @@ async fn handler( model: MODEL, restart: true, system_prompt: Some(system), - retry_times: 3, }; let question = "Here is a set of summaries for software source code patches. Each summary starts with a ------ line. Please write an overall summary considering all the individual summary. Please present the potential issues and errors first, following by the most important findings, in your summary.\n\n".to_string() + &reviews_text; - match chat_completion("gpt4", &chat_id, &question, &co).await { + match openai.chat_completion(&chat_id, &question, &co).await { Ok(r) => { resp.push_str(&r.choice); resp.push_str("\n\n## Details\n\n");