Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions src/kit/pr_review/agentic_reviewer.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from .base_reviewer import BaseReviewer
from .config import LLMProvider, ReviewConfig
from .diff_parser import DiffParser
from .error_utils import is_non_actionable_error
from .file_prioritizer import FilePrioritizer
from .priority_filter import filter_review_by_priority

Expand Down Expand Up @@ -1094,10 +1095,14 @@ def review_pr_agentic(self, pr_input: str) -> str:
# Generate final comment
review_comment = self._generate_agentic_comment(pr_details, files, analysis)

# Post comment if configured to do so AND analysis completed successfully
# Post comment if configured to do so AND analysis completed successfully.
# Never post non-actionable infrastructure errors (token limits, 5xx, rate limits).
if self.config.post_as_comment:
comment_result = self.post_pr_comment(owner, repo, pr_number, review_comment)
print(f"Posted comment: {comment_result['html_url']}")
if is_non_actionable_error(review_comment):
print("Skipping GitHub comment: LLM provider returned a non-actionable error")
else:
comment_result = self.post_pr_comment(owner, repo, pr_number, review_comment)
print(f"Posted comment: {comment_result['html_url']}")
else:
print("Comment posting disabled in configuration")

Expand Down
73 changes: 73 additions & 0 deletions src/kit/pr_review/error_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
"""Utilities for classifying and handling non-actionable review errors."""

# Prefixes emitted by _analyze_with_*_enhanced and agentic analysis methods
# when the underlying LLM/provider call fails.
_ERROR_PREFIXES = (
"error during enhanced llm analysis:",
"error during enhanced ollama analysis:",
"error during agentic analysis turn",
)

# Token / context-length limit patterns (provider-agnostic)
_TOKEN_LIMIT_PATTERNS = (
"maximum context length",
"context_length_exceeded",
"context length exceeded",
"too many tokens",
"token limit",
"context window",
"prompt is too long",
"request too large",
"content_too_large",
"resource_exhausted",
"max_tokens",
"maximum number of tokens",
"input is too long",
"string_above_max_length",
)

# Upstream HTTP 5xx patterns
_HTTP_5XX_PATTERNS = (
"500 internal server error",
"502 bad gateway",
"503 service unavailable",
"504 gateway timeout",
"overloaded_error",
"overloaded",
"internal server error",
"server_error",
"the server had an error",
)

# Rate-limit / 429 patterns
_RATE_LIMIT_PATTERNS = (
"rate_limit",
"rate limit",
"429",
"too many requests",
)


def is_non_actionable_error(text: str) -> bool:
"""Return True if *text* contains a non-actionable infrastructure error.

Non-actionable errors are transient provider/infrastructure failures that
a PR author cannot fix (token limits, upstream 5xx, rate limits). These
should never be posted as GitHub review comments.

The check is intentionally conservative: it only fires when the text
contains one of the known error prefixes **and** a recognised
infrastructure-error pattern, so legitimate reviews that happen to
mention "500" or "token" are not affected.
"""
if not text:
return False

lower = text.lower()

# Only consider text that was produced by an LLM analysis error path.
if not any(prefix in lower for prefix in _ERROR_PREFIXES):
return False

all_patterns = _TOKEN_LIMIT_PATTERNS + _HTTP_5XX_PATTERNS + _RATE_LIMIT_PATTERNS
return any(pattern in lower for pattern in all_patterns)
14 changes: 10 additions & 4 deletions src/kit/pr_review/reviewer.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from .base_reviewer import BaseReviewer
from .config import LLMProvider, ReviewConfig
from .diff_parser import DiffParser, FileDiff
from .error_utils import is_non_actionable_error
from .file_prioritizer import FilePrioritizer
from .priority_filter import filter_review_by_priority
from .validator import validate_review_quality
Expand Down Expand Up @@ -478,11 +479,16 @@ def review_pr(self, pr_input: str) -> str:
basic_analysis = f"Quick analysis mode.\n\nFiles changed: {len(files)} files with {sum(f['additions'] for f in files)} additions and {sum(f['deletions'] for f in files)} deletions."
review_comment = self._generate_intelligent_comment(pr_details, files, basic_analysis)

# Post comment if configured to do so
# Post comment if configured to do so, but never post non-actionable
# infrastructure errors (token limits, upstream 5xx, rate limits).
if self.config.post_as_comment:
comment_result = self.post_pr_comment(owner, repo, pr_number, review_comment)
if not quiet:
print(f"Posted comment: {comment_result['html_url']}")
if is_non_actionable_error(review_comment):
if not quiet:
print("Skipping GitHub comment: LLM provider returned a non-actionable error")
else:
comment_result = self.post_pr_comment(owner, repo, pr_number, review_comment)
if not quiet:
print(f"Posted comment: {comment_result['html_url']}")

# Display cost summary
if not quiet:
Expand Down
Loading