pranav8tripathi@gmail.com commited on
Commit
45cd58a
Β·
1 Parent(s): c0a24ec

enhanced logging

Browse files
Files changed (4) hide show
  1. app/code_reviewer.py +30 -15
  2. app/github_service.py +11 -1
  3. app/github_webhook.py +38 -1
  4. app/main.py +18 -1
app/code_reviewer.py CHANGED
@@ -1,6 +1,9 @@
1
  import os
2
  import json
3
  import httpx
 
 
 
4
 
5
  DEEPSEEK_API_URL = "https://api.deepseek.com/v1/chat/completions"
6
  DEEPSEEK_API_KEY = os.getenv("DEEPSEEK_API_KEY")
@@ -10,6 +13,8 @@ async def analyze_code(file_name: str, patch: str) -> list:
10
  Analyze a single file diff using DeepSeek AI model.
11
  Returns a list of structured comments.
12
  """
 
 
13
  prompt = f"""
14
  You are a senior code reviewer.
15
  Review the following diff from `{file_name}` and provide detailed, line-specific feedback.
@@ -37,21 +42,31 @@ async def analyze_code(file_name: str, patch: str) -> list:
37
  "messages": [{"role": "user", "content": prompt}]
38
  }
39
 
40
- async with httpx.AsyncClient(timeout=60.0) as client:
41
- response = await client.post(DEEPSEEK_API_URL, headers=headers, json=payload)
42
- response.raise_for_status()
43
- data = response.json()
 
 
 
44
 
45
- text_output = data["choices"][0]["message"]["content"].strip()
 
46
 
47
- # Defensive: handle non-JSON outputs
48
- try:
49
- # Remove markdown code blocks if present
50
- if text_output.startswith("```json"):
51
- text_output = text_output.replace("```json", "").replace("```", "").strip()
52
- elif text_output.startswith("```"):
53
- text_output = text_output.replace("```", "").strip()
54
-
55
- return json.loads(text_output)
 
 
 
 
 
56
  except Exception as e:
57
- return [{"line": 1, "severity": "info", "comment": text_output}]
 
 
1
  import os
2
  import json
3
  import httpx
4
+ import logging
5
+
6
+ logger = logging.getLogger(__name__)
7
 
8
  DEEPSEEK_API_URL = "https://api.deepseek.com/v1/chat/completions"
9
  DEEPSEEK_API_KEY = os.getenv("DEEPSEEK_API_KEY")
 
13
  Analyze a single file diff using DeepSeek AI model.
14
  Returns a list of structured comments.
15
  """
16
+ logger.info(f"πŸ€– Sending to DeepSeek AI: {file_name} ({len(patch)} chars)")
17
+
18
  prompt = f"""
19
  You are a senior code reviewer.
20
  Review the following diff from `{file_name}` and provide detailed, line-specific feedback.
 
42
  "messages": [{"role": "user", "content": prompt}]
43
  }
44
 
45
+ try:
46
+ async with httpx.AsyncClient(timeout=60.0) as client:
47
+ logger.info("⏳ Waiting for DeepSeek response...")
48
+ response = await client.post(DEEPSEEK_API_URL, headers=headers, json=payload)
49
+ response.raise_for_status()
50
+ data = response.json()
51
+ logger.info("βœ… DeepSeek response received")
52
 
53
+ text_output = data["choices"][0]["message"]["content"].strip()
54
+ logger.info(f"πŸ“„ Response length: {len(text_output)} chars")
55
 
56
+ # Defensive: handle non-JSON outputs
57
+ try:
58
+ # Remove markdown code blocks if present
59
+ if text_output.startswith("```json"):
60
+ text_output = text_output.replace("```json", "").replace("```", "").strip()
61
+ elif text_output.startswith("```"):
62
+ text_output = text_output.replace("```", "").strip()
63
+
64
+ parsed = json.loads(text_output)
65
+ logger.info(f"βœ… Parsed {len(parsed)} review comments")
66
+ return parsed
67
+ except Exception as e:
68
+ logger.warning(f"⚠️ Failed to parse JSON, returning raw text: {str(e)}")
69
+ return [{"line": 1, "severity": "info", "comment": text_output}]
70
  except Exception as e:
71
+ logger.error(f"❌ DeepSeek API error: {str(e)}")
72
+ raise
app/github_service.py CHANGED
@@ -1,5 +1,8 @@
1
  import httpx
2
  import os
 
 
 
3
 
4
  GITHUB_TOKEN = os.getenv("GITHUB_TOKEN")
5
  HEADERS = {
@@ -26,8 +29,11 @@ async def post_review_summary(owner: str, repo: str, pr_number: int, review_comm
26
  Formats all AI feedback into a single markdown summary
27
  and posts it as a PR comment.
28
  """
 
 
29
  if not review_comments:
30
  summary_body = "βœ… **No issue found by AI reviewer!**"
 
31
  else:
32
  summary_body = "### πŸ€– AI Code Review Summary\n\n"
33
  summary_body += "The following findings were automatically generated by the AI reviewer:\n\n"
@@ -42,6 +48,10 @@ async def post_review_summary(owner: str, repo: str, pr_number: int, review_comm
42
  summary_body += f"{emoji} **{severity}** β€” `{file}` (L{line}): {comment}\n"
43
 
44
  summary_body += "\n---\n_This review was auto-generated by **PRism AI Reviewer**._ πŸ€–"
 
45
 
46
  # Post the formatted comment
47
- return await post_pr_comment(owner, repo, pr_number, summary_body)
 
 
 
 
1
  import httpx
2
  import os
3
+ import logging
4
+
5
+ logger = logging.getLogger(__name__)
6
 
7
  GITHUB_TOKEN = os.getenv("GITHUB_TOKEN")
8
  HEADERS = {
 
29
  Formats all AI feedback into a single markdown summary
30
  and posts it as a PR comment.
31
  """
32
+ logger.info(f"πŸ“ Formatting review summary ({len(review_comments)} comments)...")
33
+
34
  if not review_comments:
35
  summary_body = "βœ… **No issue found by AI reviewer!**"
36
+ logger.info("✨ No issues found - posting clean review")
37
  else:
38
  summary_body = "### πŸ€– AI Code Review Summary\n\n"
39
  summary_body += "The following findings were automatically generated by the AI reviewer:\n\n"
 
48
  summary_body += f"{emoji} **{severity}** β€” `{file}` (L{line}): {comment}\n"
49
 
50
  summary_body += "\n---\n_This review was auto-generated by **PRism AI Reviewer**._ πŸ€–"
51
+ logger.info(f"πŸ“‹ Summary length: {len(summary_body)} characters")
52
 
53
  # Post the formatted comment
54
+ logger.info(f"πŸš€ Posting comment to PR #{pr_number}...")
55
+ result = await post_pr_comment(owner, repo, pr_number, summary_body)
56
+ logger.info(f"βœ… Comment posted successfully (ID: {result.get('id', 'unknown')})")
57
+ return result
app/github_webhook.py CHANGED
@@ -6,7 +6,9 @@ import asyncio
6
  import hmac
7
  import hashlib
8
  import os
 
9
 
 
10
  router = APIRouter()
11
 
12
  def verify_signature(payload_body: bytes, signature_header: str) -> bool:
@@ -21,13 +23,24 @@ def verify_signature(payload_body: bytes, signature_header: str) -> bool:
21
 
22
  @router.post("/webhook/github")
23
  async def github_webhook(payload: PullRequestPayload, request: Request):
 
 
 
 
24
  # Verify webhook signature
25
  signature = request.headers.get("X-Hub-Signature-256", "")
26
  body = await request.body()
 
 
 
27
  if not verify_signature(body, signature):
 
28
  raise HTTPException(status_code=403, detail="Invalid signature")
29
 
 
 
30
  if payload.action not in ["opened", "synchronize"]:
 
31
  return {"message": "Ignored non-PR-open events."}
32
 
33
  repo_info = payload.repository
@@ -35,16 +48,32 @@ async def github_webhook(payload: PullRequestPayload, request: Request):
35
  owner = repo_info["owner"]["login"]
36
  repo = repo_info["name"]
37
  pr_number = pr["number"]
 
 
 
 
 
38
 
39
  # Fetch changed files
 
40
  files = await fetch_pr_files(owner, repo, pr_number)
 
41
 
42
  review_comments = []
 
43
 
44
- for f in files:
45
  if not f.get("patch"):
 
46
  continue
 
 
 
 
 
47
  ai_feedback = await analyze_code(f["filename"], f["patch"])
 
 
48
  for c in ai_feedback:
49
  review_comments.append({
50
  "file": f["filename"],
@@ -53,9 +82,17 @@ async def github_webhook(payload: PullRequestPayload, request: Request):
53
  "comment": c.get("comment", "")
54
  })
55
 
 
 
56
  # Format feedback as PR comment
57
  from .github_service import post_review_summary
58
 
 
59
  await post_review_summary(owner, repo, pr_number, review_comments)
 
 
 
 
 
60
 
61
  return {"status": "AI review posted", "count": len(review_comments)}
 
6
  import hmac
7
  import hashlib
8
  import os
9
+ import logging
10
 
11
+ logger = logging.getLogger(__name__)
12
  router = APIRouter()
13
 
14
  def verify_signature(payload_body: bytes, signature_header: str) -> bool:
 
23
 
24
  @router.post("/webhook/github")
25
  async def github_webhook(payload: PullRequestPayload, request: Request):
26
+ logger.info("=" * 60)
27
+ logger.info("πŸ”” WEBHOOK RECEIVED")
28
+ logger.info("=" * 60)
29
+
30
  # Verify webhook signature
31
  signature = request.headers.get("X-Hub-Signature-256", "")
32
  body = await request.body()
33
+
34
+ logger.info(f"πŸ“ Action: {payload.action}")
35
+
36
  if not verify_signature(body, signature):
37
+ logger.error("❌ Invalid webhook signature!")
38
  raise HTTPException(status_code=403, detail="Invalid signature")
39
 
40
+ logger.info("βœ… Signature verified")
41
+
42
  if payload.action not in ["opened", "synchronize"]:
43
+ logger.info(f"⏭️ Ignoring action: {payload.action}")
44
  return {"message": "Ignored non-PR-open events."}
45
 
46
  repo_info = payload.repository
 
48
  owner = repo_info["owner"]["login"]
49
  repo = repo_info["name"]
50
  pr_number = pr["number"]
51
+
52
+ logger.info(f"πŸ“¦ Repository: {owner}/{repo}")
53
+ logger.info(f"πŸ”’ PR Number: #{pr_number}")
54
+ logger.info(f"πŸ‘€ Author: {pr['user']['login']}")
55
+ logger.info(f"πŸ“‹ Title: {pr['title']}")
56
 
57
  # Fetch changed files
58
+ logger.info("πŸ“₯ Fetching changed files from GitHub...")
59
  files = await fetch_pr_files(owner, repo, pr_number)
60
+ logger.info(f"βœ… Found {len(files)} changed files")
61
 
62
  review_comments = []
63
+ files_analyzed = 0
64
 
65
+ for idx, f in enumerate(files, 1):
66
  if not f.get("patch"):
67
+ logger.info(f"⏭️ Skipping {f['filename']} (no patch)")
68
  continue
69
+
70
+ files_analyzed += 1
71
+ logger.info(f"πŸ” [{idx}/{len(files)}] Analyzing: {f['filename']}")
72
+ logger.info(f" Status: {f.get('status', 'unknown')}, Changes: {f.get('changes', 0)} lines")
73
+
74
  ai_feedback = await analyze_code(f["filename"], f["patch"])
75
+ logger.info(f" βœ… AI returned {len(ai_feedback)} comments")
76
+
77
  for c in ai_feedback:
78
  review_comments.append({
79
  "file": f["filename"],
 
82
  "comment": c.get("comment", "")
83
  })
84
 
85
+ logger.info(f"πŸ“Š Analysis complete: {files_analyzed} files analyzed, {len(review_comments)} total comments")
86
+
87
  # Format feedback as PR comment
88
  from .github_service import post_review_summary
89
 
90
+ logger.info("πŸ“€ Posting review summary to GitHub PR...")
91
  await post_review_summary(owner, repo, pr_number, review_comments)
92
+ logger.info("βœ… Review posted successfully!")
93
+
94
+ logger.info("=" * 60)
95
+ logger.info("πŸŽ‰ WEBHOOK PROCESSING COMPLETE")
96
+ logger.info("=" * 60)
97
 
98
  return {"status": "AI review posted", "count": len(review_comments)}
app/main.py CHANGED
@@ -3,16 +3,33 @@ from .github_webhook import router as github_router
3
  from dotenv import load_dotenv
4
  import logging
5
  import json
 
6
 
7
  load_dotenv() # call this if you want dotenv to load env vars
8
 
9
- logging.basicConfig(level=logging.INFO)
 
 
 
10
  logger = logging.getLogger(__name__)
11
 
12
  app = FastAPI(title="AI Code Review System")
13
 
14
  app.include_router(github_router)
15
 
 
 
 
 
 
 
 
 
 
 
 
 
 
16
  @app.get("/")
17
  def root():
18
  return {"message": "AI Code Review System is running πŸš€"}
 
3
  from dotenv import load_dotenv
4
  import logging
5
  import json
6
+ import os
7
 
8
  load_dotenv() # call this if you want dotenv to load env vars
9
 
10
+ logging.basicConfig(
11
+ level=logging.INFO,
12
+ format='%(levelname)s: %(message)s'
13
+ )
14
  logger = logging.getLogger(__name__)
15
 
16
  app = FastAPI(title="AI Code Review System")
17
 
18
  app.include_router(github_router)
19
 
20
+ @app.on_event("startup")
21
+ async def startup_event():
22
+ logger.info("=" * 60)
23
+ logger.info("πŸš€ PRism AI Code Reviewer Starting...")
24
+ logger.info("=" * 60)
25
+ logger.info(f"βœ… GITHUB_TOKEN: {'Set' if os.getenv('GITHUB_TOKEN') else '❌ MISSING'}")
26
+ logger.info(f"βœ… DEEPSEEK_API_KEY: {'Set' if os.getenv('DEEPSEEK_API_KEY') else '❌ MISSING'}")
27
+ logger.info(f"βœ… GITHUB_WEBHOOK_SECRET: {'Set' if os.getenv('GITHUB_WEBHOOK_SECRET') else '⚠️ Not set (signature verification disabled)'}")
28
+ logger.info("=" * 60)
29
+ logger.info("πŸ“‘ Webhook endpoint: POST /webhook/github")
30
+ logger.info("πŸ₯ Health check: GET /")
31
+ logger.info("=" * 60)
32
+
33
  @app.get("/")
34
  def root():
35
  return {"message": "AI Code Review System is running πŸš€"}