mirror of
https://github.com/Sea-Haven-Industries/open-swe.git
synced 2026-09-30 13:53:15 +00:00
Part of the domain-reorg adoption (build plan step C2): fork content,
upstream layout. Nine 1:1 module moves (reviewer_diff/eval_store/
findings/groups/publish/reconcile/trace_context + review_style_
collector/guidance) into agent/review/, with internal relative
imports re-wired to the new package depth. agent/review/__init__.py
mirrors upstream's thin re-export shim (one of the 21 verified "A"
structural adds).
Rewrote the 38 grep hits across importer files (agent/{analyzer,
ci_autofix,reviewer,webapp}.py, agent/dashboard/*, agent/middleware/
settle_review_check.py, agent/tools/*, agent/utils/github_feedback.py,
agent/webhooks/github.py, evals/reviewer/*, and the reviewer test
suite) to point at agent.review.*; 4 of the 38 hits were name
collisions (list_reviewer_findings, reviewer_outcomes,
_reviewer_thread_id, reviewer_thread_id — not the moved modules) and
were left untouched. tests/test_github_checks.py's module-alias
import (`from agent import reviewer_publish`) follows upstream's own
`from agent.review import publish as reviewer_publish` pattern so
downstream `reviewer_publish.*` call sites needed no changes.
agent/reviewer.py and agent/webapp.py stay in place per the hard
rule (fork content, import-only rewire) and are not part of this
package.
Gates: ruff check + ruff format --check, pytest --co -q (1637
collected), full unit suite (1637 passed), and the reviewer/findings
suite in isolation (pytest -k "review or finding", 421 passed).
86 lines
3.4 KiB
Python
86 lines
3.4 KiB
Python
"""After-agent middleware that closes a still-open review check run.
|
|
|
|
``publish_review`` normally completes the ``Open SWE Review`` check run and
|
|
clears ``review_check_run_id`` from reviewer thread metadata. If the run ends
|
|
without ever publishing (crash, model-call limit, sandbox failure), the check
|
|
would hang "in progress" on the PR forever. This hook closes it as neutral —
|
|
the review not completing is reviewer infrastructure failing, not the PR.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import logging
|
|
from typing import Any
|
|
|
|
from langchain.agents.middleware import AgentState, after_agent
|
|
from langgraph.config import get_config
|
|
from langgraph.runtime import Runtime
|
|
|
|
from ..review.findings import get_thread_metadata
|
|
from ..review.publish import settle_review_check_run
|
|
from ..utils.github_token import get_github_token
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
@after_agent
|
|
async def settle_review_check_on_exit(
|
|
state: AgentState,
|
|
runtime: Runtime,
|
|
) -> dict[str, Any] | None:
|
|
"""Fail the tracked review check run if the run ended without publishing."""
|
|
config = get_config()
|
|
configurable = config.get("configurable", {})
|
|
if not isinstance(configurable, dict):
|
|
return None
|
|
thread_id = configurable.get("thread_id")
|
|
repo_config = configurable.get("repo")
|
|
if not isinstance(thread_id, str) or not thread_id or not isinstance(repo_config, dict):
|
|
return None
|
|
owner = repo_config.get("owner")
|
|
repo = repo_config.get("name")
|
|
if not isinstance(owner, str) or not owner or not isinstance(repo, str) or not repo:
|
|
return None
|
|
|
|
try:
|
|
metadata = await get_thread_metadata(thread_id)
|
|
if not isinstance(metadata.get("review_check_run_id"), int):
|
|
return None
|
|
token = get_github_token()
|
|
if not token:
|
|
logger.warning("No GitHub token to settle stale review check on thread %s", thread_id)
|
|
return None
|
|
# A pending result means publish_review DID finish but its completion
|
|
# PATCH failed transiently — retry with the real conclusion instead of
|
|
# misreporting a published review as failed.
|
|
pending = metadata.get("review_check_pending_result")
|
|
if isinstance(pending, dict) and pending.get("conclusion") in {
|
|
"success",
|
|
"neutral",
|
|
"failure",
|
|
}:
|
|
conclusion = pending["conclusion"]
|
|
title = str(pending.get("title") or "Review completed")
|
|
summary = str(pending.get("summary") or "")
|
|
else:
|
|
# Neutral, not failure: an incomplete review is a reviewer-infra
|
|
# problem, and a red X on the PR misreads as a code problem.
|
|
conclusion = "neutral"
|
|
title = "Review did not complete"
|
|
summary = (
|
|
"The Open SWE review run ended without publishing a review. "
|
|
"Re-trigger the review by pushing a commit or re-requesting it."
|
|
)
|
|
await settle_review_check_run(
|
|
thread_id=thread_id,
|
|
owner=owner,
|
|
repo=repo,
|
|
token=token,
|
|
conclusion=conclusion,
|
|
title=title,
|
|
summary=summary,
|
|
)
|
|
logger.info("Settled stale review check run for thread %s", thread_id)
|
|
except Exception:
|
|
logger.exception("Failed to settle stale review check run for thread %s", thread_id)
|
|
return None
|