Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Code Review Feature #1066

Open
wants to merge 12 commits into
base: preview
Choose a base branch
from
3 changes: 2 additions & 1 deletion core/commands/slash/commit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ const CommitMessageCommand: SlashCommand = {
name: "commit",
description: "Generate a commit message for current changes",
run: async function* ({ ide, llm, input }) {
const diff = await ide.getDiff();
const diffs = await ide.getDiff();
const diff = Object.values(diffs).join("\n");
const prompt = `${diff}\n\nGenerate a commit message for the above set of changes. First, give a single sentence, no more than 80 characters. Then, after 2 line breaks, give a list of no more than 5 short bullet points, each no more than 40 characters. Output nothing except for the commit message, and don't surround it in quotes.`;
for await (const chunk of llm.streamChat([
{ role: "user", content: prompt },
Expand Down
2 changes: 1 addition & 1 deletion core/config/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -327,7 +327,7 @@ declare global {

export interface IDE {
getIdeInfo(): Promise<IdeInfo>;
getDiff(): Promise<string>;
getDiff(): Promise<{ [workspaceDir: string]: string }>;
isTelemetryEnabled(): Promise<boolean>;
getUniqueId(): Promise<string>;
getTerminalContents(): Promise<string>;
Expand Down
3 changes: 2 additions & 1 deletion core/context/providers/DiffContextProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ class DiffContextProvider extends BaseContextProvider {
query: string,
extras: ContextProviderExtras,
): Promise<ContextItem[]> {
const diff = await extras.ide.getDiff();
const diffs = await extras.ide.getDiff();
const diff = Object.values(diffs).join("\n");
return [
{
description: "The current git diff",
Expand Down
14 changes: 12 additions & 2 deletions core/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ export interface ILLM extends LLMOptions {
): AsyncGenerator<ChatMessage, LLMReturnValue>;

chat(
messages: ChatMessage[],
messages: ChatMessage[] | string,
options?: LLMFullCompletionOptions,
): Promise<ChatMessage>;

Expand Down Expand Up @@ -336,7 +336,7 @@ export interface IndexTag {

export interface IDE {
getIdeInfo(): Promise<IdeInfo>;
getDiff(): Promise<string>;
getDiff(): Promise<{ [repoRoot: string]: string }>;
isTelemetryEnabled(): Promise<boolean>;
getUniqueId(): Promise<string>;
getTerminalContents(): Promise<string>;
Expand Down Expand Up @@ -608,6 +608,11 @@ export interface TabAutocompleteOptions {
useOtherFiles: boolean;
}

export interface CodeReviewOptions {
prompt: string;
modelTitle: string;
}

export interface SerializedContinueConfig {
env?: string[];
allowAnonymousTelemetry?: boolean;
Expand All @@ -623,6 +628,7 @@ export interface SerializedContinueConfig {
embeddingsProvider?: EmbeddingsProviderDescription;
tabAutocompleteModel?: ModelDescription;
tabAutocompleteOptions?: Partial<TabAutocompleteOptions>;
review?: CodeReviewOptions;
}

export type ConfigMergeType = "merge" | "overwrite";
Expand Down Expand Up @@ -661,6 +667,8 @@ export interface Config {
tabAutocompleteModel?: CustomLLM | ModelDescription;
/** Options for tab autocomplete */
tabAutocompleteOptions?: Partial<TabAutocompleteOptions>;
/** Options for the code review feature */
review?: CodeReviewOptions;
}

export interface ContinueConfig {
Expand All @@ -676,6 +684,7 @@ export interface ContinueConfig {
embeddingsProvider: EmbeddingsProvider;
tabAutocompleteModel?: ILLM;
tabAutocompleteOptions?: Partial<TabAutocompleteOptions>;
review?: CodeReviewOptions;
}

export interface BrowserSerializedContinueConfig {
Expand All @@ -689,4 +698,5 @@ export interface BrowserSerializedContinueConfig {
disableSessionTitles?: boolean;
userToken?: string;
embeddingsProvider?: string;
review?: CodeReviewOptions;
}
8 changes: 1 addition & 7 deletions core/indexing/refreshIndex.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import crypto from "crypto";
import * as fs from "fs";
import { Database, open } from "sqlite";
import sqlite3 from "sqlite3";
import { IndexTag, IndexingProgressUpdate } from "..";
import { calculateHash } from "../util";
import { getIndexSqlitePath } from "../util/paths";
import {
CodebaseIndex,
Expand Down Expand Up @@ -244,12 +244,6 @@ async function getTagsFromGlobalCache(
return rows;
}

function calculateHash(fileContents: string): string {
const hash = crypto.createHash("sha256");
hash.update(fileContents);
return hash.digest("hex");
}

export async function getComputeDeleteAddRemove(
tag: IndexTag,
currentFiles: LastModifiedMap,
Expand Down
9 changes: 8 additions & 1 deletion core/llm/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -328,7 +328,14 @@ ${prompt}`;
return completion;
}

async chat(messages: ChatMessage[], options: LLMFullCompletionOptions = {}) {
async chat(
messages: ChatMessage[] | string,
options: LLMFullCompletionOptions = {},
) {
if (typeof messages === "string") {
messages = [{ role: "user", content: messages }];
}

let completion = "";
for await (const chunk of this.streamChat(messages, options)) {
completion += chunk.content;
Expand Down
3 changes: 3 additions & 0 deletions core/protocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
SerializedContinueConfig,
SessionInfo,
} from ".";
import { ReviewResult } from "./review/review";
import { IdeProtocol } from "./web/webviewProtocol";

export type ProtocolGeneratorType<T> = AsyncGenerator<{
Expand All @@ -20,6 +21,7 @@ export type ProtocolGeneratorType<T> = AsyncGenerator<{
export type Protocol = {
// New
"update/modelChange": [string, void];
"update/fileSave": [{ filepath: string }, void];
// Special
ping: [string, string];
abort: [undefined, void];
Expand Down Expand Up @@ -97,6 +99,7 @@ export type Protocol = {
},
ProtocolGeneratorType<MessageContent>,
];
"review/getResults": [undefined, ReviewResult[]];
};

export interface IdeSettings {
Expand Down
26 changes: 26 additions & 0 deletions core/review/parseDiff.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
export function getDiffPerFile(diff: string): { [filepath: string]: string } {
/**
* Example of the lines before the diff for each file:
a/core/index.d.ts b/core/index.d.ts
index 18f88a2c..719fd6d2 100644
--- a/core/index.d.ts
+++ b/core/index.d.ts
*/
const perFile: { [filepath: string]: string } = {};

const parts = diff.split("diff --git ").slice(1);
for (const part of parts) {
const lines = part.split("\n");
// Splitting a line like this: `a/core/index.d.ts b/core/index.d.ts`
const filepath = lines[0].slice(2).split(" ")[0];
const diff = lines.slice(4).join("\n");
perFile[filepath] = diff;
}

return perFile;
}

export function getChangedFiles(diff: string): string[] {
const parts = diff.split("diff --git ").slice(1);
return parts.map((part) => part.split("\n")[0].slice(2).split(" ")[0]);
}
14 changes: 14 additions & 0 deletions core/review/prompts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
export const reviewSystemMessage = [
'Assistant is "ReviewGPT", an AI that does code reviews of code in Github.',
"Assistant will review the git diff, where lines starting with '-' are being removed, lines starting with '+' are being added, and other lines are surrounding context.",
// "Assistant does not introduce itself. Assistant's responses ONLY include the actual code review without any other context.",
"Assistant ONLY uses backticks to surround code snippets and never uses backticks to format non-code.",
"Assistant does not criticize for minor style issues and does not criticize for things that are not in the diff, like missing PR description items.",
"Assistant offers concrete, actionable, and numbered suggestions based on best practices and observed bugs, and does not suggest tangential, vague, nitpicky, or out-of-scope changes.",
"Assistant does not needlessly summarize the changes made by the user",
"If Assistant doesn't have any constructive review comments to add, it will simply respond 'LGTM'.",
"100% of the response is always in github markdown (without any other text or headings, and without escaping the markdown), suitable for copying into a github comment directly.",
].join("\n");

export const reviewPrompt = `File: {{{filepath}}}
Provide useful suggestions based on the lines in the following git diff:\`\`\`{{{diff}}}\`\`\``;