Skip to content

Conversation

@gwilkes-rv
Copy link
Collaborator

@gwilkes-rv gwilkes-rv commented Oct 12, 2024

Summary by RedRover

Here’s a draft Pull Request description using the provided “context7” summary:

Title
Rebrand to “red-rover” / Swap OpenAI → Anthropic (Claude)

Description
This PR rebrands our GitHub Action and package from “openai-pr-reviewer” to “red-rover” and replaces all OpenAI integrations with Anthropic’s Claude models. It updates workflows, action metadata, code, prompts, README, licensing, and dependencies accordingly.


What changed?

  1. Project metadata & licensing
    • Renamed project from openai-pr-reviewerred-rover
    • Appended “tmokmss (2023)” to the MIT header in LICENSE

  2. CI workflow & Action manifest
    .github/workflows/redrover_review.yml
    – Uses RedVentures/red-rover@anthropic
    – Swaps OPENAI_API_KEYANTHROPIC_API_KEY
    – Removes OpenAI models/inputs, enables debug mode
    action.yml
    – Renamed inputs (openai_*anthropic_*), added review_file_diff & bot_icon
    – Commented out leftover OpenAI entries (system_message, openai_heavy_model)
    – Updated descriptions, prompts, and default values

  3. Documentation & examples
    README.md
    – All usage, examples and environment variables now reference Anthropic (claude-instant-v1, claude-v2)
    – Updated SDK instructions, cost/disclaimer, debug guidance

  4. Dependencies & packaging
    package.json & package-lock.json
    – Renamed package, updated metadata, removed chatgpt dependency
    – Added @anthropic-ai/sdk@0.29.0 and sub-deps

  5. Core source changes
    src/* files renamed types and options from OpenAI → Anthropic
    • main.ts
    – Instantiates Bot with AnthropicOptions, enforces ANTHROPIC_API_KEY
    – Wires in new review_file_diff input
    • inputs.ts & options.ts
    – Support include/exclude path filters
    – Define Claude token limits (100 000 total; 4 000/3 000 response)
    • bot.ts
    – Replaces OpenAI client with Anthropic SDK (createMessage) and retry logic
    • review.ts & review-comment.ts
    – Wrap diffs in XML tags, remove OpenAI concurrency, parse JSON from Claude
    – Strip tags before rendering comments
    • prompts.ts
    – All templates refactored to XML-style tags, new JSON schema examples, new reviewFileDiff & comment templates
    • commenter.ts
    – Adds SELF_LOGIN logic to prefix comments as “A (You):” or “H (@username):”


How to test

  1. Set ANTHROPIC_API_KEY in your environment or repository secrets.
  2. Create or update a PR in a test repo to trigger the red-rover_review.yml workflow.
  3. Inspect the generated review comments; verify that:
    • All OpenAI references are gone
    • The bot uses claude-instant-v1 or claude-v2 as configured
    • Path filters (include/exclude) behave as expected
    • The JSON schema in prompts is respected

Notes

  • We no longer support OpenAI keys or models—this is a one-way migration.
  • Claude’s token limits have been baked into defaults. Adjust in your workflow inputs if needed.
  • Legacy users must update their secrets, inputs, and marketplace workflow references.

Please review and let me know if anything’s missing or if you spot residual OpenAI references.

@gwilkes-rv gwilkes-rv requested review from rv-github-copilot-svc and removed request for rv-github-copilot-svc February 27, 2025 20:22
@gwilkes-rv
Copy link
Collaborator Author

@claude review this PR

1 similar comment
@gwilkes-rv
Copy link
Collaborator Author

@claude review this PR

@claude
Copy link

claude bot commented May 22, 2025

Claude finished @gwilkes-rv's task —— View job


I'll review this PR that transitions from OpenAI to Anthropic's Claude models.

  • Analyze PR content and changes
  • Review code implementation
  • Identify potential issues
  • Provide feedback

PR Summary

This PR implements a complete transition from OpenAI to Anthropic's Claude models for the Red Rover PR reviewer and summarizer. The changes include:

  1. Integration with Anthropic's SDK (@anthropic-ai/sdk)
  2. Updated model references (using Claude 3 Haiku and Claude 3.5 Sonnet)
  3. Modified token limits and context window handling
  4. Adjusted prompt formatting and response parsing
  5. Changes to review processing to accommodate Claude's different API structure

Code Review

Model and API Integration

  • Good implementation of Anthropic client initialization in bot.ts
  • Appropriate handling of Claude's content structure which differs from OpenAI's format
  • Well-structured JSON response parsing with error handling

Token Handling and Limits

  • The limits.ts implementation now correctly handles Claude's larger context windows (up to 200K tokens)
  • Good approach to calculating request tokens with appropriate margins

Configuration

  • Clean renaming of parameters from "openai_" to "anthropic_" in options.ts
  • Well-defined model defaults in action.yml:
    • Light model: claude-3-haiku-20240307
    • Heavy model: claude-3-5-sonnet-latest

Response Parsing

  • The JSON parsing logic in bot.ts (lines 92-125) handles Claude's different response format
  • Good handling of edge cases like responses that begin with "reviews:"

Potential Issues

  1. JSON Parsing Robustness: The custom parsing in bot.ts might need more refinement for certain edge cases. The approach of checking for responses that start with "reviews:" and wrapping them in curly braces could be fragile.

  2. Error Handling: While there is error handling for JSON parsing, some error messages could be more descriptive to help with debugging.

  3. Token Limit Assumptions: The implementation makes assumptions about Claude's models that might need adjustment as newer models are released.

  4. Response Format Expectations: The PR seems to assume Claude will follow certain conventions in its responses. It would be good to test with various prompt complexities.

Suggestions

  1. Consider adding more robust error handling around the JSON response parsing in bot.ts.

  2. It might be worth adding more descriptive comments about the expected Claude response format to help future maintainers.

  3. The README could use further updates to explicitly mention Claude models rather than references to OpenAI.

Overall Assessment

The PR represents a well-executed transition from OpenAI to Anthropic's Claude models. The code maintains the existing architecture while adapting to Claude's different API requirements. The changes are thorough and maintain compatibility with the existing GitHub integration workflow.

The implementation is clean, follows good practices, and should provide a solid foundation for using Claude models going forward.

@gwilkes-rv
Copy link
Collaborator Author

gwilkes-rv commented May 28, 2025

🐶 RedRover

🐶 RedRover

🐶 RedRover

🐶 RedRover

🐶 RedRover

🐶 RedRover

🐶 RedRover

🐶 RedRover

Currently reviewing new changes in this PR...

Commits Files that changed from the base of the PR and between 393345c and 6a0d4e3 commits.
Files selected (15)
  • .github/workflows/redrover_review.yml (1)
  • LICENSE (1)
  • README.md (6)
  • action.yml (3)
  • package-lock.json (35)
  • package.json (4)
  • src/bot.ts (2)
  • src/commenter.ts (2)
  • src/inputs.ts (5)
  • src/limits.ts (1)
  • src/main.ts (4)
  • src/options.ts (6)
  • src/prompts.ts (4)
  • src/review-comment.ts (1)
  • src/review.ts (12)
Files ignored due to filter (4)
  • dist/37.index.js
  • dist/73.index.js
  • dist/index.js
  • dist/licenses.txt

🐶 RedRover

It looks like you’d like me to adopt that summary as “context7.” Could you clarify what you’d like me to do next with context7? For example:

• Draft a PR description or release notes using that summary
• Generate detailed documentation or changelog entries
• Write commit messages
• Run through a checklist or QA based on those changes

Let me know how I can help!

Copy link
Collaborator Author

@gwilkes-rv gwilkes-rv left a comment

Choose a reason for hiding this comment

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

🐶 RedRover

Commits Files that changed from the base of the PR and between 393345c and 6a0d4e3 commits.
Files selected (15)
  • .github/workflows/redrover_review.yml (1)
  • LICENSE (1)
  • README.md (6)
  • action.yml (3)
  • package-lock.json (35)
  • package.json (4)
  • src/bot.ts (2)
  • src/commenter.ts (2)
  • src/inputs.ts (5)
  • src/limits.ts (1)
  • src/main.ts (4)
  • src/options.ts (6)
  • src/prompts.ts (4)
  • src/review-comment.ts (1)
  • src/review.ts (12)
Files ignored due to filter (4)
  • dist/37.index.js
  • dist/73.index.js
  • dist/index.js
  • dist/licenses.txt
Files skipped from review due to trivial changes (4)
  • LICENSE
  • README.md
  • package.json
  • src/prompts.ts
Review comments generated (78)
  • Review: 4
  • LGTM: 74

Tips

Chat with RedRover (@redrover)

  • Reply on review comments left by this bot to ask follow-up questions. A review comment is a comment on a diff or a file.
  • Invite the bot into a review comment chain by tagging @redrover in a reply.

Code suggestions

  • The bot may make code suggestions, but please review them carefully before committing since the line number ranges may be misaligned.
  • You can edit the comment made by the bot and manually tweak the suggestion if it is slightly off.

Pausing incremental reviews

  • Add @redrover: ignore anywhere in the PR description to pause further reviews from the bot.

Copy link
Collaborator Author

@gwilkes-rv gwilkes-rv left a comment

Choose a reason for hiding this comment

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

🐶 RedRover

Commits Files that changed from the base of the PR and between 393345c and 6a0d4e3 commits.
Files selected (15)
  • .github/workflows/redrover_review.yml (1)
  • LICENSE (1)
  • README.md (6)
  • action.yml (3)
  • package-lock.json (35)
  • package.json (4)
  • src/bot.ts (2)
  • src/commenter.ts (2)
  • src/inputs.ts (5)
  • src/limits.ts (1)
  • src/main.ts (4)
  • src/options.ts (6)
  • src/prompts.ts (4)
  • src/review-comment.ts (1)
  • src/review.ts (12)
Files ignored due to filter (4)
  • dist/37.index.js
  • dist/73.index.js
  • dist/index.js
  • dist/licenses.txt
Files skipped from review due to trivial changes (3)
  • LICENSE
  • package-lock.json
  • src/prompts.ts
Review comments generated (69)
  • Review: 9
  • LGTM: 60

Tips

Chat with RedRover (@redrover)

  • Reply on review comments left by this bot to ask follow-up questions. A review comment is a comment on a diff or a file.
  • Invite the bot into a review comment chain by tagging @redrover in a reply.

Code suggestions

  • The bot may make code suggestions, but please review them carefully before committing since the line number ranges may be misaligned.
  • You can edit the comment made by the bot and manually tweak the suggestion if it is slightly off.

Pausing incremental reviews

  • Add @redrover: ignore anywhere in the PR description to pause further reviews from the bot.

Comment on lines +93 to +131
if (Array.isArray(response.content) && response.content.length > 0) {
const textContent = response.content.find(item => item.type === 'text');
if (textContent && 'text' in textContent) {
responseText = textContent.text;

// Trim leading and trailing whitespace
responseText = responseText.trim();

// Check if the response starts with "reviews":
if (responseText.startsWith('"reviews":')) {
responseText = `{${responseText}`;
}

// Replace escaped newlines with actual newlines
responseText = responseText.replace(/\\u000a/g, '\n');

try {
// Attempt to parse the JSON content
const parsedContent = JSON.parse(responseText);

// Ensure the structure is correct
if (!parsedContent.reviews) {
parsedContent.reviews = [];
}
if (typeof parsedContent.lgtm === 'undefined') {
parsedContent.lgtm = false;
}

responseText = JSON.stringify(parsedContent, null, 2); // Pretty print the JSON
} catch (parseError) {
warning(`Failed to parse response as JSON: ${parseError}`);
info(`Raw Response Text: ${responseText}`);
}
} else {
warning('No text content found in the response');
}
} else {
warning(`Unexpected content structure in the response: ${JSON.stringify(response.content)}`);
}
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

🐶 RedRover

The logic for parsing the Anthropic response assumes that the content array will always contain an object with a type of 'text' and a text property. However, if the API changes or returns a different structure, this could cause runtime errors. Consider adding more robust error handling or type checking to ensure that textContent.text exists and is a string before attempting to use it.

Additionally, forcibly wrapping the response in {} if it starts with "reviews": (line 103) may lead to invalid JSON if the response is not exactly as expected. This could cause parsing errors or incorrect data extraction. It's safer to validate the response format before attempting such string manipulations.

-           if (responseText.startsWith('"reviews":')) {
-             responseText = `{${responseText}`;
-           }
+           if (responseText.startsWith('"reviews":')) {
+             responseText = `{${responseText}`;
+             // Optionally, check if responseText ends with '}' and add if missing
+             if (!responseText.trim().endsWith('}')) {
+               responseText = `${responseText}}`;
+             }
+           }

@@ -1,15 +1,18 @@
# RedRover - An OpenAI ChatGPT-based PR reviewer and summarizer
<h1><img align="center" height="60" src="https://github.com/user-attachments/assets/84111c2e-fe44-4de1-b82d-45f38199724a"> &nbsp;&nbsp;Red Rover</h1>
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

🐶 RedRover

The image link in the <img> tag appears to be broken or points to a user-attachments path that may not be accessible to all users. Consider hosting the image in a public location or using a valid image URL to ensure the logo renders correctly for all users.

-<h1><img align="center" height="60" src="https://github.com/user-attachments/assets/84111c2e-fe44-4de1-b82d-45f38199724a">  &nbsp;&nbsp;Red Rover</h1>
+<h1><img align="center" height="60" src="https://github.com/RedVentures/red-rover/assets/64108082/9ee5df3f-bc3f-4bfc-878e-171d7ccca96e">  &nbsp;&nbsp;Red Rover</h1>

Comment on lines 5 to +16

constructor(model = 'gpt-4o-mini') {
this.knowledgeCutOff = '2021-09-01'
if (model === 'gpt-4-32k') {
this.maxTokens = 32600
constructor(model = 'anthropic.claude-instant-v1') {
if (model === 'anthropic.claude-instant-v1') {
this.maxTokens = 100_000
this.responseTokens = 4000
} else if (model === 'gpt-3.5-turbo-16k') {
this.maxTokens = 16300
} else if (model === 'anthropic.claude-v2') {
this.maxTokens = 100_000
this.responseTokens = 3000
} else if (model === 'gpt-4') {
this.maxTokens = 8192
this.responseTokens = 8192
} else if (model === 'gpt-4o') {
this.maxTokens = 128000
this.responseTokens = 4096
} else if (model === 'o1-mini') {
this.maxTokens = 128000
this.responseTokens = 65536
} else if (model === 'o1-preview') {
this.maxTokens = 128000
this.responseTokens = 32768
} else {
this.maxTokens = 128000
this.responseTokens = 16384
// The latest models usually have this level of limits.
this.maxTokens = 200_000
this.responseTokens = 4096
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

🐶 RedRover

The indentation is inconsistent on lines 9 and 12. The assignment to this.responseTokens is indented one space further than the surrounding code. This could reduce code readability and should be fixed.

-       this.responseTokens = 4000
+      this.responseTokens = 4000
-       this.responseTokens = 3000
+      this.responseTokens = 3000

Comment on lines +872 to 892
// eslint-disable-next-line no-unused-vars
patches: Array<[number, number, string]>
): Review[] {
const reviews: Review[] = []

response = sanitizeResponse(response.trim())

const lines = response.split('\n')
const lineNumberRangeRegex = /(?:^|\s)(\d+)-(\d+):\s*$/
const commentSeparator = '---'

let currentStartLine: number | null = null
let currentEndLine: number | null = null
let currentComment = ''
function storeReview(): void {
if (currentStartLine !== null && currentEndLine !== null) {
const review: Review = {
startLine: currentStartLine,
endLine: currentEndLine,
comment: currentComment
}

let withinPatch = false
let bestPatchStartLine = -1
let bestPatchEndLine = -1
let maxIntersection = 0

for (const [startLine, endLine] of patches) {
const intersectionStart = Math.max(review.startLine, startLine)
const intersectionEnd = Math.min(review.endLine, endLine)
const intersectionLength = Math.max(
0,
intersectionEnd - intersectionStart + 1
)

if (intersectionLength > maxIntersection) {
maxIntersection = intersectionLength
bestPatchStartLine = startLine
bestPatchEndLine = endLine
withinPatch =
intersectionLength === review.endLine - review.startLine + 1
}

if (withinPatch) break
}

if (!withinPatch) {
if (bestPatchStartLine !== -1 && bestPatchEndLine !== -1) {
review.comment = `> Note: This review was outside of the patch, so it was mapped to the patch with the greatest overlap. Original lines [${review.startLine}-${review.endLine}]

${review.comment}`
review.startLine = bestPatchStartLine
review.endLine = bestPatchEndLine
} else {
review.comment = `> Note: This review was outside of the patch, but no patch was found that overlapped with it. Original lines [${review.startLine}-${review.endLine}]

${review.comment}`
review.startLine = patches[0][0]
review.endLine = patches[0][1]
}
try {
const rawReviews = JSON.parse(response).reviews
for (const r of rawReviews) {
if (r.comment) {
reviews.push({
startLine: r.line_start ?? 0,
endLine: r.line_end ?? 0,
comment: r.comment
})
}

reviews.push(review)

info(
`Stored comment for line range ${currentStartLine}-${currentEndLine}: ${currentComment.trim()}`
)
}
}

function sanitizeCodeBlock(comment: string, codeBlockLabel: string): string {
const codeBlockStart = `\`\`\`${codeBlockLabel}`
const codeBlockEnd = '```'
const lineNumberRegex = /^ *(\d+): /gm

let codeBlockStartIndex = comment.indexOf(codeBlockStart)

while (codeBlockStartIndex !== -1) {
const codeBlockEndIndex = comment.indexOf(
codeBlockEnd,
codeBlockStartIndex + codeBlockStart.length
)

if (codeBlockEndIndex === -1) break

const codeBlock = comment.substring(
codeBlockStartIndex + codeBlockStart.length,
codeBlockEndIndex
)
const sanitizedBlock = codeBlock.replace(lineNumberRegex, '')

comment =
comment.slice(0, codeBlockStartIndex + codeBlockStart.length) +
sanitizedBlock +
comment.slice(codeBlockEndIndex)

codeBlockStartIndex = comment.indexOf(
codeBlockStart,
codeBlockStartIndex +
codeBlockStart.length +
sanitizedBlock.length +
codeBlockEnd.length
)
}

return comment
}

function sanitizeResponse(comment: string): string {
comment = sanitizeCodeBlock(comment, 'suggestion')
comment = sanitizeCodeBlock(comment, 'diff')
return comment
}

for (const line of lines) {
const lineNumberRangeMatch = line.match(lineNumberRangeRegex)

if (lineNumberRangeMatch != null) {
storeReview()
currentStartLine = parseInt(lineNumberRangeMatch[1], 10)
currentEndLine = parseInt(lineNumberRangeMatch[2], 10)
currentComment = ''
if (debug) {
info(`Found line number range: ${currentStartLine}-${currentEndLine}`)
}
continue
}

if (line.trim() === commentSeparator) {
storeReview()
currentStartLine = null
currentEndLine = null
currentComment = ''
if (debug) {
info('Found comment separator')
}
continue
}

if (currentStartLine !== null && currentEndLine !== null) {
currentComment += `${line}\n`
}
} catch (e: any) {
error(e.message)
return []
}

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

🐶 RedRover

If the Anthropic API ever returns a response that is not valid JSON or does not contain a reviews array, this will throw and silently return an empty array. Consider adding a check to ensure rawReviews is an array before iterating, and provide a more descriptive error message for easier debugging.

-    const rawReviews = JSON.parse(response).reviews
-    for (const r of rawReviews) {
-      if (r.comment) {
-        reviews.push({
-          startLine: r.line_start ?? 0,
-          endLine: r.line_end ?? 0,
-          comment: r.comment
-        })
-      }
-    }
+    const parsed = JSON.parse(response)
+    const rawReviews = Array.isArray(parsed.reviews) ? parsed.reviews : []
+    for (const r of rawReviews) {
+      if (r.comment) {
+        reviews.push({
+          startLine: r.line_start ?? 0,
+          endLine: r.line_end ?? 0,
+          comment: r.comment
+        })
+      }
+    }
+    if (!Array.isArray(parsed.reviews)) {
+      error('Anthropic response did not contain a "reviews" array.')
+    }

Copy link
Collaborator Author

@gwilkes-rv gwilkes-rv left a comment

Choose a reason for hiding this comment

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

🐶 RedRover

Commits Files that changed from the base of the PR and between 393345c and 6a0d4e3 commits.
Files selected (15)
  • .github/workflows/redrover_review.yml (1)
  • LICENSE (1)
  • README.md (6)
  • action.yml (3)
  • package-lock.json (35)
  • package.json (4)
  • src/bot.ts (2)
  • src/commenter.ts (2)
  • src/inputs.ts (5)
  • src/limits.ts (1)
  • src/main.ts (4)
  • src/options.ts (6)
  • src/prompts.ts (4)
  • src/review-comment.ts (1)
  • src/review.ts (12)
Files ignored due to filter (4)
  • dist/37.index.js
  • dist/73.index.js
  • dist/index.js
  • dist/licenses.txt
Files not reviewed due to errors (10)
  • src/bot.ts (no response)
  • .github/workflows/redrover_review.yml (no response)
  • src/commenter.ts (no response)
  • src/inputs.ts (no response)
  • src/limits.ts (no response)
  • action.yml (no response)
  • src/main.ts (no response)
  • src/options.ts (no response)
  • src/review-comment.ts (no response)
  • src/review.ts (no response)
Files skipped from review due to trivial changes (5)
  • LICENSE
  • README.md
  • package-lock.json
  • package.json
  • src/prompts.ts
Review comments generated (0)
  • Review: 0
  • LGTM: 0

Tips

Chat with RedRover (@redrover)

  • Reply on review comments left by this bot to ask follow-up questions. A review comment is a comment on a diff or a file.
  • Invite the bot into a review comment chain by tagging @redrover in a reply.

Code suggestions

  • The bot may make code suggestions, but please review them carefully before committing since the line number ranges may be misaligned.
  • You can edit the comment made by the bot and manually tweak the suggestion if it is slightly off.

Pausing incremental reviews

  • Add @redrover: ignore anywhere in the PR description to pause further reviews from the bot.

Copy link
Collaborator Author

@gwilkes-rv gwilkes-rv left a comment

Choose a reason for hiding this comment

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

🐶 RedRover

Commits Files that changed from the base of the PR and between 393345c and 6a0d4e3 commits.
Files selected (15)
  • .github/workflows/redrover_review.yml (1)
  • LICENSE (1)
  • README.md (6)
  • action.yml (3)
  • package-lock.json (35)
  • package.json (4)
  • src/bot.ts (2)
  • src/commenter.ts (2)
  • src/inputs.ts (5)
  • src/limits.ts (1)
  • src/main.ts (4)
  • src/options.ts (6)
  • src/prompts.ts (4)
  • src/review-comment.ts (1)
  • src/review.ts (12)
Files ignored due to filter (4)
  • dist/37.index.js
  • dist/73.index.js
  • dist/index.js
  • dist/licenses.txt
Files skipped from review due to trivial changes (4)
  • LICENSE
  • package-lock.json
  • package.json
  • src/prompts.ts
Review comments generated (56)
  • Review: 31
  • LGTM: 25

Tips

Chat with RedRover (@redrover)

  • Reply on review comments left by this bot to ask follow-up questions. A review comment is a comment on a diff or a file.
  • Invite the bot into a review comment chain by tagging @redrover in a reply.

Code suggestions

  • The bot may make code suggestions, but please review them carefully before committing since the line number ranges may be misaligned.
  • You can edit the comment made by the bot and manually tweak the suggestion if it is slightly off.

Pausing incremental reviews

  • Add @redrover: ignore anywhere in the PR description to pause further reviews from the bot.

Comment on lines 501 to +504
async composeCommentChain(reviewComments: any[], topLevelComment: any) {
const conversationChain = reviewComments
.filter((cmt: any) => cmt.in_reply_to_id === topLevelComment.id)
.map((cmt: any) => `${cmt.user.login}: ${cmt.body}`)
.map((cmt: any) => `${this.getRole(cmt.user.login)} ${cmt.body}`)
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

🐶 RedRover

Indentation is inconsistent. Align the async composeCommentChain signature and its chained calls with the rest of the class (2 spaces), and make sure .map lines up under .filter:

-    async composeCommentChain(reviewComments: any[], topLevelComment: any) {
+  async composeCommentChain(reviewComments: any[], topLevelComment: any) {
       const conversationChain = reviewComments
-        .filter((cmt: any) => cmt.in_reply_to_id === topLevelComment.id)
-       .map((cmt: any) => `${this.getRole(cmt.user.login)} ${cmt.body}`)
+        .filter((cmt: any) => cmt.in_reply_to_id === topLevelComment.id)
+        .map((cmt: any) => `${this.getRole(cmt.user.login)} ${cmt.body}`)

)

return conversationChain.join('\n---\n')
return `${conversationChain.join('\n')}`
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

🐶 RedRover

No need for a template literal when directly returning the joined string. Simplify to:

-    return `${conversationChain.join('\n')}`
+    return conversationChain.join('\n')

Comment on lines 30 to +31
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

🐶 RedRover

Environment variables are inconsistently indented under env:. Align both keys at the same level:

-            GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-           ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
+            GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+            ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}

Comment on lines +33 to 37
debug: true
poem_enabled: true
less_verbose_review: false
review_comment_lgtm: false
openai_heavy_model: gpt-4
path_filters: |
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

🐶 RedRover

Keys under with: have mismatched indentation. Align them consistently:

-           debug: true
-            poem_enabled: true
-            less_verbose_review: false
-            review_comment_lgtm: false
-            path_filters: |
+            debug: true
+            poem_enabled: true
+            less_verbose_review: false
+            review_comment_lgtm: false
+            path_filters: |

Comment on lines 38 to 39
!dist/**
!**/*.lock
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

🐶 RedRover

List items under path_filters: need to be indented uniformly two spaces deeper than the pipe:

-              !dist/**
-             !**/*.lock
+                !dist/**
+                !**/*.lock

if (options.maxFiles <= 0 || summaryPromises.length < options.maxFiles) {
summaryPromises.push(
openaiConcurrencyLimit(
anthropicConcurrencyLimit(
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

🐶 RedRover

Remove one extra space before anthropicConcurrencyLimit( so it nests correctly under summaryPromises.push(:

-         anthropicConcurrencyLimit(
+        anthropicConcurrencyLimit(

Comment on lines +407 to +409
// ask Anthropic to summarize the summaries
const [summarizeResp] = await heavyBot.chat(
prompts.renderSummarizeChangesets(inputs),
{}
prompts.renderSummarizeChangesets(inputs)
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

🐶 RedRover

These lines are indented one space too far. They should align with the other heavyBot.chat calls:

-       // ask Anthropic to summarize the summaries
+      // ask Anthropic to summarize the summaries
-        const [summarizeResp] = await heavyBot.chat(
+       const [summarizeResp] = await heavyBot.chat(
-         prompts.renderSummarizeChangesets(inputs)
+        prompts.renderSummarizeChangesets(inputs)

const [summarizeFinalResponse] = await heavyBot.chat(
prompts.renderSummarize(inputs),
{}
prompts.renderSummarize(inputs)
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

🐶 RedRover

Adjust indentation to match the surrounding chat call:

-     prompts.renderSummarize(inputs)
+    prompts.renderSummarize(inputs)

const [releaseNotesResponse] = await heavyBot.chat(
prompts.renderSummarizeReleaseNotes(inputs),
{}
prompts.renderSummarizeReleaseNotes(inputs)
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

🐶 RedRover

Drop one space so the argument lines line up under heavyBot.chat(:

-       prompts.renderSummarizeReleaseNotes(inputs)
+      prompts.renderSummarizeReleaseNotes(inputs)

const [summarizeShortResponse] = await heavyBot.chat(
prompts.renderSummarizeShort(inputs),
{}
prompts.renderSummarizeShort(inputs)
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

🐶 RedRover

Align this line with the other chat arguments by removing one leading space:

-     prompts.renderSummarizeShort(inputs)
+    prompts.renderSummarizeShort(inputs)

Copy link
Collaborator Author

@gwilkes-rv gwilkes-rv left a comment

Choose a reason for hiding this comment

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

🐶 RedRover

Commits Files that changed from the base of the PR and between 393345c and 6a0d4e3 commits.
Files selected (15)
  • .github/workflows/redrover_review.yml (1)
  • LICENSE (1)
  • README.md (6)
  • action.yml (3)
  • package-lock.json (35)
  • package.json (4)
  • src/bot.ts (2)
  • src/commenter.ts (2)
  • src/inputs.ts (5)
  • src/limits.ts (1)
  • src/main.ts (4)
  • src/options.ts (6)
  • src/prompts.ts (4)
  • src/review-comment.ts (1)
  • src/review.ts (12)
Files ignored due to filter (4)
  • dist/37.index.js
  • dist/73.index.js
  • dist/index.js
  • dist/licenses.txt
Files skipped from review due to trivial changes (2)
  • LICENSE
  • src/prompts.ts
Review comments generated (85)
  • Review: 13
  • LGTM: 72

Tips

Chat with RedRover (@redrover)

  • Reply on review comments left by this bot to ask follow-up questions. A review comment is a comment on a diff or a file.
  • Invite the bot into a review comment chain by tagging @redrover in a reply.

Code suggestions

  • The bot may make code suggestions, but please review them carefully before committing since the line number ranges may be misaligned.
  • You can edit the comment made by the bot and manually tweak the suggestion if it is slightly off.

Pausing incremental reviews

  • Add @redrover: ignore anywhere in the PR description to pause further reviews from the bot.

default: 'claude-3-5-sonnet-latest'
anthropic_model_temperature:
required: false
description: 'Temperature for GPT model'
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

🐶 RedRover

The description references “GPT model” but should refer to the Anthropic/Claude model for clarity.

-      description: 'Temperature for GPT model'
+      description: 'Temperature for Anthropic model'

}

getRole(login: string) {
if (login === SELF_LOGIN) return '\nA (You): '
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

🐶 RedRover

The returned string for the bot role has a trailing space, which combined with the template’s extra space causes two spaces before the comment body. Remove the extra space inside the string literal:

-    if (login === SELF_LOGIN) return '\nA (You): '
+    if (login === SELF_LOGIN) return '\nA (You):'

};
response = await pRetry(
() =>
this.client.messages.create(params),
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

🐶 RedRover

The Anthropic SDK v0.29.0 does not expose client.messages.create. Use the chat completions endpoint instead and correct parameter names:

-          this.client.messages.create(params),
+          this.client.chat.completions.create({
+            model: params.model,
+            messages: params.messages,
+            max_tokens_to_sample: params.max_tokens,
+          }),

Comment on lines 159 to 160
if (tokenLimits != null) {
this.tokenLimits = tokenLimits
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

🐶 RedRover

The tokenLimits property is only assigned when tokenLimits is non-null, leaving it undefined otherwise. You should default it (e.g., to new TokenLimits(this.model)) to ensure it's always initialized:

-    if (tokenLimits != null) {
-      this.tokenLimits = tokenLimits
+    if (tokenLimits != null) {
+      this.tokenLimits = tokenLimits
+    } else {
+      this.tokenLimits = new TokenLimits(this.model)
+    }

Comment on lines 609 to +610
prompts.renderReviewFileDiff(ins),
{}
'{'
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

🐶 RedRover

The second argument to heavyBot.chat is incorrect; passing a string '{ instead of an options object. It should only receive the prompt call without a trailing comma or extra argument.

-              prompts.renderReviewFileDiff(ins),
-             '{'
+              prompts.renderReviewFileDiff(ins)

Comment on lines +872 to 874
// eslint-disable-next-line no-unused-vars
patches: Array<[number, number, string]>
): Review[] {
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

🐶 RedRover

The patches parameter is no longer used due to removal of mapping logic, which may cause comments outside patch ranges to be posted incorrectly. Consider reintroducing logic to map or validate review line ranges against the original patches.

review.endLine = patches[0][1]
}
try {
const rawReviews = JSON.parse(response).reviews
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

🐶 RedRover

Relying solely on JSON.parse(response).reviews assumes the model always returns valid JSON with a reviews field. If the response deviates, parsing will fail silently, returning an empty array. Consider adding sanitization or fallback handling for non-JSON outputs.

Copy link
Collaborator Author

@gwilkes-rv gwilkes-rv left a comment

Choose a reason for hiding this comment

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

🐶 RedRover

Commits Files that changed from the base of the PR and between 393345c and 6a0d4e3 commits.
Files selected (15)
  • .github/workflows/redrover_review.yml (1)
  • LICENSE (1)
  • README.md (6)
  • action.yml (3)
  • package-lock.json (35)
  • package.json (4)
  • src/bot.ts (2)
  • src/commenter.ts (2)
  • src/inputs.ts (5)
  • src/limits.ts (1)
  • src/main.ts (4)
  • src/options.ts (6)
  • src/prompts.ts (4)
  • src/review-comment.ts (1)
  • src/review.ts (12)
Files ignored due to filter (4)
  • dist/37.index.js
  • dist/73.index.js
  • dist/index.js
  • dist/licenses.txt
Files skipped from review due to trivial changes (4)
  • LICENSE
  • README.md
  • package-lock.json
  • src/prompts.ts
Review comments generated (44)
  • Review: 8
  • LGTM: 36

Tips

Chat with RedRover (@redrover)

  • Reply on review comments left by this bot to ask follow-up questions. A review comment is a comment on a diff or a file.
  • Invite the bot into a review comment chain by tagging @redrover in a reply.

Code suggestions

  • The bot may make code suggestions, but please review them carefully before committing since the line number ranges may be misaligned.
  • You can edit the comment made by the bot and manually tweak the suggestion if it is slightly off.

Pausing incremental reviews

  • Add @redrover: ignore anywhere in the PR description to pause further reviews from the bot.

Comment on lines +83 to +85
if (this.reviewFileDiff) {
content = content.replace('$review_file_diff', this.reviewFileDiff)
}
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

🐶 RedRover

The conditional replacement may leave the $review_file_diff placeholder in the content when reviewFileDiff is empty. It’s safer to always replace it, even if the value is an empty string.

-    if (this.reviewFileDiff) {
-      content = content.replace('$review_file_diff', this.reviewFileDiff)
-    }
+    content = content.replace('$review_file_diff', this.reviewFileDiff)

Comment on lines +496 to +499
getRole(login: string) {
if (login === SELF_LOGIN) return '\nA (You): '
return `\nH (@${login}):`
}
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

🐶 RedRover

The leading \n in each role string will introduce extra blank lines when building the conversation. It’s clearer to return the role without a newline and rely on the join for line breaks:

@@ 496-499
-  getRole(login: string) {
-    if (login === SELF_LOGIN) return '\nA (You): '
-    return `\nH (@${login}):`
-  }
+  getRole(login: string) {
+    if (login === SELF_LOGIN) return 'A (You):'
+    return `H (@${login}):`
+  }

bot_icon:
required: false
description: 'The icon for Red Rover'
default: 🐶
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

🐶 RedRover

The emoji default value should be quoted to ensure valid YAML.

-    default: 🐶
+    default: '🐶'

throw new Error(err)
}
this.anthropicOptions = anthropicOptions
this.client = new Anthropic();
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

🐶 RedRover

The Anthropic client needs your API key to authenticate. You should pass it in the constructor instead of calling new Anthropic() with no arguments.

-    this.client = new Anthropic();
+    this.client = new Anthropic({ apiKey: this.anthropicOptions.apiKey });

getInput('openai_retries'),
getInput('openai_timeout_ms'),
getInput('openai_concurrency_limit'),
getInput('review_file_diff'),
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

🐶 RedRover

The input review_file_diff appears to be a boolean flag. Consider using getBooleanInput to correctly parse it as a boolean:

-    getInput('review_file_diff'),
+    getBooleanInput('review_file_diff'),

Comment on lines 608 to 611
const [response] = await heavyBot.chat(
prompts.renderReviewFileDiff(ins),
{}
'{'
)
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

🐶 RedRover

The second argument '{’ passed to heavyBot.chat is incorrect. Remove it so that only the prompt is passed to chat.

-            const [response] = await heavyBot.chat(
-              prompts.renderReviewFileDiff(ins),
-             '{'
-            )
+            const [response] = await heavyBot.chat(
+              prompts.renderReviewFileDiff(ins)
+            )

Copy link
Collaborator Author

@gwilkes-rv gwilkes-rv left a comment

Choose a reason for hiding this comment

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

🐶 RedRover

Commits Files that changed from the base of the PR and between 393345c and 6a0d4e3 commits.
Files selected (15)
  • .github/workflows/redrover_review.yml (1)
  • LICENSE (1)
  • README.md (6)
  • action.yml (3)
  • package-lock.json (35)
  • package.json (4)
  • src/bot.ts (2)
  • src/commenter.ts (2)
  • src/inputs.ts (5)
  • src/limits.ts (1)
  • src/main.ts (4)
  • src/options.ts (6)
  • src/prompts.ts (4)
  • src/review-comment.ts (1)
  • src/review.ts (12)
Files ignored due to filter (4)
  • dist/37.index.js
  • dist/73.index.js
  • dist/index.js
  • dist/licenses.txt
Files not summarized due to errors (15)
  • action.yml (nothing fetched from RedRover)
  • README.md (nothing fetched from RedRover)
  • package.json (nothing fetched from RedRover)
  • package-lock.json (nothing fetched from RedRover)
  • .github/workflows/redrover_review.yml (nothing fetched from RedRover)
  • LICENSE (nothing fetched from RedRover)
  • src/commenter.ts (nothing fetched from RedRover)
  • src/inputs.ts (nothing fetched from RedRover)
  • src/bot.ts (nothing fetched from RedRover)
  • src/main.ts (nothing fetched from RedRover)
  • src/limits.ts (nothing fetched from RedRover)
  • src/options.ts (nothing fetched from RedRover)
  • src/prompts.ts (nothing fetched from RedRover)
  • src/review-comment.ts (nothing fetched from RedRover)
  • src/review.ts (nothing fetched from RedRover)
Review comments generated (92)
  • Review: 9
  • LGTM: 83

Tips

Chat with RedRover (@redrover)

  • Reply on review comments left by this bot to ask follow-up questions. A review comment is a comment on a diff or a file.
  • Invite the bot into a review comment chain by tagging @redrover in a reply.

Code suggestions

  • The bot may make code suggestions, but please review them carefully before committing since the line number ranges may be misaligned.
  • You can edit the comment made by the bot and manually tweak the suggestion if it is slightly off.

Pausing incremental reviews

  • Add @redrover: ignore anywhere in the PR description to pause further reviews from the bot.

Comment on lines +6 to +16
constructor(model = 'anthropic.claude-instant-v1') {
if (model === 'anthropic.claude-instant-v1') {
this.maxTokens = 100_000
this.responseTokens = 4000
} else if (model === 'gpt-3.5-turbo-16k') {
this.maxTokens = 16300
} else if (model === 'anthropic.claude-v2') {
this.maxTokens = 100_000
this.responseTokens = 3000
} else if (model === 'gpt-4') {
this.maxTokens = 8192
this.responseTokens = 8192
} else if (model === 'gpt-4o') {
this.maxTokens = 128000
this.responseTokens = 4096
} else if (model === 'o1-mini') {
this.maxTokens = 128000
this.responseTokens = 65536
} else if (model === 'o1-preview') {
this.maxTokens = 128000
this.responseTokens = 32768
} else {
this.maxTokens = 128000
this.responseTokens = 16384
// The latest models usually have this level of limits.
this.maxTokens = 200_000
this.responseTokens = 4096
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

🐶 RedRover

The model string comparisons use incorrect model identifiers for Anthropic's Claude models. The correct model names should be claude-instant-1.2 and claude-2.1 (or similar versioned names), not the AWS Bedrock format.

-  constructor(model = 'anthropic.claude-instant-v1') {
-    if (model === 'anthropic.claude-instant-v1') {
+  constructor(model = 'claude-instant-1.2') {
+    if (model === 'claude-instant-1.2') {
       this.maxTokens = 100_000
        this.responseTokens = 4000
-    } else if (model === 'anthropic.claude-v2') {
+    } else if (model === 'claude-2.1') {
       this.maxTokens = 100_000
        this.responseTokens = 3000
      } else {

Comment on lines +37 to 44
reviewFileDiff = '',
anthropicLightModel: string,
anthropicHeavyModel: string,
anthropicModelTemperature = '0.0',
anthropicRetries = '3',
anthropicTimeoutMS = '120000',
anthropicConcurrencyLimit = '6',
githubConcurrencyLimit = '6',
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

🐶 RedRover

The constructor parameters for anthropicLightModel and anthropicHeavyModel are missing default values, which could cause runtime errors if these parameters are not provided.

-    anthropicLightModel: string,
-    anthropicHeavyModel: string,
+    anthropicLightModel = 'claude-instant-v1',
+    anthropicHeavyModel = 'claude-v2',

Comment on lines +133 to 135
24:
24: def multiply(x, y):
25: return x * y
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

🐶 RedRover

There's a duplicate line number in the example input.

- 24: 
- 24: def multiply(x, y):
+ 23: 
+ 24: def multiply(x, y):

Comment on lines +203 to +207
Here is the diff that is now comment on.

\`\`\`diff
<partial_diff>
$diff
\`\`\`
<partial_diff>
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

🐶 RedRover

There's malformed XML with inconsistent closing tags in the partial_diff section.

- <partial_diff>
-  no diff
- <partial_diff>
+ <partial_diff>
+  no diff
+ </partial_diff>

Comment on lines +596 to 616
<comment_chains>
\`\`\`
${commentChain}
\`\`\`
</comment_chains>
`
}

ins.patches += `
---end_change_section---
`
}

if (patchesPacked > 0) {
// perform review
try {
const [response] = await heavyBot.chat(
prompts.renderReviewFileDiff(ins),
{}
'{'
)
if (response === '') {
info('review: nothing obtained from openai')
info('review: nothing obtained from anthropic')
reviewsFailed.push(`${filename} (no response)`)
return
}
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

🐶 RedRover

The heavyBot.chat() call is missing proper error handling for the JSON parsing that occurs in parseReview(). If the response is malformed JSON, this will cause the review to fail silently.

            const [response] = await heavyBot.chat(
              prompts.renderReviewFileDiff(ins),
             '{'
            )
            if (response === '') {
             info('review: nothing obtained from anthropic')
              reviewsFailed.push(`${filename} (no response)`)
              return
            }
+           if (!response.startsWith('{')) {
+             warning(`Invalid JSON response from Anthropic for ${filename}`)
+             reviewsFailed.push(`${filename} (invalid response format)`)
+             return
+           }

Copy link
Collaborator Author

@gwilkes-rv gwilkes-rv left a comment

Choose a reason for hiding this comment

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

🐶 RedRover

Commits Files that changed from the base of the PR and between 393345c and 6a0d4e3 commits.
Files selected (15)
  • .github/workflows/redrover_review.yml (1)
  • LICENSE (1)
  • README.md (6)
  • action.yml (3)
  • package-lock.json (35)
  • package.json (4)
  • src/bot.ts (2)
  • src/commenter.ts (2)
  • src/inputs.ts (5)
  • src/limits.ts (1)
  • src/main.ts (4)
  • src/options.ts (6)
  • src/prompts.ts (4)
  • src/review-comment.ts (1)
  • src/review.ts (12)
Files ignored due to filter (4)
  • dist/37.index.js
  • dist/73.index.js
  • dist/index.js
  • dist/licenses.txt
Files not summarized due to errors (15)
  • LICENSE (nothing fetched from RedRover)
  • package.json (nothing fetched from RedRover)
  • action.yml (nothing fetched from RedRover)
  • package-lock.json (nothing fetched from RedRover)
  • README.md (nothing fetched from RedRover)
  • .github/workflows/redrover_review.yml (nothing fetched from RedRover)
  • src/bot.ts (nothing fetched from RedRover)
  • src/commenter.ts (nothing fetched from RedRover)
  • src/limits.ts (nothing fetched from RedRover)
  • src/inputs.ts (nothing fetched from RedRover)
  • src/main.ts (nothing fetched from RedRover)
  • src/options.ts (nothing fetched from RedRover)
  • src/prompts.ts (nothing fetched from RedRover)
  • src/review-comment.ts (nothing fetched from RedRover)
  • src/review.ts (nothing fetched from RedRover)
Review comments generated (100)
  • Review: 12
  • LGTM: 88

Tips

Chat with RedRover (@redrover)

  • Reply on review comments left by this bot to ask follow-up questions. A review comment is a comment on a diff or a file.
  • Invite the bot into a review comment chain by tagging @redrover in a reply.

Code suggestions

  • The bot may make code suggestions, but please review them carefully before committing since the line number ranges may be misaligned.
  • You can edit the comment made by the bot and manually tweak the suggestion if it is slightly off.

Pausing incremental reviews

  • Add @redrover: ignore anywhere in the PR description to pause further reviews from the bot.

constructor(model = 'anthropic.claude-instant-v1') {
if (model === 'anthropic.claude-instant-v1') {
this.maxTokens = 100_000
this.responseTokens = 4000
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

🐶 RedRover

There's inconsistent indentation in the responseTokens assignment.

-       this.responseTokens = 4000
+      this.responseTokens = 4000

this.maxTokens = 16300
} else if (model === 'anthropic.claude-v2') {
this.maxTokens = 100_000
this.responseTokens = 3000
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

🐶 RedRover

There's inconsistent indentation in the responseTokens assignment.

-       this.responseTokens = 3000
+      this.responseTokens = 3000

} else if (model === 'o1-preview') {
this.maxTokens = 128000
this.responseTokens = 32768
} else {
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

🐶 RedRover

There's inconsistent indentation in the else clause.

-     } else {
+    } else {

Comment on lines +26 to 35
chat = async (message: string, prefix?: string): Promise<[string, Ids]> => {
let res: [string, Ids] = ['', {}]
try {
res = await this.chat_(message, ids)
res = await this.chat_(message, prefix)
return res
} catch (e: unknown) {
if (e instanceof ChatGPTError) {
warning(`Failed to chat: ${e}, backtrace: ${e.stack}`)
}
warning(`Failed to chat: ${e}`)
return res
}
}
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

🐶 RedRover

The method signature changed from accepting ids: Ids to prefix?: string but the return type still includes Ids. This inconsistency could break existing callers.

Comment on lines 86 to +141
let responseText = ''
const newIds: Ids = {};

if (response != null) {
responseText = response.text
info(`Response type: ${typeof response}`);
info(`Response keys: ${Object.keys(response).join(', ')}`);

if (Array.isArray(response.content) && response.content.length > 0) {
const textContent = response.content.find(item => item.type === 'text');
if (textContent && 'text' in textContent) {
responseText = textContent.text;

// Trim leading and trailing whitespace
responseText = responseText.trim();

// Check if the response starts with "reviews":
if (responseText.startsWith('"reviews":')) {
responseText = `{${responseText}`;
}

// Replace escaped newlines with actual newlines
responseText = responseText.replace(/\\u000a/g, '\n');

try {
// Attempt to parse the JSON content
const parsedContent = JSON.parse(responseText);

// Ensure the structure is correct
if (!parsedContent.reviews) {
parsedContent.reviews = [];
}
if (typeof parsedContent.lgtm === 'undefined') {
parsedContent.lgtm = false;
}

responseText = JSON.stringify(parsedContent, null, 2); // Pretty print the JSON
} catch (parseError) {
warning(`Failed to parse response as JSON: ${parseError}`);
info(`Raw Response Text: ${responseText}`);
}
} else {
warning('No text content found in the response');
}
} else {
warning(`Unexpected content structure in the response: ${JSON.stringify(response.content)}`);
}

newIds.parentMessageId = response.id;
} else {
warning('openai response is null')
}
// remove the prefix "with " in the response
if (responseText.startsWith('with ')) {
responseText = responseText.substring(5)
warning('Anthropic response is null');
}

if (this.options.debug) {
info(`openai responses: ${responseText}`)
}
const newIds: Ids = {
parentMessageId: response?.id,
conversationId: response?.conversationId
info(`Anthropic response text: ${responseText}\n-----------`);
}

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

🐶 RedRover

The JSON parsing logic assumes the response will always be in a specific format and attempts to fix malformed JSON by prepending {. This could mask actual API response issues and create unpredictable behavior.

-          // Check if the response starts with "reviews":
-          if (responseText.startsWith('"reviews":')) {
-            responseText = `{${responseText}`;
-          }

Comment on lines +182 to +231
comment = `
$system_message

## GitHub PR Title
A comment was made on a GitHub PR review for a diff hunk on a file - \`$filename\`. I would like you to follow the instructions in that comment.

\`$title\`
<pull_request_title>
$title
</pull_request_title>

## Description

\`\`\`
<pull_request_description>
$description
\`\`\`
</pull_request_description>

## Summary generated by the AI bot

\`\`\`
<short_summary>
$short_summary
\`\`\`

## Entire diff
</short_summary>

\`\`\`diff
<entire_diff>
$file_diff
\`\`\`
</entire_diff>

## Diff being commented on
Here is the diff that is now comment on.

\`\`\`diff
<partial_diff>
$diff
\`\`\`
<partial_diff>

## Instructions

Please reply directly to the new comment (instead of suggesting
a reply) and your reply will be posted as-is.
Please reply directly to the new comment (instead of suggesting a reply) and your reply will be posted as-is.

If the comment contains instructions/requests for you, please comply.
For example, if the comment is asking you to generate documentation
comments on the code, in your reply please generate the required code.

In your reply, please make sure to begin the reply by tagging the user
with "@user".
For example, if the comment is asking you to generate documentation comments on the code, in your reply please generate the required code.

## Comment format
In your reply, please make sure to begin the reply by tagging the user with "@user".

\`user: comment\`
<example>
@username You are right. Thanks for the explanation!
</example>

## Comment chain (including the new comment)
Here is the comment history YOU and the users had. Note that H=human and A=you(assistant).

\`\`\`
<history>
$comment_chain
\`\`\`

## The comment/request that you need to directly reply to
</history>

\`\`\`
This is the comment/request that you need to directly reply to
<comment>
$comment
\`\`\`
</comment>
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

🐶 RedRover

There's an incomplete tag in the partial_diff section that will cause XML parsing issues.

- <partial_diff>
-  no diff
- <partial_diff>
+  <partial_diff>
+  no diff
+ </partial_diff>

Comment on lines +38 to +39
anthropicLightModel: string,
anthropicHeavyModel: string,
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

🐶 RedRover

The parameter type definitions for anthropicLightModel and anthropicHeavyModel are missing. These parameters lack type annotations while other string parameters have them.

-    anthropicLightModel: string,
-    anthropicHeavyModel: string,
+    anthropicLightModel = 'claude-instant-1.2',
+    anthropicHeavyModel = 'claude-2.1',

Comment on lines +2298 to +2320
"integrity": "sha512-Dd0BYtWgnWJKwO1jkmTrzofjK2QXXcai0dmtzvIBhcA+RsG5h8R3xlyta0kGOZRNfL9GuRtb1knmPEhQrePCEw=="
},
"node_modules/@types/normalize-package-data": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz",
"integrity": "sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==",
"dev": true
"node_modules/@types/node-fetch": {
"version": "2.6.11",
"resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.11.tgz",
"integrity": "sha512-24xFj9R5+rfQJLRyM56qh+wnVSYhyXC2tkoBndtY0U+vubqNsYXGjufB2nn8Q6gt0LrARwL6UBtMCSVCwl4B1g==",
"dependencies": {
"@types/node": "*",
"form-data": "^4.0.0"
}
},
"node_modules/@types/node-fetch/node_modules/form-data": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz",
"integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==",
"dependencies": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
"mime-types": "^2.1.12"
},
"engines": {
"node": ">= 6"
}
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

🐶 RedRover

There's an issue with the dev dependency flag for @types/node being removed. The package should remain a dev dependency since it's only needed during development.

-      "integrity": "sha512-Dd0BYtWgnWJKwO1jkmTrzofjK2QXXcai0dmtzvIBhcA+RsG5h8R3xlyta0kGOZRNfL9GuRtb1knmPEhQrePCEw=="
+      "integrity": "sha512-Dd0BYtWgnWJKwO1jkmTrzofjK2QXXcai0dmtzvIBhcA+RsG5h8R3xlyta0kGOZRNfL9GuRtb1knmPEhQrePCEw==",
+      "dev": true

Copy link
Collaborator Author

@gwilkes-rv gwilkes-rv left a comment

Choose a reason for hiding this comment

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

🐶 RedRover

Commits Files that changed from the base of the PR and between 393345c and 6a0d4e3 commits.
Files selected (15)
  • .github/workflows/redrover_review.yml (1)
  • LICENSE (1)
  • README.md (6)
  • action.yml (3)
  • package-lock.json (35)
  • package.json (4)
  • src/bot.ts (2)
  • src/commenter.ts (2)
  • src/inputs.ts (5)
  • src/limits.ts (1)
  • src/main.ts (4)
  • src/options.ts (6)
  • src/prompts.ts (4)
  • src/review-comment.ts (1)
  • src/review.ts (12)
Files ignored due to filter (4)
  • dist/37.index.js
  • dist/73.index.js
  • dist/index.js
  • dist/licenses.txt
Files skipped from review due to trivial changes (2)
  • LICENSE
  • src/prompts.ts
Review comments generated (93)
  • Review: 8
  • LGTM: 85

Tips

Chat with RedRover (@redrover)

  • Reply on review comments left by this bot to ask follow-up questions. A review comment is a comment on a diff or a file.
  • Invite the bot into a review comment chain by tagging @redrover in a reply.

Code suggestions

  • The bot may make code suggestions, but please review them carefully before committing since the line number ranges may be misaligned.
  • You can edit the comment made by the bot and manually tweak the suggestion if it is slightly off.

Pausing incremental reviews

  • Add @redrover: ignore anywhere in the PR description to pause further reviews from the bot.

Comment on lines +15 to 24
private readonly client: Anthropic

private readonly options: Options
private readonly anthropicOptions: AnthropicOptions

constructor(options: Options, openaiOptions: OpenAIOptions) {
constructor(options: Options, anthropicOptions: AnthropicOptions) {
this.options = options
if (process.env.OPENAI_API_KEY) {
const currentDate = new Date().toISOString().split('T')[0]
const systemMessage = `${options.systemMessage}
Knowledge cutoff: ${openaiOptions.tokenLimits.knowledgeCutOff}
Current date: ${currentDate}

IMPORTANT: Entire response must be in the language with ISO code: ${options.language}
`

const apiConfig: any = {
apiBaseUrl: options.apiBaseUrl,
apiKey: process.env.OPENAI_API_KEY,
apiOrg: process.env.OPENAI_API_ORG ?? undefined,
debug: options.debug,
maxModelTokens: openaiOptions.tokenLimits.maxTokens,
maxResponseTokens: openaiOptions.tokenLimits.responseTokens,
completionParams: {
temperature: 1,
model: openaiOptions.model
}
}

if (!openaiOptions.model.startsWith('o1')) {
apiConfig.systemMessage = systemMessage
apiConfig.completionParams.temperature = options.openaiModelTemperature
}

this.api = new ChatGPTAPI(apiConfig)
} else {
const err =
"Unable to initialize the OpenAI API, both 'OPENAI_API_KEY' environment variable are not available"
throw new Error(err)
}
this.anthropicOptions = anthropicOptions
this.client = new Anthropic();
}
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

🐶 RedRover

The Anthropic client is initialized without passing the API key or any configuration. This could cause authentication failures.

-    this.client = new Anthropic();
+    this.client = new Anthropic({
+      apiKey: process.env.ANTHROPIC_API_KEY
+    });

Comment on lines +37 to +43
reviewFileDiff = '',
anthropicLightModel: string,
anthropicHeavyModel: string,
anthropicModelTemperature = '0.0',
anthropicRetries = '3',
anthropicTimeoutMS = '120000',
anthropicConcurrencyLimit = '6',
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

🐶 RedRover

The constructor parameters anthropicLightModel and anthropicHeavyModel are missing default values, which will cause runtime errors when these parameters are not provided.

-    anthropicLightModel: string,
-    anthropicHeavyModel: string,
+    anthropicLightModel = 'claude-instant-v1',
+    anthropicHeavyModel = 'claude-v2',

Comment on lines +6 to +7
constructor(model = 'anthropic.claude-instant-v1') {
if (model === 'anthropic.claude-instant-v1') {
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

🐶 RedRover

The model name format is inconsistent with typical Anthropic model naming conventions. Anthropic models are usually referenced as claude-instant-v1 or claude-2, not with the anthropic. prefix.

-  constructor(model = 'anthropic.claude-instant-v1') {
-    if (model === 'anthropic.claude-instant-v1') {
+  constructor(model = 'claude-instant-v1') {
+    if (model === 'claude-instant-v1') {

Comment on lines +8 to 12
this.maxTokens = 100_000
this.responseTokens = 4000
} else if (model === 'gpt-3.5-turbo-16k') {
this.maxTokens = 16300
} else if (model === 'anthropic.claude-v2') {
this.maxTokens = 100_000
this.responseTokens = 3000
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

🐶 RedRover

Inconsistent indentation with extra spaces before this.responseTokens.

     if (model === 'claude-instant-v1') {
       this.maxTokens = 100_000
-       this.responseTokens = 4000
+      this.responseTokens = 4000
     } else if (model === 'claude-v2') {
       this.maxTokens = 100_000
-       this.responseTokens = 3000
+      this.responseTokens = 3000

this.responseTokens = 4000
} else if (model === 'gpt-3.5-turbo-16k') {
this.maxTokens = 16300
} else if (model === 'anthropic.claude-v2') {
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

🐶 RedRover

The model name should be claude-2 instead of claude-v2 to match Anthropic's actual model naming.

-    } else if (model === 'anthropic.claude-v2') {
+    } else if (model === 'claude-2') {

Comment on lines +872 to 891
// eslint-disable-next-line no-unused-vars
patches: Array<[number, number, string]>
): Review[] {
const reviews: Review[] = []

response = sanitizeResponse(response.trim())

const lines = response.split('\n')
const lineNumberRangeRegex = /(?:^|\s)(\d+)-(\d+):\s*$/
const commentSeparator = '---'

let currentStartLine: number | null = null
let currentEndLine: number | null = null
let currentComment = ''
function storeReview(): void {
if (currentStartLine !== null && currentEndLine !== null) {
const review: Review = {
startLine: currentStartLine,
endLine: currentEndLine,
comment: currentComment
}

let withinPatch = false
let bestPatchStartLine = -1
let bestPatchEndLine = -1
let maxIntersection = 0

for (const [startLine, endLine] of patches) {
const intersectionStart = Math.max(review.startLine, startLine)
const intersectionEnd = Math.min(review.endLine, endLine)
const intersectionLength = Math.max(
0,
intersectionEnd - intersectionStart + 1
)

if (intersectionLength > maxIntersection) {
maxIntersection = intersectionLength
bestPatchStartLine = startLine
bestPatchEndLine = endLine
withinPatch =
intersectionLength === review.endLine - review.startLine + 1
}

if (withinPatch) break
}

if (!withinPatch) {
if (bestPatchStartLine !== -1 && bestPatchEndLine !== -1) {
review.comment = `> Note: This review was outside of the patch, so it was mapped to the patch with the greatest overlap. Original lines [${review.startLine}-${review.endLine}]

${review.comment}`
review.startLine = bestPatchStartLine
review.endLine = bestPatchEndLine
} else {
review.comment = `> Note: This review was outside of the patch, but no patch was found that overlapped with it. Original lines [${review.startLine}-${review.endLine}]

${review.comment}`
review.startLine = patches[0][0]
review.endLine = patches[0][1]
}
try {
const rawReviews = JSON.parse(response).reviews
for (const r of rawReviews) {
if (r.comment) {
reviews.push({
startLine: r.line_start ?? 0,
endLine: r.line_end ?? 0,
comment: r.comment
})
}

reviews.push(review)

info(
`Stored comment for line range ${currentStartLine}-${currentEndLine}: ${currentComment.trim()}`
)
}
}

function sanitizeCodeBlock(comment: string, codeBlockLabel: string): string {
const codeBlockStart = `\`\`\`${codeBlockLabel}`
const codeBlockEnd = '```'
const lineNumberRegex = /^ *(\d+): /gm

let codeBlockStartIndex = comment.indexOf(codeBlockStart)

while (codeBlockStartIndex !== -1) {
const codeBlockEndIndex = comment.indexOf(
codeBlockEnd,
codeBlockStartIndex + codeBlockStart.length
)

if (codeBlockEndIndex === -1) break

const codeBlock = comment.substring(
codeBlockStartIndex + codeBlockStart.length,
codeBlockEndIndex
)
const sanitizedBlock = codeBlock.replace(lineNumberRegex, '')

comment =
comment.slice(0, codeBlockStartIndex + codeBlockStart.length) +
sanitizedBlock +
comment.slice(codeBlockEndIndex)

codeBlockStartIndex = comment.indexOf(
codeBlockStart,
codeBlockStartIndex +
codeBlockStart.length +
sanitizedBlock.length +
codeBlockEnd.length
)
}

return comment
}

function sanitizeResponse(comment: string): string {
comment = sanitizeCodeBlock(comment, 'suggestion')
comment = sanitizeCodeBlock(comment, 'diff')
return comment
}

for (const line of lines) {
const lineNumberRangeMatch = line.match(lineNumberRangeRegex)

if (lineNumberRangeMatch != null) {
storeReview()
currentStartLine = parseInt(lineNumberRangeMatch[1], 10)
currentEndLine = parseInt(lineNumberRangeMatch[2], 10)
currentComment = ''
if (debug) {
info(`Found line number range: ${currentStartLine}-${currentEndLine}`)
}
continue
}

if (line.trim() === commentSeparator) {
storeReview()
currentStartLine = null
currentEndLine = null
currentComment = ''
if (debug) {
info('Found comment separator')
}
continue
}

if (currentStartLine !== null && currentEndLine !== null) {
currentComment += `${line}\n`
}
} catch (e: any) {
error(e.message)
return []
}
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

🐶 RedRover

The parseReview function has a logic issue. The JSON parsing assumes a specific structure with reviews array containing objects with line_start, line_end, and comment properties, but there's no validation that these properties exist before accessing them.

-    for (const r of rawReviews) {
-      if (r.comment) {
-        reviews.push({
-          startLine: r.line_start ?? 0,
-          endLine: r.line_end ?? 0,
-          comment: r.comment
-        })
-      }
-    }
+    for (const r of rawReviews) {
+      if (r.comment && typeof r.line_start === 'number' && typeof r.line_end === 'number') {
+        reviews.push({
+          startLine: r.line_start,
+          endLine: r.line_end,
+          comment: r.comment
+        })
+      }
+    }

Copy link
Collaborator Author

@gwilkes-rv gwilkes-rv left a comment

Choose a reason for hiding this comment

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

🐶 RedRover

Commits Files that changed from the base of the PR and between 393345c and 6a0d4e3 commits.
Files selected (15)
  • .github/workflows/redrover_review.yml (1)
  • LICENSE (1)
  • README.md (6)
  • action.yml (3)
  • package-lock.json (35)
  • package.json (4)
  • src/bot.ts (2)
  • src/commenter.ts (2)
  • src/inputs.ts (5)
  • src/limits.ts (1)
  • src/main.ts (4)
  • src/options.ts (6)
  • src/prompts.ts (4)
  • src/review-comment.ts (1)
  • src/review.ts (12)
Files ignored due to filter (4)
  • dist/37.index.js
  • dist/73.index.js
  • dist/index.js
  • dist/licenses.txt
Files skipped from review due to trivial changes (3)
  • LICENSE
  • package-lock.json
  • src/prompts.ts
Review comments generated (49)
  • Review: 4
  • LGTM: 45

Tips

Chat with RedRover (@redrover)

  • Reply on review comments left by this bot to ask follow-up questions. A review comment is a comment on a diff or a file.
  • Invite the bot into a review comment chain by tagging @redrover in a reply.

Code suggestions

  • The bot may make code suggestions, but please review them carefully before committing since the line number ranges may be misaligned.
  • You can edit the comment made by the bot and manually tweak the suggestion if it is slightly off.

Pausing incremental reviews

  • Add @redrover: ignore anywhere in the PR description to pause further reviews from the bot.

Comment on lines 29 to 36
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
with:
debug: false
debug: true
poem_enabled: true
less_verbose_review: false
review_comment_lgtm: false
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

🐶 RedRover

The env: and with: blocks aren’t indented correctly relative to the - uses: line, which will break YAML parsing. They should be indented two spaces under - uses:. For example:

-      - uses: RedVentures/red-rover@anthropic
-          env:
-            GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-            ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
-          with:
-           debug: true
-            poem_enabled: true
-            less_verbose_review: false
-            review_comment_lgtm: false
+      - uses: RedVentures/red-rover@anthropic
+        env:
+          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+          ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
+        with:
+          debug: true
+          poem_enabled: true
+          less_verbose_review: false
+          review_comment_lgtm: false

Comment on lines +184 to 187
### Inspect the messages between Anthropic server

Set `debug: true` in the workflow file to enable debug mode, which will show the
messages
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

🐶 RedRover

The removal of the OpenAI‐specific disclaimer also removed any notice about data being sent to Anthropic. It’s important to inform users about data transmission and link to Anthropic’s data usage policy. For example:

+### Disclaimer
+
+- Your code, diffs, and PR metadata will be sent to Anthropic’s servers for processing. Please review Anthropic’s API data usage policy [here](https://console.anthropic.com/policies) before using this action.

Comment on lines +53 to +57
const params: Anthropic.MessageCreateParams = {
model: this.anthropicOptions.model,
max_tokens: 4096,
messages: [
{
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

🐶 RedRover

The max_tokens parameter is hard-coded, which may not respect the configured token limits. Consider using the response token limit from anthropicOptions.tokenLimits.responseTokens instead of a fixed value.

-         max_tokens: 4096,
+         max_tokens: this.anthropicOptions.tokenLimits.responseTokens,

warning(`Unexpected content structure in the response: ${JSON.stringify(response.content)}`);
}

newIds.parentMessageId = response.id;
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

🐶 RedRover

The conversationId returned by Anthropic is not being captured, so multi-turn context may be lost. You should set newIds.conversationId from the response (e.g., response.conversationId or response.conversation_id).

+      newIds.conversationId = response.conversationId ?? response.conversation_id;

Copy link
Collaborator Author

@gwilkes-rv gwilkes-rv left a comment

Choose a reason for hiding this comment

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

🐶 RedRover

Commits Files that changed from the base of the PR and between 393345c and 6a0d4e3 commits.
Files selected (15)
  • .github/workflows/redrover_review.yml (1)
  • LICENSE (1)
  • README.md (6)
  • action.yml (3)
  • package-lock.json (35)
  • package.json (4)
  • src/bot.ts (2)
  • src/commenter.ts (2)
  • src/inputs.ts (5)
  • src/limits.ts (1)
  • src/main.ts (4)
  • src/options.ts (6)
  • src/prompts.ts (4)
  • src/review-comment.ts (1)
  • src/review.ts (12)
Files ignored due to filter (4)
  • dist/37.index.js
  • dist/73.index.js
  • dist/index.js
  • dist/licenses.txt
Files skipped from review due to trivial changes (3)
  • LICENSE
  • package-lock.json
  • src/prompts.ts
Review comments generated (47)
  • Review: 7
  • LGTM: 40

Tips

Chat with RedRover (@redrover)

  • Reply on review comments left by this bot to ask follow-up questions. A review comment is a comment on a diff or a file.
  • Invite the bot into a review comment chain by tagging @redrover in a reply.

Code suggestions

  • The bot may make code suggestions, but please review them carefully before committing since the line number ranges may be misaligned.
  • You can edit the comment made by the bot and manually tweak the suggestion if it is slightly off.

Pausing incremental reviews

  • Add @redrover: ignore anywhere in the PR description to pause further reviews from the bot.

Comment on lines +28 to 38
- uses: RedVentures/red-rover@anthropic
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
with:
debug: false
debug: true
poem_enabled: true
less_verbose_review: false
review_comment_lgtm: false
openai_heavy_model: gpt-4
path_filters: |
!dist/**
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

🐶 RedRover

The env: and with: mappings under the - uses: step aren’t aligned properly and will break YAML parsing. They should be indented two spaces further under the list item. For example:

-      - uses: RedVentures/red-rover@anthropic
-        env:
-          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-         ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
-        with:
-         debug: true
-          poem_enabled: true
-          less_verbose_review: false
-          review_comment_lgtm: false
-          path_filters: |
-            !dist/**
-            !**/*.lock
+      - uses: RedVentures/red-rover@anthropic
+        env:
+          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+          ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
+        with:
+          debug: true
+          poem_enabled: true
+          less_verbose_review: false
+          review_comment_lgtm: false
+          path_filters: |
+            !dist/**
+            !**/*.lock

![small-red-rover](https://github.com/bankrate/red-rover/assets/64108082/9ee5df3f-bc3f-4bfc-878e-171d7ccca96e)

## Overview
### Overview
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

🐶 RedRover

The heading “### Overview” is one level too deep under your <h1>; please use ## Overview to maintain proper Markdown hierarchy.

you can refer to the sections below.

### Reviewer Features:

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

🐶 RedRover

Similarly, “### Reviewer Features:” should be ## Reviewer Features: to follow the <h1>##### structure.

Comment on lines +72 to +75
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
with:
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

🐶 RedRover

The env: and with: keys under the - uses: step are over-indented and may break the YAML. They should align two spaces under the step, for example:

- uses: RedVentures/red-rover@anthropic
  env:
    GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
    ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
  with:
    debug: false
    review_simple_changes: false
    review_comment_lgtm: false

Comment on lines +71 to +73
response = await pRetry(
() =>
this.client.messages.create(params),
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

🐶 RedRover

The SDK does not expose messages.create; use the chat completions endpoint instead:

-           () =>
-             this.client.messages.create(params),
+           () =>
+             this.client.chat.completions.create(params),

Copy link
Collaborator Author

@gwilkes-rv gwilkes-rv left a comment

Choose a reason for hiding this comment

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

🐶 RedRover

Commits Files that changed from the base of the PR and between 393345c and 6a0d4e3 commits.
Files selected (15)
  • .github/workflows/redrover_review.yml (1)
  • LICENSE (1)
  • README.md (6)
  • action.yml (3)
  • package-lock.json (35)
  • package.json (4)
  • src/bot.ts (2)
  • src/commenter.ts (2)
  • src/inputs.ts (5)
  • src/limits.ts (1)
  • src/main.ts (4)
  • src/options.ts (6)
  • src/prompts.ts (4)
  • src/review-comment.ts (1)
  • src/review.ts (12)
Files ignored due to filter (4)
  • dist/37.index.js
  • dist/73.index.js
  • dist/index.js
  • dist/licenses.txt
Files skipped from review due to trivial changes (5)
  • LICENSE
  • README.md
  • package.json
  • src/prompts.ts
  • package-lock.json (diff too large)
Review comments generated (38)
  • Review: 3
  • LGTM: 35

Tips

Chat with RedRover (@redrover)

  • Reply on review comments left by this bot to ask follow-up questions. A review comment is a comment on a diff or a file.
  • Invite the bot into a review comment chain by tagging @redrover in a reply.

Code suggestions

  • The bot may make code suggestions, but please review them carefully before committing since the line number ranges may be misaligned.
  • You can edit the comment made by the bot and manually tweak the suggestion if it is slightly off.

Pausing incremental reviews

  • Add @redrover: ignore anywhere in the PR description to pause further reviews from the bot.

default: |
- Do NOT provide general feedback, summaries, explanations of changes, or praises for making good additions.
- 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.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

🐶 RedRover

The summarize_release_notes key is mis-indented and won’t be recognized under inputs. It should align with the other input keys (2 spaces).

-   summarize_release_notes:
+  summarize_release_notes:

)
const params: Anthropic.MessageCreateParams = {
model: this.anthropicOptions.model,
max_tokens: 4096,
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

🐶 RedRover

The max_tokens parameter is hard-coded to 4096. To respect model-specific token limits provided by AnthropicOptions, use its tokenLimits.responseTokens instead:

-        max_tokens: 4096,
+        max_tokens: this.anthropicOptions.tokenLimits.responseTokens,

Comment on lines +497 to +498
if (login === SELF_LOGIN) return '\nA (You): '
return `\nH (@${login}):`
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

🐶 RedRover

The returned strings include a leading newline and inconsistent trailing spaces, which can introduce unwanted blank lines and spacing in the composed comment chain. Remove the leading \n and ensure both branches include a single trailing space:

-    if (login === SELF_LOGIN) return '\nA (You): '
+    if (login === SELF_LOGIN) return 'A (You): '
-    return `\nH (@${login}):`
+    return `H (@${login}): `

Copy link
Collaborator Author

@gwilkes-rv gwilkes-rv left a comment

Choose a reason for hiding this comment

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

🐶 RedRover

Commits Files that changed from the base of the PR and between 393345c and 6a0d4e3 commits.
Files selected (15)
  • .github/workflows/redrover_review.yml (1)
  • LICENSE (1)
  • README.md (6)
  • action.yml (3)
  • package-lock.json (35)
  • package.json (4)
  • src/bot.ts (2)
  • src/commenter.ts (2)
  • src/inputs.ts (5)
  • src/limits.ts (1)
  • src/main.ts (4)
  • src/options.ts (6)
  • src/prompts.ts (4)
  • src/review-comment.ts (1)
  • src/review.ts (12)
Files ignored due to filter (4)
  • dist/37.index.js
  • dist/73.index.js
  • dist/index.js
  • dist/licenses.txt
Files skipped from review due to trivial changes (4)
  • LICENSE
  • README.md
  • package-lock.json
  • src/prompts.ts
Review comments generated (43)
  • Review: 5
  • LGTM: 38

Tips

Chat with RedRover (@redrover)

  • Reply on review comments left by this bot to ask follow-up questions. A review comment is a comment on a diff or a file.
  • Invite the bot into a review comment chain by tagging @redrover in a reply.

Code suggestions

  • The bot may make code suggestions, but please review them carefully before committing since the line number ranges may be misaligned.
  • You can edit the comment made by the bot and manually tweak the suggestion if it is slightly off.

Pausing incremental reviews

  • Add @redrover: ignore anywhere in the PR description to pause further reviews from the bot.

Comment on lines 37 to 39
path_filters: |
!dist/**
!**/*.lock
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

🐶 RedRover

-            path_filters: |
-              !dist/**
-             !**/*.lock
+            path_filters: |
+              !dist/**
+              !**/*.lock

Comment on lines +53 to +55
const params: Anthropic.MessageCreateParams = {
model: this.anthropicOptions.model,
max_tokens: 4096,
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

🐶 RedRover

The Anthropic request parameters don’t include a temperature setting, so the model always uses its default randomness. You should pass the configured anthropicModelTemperature from options into the params:

  const params: Anthropic.MessageCreateParams = {
    model: this.anthropicOptions.model,
-   max_tokens: 4096,
+   max_tokens: 4096,
+   temperature: this.options.anthropicModelTemperature,
    messages: [
      {
        role: 'user',
        content: message
      },

Comment on lines 177 to 180
github_concurrency_limit:
required: false
description: 'How many concurrent API calls to make to GitHub?'
default: '6'
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

🐶 RedRover

The github_concurrency_limit block is over-indented, which will break the YAML structure. It should align with the other top-level inputs:

-    github_concurrency_limit:
-      required: false
-      description: 'How many concurrent API calls to make to GitHub?'
-      default: '6'
+  github_concurrency_limit:
+    required: false
+    description: 'How many concurrent API calls to make to GitHub?'
+    default: '6'

Comment on lines +210 to 212

summarize_release_notes:
required: false
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

🐶 RedRover

The summarize_release_notes key (and its children) is under-indented and falls outside the inputs mapping. It needs one fewer leading space:

-   summarize_release_notes:
-     required: false
-     description:
+  summarize_release_notes:
+    required: false
+    description:

Comment on lines +6 to 13
constructor(model = 'anthropic.claude-instant-v1') {
if (model === 'anthropic.claude-instant-v1') {
this.maxTokens = 100_000
this.responseTokens = 4000
} else if (model === 'gpt-3.5-turbo-16k') {
this.maxTokens = 16300
} else if (model === 'anthropic.claude-v2') {
this.maxTokens = 100_000
this.responseTokens = 3000
} else if (model === 'gpt-4') {
this.maxTokens = 8192
this.responseTokens = 8192
} else if (model === 'gpt-4o') {
this.maxTokens = 128000
this.responseTokens = 4096
} else if (model === 'o1-mini') {
this.maxTokens = 128000
this.responseTokens = 65536
} else if (model === 'o1-preview') {
this.maxTokens = 128000
this.responseTokens = 32768
} else {
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

🐶 RedRover

The constructor’s checks expect model names prefixed with anthropic., but the inputs/defaults use bare model IDs like claude-instant-v1 and claude-v2. As a result, both cases fall through to the else branch. Update the comparisons (and the default) to match the actual model strings:

- constructor(model = 'anthropic.claude-instant-v1') {
-   if (model === 'anthropic.claude-instant-v1') {
+ constructor(model = 'claude-instant-v1') {
+   if (
+     model === 'claude-instant-v1' ||
+     model === 'anthropic.claude-instant-v1'
+   ) {
     this.maxTokens = 100_000
     this.responseTokens = 4000
-   } else if (model === 'anthropic.claude-v2') {
+   } else if (
+     model === 'claude-v2' ||
+     model === 'anthropic.claude-v2'
+   ) {
     this.maxTokens = 100_000
     this.responseTokens = 3000
   } else {

Copy link
Collaborator Author

@gwilkes-rv gwilkes-rv left a comment

Choose a reason for hiding this comment

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

🐶 RedRover

Commits Files that changed from the base of the PR and between 393345c and 6a0d4e3 commits.
Files selected (15)
  • .github/workflows/redrover_review.yml (1)
  • LICENSE (1)
  • README.md (6)
  • action.yml (3)
  • package-lock.json (35)
  • package.json (4)
  • src/bot.ts (2)
  • src/commenter.ts (2)
  • src/inputs.ts (5)
  • src/limits.ts (1)
  • src/main.ts (4)
  • src/options.ts (6)
  • src/prompts.ts (4)
  • src/review-comment.ts (1)
  • src/review.ts (12)
Files ignored due to filter (4)
  • dist/37.index.js
  • dist/73.index.js
  • dist/index.js
  • dist/licenses.txt
Files skipped from review due to trivial changes (4)
  • LICENSE
  • README.md
  • package-lock.json
  • src/prompts.ts
Review comments generated (40)
  • Review: 8
  • LGTM: 32

Tips

Chat with RedRover (@redrover)

  • Reply on review comments left by this bot to ask follow-up questions. A review comment is a comment on a diff or a file.
  • Invite the bot into a review comment chain by tagging @redrover in a reply.

Code suggestions

  • The bot may make code suggestions, but please review them carefully before committing since the line number ranges may be misaligned.
  • You can edit the comment made by the bot and manually tweak the suggestion if it is slightly off.

Pausing incremental reviews

  • Add @redrover: ignore anywhere in the PR description to pause further reviews from the bot.

!dist/**
!**/*.lock
system_message: ${{ steps.file.outputs.content }}
# system_message: ${{ steps.file.outputs.content }}
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

🐶 RedRover

You’ve left the system_message input commented out, so the custom prompt you read isn’t passed into the action. If you intend to use it, uncomment or add the correct input. For example:

-          # system_message: ${{ steps.file.outputs.content }}
+          system_message: ${{ steps.file.outputs.content }}

Comment on lines +496 to +498
getRole(login: string) {
if (login === SELF_LOGIN) return '\nA (You): '
return `\nH (@${login}):`
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

🐶 RedRover

The leading \n in the role prefixes causes an extra blank line before each comment. It’s clearer to let the joiner insert newlines.

-  getRole(login: string) {
-    if (login === SELF_LOGIN) return '\nA (You): '
-    return `\nH (@${login}):`
-  }
+  getRole(login: string) {
+    if (login === SELF_LOGIN) return 'A (You):'
+    return `H (@${login}):`
+  }

Comment on lines 509 to +510

return conversationChain.join('\n---\n')
return `${conversationChain.join('\n')}`
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

🐶 RedRover

Switching from '\n---\n' to '\n' removes the visual separators between comments and may reduce readability. Consider restoring a clear delimiter:

-    return `${conversationChain.join('\n')}`
+    return conversationChain.join('\n---\n')

icon: 'git-merge'
color: 'orange'
author: 'CodeRabbit LLC'
author: 'gwilkes-rv'
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

🐶 RedRover

There's an extra space before author—it should align with name at the root level.

- author: 'gwilkes-rv'
+author: 'gwilkes-rv'

Comment on lines +151 to +210
anthropic_light_model:
required: false
description:
'Model to use for simple tasks like summarizing diff on a file.'
default: 'gpt-4o-mini'
openai_heavy_model:
default: 'claude-3-haiku-20240307'
anthropic_heavy_model:
required: false
description: 'Model to use for complex tasks such as code reviews.'
default: 'gpt-4o'
openai_model_temperature:
default: 'claude-3-5-sonnet-latest'
anthropic_model_temperature:
required: false
description: 'Temperature for GPT model'
default: '0.05'
openai_retries:
anthropic_retries:
required: false
description:
'How many times to retry OpenAI API in case of timeouts or errors?'
'How many times to retry Anthropic API in case of timeouts or errors?'
default: '5'
openai_timeout_ms:
anthropic_timeout_ms:
required: false
description: 'Timeout for OpenAI API call in millis'
description: 'Timeout for Anthropic API call in millis'
default: '360000'
openai_concurrency_limit:
anthropic_concurrency_limit:
required: false
description: 'How many concurrent API calls to make to OpenAI servers?'
description: 'How many concurrent API calls to make to Anthropic servers?'
default: '6'
github_concurrency_limit:
required: false
description: 'How many concurrent API calls to make to GitHub?'
default: '6'
system_message:
required: false
description: 'System message to be sent to OpenAI'
description: 'System message to be sent to Anthropic'
default: |
You are `@redrover` (aka `github-actions[bot]`), a language model
trained by OpenAI. Your purpose is to act as a highly experienced
software engineer and provide a thorough review of the code hunks
and suggest code snippets to improve key areas such as:
- Logic
- Security
- Performance
- Data races
- Consistency
- Error handling
- Maintainability
- Modularity
- Complexity
- Optimization
- Best practices: DRY, SOLID, KISS
You are `@redrover` (aka `github-actions[bot]`), a language model
trained by Anthropic. Your purpose is to act as a highly experienced
software engineer and provide a thorough review of the code, related documents, and articles
and suggest code snippets to improve these with various aspects.

Do not comment on minor code style issues, missing
comments/documentation. Identify and resolve significant
concerns to improve overall code quality while deliberately
disregarding minor issues.
Follow the following instructions.
summarize:
required: false
description: 'The prompt for final summarization response'
default: |
Provide your final response in markdown with the following content:

**Walkthrough**: A high-level summary of the overall change instead of
specific files within 80 words.
**Changes**: A markdown table of files and their summaries. Group files
with similar changes together into a single row to save space.
- **Walkthrough**: A high-level summary of the overall change instead of
specific files within 80 words.
- **Changes**: A markdown table of files and their summaries. Group files
with similar changes together into a single row to save space.

Avoid additional commentary as this summary will be added as a comment on the
GitHub pull request. Use the titles "Walkthrough" and "Changes" and they must be H2.
review_file_diff:
required: false
description: 'The additional instruction for reviewing file diff'
default: |
- Do NOT provide general feedback, summaries, explanations of changes, or praises for making good additions.
- 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.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

🐶 RedRover

The nested keys under each anthropic_* (and related inputs) are over-indented by two spaces, which will break YAML parsing. All required, description, and default lines should be indented exactly 4 spaces from the document root (2 under inputs: and 2 for nesting), not 6. For example:

-      required: false
+    required: false
-      description: 'Model to use for simple tasks like summarizing diff on a file.'
+    description: 'Model to use for simple tasks like summarizing diff on a file.'
...

Please correct the indentation for the entire anthropic inputs block.

Comment on lines +175 to +177
inputs.commentChain = inputs.commentChain
.replace(COMMENT_TAG, '')
.replace(COMMENT_REPLY_TAG, '')
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

🐶 RedRover

The current .replace() calls only remove the first occurrence of each tag. To ensure all instances are stripped, use .replaceAll() (or a global regex) instead:

- inputs.commentChain = inputs.commentChain
-   .replace(COMMENT_TAG, '')
-   .replace(COMMENT_REPLY_TAG, '')
+ inputs.commentChain = inputs.commentChain
+   .replaceAll(COMMENT_TAG, '')
+   .replaceAll(COMMENT_REPLY_TAG, '')

Comment on lines +53 to +56
const params: Anthropic.MessageCreateParams = {
model: this.anthropicOptions.model,
max_tokens: 4096,
messages: [
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

🐶 RedRover

Hardcoding max_tokens: 4096 may exceed the model’s configured response limit and ignores the token limits provided in AnthropicOptions. Consider pulling the response token limit from this.anthropicOptions.tokenLimits.responseTokens:

-         max_tokens: 4096,
+         max_tokens: this.anthropicOptions.tokenLimits.responseTokens,

Comment on lines +93 to +94
if (Array.isArray(response.content) && response.content.length > 0) {
const textContent = response.content.find(item => item.type === 'text');
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

🐶 RedRover

The code only handles the case where response.content is an array. If the SDK returns a simple string, it will fall through and be ignored. You should add a branch to handle string content, for example:

if (typeof response.content === 'string') {
  responseText = response.content.trim();
}

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.

1 participant