mirror of
https://github.com/gitui-org/gitui
synced 2026-05-23 00:48:35 +00:00
new crate that uses openai to summarize git diffs
This commit is contained in:
parent
7b7c5c4131
commit
51d8f96348
8 changed files with 271 additions and 1 deletions
100
Cargo.lock
generated
100
Cargo.lock
generated
|
|
@ -725,6 +725,16 @@ dependencies = [
|
|||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "git2-summarize"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"git2",
|
||||
"log",
|
||||
"openai-api-rs",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "git2-testing"
|
||||
version = "0.1.0"
|
||||
|
|
@ -1056,6 +1066,21 @@ dependencies = [
|
|||
"adler",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "minreq"
|
||||
version = "2.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cb3371dfc7b772c540da1380123674a8e20583aca99907087d990ca58cf44203"
|
||||
dependencies = [
|
||||
"log",
|
||||
"once_cell",
|
||||
"rustls",
|
||||
"rustls-webpki",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"webpki-roots",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mio"
|
||||
version = "0.8.5"
|
||||
|
|
@ -1142,6 +1167,17 @@ dependencies = [
|
|||
"pkg-config",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "openai-api-rs"
|
||||
version = "2.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ead8e910b541f342b445ab68dd52ae24d695abaf781aee2ae933a77ac29ea5de"
|
||||
dependencies = [
|
||||
"minreq",
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "openssl-probe"
|
||||
version = "0.1.5"
|
||||
|
|
@ -1382,6 +1418,20 @@ version = "0.7.4"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2"
|
||||
|
||||
[[package]]
|
||||
name = "ring"
|
||||
version = "0.17.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9babe80d5c16becf6594aa32ad2be8fe08498e7ae60b77de8df700e67f191d7e"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"getrandom",
|
||||
"libc",
|
||||
"spin",
|
||||
"untrusted",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ron"
|
||||
version = "0.8.0"
|
||||
|
|
@ -1413,6 +1463,28 @@ dependencies = [
|
|||
"windows-sys 0.45.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls"
|
||||
version = "0.21.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f9d5a6813c0759e4609cd494e8e725babae6a2ca7b62a5536a13daaec6fcb7ba"
|
||||
dependencies = [
|
||||
"log",
|
||||
"ring",
|
||||
"rustls-webpki",
|
||||
"sct",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls-webpki"
|
||||
version = "0.101.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765"
|
||||
dependencies = [
|
||||
"ring",
|
||||
"untrusted",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.14"
|
||||
|
|
@ -1447,6 +1519,16 @@ version = "1.0.3"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ddccb15bcce173023b3fedd9436f882a0739b8dfb45e4f6b6002bee5929f61b2"
|
||||
|
||||
[[package]]
|
||||
name = "sct"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414"
|
||||
dependencies = [
|
||||
"ring",
|
||||
"untrusted",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.156"
|
||||
|
|
@ -1579,6 +1661,12 @@ version = "1.11.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9"
|
||||
|
||||
[[package]]
|
||||
name = "spin"
|
||||
version = "0.9.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.10.0"
|
||||
|
|
@ -1783,6 +1871,12 @@ version = "0.1.11"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85"
|
||||
|
||||
[[package]]
|
||||
name = "untrusted"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
|
||||
|
||||
[[package]]
|
||||
name = "url"
|
||||
version = "2.5.0"
|
||||
|
|
@ -1882,6 +1976,12 @@ version = "0.2.84"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d"
|
||||
|
||||
[[package]]
|
||||
name = "webpki-roots"
|
||||
version = "0.25.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1778a42e8b3b90bff8d0f5032bf22250792889a5cdc752aa0020c84abe3aaf10"
|
||||
|
||||
[[package]]
|
||||
name = "which"
|
||||
version = "4.4.0"
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ trace-libgit = ["asyncgit/trace-libgit"]
|
|||
vendor-openssl = ["asyncgit/vendor-openssl"]
|
||||
|
||||
[workspace]
|
||||
members = ["asyncgit", "filetreelist", "git2-hooks", "git2-testing", "scopetime"]
|
||||
members = ["asyncgit", "filetreelist", "git2-hooks", "git2-summarize", "git2-testing", "scopetime"]
|
||||
|
||||
[profile.release]
|
||||
lto = true
|
||||
|
|
|
|||
19
git2-summarize/Cargo.toml
Normal file
19
git2-summarize/Cargo.toml
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
[package]
|
||||
name = "git2-summarize"
|
||||
version = "0.1.0"
|
||||
authors = ["extrawurst <mail@rusticorn.com>"]
|
||||
edition = "2021"
|
||||
description = "use openai gpt to summarize git2 diffs"
|
||||
homepage = "https://github.com/extrawurst/gitui"
|
||||
repository = "https://github.com/extrawurst/gitui"
|
||||
documentation = "https://docs.rs/git2-summarize/"
|
||||
readme = "README.md"
|
||||
license = "MIT"
|
||||
categories = ["development-tools"]
|
||||
keywords = ["git"]
|
||||
|
||||
[dependencies]
|
||||
git2 = ">=0.17"
|
||||
log = "0.4"
|
||||
thiserror = "1.0"
|
||||
openai-api-rs = "2.1"
|
||||
1
git2-summarize/LICENSE.md
Symbolic link
1
git2-summarize/LICENSE.md
Symbolic link
|
|
@ -0,0 +1 @@
|
|||
../LICENSE.md
|
||||
3
git2-summarize/README.md
Normal file
3
git2-summarize/README.md
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
# git2-summarize
|
||||
|
||||
this uses open ai chat-gpt to summarize a git diff
|
||||
49
git2-summarize/examples/simple.diff
Normal file
49
git2-summarize/examples/simple.diff
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
diff --git a/git2-hooks/src/lib.rs b/git2-hooks/src/lib.rs
|
||||
index 0cf5ac8..042ac87 100644
|
||||
--- a/git2-hooks/src/lib.rs
|
||||
+++ b/git2-hooks/src/lib.rs
|
||||
@@ -27,6 +27,7 @@ use git2::Repository;
|
||||
pub const HOOK_POST_COMMIT: &str = "post-commit";
|
||||
pub const HOOK_PRE_COMMIT: &str = "pre-commit";
|
||||
pub const HOOK_COMMIT_MSG: &str = "commit-msg";
|
||||
+pub const HOOK_PRE_PUSH: &str = "pre-push";
|
||||
|
||||
const HOOK_COMMIT_MSG_TEMP_FILE: &str = "COMMIT_EDITMSG";
|
||||
|
||||
@@ -152,6 +153,36 @@ pub fn hooks_post_commit(
|
||||
hook.run_hook(&[])
|
||||
}
|
||||
|
||||
+/// see [`hooks_pre_push`]
|
||||
+pub enum PrePushHookLocalRef<'a> {
|
||||
+ Delete,
|
||||
+ Ref {
|
||||
+ local_ref: &'a str,
|
||||
+ local_obj_name: &'a str,
|
||||
+ },
|
||||
+}
|
||||
+
|
||||
+/// see https://git-scm.com/docs/githooks#_pre_push
|
||||
+///
|
||||
+/// # Arguments
|
||||
+///
|
||||
+/// * `remote_obj_name` - pass `None` if foreign ref not yet exists
|
||||
+pub fn hooks_pre_push(
|
||||
+ repo: &Repository,
|
||||
+ other_paths: Option<&[&str]>,
|
||||
+ local_ref: PrePushHookLocalRef,
|
||||
+ remote_ref: &str,
|
||||
+ remote_obj_name: Option<&str>,
|
||||
+) -> Result<HookResult> {
|
||||
+ let hook = HookPaths::new(repo, other_paths, HOOK_PRE_PUSH)?;
|
||||
+
|
||||
+ if !hook.found() {
|
||||
+ return Ok(HookResult::NoHookFound);
|
||||
+ }
|
||||
+
|
||||
+ hook.run_hook(&[])
|
||||
+}
|
||||
+
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
13
git2-summarize/examples/simple.rs
Normal file
13
git2-summarize/examples/simple.rs
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
use std::env;
|
||||
|
||||
fn main() {
|
||||
let diff = include_str!("simple.diff");
|
||||
|
||||
let summary = git2_summarize::git_diff_summarize_old(
|
||||
&env::var("OPENAI_API_KEY").unwrap(),
|
||||
diff,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
println!("{summary}");
|
||||
}
|
||||
85
git2-summarize/src/lib.rs
Normal file
85
git2-summarize/src/lib.rs
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
//! Uses Open API GPT-3 to summarize unified git diffs
|
||||
|
||||
use openai_api_rs::v1::{
|
||||
api::Client,
|
||||
chat_completion::{self, ChatCompletionRequest},
|
||||
common::GPT3_5_TURBO,
|
||||
completion::{self, CompletionRequest},
|
||||
};
|
||||
|
||||
/// Uses old GPT3_TEXT_DAVINCI_003 model to generate message
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `api_key` - open api key
|
||||
/// * `diff` - expects a diff formatted as a unified diff
|
||||
pub fn git_diff_summarize_old(
|
||||
api_key: &str,
|
||||
diff: &str,
|
||||
) -> Result<String, String> {
|
||||
let client = Client::new(api_key.to_string());
|
||||
|
||||
let req = CompletionRequest::new(
|
||||
completion::GPT3_TEXT_DAVINCI_003.to_string(),
|
||||
format!("Generate a Git commit message based on the following summary: {}\n\nCommit message: ",diff),
|
||||
)
|
||||
.max_tokens(500)
|
||||
.temperature(0.5)
|
||||
.n(1);
|
||||
|
||||
let result = client.completion(req).map_err(|e| e.message)?;
|
||||
Ok(result
|
||||
.choices
|
||||
.get(0)
|
||||
.ok_or_else(|| String::from("choises empty"))?
|
||||
.text
|
||||
.clone())
|
||||
}
|
||||
|
||||
/// Uses GPT3_5_TURBO model to generate message using chat completion API
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `api_key` - open api key
|
||||
/// * `diff` - expects a diff formatted as a unified diff
|
||||
pub fn git_diff_summarize(
|
||||
api_key: &str,
|
||||
diff: &str,
|
||||
line_length: usize,
|
||||
) -> Result<String, String> {
|
||||
let client = Client::new(api_key.to_string());
|
||||
|
||||
let prompt = format!(
|
||||
r#"You are a smart git commit message creator software.
|
||||
Now you are going to create a git commit message.
|
||||
The commit messages you generate aim to explain why the changes were introduced.
|
||||
Write a one-sentence message no longer than {line_length} characters, followed by two newline characters.
|
||||
Create a commit message for these changes:\n{}
|
||||
"#,
|
||||
diff
|
||||
);
|
||||
|
||||
let req = ChatCompletionRequest::new(
|
||||
GPT3_5_TURBO.to_string(),
|
||||
vec![chat_completion::ChatCompletionMessage {
|
||||
role: chat_completion::MessageRole::system,
|
||||
content: prompt,
|
||||
name: None,
|
||||
function_call: None,
|
||||
}],
|
||||
)
|
||||
.max_tokens(200);
|
||||
|
||||
let result =
|
||||
client.chat_completion(req).map_err(|e| e.message)?;
|
||||
|
||||
Ok(result
|
||||
.choices
|
||||
.get(0)
|
||||
.ok_or_else(|| String::from("response.choises empty"))?
|
||||
.message
|
||||
.content
|
||||
.as_ref()
|
||||
.ok_or_else(|| String::from("choise[0].message empty"))?
|
||||
.clone())
|
||||
}
|
||||
Loading…
Reference in a new issue