Skip to content

Review Agent #298

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

Merged
merged 16 commits into from
May 12, 2025
Merged

Review Agent #298

merged 16 commits into from
May 12, 2025

Conversation

msukkari
Copy link
Contributor

@msukkari msukkari commented May 7, 2025

Summary by CodeRabbit

  • New Features

    • Introduced an AI-powered code review agent that automatically analyzes GitHub pull requests and generates actionable review comments.
    • Added a new Agents page displaying available AI agents for each domain, with options to deploy or configure them.
    • Integrated support for GitHub App and OpenAI API configuration through environment variables.
    • Added a server-side webhook handler for GitHub events to trigger automated code reviews.
  • Improvements

    • Enhanced the workspace configuration and development scripts to support the new review agent.
    • Added new dependencies to enable GitHub and OpenAI API interactions.
  • Chores

    • Updated environment variable documentation with placeholders for agent configuration.
    • Removed obsolete deployment configuration for the staging environment.
    • Added comprehensive documentation for the Agents feature and the AI Code Review Agent.
    • Added "Agents" navigation menu item for easier access within domains.

@msukkari msukkari requested a review from brendan-kellam May 7, 2025 23:31
Copy link

coderabbitai bot commented May 7, 2025

Walkthrough

This update introduces a new automated GitHub pull request review agent, including its type definitions, control flow, and integration with OpenAI for code review generation. It adds a new agents page to the web app, updates workspace and dependency configurations, and implements a GitHub webhook API route for handling PR events. The Fly.io deployment configuration for staging is removed.

Changes

Files / Groups Change Summary
staging/fly.toml Deleted Fly.io deployment configuration for the sourcebot-staging app, removing deployment, volume, and service settings.
.env.development, packages/web/src/env.mjs, packages/backend/src/env.ts Added commented placeholders and environment variable declarations for GitHub App and OpenAI integration, including cache directory and review agent control flags.
package.json, packages/web/package.json Added new workspace for agents, a dev script for the review agent, and new dependencies: octokit, openai, parse-diff, zod-to-json-schema.
packages/web/src/app/[domain]/agents/page.tsx Added a new React component to display available AI agents for a domain, with deploy and configure actions.
packages/web/src/app/api/(server)/webhook/route.ts Added a new server API route to handle GitHub webhook events, initializing a GitHub App client, parsing PR and issue comment events, and invoking the review agent.
packages/web/src/app/[domain]/browse/[...path]/page.tsx Refactored import statements for utility functions without changing logic.
packages/web/src/features/agents/review-agent/app.ts Introduced the main control flow for processing GitHub PR events: parsing, generating reviews with OpenAI, and posting comments back to GitHub.
packages/web/src/features/agents/review-agent/types.ts Added Zod schemas and TypeScript types for diffs, PR payloads, context, and review comments to support structured data handling in the review agent.
packages/web/src/features/agents/review-agent/nodes/fetchFileContent.ts Added a function to fetch file content context for a PR from an internal API, decoding and validating the response.
packages/web/src/features/agents/review-agent/nodes/generateDiffReviewPrompt.ts Added a function to generate a detailed prompt for reviewing code diffs, including context and rules, formatted for LLM input.
packages/web/src/features/agents/review-agent/nodes/generatePrReview.ts Added a function to generate review comments for each file diff in a PR by invoking the LLM with appropriate context and rules.
packages/web/src/features/agents/review-agent/nodes/githubPrParser.ts Added a parser to extract and structure PR diff data from GitHub webhook events using Octokit and parse-diff.
packages/web/src/features/agents/review-agent/nodes/githubPushPrReviews.ts Added a function to post generated review comments back to GitHub as PR review comments, handling both single- and multi-line comments.
packages/web/src/features/agents/review-agent/nodes/invokeDiffReviewLlm.ts Added a function to call the OpenAI API with a review prompt, parse and validate the LLM response, and return structured review data.
packages/web/src/app/api/(server)/source/route.ts Exported the postSource function to make it publicly accessible outside its module without changing its implementation.
docs/docs.json Added a new "Agents" group with overview and review-agent documentation pages to the docs navigation.
docs/docs/agents/overview.mdx Added documentation overview for agents, describing their purpose and linking to the review agent.
docs/docs/agents/review-agent.mdx Added detailed documentation for the AI Code Review Agent, including setup, configuration, and verification steps.
packages/web/src/app/[domain]/components/navigationMenu.tsx Added a new "Agents" navigation menu item linking to the agents page.
packages/backend/src/index.ts Removed CLI argument parsing for cache directory; now reads cache directory from environment variables. Added debug log for current working directory.
supervisord.conf Removed --cacheDir argument from backend program command line in supervisor configuration.
packages/mcp/package.json Added a newline at the end of the file.
docs/docs/more/mcp-server.mdx Fixed grammatical error: changed "a open standard" to "an open standard".

Sequence Diagram(s)

sequenceDiagram
    participant GitHub
    participant WebhookAPI as Webhook API Route
    participant ReviewAgent as Review Agent
    participant Octokit
    participant OpenAI

    GitHub->>WebhookAPI: POST /api/(server)/webhook (PR event)
    WebhookAPI->>Octokit: Authenticate as GitHub App
    WebhookAPI->>ReviewAgent: processGitHubPullRequest(octokit, payload)
    ReviewAgent->>Octokit: githubPrParser (fetch PR diff)
    ReviewAgent->>ReviewAgent: generatePrReviews (for each file diff)
    loop For each diff
        ReviewAgent->>WebhookAPI: fetchFileContent (context)
        ReviewAgent->>ReviewAgent: generateDiffReviewPrompt
        ReviewAgent->>OpenAI: invokeDiffReviewLlm(prompt)
        OpenAI-->>ReviewAgent: Diff review response
    end
    ReviewAgent->>Octokit: githubPushPrReviews (post review comments)
    WebhookAPI-->>GitHub: 200 OK
Loading

Possibly related PRs

  • connections qol improvements #195: Adds client polling and fixes repository image URL fetching in connection lists, related to UI components for managing connections, which may interact with agents or repository data used by the review agent.

Poem

🐇
A clever new agent hops into play,
Reviewing your PRs in a thoughtful way.
With OpenAI’s wisdom and GitHub’s might,
It reads your diffs, both day and night.
No more manual reviews to dread—
Let the rabbit bot comment instead!
🥕


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fa40daf and ec9526f.

📒 Files selected for processing (1)
  • docs/docs/more/mcp-server.mdx (1 hunks)
🧰 Additional context used
🪛 LanguageTool
docs/docs/more/mcp-server.mdx

[misspelling] ~10-~10: Use “an” instead of ‘a’ if the following word starts with a vowel sound, e.g. ‘an article’, ‘an hour’.
Context: ....com/package/@sourcebot/mcp) package is a MCP server that enables LLMs to interfa...

(EN_A_VS_AN)

⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: build
🔇 Additional comments (1)
docs/docs/more/mcp-server.mdx (1)

10-10: Fix indefinite article usage
Using “an” before “open standard” correctly addresses the vowel sound.

🧰 Tools
🪛 LanguageTool

[misspelling] ~10-~10: Use “an” instead of ‘a’ if the following word starts with a vowel sound, e.g. ‘an article’, ‘an hour’.
Context: ....com/package/@sourcebot/mcp) package is a MCP server that enables LLMs to interfa...

(EN_A_VS_AN)


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 21

🧹 Nitpick comments (13)
packages/agents/reviewAgent/.gitignore (2)

83-83: Remove redundant .cache entries

The .cache entry under the parcel-bundler section (line 83) and the .cache entry under the VuePress v2 section (line 106) both duplicate the global .cache ignore. Consider removing these duplicates to declutter the file.

- .cache    # parcel-bundler cache (line 83)
...
- .cache    # vuepress v2.x temp and cache directory (line 106)

Also applies to: 106-106


136-137: Add OS-specific ignore patterns

To prevent committing common machine-specific files, consider adding entries for macOS and Windows artifacts at the end of the file:

+# macOS
+.DS_Store
+
+# Windows
+Thumbs.db
+ehthumbs.db
packages/agents/reviewAgent/tsconfig.json (1)

1-114: TypeScript configuration looks solid, consider enabling additional checks for production code

The TypeScript configuration is well-structured with appropriate target (ES2020) and module settings (NodeNext). The basic strict type checking is enabled, which is excellent.

Consider enabling these additional compiler options for a more robust codebase:

-    // "sourceMap": true,                                /* Create source map files for emitted JavaScript files. */
+    "sourceMap": true,                                /* Create source map files for emitted JavaScript files. */
-    // "noUnusedLocals": true,                           /* Enable error reporting when local variables aren't read. */
+    "noUnusedLocals": true,                           /* Enable error reporting when local variables aren't read. */
-    // "noUnusedParameters": true,                       /* Raise an error when a function parameter isn't read. */
+    "noUnusedParameters": true,                       /* Raise an error when a function parameter isn't read. */
-    // "noImplicitReturns": true,                        /* Enable error reporting for codepaths that do not explicitly return in a function. */
+    "noImplicitReturns": true,                        /* Enable error reporting for codepaths that do not explicitly return in a function. */
packages/agents/reviewAgent/nodes/generate_pr_reviews.ts (1)

6-6: Validate input rules array

The function takes a rules array but doesn't validate that it's non-empty or contains valid rules.

Consider adding validation for the rules array:

export const generate_pr_reviews = async (pr_payload: sourcebot_pr_payload, rules: string[]): Promise<sourcebot_file_diff_review[]> => {
    console.log("Executing generate_pr_reviews");
+   
+   if (!rules || rules.length === 0) {
+       console.warn("No review rules provided, reviews may be less focused");
+   }
packages/agents/reviewAgent/package.json (1)

4-4: Add a meaningful description

The description field is empty. Adding a clear description will help others understand what this package does.

- "description": "",
+ "description": "GitHub pull request review agent using LLM for automated code reviews",
packages/agents/reviewAgent/nodes/github_push_pr_reviews.ts (1)

10-29: Consider rate limiting for GitHub API calls

The function makes multiple API calls in a loop without any rate limiting or batching, which could hit GitHub's rate limits if there are many reviews.

Consider adding a delay between API calls or implementing a batching mechanism:

import { App } from "octokit";
import { sourcebot_pr_payload, sourcebot_file_diff_review } from "../types.js";

+ // Helper function to add delay between API calls
+ const delay = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));

export const github_push_pr_reviews = async (app: App, pr_payload: sourcebot_pr_payload, file_diff_reviews: sourcebot_file_diff_review[]) => {
    console.log("Executing github_push_pr_reviews");

    const installationId = pr_payload.installation_id;
    const installation = await app.getInstallationOctokit(installationId);

    for (const file_diff_review of file_diff_reviews) {
        for (const review of file_diff_review.reviews) {
            await installation.rest.pulls.createReviewComment({
                owner: pr_payload.owner,
                repo: pr_payload.repo,
                pull_number: pr_payload.number,
                body: review.review,
                path: file_diff_review.filename,
                commit_id: pr_payload.head_sha,
                side: "RIGHT",
                ...(review.line_start === review.line_end
                    ? { line: review.line_start }
                    : {
                        start_line: review.line_start,
                        line: review.line_end,
                        start_side: "RIGHT",
                    }),
            });
+           // Add a small delay between API calls to avoid rate limits
+           await delay(100);
        }
    }
packages/agents/reviewAgent/nodes/generate_diff_review_prompt.ts (2)

8-9: Fix typo in prompt text.

There's a typo in the prompt text: "excells" should be "excels".

-    You are an expert software engineer that excells at reviewing code changes. Given the input, additional context, and rules defined below, review the code changes and provide a detailed review. The review you provide
+    You are an expert software engineer that excels at reviewing code changes. Given the input, additional context, and rules defined below, review the code changes and provide a detailed review. The review you provide

4-40: Consider enhancing prompt structure for maintainability.

The prompt is constructed using template literals with complex nesting, which could make maintenance difficult. Consider using an array-based approach with join operations for better maintainability.

I recommend restructuring the prompt construction like this:

export const generate_diff_review_prompt = async (diff: sourcebot_diff, context: sourcebot_context[], rules: string[]) => {
    console.log("Executing generate_diff_review_prompt");
    
    const promptSections = [
        `You are an expert software engineer that excels at reviewing code changes. Given the input, additional context, and rules defined below, review the code changes and provide a detailed review. The review you provide must conform to all of the rules defined below. The output format of your review must conform to the output format defined below.`,
        
        `# Input`,
        
        `The input is the old and new code snippets, which represent a single hunk from a git diff. The old code snippet is the code before the changes were made, and the new code snippet is the code after the changes were made. Each code snippet is a sequence of lines each with a line number.`,
        
        `## Old Code Snippet`,
        
        "```",
        diff.oldSnippet,
        "```",
        
        `## New Code Snippet`,
        
        "```",
        diff.newSnippet,
        "```",
        
        `# Additional Context`,
        
        context.length > 0 
            ? context.map(c => `${c.type}: ${c.description}\n\n${c.context}`).join("\n\n----------------------\n\n")
            : "No additional context provided.",
        
        `# Rules`,
        
        rules.map(rule => `- ${rule}`).join("\n"),
        
        `# Output Format (JSON Schema)`,
        `The output must be a valid JSON object that conforms to the following JSON schema. Do NOT respond with anything other than the JSON object. Do NOT respond with the JSON object in a markdown code block.`,
        JSON.stringify(zodToJsonSchema(sourcebot_diff_review_schema), null, 2)
    ];
    
    const prompt = promptSections.join("\n\n");
    
    console.log("Completed generate_diff_review_prompt");
    return prompt;
}

This structure makes it easier to modify individual sections of the prompt.

packages/agents/reviewAgent/nodes/invoke_diff_review_llm.ts (1)

10-10: Remove unused filename parameter.

The filename parameter is declared but never used in the function. Either use it or remove it.

-export const invoke_diff_review_llm = async (prompt: string, filename: string): Promise<sourcebot_diff_review> => {
+export const invoke_diff_review_llm = async (prompt: string): Promise<sourcebot_diff_review> => {
packages/agents/reviewAgent/nodes/github_pr_parser.ts (1)

45-45: Use type predicates instead of type assertions.

The code uses a type assertion to convert the filtered array, but a type predicate would be safer and more idiomatic.

-    const filteredSourcebotFileDiffs: sourcebot_file_diff[] = sourcebotFileDiffs.filter((file) => file !== null) as sourcebot_file_diff[];
+    function isNotNull(file: sourcebot_file_diff | null): file is sourcebot_file_diff {
+        return file !== null;
+    }
+    const filteredSourcebotFileDiffs: sourcebot_file_diff[] = sourcebotFileDiffs.filter(isNotNull);
packages/agents/reviewAgent/types.ts (3)

3-48: Use PascalCase for type names to follow TypeScript conventions

The file uses snake_case for type names (e.g., sourcebot_diff), which is unusual in TypeScript. TypeScript convention typically uses PascalCase for types and interfaces.

import { z } from "zod";

export const sourcebot_diff_schema = z.object({
    oldSnippet: z.string(),
    newSnippet: z.string(),
});
-export type sourcebot_diff = z.infer<typeof sourcebot_diff_schema>;
+export type SourcebotDiff = z.infer<typeof sourcebot_diff_schema>;

export const sourcebot_file_diff_schema = z.object({
    from: z.string(),
    to: z.string(),
    diffs: z.array(sourcebot_diff_schema),
});
-export type sourcebot_file_diff = z.infer<typeof sourcebot_file_diff_schema>;
+export type SourcebotFileDiff = z.infer<typeof sourcebot_file_diff_schema>;

export const sourcebot_pr_payload_schema = z.object({
    title: z.string(),
    description: z.string(),
    hostDomain: z.string(),
    owner: z.string(),
    repo: z.string(),
    file_diffs: z.array(sourcebot_file_diff_schema),
    number: z.number(),
    head_sha: z.string(),
    installation_id: z.number(),
});
-export type sourcebot_pr_payload = z.infer<typeof sourcebot_pr_payload_schema>;
+export type SourcebotPrPayload = z.infer<typeof sourcebot_pr_payload_schema>;

export const sourcebot_context_schema = z.object({
    type: z.enum(["pr_title", "pr_description", "pr_summary", "comment_chains", "file_content"]),
    description: z.string().optional(),
    context: z.string(),
});
-export type sourcebot_context = z.infer<typeof sourcebot_context_schema>;
+export type SourcebotContext = z.infer<typeof sourcebot_context_schema>;

export const sourcebot_diff_review_schema = z.object({
    line_start: z.number(),
    line_end: z.number(),
    review: z.string(),
});
-export type sourcebot_diff_review = z.infer<typeof sourcebot_diff_review_schema>;
+export type SourcebotDiffReview = z.infer<typeof sourcebot_diff_review_schema>;

export const sourcebot_file_diff_review_schema = z.object({
    filename: z.string(),
    reviews: z.array(sourcebot_diff_review_schema),
});
-export type sourcebot_file_diff_review = z.infer<typeof sourcebot_file_diff_review_schema>;
+export type SourcebotFileDiffReview = z.infer<typeof sourcebot_file_diff_review_schema>;

3-48: Add JSDoc comments to improve code documentation

The file lacks JSDoc comments to explain the purpose and usage of each type and schema. Adding documentation would improve code maintainability.

Here's an example of how you could add JSDoc comments:

import { z } from "zod";

+/**
+ * Schema for code diff representation containing old and new code snippets
+ */
export const sourcebot_diff_schema = z.object({
    oldSnippet: z.string(),
    newSnippet: z.string(),
});
export type sourcebot_diff = z.infer<typeof sourcebot_diff_schema>;

+/**
+ * Schema for file-level diff containing path information and an array of code diffs
+ */
export const sourcebot_file_diff_schema = z.object({
    from: z.string(),
    to: z.string(),
    diffs: z.array(sourcebot_diff_schema),
});
export type sourcebot_file_diff = z.infer<typeof sourcebot_file_diff_schema>;

// Add similar JSDoc comments for the remaining schemas

36-40: Add validation constraints for line numbers

The line numbers in the diff review schema should be positive integers and line_end should be greater than or equal to line_start. Adding these constraints would prevent invalid data.

export const sourcebot_diff_review_schema = z.object({
-    line_start: z.number(),
-    line_end: z.number(),
+    line_start: z.number().int().positive(),
+    line_end: z.number().int().positive().refine(val => val >= this.line_start, {
+        message: "line_end must be greater than or equal to line_start"
+    }),
    review: z.string(),
});
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 873c9ef and d37daee.

⛔ Files ignored due to path filters (1)
  • packages/agents/reviewAgent/yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (12)
  • packages/agents/reviewAgent/.gitignore (1 hunks)
  • packages/agents/reviewAgent/app.ts (1 hunks)
  • packages/agents/reviewAgent/nodes/fetch_file_content.ts (1 hunks)
  • packages/agents/reviewAgent/nodes/generate_diff_review_prompt.ts (1 hunks)
  • packages/agents/reviewAgent/nodes/generate_pr_reviews.ts (1 hunks)
  • packages/agents/reviewAgent/nodes/github_pr_parser.ts (1 hunks)
  • packages/agents/reviewAgent/nodes/github_push_pr_reviews.ts (1 hunks)
  • packages/agents/reviewAgent/nodes/invoke_diff_review_llm.ts (1 hunks)
  • packages/agents/reviewAgent/package.json (1 hunks)
  • packages/agents/reviewAgent/tsconfig.json (1 hunks)
  • packages/agents/reviewAgent/types.ts (1 hunks)
  • staging/fly.toml (0 hunks)
💤 Files with no reviewable changes (1)
  • staging/fly.toml
🧰 Additional context used
🧬 Code Graph Analysis (5)
packages/agents/reviewAgent/nodes/fetch_file_content.ts (3)
packages/web/src/features/search/schemas.ts (1)
  • fileSourceResponseSchema (101-104)
packages/web/src/lib/utils.ts (1)
  • base64Decode (191-194)
packages/agents/reviewAgent/types.ts (2)
  • sourcebot_pr_payload (27-27)
  • sourcebot_context (34-34)
packages/agents/reviewAgent/nodes/invoke_diff_review_llm.ts (1)
packages/agents/reviewAgent/types.ts (2)
  • sourcebot_diff_review (41-41)
  • sourcebot_diff_review_schema (36-40)
packages/agents/reviewAgent/nodes/github_pr_parser.ts (1)
packages/agents/reviewAgent/types.ts (3)
  • sourcebot_pr_payload (27-27)
  • sourcebot_file_diff (14-14)
  • sourcebot_diff (7-7)
packages/agents/reviewAgent/nodes/generate_diff_review_prompt.ts (1)
packages/agents/reviewAgent/types.ts (3)
  • sourcebot_diff (7-7)
  • sourcebot_context (34-34)
  • sourcebot_diff_review_schema (36-40)
packages/agents/reviewAgent/nodes/github_push_pr_reviews.ts (1)
packages/agents/reviewAgent/types.ts (2)
  • sourcebot_pr_payload (27-27)
  • sourcebot_file_diff_review (47-47)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: build
🔇 Additional comments (3)
packages/agents/reviewAgent/.gitignore (1)

1-137: Comprehensive .gitignore coverage

The new .gitignore covers logs, diagnostic reports, runtime data, dependency directories, build outputs, and tool caches for various frameworks and bundlers, aligning well with Node.js and TypeScript project best practices.

packages/agents/reviewAgent/package.json (2)

11-20: Repository URLs don't match current project structure

The repository URLs point to "sourcebot-dev/review-agent" on GitHub, but this code appears to be located within "packages/agents/reviewAgent" in a different repository.

Please verify if these repository settings need to be updated to match the current project's structure and hosting location.


24-24: ```shell
npm view @octokit/webhooks-definitions version


</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->

brendan-kellam
brendan-kellam previously approved these changes May 7, 2025
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 17

🧹 Nitpick comments (8)
packages/agents/reviewAgent/src/nodes/invoke_diff_review_llm.ts (2)

33-36: Improve error handling

The current error handling logs the entire error object, which might expose sensitive information in production. Consider sanitizing the error message.

} catch (error) {
-    console.error('Error calling OpenAI:', error);
+    const errorMessage = error instanceof Error ? error.message : String(error);
+    console.error('Error calling OpenAI:', errorMessage);
    throw error;
}

10-37: Implement retry mechanism for API failures

The function doesn't have any retry logic for handling transient API failures, which are common with external services.

Consider implementing a simple retry mechanism with exponential backoff:

export const invoke_diff_review_llm = async (prompt: string, filename: string, maxRetries = 3): Promise<sourcebot_diff_review> => {
    console.log("Executing invoke_diff_review_llm");
    
    let retries = 0;
    while (true) {
        try {
            const completion = await openai.chat.completions.create({
                model: MODEL,
                timeout: TIMEOUT_MS,
                messages: [
                    { role: "system", content: prompt }
                ]
            });
        
            const openaiResponse = completion.choices[0].message.content;
            console.log("OpenAI response: ", openaiResponse);
            
            const diffReviewJson = JSON.parse(openaiResponse || '{}');
            const diffReview = sourcebot_diff_review_schema.safeParse(diffReviewJson);

            if (!diffReview.success) {
                throw new Error(`Invalid diff review format: ${diffReview.error}`);
            }

            console.log("Completed invoke_diff_review_llm");
            return diffReview.data;
        } catch (error) {
            const errorMessage = error instanceof Error ? error.message : String(error);
            console.error(`Error calling OpenAI (attempt ${retries + 1}/${maxRetries}):`, errorMessage);
            
            if (retries >= maxRetries) {
                throw error;
            }
            
            // Exponential backoff
            const delay = Math.pow(2, retries) * 1000; 
            console.log(`Retrying in ${delay}ms...`);
            await new Promise(resolve => setTimeout(resolve, delay));
            retries++;
        }
    }
}
packages/agents/reviewAgent/src/nodes/generate_diff_review_prompt.ts (2)

30-30: Handle empty context or context with special characters

The context mapping doesn't handle the case where a context item might be empty or contain special characters that could interfere with the prompt formatting.

-    ${context.map(c => `${c.type}: ${c.description}\n\n${c.context}`).join("\n\n----------------------\n\n")}
+    ${context.length === 0 ? 'No additional context provided.' : 
+      context.map(c => {
+        const safeContext = (c.context || '').replace(/`/g, '\\`'); // Escape backticks
+        return `${c.type || 'Unknown'}: ${c.description || ''}\n\n${safeContext}`;
+      }).join("\n\n----------------------\n\n")}

34-34: Handle empty rules list

Similar to the context, the rules mapping doesn't handle the case where the rules array might be empty.

-    - ${rules.join("\n- ")}
+    ${rules.length === 0 ? 'No specific rules provided.' : rules.map(rule => `- ${rule}`).join("\n")}
packages/agents/reviewAgent/src/nodes/generate_pr_reviews.ts (1)

30-33: Add specific error handling for LLM invocation

The current error handling only catches errors from fetching file content, but errors from generating prompts or invoking the LLM are not specifically caught and handled.

try {
    const fileContentContext = await fetch_file_content(pr_payload, file_diff.to);
    const context: sourcebot_context[] = [
        // ...context definitions...
    ];

-   const prompt = await generate_diff_review_prompt(diff, context, rules);
-   
-   const diffReview = await invoke_diff_review_llm(prompt, file_diff.to);
-   reviews.push(diffReview);
+   try {
+       const prompt = await generate_diff_review_prompt(diff, context, rules);
+       
+       try {
+           const diffReview = await invoke_diff_review_llm(prompt, file_diff.to);
+           reviews.push(diffReview);
+       } catch (llmError) {
+           console.error(`Error invoking LLM for ${file_diff.to}: ${llmError}`);
+       }
+   } catch (promptError) {
+       console.error(`Error generating prompt for ${file_diff.to}: ${promptError}`);
+   }
} catch (error) {
    console.error(`Error fetching file content for ${file_diff.to}: ${error}`);
}
packages/agents/reviewAgent/src/nodes/github_push_pr_reviews.ts (2)

30-32: Implement retry mechanism for failed API calls

The current implementation doesn't retry failed API calls, which could lead to missing review comments for transient API failures.

try {
    await limiter.schedule(() => installation.rest.pulls.createReviewComment({
        // ... comment parameters
    }));
    successCount++;
} catch (error) {
    console.error(`Error pushing pr reviews for ${file_diff_review.filename}: ${error}`);
-   failCount++;
+   // Retry the API call up to 3 times with exponential backoff
+   let retryCount = 0;
+   const maxRetries = 3;
+   let success = false;
+   
+   while (retryCount < maxRetries && !success) {
+       const delay = Math.pow(2, retryCount) * 1000; // 1s, 2s, 4s
+       console.log(`Retrying (${retryCount + 1}/${maxRetries}) after ${delay}ms...`);
+       
+       await new Promise(resolve => setTimeout(resolve, delay));
+       
+       try {
+           await limiter.schedule(() => installation.rest.pulls.createReviewComment({
+               // ... same comment parameters
+               owner: pr_payload.owner,
+               repo: pr_payload.repo,
+               pull_number: pr_payload.number,
+               body: review.review,
+               path: file_diff_review.filename,
+               commit_id: pr_payload.head_sha,
+               side: "RIGHT",
+               ...(review.line_start === review.line_end
+                   ? { line: review.line_start }
+                   : {
+                       start_line: review.line_start,
+                       line: review.line_end,
+                       start_side: "RIGHT",
+                   }),
+           }));
+           success = true;
+           successCount++;
+           console.log(`Retry successful for comment on ${file_diff_review.filename}`);
+       } catch (retryError) {
+           console.error(`Retry failed (${retryCount + 1}/${maxRetries}): ${retryError}`);
+           retryCount++;
+       }
+   }
+   
+   if (!success) {
+       failCount++;
+       console.error(`All retries failed for comment on ${file_diff_review.filename}`);
+   }
}

4-40: Batch review comments for efficiency

The current implementation creates individual review comments for each review, which is inefficient for PRs with many reviews. Consider batching reviews using GitHub's Pull Request Review API.

While the current approach of pushing individual review comments works, GitHub also offers a more efficient way to post multiple comments at once using the pulls.createReview endpoint. This can reduce the number of API calls and better handle rate limits.

Consider refactoring to use the batched approach. Here's a simplified example:

// Group comments by file
const commentsByFile = new Map<string, Array<{line: number, body: string}>>();

for (const file_diff_review of file_diff_reviews) {
  for (const review of file_diff_review.reviews) {
    if (!commentsByFile.has(file_diff_review.filename)) {
      commentsByFile.set(file_diff_review.filename, []);
    }
    commentsByFile.get(file_diff_review.filename)?.push({
      line: review.line_end,
      body: review.review
    });
  }
}

// Create a single review with all comments
await installation.rest.pulls.createReview({
  owner: pr_payload.owner,
  repo: pr_payload.repo,
  pull_number: pr_payload.number,
  commit_id: pr_payload.head_sha,
  event: "COMMENT",
  comments: Array.from(commentsByFile.entries()).flatMap(([path, comments]) => 
    comments.map(comment => ({
      path,
      line: comment.line,
      body: comment.body
    }))
  )
});

This approach would significantly reduce API calls and improve performance for larger PRs.

packages/agents/reviewAgent/src/nodes/github_pr_parser.ts (1)

22-41: Add a size limit for snippets to avoid memory issues

For large diffs, the accumulated snippets could consume excessive memory. Consider limiting their size or using a more memory-efficient approach.

        const diffs: sourcebot_diff[] = file.chunks.map((chunk) => {
            let oldSnippet = "";
            let newSnippet = "";
+           const MAX_SNIPPET_SIZE = 10000; // Limit snippet size to avoid memory issues
+           let snippetSizeExceeded = false;

            for (const change of chunk.changes) {
+               // Check if we've already exceeded the size limit
+               if (snippetSizeExceeded) continue;
+               
                if (change.type === "normal") {
                    oldSnippet += change.ln1 + ":" + change.content;
                    newSnippet += change.ln2 + ":" + change.content;
                } else if (change.type === "add") {
                    newSnippet += change.ln + ":" + change.content;
                } else if (change.type === "del") {
                    oldSnippet += change.ln + ":" + change.content;
                }
+               
+               // Check if we've exceeded the size limit after adding this change
+               if (oldSnippet.length > MAX_SNIPPET_SIZE || newSnippet.length > MAX_SNIPPET_SIZE) {
+                   snippetSizeExceeded = true;
+                   oldSnippet += "\n... (snippet truncated due to size)";
+                   newSnippet += "\n... (snippet truncated due to size)";
+               }
            }

            return {
                oldSnippet: oldSnippet,
                newSnippet: newSnippet,
            }
        });
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d37daee and 17fce5c.

📒 Files selected for processing (10)
  • packages/agents/reviewAgent/nodes/fetch_file_content.ts (1 hunks)
  • packages/agents/reviewAgent/package.json (1 hunks)
  • packages/agents/reviewAgent/src/app.ts (1 hunks)
  • packages/agents/reviewAgent/src/nodes/fetch_file_content.ts (1 hunks)
  • packages/agents/reviewAgent/src/nodes/generate_diff_review_prompt.ts (1 hunks)
  • packages/agents/reviewAgent/src/nodes/generate_pr_reviews.ts (1 hunks)
  • packages/agents/reviewAgent/src/nodes/github_pr_parser.ts (1 hunks)
  • packages/agents/reviewAgent/src/nodes/github_push_pr_reviews.ts (1 hunks)
  • packages/agents/reviewAgent/src/nodes/invoke_diff_review_llm.ts (1 hunks)
  • packages/agents/reviewAgent/src/types.ts (1 hunks)
✅ Files skipped from review due to trivial changes (2)
  • packages/agents/reviewAgent/package.json
  • packages/agents/reviewAgent/src/types.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/agents/reviewAgent/nodes/fetch_file_content.ts
🧰 Additional context used
🧬 Code Graph Analysis (3)
packages/agents/reviewAgent/src/nodes/fetch_file_content.ts (4)
packages/agents/reviewAgent/nodes/fetch_file_content.ts (1)
  • fetch_file_content (15-49)
packages/agents/reviewAgent/src/types.ts (2)
  • sourcebot_pr_payload (27-27)
  • sourcebot_context (34-34)
packages/web/src/features/search/schemas.ts (1)
  • fileSourceResponseSchema (101-104)
packages/web/src/lib/utils.ts (1)
  • base64Decode (191-194)
packages/agents/reviewAgent/src/app.ts (3)
packages/agents/reviewAgent/src/nodes/github_pr_parser.ts (1)
  • github_pr_parser (6-63)
packages/agents/reviewAgent/src/nodes/generate_pr_reviews.ts (1)
  • generate_pr_reviews (6-49)
packages/agents/reviewAgent/src/nodes/github_push_pr_reviews.ts (1)
  • github_push_pr_reviews (4-40)
packages/agents/reviewAgent/src/nodes/generate_diff_review_prompt.ts (1)
packages/agents/reviewAgent/src/types.ts (3)
  • sourcebot_diff (7-7)
  • sourcebot_context (34-34)
  • sourcebot_diff_review_schema (36-40)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: build

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 16

🧹 Nitpick comments (18)
packages/web/src/app/[domain]/agents/page.tsx (6)

1-7: Remove unused imports.

There are several imports that are never used in the component:

  • Header (line 1)
  • Image (line 3)
  • FaRobot (line 5)

This increases bundle size unnecessarily.

-import { Header } from "../components/header";
+import { NavigationMenu } from "../components/navigationMenu";
 import Link from "next/link";
-import Image from "next/image";
-import { NavigationMenu } from "../components/navigationMenu";
-import { FaRobot, FaCogs } from "react-icons/fa";
+import { FaCogs } from "react-icons/fa";
 import { MdRocketLaunch } from "react-icons/md";

8-17: Move agent data outside the component for better maintainability.

The agents array is defined inline within the module but outside of the component, which is good. However, consider moving this data to a separate file or service as the number of agents grows, especially since the comment suggests more agents will be added.

As the application grows, you might want to fetch this data from an API endpoint rather than hardcoding it. If you decide to keep it as a static array for now, consider:

+// Consider moving to a separate file like: src/app/[domain]/agents/agentConfig.ts
 const agents = [
   {
     id: "review-agent",
     name: "Review Agent",
     description: "An agent that reviews your PRs. Uses the code indexed on Sourcebot to provide codebase wide context.",
     deployUrl: "/agents/review-agent/deploy",
     configureUrl: "/agents/review-agent/configure",
   },
   // Add more agents here as needed
 ];

26-38: Simplify conditional class logic with a more maintainable approach.

The conditional class logic is repeated in two places, making the code less DRY. Consider extracting this logic or using a utility like clsx or tailwind-merge to handle conditional classes more elegantly.

+// Add at the top of your file
+import clsx from 'clsx';

// Then in the component
 <div
-  className={
-    agents.length === 1
-      ? "flex justify-center items-center min-h-[60vh]"
-      : "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-10"
-  }
+  className={clsx(
+    agents.length === 1
+      ? "flex justify-center items-center min-h-[60vh]"
+      : "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-10"
+  )}
 >
   {agents.map((agent) => (
     <div
       key={agent.id}
-      className={
-        agents.length === 1
-          ? "relative flex flex-col items-center border border-border rounded-2xl p-8 bg-card shadow-xl w-full max-w-xl"
-          : "relative flex flex-col items-center border border-border rounded-2xl p-8 bg-card shadow-xl"
-      }
+      className={clsx(
+        "relative flex flex-col items-center border border-border rounded-2xl p-8 bg-card shadow-xl",
+        agents.length === 1 && "w-full max-w-xl"
+      )}
     >

45-47: Ensure description height is consistent for better UI alignment.

The description has a min-height of 56px, which helps maintain consistent card heights. However, if descriptions vary significantly in length, consider adding truncation or handling overflow gracefully.

-<p className="text-base text-muted-foreground text-center mb-4 min-h-[56px]">
+<p className="text-base text-muted-foreground text-center mb-4 min-h-[56px] line-clamp-2">
   {agent.description}
 </p>

This will limit the description to two lines and add an ellipsis if it's longer, maintaining visual consistency.


21-22: Add page title and improve layout structure.

The page is missing a title section. For better UX and accessibility, add a page title that clearly indicates what this page is about.

 <div className="flex flex-col items-center overflow-hidden min-h-screen">
   <NavigationMenu domain={domain} />
+  <div className="w-full max-w-6xl px-4 mt-8">
+    <h1 className="text-3xl font-bold text-foreground mb-4">AI Agents</h1>
+    <p className="text-muted-foreground mb-8">Deploy and configure AI agents to enhance your workflow.</p>
+  </div>
   <div className="w-full max-w-6xl px-4 mt-12 mb-24">

50-63: Improve button accessibility.

The action buttons look good visually, but we should ensure they're fully accessible. Consider adding aria labels for screen readers, especially since the buttons use icons.

 <Link
   href={agent.deployUrl}
+  aria-label={`Deploy ${agent.name}`}
   className="flex items-center justify-center gap-2 px-5 py-2.5 rounded-md bg-primary text-primary-foreground font-mono font-semibold text-base border border-primary shadow-sm hover:bg-primary/80 focus:outline-none focus:ring-2 focus:ring-primary/60 transition w-1/2"
 >
   <MdRocketLaunch className="text-lg" /> Deploy
 </Link>
 <Link
   href={agent.configureUrl}
+  aria-label={`Configure ${agent.name}`}
   className="flex items-center justify-center gap-2 px-5 py-2.5 rounded-md bg-muted text-foreground font-mono font-semibold text-base border border-border shadow-sm hover:bg-card/80 focus:outline-none focus:ring-2 focus:ring-border/60 transition w-1/2"
 >
   <FaCogs className="text-lg" /> Configure
 </Link>
packages/agents/review-agent/src/nodes/invoke_diff_review_llm.ts (1)

22-22: Limit logging of OpenAI response for security and performance.

Logging the entire OpenAI response could expose sensitive information and clutter logs with large amounts of data.

-        console.log("OpenAI response: ", openaiResponse);
+        console.log("OpenAI response received with length: ", openaiResponse?.length || 0);
packages/agents/review-agent/src/nodes/generate_diff_review_prompt.ts (3)

7-40: Consider using a more structured approach for prompt generation.

The current large template literal with many interpolated values could become difficult to maintain as the prompt complexity grows.

Consider refactoring to use a more structured approach:

-    const prompt = `
-    You are an expert software engineer that excells at reviewing code changes. Given the input, additional context, and rules defined below, review the code changes and provide a detailed review. The review you provide
-    must conform to all of the rules defined below. The output format of your review must conform to the output format defined below.
-
-    # Input
-
-    The input is the old and new code snippets, which represent a single hunk from a git diff. The old code snippet is the code before the changes were made, and the new code snippet is the code after the changes were made. Each code snippet
-    is a sequence of lines each with a line number.
-
-    ## Old Code Snippet
-
-    \`\`\`
-    ${diff.oldSnippet}
-    \`\`\`
-
-    ## New Code Snippet
-
-    \`\`\`
-    ${diff.newSnippet}
-    \`\`\`
-
-    # Additional Context
-
-    ${context.map(c => `${c.type}: ${c.description}\n\n${c.context}`).join("\n\n----------------------\n\n")}
-
-    # Rules
-
-    - ${rules.join("\n- ")}
-
-    # Output Format (JSON Schema)
-    The output must be a valid JSON object that conforms to the following JSON schema. Do NOT respond with anything other than the JSON object. Do NOT respond with
-    the JSON object in a markdown code block.
-    ${JSON.stringify(zodToJsonSchema(sourcebot_diff_review_schema), null, 2)}
-    `;
+    const sections = {
+        introduction: "You are an expert software engineer that excells at reviewing code changes. Given the input, additional context, and rules defined below, review the code changes and provide a detailed review. The review you provide must conform to all of the rules defined below. The output format of your review must conform to the output format defined below.",
+        input: {
+            description: "The input is the old and new code snippets, which represent a single hunk from a git diff. The old code snippet is the code before the changes were made, and the new code snippet is the code after the changes were made. Each code snippet is a sequence of lines each with a line number.",
+            oldSnippet: diff.oldSnippet,
+            newSnippet: diff.newSnippet
+        },
+        additionalContext: context.map(c => ({ type: c.type, description: c.description, context: c.context })),
+        rules: rules,
+        outputFormat: {
+            description: "The output must be a valid JSON object that conforms to the following JSON schema. Do NOT respond with anything other than the JSON object. Do NOT respond with the JSON object in a markdown code block.",
+            schema: JSON.stringify(zodToJsonSchema(sourcebot_diff_review_schema), null, 2)
+        }
+    };
+
+    const formatSection = (title, content) => `# ${title}\n\n${content}`;
+    
+    // Cache the JSON schema conversion to avoid doing it on every call
+    const jsonSchema = JSON.stringify(zodToJsonSchema(sourcebot_diff_review_schema), null, 2);
+
+    const prompt = [
+        sections.introduction,
+        formatSection("Input", sections.input.description),
+        "## Old Code Snippet",
+        "```",
+        sections.input.oldSnippet,
+        "```",
+        "## New Code Snippet",
+        "```",
+        sections.input.newSnippet,
+        "```",
+        formatSection("Additional Context", sections.additionalContext.map(c => `${c.type}: ${c.description}\n\n${c.context}`).join("\n\n----------------------\n\n")),
+        formatSection("Rules", `- ${sections.rules.join("\n- ")}`),
+        formatSection("Output Format (JSON Schema)", `${sections.outputFormat.description}\n${jsonSchema}`)
+    ].join("\n\n");

You could also consider moving this prompt template to a separate file for better maintainability.


39-39: Cache the JSON schema conversion.

Converting the Zod schema to a JSON schema on every function call could be inefficient, especially for complex schemas.

+// Move outside the function to cache the conversion
+const diffReviewJsonSchema = JSON.stringify(zodToJsonSchema(sourcebot_diff_review_schema), null, 2);

export const generate_diff_review_prompt = async (diff: sourcebot_diff, context: sourcebot_context[], rules: string[]) => {
    // ...

    // Then use the cached schema in the template
-    ${JSON.stringify(zodToJsonSchema(sourcebot_diff_review_schema), null, 2)}
+    ${diffReviewJsonSchema}

8-8: Typo in prompt text.

There's a spelling error in the prompt text.

-    You are an expert software engineer that excells at reviewing code changes. Given the input, additional context, and rules defined below, review the code changes and provide a detailed review. The review you provide
+    You are an expert software engineer that excels at reviewing code changes. Given the input, additional context, and rules defined below, review the code changes and provide a detailed review. The review you provide
packages/agents/review-agent/src/nodes/generate_pr_reviews.ts (1)

34-36: Improve error tracking and reporting.

Currently, errors during processing are logged but not aggregated, making it hard to determine the overall success rate.

+    const errors: {filename: string, error: string}[] = [];
     for (const file_diff of pr_payload.file_diffs) {
         // ... existing code

         } catch (error) {
             console.error(`Error fetching file content for ${file_diff.to}: ${error}`);
+            errors.push({filename: file_diff.to, error: error.message || String(error)});
         }
     }

     console.log("Completed generate_pr_reviews");
+    if (errors.length > 0) {
+        console.log(`Completed with ${errors.length} errors out of ${pr_payload.file_diffs.length} files`);
+    }
packages/agents/review-agent/src/nodes/github_push_pr_reviews.ts (2)

21-21: Make diff side configurable.

The side is hardcoded to "RIGHT", assuming reviews are always on the new version of the file.

-                        side: "RIGHT",
+                        side: process.env.GITHUB_REVIEW_SIDE || "RIGHT",

30-32: Improve error message detail.

The current error message doesn't provide enough context about which specific review comment failed.

-                    console.error(`Error pushing pr reviews for ${file_diff_review.filename}: ${error}`);
+                    console.error(`Error pushing review comment for ${file_diff_review.filename} at line ${review.line_start}-${review.line_end}: ${error.status ? `HTTP ${error.status}: ` : ''}${error.message || String(error)}`);
packages/agents/review-agent/src/nodes/github_pr_parser.ts (2)

26-35: Improve snippet formatting with newlines for better readability.

The current implementation concatenates line content without newlines, making the snippets harder to read.

 for (const change of chunk.changes) {
     if (change.type === "normal") {
-        oldSnippet += change.ln1 + ":" + change.content;
-        newSnippet += change.ln2 + ":" + change.content;
+        oldSnippet += change.ln1 + ":" + change.content + "\n";
+        newSnippet += change.ln2 + ":" + change.content + "\n";
     } else if (change.type === "add") {
-        newSnippet += change.ln + ":" + change.content;
+        newSnippet += change.ln + ":" + change.content + "\n";
     } else if (change.type === "del") {
-        oldSnippet += change.ln + ":" + change.content;
+        oldSnippet += change.ln + ":" + change.content + "\n";
     }
 }

61-61: Avoid non-null assertion for installation ID.

Using the non-null assertion operator (!) is discouraged as it bypasses TypeScript's null checks. Since you've already validated the installation exists, simply use it directly.

-        installation_id: payload.installation!.id,
+        installation_id: payload.installation.id,
packages/agents/review-agent/src/nodes/fetch_file_content.ts (1)

15-23: Add timeout and retry logic for fetch operations.

Network requests can fail or timeout. Adding timeout and retry logic improves resilience.

+    const MAX_RETRIES = 3;
+    const TIMEOUT_MS = 10000;
+    
+    let retries = 0;
+    let response;
+    
+    while (retries < MAX_RETRIES) {
+        try {
+            const controller = new AbortController();
+            const timeoutId = setTimeout(() => controller.abort(), TIMEOUT_MS);
+            
+            response = await fetch('http://localhost:3000/api/source', {
+                method: 'POST',
+                headers: {
+                    'Content-Type': 'application/json',
+                    'X-Org-Domain': '~'
+                },
+                body: JSON.stringify(fileSourceRequest),
+                signal: controller.signal
+            });
+            
+            clearTimeout(timeoutId);
+            
+            if (response.ok) break;
+            retries++;
+            
+            if (retries < MAX_RETRIES) {
+                console.log(`Retry ${retries}/${MAX_RETRIES} for ${filename}`);
+                await new Promise(resolve => setTimeout(resolve, 1000 * retries));
+            }
+        } catch (error) {
+            retries++;
+            if (error instanceof DOMException && error.name === 'AbortError') {
+                console.error(`Request timed out for ${filename}`);
+            } else {
+                console.error(`Error fetching ${filename}:`, error);
+            }
+            
+            if (retries >= MAX_RETRIES) throw error;
+            
+            console.log(`Retry ${retries}/${MAX_RETRIES} for ${filename}`);
+            await new Promise(resolve => setTimeout(resolve, 1000 * retries));
+        }
+    }
+    
-    if (!response.ok) {
+    if (!response || !response.ok) {
         throw new Error(`Failed to fetch file content for ${filename} from ${repoPath}: ${response.statusText}`);
     }
packages/agents/review-agent/src/types.ts (2)

17-18: Allow empty strings for PR title and description.

In some cases, PRs might have empty descriptions. Consider using z.string().optional().default("") for more flexibility.

-    title: z.string(),
-    description: z.string(),
+    title: z.string().min(1, "PR title cannot be empty"),
+    description: z.string().optional().default(""),

29-34: Add documentation for context type enum values.

The enum for context types would benefit from documentation explaining what each type represents.

 export const sourcebot_context_schema = z.object({
-    type: z.enum(["pr_title", "pr_description", "pr_summary", "comment_chains", "file_content"]),
+    type: z.enum([
+        "pr_title",       // The title of the pull request
+        "pr_description", // The description/body of the pull request
+        "pr_summary",     // A generated summary of the pull request
+        "comment_chains", // Existing comment threads on the pull request
+        "file_content"    // The content of a specific file
+    ]),
     description: z.string().optional(),
     context: z.string(),
 });
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 17fce5c and 12002f2.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (15)
  • .env.development (1 hunks)
  • package.json (1 hunks)
  • packages/agents/review-agent/.gitignore (1 hunks)
  • packages/agents/review-agent/package.json (1 hunks)
  • packages/agents/review-agent/src/app.ts (1 hunks)
  • packages/agents/review-agent/src/env.ts (1 hunks)
  • packages/agents/review-agent/src/nodes/fetch_file_content.ts (1 hunks)
  • packages/agents/review-agent/src/nodes/generate_diff_review_prompt.ts (1 hunks)
  • packages/agents/review-agent/src/nodes/generate_pr_reviews.ts (1 hunks)
  • packages/agents/review-agent/src/nodes/github_pr_parser.ts (1 hunks)
  • packages/agents/review-agent/src/nodes/github_push_pr_reviews.ts (1 hunks)
  • packages/agents/review-agent/src/nodes/invoke_diff_review_llm.ts (1 hunks)
  • packages/agents/review-agent/src/types.ts (1 hunks)
  • packages/agents/review-agent/tsconfig.json (1 hunks)
  • packages/web/src/app/[domain]/agents/page.tsx (1 hunks)
✅ Files skipped from review due to trivial changes (6)
  • package.json
  • .env.development
  • packages/agents/review-agent/src/env.ts
  • packages/agents/review-agent/tsconfig.json
  • packages/agents/review-agent/.gitignore
  • packages/agents/review-agent/package.json
🧰 Additional context used
🧬 Code Graph Analysis (5)
packages/agents/review-agent/src/nodes/github_push_pr_reviews.ts (1)
packages/agents/review-agent/src/types.ts (2)
  • sourcebot_pr_payload (27-27)
  • sourcebot_file_diff_review (47-47)
packages/agents/review-agent/src/app.ts (3)
packages/agents/review-agent/src/nodes/github_pr_parser.ts (1)
  • github_pr_parser (6-63)
packages/agents/review-agent/src/nodes/generate_pr_reviews.ts (1)
  • generate_pr_reviews (6-49)
packages/agents/review-agent/src/nodes/github_push_pr_reviews.ts (1)
  • github_push_pr_reviews (4-40)
packages/agents/review-agent/src/nodes/fetch_file_content.ts (3)
packages/agents/review-agent/src/types.ts (2)
  • sourcebot_pr_payload (27-27)
  • sourcebot_context (34-34)
packages/web/src/features/search/schemas.ts (1)
  • fileSourceResponseSchema (101-104)
packages/web/src/lib/utils.ts (1)
  • base64Decode (191-194)
packages/agents/review-agent/src/nodes/github_pr_parser.ts (1)
packages/agents/review-agent/src/types.ts (3)
  • sourcebot_pr_payload (27-27)
  • sourcebot_file_diff (14-14)
  • sourcebot_diff (7-7)
packages/agents/review-agent/src/nodes/generate_diff_review_prompt.ts (1)
packages/agents/review-agent/src/types.ts (3)
  • sourcebot_diff (7-7)
  • sourcebot_context (34-34)
  • sourcebot_diff_review_schema (36-40)

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 11

🧹 Nitpick comments (15)
packages/web/src/features/agents/review-agent/nodes/github_push_pr_reviews.ts (1)

4-37: Well-structured GitHub review comment creation with proper error handling.

The implementation efficiently posts review comments to GitHub PRs with:

  • Appropriate handling of single vs. multi-line comments
  • Error handling at both individual comment and overall process levels
  • Proper logging for tracking execution flow

Consider adding a delay between API calls to avoid hitting GitHub's rate limits:

                try {
                    await octokit.rest.pulls.createReviewComment({
                        owner: pr_payload.owner,
                        repo: pr_payload.repo,
                        pull_number: pr_payload.number,
                        body: review.review,
                        path: file_diff_review.filename,
                        commit_id: pr_payload.head_sha,
                        side: "RIGHT",
                        ...(review.line_start === review.line_end
                            ? { line: review.line_start }
                            : {
                                start_line: review.line_start,
                                line: review.line_end,
                                start_side: "RIGHT",
                            }),
                    });
+                   // Add a small delay to avoid hitting GitHub's rate limits
+                   await new Promise(resolve => setTimeout(resolve, 1000));
                } catch (error) {
packages/web/src/app/api/(server)/webhook/route.ts (2)

10-24: Consider moving GitHub App initialization to a separate utility function.

The module-level initialization of the GitHub App is appropriate but could be improved for maintainability.

-let githubApp: App | undefined;
-if (env.GITHUB_APP_ID && env.GITHUB_APP_WEBHOOK_SECRET && env.GITHUB_APP_PRIVATE_KEY_PATH) {
-    try {
-        const privateKey = fs.readFileSync(env.GITHUB_APP_PRIVATE_KEY_PATH, "utf8");
-        githubApp = new App({
-            appId: env.GITHUB_APP_ID,
-            privateKey: privateKey,
-            webhooks: {
-                secret: env.GITHUB_APP_WEBHOOK_SECRET,
-            },
-        });
-    } catch (error) {
-        console.error(`Error initializing GitHub app: ${error}`);
-    }
-}
+import { initializeGitHubApp } from "@/utils/github";
+
+// This will be lazily initialized when needed
+let githubApp: App | undefined;
+
+function getGitHubApp(): App | undefined {
+    if (githubApp === undefined) {
+        githubApp = initializeGitHubApp(
+            env.GITHUB_APP_ID,
+            env.GITHUB_APP_WEBHOOK_SECRET,
+            env.GITHUB_APP_PRIVATE_KEY_PATH
+        );
+    }
+    return githubApp;
+}

26-28: Enhanced type guard for pull request events.

The current implementation correctly identifies pull request events for "opened" and "synchronize" actions.

Consider adding support for more PR events like "reopened" that might also benefit from automated reviews:

function isPullRequestEvent(eventHeader: string, payload: unknown): payload is WebhookEventDefinition<"pull-request-opened"> | WebhookEventDefinition<"pull-request-synchronize"> {
-    return eventHeader === "pull_request" && typeof payload === "object" && payload !== null && "action" in payload && typeof payload.action === "string" && (payload.action === "opened" || payload.action === "synchronize");
+    const supportedActions = ["opened", "synchronize", "reopened"];
+    return eventHeader === "pull_request" && 
+           typeof payload === "object" && 
+           payload !== null && 
+           "action" in payload && 
+           typeof payload.action === "string" && 
+           supportedActions.includes(payload.action);
}
packages/web/src/features/agents/review-agent/nodes/generate_diff_review_prompt.ts (4)

4-4: Function can be synchronous

This function doesn't perform any asynchronous operations internally, so it doesn't need to be marked as async. While it works as-is, removing the async keyword would better reflect the function's synchronous nature.

-export const generate_diff_review_prompt = async (diff: sourcebot_diff, context: sourcebot_context[], rules: string[]) => {
+export const generate_diff_review_prompt = (diff: sourcebot_diff, context: sourcebot_context[], rules: string[]) => {

8-9: Fix typo in prompt text

There's a spelling error in the word "excells" which should be "excels".

-    You are an expert software engineer that excells at reviewing code changes. Given the input, additional context, and rules defined below, review the code changes and provide a detailed review. The review you provide
+    You are an expert software engineer that excels at reviewing code changes. Given the input, additional context, and rules defined below, review the code changes and provide a detailed review. The review you provide

7-40: Consider reducing extra indentation in the prompt template

The template string has 4-space indentation that will be included in the prompt sent to the LLM. This extra indentation isn't necessary and might make the prompt longer than needed. Consider removing the leading spaces to create a cleaner prompt.

    const prompt = `
-    You are an expert software engineer that excells at reviewing code changes. Given the input, additional context, and rules defined below, review the code changes and provide a detailed review. The review you provide
-    must conform to all of the rules defined below. The output format of your review must conform to the output format defined below.
+You are an expert software engineer that excels at reviewing code changes. Given the input, additional context, and rules defined below, review the code changes and provide a detailed review. The review you provide
+must conform to all of the rules defined below. The output format of your review must conform to the output format defined below.

-    # Input
+# Input

-    The input is the old and new code snippets, which represent a single hunk from a git diff. The old code snippet is the code before the changes were made, and the new code snippet is the code after the changes were made. Each code snippet
-    is a sequence of lines each with a line number.
+The input is the old and new code snippets, which represent a single hunk from a git diff. The old code snippet is the code before the changes were made, and the new code snippet is the code after the changes were made. Each code snippet
+is a sequence of lines each with a line number.

30-30: Consider handling empty context array

The current implementation doesn't handle the case where the context array is empty, which could result in an empty "Additional Context" section. Consider adding a check to output a meaningful message when no context is available.

-    ${context.map(c => `${c.type}: ${c.description}\n\n${c.context}`).join("\n\n----------------------\n\n")}
+    ${context.length > 0 
+      ? context.map(c => `${c.type}: ${c.description}\n\n${c.context}`).join("\n\n----------------------\n\n")
+      : "No additional context available."}
packages/web/src/features/agents/review-agent/nodes/generate_pr_reviews.ts (2)

14-36: Handle errors from invoke_diff_review_llm separately

The current implementation catches all errors in the same catch block, not distinguishing between errors from fetch_file_content and invoke_diff_review_llm. This could lead to confusion during debugging.

Consider separating the error handling logic for better clarity:

try {
    const fileContentContext = await fetch_file_content(pr_payload, file_diff.to);
    const context: sourcebot_context[] = [
        /* context items */
    ];

    const prompt = await generate_diff_review_prompt(diff, context, rules);
    
+   try {
        const diffReview = await invoke_diff_review_llm(prompt);
        reviews.push(diffReview);
+   } catch (llmError) {
+       console.error(`Error invoking LLM for review of ${file_diff.to}: ${llmError}`);
+   }
} catch (error) {
-   console.error(`Error fetching file content for ${file_diff.to}: ${error}`);
+   console.error(`Error fetching file content or generating prompt for ${file_diff.to}: ${error}`);
}

41-42: Consider handling renamed files explicitly

The current implementation only uses file_diff.to for the filename, which might not properly handle renamed files. Consider keeping track of both the original and new filenames.

file_diff_reviews.push({
    filename: file_diff.to,
+   originalFilename: file_diff.from,
+   wasRenamed: file_diff.from !== file_diff.to,
    reviews: reviews,
});
packages/web/src/features/agents/review-agent/nodes/fetch_file_content.ts (1)

5-40: Consider implementing caching for repeated file content requests

The current implementation fetches file content for each diff chunk individually, which could result in multiple requests for the same file. Consider implementing a caching mechanism to avoid redundant fetches:

+// Add to imports
+import { LRUCache } from 'lru-cache';

+// Create a cache for file content (outside the function)
+const fileContentCache = new LRUCache<string, sourcebot_context>({
+   max: 100, // Maximum number of items to store
+   ttl: 1000 * 60 * 5, // 5 minute TTL
+});

export const fetch_file_content = async (pr_payload: sourcebot_pr_payload, filename: string): Promise<sourcebot_context> => {
    console.log("Executing fetch_file_content");

+   // Create a cache key combining repository and filename
+   const cacheKey = `${pr_payload.hostDomain}/${pr_payload.owner}/${pr_payload.repo}/${filename}`;
+   
+   // Check if file content is already in cache
+   const cachedContent = fileContentCache.get(cacheKey);
+   if (cachedContent) {
+       console.log(`Using cached content for ${filename}`);
+       return cachedContent;
+   }

    // Original fetch logic...
    
    const fileContentContext: sourcebot_context = {
        type: "file_content",
        description: `The content of the file ${filename}`,
        context: fileContent,
    }

+   // Store in cache
+   fileContentCache.set(cacheKey, fileContentContext);

    console.log("Completed fetch_file_content");
    return fileContentContext;
}
packages/web/src/features/agents/review-agent/nodes/github_pr_parser.ts (3)

61-61: Avoid non-null assertion for installation_id

The code uses a non-null assertion (!) for the installation ID, even though you've already checked for its presence earlier. Consider using a safer approach:

-installation_id: payload.installation!.id,
+installation_id: payload.installation?.id ?? 
+   (() => { throw new Error("Installation ID should be present but is missing"); })(),

Or alternatively, since you've already checked for the installation's presence at the start of the function, you can simply use:

-installation_id: payload.installation!.id,
+installation_id: payload.installation.id,

16-48: Consider detecting and skipping binary files

The current implementation doesn't handle binary files explicitly, which could lead to issues when trying to parse them as text. Consider adding detection and skipping of binary files:

const sourcebotFileDiffs: (sourcebot_file_diff | null)[] = parsedDiff.map((file) => {
    if (!file.from || !file.to) {
        console.log(`Skipping file due to missing from (${file.from}) or to (${file.to})`)
        return null;
    }
    
+   // Skip binary files that shouldn't be diffed
+   if (file.binary) {
+       console.log(`Skipping binary file: ${file.to}`);
+       return null;
+   }
+   
+   // Check file extension for common binary types
+   const binaryExtensions = ['.png', '.jpg', '.jpeg', '.gif', '.pdf', '.zip', '.exe'];
+   if (binaryExtensions.some(ext => file.to.toLowerCase().endsWith(ext))) {
+       console.log(`Skipping likely binary file based on extension: ${file.to}`);
+       return null;
+   }

    const diffs: sourcebot_diff[] = file.chunks.map((chunk) => {
        // Existing code
    });

    return {
        from: file.from,
        to: file.to,
        diffs: diffs,
    }
});

22-41: Handle large diffs more efficiently

The current implementation might struggle with very large diffs. Consider adding limits or warnings for unusually large diffs:

const diffs: sourcebot_diff[] = file.chunks.map((chunk) => {
+   // Check for excessive chunk size that might impact performance
+   if (chunk.changes.length > 300) {
+       console.warn(`Large diff chunk with ${chunk.changes.length} changes in ${file.to}. This may impact performance.`);
+   }

    let oldSnippet = "";
    let newSnippet = "";

+   // Limit very large diffs to a reasonable size if needed
+   const changesToProcess = chunk.changes.length > 500 ? 
+       chunk.changes.slice(0, 500) : 
+       chunk.changes;
+   
+   if (changesToProcess.length < chunk.changes.length) {
+       console.warn(`Truncated diff chunk from ${chunk.changes.length} to ${changesToProcess.length} changes for ${file.to}`);
+       oldSnippet += "// Note: This diff was truncated as it was too large\n";
+       newSnippet += "// Note: This diff was truncated as it was too large\n";
+   }

-   for (const change of chunk.changes) {
+   for (const change of changesToProcess) {
        // Existing code for processing changes
    }

    return {
        oldSnippet: oldSnippet,
        newSnippet: newSnippet,
    }
});
packages/web/src/features/agents/review-agent/app.ts (2)

8-14: Consider extracting rules to a separate module.

The rules array defines important constraints for the review agent, but it's embedded directly in this file. Extracting it to a separate module (e.g., review-rules.ts) would improve maintainability and allow reuse across different parts of the application.

- const rules = [
-     "Do NOT provide general feedback, summaries, explanations of changes, or praises for making good additions.",
-     "Do NOT provide any advice that is not actionable or directly related to the changes.",
-     "Focus solely on offering specific, objective insights based on the given context and refrain from making broad comments about potential impacts on the system or question intentions behind the changes.",
-     "Keep comments concise and to the point. Every comment must highlight a specific issue and provide a clear and actionable solution to the developer.",
-     "If there are no issues found on a line range, do NOT respond with any comments. This includes comments such as \"No issues found\" or \"LGTM\"."
- ]

Then import it:

import { REVIEW_RULES } from "@/features/agents/review-agent/constants/review-rules";

16-16: Add JSDoc comment to document the function.

Adding a JSDoc comment would help explain the purpose, parameters, and expected behavior of this function.

+/**
+ * Processes a GitHub pull request webhook event to generate and post automated code reviews.
+ * Only processes events with action types "opened" or "synchronize".
+ * 
+ * @param octokit - The authenticated Octokit instance for GitHub API calls
+ * @param payload - The GitHub webhook payload containing pull request data
+ */
 export async function processGitHubPullRequest(octokit: Octokit, payload: WebhookEventDefinition<"pull-request-opened"> | WebhookEventDefinition<"pull-request-synchronize">) {
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 12002f2 and a81cd24.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (13)
  • package.json (2 hunks)
  • packages/web/package.json (2 hunks)
  • packages/web/src/app/[domain]/browse/[...path]/page.tsx (1 hunks)
  • packages/web/src/app/api/(server)/webhook/route.ts (1 hunks)
  • packages/web/src/env.mjs (1 hunks)
  • packages/web/src/features/agents/review-agent/app.ts (1 hunks)
  • packages/web/src/features/agents/review-agent/nodes/fetch_file_content.ts (1 hunks)
  • packages/web/src/features/agents/review-agent/nodes/generate_diff_review_prompt.ts (1 hunks)
  • packages/web/src/features/agents/review-agent/nodes/generate_pr_reviews.ts (1 hunks)
  • packages/web/src/features/agents/review-agent/nodes/github_pr_parser.ts (1 hunks)
  • packages/web/src/features/agents/review-agent/nodes/github_push_pr_reviews.ts (1 hunks)
  • packages/web/src/features/agents/review-agent/nodes/invoke_diff_review_llm.ts (1 hunks)
  • packages/web/src/features/agents/review-agent/types.ts (1 hunks)
✅ Files skipped from review due to trivial changes (3)
  • packages/web/src/app/[domain]/browse/[...path]/page.tsx
  • packages/web/src/env.mjs
  • packages/web/src/features/agents/review-agent/types.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • package.json
🧰 Additional context used
🧬 Code Graph Analysis (6)
packages/web/src/app/api/(server)/webhook/route.ts (2)
packages/backend/src/env.ts (1)
  • env (22-52)
packages/web/src/features/agents/review-agent/app.ts (1)
  • processGitHubPullRequest (16-27)
packages/web/src/features/agents/review-agent/nodes/github_push_pr_reviews.ts (1)
packages/web/src/features/agents/review-agent/types.ts (2)
  • sourcebot_pr_payload (27-27)
  • sourcebot_file_diff_review (47-47)
packages/web/src/features/agents/review-agent/nodes/fetch_file_content.ts (3)
packages/web/src/features/agents/review-agent/types.ts (2)
  • sourcebot_pr_payload (27-27)
  • sourcebot_context (34-34)
packages/web/src/features/search/schemas.ts (1)
  • fileSourceResponseSchema (101-104)
packages/web/src/lib/utils.ts (1)
  • base64Decode (191-194)
packages/web/src/features/agents/review-agent/nodes/generate_diff_review_prompt.ts (1)
packages/web/src/features/agents/review-agent/types.ts (3)
  • sourcebot_diff (7-7)
  • sourcebot_context (34-34)
  • sourcebot_diff_review_schema (36-40)
packages/web/src/features/agents/review-agent/nodes/github_pr_parser.ts (2)
packages/web/src/features/agents/review-agent/types.ts (3)
  • sourcebot_pr_payload (27-27)
  • sourcebot_file_diff (14-14)
  • sourcebot_diff (7-7)
packages/web/src/app/error.tsx (1)
  • Error (14-54)
packages/web/src/features/agents/review-agent/nodes/generate_pr_reviews.ts (4)
packages/web/src/features/agents/review-agent/types.ts (4)
  • sourcebot_pr_payload (27-27)
  • sourcebot_file_diff_review (47-47)
  • sourcebot_diff_review (41-41)
  • sourcebot_context (34-34)
packages/web/src/features/agents/review-agent/nodes/fetch_file_content.ts (1)
  • fetch_file_content (5-40)
packages/web/src/features/agents/review-agent/nodes/generate_diff_review_prompt.ts (1)
  • generate_diff_review_prompt (4-44)
packages/web/src/features/agents/review-agent/nodes/invoke_diff_review_llm.ts (1)
  • invoke_diff_review_llm (5-41)
🔇 Additional comments (3)
packages/web/package.json (2)

121-123: Dependencies for GitHub integration and AI code reviews look appropriate.

The added dependencies align with the new review agent feature:

  • octokit for GitHub API interactions
  • openai for AI model access
  • parse-diff for handling Git diffs

141-142:

✅ Verification successful

Ensure zod-to-json-schema is compatible with your zod version.

The addition of zod-to-json-schema alongside the existing zod package is appropriate for generating JSON schemas from Zod schemas, which will be useful for validating API responses.


🏁 Script executed:

#!/bin/bash
# Check if zod-to-json-schema version 3.24.5 is compatible with zod version 3.24.2
npm view [email protected] peerDependencies

Length of output: 72


zod-to-json-schema Compatibility Verified

[email protected] declares a peerDependency of zod: ^3.24.1, and your project uses zod: ^3.24.2, which satisfies that range. No further action is needed.

packages/web/src/features/agents/review-agent/nodes/invoke_diff_review_llm.ts (1)

8-11: Good error handling for missing API key.

The check for the presence of the OPENAI_API_KEY environment variable is appropriate, failing fast with a clear error message.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 9

🧹 Nitpick comments (12)
packages/web/src/features/agents/review-agent/nodes/invokeDiffReviewLlm.ts (2)

19-42: Add better error handling for OpenAI API responses

The current implementation handles API errors but could be more robust when dealing with invalid or unexpected responses.

Enhance error handling, particularly for JSON parsing:

    try {
        const completion = await openai.chat.completions.create({
          model: "gpt-4.1-mini",
          messages: [
            { role: "system", content: prompt }
          ]
        });
    
        const openaiResponse = completion.choices[0].message.content;
        console.log("OpenAI response: ", openaiResponse);
        
-       const diffReviewJson = JSON.parse(openaiResponse || '{}');
+       if (!openaiResponse) {
+           throw new Error("Empty response from OpenAI API");
+       }
+       
+       let diffReviewJson;
+       try {
+           diffReviewJson = JSON.parse(openaiResponse);
+       } catch (parseError) {
+           console.error("Failed to parse OpenAI response as JSON:", openaiResponse);
+           throw new Error(`Invalid JSON response: ${parseError.message}`);
+       }
+       
        const diffReview = sourcebot_diff_review_schema.safeParse(diffReviewJson);

        if (!diffReview.success) {
            throw new Error(`Invalid diff review format: ${diffReview.error}`);
        }

        console.log("Completed invoke_diff_review_llm");
        return diffReview.data;
    } catch (error) {
        console.error('Error calling OpenAI:', error);
        throw error;
    }

6-41: Consider using a structured logging approach

The code contains multiple console.log/console.error statements that would be better handled with a proper logging library.

Replace direct console logging with a structured logging approach that supports different environments and log levels. This would make debugging easier and allow for better production monitoring.

packages/web/src/features/agents/review-agent/nodes/generateDiffReviewPrompt.ts (2)

7-40: Improve prompt template maintainability

The current prompt is constructed as a large template literal, which can be difficult to maintain, test, and update.

Consider moving the prompt template to a separate file or breaking it into smaller, more manageable sections. This would make it easier to update and maintain the prompt structure.

For example:

+ import { readFileSync } from 'fs';
+ import path from 'path';
+
+ const PROMPT_TEMPLATE = readFileSync(
+   path.join(process.cwd(), 'src/features/agents/review-agent/templates/diff_review_prompt.txt'),
+   'utf8'
+ );
+
  export const generateDiffReviewPrompt = async (diff: sourcebot_diff, context: sourcebot_context[], rules: string[]) => {
      console.log("Executing generate_diff_review_prompt");
          
-     const prompt = `
-     You are an expert software engineer that excells at reviewing code changes. Given the input, additional context, and rules defined below, review the code changes and provide a detailed review. The review you provide
-     must conform to all of the rules defined below. The output format of your review must conform to the output format defined below.
- 
-     # Input
- 
-     The input is the old and new code snippets, which represent a single hunk from a git diff. The old code snippet is the code before the changes were made, and the new code snippet is the code after the changes were made. Each code snippet
-     is a sequence of lines each with a line number.
- 
-     ## Old Code Snippet
- 
-     \`\`\`
-     ${diff.oldSnippet}
-     \`\`\`
- 
-     ## New Code Snippet
- 
-     \`\`\`
-     ${diff.newSnippet}
-     \`\`\`
- 
-     # Additional Context
- 
-     ${context.map(c => `${c.type}: ${c.description}\n\n${c.context}`).join("\n\n----------------------\n\n")}
- 
-     # Rules
- 
-     - ${rules.join("\n- ")}
- 
-     # Output Format (JSON Schema)
-     The output must be a valid JSON object that conforms to the following JSON schema. Do NOT respond with anything other than the JSON object. Do NOT respond with
-     the JSON object in a markdown code block.
-     ${JSON.stringify(zodToJsonSchema(sourcebot_diff_review_schema), null, 2)}
-     `;
+     const contextSection = context.length > 0 
+       ? context.map(c => `${c.type}: ${c.description}\n\n${c.context}`).join("\n\n----------------------\n\n")
+       : "No additional context provided.";
+
+     const rulesSection = rules.length > 0
+       ? `- ${rules.join("\n- ")}`
+       : "No specific rules provided.";
+
+     const schemaJson = JSON.stringify(zodToJsonSchema(sourcebot_diff_review_schema), null, 2);
+
+     const prompt = PROMPT_TEMPLATE
+       .replace('{{OLD_SNIPPET}}', diff.oldSnippet)
+       .replace('{{NEW_SNIPPET}}', diff.newSnippet)
+       .replace('{{CONTEXT}}', contextSection)
+       .replace('{{RULES}}', rulesSection)
+       .replace('{{JSON_SCHEMA}}', schemaJson);

Then create a template file diff_review_prompt.txt with placeholders.


30-30: Add safeguards for empty context arrays

The context mapping doesn't check if the context array is empty, which could lead to unexpected results.

Add a check for empty context arrays:

-    ${context.map(c => `${c.type}: ${c.description}\n\n${c.context}`).join("\n\n----------------------\n\n")}
+    ${context.length > 0 
+        ? context.map(c => `${c.type}: ${c.description}\n\n${c.context}`).join("\n\n----------------------\n\n")
+        : "No additional context provided."}
packages/web/src/features/agents/review-agent/nodes/githubPushPrReviews.ts (1)

8-32: Consider batch processing reviews for efficiency

The current implementation processes reviews sequentially in nested loops, which could be inefficient for PRs with many files and comments.

Consider implementing batch processing or parallel processing with controlled concurrency:

import { Octokit } from "octokit";
import { sourcebot_pr_payload, sourcebot_file_diff_review } from "@/features/agents/review-agent/types";
+import pLimit from 'p-limit';

export const githubPushPrReviews = async (octokit: Octokit, pr_payload: sourcebot_pr_payload, file_diff_reviews: sourcebot_file_diff_review[]) => {
    console.log("Executing github_push_pr_reviews");

+   const limit = pLimit(3); // Limit concurrent requests to 3
+   const promises = [];

    try {
        for (const file_diff_review of file_diff_reviews) {
            for (const review of file_diff_review.reviews) {
-               try {
-                   await octokit.rest.pulls.createReviewComment({
+               promises.push(limit(() => octokit.rest.pulls.createReviewComment({
                        owner: pr_payload.owner,
                        repo: pr_payload.repo,
                        pull_number: pr_payload.number,
                        body: review.review,
                        path: file_diff_review.filename,
                        commit_id: pr_payload.head_sha,
                        side: "RIGHT",
                        ...(review.line_start === review.line_end
                            ? { line: review.line_start }
                            : {
                                start_line: review.line_start,
                                line: review.line_end,
                                start_side: "RIGHT",
                            }),
-                   });
-               } catch (error) {
-                   console.error(`Error pushing pr reviews for ${file_diff_review.filename}: ${error}`);
-               }
+                   }).catch(error => {
+                       console.error(`Error pushing pr reviews for ${file_diff_review.filename}: ${error}`);
+                   })
+               ));
            }
        }
+       
+       await Promise.all(promises);
    } catch (error) {
        console.error(`Error pushing pr reviews: ${error}`);
    }

    console.log("Completed github_push_pr_reviews");
}

This would require adding p-limit as a dependency, but it provides a clean way to handle concurrent requests with a limit.

packages/web/src/features/agents/review-agent/nodes/generatePrReview.ts (1)

34-36: Enhance error handling with retries

The current error handling for file content fetching simply logs the error without attempting to retry or recover.

Implement a retry mechanism for transient failures:

import { sourcebot_pr_payload, sourcebot_diff_review, sourcebot_file_diff_review, sourcebot_context } from "@/features/agents/review-agent/types";
import { generateDiffReviewPrompt } from "@/features/agents/review-agent/nodes/generateDiffReviewPrompt";
import { invokeDiffReviewLlm } from "@/features/agents/review-agent/nodes/invokeDiffReviewLlm";
import { fetchFileContent } from "@/features/agents/review-agent/nodes/fetchFileContent";

+// Helper function to retry operations
+const retryOperation = async <T>(
+   operation: () => Promise<T>,
+   retries = 3,
+   delay = 1000,
+   backoff = 2
+): Promise<T> => {
+   let lastError: Error;
+   for (let attempt = 0; attempt < retries; attempt++) {
+       try {
+           return await operation();
+       } catch (error) {
+           lastError = error;
+           console.log(`Attempt ${attempt + 1} failed, retrying in ${delay}ms...`);
+           await new Promise(resolve => setTimeout(resolve, delay));
+           delay *= backoff; // Exponential backoff
+       }
+   }
+   throw lastError;
+};

export const generatePrReviews = async (pr_payload: sourcebot_pr_payload, rules: string[]): Promise<sourcebot_file_diff_review[]> => {
    console.log("Executing generate_pr_reviews");

    const file_diff_reviews: sourcebot_file_diff_review[] = [];
    for (const file_diff of pr_payload.file_diffs) {
        const reviews: sourcebot_diff_review[] = [];

        for (const diff of file_diff.diffs) {
            try {
-               const fileContentContext = await fetchFileContent(pr_payload, file_diff.to);
+               const fileContentContext = await retryOperation(() => fetchFileContent(pr_payload, file_diff.to));
                const context: sourcebot_context[] = [
                    {
                        type: "pr_title",
                        description: "The title of the pull request",
                        context: pr_payload.title,
                    },
                    {
                        type: "pr_description",
                        description: "The description of the pull request",
                        context: pr_payload.description,
                    },
                    fileContentContext,
                ];

                const prompt = await generateDiffReviewPrompt(diff, context, rules);
                
-               const diffReview = await invokeDiffReviewLlm(prompt);
+               const diffReview = await retryOperation(() => invokeDiffReviewLlm(prompt));
                reviews.push(diffReview);
            } catch (error) {
                console.error(`Error fetching file content for ${file_diff.to}: ${error}`);
            }
        }

This implementation adds a retry mechanism with exponential backoff for operations that might fail due to transient issues.

packages/web/src/features/agents/review-agent/nodes/fetchFileContent.ts (1)

6-6: Use structured logging for better traceability

The console logs are basic strings without structured data, making it harder to trace and filter logs. Consider using a structured logging approach with consistent fields.

-    console.log("Executing fetch_file_content");
+    console.log({
+        action: "fetch_file_content_start",
+        filename,
+        repo: `${pr_payload.owner}/${pr_payload.repo}`
+    });

// Later in the code:
-    console.log(JSON.stringify(fileSourceRequest, null, 2));
+    console.log({
+        action: "fetch_file_content_request",
+        request: fileSourceRequest
+    });

// And at the end:
-    console.log("Completed fetch_file_content");
+    console.log({
+        action: "fetch_file_content_complete",
+        filename,
+        success: true
+    });

Also applies to: 13-13, 38-38

packages/web/src/features/agents/review-agent/nodes/githubPrParser.ts (5)

18-18: Incomplete log message for skipped files

The log message when skipping files with missing properties doesn't include the file name, making it difficult to identify which file was skipped.

-            console.log(`Skipping file due to missing from (${file.from}) or to (${file.to})`)
+            console.log(`Skipping file ${file.index ?? 'unknown'} due to missing from (${file.from}) or to (${file.to})`)

28-29: Inconsistent line content format

The code constructs line-prefixed snippets with a colon (ln:content) but doesn't prefix the content with diff markers (+/-) that would help the AI understand line changes.

             for (const change of chunk.changes) {
                 if (change.type === "normal") {
-                    oldSnippet += change.ln1 + ":" + change.content + "\n";
-                    newSnippet += change.ln2 + ":" + change.content + "\n";
+                    oldSnippet += change.ln1 + ": " + change.content + "\n";
+                    newSnippet += change.ln2 + ": " + change.content + "\n";
                 } else if (change.type === "add") {
-                    newSnippet += change.ln + ":" + change.content + "\n";
+                    newSnippet += change.ln + ": + " + change.content + "\n";
                 } else if (change.type === "del") {
-                    oldSnippet += change.ln + ":" + change.content + "\n";
+                    oldSnippet += change.ln + ": - " + change.content + "\n";
                 }
             }

Also applies to: 31-33


49-49: Type assertion can be avoided with proper filter

The code uses a type assertion to convert from (sourcebot_file_diff | null)[] to sourcebot_file_diff[]. This can be avoided by using a type predicate function.

-    const filteredSourcebotFileDiffs: sourcebot_file_diff[] = sourcebotFileDiffs.filter((file) => file !== null) as sourcebot_file_diff[];
+    // Type guard function
+    const isNotNull = <T>(value: T | null): value is T => value !== null;
+    const filteredSourcebotFileDiffs: sourcebot_file_diff[] = sourcebotFileDiffs.filter(isNotNull);

61-61: Non-null assertion can be avoided

The code uses a non-null assertion on payload.installation!.id despite already checking for its existence earlier. This is unnecessary and could be avoided.

-        installation_id: payload.installation!.id,
+        installation_id: payload.installation.id,

7-7: Consider more informative logging

Like in the other file, the logging here is very basic. Consider using structured logging with more context about the PR being processed.

-    console.log("Executing github_pr_parser");
+    console.log({
+        action: "github_pr_parser_start",
+        pr_number: payload.pull_request.number,
+        repo: `${payload.repository.owner.login}/${payload.repository.name}`
+    });

// And at the end:
-    console.log("Completed github_pr_parser");
+    console.log({
+        action: "github_pr_parser_complete",
+        pr_number: payload.pull_request.number,
+        files_processed: filteredSourcebotFileDiffs.length
+    });

Also applies to: 51-51

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a81cd24 and 79dd288.

📒 Files selected for processing (8)
  • packages/web/src/app/api/(server)/webhook/route.ts (1 hunks)
  • packages/web/src/features/agents/review-agent/app.ts (1 hunks)
  • packages/web/src/features/agents/review-agent/nodes/fetchFileContent.ts (1 hunks)
  • packages/web/src/features/agents/review-agent/nodes/generateDiffReviewPrompt.ts (1 hunks)
  • packages/web/src/features/agents/review-agent/nodes/generatePrReview.ts (1 hunks)
  • packages/web/src/features/agents/review-agent/nodes/githubPrParser.ts (1 hunks)
  • packages/web/src/features/agents/review-agent/nodes/githubPushPrReviews.ts (1 hunks)
  • packages/web/src/features/agents/review-agent/nodes/invokeDiffReviewLlm.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/web/src/app/api/(server)/webhook/route.ts
  • packages/web/src/features/agents/review-agent/app.ts
🧰 Additional context used
🧬 Code Graph Analysis (5)
packages/web/src/features/agents/review-agent/nodes/generateDiffReviewPrompt.ts (1)
packages/web/src/features/agents/review-agent/types.ts (3)
  • sourcebot_diff (7-7)
  • sourcebot_context (34-34)
  • sourcebot_diff_review_schema (36-40)
packages/web/src/features/agents/review-agent/nodes/githubPrParser.ts (2)
packages/web/src/features/agents/review-agent/types.ts (3)
  • sourcebot_pr_payload (27-27)
  • sourcebot_file_diff (14-14)
  • sourcebot_diff (7-7)
packages/web/src/app/error.tsx (1)
  • Error (14-54)
packages/web/src/features/agents/review-agent/nodes/invokeDiffReviewLlm.ts (2)
packages/web/src/features/agents/review-agent/types.ts (2)
  • sourcebot_diff_review (41-41)
  • sourcebot_diff_review_schema (36-40)
packages/backend/src/env.ts (1)
  • env (22-52)
packages/web/src/features/agents/review-agent/nodes/githubPushPrReviews.ts (1)
packages/web/src/features/agents/review-agent/types.ts (2)
  • sourcebot_pr_payload (27-27)
  • sourcebot_file_diff_review (47-47)
packages/web/src/features/agents/review-agent/nodes/fetchFileContent.ts (3)
packages/web/src/features/agents/review-agent/types.ts (2)
  • sourcebot_pr_payload (27-27)
  • sourcebot_context (34-34)
packages/web/src/features/search/schemas.ts (1)
  • fileSourceResponseSchema (101-104)
packages/web/src/lib/utils.ts (1)
  • base64Decode (191-194)

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (1)
packages/web/src/app/[domain]/agents/page.tsx (1)

21-67: Add error handling and empty state handling

The component doesn't handle cases where the agents array might be empty. While it's statically defined now, if this data eventually comes from an API, it's good practice to handle empty states.

 <div className="flex flex-col items-center overflow-hidden min-h-screen">
   <NavigationMenu domain={domain} />
   <div className="w-full max-w-6xl px-4 mt-12 mb-24">
+    {agents.length === 0 ? (
+      <div className="flex flex-col items-center justify-center min-h-[60vh]">
+        <p className="text-lg text-muted-foreground">No agents available at this time.</p>
+      </div>
+    ) : (
     <div
       className={
         agents.length === 1
           ? "flex justify-center items-center min-h-[60vh]"
           : "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-10"
       }
     >
       {agents.map((agent) => (
         // Agent card content
       ))}
     </div>
+    )}
   </div>
 </div>
🧹 Nitpick comments (4)
packages/web/src/app/[domain]/agents/page.tsx (4)

7-15: Consider making the agents array configurable or fetched from an API

The agents are currently hardcoded in the component. As more agents are added in the future, you might want to consider making this array configurable through environment variables, a configuration file, or fetched from an API.


49-53: Improve environment variable check logic and add more detailed status information

The current environment variable check is complex and only verifies existence but not validity. Consider adding more detailed status information to help users troubleshoot configuration issues.

-{agent.requiredEnvVars.every(envVar => envVar in env && env[envVar as keyof typeof env] !== undefined) ? (
-  <div className="text-green-500 font-semibold">
-    Agent is configured and accepting requests on /api/webhook 
-  </div>
+{agent.requiredEnvVars.every(envVar => {
+  const value = env[envVar as keyof typeof env];
+  return value !== undefined && value !== '';
+}) ? (
+  <div className="flex items-center gap-2 text-green-500 font-semibold">
+    <MdRocketLaunch className="text-lg" />
+    <span>Agent is configured and accepting requests on /api/webhook</span>
+  </div>

The MdRocketLaunch icon is already imported but not used - this gives it a purpose.


54-61: Enhance accessibility for the Configure button

While the button styling is good, consider adding ARIA attributes to improve accessibility. Also, add a tooltip or visual indicator that this link opens in a new tab.

 <Link
   href={agent.configureUrl}
   target="_blank"
   rel="noopener noreferrer"
-  className="flex items-center justify-center gap-2 px-5 py-2.5 rounded-md bg-primary text-primary-foreground font-mono font-semibold text-base border border-primary shadow-sm hover:bg-primary/80 focus:outline-none focus:ring-2 focus:ring-primary/60 transition w-1/2"
+  className="flex items-center justify-center gap-2 px-5 py-2.5 rounded-md bg-primary text-primary-foreground font-mono font-semibold text-base border border-primary shadow-sm hover:bg-primary/80 focus:outline-none focus:ring-2 focus:ring-primary/60 transition w-1/2"
+  aria-label={`Configure ${agent.name}`}
 >
-  <FaCogs className="text-lg" /> Configure
+  <FaCogs className="text-lg" aria-hidden="true" /> 
+  <span>Configure</span>
+  <span className="sr-only">(opens in a new tab)</span>
 </Link>

30-37: Extract card component for better maintainability

The agent card is a complex component that could be extracted to improve maintainability, especially as more agents are added.

You could create a separate AgentCard component to encapsulate this logic. For example:

// components/AgentCard.tsx
import Link from "next/link";
import { FaCogs } from "react-icons/fa";
import { MdRocketLaunch } from "react-icons/md";
import { env } from "@/env.mjs";

interface AgentProps {
  agent: {
    id: string;
    name: string;
    description: string;
    requiredEnvVars: string[];
    configureUrl: string;
  };
  fullWidth?: boolean;
}

export function AgentCard({ agent, fullWidth = false }: AgentProps) {
  const isConfigured = agent.requiredEnvVars.every(
    (envVar) => envVar in env && env[envVar as keyof typeof env] !== undefined
  );

  return (
    <div
      className={`relative flex flex-col items-center border border-border rounded-2xl p-8 bg-card shadow-xl ${
        fullWidth ? "w-full max-w-xl" : ""
      }`}
    >
      {/* Card content... */}
    </div>
  );
}

Then use it in your main component:

{agents.map((agent) => (
  <AgentCard key={agent.id} agent={agent} fullWidth={agents.length === 1} />
))}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 944aeb5 and 2d0977e.

📒 Files selected for processing (1)
  • packages/web/src/app/[domain]/agents/page.tsx (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
packages/web/src/app/[domain]/agents/page.tsx (1)
packages/backend/src/env.ts (1)
  • env (22-52)
🔇 Additional comments (3)
packages/web/src/app/[domain]/agents/page.tsx (3)

1-6: LGTM: Clean and appropriate imports

The imports are well-organized and appropriate for the component's needs.


17-20: LGTM: Component signature and initial structure

The component signature is well-defined with proper TypeScript typing for the parameters.


38-46: LGTM: Clean name and description section

The name and description section is well-structured with appropriate styles and semantic HTML.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (3)
docs/docs/agents/overview.mdx (3)

10-11: Use correct verb form 'set up' instead of 'setup'.

In line 10, “Once you've setup Sourcebot…” should use the verb phrase “set up” rather than the noun “setup”.

Apply this diff:

- Once you've setup Sourcebot, check out the
+ Once you've set up Sourcebot, check out the
🧰 Tools
🪛 LanguageTool

[style] ~10-~10: In American English, “you” and “have” do not usually form a contraction unless they’re followed by a past participle.
Context: ...rcebot to perform a specific task. Once you've setup Sourcebot, check out the guides b...

(IVE_I_HAVE_AMERICAN_STYLE)


10-11: Clarify the reference to 'guides below'.

The text refers to “guides below” but the page displays agent cards, not guides. Consider replacing “guides” with “agent cards” or “agents” for accuracy.

Apply this diff:

- Once you've set up Sourcebot, check out the guides below to configure additional agents.
+ Once you've set up Sourcebot, check out the agent cards below to configure additional agents.
🧰 Tools
🪛 LanguageTool

[style] ~10-~10: In American English, “you” and “have” do not usually form a contraction unless they’re followed by a past participle.
Context: ...rcebot to perform a specific task. Once you've setup Sourcebot, check out the guides b...

(IVE_I_HAVE_AMERICAN_STYLE)


7-7: Ensure consistent punctuation in the note.

The sentence in the <Note> block should end with a period for readability.

Apply this diff:

- Have an idea for an agent that we haven't built? Submit a [feature request](https://github.com/sourcebot-dev/sourcebot/discussions/categories/feature-requests) on our GitHub
+ Have an idea for an agent that we haven't built? Submit a [feature request](https://github.com/sourcebot-dev/sourcebot/discussions/categories/feature-requests) on our GitHub.
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2d0977e and 871d410.

⛔ Files ignored due to path filters (3)
  • docs/images/github_app_private_key.png is excluded by !**/*.png
  • docs/images/review_agent_configured.png is excluded by !**/*.png
  • docs/images/review_agent_example.png is excluded by !**/*.png
📒 Files selected for processing (5)
  • docs/docs.json (1 hunks)
  • docs/docs/agents/overview.mdx (1 hunks)
  • docs/docs/agents/review-agent.mdx (1 hunks)
  • packages/web/src/app/[domain]/agents/page.tsx (1 hunks)
  • packages/web/src/app/[domain]/components/navigationMenu.tsx (1 hunks)
✅ Files skipped from review due to trivial changes (3)
  • docs/docs.json
  • packages/web/src/app/[domain]/components/navigationMenu.tsx
  • docs/docs/agents/review-agent.mdx
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/web/src/app/[domain]/agents/page.tsx
🧰 Additional context used
🪛 LanguageTool
docs/docs/agents/overview.mdx

[style] ~10-~10: In American English, “you” and “have” do not usually form a contraction unless they’re followed by a past participle.
Context: ...rcebot to perform a specific task. Once you've setup Sourcebot, check out the guides b...

(IVE_I_HAVE_AMERICAN_STYLE)

@msukkari
Copy link
Contributor Author

msukkari commented May 9, 2025

@CodeRabbit /review

Copy link

coderabbitai bot commented May 9, 2025

Tip

For best results, initiate chat on the files or code changes.

@msukkari I've received your request to review this pull request. I'll trigger an incremental review of your PR now. The review will analyze the changes you've made to implement the new GitHub PR review agent feature.

The review will examine:

  • The webhook API implementation
  • GitHub app integration
  • OpenAI integration for code reviews
  • The agents UI additions
  • Documentation changes

The review process may take a few minutes. Once complete, you'll receive comments on specific parts of your code if any issues are found. If you have any specific questions about your implementation while waiting, feel free to ask!

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🔭 Outside diff range comments (1)
packages/backend/src/index.ts (1)

3-11: 🛠️ Refactor suggestion

Unused import: ArgumentParser

The ArgumentParser import from argparse is no longer used after removing command-line argument parsing, but it's still being imported.

 import * as Sentry from "@sentry/node";
-import { ArgumentParser } from "argparse";
 import { existsSync } from 'fs';
 import { mkdir } from 'fs/promises';
 import path from 'path';
 import { AppContext } from "./types.js";
 import { main } from "./main.js"
 import { PrismaClient } from "@sourcebot/db";
 import { env } from "./env.js";
♻️ Duplicate comments (1)
packages/web/src/features/agents/review-agent/nodes/invokeDiffReviewLlm.ts (1)

23-24: Consider making the model configurable instead of hardcoding

The model name "gpt-4.1-mini" is hardcoded, which could create maintenance challenges when new models are released or if different models are needed for different environments.

🧹 Nitpick comments (4)
packages/backend/src/index.ts (1)

40-40: Debug log statement should use a logger

A raw console.log call to print the current working directory appears to be a debug statement. Consider using a proper logging framework with appropriate log levels to avoid polluting production logs.

-console.log(process.cwd());
+logger.debug(`Current working directory: ${process.cwd()}`);
packages/web/src/features/agents/review-agent/nodes/invokeDiffReviewLlm.ts (1)

18-20: Consider the security and performance impact of extensive logging

Logging entire prompts and responses to files could lead to security concerns (if prompts contain sensitive data) and performance issues (with large responses).

Consider adding truncation or sanitization for logs:

 if (reviewAgentLogPath) {
-    fs.appendFileSync(reviewAgentLogPath, `\n\nPrompt:\n${prompt}`);
+    // Truncate long prompts in logs to prevent excessive file sizes
+    const truncatedPrompt = prompt.length > 1000 ? prompt.substring(0, 1000) + '... [truncated]' : prompt;
+    fs.appendFileSync(reviewAgentLogPath, `\n\nPrompt:\n${truncatedPrompt}`);
 }

 // Similar change for response logging

Also applies to: 31-33

packages/web/src/app/api/(server)/webhook/route.ts (2)

69-74: Extract repeated installation validation into a helper function

The installation validation logic is duplicated in both the pull request and issue comment event handlers.

+ function validateInstallation(installation: unknown): number {
+     if (!installation) {
+         throw new Error('Installation object is not present');
+     }
+     return (installation as { id: number }).id;
+ }

// Then in the PR handler:
- if (!body.installation) {
-     console.error('Received github pull request event but installation is not present');
-     return Response.json({ status: 'ok' });
- }
-
- const installationId = body.installation.id;
+ let installationId;
+ try {
+     installationId = validateInstallation(body.installation);
+ } catch (error) {
+     console.error(`Error in pull request handler: ${error.message}`);
+     return Response.json({ status: 'ok' });
+ }

// And similarly in the issue comment handler

Also applies to: 91-94


42-44: Consider refactoring type guards to improve readability

The current implementation of the type guards is hard to read due to multiple conditions on a single line.

- function isPullRequestEvent(eventHeader: string, payload: unknown): payload is WebhookEventDefinition<"pull-request-opened"> | WebhookEventDefinition<"pull-request-synchronize"> {
-     return eventHeader === "pull_request" && typeof payload === "object" && payload !== null && "action" in payload && typeof payload.action === "string" && (payload.action === "opened" || payload.action === "synchronize");
- }
+ function isPullRequestEvent(
+     eventHeader: string, 
+     payload: unknown
+ ): payload is WebhookEventDefinition<"pull-request-opened"> | WebhookEventDefinition<"pull-request-synchronize"> {
+     if (eventHeader !== "pull_request") return false;
+     if (typeof payload !== "object" || payload === null) return false;
+     if (!("action" in payload) || typeof (payload as any).action !== "string") return false;
+     
+     const action = (payload as any).action;
+     return action === "opened" || action === "synchronize";
+ }

Apply similar formatting to the isIssueCommentEvent function.

Also applies to: 46-48

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 871d410 and a242a69.

📒 Files selected for processing (12)
  • .env.development (3 hunks)
  • docs/docs/agents/review-agent.mdx (1 hunks)
  • packages/backend/src/env.ts (1 hunks)
  • packages/backend/src/index.ts (2 hunks)
  • packages/web/src/app/api/(server)/webhook/route.ts (1 hunks)
  • packages/web/src/env.mjs (2 hunks)
  • packages/web/src/features/agents/review-agent/app.ts (1 hunks)
  • packages/web/src/features/agents/review-agent/nodes/generatePrReview.ts (1 hunks)
  • packages/web/src/features/agents/review-agent/nodes/githubPrParser.ts (1 hunks)
  • packages/web/src/features/agents/review-agent/nodes/invokeDiffReviewLlm.ts (1 hunks)
  • packages/web/src/features/agents/review-agent/types.ts (1 hunks)
  • supervisord.conf (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (7)
  • .env.development
  • packages/web/src/features/agents/review-agent/nodes/githubPrParser.ts
  • packages/web/src/features/agents/review-agent/nodes/generatePrReview.ts
  • docs/docs/agents/review-agent.mdx
  • packages/web/src/features/agents/review-agent/app.ts
  • packages/web/src/env.mjs
  • packages/web/src/features/agents/review-agent/types.ts
🧰 Additional context used
🧬 Code Graph Analysis (2)
packages/backend/src/index.ts (1)
packages/backend/src/env.ts (1)
  • env (22-54)
packages/web/src/features/agents/review-agent/nodes/invokeDiffReviewLlm.ts (2)
packages/web/src/features/agents/review-agent/types.ts (2)
  • sourcebot_diff_review (43-43)
  • sourcebot_diff_review_schema (38-42)
packages/backend/src/env.ts (1)
  • env (22-54)
🔇 Additional comments (2)
supervisord.conf (1)

25-25: Command-line argument removal aligns with environment variable change

The removal of the --cacheDir command-line argument from the backend startup command matches the addition of the DATA_CACHE_DIR environment variable in the environment schema. This change centralizes configuration through environment variables rather than CLI arguments.

packages/backend/src/index.ts (1)

42-42: Environment variable replaces command-line argument

Using env.DATA_CACHE_DIR instead of CLI arguments is a good improvement that centralizes configuration. This change aligns with modern practices for containerized applications and makes configuration more consistent.

@msukkari msukkari merged commit e64f371 into main May 12, 2025
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants