2026-05-05 13:04:23 -07:00
|
|
|
"""Run the reviewer eval against the LangSmith dataset.
|
|
|
|
|
|
|
|
|
|
Usage:
|
2026-05-18 15:47:13 -07:00
|
|
|
uv run python -m evals.reviewer.run_eval
|
2026-05-05 13:04:23 -07:00
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
from __future__ import annotations
|
|
|
|
|
|
|
|
|
|
import argparse
|
feat: add reviewer graph + eval target wiring (#1241)
* feat: add reviewer graph + eval target wiring
- New `reviewer` graph (`agent/reviewer.py`) registered in langgraph.json
alongside the main `agent` graph. Reuses the same sandbox lifecycle,
GH proxy auth, and middleware primitives from `agent.server`, but with
a narrower tool set, a reviewer-specific system prompt, no
commit/push, and the `task` (subagent) tool stripped via
`_ToolExclusionMiddleware` so review stays in one context.
- New `github_comment` tool: agents call it once per issue with
`(file, line, body, severity)` and the eval scores those calls
against golden comments.
- `ensure_no_empty_msg` middleware (the no_op nudge) is intentionally
*not* on the reviewer's stack — that middleware exists to enforce the
main agent's "always finalize via Slack/Linear/PR" contract, which
the reviewer doesn't have. The main agent's behavior is unchanged.
- `evals/reviewer/target.py`: send PR info as a user message, extract
every `github_comment` tool call (multiple expected per review) into
the run output.
- `evals/reviewer/judge.py`: per-example evaluator now returns a list
of metrics under `{"results": [...]}` so LangSmith averages each
numeric key (f1/precision/recall/tp/fp/fn) across the experiment in
the UI. Dropped the broken `aggregate_pr` summary evaluator that
reached for an attribute that doesn't exist on `RunTree`.
- `evals/reviewer/run_eval.py`: `--limit` now slices the dataset via
`client.list_examples(limit=N)` since `aevaluate` doesn't accept
`max_examples`.
- Makefile: `dev` and `run` targets now use `uv run` so they work
without an activated venv.
* resolve comments
---------
Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
2026-05-06 10:15:58 -07:00
|
|
|
import logging
|
2026-05-18 15:47:13 -07:00
|
|
|
import os
|
|
|
|
|
import tomllib
|
feat: add reviewer graph + eval target wiring (#1241)
* feat: add reviewer graph + eval target wiring
- New `reviewer` graph (`agent/reviewer.py`) registered in langgraph.json
alongside the main `agent` graph. Reuses the same sandbox lifecycle,
GH proxy auth, and middleware primitives from `agent.server`, but with
a narrower tool set, a reviewer-specific system prompt, no
commit/push, and the `task` (subagent) tool stripped via
`_ToolExclusionMiddleware` so review stays in one context.
- New `github_comment` tool: agents call it once per issue with
`(file, line, body, severity)` and the eval scores those calls
against golden comments.
- `ensure_no_empty_msg` middleware (the no_op nudge) is intentionally
*not* on the reviewer's stack — that middleware exists to enforce the
main agent's "always finalize via Slack/Linear/PR" contract, which
the reviewer doesn't have. The main agent's behavior is unchanged.
- `evals/reviewer/target.py`: send PR info as a user message, extract
every `github_comment` tool call (multiple expected per review) into
the run output.
- `evals/reviewer/judge.py`: per-example evaluator now returns a list
of metrics under `{"results": [...]}` so LangSmith averages each
numeric key (f1/precision/recall/tp/fp/fn) across the experiment in
the UI. Dropped the broken `aggregate_pr` summary evaluator that
reached for an attribute that doesn't exist on `RunTree`.
- `evals/reviewer/run_eval.py`: `--limit` now slices the dataset via
`client.list_examples(limit=N)` since `aevaluate` doesn't accept
`max_examples`.
- Makefile: `dev` and `run` targets now use `uv run` so they work
without an activated venv.
* resolve comments
---------
Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
2026-05-06 10:15:58 -07:00
|
|
|
from collections.abc import Iterable
|
2026-05-18 15:47:13 -07:00
|
|
|
from pathlib import Path
|
|
|
|
|
from typing import Any, Literal, TypedDict
|
2026-05-05 13:04:23 -07:00
|
|
|
|
|
|
|
|
from dotenv import load_dotenv
|
feat: add reviewer graph + eval target wiring (#1241)
* feat: add reviewer graph + eval target wiring
- New `reviewer` graph (`agent/reviewer.py`) registered in langgraph.json
alongside the main `agent` graph. Reuses the same sandbox lifecycle,
GH proxy auth, and middleware primitives from `agent.server`, but with
a narrower tool set, a reviewer-specific system prompt, no
commit/push, and the `task` (subagent) tool stripped via
`_ToolExclusionMiddleware` so review stays in one context.
- New `github_comment` tool: agents call it once per issue with
`(file, line, body, severity)` and the eval scores those calls
against golden comments.
- `ensure_no_empty_msg` middleware (the no_op nudge) is intentionally
*not* on the reviewer's stack — that middleware exists to enforce the
main agent's "always finalize via Slack/Linear/PR" contract, which
the reviewer doesn't have. The main agent's behavior is unchanged.
- `evals/reviewer/target.py`: send PR info as a user message, extract
every `github_comment` tool call (multiple expected per review) into
the run output.
- `evals/reviewer/judge.py`: per-example evaluator now returns a list
of metrics under `{"results": [...]}` so LangSmith averages each
numeric key (f1/precision/recall/tp/fp/fn) across the experiment in
the UI. Dropped the broken `aggregate_pr` summary evaluator that
reached for an attribute that doesn't exist on `RunTree`.
- `evals/reviewer/run_eval.py`: `--limit` now slices the dataset via
`client.list_examples(limit=N)` since `aevaluate` doesn't accept
`max_examples`.
- Makefile: `dev` and `run` targets now use `uv run` so they work
without an activated venv.
* resolve comments
---------
Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
2026-05-06 10:15:58 -07:00
|
|
|
from langgraph_sdk import get_client
|
|
|
|
|
from langsmith import Client, aevaluate
|
|
|
|
|
from langsmith.schemas import Example
|
2026-05-05 13:04:23 -07:00
|
|
|
|
|
|
|
|
from evals.reviewer.judge import aggregate_pr, judge_match
|
2026-05-18 15:47:13 -07:00
|
|
|
from evals.reviewer.target import drain_thread_ids, get_langgraph_url, review_pr
|
2026-05-05 13:04:23 -07:00
|
|
|
|
|
|
|
|
load_dotenv()
|
|
|
|
|
|
feat: add reviewer graph + eval target wiring (#1241)
* feat: add reviewer graph + eval target wiring
- New `reviewer` graph (`agent/reviewer.py`) registered in langgraph.json
alongside the main `agent` graph. Reuses the same sandbox lifecycle,
GH proxy auth, and middleware primitives from `agent.server`, but with
a narrower tool set, a reviewer-specific system prompt, no
commit/push, and the `task` (subagent) tool stripped via
`_ToolExclusionMiddleware` so review stays in one context.
- New `github_comment` tool: agents call it once per issue with
`(file, line, body, severity)` and the eval scores those calls
against golden comments.
- `ensure_no_empty_msg` middleware (the no_op nudge) is intentionally
*not* on the reviewer's stack — that middleware exists to enforce the
main agent's "always finalize via Slack/Linear/PR" contract, which
the reviewer doesn't have. The main agent's behavior is unchanged.
- `evals/reviewer/target.py`: send PR info as a user message, extract
every `github_comment` tool call (multiple expected per review) into
the run output.
- `evals/reviewer/judge.py`: per-example evaluator now returns a list
of metrics under `{"results": [...]}` so LangSmith averages each
numeric key (f1/precision/recall/tp/fp/fn) across the experiment in
the UI. Dropped the broken `aggregate_pr` summary evaluator that
reached for an attribute that doesn't exist on `RunTree`.
- `evals/reviewer/run_eval.py`: `--limit` now slices the dataset via
`client.list_examples(limit=N)` since `aevaluate` doesn't accept
`max_examples`.
- Makefile: `dev` and `run` targets now use `uv run` so they work
without an activated venv.
* resolve comments
---------
Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
2026-05-06 10:15:58 -07:00
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
2026-05-18 15:47:13 -07:00
|
|
|
CONFIG_PATH = Path(__file__).with_name("config.toml")
|
|
|
|
|
ScoreMode = Literal["all_findings", "surfaced_findings"]
|
|
|
|
|
Severity = Literal["informational", "low", "medium", "high", "critical"]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ReviewerEvalConfig(TypedDict, total=False):
|
|
|
|
|
dataset_name: str
|
|
|
|
|
experiment_prefix: str
|
|
|
|
|
max_concurrency: int
|
|
|
|
|
langgraph_url: str
|
|
|
|
|
assistant_id: str
|
|
|
|
|
model_id: str
|
|
|
|
|
reasoning_effort: str
|
|
|
|
|
score_mode: ScoreMode
|
|
|
|
|
severity_threshold: Severity
|
|
|
|
|
cap: int
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _load_config() -> ReviewerEvalConfig:
|
|
|
|
|
if not CONFIG_PATH.exists():
|
|
|
|
|
return {}
|
|
|
|
|
with CONFIG_PATH.open("rb") as f:
|
|
|
|
|
raw = tomllib.load(f)
|
|
|
|
|
return _coerce_config(raw)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _coerce_config(raw: dict[str, Any]) -> ReviewerEvalConfig:
|
|
|
|
|
config: ReviewerEvalConfig = {}
|
|
|
|
|
dataset_name = raw.get("dataset_name")
|
|
|
|
|
if isinstance(dataset_name, str) and dataset_name:
|
|
|
|
|
config["dataset_name"] = dataset_name
|
|
|
|
|
|
|
|
|
|
experiment_prefix = raw.get("experiment_prefix")
|
|
|
|
|
if isinstance(experiment_prefix, str) and experiment_prefix:
|
|
|
|
|
config["experiment_prefix"] = experiment_prefix
|
|
|
|
|
|
|
|
|
|
langgraph_url = raw.get("langgraph_url")
|
|
|
|
|
if isinstance(langgraph_url, str) and langgraph_url:
|
|
|
|
|
config["langgraph_url"] = langgraph_url
|
|
|
|
|
|
|
|
|
|
assistant_id = raw.get("assistant_id")
|
|
|
|
|
if isinstance(assistant_id, str) and assistant_id:
|
|
|
|
|
config["assistant_id"] = assistant_id
|
|
|
|
|
|
|
|
|
|
model_id = raw.get("model_id")
|
|
|
|
|
if isinstance(model_id, str) and model_id:
|
|
|
|
|
config["model_id"] = model_id
|
|
|
|
|
|
|
|
|
|
reasoning_effort = raw.get("reasoning_effort")
|
|
|
|
|
if isinstance(reasoning_effort, str) and reasoning_effort:
|
|
|
|
|
config["reasoning_effort"] = reasoning_effort
|
|
|
|
|
|
|
|
|
|
max_concurrency = raw.get("max_concurrency")
|
|
|
|
|
if isinstance(max_concurrency, int) and max_concurrency > 0:
|
|
|
|
|
config["max_concurrency"] = max_concurrency
|
|
|
|
|
|
|
|
|
|
score_mode = raw.get("score_mode")
|
|
|
|
|
if score_mode in {"all_findings", "surfaced_findings"}:
|
|
|
|
|
config["score_mode"] = score_mode
|
|
|
|
|
|
|
|
|
|
severity_threshold = raw.get("severity_threshold")
|
|
|
|
|
if severity_threshold in {"informational", "low", "medium", "high", "critical"}:
|
|
|
|
|
config["severity_threshold"] = severity_threshold
|
|
|
|
|
|
|
|
|
|
cap = raw.get("cap")
|
|
|
|
|
if isinstance(cap, int) and cap >= 0:
|
|
|
|
|
config["cap"] = cap
|
|
|
|
|
return config
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _apply_config_to_env(config: ReviewerEvalConfig) -> None:
|
|
|
|
|
env_mapping = {
|
|
|
|
|
"langgraph_url": "LANGGRAPH_URL",
|
|
|
|
|
"assistant_id": "REVIEWER_ASSISTANT_ID",
|
|
|
|
|
"model_id": "REVIEWER_EVAL_MODEL_ID",
|
|
|
|
|
"reasoning_effort": "REVIEWER_EVAL_REASONING_EFFORT",
|
|
|
|
|
"score_mode": "REVIEWER_EVAL_SCORE_MODE",
|
|
|
|
|
"severity_threshold": "REVIEWER_EVAL_SEVERITY_THRESHOLD",
|
|
|
|
|
"cap": "REVIEWER_EVAL_CAP",
|
|
|
|
|
}
|
|
|
|
|
for config_key, env_key in env_mapping.items():
|
|
|
|
|
value = config.get(config_key)
|
|
|
|
|
if value is not None:
|
|
|
|
|
os.environ[env_key] = str(value)
|
|
|
|
|
|
feat: add reviewer graph + eval target wiring (#1241)
* feat: add reviewer graph + eval target wiring
- New `reviewer` graph (`agent/reviewer.py`) registered in langgraph.json
alongside the main `agent` graph. Reuses the same sandbox lifecycle,
GH proxy auth, and middleware primitives from `agent.server`, but with
a narrower tool set, a reviewer-specific system prompt, no
commit/push, and the `task` (subagent) tool stripped via
`_ToolExclusionMiddleware` so review stays in one context.
- New `github_comment` tool: agents call it once per issue with
`(file, line, body, severity)` and the eval scores those calls
against golden comments.
- `ensure_no_empty_msg` middleware (the no_op nudge) is intentionally
*not* on the reviewer's stack — that middleware exists to enforce the
main agent's "always finalize via Slack/Linear/PR" contract, which
the reviewer doesn't have. The main agent's behavior is unchanged.
- `evals/reviewer/target.py`: send PR info as a user message, extract
every `github_comment` tool call (multiple expected per review) into
the run output.
- `evals/reviewer/judge.py`: per-example evaluator now returns a list
of metrics under `{"results": [...]}` so LangSmith averages each
numeric key (f1/precision/recall/tp/fp/fn) across the experiment in
the UI. Dropped the broken `aggregate_pr` summary evaluator that
reached for an attribute that doesn't exist on `RunTree`.
- `evals/reviewer/run_eval.py`: `--limit` now slices the dataset via
`client.list_examples(limit=N)` since `aevaluate` doesn't accept
`max_examples`.
- Makefile: `dev` and `run` targets now use `uv run` so they work
without an activated venv.
* resolve comments
---------
Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
2026-05-06 10:15:58 -07:00
|
|
|
|
|
|
|
|
async def _cleanup_threads(thread_ids: Iterable[str]) -> None:
|
|
|
|
|
"""Delete LangGraph threads created during the eval.
|
|
|
|
|
|
|
|
|
|
Underlying sandboxes are reclaimed by the provider's TTL — this only
|
|
|
|
|
drops the LangGraph checkpoint/metadata records.
|
|
|
|
|
"""
|
2026-05-18 15:47:13 -07:00
|
|
|
sdk = get_client(url=get_langgraph_url())
|
feat: add reviewer graph + eval target wiring (#1241)
* feat: add reviewer graph + eval target wiring
- New `reviewer` graph (`agent/reviewer.py`) registered in langgraph.json
alongside the main `agent` graph. Reuses the same sandbox lifecycle,
GH proxy auth, and middleware primitives from `agent.server`, but with
a narrower tool set, a reviewer-specific system prompt, no
commit/push, and the `task` (subagent) tool stripped via
`_ToolExclusionMiddleware` so review stays in one context.
- New `github_comment` tool: agents call it once per issue with
`(file, line, body, severity)` and the eval scores those calls
against golden comments.
- `ensure_no_empty_msg` middleware (the no_op nudge) is intentionally
*not* on the reviewer's stack — that middleware exists to enforce the
main agent's "always finalize via Slack/Linear/PR" contract, which
the reviewer doesn't have. The main agent's behavior is unchanged.
- `evals/reviewer/target.py`: send PR info as a user message, extract
every `github_comment` tool call (multiple expected per review) into
the run output.
- `evals/reviewer/judge.py`: per-example evaluator now returns a list
of metrics under `{"results": [...]}` so LangSmith averages each
numeric key (f1/precision/recall/tp/fp/fn) across the experiment in
the UI. Dropped the broken `aggregate_pr` summary evaluator that
reached for an attribute that doesn't exist on `RunTree`.
- `evals/reviewer/run_eval.py`: `--limit` now slices the dataset via
`client.list_examples(limit=N)` since `aevaluate` doesn't accept
`max_examples`.
- Makefile: `dev` and `run` targets now use `uv run` so they work
without an activated venv.
* resolve comments
---------
Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
2026-05-06 10:15:58 -07:00
|
|
|
for tid in thread_ids:
|
|
|
|
|
try:
|
|
|
|
|
await sdk.threads.delete(tid)
|
|
|
|
|
except Exception as exc:
|
|
|
|
|
logger.warning("Failed to delete thread %s: %s", tid, exc)
|
|
|
|
|
|
2026-05-05 13:04:23 -07:00
|
|
|
|
|
|
|
|
async def main() -> None:
|
2026-05-18 15:47:13 -07:00
|
|
|
config = _load_config()
|
|
|
|
|
_apply_config_to_env(config)
|
|
|
|
|
|
2026-05-05 13:04:23 -07:00
|
|
|
ap = argparse.ArgumentParser()
|
|
|
|
|
ap.add_argument("--limit", type=int, default=None, help="Run only the first N examples.")
|
feat: add reviewer graph + eval target wiring (#1241)
* feat: add reviewer graph + eval target wiring
- New `reviewer` graph (`agent/reviewer.py`) registered in langgraph.json
alongside the main `agent` graph. Reuses the same sandbox lifecycle,
GH proxy auth, and middleware primitives from `agent.server`, but with
a narrower tool set, a reviewer-specific system prompt, no
commit/push, and the `task` (subagent) tool stripped via
`_ToolExclusionMiddleware` so review stays in one context.
- New `github_comment` tool: agents call it once per issue with
`(file, line, body, severity)` and the eval scores those calls
against golden comments.
- `ensure_no_empty_msg` middleware (the no_op nudge) is intentionally
*not* on the reviewer's stack — that middleware exists to enforce the
main agent's "always finalize via Slack/Linear/PR" contract, which
the reviewer doesn't have. The main agent's behavior is unchanged.
- `evals/reviewer/target.py`: send PR info as a user message, extract
every `github_comment` tool call (multiple expected per review) into
the run output.
- `evals/reviewer/judge.py`: per-example evaluator now returns a list
of metrics under `{"results": [...]}` so LangSmith averages each
numeric key (f1/precision/recall/tp/fp/fn) across the experiment in
the UI. Dropped the broken `aggregate_pr` summary evaluator that
reached for an attribute that doesn't exist on `RunTree`.
- `evals/reviewer/run_eval.py`: `--limit` now slices the dataset via
`client.list_examples(limit=N)` since `aevaluate` doesn't accept
`max_examples`.
- Makefile: `dev` and `run` targets now use `uv run` so they work
without an activated venv.
* resolve comments
---------
Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
2026-05-06 10:15:58 -07:00
|
|
|
ap.add_argument(
|
|
|
|
|
"--no-cleanup",
|
|
|
|
|
action="store_true",
|
|
|
|
|
help="Skip deleting LangGraph threads after the experiment finishes.",
|
|
|
|
|
)
|
2026-05-05 13:04:23 -07:00
|
|
|
args = ap.parse_args()
|
|
|
|
|
|
2026-05-18 15:47:13 -07:00
|
|
|
dataset_name = config.get("dataset_name", "openswe-reviewer-v1")
|
|
|
|
|
experiment_prefix = config.get("experiment_prefix", "openswe-reviewer-baseline")
|
|
|
|
|
max_concurrency = config.get("max_concurrency", 5)
|
|
|
|
|
|
feat: add reviewer graph + eval target wiring (#1241)
* feat: add reviewer graph + eval target wiring
- New `reviewer` graph (`agent/reviewer.py`) registered in langgraph.json
alongside the main `agent` graph. Reuses the same sandbox lifecycle,
GH proxy auth, and middleware primitives from `agent.server`, but with
a narrower tool set, a reviewer-specific system prompt, no
commit/push, and the `task` (subagent) tool stripped via
`_ToolExclusionMiddleware` so review stays in one context.
- New `github_comment` tool: agents call it once per issue with
`(file, line, body, severity)` and the eval scores those calls
against golden comments.
- `ensure_no_empty_msg` middleware (the no_op nudge) is intentionally
*not* on the reviewer's stack — that middleware exists to enforce the
main agent's "always finalize via Slack/Linear/PR" contract, which
the reviewer doesn't have. The main agent's behavior is unchanged.
- `evals/reviewer/target.py`: send PR info as a user message, extract
every `github_comment` tool call (multiple expected per review) into
the run output.
- `evals/reviewer/judge.py`: per-example evaluator now returns a list
of metrics under `{"results": [...]}` so LangSmith averages each
numeric key (f1/precision/recall/tp/fp/fn) across the experiment in
the UI. Dropped the broken `aggregate_pr` summary evaluator that
reached for an attribute that doesn't exist on `RunTree`.
- `evals/reviewer/run_eval.py`: `--limit` now slices the dataset via
`client.list_examples(limit=N)` since `aevaluate` doesn't accept
`max_examples`.
- Makefile: `dev` and `run` targets now use `uv run` so they work
without an activated venv.
* resolve comments
---------
Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
2026-05-06 10:15:58 -07:00
|
|
|
data: str | list[Example]
|
|
|
|
|
if args.limit:
|
|
|
|
|
client = Client()
|
2026-05-18 15:47:13 -07:00
|
|
|
data = list(client.list_examples(dataset_name=dataset_name, limit=args.limit))
|
feat: add reviewer graph + eval target wiring (#1241)
* feat: add reviewer graph + eval target wiring
- New `reviewer` graph (`agent/reviewer.py`) registered in langgraph.json
alongside the main `agent` graph. Reuses the same sandbox lifecycle,
GH proxy auth, and middleware primitives from `agent.server`, but with
a narrower tool set, a reviewer-specific system prompt, no
commit/push, and the `task` (subagent) tool stripped via
`_ToolExclusionMiddleware` so review stays in one context.
- New `github_comment` tool: agents call it once per issue with
`(file, line, body, severity)` and the eval scores those calls
against golden comments.
- `ensure_no_empty_msg` middleware (the no_op nudge) is intentionally
*not* on the reviewer's stack — that middleware exists to enforce the
main agent's "always finalize via Slack/Linear/PR" contract, which
the reviewer doesn't have. The main agent's behavior is unchanged.
- `evals/reviewer/target.py`: send PR info as a user message, extract
every `github_comment` tool call (multiple expected per review) into
the run output.
- `evals/reviewer/judge.py`: per-example evaluator now returns a list
of metrics under `{"results": [...]}` so LangSmith averages each
numeric key (f1/precision/recall/tp/fp/fn) across the experiment in
the UI. Dropped the broken `aggregate_pr` summary evaluator that
reached for an attribute that doesn't exist on `RunTree`.
- `evals/reviewer/run_eval.py`: `--limit` now slices the dataset via
`client.list_examples(limit=N)` since `aevaluate` doesn't accept
`max_examples`.
- Makefile: `dev` and `run` targets now use `uv run` so they work
without an activated venv.
* resolve comments
---------
Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
2026-05-06 10:15:58 -07:00
|
|
|
else:
|
2026-05-18 15:47:13 -07:00
|
|
|
data = dataset_name
|
feat: add reviewer graph + eval target wiring (#1241)
* feat: add reviewer graph + eval target wiring
- New `reviewer` graph (`agent/reviewer.py`) registered in langgraph.json
alongside the main `agent` graph. Reuses the same sandbox lifecycle,
GH proxy auth, and middleware primitives from `agent.server`, but with
a narrower tool set, a reviewer-specific system prompt, no
commit/push, and the `task` (subagent) tool stripped via
`_ToolExclusionMiddleware` so review stays in one context.
- New `github_comment` tool: agents call it once per issue with
`(file, line, body, severity)` and the eval scores those calls
against golden comments.
- `ensure_no_empty_msg` middleware (the no_op nudge) is intentionally
*not* on the reviewer's stack — that middleware exists to enforce the
main agent's "always finalize via Slack/Linear/PR" contract, which
the reviewer doesn't have. The main agent's behavior is unchanged.
- `evals/reviewer/target.py`: send PR info as a user message, extract
every `github_comment` tool call (multiple expected per review) into
the run output.
- `evals/reviewer/judge.py`: per-example evaluator now returns a list
of metrics under `{"results": [...]}` so LangSmith averages each
numeric key (f1/precision/recall/tp/fp/fn) across the experiment in
the UI. Dropped the broken `aggregate_pr` summary evaluator that
reached for an attribute that doesn't exist on `RunTree`.
- `evals/reviewer/run_eval.py`: `--limit` now slices the dataset via
`client.list_examples(limit=N)` since `aevaluate` doesn't accept
`max_examples`.
- Makefile: `dev` and `run` targets now use `uv run` so they work
without an activated venv.
* resolve comments
---------
Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
2026-05-06 10:15:58 -07:00
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
await aevaluate(
|
|
|
|
|
review_pr,
|
|
|
|
|
data=data,
|
|
|
|
|
evaluators=[judge_match],
|
|
|
|
|
summary_evaluators=[aggregate_pr],
|
2026-05-18 15:47:13 -07:00
|
|
|
experiment_prefix=experiment_prefix,
|
|
|
|
|
max_concurrency=max_concurrency,
|
feat: add reviewer graph + eval target wiring (#1241)
* feat: add reviewer graph + eval target wiring
- New `reviewer` graph (`agent/reviewer.py`) registered in langgraph.json
alongside the main `agent` graph. Reuses the same sandbox lifecycle,
GH proxy auth, and middleware primitives from `agent.server`, but with
a narrower tool set, a reviewer-specific system prompt, no
commit/push, and the `task` (subagent) tool stripped via
`_ToolExclusionMiddleware` so review stays in one context.
- New `github_comment` tool: agents call it once per issue with
`(file, line, body, severity)` and the eval scores those calls
against golden comments.
- `ensure_no_empty_msg` middleware (the no_op nudge) is intentionally
*not* on the reviewer's stack — that middleware exists to enforce the
main agent's "always finalize via Slack/Linear/PR" contract, which
the reviewer doesn't have. The main agent's behavior is unchanged.
- `evals/reviewer/target.py`: send PR info as a user message, extract
every `github_comment` tool call (multiple expected per review) into
the run output.
- `evals/reviewer/judge.py`: per-example evaluator now returns a list
of metrics under `{"results": [...]}` so LangSmith averages each
numeric key (f1/precision/recall/tp/fp/fn) across the experiment in
the UI. Dropped the broken `aggregate_pr` summary evaluator that
reached for an attribute that doesn't exist on `RunTree`.
- `evals/reviewer/run_eval.py`: `--limit` now slices the dataset via
`client.list_examples(limit=N)` since `aevaluate` doesn't accept
`max_examples`.
- Makefile: `dev` and `run` targets now use `uv run` so they work
without an activated venv.
* resolve comments
---------
Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
2026-05-06 10:15:58 -07:00
|
|
|
num_repetitions=1,
|
|
|
|
|
)
|
|
|
|
|
finally:
|
|
|
|
|
if not args.no_cleanup:
|
|
|
|
|
thread_ids = drain_thread_ids()
|
|
|
|
|
if thread_ids:
|
|
|
|
|
logger.info("Cleaning up %d LangGraph threads", len(thread_ids))
|
|
|
|
|
await _cleanup_threads(thread_ids)
|
2026-05-05 13:04:23 -07:00
|
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
|
import asyncio
|
|
|
|
|
|
|
|
|
|
asyncio.run(main())
|