feat: upgrade AI code reviewer to Gemini 3.5 Flash using official SDK

swadhinbiswas committed 3 months ago
5905356
1 parent 1e0575b
commit 5905356
6 files changed
+53 -34
▼bun.lock
+20-1
16
16
"@dnd-kit/core": "^6.3.1",
17
17
"@dnd-kit/sortable": "^10.0.0",
18
18
"@dnd-kit/utilities": "^3.2.2",
19
+ "@google/genai": "^2.11.0",
19
20
"@libsql/client": "^0.15.15",
20
21
"@monaco-editor/react": "^4.6.0",
21
22
"@opentelemetry/auto-instrumentations-node": "^0.76.0",
468
469
469
470
"@floating-ui/utils": ["@floating-ui/[email protected]", "https://registry.npmmirror.com/@floating-ui/utils/-/utils-0.2.11.tgz", {}, "sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg=="],
470
471
472
+ "@google/genai": ["@google/[email protected]", "https://registry.npmmirror.com/@google/genai/-/genai-2.11.0.tgz", { "dependencies": { "google-auth-library": "^10.3.0", "p-retry": "^4.6.2", "protobufjs": "^7.5.4", "ws": "^8.18.0" }, "peerDependencies": { "@modelcontextprotocol/sdk": "^1.25.2" }, "optionalPeers": ["@modelcontextprotocol/sdk"] }, "sha512-d2Csf29vS0GfHc52H0MG25ccY4FKvvbDgqDlEovLrPLF8sPegWr/GGO+LMOy85/1SnX0iV0zDAW7R8SsvWg8Vg=="],
473
+
471
474
"@graphql-tools/executor": ["@graphql-tools/[email protected]", "https://registry.npmmirror.com/@graphql-tools/executor/-/executor-1.5.3.tgz", { "dependencies": { "@graphql-tools/utils": "^11.1.0", "@graphql-typed-document-node/core": "^3.2.0", "@repeaterjs/repeater": "^3.0.4", "@whatwg-node/disposablestack": "^0.0.6", "@whatwg-node/promise-helpers": "^1.0.0", "tslib": "^2.4.0" }, "peerDependencies": { "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, "sha512-mgBFC0bsrZPZLu9EnydpMnAuQ8Iiq0CEbUcsmvXsm2/iYektGHDN/+bmb7hicA6dWZtdPfklYJmr21WD0GnOfA=="],
472
475
473
476
"@graphql-tools/merge": ["@graphql-tools/[email protected]", "https://registry.npmmirror.com/@graphql-tools/merge/-/merge-9.1.9.tgz", { "dependencies": { "@graphql-tools/utils": "^11.1.0", "tslib": "^2.4.0" }, "peerDependencies": { "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, "sha512-iHUWNjRHeQRYdgIMIuChThOwoKzA9vrzYeslgfBo5eUYEyHGZCoDPjAavssoYXLwstYt1dZj2J22jSzc2DrN0Q=="],
1166
1169
1167
1170
"@types/react-dom": ["@types/[email protected]", "https://registry.npmmirror.com/@types/react-dom/-/react-dom-18.3.7.tgz", { "peerDependencies": { "@types/react": "^18.0.0" } }, "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ=="],
1168
1171
1172
+ "@types/retry": ["@types/[email protected]", "https://registry.npmmirror.com/@types/retry/-/retry-0.12.0.tgz", {}, "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA=="],
1173
+
1169
1174
"@types/ssh2": ["@types/[email protected]", "https://registry.npmmirror.com/@types/ssh2/-/ssh2-1.15.5.tgz", { "dependencies": { "@types/node": "^18.11.18" } }, "sha512-N1ASjp/nXH3ovBHddRJpli4ozpk6UdDYIX4RJWFa9L1YKnzdhTlVmiGHm4DZnj/jLbqZpes4aeR30EFGQtvhQQ=="],
1170
1175
1171
1176
"@types/stats.js": ["@types/[email protected]", "https://registry.npmmirror.com/@types/stats.js/-/stats.js-0.17.4.tgz", {}, "sha512-jIBvWWShCvlBqBNIZt0KAshWpvSjhkwkEu4ZUcASoAvhmrgAUI2t1dXrjSL4xXVLB4FznPrIsX3nKXFl/Dt4vA=="],
1368
1373
1369
1374
"buffer": ["[email protected]", "https://registry.npmmirror.com/buffer/-/buffer-5.6.0.tgz", { "dependencies": { "base64-js": "^1.0.2", "ieee754": "^1.1.4" } }, "sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw=="],
1370
1375
1376
+ "buffer-equal-constant-time": ["[email protected]", "https://registry.npmmirror.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", {}, "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA=="],
1377
+
1371
1378
"buffer-from": ["[email protected]", "https://registry.npmmirror.com/buffer-from/-/buffer-from-1.1.2.tgz", {}, "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="],
1372
1379
1373
1380
"buildcheck": ["[email protected]", "https://registry.npmmirror.com/buildcheck/-/buildcheck-0.0.7.tgz", {}, "sha512-lHblz4ahamxpTmnsk+MNTRWsjYKv965MwOrSJyeD588rR3Jcu7swE+0wN5F+PbL5cjgu/9ObkhfzEPuofEMwLA=="],
1602
1609
1603
1610
"eastasianwidth": ["[email protected]", "https://registry.npmmirror.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz", {}, "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA=="],
1604
1611
1612
+ "ecdsa-sig-formatter": ["[email protected]", "https://registry.npmmirror.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", { "dependencies": { "safe-buffer": "^5.0.1" } }, "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ=="],
1613
+
1605
1614
"ee-first": ["[email protected]", "https://registry.npmmirror.com/ee-first/-/ee-first-1.1.1.tgz", {}, "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="],
1606
1615
1607
1616
"electron-to-chromium": ["[email protected]", "https://registry.npmmirror.com/electron-to-chromium/-/electron-to-chromium-1.5.366.tgz", {}, "sha512-OlRuhb688YTCzzU3gXPLn6nGyd+F+53INE1qaKKlu6kETErE8FYsyDh0XqXEU+uBRn0MpCzz2vfNwORhkap8qg=="],
1770
1779
1771
1780
"globe.gl": ["[email protected]", "https://registry.npmmirror.com/globe.gl/-/globe.gl-2.46.1.tgz", { "dependencies": { "@tweenjs/tween.js": "18 - 25", "accessor-fn": "1", "kapsule": "^1.16", "three": ">=0.179 <1", "three-globe": "^2.45", "three-render-objects": "^1.41" } }, "sha512-h+OvX52EBIPLtM0/2JkM+JZ9gPAhPJ4y3+hxUwD5Ey/O0Zk2ockuTiJ71bZbnNBGmNiIZzA5Vr3TMT0b3d35IQ=="],
1772
1781
1782
+ "google-auth-library": ["[email protected]", "https://registry.npmmirror.com/google-auth-library/-/google-auth-library-10.9.0.tgz", { "dependencies": { "base64-js": "^1.3.0", "ecdsa-sig-formatter": "^1.0.11", "gaxios": "^7.1.4", "gcp-metadata": "8.1.2", "google-logging-utils": "1.1.3", "jws": "^4.0.0" } }, "sha512-xtvUqvINPhTaBm7nXqlYPcrMHJPm1lCNdSovxnKKhTm+4JsvQ+KGVYJViLoH9Yxu8w+T0Qv5HubzYT9BLrppJg=="],
1783
+
1773
1784
"google-logging-utils": ["[email protected]", "https://registry.npmmirror.com/google-logging-utils/-/google-logging-utils-1.1.3.tgz", {}, "sha512-eAmLkjDjAFCVXg7A1unxHsLf961m6y17QFqXqAXGj/gVkKFrEICfStRfwUlGNfeCEjNRa32JEWOUTlYXPyyKvA=="],
1774
1785
1775
1786
"gopd": ["[email protected]", "https://registry.npmmirror.com/gopd/-/gopd-1.2.0.tgz", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="],
1952
1963
1953
1964
"jsonfile": ["[email protected]", "https://registry.npmmirror.com/jsonfile/-/jsonfile-4.0.0.tgz", { "optionalDependencies": { "graceful-fs": "^4.1.6" } }, "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg=="],
1954
1965
1966
+ "jwa": ["[email protected]", "https://registry.npmmirror.com/jwa/-/jwa-2.0.1.tgz", { "dependencies": { "buffer-equal-constant-time": "^1.0.1", "ecdsa-sig-formatter": "1.0.11", "safe-buffer": "^5.0.1" } }, "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg=="],
1967
+
1968
+ "jws": ["[email protected]", "https://registry.npmmirror.com/jws/-/jws-4.0.1.tgz", { "dependencies": { "jwa": "^2.0.1", "safe-buffer": "^5.0.1" } }, "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA=="],
1969
+
1955
1970
"kapsule": ["[email protected]", "https://registry.npmmirror.com/kapsule/-/kapsule-1.16.3.tgz", { "dependencies": { "lodash-es": "4" } }, "sha512-4+5mNNf4vZDSwPhKprKwz3330iisPrb08JyMgbsdFrimBCKNHecua/WBwvVg3n7vwx0C1ARjfhwIpbrbd9n5wg=="],
1956
1971
1957
1972
"keyv": ["[email protected]", "https://registry.npmmirror.com/keyv/-/keyv-5.6.0.tgz", { "dependencies": { "@keyv/serialize": "^1.1.1" } }, "sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw=="],
2260
2275
2261
2276
"p-queue": ["[email protected]", "https://registry.npmmirror.com/p-queue/-/p-queue-8.1.1.tgz", { "dependencies": { "eventemitter3": "^5.0.1", "p-timeout": "^6.1.2" } }, "sha512-aNZ+VfjobsWryoiPnEApGGmf5WmNsCo9xu8dfaYamG5qaLP7ClhLN6NgsFe6SwJ2UbLEBK5dv9x8Mn5+RVhMWQ=="],
2262
2277
2278
+ "p-retry": ["[email protected]", "https://registry.npmmirror.com/p-retry/-/p-retry-4.6.2.tgz", { "dependencies": { "@types/retry": "0.12.0", "retry": "^0.13.1" } }, "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ=="],
2279
+
2263
2280
"p-timeout": ["[email protected]", "https://registry.npmmirror.com/p-timeout/-/p-timeout-6.1.4.tgz", {}, "sha512-MyIV3ZA/PmyBN/ud8vV9XzwTrNtR4jFrObymZYnZqMmW0zA8Z17vnT0rBgFE/TlohB+YCHqXMgZzb3Csp49vqg=="],
2264
2281
2265
2282
"p-try": ["[email protected]", "https://registry.npmmirror.com/p-try/-/p-try-2.2.0.tgz", {}, "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ=="],
2504
2521
2505
2522
"retext-stringify": ["[email protected]", "https://registry.npmmirror.com/retext-stringify/-/retext-stringify-4.0.0.tgz", { "dependencies": { "@types/nlcst": "^2.0.0", "nlcst-to-string": "^4.0.0", "unified": "^11.0.0" } }, "sha512-rtfN/0o8kL1e+78+uxPTqu1Klt0yPzKuQ2BfWwwfgIUSayyzxpM1PJzkKt4V8803uB9qSy32MvI7Xep9khTpiA=="],
2506
2523
2507
- "retry": ["[email protected]", "https://registry.npmmirror.com/retry/-/retry-0.12.0.tgz", {}, "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow=="],
2524
+ "retry": ["[email protected]", "https://registry.npmmirror.com/retry/-/retry-0.13.1.tgz", {}, "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg=="],
2508
2525
2509
2526
"reusify": ["[email protected]", "https://registry.npmmirror.com/reusify/-/reusify-1.1.0.tgz", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="],
2510
2527
3114
3131
3115
3132
"prebuild-install/detect-libc": ["[email protected]", "https://registry.npmmirror.com/detect-libc/-/detect-libc-2.1.2.tgz", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="],
3116
3133
3134
+ "promise-retry/retry": ["[email protected]", "https://registry.npmmirror.com/retry/-/retry-0.12.0.tgz", {}, "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow=="],
3135
+
3117
3136
"prompts/kleur": ["[email protected]", "https://registry.npmmirror.com/kleur/-/kleur-3.0.3.tgz", {}, "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w=="],
3118
3137
3119
3138
"qrcode/yargs": ["[email protected]", "https://registry.npmmirror.com/yargs/-/yargs-15.4.1.tgz", { "dependencies": { "cliui": "^6.0.0", "decamelize": "^1.2.0", "find-up": "^4.1.0", "get-caller-file": "^2.0.1", "require-directory": "^2.1.1", "require-main-filename": "^2.0.0", "set-blocking": "^2.0.0", "string-width": "^4.2.0", "which-module": "^2.0.0", "y18n": "^4.0.0", "yargs-parser": "^18.1.2" } }, "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A=="],
3120
3139
▼features.md
+1-1
5
5
6
6
## Recently completed
7
7
- Local free-model AI support (`local` provider, Ollama/OpenAI-compatible) with configurable `localBaseUrl` and key.
8
-- External-agent (CodeRabbit-like) callback and async AI review flow documented and supported.
8
+- External-agent (Greptile-like) callback and async AI review flow documented and supported.
9
9
- PR dependency APIs:
10
10
- `GET /api/repos/{owner}/{repo}/pulls/dependencies`
11
11
- `POST /api/repos/{owner}/{repo}/pulls/stack-order`
12
12
▼package.json
+2-1
55
55
"@dnd-kit/core": "^6.3.1",
56
56
"@dnd-kit/sortable": "^10.0.0",
57
57
"@dnd-kit/utilities": "^3.2.2",
58
+ "@google/genai": "^2.11.0",
58
59
"@libsql/client": "^0.15.15",
59
60
"@monaco-editor/react": "^4.6.0",
60
61
"@opentelemetry/auto-instrumentations-node": "^0.76.0",
137
138
"remark-gfm": "^4.0.1",
138
139
"remark-parse": "^11.0.0",
139
140
"remark-rehype": "^11.1.2",
141
+ "shiki": "^1.29.0",
140
142
"simple-git": "^3.36.0",
141
143
"sonner": "^2.0.7",
142
144
"ssh2": "^1.15.0",
145
147
"three": "^0.182.0",
146
148
"unified": "^11.0.5",
147
149
"unist-util-visit": "^5.1.0",
148
- "shiki": "^1.29.0",
149
150
"yaml": "^2.8.0",
150
151
"zod": "^3.23.8",
151
152
"zustand": "^4.5.4"
152
153
▼scripts/ai-review.ts
+28-29
1
1
import { execSync } from 'child_process';
2
+import { GoogleGenAI } from '@google/genai';
3
+import type { Interactions } from '@google/genai';
2
4
3
-const GEMINI_API_KEY = process.env.GEMINI_API_KEY;
5
+const GEMINI_API_KEY = process.env.GEMINI_KEY || process.env.GEMINI_API_KEY;
4
6
const GITHUB_TOKEN = process.env.GITHUB_TOKEN;
5
7
const PR_NUMBER = process.env.PR_NUMBER;
6
8
const REPO = process.env.REPO;
8
10
const HEAD_SHA = process.env.HEAD_SHA;
9
11
10
12
if (!GEMINI_API_KEY) {
11
- console.log("GEMINI_API_KEY is not set. Skipping AI review.");
13
+ console.log("GEMINI_KEY is not set. Skipping AI review.");
12
14
process.exit(0);
13
15
}
14
16
17
19
process.exit(0);
18
20
}
19
21
22
+const ai = new GoogleGenAI({
23
+ apiKey: GEMINI_API_KEY,
24
+});
25
+
26
+const tools: Interactions.Tool[] = [
27
+ {
28
+ type: 'google_search',
29
+ },
30
+];
31
+
32
+const generationConfig = {
33
+ max_output_tokens: 65536,
34
+ thinkingLevel: 'medium',
35
+};
36
+
20
37
async function run() {
21
38
try {
22
39
// 1. Get the diff
34
51
}
35
52
36
53
// 2. Call Gemini API
37
- console.log("Requesting review from Gemini 1.5 Flash...");
54
+ console.log("Requesting review from Gemini 3.5 Flash...");
38
55
const prompt = `You are a senior software engineer conducting a code review on a pull request.
39
56
Review the following git diff and provide constructive, human-like, and professional feedback.
40
57
Focus on identifying logic errors, security issues, performance bottlenecks, and best practice violations.
51
68
\`\`\`
52
69
`;
53
70
54
- // We use the same API endpoint format from your curl command
55
- const response = await fetch(`https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-flash-latest:generateContent?key=${GEMINI_API_KEY}`, {
56
- method: 'POST',
57
- headers: {
58
- 'Content-Type': 'application/json'
59
- },
60
- body: JSON.stringify({
61
- contents: [
62
- {
63
- parts: [
64
- {
65
- text: prompt
66
- }
67
- ]
68
- }
69
- ]
70
- })
71
+ const interaction = await ai.interactions.create({
72
+ model: 'models/gemini-3.5-flash',
73
+ input: prompt,
74
+ tools: tools,
75
+ generation_config: generationConfig,
71
76
});
72
77
73
- if (!response.ok) {
74
- const errorText = await response.text();
75
- console.error(`Gemini API Error: ${response.status} - ${errorText}`);
76
- process.exit(1);
77
- }
78
-
79
- const data = await response.json();
80
- let reviewComment = data.candidates?.[0]?.content?.parts?.[0]?.text;
78
+ const step = interaction.steps?.at(-1);
79
+ let reviewComment = step?.text || step?.parts?.[0]?.text;
81
80
82
81
if (!reviewComment) {
83
- console.error("No content received from Gemini.");
82
+ console.error("No content received from Gemini.", JSON.stringify(step));
84
83
process.exit(1);
85
84
}
86
85
87
86
▼src/lib/openapi.ts
+1-1
496
496
post: {
497
497
tags: ["AI Review"],
498
498
summary: "Receive external AI agent callback",
499
- description: "Authenticated callback endpoint for external agents (CodeRabbit-like) to finalize async AI reviews.",
499
+ description: "Authenticated callback endpoint for external agents (Greptile-like) to finalize async AI reviews.",
500
500
parameters: [
501
501
{ name: "owner", in: "path", required: true, schema: { type: "string" } },
502
502
{ name: "repo", in: "path", required: true, schema: { type: "string" } },
503
503
▼src/pages/settings/ai.astro
+1-1
153
153
value="external_agent"
154
154
selected={aiConfig.provider ===
155
155
"external_agent"}
156
- >External Agent (CodeRabbit-like)</option
156
+ >External Agent (Greptile-like)</option
157
157
>
158
158
<option
159
159
value="anthropic"
160
160