From 19de4634bc11d55066d2bfd3dfc266a267576bc0 Mon Sep 17 00:00:00 2001 From: Gahow Wang Date: Wed, 6 May 2026 16:13:24 +0800 Subject: [PATCH] =?UTF-8?q?agent:=20implement=20issue=20#3=20-=20Enhanceme?= =?UTF-8?q?nt:=20=E6=A3=80=E6=9F=A5=E7=8E=B0=E6=9C=89=E7=9A=84=E4=BB=A3?= =?UTF-8?q?=E7=A0=81=E6=A1=86=E6=9E=B6=EF=BC=8C=E7=A1=AE=E8=AE=A4=E6=98=AF?= =?UTF-8?q?=E5=90=A6=E9=9C=80=E8=A6=81=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/agent_gitea/gitea.py | 43 +++++++++++++++++++------------ src/agent_gitea/rendering.py | 10 ++++++- tests/test_gitea_service.py | 43 +++++++++++++++++++++++++++++++ tests/test_rendering_workspace.py | 12 +++++++++ 4 files changed, 90 insertions(+), 18 deletions(-) diff --git a/src/agent_gitea/gitea.py b/src/agent_gitea/gitea.py index 7e1ea03..ff73f76 100644 --- a/src/agent_gitea/gitea.py +++ b/src/agent_gitea/gitea.py @@ -85,25 +85,34 @@ class GiteaClient: return repositories def list_open_issues(self, owner: str, name: str) -> list[GiteaIssue]: - response = self.client.get( - f"/repos/{owner}/{name}/issues", - params={"state": "open", "type": "issues", "limit": 50}, - ) - response.raise_for_status() issues: list[GiteaIssue] = [] - for item in response.json(): - if item.get("pull_request"): - continue - issues.append( - GiteaIssue( - number=int(item["number"]), - title=item.get("title") or "", - body=item.get("body") or "", - labels=labels_from_gitea(item.get("labels")), - state=item.get("state") or "open", - html_url=item.get("html_url") or item.get("url") or "", - ) + page = 1 + limit = 50 + while True: + response = self.client.get( + f"/repos/{owner}/{name}/issues", + params={"state": "open", "type": "issues", "page": page, "limit": limit}, ) + response.raise_for_status() + payload = response.json() + if not payload: + break + for item in payload: + if item.get("pull_request"): + continue + issues.append( + GiteaIssue( + number=int(item["number"]), + title=item.get("title") or "", + body=item.get("body") or "", + labels=labels_from_gitea(item.get("labels")), + state=item.get("state") or "open", + html_url=item.get("html_url") or item.get("url") or "", + ) + ) + if len(payload) < limit: + break + page += 1 return issues def create_pull_request( diff --git a/src/agent_gitea/rendering.py b/src/agent_gitea/rendering.py index 50032e1..b1fb18d 100644 --- a/src/agent_gitea/rendering.py +++ b/src/agent_gitea/rendering.py @@ -86,11 +86,13 @@ def render_pr_body(issue: IssueRecord, implementation_report: str) -> str: def parse_review_report(raw: str) -> ReviewReport: - verdict_match = re.search(r"(?im)^\s*(?:##\s*)?Verdict\s*:?\s*`?([A-Z_]+)`?", raw) + verdict_match = re.search(r"(?im)^\s*(?:[-*]\s*)?(?:##\s*)?Verdict\s*:?\s*`?([A-Z_]+)`?", raw) verdict = verdict_match.group(1) if verdict_match else "NEEDS_HUMAN_DECISION" if verdict not in VALID_VERDICTS: verdict = "NEEDS_HUMAN_DECISION" suggested = extract_section(raw, "Suggested PR Comment").strip() + if not suggested: + suggested = extract_inline_field(raw, "Suggested PR Comment").strip() if not suggested: suggested = raw.strip() return ReviewReport(verdict=verdict, raw=raw, suggested_pr_comment=suggested) @@ -104,6 +106,12 @@ def extract_section(raw: str, title: str) -> str: return match.group("body") if match else "" +def extract_inline_field(raw: str, title: str) -> str: + pattern = re.compile(rf"(?im)^\s*(?:[-*]\s*)?{re.escape(title)}\s*:\s*(?P.+?)\s*$") + match = pattern.search(raw) + return match.group("body") if match else "" + + def render_human_review_summary(review: ReviewReport) -> str: return f"""## 代理评审摘要 diff --git a/tests/test_gitea_service.py b/tests/test_gitea_service.py index 7475921..633cad5 100644 --- a/tests/test_gitea_service.py +++ b/tests/test_gitea_service.py @@ -119,6 +119,49 @@ def test_list_open_issues_keeps_normal_issues_with_null_pull_request(): assert [issue.number for issue in issues] == [1] +def test_list_open_issues_reads_all_pages(): + seen_pages: list[int] = [] + + def handler(request: httpx.Request) -> httpx.Response: + assert request.url.path == "/api/v1/repos/acme/service/issues" + page = int(request.url.params["page"]) + limit = int(request.url.params["limit"]) + seen_pages.append(page) + if page == 1: + return httpx.Response( + 200, + json=[ + { + "number": number, + "title": f"Issue {number}", + "body": "", + "state": "open", + "labels": [], + } + for number in range(1, limit + 1) + ], + ) + if page == 2: + return httpx.Response( + 200, + json=[ + { + "number": 51, + "title": "Issue 51", + "body": "", + "state": "open", + "labels": [], + } + ], + ) + return httpx.Response(500) + + issues = make_client(handler).list_open_issues("acme", "service") + + assert [issue.number for issue in issues] == list(range(1, 52)) + assert seen_pages == [1, 2] + + class FakeWorkspaceManager: def __init__(self, root: Path, *, diff: bool = True): self.root = root diff --git a/tests/test_rendering_workspace.py b/tests/test_rendering_workspace.py index f610cbf..a297e5a 100644 --- a/tests/test_rendering_workspace.py +++ b/tests/test_rendering_workspace.py @@ -58,3 +58,15 @@ Please add tests. assert parsed.verdict == "REQUEST_CHANGES" assert parsed.suggested_pr_comment == "Please add tests." + + +def test_review_report_parsing_accepts_prompt_bullet_contract(): + report = """- Verdict: APPROVE +- Summary: Done +- Suggested PR Comment: Looks good. +""" + + parsed = parse_review_report(report) + + assert parsed.verdict == "APPROVE" + assert parsed.suggested_pr_comment == "Looks good."