From ab6f6faa32c26ef8a76f10fa98c3cbfc2e679d79 Mon Sep 17 00:00:00 2001 From: OG T Date: Fri, 10 Apr 2026 11:09:11 +0800 Subject: [PATCH] =?UTF-8?q?feat(phase32):=20=E5=AF=A6=E4=BD=9C=20review=5F?= =?UTF-8?q?push=20+=20gitea=5Fwebhook=20=E6=94=B9=E7=94=A8=E6=9C=AC?= =?UTF-8?q?=E5=9C=B0=20Ollama=20=E5=AF=A9=E6=9F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - local_code_review_service: 新增 review_push() 方法 使用 qwen2.5-coder:7b 審查 push event(非 PR) - gitea_webhook_service: _call_openclaw_push_review 改用本地推理 OpenClaw 無 push-review 端點(404) → 改用 LocalCodeReviewService Co-Authored-By: Claude Sonnet 4.6 --- .../api/src/services/gitea_webhook_service.py | 36 +++++++++------- .../src/services/local_code_review_service.py | 43 +++++++++++++++++++ 2 files changed, 64 insertions(+), 15 deletions(-) diff --git a/apps/api/src/services/gitea_webhook_service.py b/apps/api/src/services/gitea_webhook_service.py index 43281a97..c5238ce5 100644 --- a/apps/api/src/services/gitea_webhook_service.py +++ b/apps/api/src/services/gitea_webhook_service.py @@ -244,25 +244,31 @@ class GiteaWebhookService: files_changed: dict, ) -> CodeReviewResult | None: """ - 呼叫 OpenClaw 進行 Push 代碼審查 - - Phase 22 P0 修復: 使用 OpenClawHttpService (2026-03-31) + Phase 32 ADR-067: Ollama qwen2.5-coder:7b push 代碼審查 + 2026-04-10 Claude Sonnet 4.6: OpenClaw 無 push-review 端點 → 改用 Ollama 本地推理 """ try: - from src.services.openclaw_http_service import get_openclaw_http_service - service = get_openclaw_http_service() - data = await service.push_review( - repo_name=repo_name, - ref=ref, - commits=commits, - files_changed=files_changed, - prefer_local=True, - timeout=120.0, + from src.services.local_code_review_service import get_local_code_review_service + svc = get_local_code_review_service() + + branch = ref.split("/")[-1] + commit_msgs = "\n".join(f'- {c["sha"]}: {c["message"]}' for c in commits[:5]) + added = files_changed.get("added", []) + modified = files_changed.get("modified", []) + removed = files_changed.get("removed", []) + files_summary = ( + f"Added: {', '.join(added[:5]) or '無'}\n" + f"Modified: {', '.join(modified[:5]) or '無'}\n" + f"Removed: {', '.join(removed[:5]) or '無'}" ) - if data: - return CodeReviewResult(**data) - return None + result = await svc.review_push( + repo_name=repo_name, + branch=branch, + commit_summary=commit_msgs, + files_summary=files_summary, + ) + return result except Exception as e: logger.exception("openclaw_push_review_error", error=str(e)) diff --git a/apps/api/src/services/local_code_review_service.py b/apps/api/src/services/local_code_review_service.py index 46c97d4b..4c668dac 100644 --- a/apps/api/src/services/local_code_review_service.py +++ b/apps/api/src/services/local_code_review_service.py @@ -180,6 +180,49 @@ class LocalCodeReviewService: except Exception as e: logger.warning("pr_review_db_save_failed", error=str(e)) + async def review_push( + self, + repo_name: str, + branch: str, + commit_summary: str, + files_summary: str, + ) -> dict[str, Any] | None: + """ + 審查 push event(非 PR),用 qwen2.5-coder:7b 快速掃描 + 2026-04-10 Claude Sonnet 4.6: Phase 32 ADR-067 — gitea_webhook_service 呼叫 + """ + async with _SEMAPHORE: + prompt = ( + f"你是資深程式審查員,請用繁體中文快速審查以下 Git Push。\n" + f"Repository: {repo_name} Branch: {branch}\n\n" + f"=== Commits ===\n{commit_summary}\n\n" + f"=== Changed Files ===\n{files_summary}\n\n" + "請簡要說明:1) 有無明顯安全風險 2) 有無破壞性變更 3) 整體品質評估\n" + "格式:每個問題以「⚠️」開頭,如無問題說「✅ Push 品質正常」\n" + "5 行以內,繁體中文。" + ) + try: + http = await self._get_http() + resp = await http.post( + f"{settings.OLLAMA_URL}/api/generate", + json={ + "model": _MODEL_OLLAMA, + "prompt": prompt, + "stream": False, + "options": {"num_predict": 512, "temperature": 0.1}, + }, + ) + if resp.status_code == 200: + text = resp.json().get("response", "").strip() + issues = text.count("⚠️") + logger.info("push_review_ollama_done", repo=repo_name, branch=branch, issues=issues) + return {"review_text": text, "issues_count": issues, "model": _MODEL_OLLAMA, "provider": "ollama"} + except httpx.TimeoutException: + logger.warning("push_review_ollama_timeout", repo=repo_name) + except Exception as e: + logger.error("push_review_ollama_failed", repo=repo_name, error=str(e)) + return None + async def close(self) -> None: if self._http and not self._http.is_closed: await self._http.aclose()