Repository: preslaff/ai-code-reviewer
Pull request #3: Add eval test for new caption code in summary
Author: login: preslaff
Created: 2025-03-28T15:33:31Z | Merged: 2025-03-29T17:03:17Z

testing new caption code in summary


Diff:
diff --git a/insecure.py b/insecure.py
new file mode 100644
index 0000000..2592490
--- /dev/null
+++ b/insecure.py
@@ -0,0 +1 @@
+eval('2+2')
diff --git a/langgraph_agent/agent.py b/langgraph_agent/agent.py
index 6e91647..58f8b81 100644
--- a/langgraph_agent/agent.py
+++ b/langgraph_agent/agent.py
@@ -1,86 +1,114 @@
 import os
+import re
 import argparse
-from typing import TypedDict, Optional
-from dotenv import load_dotenv
+import sqlite3
+from typing import TypedDict
 from github import Github
+from langchain_core.runnables import RunnableLambda
+from langgraph.graph import StateGraph
 from langchain_openai import ChatOpenAI
-from langgraph.graph import StateGraph, END
-from .prompt import prompt
-from .review_utils import parse_feedback_to_comments, store_review_db
+from langgraph_agent.prompt import SYSTEM_PROMPT, HUMAN_PROMPT
+from langchain_core.messages import HumanMessage, SystemMessage
+from langgraph_agent.review_utils import parse_feedback_to_comments, store_review_db
+
 
 class ReviewState(TypedDict):
-    file: Optional[object]
-    review: str
+    file: object
+    review_text: str
+
+def extract_diff_snippet(diff, target_line, context=3):
+    lines = diff.splitlines()
+    snippet = []
+    found = False
+    for i, line in enumerate(lines):
+        if re.search(rf'^[\+\- ].*', line):
+            lineno = i + 1
+            if abs(lineno - target_line) <= context:
+                start = max(0, i - context)
+                end = min(len(lines), i + context + 1)
+                snippet = lines[start:end]
+                found = True
+                break
+    if found:
+        return "```diff\n" + "\n".join(snippet) + "\n```"
+    return ""
 
 def main():
-    parser = argparse.ArgumentParser(description="Run the AI code reviewer on a GitHub PR.")
-    parser.add_argument("--pr", type=int, help="PR number (overrides .env PR_NUMBER)", default=None)
-    parser.add_argument("--dry-run", action="store_true", help="Print comments instead of posting them")
-    parser.add_argument("--save-db", action="store_false", help="Do not save review data to the database")
-    parser.add_argument("--model", type=str, default="gpt-4", help="Language model to use (default: gpt-4)")
-    parser.add_argument("--repo", type=str, help="GitHub repository override in format owner/repo")
+    parser = argparse.ArgumentParser()
+    parser.add_argument("--dry-run", action="store_true")
+    parser.add_argument("--save-db", default=True)
+    parser.add_argument("--model", default="gpt-4")
     args = parser.parse_args()
 
-    load_dotenv()
-
-    openai_key = os.getenv("OPENAI_API_KEY")
-    github_token = os.getenv("GITHUB_TOKEN")
-    repo_name = args.repo or os.getenv("GITHUB_REPOSITORY")
-    pr_number = args.pr or os.getenv("PR_NUMBER")
-
-    if not all([openai_key, github_token, repo_name, pr_number]):
-        print("❌ Missing environment variables. Please check your .env file or use --pr.")
-        return
+    repo_name = os.getenv("GITHUB_REPOSITORY")
+    pr_number = int(os.getenv("PR_NUMBER"))
+    token = os.getenv("GITHUB_TOKEN")
 
-    pr_number = int(pr_number)
-    print(f"🔍 Running AI Code Review for PR #{pr_number} in {repo_name} using model {args.model}...")
-
-    g = Github(github_token)
+    g = Github(token)
     repo = g.get_repo(repo_name)
     pr = repo.get_pull(pr_number)
-    files = pr.get_files()
 
-    llm = ChatOpenAI(model=args.model, api_key=openai_key)
+    llm = ChatOpenAI(model=args.model)
 
-    def review_file(state):
+    def review_code(state):
         file = state["file"]
-        response = llm.invoke(prompt.format(filename=file.filename, patch=file.patch)).content
-        return {"review": response, "file": file}
+        patch = file.patch or ""
+        filename = file.filename
+        prompt = [
+            SystemMessage(content=SYSTEM_PROMPT),
+            HumanMessage(content=HUMAN_PROMPT.format(file_name=filename, patch=patch)),
+        ]
+        response = llm.invoke(prompt)
+        return {"file": file, "review_text": response.content}
 
     def post_inline_comments(state):
         file = state["file"]
-        review = state["review"]
+        review = state["review_text"]
         comments = parse_feedback_to_comments(review, file)
 
         if args.dry_run:
             print(f"\n📄 Review for {file.filename}:")
             for c in comments:
                 print(f"  Line {c['line']}: {c['body']}")
+                snippet = extract_diff_snippet(file.patch or "", c['line'])
+                if snippet:
+                    print(snippet)
         else:
+            commit = repo.get_commit(pr.head.sha)
+            for c in comments:
+                snippet = extract_diff_snippet(file.patch or "", c['line'])
+                comment_body = f"{c['body']}\n\n{snippet}" if snippet else c['body']
+                pr.create_review_comment(
+                    commit=commit,
+                    body=comment_body,
+                    path=file.filename,
+                    line=c['line'],
+                    side="RIGHT"
+                )
+
             if args.save_db:
                 store_review_db(pr_number, file.filename, comments)
+
         return {}
 
-    graph = StateGraph(ReviewState)
-    graph.add_node("review_file", review_file)
-    graph.add_node("post_inline_comments", post_inline_comments)
-    graph.set_entry_point("review_file")
-    graph.add_edge("review_file", "post_inline_comments")
-    graph.add_edge("post_inline_comments", END)
+    builder = StateGraph(ReviewState)
+    builder.add_node("review", RunnableLambda(review_code))  # expects full state
+    builder.add_node("comment", RunnableLambda(post_inline_comments))
+    builder.add_edge("review", "comment")
+    builder.set_entry_point("review")
 
-    app = graph.compile()
-    all_summaries = []
+    app = builder.compile()
 
-    for file in files:
-        if file.patch:
-            result = app.invoke({"file": file})
-            review_text = result.get("review", "")
-            if review_text.strip():
-                all_summaries.append(f"<details><summary>📄 `{file.filename}`</summary>\n\n{review_text}\n\n</details>")
+    all_summaries = []
+    for file in pr.get_files():
+        result = app.invoke({"file": file})  # wraps state input
+        review_text = result['review_text']
+        all_summaries.append(f"<details><summary>📄 {file.filename}</summary>\n\n{review_text}\n\n</details>")
 
     if all_summaries and not args.dry_run:
         summary_text = "\n\n".join(all_summaries)
         formatted_summary = f"## 🤖 AI Review Summary for PR #{pr_number}\n\n{summary_text}"
         pr.create_issue_comment(formatted_summary)
 
-    print("✅ Review complete." + (" (dry run, no comments posted)" if args.dry_run else " Comments added to PR and summary posted."))
\ No newline at end of file
+        print("\n--- AI Review Summary ---\n")
+        print(formatted_summary)
diff --git a/langgraph_agent/prompt.py b/langgraph_agent/prompt.py
index 1ee8605..7e77ae2 100644
--- a/langgraph_agent/prompt.py
+++ b/langgraph_agent/prompt.py
@@ -1,28 +1,25 @@
-from langchain_core.prompts import ChatPromptTemplate
+# langgraph_agent/prompt.py
 
-prompt = ChatPromptTemplate.from_template(
-    """
-You are an expert AI code reviewer. For the given code diff, identify issues in the following categories:
+SYSTEM_PROMPT = (
+    "You are an expert AI code reviewer. For the given code diff, identify issues in the following categories:\n"
+    "\n"
+    "1. 🐛 Bugs\n"
+    "2. 🔐 Security vulnerabilities\n"
+    "3. 🚀 Performance issues\n"
+    "4. 📚 Readability and maintainability problems\n"
+    "\n"
+    "Provide actionable, concise feedback. Use Markdown formatting, include line numbers where possible."
+)
 
-1. 🐛 Bugs
-2. 🔐 Security vulnerabilities
-3. 🚀 Performance issues
-4. 📚 Readability and maintainability problems
-
-Provide actionable, concise feedback. Use Markdown formatting, include line numbers where possible.
-
-Code diff for file `{filename}`:
-```diff
-{patch}
-```
-
-Output format:
-```
-### Review
-- **Bug**: ...
-- **Security**: ...
-- **Performance**: ...
-- **Readability**: ...
-```
-"""
-)
\ No newline at end of file
+HUMAN_PROMPT = (
+    "Here is the code diff for `{file_name}`:\n\n"
+    "```diff\n"
+    "{patch}\n"
+    "```\n\n"
+    "Please review it and provide feedback in the following format:\n\n"
+    "### Review\n"
+    "- **Bug**: ...\n"
+    "- **Security**: ...\n"
+    "- **Performance**: ...\n"
+    "- **Readability**: ..."
+)


Reviews:
user: login: github-actions[bot]
state: COMMENTED
author_association: NONE
submitted_at: 2025-03-28T21:19:10Z
user: login: github-actions[bot]
state: COMMENTED
author_association: NONE
submitted_at: 2025-03-28T21:19:12Z
user: login: github-actions[bot]
state: COMMENTED
author_association: NONE
submitted_at: 2025-03-28T21:19:22Z
user: login: github-actions[bot]
state: COMMENTED
author_association: NONE
submitted_at: 2025-03-29T14:00:36Z
user: login: preslaff
state: COMMENTED
author_association: OWNER
submitted_at: 2025-03-29T19:13:08Z
user: login: preslaff
state: COMMENTED
author_association: OWNER
submitted_at: 2025-03-29T19:13:27Z

Review comments:
diff_hunk: @@ -1,86 +1,126 @@
 import os
+import re
 import argparse
-from typing import TypedDict, Optional
-from dotenv import load_dotenv
+import sqlite3
+from typing import TypedDict
 from github import Github
+from langchain_core.runnables import RunnableLambda
+from langgraph.graph import StateGraph
 from langchain_openai import ChatOpenAI
-from langgraph.graph import StateGraph, END
-from .prompt import prompt
-from .review_utils import parse_feedback_to_comments, store_review_db
+from langgraph_agent.prompt import SYSTEM_PROMPT, HUMAN_PROMPT
+from langchain_core.messages import HumanMessage, SystemMessage
+from langgraph_agent.review_utils import parse_feedback_to_comments, store_review_db
+
 
 class ReviewState(TypedDict):
-    file: Optional[object]
-    review: str
+    file: object
+    review_text: str
+
+def extract_diff_snippet(diff, target_line, context=3):
+    lines = diff.splitlines()
+    snippet = []
+    found = False
+    for i, line in enumerate(lines):
+        if re.search(rf'^[\+\- ].*', line):
+            lineno = i + 1
+            if abs(lineno - target_line) <= context:
+                start = max(0, i - context)
+                end = min(len(lines), i + context + 1)
+                snippet = lines[start:end]
+                found = True
+                break
+    if found:
+        return "```diff\n" + "\n".join(snippet) + "\n```"
+    return ""
+
+def extract_snippets_from_review(review_text: str, patch: str) -> str:
+    snippets = []
+    line_refs = re.findall(r"[Ll]ine (\d+)", review_text)
+    for ref in set(map(int, line_refs)):
+        snippet = extract_diff_snippet(patch, ref)
+        if snippet:
+            snippets.append(f"Line {ref}:")
+            snippets.append(snippet)
+    return "\n\n".join(snippets)
 
 def main():
-    parser = argparse.ArgumentParser(description="Run the AI code reviewer on a GitHub PR.")
-    parser.add_argument("--pr", type=int, help="PR number (overrides .env PR_NUMBER)", default=None)
-    parser.add_argument("--dry-run", action="store_true", help="Print comments instead of posting them")
-    parser.add_argument("--save-db", action="store_false", help="Do not save review data to the database")
-    parser.add_argument("--model", type=str, default="gpt-4", help="Language model to use (default: gpt-4)")
-    parser.add_argument("--repo", type=str, help="GitHub repository override in format owner/repo")
+    parser = argparse.ArgumentParser()
+    parser.add_argument("--dry-run", action="store_true")
+    parser.add_argument("--save-db", default=True)
+    parser.add_argument("--model", default="gpt-4")
     args = parser.parse_args()
 
-    load_dotenv()
-
-    openai_key = os.getenv("OPENAI_API_KEY")
-    github_token = os.getenv("GITHUB_TOKEN")
-    repo_name = args.repo or os.getenv("GITHUB_REPOSITORY")
-    pr_number = args.pr or os.getenv("PR_NUMBER")
-
-    if not all([openai_key, github_token, repo_name, pr_number]):
-        print("❌ Missing environment variables. Please check your .env file or use --pr.")
-        return
+    repo_name = os.getenv("GITHUB_REPOSITORY")
+    pr_number = int(os.getenv("PR_NUMBER"))
+    token = os.getenv("GITHUB_TOKEN")
 
-    pr_number = int(pr_number)
-    print(f"🔍 Running AI Code Review for PR #{pr_number} in {repo_name} using model {args.model}...")
-
-    g = Github(github_token)
+    g = Github(token)
     repo = g.get_repo(repo_name)
     pr = repo.get_pull(pr_number)
-    files = pr.get_files()
 
path: langgraph_agent/agent.py
user: login: github-actions[bot]
body: Undefined variables 'file' in `review_code(state)`. This doesn't seem to be defined anywhere in this function, so the code may throw an error at runtime. If `file` is supposed to be taken from `state`, this should be done explicitly (as in `file = state['file']`).

```diff
 def main():
-    parser = argparse.ArgumentParser(description="Run the AI code reviewer on a GitHub PR.")
-    parser.add_argument("--pr", type=int, help="PR number (overrides .env PR_NUMBER)", default=None)
-    parser.add_argument("--dry-run", action="store_true", help="Print comments instead of posting them")
-    parser.add_argument("--save-db", action="store_false", help="Do not save review data to the database")
-    parser.add_argument("--model", type=str, default="gpt-4", help="Language model to use (default: gpt-4)")
-    parser.add_argument("--repo", type=str, help="GitHub repository override in format owner/repo")
```
created_at: 2025-03-28T21:19:10Z
author_association: NONE
diff_hunk: @@ -1,86 +1,126 @@
 import os
+import re
 import argparse
-from typing import TypedDict, Optional
-from dotenv import load_dotenv
+import sqlite3
+from typing import TypedDict
 from github import Github
+from langchain_core.runnables import RunnableLambda
+from langgraph.graph import StateGraph
 from langchain_openai import ChatOpenAI
-from langgraph.graph import StateGraph, END
-from .prompt import prompt
-from .review_utils import parse_feedback_to_comments, store_review_db
+from langgraph_agent.prompt import SYSTEM_PROMPT, HUMAN_PROMPT
+from langchain_core.messages import HumanMessage, SystemMessage
+from langgraph_agent.review_utils import parse_feedback_to_comments, store_review_db
+
 
 class ReviewState(TypedDict):
-    file: Optional[object]
-    review: str
+    file: object
+    review_text: str
+
+def extract_diff_snippet(diff, target_line, context=3):
+    lines = diff.splitlines()
+    snippet = []
+    found = False
+    for i, line in enumerate(lines):
+        if re.search(rf'^[\+\- ].*', line):
+            lineno = i + 1
+            if abs(lineno - target_line) <= context:
+                start = max(0, i - context)
+                end = min(len(lines), i + context + 1)
+                snippet = lines[start:end]
+                found = True
+                break
+    if found:
+        return "```diff\n" + "\n".join(snippet) + "\n```"
+    return ""
+
+def extract_snippets_from_review(review_text: str, patch: str) -> str:
+    snippets = []
+    line_refs = re.findall(r"[Ll]ine (\d+)", review_text)
+    for ref in set(map(int, line_refs)):
+        snippet = extract_diff_snippet(patch, ref)
+        if snippet:
+            snippets.append(f"Line {ref}:")
+            snippets.append(snippet)
+    return "\n\n".join(snippets)
 
 def main():
-    parser = argparse.ArgumentParser(description="Run the AI code reviewer on a GitHub PR.")
-    parser.add_argument("--pr", type=int, help="PR number (overrides .env PR_NUMBER)", default=None)
-    parser.add_argument("--dry-run", action="store_true", help="Print comments instead of posting them")
-    parser.add_argument("--save-db", action="store_false", help="Do not save review data to the database")
-    parser.add_argument("--model", type=str, default="gpt-4", help="Language model to use (default: gpt-4)")
-    parser.add_argument("--repo", type=str, help="GitHub repository override in format owner/repo")
+    parser = argparse.ArgumentParser()
+    parser.add_argument("--dry-run", action="store_true")
+    parser.add_argument("--save-db", default=True)
+    parser.add_argument("--model", default="gpt-4")
     args = parser.parse_args()
 
-    load_dotenv()
-
-    openai_key = os.getenv("OPENAI_API_KEY")
-    github_token = os.getenv("GITHUB_TOKEN")
-    repo_name = args.repo or os.getenv("GITHUB_REPOSITORY")
-    pr_number = args.pr or os.getenv("PR_NUMBER")
-
-    if not all([openai_key, github_token, repo_name, pr_number]):
-        print("❌ Missing environment variables. Please check your .env file or use --pr.")
-        return
+    repo_name = os.getenv("GITHUB_REPOSITORY")
+    pr_number = int(os.getenv("PR_NUMBER"))
+    token = os.getenv("GITHUB_TOKEN")
 
-    pr_number = int(pr_number)
-    print(f"🔍 Running AI Code Review for PR #{pr_number} in {repo_name} using model {args.model}...")
-
-    g = Github(github_token)
+    g = Github(token)
     repo = g.get_repo(repo_name)
     pr = repo.get_pull(pr_number)
-    files = pr.get_files()
 
-    llm = ChatOpenAI(model=args.model, api_key=openai_key)
+    llm = ChatOpenAI(model=args.model)
 
-    def review_file(state):
+    def review_code(state):
         file = state["file"]
-        response = llm.invoke(prompt.format(filename=file.filename, patch=file.patch)).content
-        return {"review": response, "file": file}
+        patch = file.patch or ""
+        filename = file.filename
+        prompt = [
path: langgraph_agent/agent.py
user: login: github-actions[bot]
body: Signature of the `ChatOpenAI.invoke(prompt)` method usage was changed, but it seems that the `prompt` passed is not correctly configured as list of dictionaries with 'role' and 'content' keys, as it should be in the OpenAI API. Please refer to the OpenAI API documentation for correct usage.

```diff
+    parser = argparse.ArgumentParser()
+    parser.add_argument("--dry-run", action="store_true")
+    parser.add_argument("--save-db", default=True)
+    parser.add_argument("--model", default="gpt-4")
     args = parser.parse_args()
 
-    load_dotenv()
```
created_at: 2025-03-28T21:19:12Z
author_association: NONE
diff_hunk: @@ -1,28 +1,25 @@
-from langchain_core.prompts import ChatPromptTemplate
+# langgraph_agent/prompt.py
 
-prompt = ChatPromptTemplate.from_template(
-    """
-You are an expert AI code reviewer. For the given code diff, identify issues in the following categories:
+SYSTEM_PROMPT = (
path: langgraph_agent/prompt.py
user: login: github-actions[bot]
body: Consider adding a docstring at the beginning of the file to provide a brief summary of what this module does.

```diff
@@ -1,28 +1,25 @@
-from langchain_core.prompts import ChatPromptTemplate
+# langgraph_agent/prompt.py
 
-prompt = ChatPromptTemplate.from_template(
```
created_at: 2025-03-28T21:19:22Z
author_association: NONE
diff_hunk: @@ -1,86 +1,114 @@
 import os
+import re
 import argparse
-from typing import TypedDict, Optional
-from dotenv import load_dotenv
+import sqlite3
+from typing import TypedDict
 from github import Github
+from langchain_core.runnables import RunnableLambda
+from langgraph.graph import StateGraph
 from langchain_openai import ChatOpenAI
-from langgraph.graph import StateGraph, END
-from .prompt import prompt
-from .review_utils import parse_feedback_to_comments, store_review_db
+from langgraph_agent.prompt import SYSTEM_PROMPT, HUMAN_PROMPT
+from langchain_core.messages import HumanMessage, SystemMessage
+from langgraph_agent.review_utils import parse_feedback_to_comments, store_review_db
+
 
 class ReviewState(TypedDict):
-    file: Optional[object]
-    review: str
+    file: object
+    review_text: str
+
+def extract_diff_snippet(diff, target_line, context=3):
+    lines = diff.splitlines()
+    snippet = []
+    found = False
+    for i, line in enumerate(lines):
+        if re.search(rf'^[\+\- ].*', line):
+            lineno = i + 1
+            if abs(lineno - target_line) <= context:
+                start = max(0, i - context)
+                end = min(len(lines), i + context + 1)
+                snippet = lines[start:end]
+                found = True
+                break
+    if found:
+        return "```diff\n" + "\n".join(snippet) + "\n```"
+    return ""
 
path: langgraph_agent/agent.py
user: login: github-actions[bot]
body: The PR number is being converted to an integer using `int(os.getenv("PR_NUMBER"))`. There's no null checking here and `None` may cause `TypeError` if the environment variable "PR_NUMBER" is not set.

```diff
+    snippet = []
+    found = False
+    for i, line in enumerate(lines):
+        if re.search(rf'^[\+\- ].*', line):
+            lineno = i + 1
+            if abs(lineno - target_line) <= context:
+                start = max(0, i - context)
```
created_at: 2025-03-29T14:00:36Z
author_association: NONE
diff_hunk: @@ -0,0 +1 @@
+eval('2+2')
path: insecure.py
user: login: preslaff
body: should be reconsidered and alternative methods for executing code should be used. Instead of using `eval`, if you are evaluating mathematical expressions, consider using safer alternatives like `ast.literal_eval` or built-in mathematical functions.

```diff
@@ -0,0 +1 @@
+eval('2+2')
```
created_at: 2025-03-29T19:13:08Z
author_association: OWNER
diff_hunk: @@ -1,86 +1,114 @@
 import os
+import re
 import argparse
-from typing import TypedDict, Optional
-from dotenv import load_dotenv
+import sqlite3
+from typing import TypedDict
 from github import Github
+from langchain_core.runnables import RunnableLambda
+from langgraph.graph import StateGraph
 from langchain_openai import ChatOpenAI
-from langgraph.graph import StateGraph, END
-from .prompt import prompt
-from .review_utils import parse_feedback_to_comments, store_review_db
+from langgraph_agent.prompt import SYSTEM_PROMPT, HUMAN_PROMPT
+from langchain_core.messages import HumanMessage, SystemMessage
+from langgraph_agent.review_utils import parse_feedback_to_comments, store_review_db
+
 
 class ReviewState(TypedDict):
-    file: Optional[object]
-    review: str
+    file: object
+    review_text: str
+
+def extract_diff_snippet(diff, target_line, context=3):
+    lines = diff.splitlines()
+    snippet = []
+    found = False
+    for i, line in enumerate(lines):
+        if re.search(rf'^[\+\- ].*', line):
+            lineno = i + 1
+            if abs(lineno - target_line) <= context:
+                start = max(0, i - context)
+                end = min(len(lines), i + context + 1)
+                snippet = lines[start:end]
+                found = True
+                break
+    if found:
+        return "```diff\n" + "\n".join(snippet) + "\n```"
+    return ""
 
 def main():
-    parser = argparse.ArgumentParser(description="Run the AI code reviewer on a GitHub PR.")
-    parser.add_argument("--pr", type=int, help="PR number (overrides .env PR_NUMBER)", default=None)
-    parser.add_argument("--dry-run", action="store_true", help="Print comments instead of posting them")
-    parser.add_argument("--save-db", action="store_false", help="Do not save review data to the database")
-    parser.add_argument("--model", type=str, default="gpt-4", help="Language model to use (default: gpt-4)")
-    parser.add_argument("--repo", type=str, help="GitHub repository override in format owner/repo")
+    parser = argparse.ArgumentParser()
+    parser.add_argument("--dry-run", action="store_true")
+    parser.add_argument("--save-db", default=True)
+    parser.add_argument("--model", default="gpt-4")
     args = parser.parse_args()
 
-    load_dotenv()
-
-    openai_key = os.getenv("OPENAI_API_KEY")
-    github_token = os.getenv("GITHUB_TOKEN")
-    repo_name = args.repo or os.getenv("GITHUB_REPOSITORY")
-    pr_number = args.pr or os.getenv("PR_NUMBER")
-
-    if not all([openai_key, github_token, repo_name, pr_number]):
-        print("❌ Missing environment variables. Please check your .env file or use --pr.")
-        return
+    repo_name = os.getenv("GITHUB_REPOSITORY")
+    pr_number = int(os.getenv("PR_NUMBER"))
+    token = os.getenv("GITHUB_TOKEN")
 
-    pr_number = int(pr_number)
-    print(f"🔍 Running AI Code Review for PR #{pr_number} in {repo_name} using model {args.model}...")
-
-    g = Github(github_token)
+    g = Github(token)
     repo = g.get_repo(repo_name)
     pr = repo.get_pull(pr_number)
-    files = pr.get_files()
 
-    llm = ChatOpenAI(model=args.model, api_key=openai_key)
+    llm = ChatOpenAI(model=args.model)
 
-    def review_file(state):
+    def review_code(state):
         file = state["file"]
-        response = llm.invoke(prompt.format(filename=file.filename, patch=file.patch)).content
-        return {"review": response, "file": file}
+        patch = file.patch or ""
+        filename = file.filename
+        prompt = [
+            SystemMessage(content=SYSTEM_PROMPT),
+            HumanMessage(content=HUMAN_PROMPT.format(file_name=filename, patch=patch)),
+        ]
+        response = llm.invoke(prompt)
+        return {"file": file, "review_text": response.content}
 
     def post_inline_comments(state):
         file = state["file"]
-        review = state["review"]
+        review = state["review_text"]
         comments = parse_feedback_to_comments(review, file)
 
         if args.dry_run:
             print(f"\n📄 Review for {file.filename}:")
             for c in comments:
                 print(f"  Line {c['line']}: {c['body']}")
+                snippet = extract_diff_snippet(file.patch or "", c['line'])
+                if snippet:
+                    print(snippet)
         else:
+            commit = repo.get_commit(pr.head.sha)
+            for c in comments:
+                snippet = extract_diff_snippet(file.patch or "", c['line'])
+                comment_body = f"{c['body']}\n\n{snippet}" if snippet else c['body']
path: langgraph_agent/agent.py
user: login: preslaff
body: The `token` environment variable is being passed without a fallback or error handling mechanism, posing a risk of unhandled exceptions if `token` is not defined.

```diff
-    g = Github(github_token)
+    g = Github(token)
     repo = g.get_repo(repo_name)
     pr = repo.get_pull(pr_number)
-    files = pr.get_files()
 
-    llm = ChatOpenAI(model=args.model, api_key=openai_key)
```
created_at: 2025-03-29T19:13:27Z
author_association: OWNER

PR comments:
user: login: github-actions[bot]
created_at: 2025-03-28T15:34:12Z
body: ## 🤖 AI Review Summary for PR #3

<details><summary>📄 `insecure.py`</summary>

### Review
- **Bug**: No bugs are present in this one line of code.
- **Security**: The use of the `eval()` function (line 1) presents a security vulnerability. The `eval()` function takes a string and evaluates it as Python code. This means that malicious input can potentially be executed, which may lead to unauthorized access, data leakage, or other harmful actions.
- **Performance**: Using `eval()` is generally slower than executing Python code directly, especially in loops. However, this is not a significant issue in this single-line context.
- **Readability**: Although there are no significant readability issues, using `eval()` for simple calculations (when it's not really necessary) could be confusing for others reading your code, and it's bad practice in general given its potential security implications.

</details>
author_association: NONE
user: login: github-actions[bot]
created_at: 2025-03-28T19:47:50Z
body: ## 🤖 AI Review Summary for PR #3

<details><summary>📄 insecure.py</summary>

### Review
- **Bug**: No evident bugs in this code snippet.
- **Security**: High security vulnerability. The `eval` Python function can execute any arbitrary python code (line 1), which can lead to harmful usage if an attacker can manipulate the input string. Encourage the use of a safer alternative, such as utilizing the `operator` module for mathematical operations or the `ast.literal_eval` function to safely parse and evaluate an expression.
- **Performance**: The code may be inefficient if it is repeatedly initializing and invoking `eval` instead of running static code or using pre-compiled functions. 
- **Readability**: The code is simple, but lacks explanation for why `eval('2+2')` was used. Comments could be added to explain its purpose, especially when considering the potential risks that come with using `eval`. Using variable names that explicitly depict what operations or actions are being made can increase readability and clarify understanding.

```diff
@@ -0,0 +1 @@
+eval('2+2')
```</details>

<details><summary>📄 langgraph_agent/agent.py</summary>

### Review

- **Bug**: 
  - On line 39, the pull request number `pr_number` is getting from the environment and then converting directly into an integer. There should be a check to ensure that a value exists and that it can be converted to an integer. If it's not valid, a meaningful error message should be displayed.
  - On line 72, attempting to create a GitHub comment might fail due to various reasons such as lack of permissions or a network error. These potential exceptions should be caught and handled to avoid crashing the program.
  - The disappearance of error checking for mandatory environment variables like `GITHUB_TOKEN`, `GITHUB_REPOSITORY`, `PR_NUMBER` and `OPENAI_API_KEY` after lines 25-29 deletion, would lead to runtime errors if any are missing.

- **Security**: 
  - When dealing with external services (like GitHub), it's better to handle tokens or keys in a more secure way. The usage of the GitHub token on line 40 might need to be audited to ensure it's not leaking, reused or stored in insecure locations. 

- **Performance**: 
  - Regex search on each line of the diff on line 21 might be a performance issue when reviewing very large diffs. Consider a more optimized approach to finding the desired lines.
  - Depending on the frequency of reviews, the new inline comment feature could significantly increase the number of API calls to GitHub, possibly resulting in rate limiting issues.

- **Readability**: 
  - The function `extract_diff_snippet` on lines 16-30 becomes less readable with the nested conditionals and control flows. This function could be refactored to improve readability.
  - As the complexity of the `main` function has increased (lines 33-101), certain parts could be refactored into separate, reusable functions to improve readability and maintainability.
  - The variable names are generally meaningful and the coding style is consistent. Still, some code comments documenting the functionality of the main blocks would be beneficial for better understanding.

```diff
@@ -1,86 +1,115 @@
 import os
+import re
 import argparse
-from typing import TypedDict, Optional
```</details>

<details><summary>📄 langgraph_agent/prompt.py</summary>

### Review
- **Bug**: No bugs were identified in this code diff.
- **Security**: No security issues were identified in this code diff.
- **Performance**: No performance issues were identified in this code diff.
- **Readability**: 
  - The variable name change from `prompt` to `SYSTEM_PROMPT` and `HUMAN_PROMPT` (lines 3-19 and 20-30) helps to improve the readability by making it clearer what the variable's purpose is. 
  - However, the readability could be enhanced by adding comments briefly explaining what `SYSTEM_PROMPT` and `HUMAN_PROMPT` are meant for. 
  - Also, the file lacks a newline at the end of the file (line 31). This might not cause an issue with Python, but it's a common convention to end a file with a newline.
  
- **Maintainability**: The changes make the code more maintainable by constants for prompt strings, which makes it easier to modify them in the future if needed.

```diff
@@ -1,28 +1,25 @@
-from langchain_core.prompts import ChatPromptTemplate
+# langgraph_agent/prompt.py
 
-prompt = ChatPromptTemplate.from_template(
```</details>
author_association: NONE
user: login: github-actions[bot]
created_at: 2025-03-28T21:09:38Z
body: ## 🤖 AI Review Summary for PR #3

<details><summary>📄 insecure.py</summary>### Review
- **Bug**: N/A

- **Security**: 
  - Using `eval()` function (Line 1): The `eval()` function is dangerous because it evaluates an expression, which could be any arbitrary code. If this code is user-generated, it poses a significant security risk as a malicious user could execute harmful commands. Use alternative methods to execute code safely, or thoroughly validate and sanitize input before using it in `eval()`.

- **Performance**: N/A

- **Readability**: 
    - The use of `eval()` to simply perform a addition (2+2) on Line 1 is unnecessary and confusing. Simply use regular arithmetic for this, which would make the code more readable and understandable.</details>

<details><summary>📄 langgraph_agent/agent.py</summary>### Review
- **Bug**: 
    - Line 43, the variable extract_diff_snippet is defined but is not being used anywhere.
    - Line 59, the previous implementation of the tool involved user passing ‘pr’ as an argument but with the new changes, ‘pr’ is removed as an argument but still used in the code. It won’t work because command line interfaces do not have access to environment variables which are defined in the OS. It was better to keep ‘pr’ as an argument.
- **Security**: 
    - In line 60, a security vulnerability arises from the removal of the API Key as variable. Direct use of the API key in the code is a security risk. It’s recommended to get such sensitive data from environment variables or secret keys rather than hard-coding them within the application.
- **Performance**: 
    - No performance issues were identified.
- **Readability**: 
    - The change from `os.getenv()` to `os.environ[]` can lead to less readable error messages if the environment variable is not set. Using `os.getenv()` would return `None` and is safer.
    - Removal of argparse descriptions and help make the CLI user experience less friendly and understandable.
    - Also, the removal of the default values for command line arguments reduces the code's usability and might create confusion for users.
    - Having an import for 'sqlite3' but not used anywhere in the code, makes the code cleaner and more maintainable. It is always recommended to remove unused imports.</details>

<details><summary>📄 langgraph_agent/prompt.py</summary>### Review
- **Bug**: There doesn't appear to be any functional bugs.
- **Security**: No security issues have been detected.
- **Performance**: The changes should not affect performance.
- **Readability**: 
    - **Line 26 and 27**: The trailing `"..."` might make it unclear that this last item in the list is also expecting user input like the others. Consider using a more explicit prompt, such as `"Please enter any issues related to readability or maintainability here."`
    - Generally, the changes have improved readability. However, no comments have been provided regarding what the `SYSTEM_PROMPT` and `HUMAN_PROMPT` string constants are used for. Consider adding comments for clarity. Also, consider consistent use of naming conventions. If `file_name` is used in `HUMAN_PROMPT`, consider using the same style `system_prompt` and `human_prompt` for the constant names.
- **Maintainability**: No apparent maintainability issues. The changes make the code simpler and less prone to errors, especially since a specific module (`ChatPromptTemplate`) has been removed. However, that module might have had functionalities that are now missing and that could be vital for the remaining code. If no such dependency exists the change increases maintainability.</details>
author_association: NONE
user: login: github-actions[bot]
created_at: 2025-03-28T21:19:23Z
body: ## 🤖 AI Review Summary for PR #3

<details><summary>📄 insecure.py</summary>

```markdown
### Review
- **Bug**: No bugs are present.
- **Security**: The usage of the `eval` function (line 1) is a serious security concern. It can evaluate arbitrary code passed as a string, facilitating potential exploits such as code injection. 
- **Performance**: There isn't a significant performance issue for this small piece of code. However, the use of `eval` can lead to inefficient execution, particularly with more complex expressions.
- **Readability**: The code is not self-explanatory and lacks context. The constant `2+2` may not indicate the true nature of the expressions to be evaluated with `eval`. Comments or more explicit code can improve readability and understanding. 

To improve safety and readability, refrain from using `eval`. It's better to code explicit computations, or if executing dynamic code is necessary, use safe parsing and evaluation mechanisms tailored for your needs.
```

Line 1:

```diff
@@ -0,0 +1 @@
+eval('2+2')
```
</details>

<details><summary>📄 langgraph_agent/agent.py</summary>

```markdown
### Review
- **Bug**: 
  - Line 60: Undefined variables 'file' in `review_code(state)`. This doesn't seem to be defined anywhere in this function, so the code may throw an error at runtime. If `file` is supposed to be taken from `state`, this should be done explicitly (as in `file = state['file']`).
  - Line 67: Signature of the `ChatOpenAI.invoke(prompt)` method usage was changed, but it seems that the `prompt` passed is not correctly configured as list of dictionaries with 'role' and 'content' keys, as it should be in the OpenAI API. Please refer to the OpenAI API documentation for correct usage.

- **Security**: 
  - There are no severe security issues visible in this changelog.

- **Performance**: 
  - Line 30-49(`extract_diff_snippet` function), Line 51-60(`extract_snippets_from_review` function): This code iterates over the lines of diff for every reference to it, which may lead to inefficient execution for large diffs with a number of references. A more efficient approach could be to calculate the requisite line range for each reference first, sort and merge overlapping ranges, and then extract those lines from the diff.

- **Readability**: 
  - Line 26, Line 32(`extract_diff_snippet` function), Line 51(`extract_snippets_from_review` function), Line 77(`review_code(state)`), Line 86(`review_code(state)`), Line 106: The functions and code have become more complex than before since the introduction of extracting snippets. Consider abstracting some of this complexity into helper functions or documenting what is happening with more comments to better interpret the code.
  - In general, the code lacks sufficient comments to easily understand what is happening at each step. This could make maintenance and cooperation difficult for larger teams.
  - Line 86(`post_inline_comments(state)`): The temporary variable `snippet` could have a more descriptive name to better illustrate its purpose. For example: `diff_snippet`.

```

Line 32:

```diff
+
+def extract_diff_snippet(diff, target_line, context=3):
+    lines = diff.splitlines()
+    snippet = []
+    found = False
+    for i, line in enumerate(lines):
+        if re.search(rf'^[\+\- ].*', line):
```

Line 67:

```diff
+    parser = argparse.ArgumentParser()
+    parser.add_argument("--dry-run", action="store_true")
+    parser.add_argument("--save-db", default=True)
+    parser.add_argument("--model", default="gpt-4")
     args = parser.parse_args()
 
-    load_dotenv()
```

Line 106:

```diff
+        prompt = [
+            SystemMessage(content=SYSTEM_PROMPT),
+            HumanMessage(content=HUMAN_PROMPT.format(file_name=filename, patch=patch)),
+        ]
+        response = llm.invoke(prompt)
+        return {"file": file, "review_text": response.content}
 
```

Line 77:

```diff
-    repo_name = args.repo or os.getenv("GITHUB_REPOSITORY")
-    pr_number = args.pr or os.getenv("PR_NUMBER")
-
-    if not all([openai_key, github_token, repo_name, pr_number]):
-        print("❌ Missing environment variables. Please check your .env file or use --pr.")
-        return
+    repo_name = os.getenv("GITHUB_REPOSITORY")
```

Line 51:

```diff
+    snippets = []
+    line_refs = re.findall(r"[Ll]ine (\d+)", review_text)
+    for ref in set(map(int, line_refs)):
+        snippet = extract_diff_snippet(patch, ref)
+        if snippet:
+            snippets.append(f"Line {ref}:")
+            snippets.append(snippet)
```

Line 86:

```diff
 
-    pr_number = int(pr_number)
-    print(f"🔍 Running AI Code Review for PR #{pr_number} in {repo_name} using model {args.model}...")
-
-    g = Github(github_token)
+    g = Github(token)
     repo = g.get_repo(repo_name)
```

Line 26:

```diff
 
 class ReviewState(TypedDict):
-    file: Optional[object]
-    review: str
+    file: object
+    review_text: str
+
```

Line 60:

```diff
 def main():
-    parser = argparse.ArgumentParser(description="Run the AI code reviewer on a GitHub PR.")
-    parser.add_argument("--pr", type=int, help="PR number (overrides .env PR_NUMBER)", default=None)
-    parser.add_argument("--dry-run", action="store_true", help="Print comments instead of posting them")
-    parser.add_argument("--save-db", action="store_false", help="Do not save review data to the database")
-    parser.add_argument("--model", type=str, default="gpt-4", help="Language model to use (default: gpt-4)")
-    parser.add_argument("--repo", type=str, help="GitHub repository override in format owner/repo")
```

Line 30:

```diff
+    file: object
+    review_text: str
+
+def extract_diff_snippet(diff, target_line, context=3):
+    lines = diff.splitlines()
+    snippet = []
+    found = False
```
</details>

<details><summary>📄 langgraph_agent/prompt.py</summary>

```markdown
### Review
- **Bug**: No apparent bugs present.
- **Security**: No apparent security vulnerabilities.
- **Performance**: No obvious performance issues.
- **Readability/maintainability**:
  - Line 3: Consider adding a docstring at the beginning of the file to provide a brief summary of what this module does.
  - Line 17 and Line 30: It would be beneficial to keep a newline at the end of the file to adhere to PEP 8 guidelines and to ensure compatibility with certain Unix utilities.
  - The removal of ChatPromptTemplate might cause issues if it's needed somewhere else in the code. 
  - Consider adding comments to explain what SYSTEM_PROMPT and HUMAN_PROMPT are used for - this could be beneficial for developers who are not familiar with the code.
  - The change from using a template system ("ChatPromptTemplate") to using raw string manipulation is potentially error-prone and less readable/maintainable. If the system was changed because the template system was not flexible enough, consider improving the template system instead.
```

Line 17:

```diff
+    "1. 🐛 Bugs\n"
+    "2. 🔐 Security vulnerabilities\n"
+    "3. 🚀 Performance issues\n"
+    "4. 📚 Readability and maintainability problems\n"
+    "\n"
+    "Provide actionable, concise feedback. Use Markdown formatting, include line numbers where possible."
+)
```

Line 3:

```diff
@@ -1,28 +1,25 @@
-from langchain_core.prompts import ChatPromptTemplate
+# langgraph_agent/prompt.py
 
-prompt = ChatPromptTemplate.from_template(
```

Line 30:

```diff
-Provide actionable, concise feedback. Use Markdown formatting, include line numbers where possible.
-
-Code diff for file `{filename}`:
-```diff
-{patch}
-```
-
```
</details>
author_association: NONE
user: login: github-actions[bot]
created_at: 2025-03-29T14:01:00Z
body: ## 🤖 AI Review Summary for PR #3

<details><summary>📄 insecure.py</summary>

### Review
- **Bug**: The code seems not to have any observable bugs.
- **Security**: The code `eval('2+2')` uses `eval()` function which executes expressions it receives. This poses a high security vulnerability if user input is passed directly into the function, as it could execute harmful code.
- **Performance**: No specific performance issues, as the script is minimal and only performs one expression.
- **Readability**: While the code is quite simple, there is a lack of context or comments which would aid in understanding its purpose.

</details>

<details><summary>📄 langgraph_agent/agent.py</summary>

### Review
- **Bug**: 
  - Line 35: The PR number is being converted to an integer using `int(os.getenv("PR_NUMBER"))`. There's no null checking here and `None` may cause `TypeError` if the environment variable "PR_NUMBER" is not set.
  - Lines 37-38: If the environment variables "GITHUB_REPOSITORY" and "GITHUB_TOKEN" are not set, this won't be caught until a `NoneType` error occurs when the script tries to interact with GitHub. This was handled in the original code by checking all required variables at the beginning and returning with an error message if any were missing. I would suggest adding null checks for required environment variables then throwing error messages if necessary.
- **Security**: 
  - There's no security issues were found related to injections or sensitive data exposure in the updated code.
  
- **Performance**: 
  - No significant performance issues detected. 
   
- **Readability** and **Maintainability**: 
  - Code readability has improved as the use of relevant function names and removal of some inputs helped clear the clutter. However, it is recommended to add brief comments to functions and major operations for better maintainability.
  - Renaming the `review_file` function to `review_code` makes sense from a readability perspective because it makes it clearer that the function is meant to review code, not files. Similarly, changing `review` to `review_text` in different locations makes the variable purpose clearer.
  - The function `extract_diff_snippet` needs an explanatory comment about its purpose, and perhaps a brief description about the regular expression used on line 22 to enhance code readability.
  - Moving the `argparse` arguments descriptions have reduced the immediate clarity of what each argument does. It might be worthwhile to reintroduce those.

</details>

<details><summary>📄 langgraph_agent/prompt.py</summary>

### Review
- **Bug**: No apparent bugs were found in the provided code diff. 
- **Security**: The code diff does not involve any security-sensitive operation, so no security vulnerabilities were found.
- **Performance**: The changes do not appear to have an impact on the performance. It's basically just modification of string formatting.
- **Readability**: 
   * The constant names (`SYSTEM_PROMPT`, `HUMAN_PROMPT`) on line 4 and 18 give clear indications of their usage which is good for maintainability. 
   * However, line 28 finishes with a string that doesn't end with a newline character. It is a good practice to end files with a newline as certain utilities can have issues with lack of newline at the end of the file.
   * Given the fact that newlines and formatting play a significant role in these strings, using triple quotes (`"""`) would increase readability as it reduces the need to manually include newline (`\n`) and other escape characters in the string. This switch would allow for an actual layout representation inside the strings, which is easier to read and manage. 

To improve readability, consider the following updates:
```python
SYSTEM_PROMPT = """You are an expert AI code reviewer. For the given code diff, identify issues in the following categories:

1. 🐛 Bugs
2. 🔐 Security vulnerabilities
3. 🚀 Performance issues
4. 📚 Readability and maintainability problems

Provide actionable, concise feedback. Use Markdown formatting, include line numbers where possible.
"""

HUMAN_PROMPT = """Here is the code diff for `{file_name}`:

```diff
{patch}
```

Please review it and provide feedback in the following format:

### Review
- **Bug**: ...
- **Security**: ...
- **Performance**: ...
- **Readability**: ...
"""
```
This preserves the meanings, improves readability, and enables seeing the real layout inside your editor.

</details>
author_association: NONE
user: login: preslaff
created_at: 2025-03-29T19:13:34Z
body: ## 🤖 AI Review Summary for PR #3

<details><summary>📄 insecure.py</summary>

### Review
- **Bug**: There doesn't appear to be a functional bug in the given single line of code.
- **Security**: The `eval` function is used, which can be a significant security risk. `eval` executes an arbitrary code supplied to it, which could allow an attacker to execute malicious code if they can influence the input. Line 1 should be reconsidered and alternative methods for executing code should be used. Instead of using `eval`, if you are evaluating mathematical expressions, consider using safer alternatives like `ast.literal_eval` or built-in mathematical functions.
- **Performance**: There isn't a performance issue discernible from the single line of code.
- **Readability**: This line of code lacks context since it's a single line code. However, hardcoding values within the `eval` function like `2+2` is generally not ideal. It's more maintainable to pass variables into any function calls.

</details>

<details><summary>📄 langgraph_agent/agent.py</summary>

### Review
- **Bug**: 
  - Lines 25-38: The `extract_diff_snippet` function may throw `re.error` if `target_line` value (which is user input) is passed into `re.search` without any sanitization or control of its value.
  - Lines 25-38: The calculation of `lineno` variable based on iteration counter seems questionable. There is no tracking of line numbers with respect to actual content of the diff. It will cause off-by-one errors when diff includes addition and deletion.

- **Security**: 
  - Lines 56-58: Handling untrusted user input (such as PR number, and Github token) can risk injection attacks if not sanitized properly. This could lead to an attacker executing actions using the oauth token of the user.
  - Line 80: The `token` environment variable is being passed without a fallback or error handling mechanism, posing a risk of unhandled exceptions if `token` is not defined.

- **Performance**: 
  - Lines 62-66: Using Github's REST API to access individual pull request files one at a time can cause performance issues when dealing with large pull requests. Using the GraphQL API might help reducing the number of requests.

- **Readability**: 
  - Lines 44-47, 53-61, and 89-97: Functions `review_code`, `post_inline_comments`, and `extract_diff_snippet` are quite complex. Arbitrary manipulations of inline strings make it harder to understand the logic at the first glance. It might be worth splitting these functions into smaller, more manageable parts.
  - Lines 73-108: There is lot of business logic inside the `main` function which makes it hard to understand and debug. Aim for single responsibility and move some of the functionality to separate functions.
  - General: There are no comments explaining the purpose and functionality of the functions and code blocks. Comments could greatly improve maintainability and readability. 

- Improvements: Encapsulate the functionality of this script into separate functions or even classes. Using OOP can help improve the structure and readability of the code.

</details>

<details><summary>📄 langgraph_agent/prompt.py</summary>

### Review
- **Bug**: No bugs are detected in the code.
- **Security**: No apparent security issues observed in the code.
- **Performance**: The changes in the code do not seem to affect the performance.
- **Readability**: 

  - The replacement of the existing multi-line string with a tuple of strings should not impact the overall functionality. However, it does slightly reduce readability because the output string is now broken into separate strings (lines 8-20).

  - As another suggestion, it may be worth considering keeping the format consistent between `SYSTEM_PROMPT` and `HUMAN_PROMPT` (lines 3-20 and 23-34). Both prompts contain strings, but they are formatted differently: one as a multiline and the other as a tuple. It could improve readability to stick with either format for both.

</details>
author_association: OWNER