Skip to content

Commit 0c483ec

Browse files
authored
Fix classification of PR type (#7)
* Fix classification of PR type * Remove mypy from pre-commit-ci * Fix mypy errors
1 parent 4ac35aa commit 0c483ec

File tree

9 files changed

+839
-53
lines changed

9 files changed

+839
-53
lines changed

.github/workflows/fix-remote-pr.yml

Lines changed: 69 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -73,48 +73,6 @@ jobs:
7373
git config user.name "aieng-bot-maintain[bot]"
7474
git config user.email "aieng-bot@vectorinstitute.ai"
7575
76-
- name: Analyze failure type
77-
id: analyze
78-
working-directory: target-repo
79-
run: |
80-
FAILED_CHECKS='${{ steps.pr-details.outputs.failed-checks }}'
81-
82-
echo "Analyzing failures..."
83-
echo "$FAILED_CHECKS" | jq -r '.[] | "\(.name): \(.conclusion)"'
84-
85-
# Check for merge conflicts first
86-
if git status | grep -q "Unmerged paths\|merge conflict"; then
87-
echo "primary-type=merge_conflict" >> $GITHUB_OUTPUT
88-
echo "failed-names=merge-conflict" >> $GITHUB_OUTPUT
89-
echo "Detected merge conflicts"
90-
exit 0
91-
fi
92-
93-
# Categorize failures
94-
TEST_FAILURES=$(echo "$FAILED_CHECKS" | jq '[.[] | select(.name | test("test|spec|jest|pytest|unittest"; "i"))]')
95-
LINT_FAILURES=$(echo "$FAILED_CHECKS" | jq '[.[] | select(.name | test("lint|format|pre-commit|eslint|prettier|black|flake8|ruff"; "i"))]')
96-
SECURITY_FAILURES=$(echo "$FAILED_CHECKS" | jq '[.[] | select(.name | test("audit|security|snyk|dependabot|pip-audit"; "i"))]')
97-
BUILD_FAILURES=$(echo "$FAILED_CHECKS" | jq '[.[] | select(.name | test("build|compile|webpack|vite|tsc"; "i"))]')
98-
MERGE_CONFLICT_FAILURES=$(echo "$FAILED_CHECKS" | jq '[.[] | select(.name | test("merge|conflict"; "i"))]')
99-
100-
# Determine primary failure type
101-
if [ "$(echo "$MERGE_CONFLICT_FAILURES" | jq 'length')" -gt 0 ]; then
102-
echo "primary-type=merge_conflict" >> $GITHUB_OUTPUT
103-
elif [ "$(echo "$TEST_FAILURES" | jq 'length')" -gt 0 ]; then
104-
echo "primary-type=test" >> $GITHUB_OUTPUT
105-
elif [ "$(echo "$LINT_FAILURES" | jq 'length')" -gt 0 ]; then
106-
echo "primary-type=lint" >> $GITHUB_OUTPUT
107-
elif [ "$(echo "$SECURITY_FAILURES" | jq 'length')" -gt 0 ]; then
108-
echo "primary-type=security" >> $GITHUB_OUTPUT
109-
elif [ "$(echo "$BUILD_FAILURES" | jq 'length')" -gt 0 ]; then
110-
echo "primary-type=build" >> $GITHUB_OUTPUT
111-
else
112-
echo "primary-type=unknown" >> $GITHUB_OUTPUT
113-
fi
114-
115-
FAILED_NAMES=$(echo "$FAILED_CHECKS" | jq -r '.[].name' | paste -sd "," -)
116-
echo "failed-names=$FAILED_NAMES" >> $GITHUB_OUTPUT
117-
11876
- name: Get failure logs
11977
id: get-logs
12078
run: |
@@ -142,19 +100,85 @@ jobs:
142100
env:
143101
GH_TOKEN: ${{ secrets.ORG_ACCESS_TOKEN }}
144102

103+
- name: Setup Python for classification
104+
uses: actions/setup-python@v5
105+
with:
106+
python-version: '3.12'
107+
108+
- name: Install classifier dependencies
109+
run: |
110+
cd bot-repo
111+
pip install -e .
112+
113+
- name: Analyze failure type with Claude
114+
id: analyze
115+
working-directory: target-repo
116+
run: |
117+
FAILED_CHECKS='${{ steps.pr-details.outputs.failed-checks }}'
118+
119+
echo "Analyzing failures with Claude-based classifier..."
120+
echo "$FAILED_CHECKS" | jq -r '.[] | "\(.name): \(.conclusion)"'
121+
122+
# Check for merge conflicts first (quick local check)
123+
if git status | grep -q "Unmerged paths\|merge conflict"; then
124+
echo "failure-type=merge_conflict" >> $GITHUB_OUTPUT
125+
echo "failed-check-names=merge-conflict" >> $GITHUB_OUTPUT
126+
echo "confidence=1.0" >> $GITHUB_OUTPUT
127+
echo "reasoning=Git merge conflicts detected in working tree" >> $GITHUB_OUTPUT
128+
echo "Detected merge conflicts via git status"
129+
exit 0
130+
fi
131+
132+
# Prepare PR context JSON
133+
PR_INFO=$(jq -n \
134+
--arg repo "${{ github.event.inputs.target_repo }}" \
135+
--arg pr_number "${{ github.event.inputs.pr_number }}" \
136+
--arg pr_title "${{ steps.pr-details.outputs.pr-title }}" \
137+
--arg pr_author "${{ steps.pr-details.outputs.pr-author }}" \
138+
--arg base_ref "${{ steps.pr-details.outputs.base-ref }}" \
139+
--arg head_ref "${{ steps.pr-details.outputs.head-ref }}" \
140+
'{repo: $repo, pr_number: $pr_number, pr_title: $pr_title, pr_author: $pr_author, base_ref: $base_ref, head_ref: $head_ref}')
141+
142+
# Get failure logs
143+
FAILURE_LOGS=""
144+
if [ -f /tmp/failure-logs.txt ]; then
145+
FAILURE_LOGS=$(cat /tmp/failure-logs.txt)
146+
fi
147+
148+
# Run Python classifier
149+
cd ../bot-repo
150+
CLASSIFICATION=$(python3 scripts/classify_pr_failure.py \
151+
--pr-info "$PR_INFO" \
152+
--failed-checks "$FAILED_CHECKS" \
153+
--failure-logs "$FAILURE_LOGS" \
154+
--output-format github)
155+
156+
# Parse and output results
157+
echo "$CLASSIFICATION" >> $GITHUB_OUTPUT
158+
echo "Classification results:"
159+
echo "$CLASSIFICATION"
160+
env:
161+
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
162+
145163
- name: Load and customize prompt
146164
id: prepare-prompt
147165
run: |
148-
FAILURE_TYPE="${{ steps.analyze.outputs.primary-type }}"
166+
FAILURE_TYPE="${{ steps.analyze.outputs.failure-type }}"
167+
CONFIDENCE="${{ steps.analyze.outputs.confidence }}"
168+
REASONING="${{ steps.analyze.outputs.reasoning }}"
149169
REPO="${{ github.event.inputs.target_repo }}"
150170
PR_NUMBER="${{ github.event.inputs.pr_number }}"
151171
PR_TITLE="${{ steps.pr-details.outputs.pr-title }}"
152172
PR_AUTHOR="${{ steps.pr-details.outputs.pr-author }}"
153-
FAILED_NAMES="${{ steps.analyze.outputs.failed-names }}"
173+
FAILED_NAMES="${{ steps.analyze.outputs.failed-check-names }}"
174+
175+
echo "Classification: $FAILURE_TYPE (confidence: $CONFIDENCE)"
176+
echo "Reasoning: $REASONING"
154177
155-
# Skip unknown failure types
178+
# Skip unknown failure types or low confidence
156179
if [ "$FAILURE_TYPE" = "unknown" ]; then
157180
echo "Failure type is unknown - skipping automated fix attempt"
181+
echo "Reasoning: $REASONING"
158182
echo "should-skip=true" >> $GITHUB_OUTPUT
159183
exit 0
160184
fi

.pre-commit-config.yaml

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,15 @@ repos:
3333
- id: ruff-format
3434
types_or: [python, jupyter]
3535

36+
- repo: https://github.com/pre-commit/mirrors-mypy
37+
rev: v1.18.2
38+
hooks:
39+
- id: mypy
40+
entry: python3 -m mypy --config-file pyproject.toml
41+
language: system
42+
types: [python]
43+
exclude: "tests"
44+
3645
- repo: https://github.com/crate-ci/typos
3746
rev: v1 # v1.19.0
3847
hooks:
@@ -58,5 +67,5 @@ ci:
5867
autoupdate_branch: ''
5968
autoupdate_commit_msg: '[pre-commit.ci] pre-commit autoupdate'
6069
autoupdate_schedule: weekly
61-
skip: [nextjs-lint]
70+
skip: [nextjs-lint, mypy]
6271
submodules: false

pyproject.toml

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ license = "Apache-2.0"
88
repository = "https://github.com/VectorInstitute/aieng-bot-maintain"
99
requires-python = ">=3.12"
1010
dependencies = [
11+
"anthropic>=0.42.0",
1112
"pyyaml>=6.0.2",
1213
]
1314

@@ -36,6 +37,31 @@ docs = [
3637
"ipython>=8.31.0",
3738
]
3839

40+
[tool.uv]
41+
default-groups = ["dev"]
42+
43+
[tool.mypy]
44+
follow_imports = "normal"
45+
ignore_missing_imports = false
46+
install_types = true
47+
pretty = true
48+
non_interactive = true
49+
allow_untyped_defs = false
50+
no_implicit_optional = true
51+
check_untyped_defs = true
52+
namespace_packages = true
53+
explicit_package_bases = true
54+
warn_unused_configs = true
55+
allow_subclassing_any = false
56+
allow_untyped_calls = false
57+
allow_incomplete_defs = false
58+
allow_untyped_decorators = false
59+
warn_redundant_casts = true
60+
warn_unused_ignores = true
61+
implicit_reexport = false
62+
strict_equality = true
63+
extra_checks = true
64+
3965
[tool.ruff]
4066
include = ["*.py", "pyproject.toml", "*.ipynb"]
4167
line-length = 88

scripts/agent_tracer.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -321,7 +321,7 @@ def finalize(
321321
files_modified: list[str] | None = None,
322322
commit_sha: str | None = None,
323323
commit_url: str | None = None,
324-
):
324+
) -> None:
325325
"""Finalize trace with execution results.
326326
327327
Args:
@@ -348,7 +348,7 @@ def finalize(
348348
}
349349
)
350350

351-
def save_trace(self, filepath: str):
351+
def save_trace(self, filepath: str) -> None:
352352
"""Save trace to JSON file.
353353
354354
Args:
@@ -405,7 +405,7 @@ def get_summary(self) -> str:
405405
Summary string for PR comments
406406
407407
"""
408-
event_counts = {}
408+
event_counts: dict[str, int] = {}
409409
for event in self.trace["events"]:
410410
event_type = event["type"]
411411
event_counts[event_type] = event_counts.get(event_type, 0) + 1

0 commit comments

Comments
 (0)