Spaces:
Running
Running
| # main.py β AutoReview AI | |
| # LangChain-based autonomous GitHub PR reviewer | |
| import os | |
| import re | |
| from dotenv import load_dotenv | |
| from langchain_groq import ChatGroq | |
| from langchain.agents import AgentExecutor, create_tool_calling_agent | |
| from langchain_core.prompts import ChatPromptTemplate | |
| from langchain_core.messages import HumanMessage, SystemMessage | |
| from tools import fetch_pr_diff, analyze_diff, post_inline_comment, post_overall_review | |
| load_dotenv() | |
| # ββ LLM ββββββββββββββββββββββββββββββββββ | |
| llm = ChatGroq(model="openai/gpt-oss-120b", temperature=0) | |
| # ββ TOOLS βββββββββββββββββββββββββββββββββ | |
| tools = [fetch_pr_diff, analyze_diff, post_inline_comment, post_overall_review] | |
| # ββ PROMPT ββββββββββββββββββββββββββββββββ | |
| prompt = ChatPromptTemplate.from_messages([ | |
| ("system", """You are AutoReview AI β an expert autonomous GitHub PR reviewer. | |
| Your job when given a PR URL: | |
| 1. Use fetch_pr_diff to get the PR details and code changes | |
| 2. Use analyze_diff for each changed file to find bugs, security issues, style problems | |
| 3. Use post_inline_comment to post specific comments on problematic lines | |
| 4. Use post_overall_review to post a final APPROVE or REQUEST_CHANGES verdict | |
| Be thorough but concise. Focus on real issues that matter. | |
| Always complete all 4 steps β don't stop after fetching. | |
| If GitHub token is not available, still analyze and return the review as text. | |
| """), | |
| ("human", "{input}"), | |
| ("placeholder", "{agent_scratchpad}") | |
| ]) | |
| # ββ AGENT βββββββββββββββββββββββββββββββββ | |
| agent = create_tool_calling_agent(llm, tools, prompt) | |
| agent_executor = AgentExecutor( | |
| agent=agent, | |
| tools=tools, | |
| verbose=True, | |
| max_iterations=10, | |
| handle_parsing_errors=True | |
| ) | |
| # βββββββββββββββββββββββββββββββββββββββββ | |
| # MAIN REVIEW FUNCTION | |
| # βββββββββββββββββββββββββββββββββββββββββ | |
| def review_pr(pr_url: str) -> dict: | |
| """ | |
| Main function to review a GitHub PR. | |
| Returns structured review result. | |
| """ | |
| print(f"\n{'='*60}") | |
| print(f"π AutoReview AI starting review...") | |
| print(f"π PR: {pr_url}") | |
| print("="*60) | |
| try: | |
| result = agent_executor.invoke({ | |
| "input": f""" | |
| Please review this GitHub Pull Request: {pr_url} | |
| Steps to follow: | |
| 1. Fetch the PR diff using fetch_pr_diff | |
| 2. Analyze each changed file using analyze_diff | |
| 3. Post inline comments for specific issues using post_inline_comment | |
| 4. Post overall verdict using post_overall_review | |
| Provide a thorough code review focusing on: | |
| - Bugs and logic errors | |
| - Security vulnerabilities | |
| - Code style and best practices | |
| - Performance issues | |
| """ | |
| }) | |
| output = result.get("output", "") | |
| print(f"\nβ Review complete!") | |
| print(f"\nOutput:\n{output}") | |
| return { | |
| "success": True, | |
| "pr_url": pr_url, | |
| "review": output, | |
| "error": None | |
| } | |
| except Exception as e: | |
| error_msg = str(e) | |
| print(f"\nβ Error: {error_msg}") | |
| return { | |
| "success": False, | |
| "pr_url": pr_url, | |
| "review": None, | |
| "error": error_msg | |
| } | |
| # βββββββββββββββββββββββββββββββββββββββββ | |
| # SIMPLE REVIEW (no GitHub token needed) | |
| # For demo purposes | |
| # βββββββββββββββββββββββββββββββββββββββββ | |
| def review_pr_simple(pr_url: str) -> dict: | |
| """ | |
| Reviews PR without posting comments to GitHub. | |
| Returns full analysis as text β good for demo. | |
| """ | |
| print(f"\nπ Fetching PR data...") | |
| # Step 1: Fetch diff | |
| diff_result = fetch_pr_diff.invoke(pr_url) | |
| if diff_result.startswith("ERROR"): | |
| return {"success": False, "error": diff_result, "review": None} | |
| print("β PR fetched!") | |
| print(f"\nπ€ Analyzing code...") | |
| # Step 2: Analyze | |
| analysis = analyze_diff.invoke(diff_result[:4000]) | |
| print("β Analysis complete!") | |
| # Step 3: Generate final verdict | |
| verdict_response = llm.invoke([ | |
| SystemMessage("You are a senior engineer. Give a final PR review verdict."), | |
| HumanMessage(f""" | |
| Based on this PR analysis, write a complete review report: | |
| PR DATA: | |
| {diff_result[:2000]} | |
| ANALYSIS: | |
| {analysis} | |
| Write a professional review with: | |
| 1. Overall verdict (APPROVE / REQUEST CHANGES) | |
| 2. Key issues found | |
| 3. Positive aspects | |
| 4. Specific suggestions | |
| """) | |
| ]) | |
| return { | |
| "success": True, | |
| "pr_url": pr_url, | |
| "pr_data": diff_result, | |
| "analysis": analysis, | |
| "verdict": verdict_response.content, | |
| "error": None | |
| } | |
| # βββββββββββββββββββββββββββββββββββββββββ | |
| # RUN | |
| # βββββββββββββββββββββββββββββββββββββββββ | |
| if __name__ == "__main__": | |
| # Test with a real public PR | |
| test_pr = "https://github.com/langchain-ai/langchain/pull/1" | |
| print("Mode: Simple review (no GitHub token needed for demo)") | |
| result = review_pr_simple(test_pr) | |
| if result["success"]: | |
| print(f"\n{'='*60}") | |
| print("π PR DATA SUMMARY:") | |
| print(result["pr_data"][:500]) | |
| print(f"\nπ ANALYSIS:") | |
| print(result["analysis"]) | |
| print(f"\nβ FINAL VERDICT:") | |
| print(result["verdict"]) | |
| else: | |
| print(f"β Failed: {result['error']}") |