mirror of
https://github.com/Sea-Haven-Industries/open-swe.git
synced 2026-10-06 12:22:11 +00:00
refactor: consolidate reviewer modules into agent/review/
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).
This commit is contained in:
parent
a82da1f907
commit
62d9945df4
44 changed files with 163 additions and 152 deletions
|
|
@ -36,7 +36,7 @@ from .middleware import (
|
||||||
TimeoutWrapupMiddleware,
|
TimeoutWrapupMiddleware,
|
||||||
ToolErrorMiddleware,
|
ToolErrorMiddleware,
|
||||||
)
|
)
|
||||||
from .review_style_guidance import REVIEWER_STYLE_THEMES
|
from .review.style_guidance import REVIEWER_STYLE_THEMES
|
||||||
from .server import (
|
from .server import (
|
||||||
DEFAULT_LLM_MAX_TOKENS,
|
DEFAULT_LLM_MAX_TOKENS,
|
||||||
DEFAULT_LLM_MODEL_ID,
|
DEFAULT_LLM_MODEL_ID,
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ from .dashboard.agent_overrides import load_profile, resolve_login_from_email_as
|
||||||
from .dashboard.autofix_state import is_pr_autofix_disabled
|
from .dashboard.autofix_state import is_pr_autofix_disabled
|
||||||
from .dashboard.enabled_repos import is_review_repo_enabled
|
from .dashboard.enabled_repos import is_review_repo_enabled
|
||||||
from .dispatch import dispatch_agent_run
|
from .dispatch import dispatch_agent_run
|
||||||
from .reviewer_findings import REVIEWER_THREAD_KIND
|
from .review.findings import REVIEWER_THREAD_KIND
|
||||||
from .utils.dashboard_links import dashboard_thread_url
|
from .utils.dashboard_links import dashboard_thread_url
|
||||||
from .utils.github_app import get_github_app_installation_token
|
from .utils.github_app import get_github_app_installation_token
|
||||||
from .utils.github_checks import post_autofix_status_check
|
from .utils.github_checks import post_autofix_status_check
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ from typing import Any, Literal
|
||||||
import httpx
|
import httpx
|
||||||
from langgraph_sdk import get_client
|
from langgraph_sdk import get_client
|
||||||
|
|
||||||
from ..reviewer_findings import REVIEWER_THREAD_KIND
|
from ..review.findings import REVIEWER_THREAD_KIND
|
||||||
from ..utils.github_app import get_github_app_installation_token
|
from ..utils.github_app import get_github_app_installation_token
|
||||||
|
|
||||||
USAGE_THREAD_NAMESPACE: list[str] = ["agent_usage", "threads"]
|
USAGE_THREAD_NAMESPACE: list[str] = ["agent_usage", "threads"]
|
||||||
|
|
|
||||||
|
|
@ -17,13 +17,13 @@ from typing import Any, Literal, TypedDict
|
||||||
|
|
||||||
from langgraph_sdk import get_client
|
from langgraph_sdk import get_client
|
||||||
|
|
||||||
from agent.reviewer_eval_store import (
|
from agent.review.eval_store import (
|
||||||
_HEARTBEAT_STALE_SECONDS,
|
_HEARTBEAT_STALE_SECONDS,
|
||||||
DEFAULT_EVAL_PROJECT,
|
DEFAULT_EVAL_PROJECT,
|
||||||
EVALS_NAMESPACE,
|
EVALS_NAMESPACE,
|
||||||
REVIEWER_EVAL_KEY,
|
REVIEWER_EVAL_KEY,
|
||||||
)
|
)
|
||||||
from agent.reviewer_findings import REVIEW_FINDING_CAP
|
from agent.review.findings import REVIEW_FINDING_CAP
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ from urllib.parse import urljoin, urlparse
|
||||||
import httpx
|
import httpx
|
||||||
from fastapi import HTTPException, Response
|
from fastapi import HTTPException, Response
|
||||||
|
|
||||||
from ..reviewer_findings import REVIEWER_THREAD_KIND
|
from ..review.findings import REVIEWER_THREAD_KIND
|
||||||
from ..utils.github_app import get_github_app_installation_token
|
from ..utils.github_app import get_github_app_installation_token
|
||||||
from ..utils.github_checks import github_headers
|
from ..utils.github_checks import github_headers
|
||||||
from ..utils.thread_ops import langgraph_client
|
from ..utils.thread_ops import langgraph_client
|
||||||
|
|
@ -443,7 +443,7 @@ async def create_review_comment(
|
||||||
|
|
||||||
|
|
||||||
_HTML_COMMENT_RE = re.compile(r"<!--.*?-->", re.DOTALL)
|
_HTML_COMMENT_RE = re.compile(r"<!--.*?-->", re.DOTALL)
|
||||||
# Inline comments the reviewer posts carry this hidden marker (see reviewer_publish).
|
# Inline comments the reviewer posts carry this hidden marker (see review.publish).
|
||||||
_OPEN_SWE_COMMENT_RE = re.compile(r"<!--\s*open-swe-review-comment\b")
|
_OPEN_SWE_COMMENT_RE = re.compile(r"<!--\s*open-swe-review-comment\b")
|
||||||
_REVIEW_COMMENTS_PER_PAGE = 100
|
_REVIEW_COMMENTS_PER_PAGE = 100
|
||||||
# Bound the fetch so a pathological PR can't trigger unbounded paging (~2000 comments).
|
# Bound the fetch so a pathological PR can't trigger unbounded paging (~2000 comments).
|
||||||
|
|
@ -715,7 +715,7 @@ async def dry_run_trace_resolution(owner: str, repo: str, pr_number: int) -> dic
|
||||||
"""Resolve a PR to its author coding-agent thread without running a review."""
|
"""Resolve a PR to its author coding-agent thread without running a review."""
|
||||||
from dataclasses import asdict
|
from dataclasses import asdict
|
||||||
|
|
||||||
from ..reviewer_trace_context import resolve_pr_trace
|
from ..review.trace_context import resolve_pr_trace
|
||||||
from ..utils.github_app import get_github_app_installation_token_with_expiry
|
from ..utils.github_app import get_github_app_installation_token_with_expiry
|
||||||
from ..utils.slack import GitHubPrRef
|
from ..utils.slack import GitHubPrRef
|
||||||
from ..webapp import fetch_github_pr_metadata
|
from ..webapp import fetch_github_pr_metadata
|
||||||
|
|
|
||||||
|
|
@ -19,8 +19,8 @@ from typing import Any
|
||||||
import httpx
|
import httpx
|
||||||
from fastapi import HTTPException
|
from fastapi import HTTPException
|
||||||
|
|
||||||
from ..reviewer_diff import fetch_pr_diff
|
from ..review.diff import fetch_pr_diff
|
||||||
from ..reviewer_findings import REVIEWER_THREAD_KIND
|
from ..review.findings import REVIEWER_THREAD_KIND
|
||||||
from ..utils.github_app import get_github_app_installation_token
|
from ..utils.github_app import get_github_app_installation_token
|
||||||
from ..utils.thread_ops import langgraph_client, langgraph_url
|
from ..utils.thread_ops import langgraph_client, langgraph_url
|
||||||
from .options import SUPPORTED_MODEL_IDS, model_supports_effort
|
from .options import SUPPORTED_MODEL_IDS, model_supports_effort
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ from typing import Any
|
||||||
|
|
||||||
from langgraph_sdk import get_client
|
from langgraph_sdk import get_client
|
||||||
|
|
||||||
from ..review_style_collector import (
|
from ..review.style_collector import (
|
||||||
collect_review_samples,
|
collect_review_samples,
|
||||||
format_samples_for_analyzer,
|
format_samples_for_analyzer,
|
||||||
generate_review_style_thread_id,
|
generate_review_style_thread_id,
|
||||||
|
|
|
||||||
|
|
@ -16,8 +16,8 @@ from langchain.agents.middleware import AgentState, after_agent
|
||||||
from langgraph.config import get_config
|
from langgraph.config import get_config
|
||||||
from langgraph.runtime import Runtime
|
from langgraph.runtime import Runtime
|
||||||
|
|
||||||
from ..reviewer_findings import get_thread_metadata
|
from ..review.findings import get_thread_metadata
|
||||||
from ..reviewer_publish import settle_review_check_run
|
from ..review.publish import settle_review_check_run
|
||||||
from ..utils.github_token import get_github_token
|
from ..utils.github_token import get_github_token
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
|
||||||
11
agent/review/__init__.py
Normal file
11
agent/review/__init__.py
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
from agent.review.findings import (
|
||||||
|
REVIEW_FINDING_CAP,
|
||||||
|
REVIEWER_THREAD_KIND,
|
||||||
|
Finding,
|
||||||
|
)
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
"Finding",
|
||||||
|
"REVIEWER_THREAD_KIND",
|
||||||
|
"REVIEW_FINDING_CAP",
|
||||||
|
]
|
||||||
|
|
@ -217,7 +217,7 @@ async def fetch_pr_diff(
|
||||||
"""
|
"""
|
||||||
import httpx
|
import httpx
|
||||||
|
|
||||||
from .utils.github_http import github_client, github_request
|
from ..utils.github_http import github_client, github_request
|
||||||
|
|
||||||
url = f"https://api.github.com/repos/{owner}/{repo}/pulls/{pr_number}"
|
url = f"https://api.github.com/repos/{owner}/{repo}/pulls/{pr_number}"
|
||||||
try:
|
try:
|
||||||
|
|
@ -253,7 +253,7 @@ async def fetch_pr_metadata(
|
||||||
"""
|
"""
|
||||||
import httpx
|
import httpx
|
||||||
|
|
||||||
from .utils.github_http import github_client, github_request
|
from ..utils.github_http import github_client, github_request
|
||||||
|
|
||||||
url = f"https://api.github.com/repos/{owner}/{repo}/pulls/{pr_number}"
|
url = f"https://api.github.com/repos/{owner}/{repo}/pulls/{pr_number}"
|
||||||
try:
|
try:
|
||||||
|
|
@ -22,8 +22,8 @@ from typing import TypedDict
|
||||||
from langchain_core.language_models.chat_models import BaseChatModel
|
from langchain_core.language_models.chat_models import BaseChatModel
|
||||||
from pydantic import BaseModel, Field
|
from pydantic import BaseModel, Field
|
||||||
|
|
||||||
from .reviewer_diff import parse_unified_diff
|
from .diff import parse_unified_diff
|
||||||
from .reviewer_findings import get_thread_metadata, set_reviewer_thread_metadata
|
from .findings import get_thread_metadata, set_reviewer_thread_metadata
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
@ -25,22 +25,22 @@ from typing import Any, TypedDict
|
||||||
|
|
||||||
import httpx
|
import httpx
|
||||||
|
|
||||||
from .reviewer_findings import (
|
from ..utils.dashboard_links import dashboard_thread_url
|
||||||
|
from ..utils.github_checks import CheckConclusion, complete_review_check_run
|
||||||
|
from ..utils.github_http import (
|
||||||
|
GITHUB_API_BASE,
|
||||||
|
GITHUB_GRAPHQL,
|
||||||
|
github_client,
|
||||||
|
github_request,
|
||||||
|
)
|
||||||
|
from ..utils.github_token import GitHubAuthError
|
||||||
|
from .findings import (
|
||||||
DiffSide,
|
DiffSide,
|
||||||
Finding,
|
Finding,
|
||||||
get_thread_metadata,
|
get_thread_metadata,
|
||||||
normalize_finding_title,
|
normalize_finding_title,
|
||||||
set_reviewer_thread_metadata,
|
set_reviewer_thread_metadata,
|
||||||
)
|
)
|
||||||
from .utils.dashboard_links import dashboard_thread_url
|
|
||||||
from .utils.github_checks import CheckConclusion, complete_review_check_run
|
|
||||||
from .utils.github_http import (
|
|
||||||
GITHUB_API_BASE,
|
|
||||||
GITHUB_GRAPHQL,
|
|
||||||
github_client,
|
|
||||||
github_request,
|
|
||||||
)
|
|
||||||
from .utils.github_token import GitHubAuthError
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
@ -2,14 +2,14 @@ from __future__ import annotations
|
||||||
|
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from .reviewer_findings import (
|
from .findings import (
|
||||||
Finding,
|
Finding,
|
||||||
FindingInteraction,
|
FindingInteraction,
|
||||||
_coerce_surface,
|
_coerce_surface,
|
||||||
list_findings,
|
list_findings,
|
||||||
replace_findings,
|
replace_findings,
|
||||||
)
|
)
|
||||||
from .reviewer_publish import parse_review_comment_marker
|
from .publish import parse_review_comment_marker
|
||||||
|
|
||||||
ReviewThread = dict[str, Any]
|
ReviewThread = dict[str, Any]
|
||||||
ReviewThreadMatch = tuple[ReviewThread, int | None]
|
ReviewThreadMatch = tuple[ReviewThread, int | None]
|
||||||
|
|
@ -14,10 +14,10 @@ from typing import Any
|
||||||
|
|
||||||
from deepagents.backends.protocol import SandboxBackendProtocol
|
from deepagents.backends.protocol import SandboxBackendProtocol
|
||||||
|
|
||||||
from .dashboard.team_credentials import get_langsmith_credentials
|
from ..dashboard.team_credentials import get_langsmith_credentials
|
||||||
from .dashboard.team_settings import get_team_review_tracing_project
|
from ..dashboard.team_settings import get_team_review_tracing_project
|
||||||
from .integrations.langsmith_tools import _client
|
from ..integrations.langsmith_tools import _client
|
||||||
from .utils.langsmith import get_langsmith_trace_url
|
from ..utils.langsmith import get_langsmith_trace_url
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
@ -55,17 +55,17 @@ from .middleware import (
|
||||||
refresh_github_proxy_before_model,
|
refresh_github_proxy_before_model,
|
||||||
settle_review_check_on_exit,
|
settle_review_check_on_exit,
|
||||||
)
|
)
|
||||||
from .reviewer_diff import compute_diff_line_set, fetch_pr_diff, fetch_pr_metadata
|
from .review.diff import compute_diff_line_set, fetch_pr_diff, fetch_pr_metadata
|
||||||
from .reviewer_findings import (
|
from .review.findings import (
|
||||||
REVIEW_FINDING_CAP,
|
REVIEW_FINDING_CAP,
|
||||||
)
|
)
|
||||||
from .reviewer_findings import (
|
from .review.findings import (
|
||||||
list_findings as list_findings_async,
|
list_findings as list_findings_async,
|
||||||
)
|
)
|
||||||
from .reviewer_groups import maybe_generate_and_store_diff_groups
|
from .review.groups import maybe_generate_and_store_diff_groups
|
||||||
from .reviewer_publish import fetch_pr_review_threads
|
from .review.publish import fetch_pr_review_threads
|
||||||
from .reviewer_reconcile import reconcile_findings_with_review_threads
|
from .review.reconcile import reconcile_findings_with_review_threads
|
||||||
from .reviewer_trace_context import (
|
from .review.trace_context import (
|
||||||
PRTraceContext,
|
PRTraceContext,
|
||||||
format_pr_trace_context_prompt,
|
format_pr_trace_context_prompt,
|
||||||
prepare_pr_trace_context,
|
prepare_pr_trace_context,
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,8 @@ from typing import Any
|
||||||
|
|
||||||
from langgraph.config import get_config
|
from langgraph.config import get_config
|
||||||
|
|
||||||
from ..reviewer_diff import is_range_in_diff
|
from ..review.diff import is_range_in_diff
|
||||||
from ..reviewer_findings import (
|
from ..review.findings import (
|
||||||
DEFAULT_FINDING_TITLE,
|
DEFAULT_FINDING_TITLE,
|
||||||
MAX_SUGGESTION_LINES,
|
MAX_SUGGESTION_LINES,
|
||||||
Confidence,
|
Confidence,
|
||||||
|
|
@ -133,7 +133,7 @@ async def add_finding(
|
||||||
|
|
||||||
diff_hunk: str | None = None
|
diff_hunk: str | None = None
|
||||||
if isinstance(diff_text, str) and diff_text:
|
if isinstance(diff_text, str) and diff_text:
|
||||||
from ..reviewer_diff import extract_diff_hunk
|
from ..review.diff import extract_diff_hunk
|
||||||
|
|
||||||
diff_hunk = extract_diff_hunk(diff_text, file, start_line, end_line)
|
diff_hunk = extract_diff_hunk(diff_text, file, start_line, end_line)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,12 +4,12 @@ from __future__ import annotations
|
||||||
|
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from ..reviewer_findings import (
|
from ..review.findings import (
|
||||||
ReviewerThreadMissingError,
|
ReviewerThreadMissingError,
|
||||||
get_thread_id_from_runtime,
|
get_thread_id_from_runtime,
|
||||||
thread_missing_tool_result,
|
thread_missing_tool_result,
|
||||||
)
|
)
|
||||||
from ..reviewer_findings import (
|
from ..review.findings import (
|
||||||
list_findings as list_findings_async,
|
list_findings as list_findings_async,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ from typing import Any
|
||||||
|
|
||||||
from langgraph.config import get_config
|
from langgraph.config import get_config
|
||||||
|
|
||||||
from ..reviewer_findings import list_findings as list_findings_async
|
from ..review.findings import list_findings as list_findings_async
|
||||||
|
|
||||||
_COMPACT_FIELDS = (
|
_COMPACT_FIELDS = (
|
||||||
"id",
|
"id",
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,8 @@ from typing import Any
|
||||||
from langgraph.config import get_config
|
from langgraph.config import get_config
|
||||||
|
|
||||||
from ..dashboard.team_settings import get_team_review_trace_links_enabled
|
from ..dashboard.team_settings import get_team_review_trace_links_enabled
|
||||||
from ..reviewer_diff import compute_diff_line_set, fetch_pr_diff, is_range_in_diff
|
from ..review.diff import compute_diff_line_set, fetch_pr_diff, is_range_in_diff
|
||||||
from ..reviewer_findings import (
|
from ..review.findings import (
|
||||||
REVIEW_FINDING_CAP,
|
REVIEW_FINDING_CAP,
|
||||||
REVIEWER_EVAL_PUBLICATION_KEY,
|
REVIEWER_EVAL_PUBLICATION_KEY,
|
||||||
SEVERITY_ORDER,
|
SEVERITY_ORDER,
|
||||||
|
|
@ -26,10 +26,10 @@ from ..reviewer_findings import (
|
||||||
set_reviewer_thread_metadata,
|
set_reviewer_thread_metadata,
|
||||||
thread_missing_tool_result,
|
thread_missing_tool_result,
|
||||||
)
|
)
|
||||||
from ..reviewer_findings import (
|
from ..review.findings import (
|
||||||
list_findings as list_findings_async,
|
list_findings as list_findings_async,
|
||||||
)
|
)
|
||||||
from ..reviewer_publish import (
|
from ..review.publish import (
|
||||||
clear_review_started_comment,
|
clear_review_started_comment,
|
||||||
fetch_pr_review_threads,
|
fetch_pr_review_threads,
|
||||||
fetch_review_comments,
|
fetch_review_comments,
|
||||||
|
|
@ -44,7 +44,7 @@ from ..reviewer_publish import (
|
||||||
resolve_review_thread,
|
resolve_review_thread,
|
||||||
settle_review_check_run,
|
settle_review_check_run,
|
||||||
)
|
)
|
||||||
from ..reviewer_reconcile import reconcile_findings_with_review_threads
|
from ..review.reconcile import reconcile_findings_with_review_threads
|
||||||
from ..utils.dashboard_links import dashboard_review_url
|
from ..utils.dashboard_links import dashboard_review_url
|
||||||
from ..utils.github_checks import review_check_conclusion
|
from ..utils.github_checks import review_check_conclusion
|
||||||
from ..utils.github_token import (
|
from ..utils.github_token import (
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ from typing import Any
|
||||||
|
|
||||||
from langgraph.config import get_config
|
from langgraph.config import get_config
|
||||||
|
|
||||||
from ..reviewer_findings import (
|
from ..review.findings import (
|
||||||
FindingInteraction,
|
FindingInteraction,
|
||||||
ReviewerThreadMissingError,
|
ReviewerThreadMissingError,
|
||||||
append_finding_interaction,
|
append_finding_interaction,
|
||||||
|
|
@ -13,7 +13,7 @@ from ..reviewer_findings import (
|
||||||
thread_missing_tool_result,
|
thread_missing_tool_result,
|
||||||
update_finding_fields,
|
update_finding_fields,
|
||||||
)
|
)
|
||||||
from ..reviewer_publish import reply_to_review_comment
|
from ..review.publish import reply_to_review_comment
|
||||||
from ..utils.github_token import get_github_token
|
from ..utils.github_token import get_github_token
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ from typing import Any
|
||||||
|
|
||||||
from langgraph.config import get_config
|
from langgraph.config import get_config
|
||||||
|
|
||||||
from ..reviewer_findings import (
|
from ..review.findings import (
|
||||||
Finding,
|
Finding,
|
||||||
ReviewerThreadMissingError,
|
ReviewerThreadMissingError,
|
||||||
get_finding,
|
get_finding,
|
||||||
|
|
@ -13,14 +13,14 @@ from ..reviewer_findings import (
|
||||||
update_finding_fields,
|
update_finding_fields,
|
||||||
update_finding_surface,
|
update_finding_surface,
|
||||||
)
|
)
|
||||||
from ..reviewer_publish import (
|
from ..review.publish import (
|
||||||
fetch_pr_review_threads,
|
fetch_pr_review_threads,
|
||||||
fetch_review_thread_id_for_comment,
|
fetch_review_thread_id_for_comment,
|
||||||
render_resolution_comment,
|
render_resolution_comment,
|
||||||
reply_to_review_comment,
|
reply_to_review_comment,
|
||||||
resolve_review_thread,
|
resolve_review_thread,
|
||||||
)
|
)
|
||||||
from ..reviewer_reconcile import reconcile_findings_with_review_threads
|
from ..review.reconcile import reconcile_findings_with_review_threads
|
||||||
from ..utils.github_token import get_github_token
|
from ..utils.github_token import get_github_token
|
||||||
from ..utils.reviewer_outcomes import emit_finding_status_outcome
|
from ..utils.reviewer_outcomes import emit_finding_status_outcome
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ from typing import Any
|
||||||
|
|
||||||
from langgraph.config import get_config
|
from langgraph.config import get_config
|
||||||
|
|
||||||
from ..reviewer_findings import (
|
from ..review.findings import (
|
||||||
DEFAULT_FINDING_TITLE,
|
DEFAULT_FINDING_TITLE,
|
||||||
MAX_SUGGESTION_LINES,
|
MAX_SUGGESTION_LINES,
|
||||||
Finding,
|
Finding,
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ from typing import Any
|
||||||
from langgraph_sdk import get_client
|
from langgraph_sdk import get_client
|
||||||
from langgraph_sdk.client import LangGraphClient
|
from langgraph_sdk.client import LangGraphClient
|
||||||
|
|
||||||
from ..reviewer_findings import list_findings
|
from ..review.findings import list_findings
|
||||||
from .langsmith import create_langsmith_feedback, delete_langsmith_feedback
|
from .langsmith import create_langsmith_feedback, delete_langsmith_feedback
|
||||||
from .reviewer_outcomes import outcome_from_score, upsert_finding_outcome
|
from .reviewer_outcomes import outcome_from_score, upsert_finding_outcome
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -49,17 +49,17 @@ from .dashboard.user_mappings import (
|
||||||
)
|
)
|
||||||
from .dashboard.workflow_approval import decide_workflow_push_approval
|
from .dashboard.workflow_approval import decide_workflow_push_approval
|
||||||
from .dispatch import dispatch_agent_run
|
from .dispatch import dispatch_agent_run
|
||||||
from .reviewer_findings import (
|
from .review.findings import (
|
||||||
REVIEWER_THREAD_KIND,
|
REVIEWER_THREAD_KIND,
|
||||||
Finding,
|
Finding,
|
||||||
append_finding_interaction, # noqa: F401
|
append_finding_interaction, # noqa: F401
|
||||||
set_reviewer_thread_metadata,
|
set_reviewer_thread_metadata,
|
||||||
)
|
)
|
||||||
from .reviewer_findings import (
|
from .review.findings import (
|
||||||
list_findings as list_reviewer_findings, # noqa: F401
|
list_findings as list_reviewer_findings, # noqa: F401
|
||||||
)
|
)
|
||||||
from .reviewer_publish import fetch_pr_review_threads, post_review_started_comment # noqa: F401
|
from .review.publish import fetch_pr_review_threads, post_review_started_comment # noqa: F401
|
||||||
from .reviewer_reconcile import reconcile_findings_with_review_threads # noqa: F401
|
from .review.reconcile import reconcile_findings_with_review_threads # noqa: F401
|
||||||
from .utils.atlassian_connect import verify_connect_webhook
|
from .utils.atlassian_connect import verify_connect_webhook
|
||||||
from .utils.auth import (
|
from .utils.auth import (
|
||||||
is_bot_token_only_mode,
|
is_bot_token_only_mode,
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ from agent import webapp
|
||||||
|
|
||||||
from ..ci_autofix import handle_ci_failure, handle_review_feedback
|
from ..ci_autofix import handle_ci_failure, handle_review_feedback
|
||||||
from ..dashboard.autofix_state import set_pr_autofix_disabled
|
from ..dashboard.autofix_state import set_pr_autofix_disabled
|
||||||
from ..reviewer_findings import FindingInteraction, ReviewerPRMeta, ReviewerSlackThread
|
from ..review.findings import FindingInteraction, ReviewerPRMeta, ReviewerSlackThread
|
||||||
from ..utils.github_ci import (
|
from ..utils.github_ci import (
|
||||||
branch_from_check_payload,
|
branch_from_check_payload,
|
||||||
head_sha_from_check_payload,
|
head_sha_from_check_payload,
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ from uuid import UUID
|
||||||
from langchain_anthropic import ChatAnthropic
|
from langchain_anthropic import ChatAnthropic
|
||||||
from langsmith.schemas import Example, Run
|
from langsmith.schemas import Example, Run
|
||||||
|
|
||||||
from agent.reviewer_findings import REVIEW_FINDING_CAP
|
from agent.review.findings import REVIEW_FINDING_CAP
|
||||||
|
|
||||||
JUDGE_MODEL = "claude-opus-4-5"
|
JUDGE_MODEL = "claude-opus-4-5"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -23,8 +23,8 @@ from langgraph_sdk import get_client
|
||||||
from langsmith import Client, aevaluate
|
from langsmith import Client, aevaluate
|
||||||
from langsmith.schemas import Example
|
from langsmith.schemas import Example
|
||||||
|
|
||||||
from agent.reviewer_eval_store import _EXPERIMENT_URL_RE, _LOG_TAIL_CHARS
|
from agent.review.eval_store import _EXPERIMENT_URL_RE, _LOG_TAIL_CHARS
|
||||||
from agent.reviewer_findings import REVIEW_FINDING_CAP
|
from agent.review.findings import REVIEW_FINDING_CAP
|
||||||
from evals.reviewer.judge import aggregate_pr, judge_match
|
from evals.reviewer.judge import aggregate_pr, judge_match
|
||||||
from evals.reviewer.store_reporter import StoreReporter, is_enabled
|
from evals.reviewer.store_reporter import StoreReporter, is_enabled
|
||||||
from evals.reviewer.target import (
|
from evals.reviewer.target import (
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ from typing import Any
|
||||||
|
|
||||||
from langgraph_sdk import get_client
|
from langgraph_sdk import get_client
|
||||||
|
|
||||||
from agent.reviewer_eval_store import (
|
from agent.review.eval_store import (
|
||||||
_HEARTBEAT_INTERVAL_SECONDS,
|
_HEARTBEAT_INTERVAL_SECONDS,
|
||||||
EVALS_NAMESPACE,
|
EVALS_NAMESPACE,
|
||||||
REVIEWER_EVAL_KEY,
|
REVIEWER_EVAL_KEY,
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ from typing import Any, Literal, cast
|
||||||
|
|
||||||
from langgraph_sdk import get_client
|
from langgraph_sdk import get_client
|
||||||
|
|
||||||
from agent.reviewer_findings import (
|
from agent.review.findings import (
|
||||||
REVIEW_FINDING_CAP,
|
REVIEW_FINDING_CAP,
|
||||||
REVIEWER_EVAL_PUBLICATION_KEY,
|
REVIEWER_EVAL_PUBLICATION_KEY,
|
||||||
Finding,
|
Finding,
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ from unittest.mock import AsyncMock
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from agent.dashboard import review_api, routes
|
from agent.dashboard import review_api, routes
|
||||||
from agent.reviewer_findings import REVIEWER_THREAD_KIND
|
from agent.review.findings import REVIEWER_THREAD_KIND
|
||||||
|
|
||||||
|
|
||||||
def _thread(owner: str, name: str, number: int, author: str) -> dict[str, Any]:
|
def _thread(owner: str, name: str, number: int, author: str) -> dict[str, Any]:
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ from typing import Any
|
||||||
import httpx
|
import httpx
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from agent import reviewer_publish
|
from agent.review import publish as reviewer_publish
|
||||||
from agent.utils import github_checks
|
from agent.utils import github_checks
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from agent.review_style_collector import (
|
from agent.review.style_collector import (
|
||||||
ReviewSample,
|
ReviewSample,
|
||||||
ReviewStyleSamples,
|
ReviewStyleSamples,
|
||||||
format_samples_for_analyzer,
|
format_samples_for_analyzer,
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ from __future__ import annotations
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from agent.reviewer_diff import (
|
from agent.review.diff import (
|
||||||
compute_diff_line_set,
|
compute_diff_line_set,
|
||||||
extract_diff_hunk,
|
extract_diff_hunk,
|
||||||
is_range_in_diff,
|
is_range_in_diff,
|
||||||
|
|
@ -103,7 +103,7 @@ async def test_compute_diff_in_sandbox_uses_three_dot_for_merge_base() -> None:
|
||||||
"""First-review path passes merge_base=True so we use base...head, not base..head."""
|
"""First-review path passes merge_base=True so we use base...head, not base..head."""
|
||||||
from unittest.mock import MagicMock
|
from unittest.mock import MagicMock
|
||||||
|
|
||||||
from agent.reviewer_diff import compute_diff_in_sandbox
|
from agent.review.diff import compute_diff_in_sandbox
|
||||||
|
|
||||||
backend = MagicMock()
|
backend = MagicMock()
|
||||||
backend.execute = MagicMock(return_value="")
|
backend.execute = MagicMock(return_value="")
|
||||||
|
|
@ -122,7 +122,7 @@ async def test_compute_diff_in_sandbox_uses_two_dot_by_default() -> None:
|
||||||
"""Re-review delta path passes merge_base=False so we use base..head."""
|
"""Re-review delta path passes merge_base=False so we use base..head."""
|
||||||
from unittest.mock import MagicMock
|
from unittest.mock import MagicMock
|
||||||
|
|
||||||
from agent.reviewer_diff import compute_diff_in_sandbox
|
from agent.review.diff import compute_diff_in_sandbox
|
||||||
|
|
||||||
backend = MagicMock()
|
backend = MagicMock()
|
||||||
backend.execute = MagicMock(return_value="")
|
backend.execute = MagicMock(return_value="")
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ from typing import Any
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from agent.reviewer_findings import new_finding
|
from agent.review.findings import new_finding
|
||||||
from evals.reviewer import target
|
from evals.reviewer import target
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ from unittest.mock import AsyncMock, patch
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from agent.reviewer_findings import (
|
from agent.review.findings import (
|
||||||
SEVERITY_ORDER,
|
SEVERITY_ORDER,
|
||||||
DiffSide,
|
DiffSide,
|
||||||
Finding,
|
Finding,
|
||||||
|
|
@ -120,7 +120,7 @@ def test_filter_findings_for_publish_caps_results() -> None:
|
||||||
async def test_list_findings_returns_empty_on_missing_metadata() -> None:
|
async def test_list_findings_returns_empty_on_missing_metadata() -> None:
|
||||||
fake_client = AsyncMock()
|
fake_client = AsyncMock()
|
||||||
fake_client.threads.get.return_value = {"metadata": {}}
|
fake_client.threads.get.return_value = {"metadata": {}}
|
||||||
with patch("agent.reviewer_findings.get_client", return_value=fake_client):
|
with patch("agent.review.findings.get_client", return_value=fake_client):
|
||||||
findings = await list_findings("tid")
|
findings = await list_findings("tid")
|
||||||
assert findings == []
|
assert findings == []
|
||||||
|
|
||||||
|
|
@ -137,7 +137,7 @@ async def test_list_findings_coerces_bad_entries() -> None:
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
with patch("agent.reviewer_findings.get_client", return_value=fake_client):
|
with patch("agent.review.findings.get_client", return_value=fake_client):
|
||||||
findings = await list_findings("tid")
|
findings = await list_findings("tid")
|
||||||
assert [f["id"] for f in findings] == ["f_ok"]
|
assert [f["id"] for f in findings] == ["f_ok"]
|
||||||
|
|
||||||
|
|
@ -146,7 +146,7 @@ async def test_list_findings_coerces_bad_entries() -> None:
|
||||||
async def test_replace_findings_calls_threads_update() -> None:
|
async def test_replace_findings_calls_threads_update() -> None:
|
||||||
fake_client = AsyncMock()
|
fake_client = AsyncMock()
|
||||||
findings = [_f(id="f_x")]
|
findings = [_f(id="f_x")]
|
||||||
with patch("agent.reviewer_findings.get_client", return_value=fake_client):
|
with patch("agent.review.findings.get_client", return_value=fake_client):
|
||||||
await replace_findings("tid", findings)
|
await replace_findings("tid", findings)
|
||||||
fake_client.threads.update.assert_awaited_once_with(
|
fake_client.threads.update.assert_awaited_once_with(
|
||||||
thread_id="tid", metadata={"findings": findings}
|
thread_id="tid", metadata={"findings": findings}
|
||||||
|
|
@ -161,7 +161,7 @@ async def test_append_finding_appends_to_existing_list() -> None:
|
||||||
fake_client = AsyncMock()
|
fake_client = AsyncMock()
|
||||||
fake_client.threads.get.return_value = {"metadata": {"findings": [existing]}}
|
fake_client.threads.get.return_value = {"metadata": {"findings": [existing]}}
|
||||||
|
|
||||||
with patch("agent.reviewer_findings.get_client", return_value=fake_client):
|
with patch("agent.review.findings.get_client", return_value=fake_client):
|
||||||
result = await append_finding("tid", new)
|
result = await append_finding("tid", new)
|
||||||
|
|
||||||
assert result["finding"]["id"] == "f_b"
|
assert result["finding"]["id"] == "f_b"
|
||||||
|
|
@ -193,7 +193,7 @@ async def test_concurrent_append_finding_preserves_distinct_findings() -> None:
|
||||||
class Client:
|
class Client:
|
||||||
threads = Threads()
|
threads = Threads()
|
||||||
|
|
||||||
with patch("agent.reviewer_findings.get_client", return_value=Client()):
|
with patch("agent.review.findings.get_client", return_value=Client()):
|
||||||
first, second = await asyncio.gather(
|
first, second = await asyncio.gather(
|
||||||
append_finding("tid", _f(id="f_a", description="first")),
|
append_finding("tid", _f(id="f_a", description="first")),
|
||||||
append_finding("tid", _f(id="f_b", description="second")),
|
append_finding("tid", _f(id="f_b", description="second")),
|
||||||
|
|
@ -224,7 +224,7 @@ async def test_concurrent_identical_findings_are_idempotent() -> None:
|
||||||
class Client:
|
class Client:
|
||||||
threads = Threads()
|
threads = Threads()
|
||||||
|
|
||||||
with patch("agent.reviewer_findings.get_client", return_value=Client()):
|
with patch("agent.review.findings.get_client", return_value=Client()):
|
||||||
first, second = await asyncio.gather(
|
first, second = await asyncio.gather(
|
||||||
append_finding("tid", _f(id="f_a")),
|
append_finding("tid", _f(id="f_a")),
|
||||||
append_finding("tid", _f(id="f_b")),
|
append_finding("tid", _f(id="f_b")),
|
||||||
|
|
@ -250,7 +250,7 @@ async def test_mutate_findings_reads_latest_before_mutating() -> None:
|
||||||
findings[0]["status"] = "resolved"
|
findings[0]["status"] = "resolved"
|
||||||
return True
|
return True
|
||||||
|
|
||||||
with patch("agent.reviewer_findings.get_client", return_value=fake_client):
|
with patch("agent.review.findings.get_client", return_value=fake_client):
|
||||||
result = await mutate_findings("tid", _mutator)
|
result = await mutate_findings("tid", _mutator)
|
||||||
|
|
||||||
assert seen == ["f_fresh"]
|
assert seen == ["f_fresh"]
|
||||||
|
|
@ -265,7 +265,7 @@ async def test_mutate_findings_skips_write_when_unchanged() -> None:
|
||||||
fake_client = AsyncMock()
|
fake_client = AsyncMock()
|
||||||
fake_client.threads.get.return_value = {"metadata": {"findings": [_f(id="f_a")]}}
|
fake_client.threads.get.return_value = {"metadata": {"findings": [_f(id="f_a")]}}
|
||||||
|
|
||||||
with patch("agent.reviewer_findings.get_client", return_value=fake_client):
|
with patch("agent.review.findings.get_client", return_value=fake_client):
|
||||||
await mutate_findings("tid", lambda _findings: False)
|
await mutate_findings("tid", lambda _findings: False)
|
||||||
|
|
||||||
fake_client.threads.update.assert_not_called()
|
fake_client.threads.update.assert_not_called()
|
||||||
|
|
@ -276,7 +276,7 @@ async def test_mutate_findings_does_not_write_after_transient_read_failure() ->
|
||||||
fake_client = AsyncMock()
|
fake_client = AsyncMock()
|
||||||
fake_client.threads.get.side_effect = RuntimeError("transient")
|
fake_client.threads.get.side_effect = RuntimeError("transient")
|
||||||
|
|
||||||
with patch("agent.reviewer_findings.get_client", return_value=fake_client):
|
with patch("agent.review.findings.get_client", return_value=fake_client):
|
||||||
with pytest.raises(RuntimeError, match="transient"):
|
with pytest.raises(RuntimeError, match="transient"):
|
||||||
await mutate_findings("tid", lambda findings: bool(findings.append(_f(id="f_new"))))
|
await mutate_findings("tid", lambda findings: bool(findings.append(_f(id="f_new"))))
|
||||||
|
|
||||||
|
|
@ -291,7 +291,7 @@ async def test_update_finding_fields_mutates_only_target() -> None:
|
||||||
fake_client = AsyncMock()
|
fake_client = AsyncMock()
|
||||||
fake_client.threads.get.return_value = {"metadata": {"findings": [a, b]}}
|
fake_client.threads.get.return_value = {"metadata": {"findings": [a, b]}}
|
||||||
|
|
||||||
with patch("agent.reviewer_findings.get_client", return_value=fake_client):
|
with patch("agent.review.findings.get_client", return_value=fake_client):
|
||||||
updated = await update_finding_fields("tid", "f_b", {"status": "resolved"})
|
updated = await update_finding_fields("tid", "f_b", {"status": "resolved"})
|
||||||
|
|
||||||
assert updated is not None
|
assert updated is not None
|
||||||
|
|
@ -306,7 +306,7 @@ async def test_update_finding_fields_mutates_only_target() -> None:
|
||||||
async def test_update_finding_fields_returns_none_for_unknown_id() -> None:
|
async def test_update_finding_fields_returns_none_for_unknown_id() -> None:
|
||||||
fake_client = AsyncMock()
|
fake_client = AsyncMock()
|
||||||
fake_client.threads.get.return_value = {"metadata": {"findings": [_f(id="f_a")]}}
|
fake_client.threads.get.return_value = {"metadata": {"findings": [_f(id="f_a")]}}
|
||||||
with patch("agent.reviewer_findings.get_client", return_value=fake_client):
|
with patch("agent.review.findings.get_client", return_value=fake_client):
|
||||||
result = await update_finding_fields("tid", "f_missing", {"status": "resolved"})
|
result = await update_finding_fields("tid", "f_missing", {"status": "resolved"})
|
||||||
assert result is None
|
assert result is None
|
||||||
fake_client.threads.update.assert_not_called()
|
fake_client.threads.update.assert_not_called()
|
||||||
|
|
@ -315,7 +315,7 @@ async def test_update_finding_fields_returns_none_for_unknown_id() -> None:
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_set_reviewer_thread_metadata_includes_kind() -> None:
|
async def test_set_reviewer_thread_metadata_includes_kind() -> None:
|
||||||
fake_client = AsyncMock()
|
fake_client = AsyncMock()
|
||||||
with patch("agent.reviewer_findings.get_client", return_value=fake_client):
|
with patch("agent.review.findings.get_client", return_value=fake_client):
|
||||||
await set_reviewer_thread_metadata("tid", watch=True, last_reviewed_sha="sha")
|
await set_reviewer_thread_metadata("tid", watch=True, last_reviewed_sha="sha")
|
||||||
metadata = fake_client.threads.update.await_args.kwargs["metadata"]
|
metadata = fake_client.threads.update.await_args.kwargs["metadata"]
|
||||||
assert metadata["kind"] == "reviewer"
|
assert metadata["kind"] == "reviewer"
|
||||||
|
|
@ -328,7 +328,7 @@ async def test_set_reviewer_thread_metadata_includes_kind() -> None:
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_set_reviewer_thread_metadata_persists_head_sha() -> None:
|
async def test_set_reviewer_thread_metadata_persists_head_sha() -> None:
|
||||||
fake_client = AsyncMock()
|
fake_client = AsyncMock()
|
||||||
with patch("agent.reviewer_findings.get_client", return_value=fake_client):
|
with patch("agent.review.findings.get_client", return_value=fake_client):
|
||||||
await set_reviewer_thread_metadata("tid", head_sha="newhead")
|
await set_reviewer_thread_metadata("tid", head_sha="newhead")
|
||||||
metadata = fake_client.threads.update.await_args.kwargs["metadata"]
|
metadata = fake_client.threads.update.await_args.kwargs["metadata"]
|
||||||
assert metadata["head_sha"] == "newhead"
|
assert metadata["head_sha"] == "newhead"
|
||||||
|
|
@ -340,7 +340,7 @@ async def test_resolve_review_head_sha_prefers_metadata_over_config() -> None:
|
||||||
the stale head frozen in the run's config."""
|
the stale head frozen in the run's config."""
|
||||||
fake_client = AsyncMock()
|
fake_client = AsyncMock()
|
||||||
fake_client.threads.get.return_value = {"metadata": {"head_sha": "metahead"}}
|
fake_client.threads.get.return_value = {"metadata": {"head_sha": "metahead"}}
|
||||||
with patch("agent.reviewer_findings.get_client", return_value=fake_client):
|
with patch("agent.review.findings.get_client", return_value=fake_client):
|
||||||
head = await resolve_review_head_sha("tid", {"head_sha": "confighead"})
|
head = await resolve_review_head_sha("tid", {"head_sha": "confighead"})
|
||||||
assert head == "metahead"
|
assert head == "metahead"
|
||||||
|
|
||||||
|
|
@ -349,7 +349,7 @@ async def test_resolve_review_head_sha_prefers_metadata_over_config() -> None:
|
||||||
async def test_resolve_review_head_sha_falls_back_to_config_when_metadata_empty() -> None:
|
async def test_resolve_review_head_sha_falls_back_to_config_when_metadata_empty() -> None:
|
||||||
fake_client = AsyncMock()
|
fake_client = AsyncMock()
|
||||||
fake_client.threads.get.return_value = {"metadata": {}}
|
fake_client.threads.get.return_value = {"metadata": {}}
|
||||||
with patch("agent.reviewer_findings.get_client", return_value=fake_client):
|
with patch("agent.review.findings.get_client", return_value=fake_client):
|
||||||
head = await resolve_review_head_sha("tid", {"head_sha": "confighead"})
|
head = await resolve_review_head_sha("tid", {"head_sha": "confighead"})
|
||||||
assert head == "confighead"
|
assert head == "confighead"
|
||||||
|
|
||||||
|
|
@ -357,7 +357,7 @@ async def test_resolve_review_head_sha_falls_back_to_config_when_metadata_empty(
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_resolve_review_head_sha_falls_back_without_thread_id() -> None:
|
async def test_resolve_review_head_sha_falls_back_without_thread_id() -> None:
|
||||||
fake_client = AsyncMock()
|
fake_client = AsyncMock()
|
||||||
with patch("agent.reviewer_findings.get_client", return_value=fake_client):
|
with patch("agent.review.findings.get_client", return_value=fake_client):
|
||||||
head = await resolve_review_head_sha("", {"head_sha": "confighead"})
|
head = await resolve_review_head_sha("", {"head_sha": "confighead"})
|
||||||
assert head == "confighead"
|
assert head == "confighead"
|
||||||
fake_client.threads.get.assert_not_called()
|
fake_client.threads.get.assert_not_called()
|
||||||
|
|
@ -368,7 +368,7 @@ async def test_replace_findings_raises_domain_error_when_thread_missing() -> Non
|
||||||
import httpx
|
import httpx
|
||||||
from langgraph_sdk.errors import NotFoundError
|
from langgraph_sdk.errors import NotFoundError
|
||||||
|
|
||||||
from agent.reviewer_findings import ReviewerThreadMissingError
|
from agent.review.findings import ReviewerThreadMissingError
|
||||||
|
|
||||||
not_found = NotFoundError(
|
not_found = NotFoundError(
|
||||||
"thread tid not found",
|
"thread tid not found",
|
||||||
|
|
@ -378,7 +378,7 @@ async def test_replace_findings_raises_domain_error_when_thread_missing() -> Non
|
||||||
fake_client = AsyncMock()
|
fake_client = AsyncMock()
|
||||||
fake_client.threads.update.side_effect = not_found
|
fake_client.threads.update.side_effect = not_found
|
||||||
|
|
||||||
with patch("agent.reviewer_findings.get_client", return_value=fake_client):
|
with patch("agent.review.findings.get_client", return_value=fake_client):
|
||||||
with pytest.raises(ReviewerThreadMissingError) as excinfo:
|
with pytest.raises(ReviewerThreadMissingError) as excinfo:
|
||||||
await replace_findings("tid", [_f(id="f_a")])
|
await replace_findings("tid", [_f(id="f_a")])
|
||||||
|
|
||||||
|
|
@ -402,30 +402,30 @@ async def test_get_thread_metadata_raises_domain_error_when_thread_missing() ->
|
||||||
"""A missing thread must surface as ReviewerThreadMissingError, not be
|
"""A missing thread must surface as ReviewerThreadMissingError, not be
|
||||||
swallowed into ``{}`` — that produced misleading tool results like
|
swallowed into ``{}`` — that produced misleading tool results like
|
||||||
"No finding found" instead of the do-not-retry contract."""
|
"No finding found" instead of the do-not-retry contract."""
|
||||||
from agent.reviewer_findings import ReviewerThreadMissingError, get_thread_metadata
|
from agent.review.findings import ReviewerThreadMissingError, get_thread_metadata
|
||||||
|
|
||||||
fake_client = AsyncMock()
|
fake_client = AsyncMock()
|
||||||
fake_client.threads.get.side_effect = _not_found()
|
fake_client.threads.get.side_effect = _not_found()
|
||||||
|
|
||||||
with patch("agent.reviewer_findings.get_client", return_value=fake_client):
|
with patch("agent.review.findings.get_client", return_value=fake_client):
|
||||||
with pytest.raises(ReviewerThreadMissingError):
|
with pytest.raises(ReviewerThreadMissingError):
|
||||||
await get_thread_metadata("tid")
|
await get_thread_metadata("tid")
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_get_thread_metadata_still_degrades_on_other_failures() -> None:
|
async def test_get_thread_metadata_still_degrades_on_other_failures() -> None:
|
||||||
from agent.reviewer_findings import get_thread_metadata
|
from agent.review.findings import get_thread_metadata
|
||||||
|
|
||||||
fake_client = AsyncMock()
|
fake_client = AsyncMock()
|
||||||
fake_client.threads.get.side_effect = RuntimeError("transient")
|
fake_client.threads.get.side_effect = RuntimeError("transient")
|
||||||
|
|
||||||
with patch("agent.reviewer_findings.get_client", return_value=fake_client):
|
with patch("agent.review.findings.get_client", return_value=fake_client):
|
||||||
assert await get_thread_metadata("tid") == {}
|
assert await get_thread_metadata("tid") == {}
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_set_reviewer_thread_metadata_raises_domain_error_when_thread_missing() -> None:
|
async def test_set_reviewer_thread_metadata_raises_domain_error_when_thread_missing() -> None:
|
||||||
from agent.reviewer_findings import (
|
from agent.review.findings import (
|
||||||
ReviewerThreadMissingError,
|
ReviewerThreadMissingError,
|
||||||
set_reviewer_thread_metadata,
|
set_reviewer_thread_metadata,
|
||||||
)
|
)
|
||||||
|
|
@ -433,6 +433,6 @@ async def test_set_reviewer_thread_metadata_raises_domain_error_when_thread_miss
|
||||||
fake_client = AsyncMock()
|
fake_client = AsyncMock()
|
||||||
fake_client.threads.update.side_effect = _not_found("PATCH")
|
fake_client.threads.update.side_effect = _not_found("PATCH")
|
||||||
|
|
||||||
with patch("agent.reviewer_findings.get_client", return_value=fake_client):
|
with patch("agent.review.findings.get_client", return_value=fake_client):
|
||||||
with pytest.raises(ReviewerThreadMissingError):
|
with pytest.raises(ReviewerThreadMissingError):
|
||||||
await set_reviewer_thread_metadata("tid", last_reviewed_sha="sha")
|
await set_reviewer_thread_metadata("tid", last_reviewed_sha="sha")
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ from unittest.mock import AsyncMock, MagicMock, patch
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from agent.reviewer_groups import (
|
from agent.review.groups import (
|
||||||
_build_prompt,
|
_build_prompt,
|
||||||
_DiffGroupingResult,
|
_DiffGroupingResult,
|
||||||
_DiffGroupModel,
|
_DiffGroupModel,
|
||||||
|
|
@ -124,13 +124,13 @@ async def test_maybe_generate_skips_when_signature_unchanged() -> None:
|
||||||
}
|
}
|
||||||
with (
|
with (
|
||||||
patch(
|
patch(
|
||||||
"agent.reviewer_groups.get_thread_metadata",
|
"agent.review.groups.get_thread_metadata",
|
||||||
new_callable=AsyncMock,
|
new_callable=AsyncMock,
|
||||||
return_value=existing,
|
return_value=existing,
|
||||||
),
|
),
|
||||||
patch("agent.reviewer_groups.generate_diff_groups", new_callable=AsyncMock) as gen,
|
patch("agent.review.groups.generate_diff_groups", new_callable=AsyncMock) as gen,
|
||||||
patch(
|
patch(
|
||||||
"agent.reviewer_groups.set_reviewer_thread_metadata",
|
"agent.review.groups.set_reviewer_thread_metadata",
|
||||||
new_callable=AsyncMock,
|
new_callable=AsyncMock,
|
||||||
) as set_meta,
|
) as set_meta,
|
||||||
):
|
):
|
||||||
|
|
@ -146,17 +146,17 @@ async def test_maybe_generate_stores_when_changed() -> None:
|
||||||
groups = [{"title": "t", "summary": "s", "files": ["foo.py"]}]
|
groups = [{"title": "t", "summary": "s", "files": ["foo.py"]}]
|
||||||
with (
|
with (
|
||||||
patch(
|
patch(
|
||||||
"agent.reviewer_groups.get_thread_metadata",
|
"agent.review.groups.get_thread_metadata",
|
||||||
new_callable=AsyncMock,
|
new_callable=AsyncMock,
|
||||||
return_value={},
|
return_value={},
|
||||||
),
|
),
|
||||||
patch(
|
patch(
|
||||||
"agent.reviewer_groups.generate_diff_groups",
|
"agent.review.groups.generate_diff_groups",
|
||||||
new_callable=AsyncMock,
|
new_callable=AsyncMock,
|
||||||
return_value=groups,
|
return_value=groups,
|
||||||
) as gen,
|
) as gen,
|
||||||
patch(
|
patch(
|
||||||
"agent.reviewer_groups.set_reviewer_thread_metadata",
|
"agent.review.groups.set_reviewer_thread_metadata",
|
||||||
new_callable=AsyncMock,
|
new_callable=AsyncMock,
|
||||||
) as set_meta,
|
) as set_meta,
|
||||||
):
|
):
|
||||||
|
|
|
||||||
|
|
@ -8,8 +8,8 @@ from unittest.mock import AsyncMock, MagicMock, patch
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from agent.reviewer_findings import Finding, new_finding
|
from agent.review.findings import Finding, new_finding
|
||||||
from agent.reviewer_publish import (
|
from agent.review.publish import (
|
||||||
clear_review_started_comment,
|
clear_review_started_comment,
|
||||||
fetch_pr_review_threads,
|
fetch_pr_review_threads,
|
||||||
open_swe_review_exists,
|
open_swe_review_exists,
|
||||||
|
|
@ -278,10 +278,10 @@ async def test_post_review_started_comment_posts_and_persists_id() -> None:
|
||||||
post = AsyncMock(return_value=4242)
|
post = AsyncMock(return_value=4242)
|
||||||
set_meta = AsyncMock()
|
set_meta = AsyncMock()
|
||||||
with (
|
with (
|
||||||
patch("agent.reviewer_publish.get_thread_metadata", AsyncMock(return_value={})),
|
patch("agent.review.publish.get_thread_metadata", AsyncMock(return_value={})),
|
||||||
patch("agent.reviewer_publish.post_status_comment", post),
|
patch("agent.review.publish.post_status_comment", post),
|
||||||
patch("agent.reviewer_publish.delete_status_comment", AsyncMock()) as delete,
|
patch("agent.review.publish.delete_status_comment", AsyncMock()) as delete,
|
||||||
patch("agent.reviewer_publish.set_reviewer_thread_metadata", set_meta),
|
patch("agent.review.publish.set_reviewer_thread_metadata", set_meta),
|
||||||
):
|
):
|
||||||
cid = await post_review_started_comment(
|
cid = await post_review_started_comment(
|
||||||
thread_id="tid", owner="o", repo="r", pr_number=7, token="t"
|
thread_id="tid", owner="o", repo="r", pr_number=7, token="t"
|
||||||
|
|
@ -298,12 +298,12 @@ async def test_post_review_started_comment_deletes_lingering_before_reposting()
|
||||||
delete = AsyncMock(return_value=True)
|
delete = AsyncMock(return_value=True)
|
||||||
with (
|
with (
|
||||||
patch(
|
patch(
|
||||||
"agent.reviewer_publish.get_thread_metadata",
|
"agent.review.publish.get_thread_metadata",
|
||||||
AsyncMock(return_value={"status_comment_id": 99}),
|
AsyncMock(return_value={"status_comment_id": 99}),
|
||||||
),
|
),
|
||||||
patch("agent.reviewer_publish.post_status_comment", post),
|
patch("agent.review.publish.post_status_comment", post),
|
||||||
patch("agent.reviewer_publish.delete_status_comment", delete),
|
patch("agent.review.publish.delete_status_comment", delete),
|
||||||
patch("agent.reviewer_publish.set_reviewer_thread_metadata", AsyncMock()),
|
patch("agent.review.publish.set_reviewer_thread_metadata", AsyncMock()),
|
||||||
):
|
):
|
||||||
cid = await post_review_started_comment(
|
cid = await post_review_started_comment(
|
||||||
thread_id="tid", owner="o", repo="r", pr_number=7, token="t"
|
thread_id="tid", owner="o", repo="r", pr_number=7, token="t"
|
||||||
|
|
@ -319,11 +319,11 @@ async def test_clear_review_started_comment_deletes_and_clears_metadata() -> Non
|
||||||
set_meta = AsyncMock()
|
set_meta = AsyncMock()
|
||||||
with (
|
with (
|
||||||
patch(
|
patch(
|
||||||
"agent.reviewer_publish.get_thread_metadata",
|
"agent.review.publish.get_thread_metadata",
|
||||||
AsyncMock(return_value={"status_comment_id": 99}),
|
AsyncMock(return_value={"status_comment_id": 99}),
|
||||||
),
|
),
|
||||||
patch("agent.reviewer_publish.delete_status_comment", delete),
|
patch("agent.review.publish.delete_status_comment", delete),
|
||||||
patch("agent.reviewer_publish.set_reviewer_thread_metadata", set_meta),
|
patch("agent.review.publish.set_reviewer_thread_metadata", set_meta),
|
||||||
):
|
):
|
||||||
await clear_review_started_comment(thread_id="tid", owner="o", repo="r", token="t")
|
await clear_review_started_comment(thread_id="tid", owner="o", repo="r", token="t")
|
||||||
delete.assert_awaited_once()
|
delete.assert_awaited_once()
|
||||||
|
|
@ -336,9 +336,9 @@ async def test_clear_review_started_comment_noop_without_tracked_id() -> None:
|
||||||
delete = AsyncMock()
|
delete = AsyncMock()
|
||||||
set_meta = AsyncMock()
|
set_meta = AsyncMock()
|
||||||
with (
|
with (
|
||||||
patch("agent.reviewer_publish.get_thread_metadata", AsyncMock(return_value={})),
|
patch("agent.review.publish.get_thread_metadata", AsyncMock(return_value={})),
|
||||||
patch("agent.reviewer_publish.delete_status_comment", delete),
|
patch("agent.review.publish.delete_status_comment", delete),
|
||||||
patch("agent.reviewer_publish.set_reviewer_thread_metadata", set_meta),
|
patch("agent.review.publish.set_reviewer_thread_metadata", set_meta),
|
||||||
):
|
):
|
||||||
await clear_review_started_comment(thread_id="tid", owner="o", repo="r", token="t")
|
await clear_review_started_comment(thread_id="tid", owner="o", repo="r", token="t")
|
||||||
delete.assert_not_called()
|
delete.assert_not_called()
|
||||||
|
|
@ -1145,8 +1145,8 @@ async def test_re_review_backfills_existing_marker_and_skips_duplicate_post() ->
|
||||||
"agent.tools.publish_review.fetch_pr_review_threads", AsyncMock(return_value=[thread])
|
"agent.tools.publish_review.fetch_pr_review_threads", AsyncMock(return_value=[thread])
|
||||||
),
|
),
|
||||||
patch("agent.tools.publish_review.list_findings_async", AsyncMock(return_value=findings)),
|
patch("agent.tools.publish_review.list_findings_async", AsyncMock(return_value=findings)),
|
||||||
patch("agent.reviewer_reconcile.list_findings", AsyncMock(return_value=findings)),
|
patch("agent.review.reconcile.list_findings", AsyncMock(return_value=findings)),
|
||||||
patch("agent.reviewer_reconcile.replace_findings", AsyncMock()),
|
patch("agent.review.reconcile.replace_findings", AsyncMock()),
|
||||||
patch("agent.tools.publish_review.post_pull_request_review", post_review),
|
patch("agent.tools.publish_review.post_pull_request_review", post_review),
|
||||||
patch(
|
patch(
|
||||||
"agent.tools.publish_review._resolve_threads_for_resolved_findings",
|
"agent.tools.publish_review._resolve_threads_for_resolved_findings",
|
||||||
|
|
@ -1221,8 +1221,8 @@ async def test_re_review_backfills_and_resolves_duplicate_existing_threads() ->
|
||||||
"agent.tools.publish_review.fetch_pr_review_threads", AsyncMock(return_value=threads)
|
"agent.tools.publish_review.fetch_pr_review_threads", AsyncMock(return_value=threads)
|
||||||
),
|
),
|
||||||
patch("agent.tools.publish_review.list_findings_async", AsyncMock(return_value=findings)),
|
patch("agent.tools.publish_review.list_findings_async", AsyncMock(return_value=findings)),
|
||||||
patch("agent.reviewer_reconcile.list_findings", AsyncMock(return_value=findings)),
|
patch("agent.review.reconcile.list_findings", AsyncMock(return_value=findings)),
|
||||||
patch("agent.reviewer_reconcile.replace_findings", AsyncMock()),
|
patch("agent.review.reconcile.replace_findings", AsyncMock()),
|
||||||
patch("agent.tools.publish_review.post_pull_request_review", AsyncMock()),
|
patch("agent.tools.publish_review.post_pull_request_review", AsyncMock()),
|
||||||
patch("agent.tools.publish_review.resolve_review_thread", resolve_thread),
|
patch("agent.tools.publish_review.resolve_review_thread", resolve_thread),
|
||||||
patch(
|
patch(
|
||||||
|
|
@ -1284,8 +1284,8 @@ async def test_publish_review_backfills_from_threads_when_review_comments_are_em
|
||||||
patch("agent.tools.publish_review.get_thread_id_from_runtime", return_value="tid"),
|
patch("agent.tools.publish_review.get_thread_id_from_runtime", return_value="tid"),
|
||||||
patch("agent.tools.publish_review.fetch_pr_review_threads", fetch_threads),
|
patch("agent.tools.publish_review.fetch_pr_review_threads", fetch_threads),
|
||||||
patch("agent.tools.publish_review.list_findings_async", AsyncMock(return_value=findings)),
|
patch("agent.tools.publish_review.list_findings_async", AsyncMock(return_value=findings)),
|
||||||
patch("agent.reviewer_reconcile.list_findings", AsyncMock(return_value=findings)),
|
patch("agent.review.reconcile.list_findings", AsyncMock(return_value=findings)),
|
||||||
patch("agent.reviewer_reconcile.replace_findings", AsyncMock()),
|
patch("agent.review.reconcile.replace_findings", AsyncMock()),
|
||||||
patch(
|
patch(
|
||||||
"agent.tools.publish_review.post_pull_request_review",
|
"agent.tools.publish_review.post_pull_request_review",
|
||||||
AsyncMock(return_value={"id": 999}),
|
AsyncMock(return_value={"id": 999}),
|
||||||
|
|
@ -2242,7 +2242,7 @@ async def test_publish_review_fetches_pr_diff_when_diff_line_set_missing() -> No
|
||||||
async def test_publish_review_tool_returns_structured_error_when_thread_missing() -> None:
|
async def test_publish_review_tool_returns_structured_error_when_thread_missing() -> None:
|
||||||
"""A missing reviewer thread surfaces as a do-not-retry tool result instead
|
"""A missing reviewer thread surfaces as a do-not-retry tool result instead
|
||||||
of an exception the middleware swallows into an empty tool message."""
|
of an exception the middleware swallows into an empty tool message."""
|
||||||
from agent.reviewer_findings import ReviewerThreadMissingError
|
from agent.review.findings import ReviewerThreadMissingError
|
||||||
from agent.tools.publish_review import publish_review
|
from agent.tools.publish_review import publish_review
|
||||||
|
|
||||||
publish_async = AsyncMock(
|
publish_async = AsyncMock(
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ from unittest.mock import AsyncMock, patch
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from agent.reviewer_reconcile import reconcile_findings_with_review_threads
|
from agent.review.reconcile import reconcile_findings_with_review_threads
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
|
|
@ -20,8 +20,8 @@ async def test_reconcile_marks_resolved_github_thread_resolved() -> None:
|
||||||
replace = AsyncMock()
|
replace = AsyncMock()
|
||||||
|
|
||||||
with (
|
with (
|
||||||
patch("agent.reviewer_reconcile.list_findings", AsyncMock(return_value=findings)),
|
patch("agent.review.reconcile.list_findings", AsyncMock(return_value=findings)),
|
||||||
patch("agent.reviewer_reconcile.replace_findings", replace),
|
patch("agent.review.reconcile.replace_findings", replace),
|
||||||
):
|
):
|
||||||
result = await reconcile_findings_with_review_threads(
|
result = await reconcile_findings_with_review_threads(
|
||||||
"tid",
|
"tid",
|
||||||
|
|
@ -53,8 +53,8 @@ async def test_reconcile_backfills_comment_and_thread_ids_from_bot_marker() -> N
|
||||||
replace = AsyncMock()
|
replace = AsyncMock()
|
||||||
|
|
||||||
with (
|
with (
|
||||||
patch("agent.reviewer_reconcile.list_findings", AsyncMock(return_value=findings)),
|
patch("agent.review.reconcile.list_findings", AsyncMock(return_value=findings)),
|
||||||
patch("agent.reviewer_reconcile.replace_findings", replace),
|
patch("agent.review.reconcile.replace_findings", replace),
|
||||||
):
|
):
|
||||||
result = await reconcile_findings_with_review_threads(
|
result = await reconcile_findings_with_review_threads(
|
||||||
"tid",
|
"tid",
|
||||||
|
|
@ -98,8 +98,8 @@ async def test_reconcile_backfills_marker_from_graphql_app_login() -> None:
|
||||||
replace = AsyncMock()
|
replace = AsyncMock()
|
||||||
|
|
||||||
with (
|
with (
|
||||||
patch("agent.reviewer_reconcile.list_findings", AsyncMock(return_value=findings)),
|
patch("agent.review.reconcile.list_findings", AsyncMock(return_value=findings)),
|
||||||
patch("agent.reviewer_reconcile.replace_findings", replace),
|
patch("agent.review.reconcile.replace_findings", replace),
|
||||||
):
|
):
|
||||||
result = await reconcile_findings_with_review_threads(
|
result = await reconcile_findings_with_review_threads(
|
||||||
"tid",
|
"tid",
|
||||||
|
|
@ -146,8 +146,8 @@ async def test_reconcile_duplicate_markers_require_all_threads_terminal() -> Non
|
||||||
)
|
)
|
||||||
|
|
||||||
with (
|
with (
|
||||||
patch("agent.reviewer_reconcile.list_findings", AsyncMock(return_value=findings)),
|
patch("agent.review.reconcile.list_findings", AsyncMock(return_value=findings)),
|
||||||
patch("agent.reviewer_reconcile.replace_findings", replace),
|
patch("agent.review.reconcile.replace_findings", replace),
|
||||||
):
|
):
|
||||||
result = await reconcile_findings_with_review_threads(
|
result = await reconcile_findings_with_review_threads(
|
||||||
"tid",
|
"tid",
|
||||||
|
|
@ -191,8 +191,8 @@ async def test_reconcile_duplicate_markers_stay_open_when_some_threads_only_outd
|
||||||
)
|
)
|
||||||
|
|
||||||
with (
|
with (
|
||||||
patch("agent.reviewer_reconcile.list_findings", AsyncMock(return_value=findings)),
|
patch("agent.review.reconcile.list_findings", AsyncMock(return_value=findings)),
|
||||||
patch("agent.reviewer_reconcile.replace_findings", replace),
|
patch("agent.review.reconcile.replace_findings", replace),
|
||||||
):
|
):
|
||||||
result = await reconcile_findings_with_review_threads(
|
result = await reconcile_findings_with_review_threads(
|
||||||
"tid",
|
"tid",
|
||||||
|
|
@ -232,8 +232,8 @@ async def test_reconcile_ignores_spoofed_non_bot_marker() -> None:
|
||||||
replace = AsyncMock()
|
replace = AsyncMock()
|
||||||
|
|
||||||
with (
|
with (
|
||||||
patch("agent.reviewer_reconcile.list_findings", AsyncMock(return_value=findings)),
|
patch("agent.review.reconcile.list_findings", AsyncMock(return_value=findings)),
|
||||||
patch("agent.reviewer_reconcile.replace_findings", replace),
|
patch("agent.review.reconcile.replace_findings", replace),
|
||||||
):
|
):
|
||||||
result = await reconcile_findings_with_review_threads(
|
result = await reconcile_findings_with_review_threads(
|
||||||
"tid",
|
"tid",
|
||||||
|
|
@ -268,8 +268,8 @@ async def test_reconcile_records_latest_human_reply_after_bot_comment() -> None:
|
||||||
replace = AsyncMock()
|
replace = AsyncMock()
|
||||||
|
|
||||||
with (
|
with (
|
||||||
patch("agent.reviewer_reconcile.list_findings", AsyncMock(return_value=findings)),
|
patch("agent.review.reconcile.list_findings", AsyncMock(return_value=findings)),
|
||||||
patch("agent.reviewer_reconcile.replace_findings", replace),
|
patch("agent.review.reconcile.replace_findings", replace),
|
||||||
):
|
):
|
||||||
result = await reconcile_findings_with_review_threads(
|
result = await reconcile_findings_with_review_threads(
|
||||||
"tid",
|
"tid",
|
||||||
|
|
|
||||||
|
|
@ -719,7 +719,7 @@ async def test_list_findings_returns_all_when_filter_omitted() -> None:
|
||||||
async def test_add_finding_returns_structured_error_when_thread_missing() -> None:
|
async def test_add_finding_returns_structured_error_when_thread_missing() -> None:
|
||||||
"""A missing reviewer thread must come back as a do-not-retry tool result,
|
"""A missing reviewer thread must come back as a do-not-retry tool result,
|
||||||
not a raised exception the agent retries against 10-30 times."""
|
not a raised exception the agent retries against 10-30 times."""
|
||||||
from agent.reviewer_findings import ReviewerThreadMissingError
|
from agent.review.findings import ReviewerThreadMissingError
|
||||||
|
|
||||||
async def fake_append(thread_id: str, finding: Any) -> Any:
|
async def fake_append(thread_id: str, finding: Any) -> Any:
|
||||||
raise ReviewerThreadMissingError(thread_id, RuntimeError("thread X not found"))
|
raise ReviewerThreadMissingError(thread_id, RuntimeError("thread X not found"))
|
||||||
|
|
@ -746,7 +746,7 @@ async def test_add_finding_returns_structured_error_when_thread_missing() -> Non
|
||||||
|
|
||||||
|
|
||||||
async def test_update_finding_returns_structured_error_when_thread_missing() -> None:
|
async def test_update_finding_returns_structured_error_when_thread_missing() -> None:
|
||||||
from agent.reviewer_findings import ReviewerThreadMissingError
|
from agent.review.findings import ReviewerThreadMissingError
|
||||||
|
|
||||||
async def fake_update(thread_id: str, finding_id: str, updates: Any) -> Any:
|
async def fake_update(thread_id: str, finding_id: str, updates: Any) -> Any:
|
||||||
raise ReviewerThreadMissingError(thread_id, RuntimeError("thread X not found"))
|
raise ReviewerThreadMissingError(thread_id, RuntimeError("thread X not found"))
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ from unittest.mock import AsyncMock, patch
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from agent.dashboard.team_credentials import LangSmithCredentials
|
from agent.dashboard.team_credentials import LangSmithCredentials
|
||||||
from agent.reviewer_trace_context import (
|
from agent.review.trace_context import (
|
||||||
PRTraceContext,
|
PRTraceContext,
|
||||||
format_pr_trace_context_prompt,
|
format_pr_trace_context_prompt,
|
||||||
prepare_pr_trace_context,
|
prepare_pr_trace_context,
|
||||||
|
|
@ -108,15 +108,15 @@ def _patches(client: _FakeLangSmithClient) -> Any:
|
||||||
creds = LangSmithCredentials(api_key="k", endpoint="https://api.smith.langchain.com")
|
creds = LangSmithCredentials(api_key="k", endpoint="https://api.smith.langchain.com")
|
||||||
return (
|
return (
|
||||||
patch(
|
patch(
|
||||||
"agent.reviewer_trace_context.get_team_review_tracing_project",
|
"agent.review.trace_context.get_team_review_tracing_project",
|
||||||
AsyncMock(return_value="pajuha"),
|
AsyncMock(return_value="pajuha"),
|
||||||
),
|
),
|
||||||
patch(
|
patch(
|
||||||
"agent.reviewer_trace_context.get_langsmith_credentials", AsyncMock(return_value=creds)
|
"agent.review.trace_context.get_langsmith_credentials", AsyncMock(return_value=creds)
|
||||||
),
|
),
|
||||||
patch("agent.reviewer_trace_context._client", return_value=client),
|
patch("agent.review.trace_context._client", return_value=client),
|
||||||
patch(
|
patch(
|
||||||
"agent.reviewer_trace_context.get_langsmith_trace_url",
|
"agent.review.trace_context.get_langsmith_trace_url",
|
||||||
return_value="https://smith/t/thread-1",
|
return_value="https://smith/t/thread-1",
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue