Skip to content

Commit

Permalink
Merge branch 'dev' into codex-init
Browse files Browse the repository at this point in the history
  • Loading branch information
jjranalli committed Apr 16, 2023
2 parents 34c6d96 + f7b4e3a commit ff2cac7
Show file tree
Hide file tree
Showing 10 changed files with 103 additions and 60 deletions.
3 changes: 2 additions & 1 deletion app/github/route.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { handleGithubAuth } from "@lib/handleGithubAuth"
import { replyIssueComment } from "@lib/replyIssueComment"
import { summarizePullRequest } from "@lib/summarizePullRequest"
import { codexCommand } from "@utils/constants"
import { NextRequest, NextResponse } from "next/server"

export const fetchCache = "force-no-store"
Expand All @@ -15,7 +16,7 @@ export async function POST(req: NextRequest) {

await summarizePullRequest(payload, octokit)
} else if (payload.action == "created") {
if (payload.comment.body.includes("/ask-codex")) {
if (payload.comment.body.includes(codexCommand)) {
// If a comment is created, reply to it
const octokit = await handleGithubAuth(payload)

Expand Down
7 changes: 0 additions & 7 deletions constants.json

This file was deleted.

12 changes: 7 additions & 5 deletions lib/joinStringsUntilMaxLength.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,24 @@
export function joinStringsUntilMaxLength(
parsedFiles: string[],
maxLength: number
): string {
let combinedString = ""
) {
let codeDiff = ""
let currentLength = 0
let maxLengthExceeded = false

for (const file of parsedFiles) {
const fileLength = file.length

if (currentLength + fileLength <= maxLength) {
combinedString += file
codeDiff += file
currentLength += fileLength
} else {
maxLengthExceeded = true
const remainingLength = maxLength - currentLength
combinedString += file.slice(0, remainingLength)
codeDiff += file.slice(0, remainingLength)
break
}
}

return combinedString
return { codeDiff, maxLengthExceeded }
}
62 changes: 45 additions & 17 deletions lib/replyIssueComment.ts
Original file line number Diff line number Diff line change
@@ -1,52 +1,80 @@
import { Octokit } from "@octokit/rest"
import { ChatCompletionRequestMessage } from "openai-streams"
import { codexCommand } from "../utils/constants"
import { generateChatGpt } from "../utils/generateChatGpt"
import { getCodeDiff } from "../utils/getCodeDiff"

export const startDescription = "\n\n<!-- start pr-codex -->"
export const endDescription = "<!-- end pr-codex -->"
const systemPrompt =
"You are a Git diff assistant. Given a code diff, you answer any question related to it. Be concise. Always wrap file names, functions, objects and similar in backticks (`)."
"You are a Git diff assistant. Given a code diff, you answer any question related to it. Be concise. Use line breaks and lists to improve readability. Always wrap file names, functions, objects and similar in backticks (`)."

export async function replyIssueComment(payload: any, octokit: Octokit) {
// Get relevant PR information
const { repository, issue, sender, comment } = payload
const { repository, issue, sender, comment, pull_request } = payload

const question = comment.body.split("/ask-codex")[1].trim()
const question = comment.body.split(codexCommand)[1].trim()

if (question) {
const { owner, repo, issue_number } = {
const { owner, repo, number, diff_hunk } = {
owner: repository.owner.login,
repo: repository.name,
issue_number: issue.number
number: issue?.number ?? pull_request.number,
diff_hunk: comment?.diff_hunk
}

// Get the diff content using Octokit and GitHub API
const { codeDiff } = await getCodeDiff(owner, repo, issue_number, octokit)
// Get the diff content
const { codeDiff } = diff_hunk
? { codeDiff: diff_hunk }
: await getCodeDiff(owner, repo, number, octokit)

// If there are changes, trigger workflow
if (codeDiff?.length != 0) {
const messages: ChatCompletionRequestMessage[] = [
{
role: "system",
content: systemPrompt
content: `${systemPrompt}\n\nHere is the code diff:\n\n${codeDiff}`
},
{
role: "user",
content: `${question}\n\nHere is the code diff:\n\n${codeDiff}`
content: `${question}`
}
]

const codexResponse = await generateChatGpt(messages)

const description = `> ${question}\n\n@${sender.login} ${codexResponse}`

await octokit.issues.createComment({
owner,
repo,
issue_number,
body: description
})
if (diff_hunk) {
const { commit_id, path, line, side, start_line, start_side, id } = {
commit_id: comment.commit_id,
path: comment.path,
line: comment.line,
side: comment.side,
start_line: comment.start_line,
start_side: comment.start_side,
id: comment.id
}

await octokit.pulls.createReviewComment({
owner,
repo,
pull_number: number,
body: description,
commit_id,
path,
line,
side,
start_line,
start_side,
in_reply_to: id
})
} else {
await octokit.issues.createComment({
owner,
repo,
issue_number: number,
body: description
})
}

return codexResponse
}
Expand Down
22 changes: 15 additions & 7 deletions lib/summarizePullRequest.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import { Octokit } from "@octokit/rest"
import { ChatCompletionRequestMessage } from "openai-streams"
import {
codexCommand,
endDescription,
startDescription
} from "../utils/constants"
import { generateChatGpt } from "../utils/generateChatGpt"
import { getCodeDiff } from "../utils/getCodeDiff"

export const startDescription = "\n\n<!-- start pr-codex -->"
export const endDescription = "<!-- end pr-codex -->"
const systemPrompt =
'You are a Git diff assistant. Always begin with "This PR". Given a code diff, you provide a simple description in prose, in less than 300 chars, which sums up the changes. Continue with "\n\n### Detailed summary\n" and make a comprehensive list of all changes, excluding any eventual skipped files. Be concise. Always wrap file names, functions, objects and similar in backticks (`).'
"You are a Git diff assistant. Given a code diff, you provide a clear and concise description of its content. Always wrap file names, functions, objects and similar in backticks (`)."

export async function summarizePullRequest(payload: any, octokit: Octokit) {
// Get relevant PR information
Expand All @@ -18,7 +21,7 @@ export async function summarizePullRequest(payload: any, octokit: Octokit) {
}

// Get the diff content using Octokit and GitHub API
const { codeDiff, skippedFiles } = await getCodeDiff(
const { codeDiff, skippedFiles, maxLengthExceeded } = await getCodeDiff(
owner,
repo,
pull_number,
Expand All @@ -30,11 +33,12 @@ export async function summarizePullRequest(payload: any, octokit: Octokit) {
const messages: ChatCompletionRequestMessage[] = [
{
role: "system",
content: systemPrompt
content: `${systemPrompt}\n\nHere is the code diff:\n\n${codeDiff}`
},
{
role: "user",
content: `Here is the code diff:\n\n${codeDiff}`
content:
'Starting with "This PR", clearly explain the focus of this PR in prose, in less than 300 characters. Then follow up with "\n\n### Detailed summary\n" and make a comprehensive list of all changes.'
}
]

Expand All @@ -55,7 +59,11 @@ export async function summarizePullRequest(payload: any, octokit: Octokit) {
", "
)}`
: ""
}\n\n${endDescription}`
}${
maxLengthExceeded
? "\n\n> The code diff in this PR exceeds the max number of characters, so this overview may be incomplete."
: ""
}\n\n✨ Ask PR-Codex anything about this PR by commenting with \`${codexCommand}{your question}\`\n\n${endDescription}`

const description = hasCodexCommented
? pr.body.split(startDescription)[0] +
Expand Down
16 changes: 5 additions & 11 deletions utils/constants.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,5 @@
import envConstants from "constants.json"

type Addresses = {}
type Constants = {}

export const constants: Constants = envConstants.values

export const addresses: Addresses =
envConstants.addresses[process.env.NEXT_PUBLIC_CHAIN_ID]

export default constants
export const startDescription = "\n\n<!-- start pr-codex -->"
export const endDescription = "<!-- end pr-codex -->"
export const codexCommand = "/codex "
export const maxChanges = 1000
export const maxCodeDiff = 11500
12 changes: 7 additions & 5 deletions utils/getCodeDiff.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
import { Octokit } from "@octokit/rest"
import { joinStringsUntilMaxLength } from "../lib/joinStringsUntilMaxLength"
import { maxChanges, maxCodeDiff } from "./constants"
import { parseDiff } from "./parseDiff"

const maxChanges = 1000
const maxCodeDiff = 10000

export const getCodeDiff = async (
owner: string,
repo: string,
Expand All @@ -26,7 +24,11 @@ export const getCodeDiff = async (
// If the number of changes in a file is greater than `maxChanges` changes, the file will be skipped.
// The codeDiff is the joined string of parsed files, up to a max length of `maxCodeDiff`.
const { parsedFiles, skippedFiles } = parseDiff(diffContent, maxChanges)
const codeDiff = joinStringsUntilMaxLength(parsedFiles, maxCodeDiff)

return { codeDiff, skippedFiles }
const { codeDiff, maxLengthExceeded } = joinStringsUntilMaxLength(
parsedFiles,
maxCodeDiff
)

return { codeDiff, skippedFiles, maxLengthExceeded }
}
4 changes: 3 additions & 1 deletion utils/github/testPayloadComment.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { codexCommand } from "../../utils/constants"

export const testPayloadComment = {
installation: { id: 35293807 },
action: "created",
Expand All @@ -6,7 +8,7 @@ export const testPayloadComment = {
},

comment: {
body: "/ask-codex Describe the changes in the homepage UI"
body: `${codexCommand}what changes have been done in the homepage?`
},
sender: {
login: "jjranalli"
Expand Down
6 changes: 3 additions & 3 deletions utils/github/testPayloadSyncPr.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import { endDescription, startDescription } from "@lib/summarizePullRequest"
import { startDescription, endDescription } from "../../utils/constants"

export const testPayloadSyncPr = {
installation: { id: 35293807 },
action: "synchronize",
pull_request: {
diff_url: "https://github.com/decentralizedlabs/pr-codex/pull/4.diff",
number: 4,
// body: null,
body: null,
// body: "\n\n<!-- start pr-codex -->\n\n## PR-Codex overview\nThis PR adds a new feature to the project: a GitHub app that explains and summarizes PR code diffs. It includes a new `github/route.ts` file and updates several existing files, including `README.md`, `Homepage.tsx`, `DefaultHead.tsx`, `AppLayout.tsx`, `Footer.tsx`, and `Navbar.tsx`.\n\n> The following files were skipped due to too many changes: `package-lock.json`.\n\n<!-- end pr-codex -->",
body: "<!-- start -->\n\nthis is a test",
// body: "<!-- start -->\n\nthis is a test",
// body:
// "<!-- start -->\n\nthis is a test" +
// startDescription +
Expand Down
19 changes: 16 additions & 3 deletions utils/parseDiff.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,31 @@ type FileChange = { parsedFiles: string[]; skippedFiles: string[] }
export function parseDiff(diff: string, maxChanges: number): FileChange {
let skippedFiles: string[] = []
const files = diff.split(/diff --git /).slice(1)
const parsedFiles = files.map((file) => {

const parsedFiles = files.flatMap((file) => {
const lines = file.split("\n")

const filepath = lines[0].split(" ")[1]
const mainContent = lines.slice(4)

// Don't consider diff in deleted files
if (lines[1].startsWith("deleted")) return `deleted ${filepath}`

const mainContent = lines.slice(6).map((line) => {
if (line.startsWith("+") || line.startsWith("-")) {
const trimContent = line.slice(1).trim()
return line[0] + trimContent
} else return line.trim()
})
const changes = mainContent.filter(
(line) => line.startsWith("+") || line.startsWith("-")
).length

if (changes <= maxChanges) {
return file
return `${filepath}\n${mainContent.join("\n")}`
}
skippedFiles.push(`\`${filepath.slice(2)}\``)
return []
})

return { parsedFiles, skippedFiles }
}

0 comments on commit ff2cac7

Please sign in to comment.