LukeMattingly commited on
Commit
3ac40d9
·
1 Parent(s): 0a1dae9

updated ui, redoing tools to work with diffs

Browse files
Files changed (3) hide show
  1. .gitignore +5 -0
  2. CustomGradioUI.py +41 -0
  3. app.py +84 -8
.gitignore ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ venv
2
+ tools
3
+ __pycache__
4
+ .env
5
+ certificate.pem
CustomGradioUI.py ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ from smolagents import GradioUI
3
+
4
+ class CustomGradioUI(GradioUI):
5
+ def launch(self, **kwargs):
6
+ with gr.Blocks(fill_height=True) as demo:
7
+ # Add your header and instructions at the very top
8
+ gr.Markdown("## Welcome my Github PR Review Agent 🤖")
9
+ gr.Markdown("Follow the instructions below to interact with the agent. Type your chat message in the box and hit enter.")
10
+
11
+
12
+ # The rest of the UI remains the same as the original launch method
13
+ stored_messages = gr.State([])
14
+ file_uploads_log = gr.State([])
15
+ chatbot = gr.Chatbot(
16
+ label="Agent",
17
+ type="messages",
18
+ avatar_images=(
19
+ None,
20
+ "https://huggingface.co/datasets/agents-course/course-images/resolve/main/en/communication/Alfred.png",
21
+ ),
22
+ resizeable=True,
23
+ scale=1,
24
+ )
25
+ # If an upload folder is provided, enable the upload feature
26
+ if self.file_upload_folder is not None:
27
+ upload_file = gr.File(label="Upload a file")
28
+ upload_status = gr.Textbox(label="Upload Status", interactive=False, visible=False)
29
+ upload_file.change(
30
+ self.upload_file,
31
+ [upload_file, file_uploads_log],
32
+ [upload_status, file_uploads_log],
33
+ )
34
+ text_input = gr.Textbox(lines=1, label="Please provide a link to your github pull request for review.")
35
+ text_input.submit(
36
+ self.log_user_message,
37
+ [text_input, file_uploads_log],
38
+ [stored_messages, text_input],
39
+ ).then(self.interact_with_agent, [stored_messages, chatbot], [chatbot])
40
+ demo.launch(debug=True, share=True, **kwargs)
41
+
app.py CHANGED
@@ -9,6 +9,7 @@ import ast
9
  from typing import List
10
  from huggingface_hub import login
11
  import os
 
12
 
13
 
14
  from Gradio_UI import GradioUI
@@ -165,11 +166,46 @@ def get_pr_files_changed(github_url: str, pr_number: int) -> List[str]:
165
  return [f"Error fetching PR files: {response.json().get('message', 'Unknown error')}"]
166
 
167
  files = response.json()
168
- return [file['filename'] for file in files]
 
 
169
 
170
  except Exception as e:
171
  return [f"Error retrieving files for PR #{pr_number}: {str(e)}"]
 
 
 
 
 
 
172
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
173
  @tool
174
  def detect_code_smells(code: str) -> str:
175
  """Detects common code smells such as long functions and deeply nested loops.
@@ -198,6 +234,40 @@ def detect_code_smells(code: str) -> str:
198
  except Exception as e:
199
  return f"Error analyzing code: {str(e)}"
200
  '''
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
201
  @tool
202
  def get_file_content(github_url: str, file_path: str) -> str:
203
  """Fetches the content of a specific file from the GitHub repository.
@@ -220,12 +290,14 @@ def get_file_content(github_url: str, file_path: str) -> str:
220
  return f"Error: {str(e)}"
221
 
222
  '''
 
 
223
  @tool
224
- def security_check_code(code: str) -> str:
225
- """Analyzes the provided code snippet for potential security vulnerabilities.
226
 
227
  Args:
228
- code: The source code to be analyzed for common security issues (e.g., hardcoded secrets, unsafe functions).
229
 
230
  Returns:
231
  A string listing detected potential security vulnerabilities based on common patterns (e.g., hardcoded credentials,
@@ -233,6 +305,7 @@ def security_check_code(code: str) -> str:
233
  """
234
  import re
235
  issues = []
 
236
 
237
  # Check for hardcoded credentials (case-insensitive search)
238
  secret_patterns = [
@@ -288,11 +361,11 @@ def check_documentation_updates(changed_files: str) -> str:
288
  return "No documentation updates detected. Consider reviewing the docs to ensure they reflect the new changes."
289
 
290
  @tool
291
- def lint_code(code: str) -> str:
292
  """Analyzes the provided code snippet for style and potential issues using a linter.
293
 
294
  Args:
295
- code: The source code to be analyzed.
296
 
297
  Returns:
298
  A string with linting warnings and suggestions for improvement, or a message indicating that no issues were found.
@@ -300,6 +373,9 @@ def lint_code(code: str) -> str:
300
  # This is a placeholder; you could integrate pylint or flake8 via subprocess or an API.
301
  # For demonstration, we'll simulate a response.
302
  issues = []
 
 
 
303
  if "print(" in code:
304
  issues.append("Consider removing debug print statements.")
305
  if not issues:
@@ -329,7 +405,7 @@ with open("prompts.yaml", 'r') as stream:
329
 
330
  agent = CodeAgent(
331
  model=model,
332
- tools=[final_answer, get_open_pull_requests, find_todo_comments, get_pr_diff, get_pr_files_changed, detect_code_smells, security_check_code, check_documentation_updates, lint_code, get_pr_diff_for_file ], ## add your tools here (don't remove final answer)
333
  max_steps=6,
334
  verbosity_level=1,
335
  grammar=None,
@@ -340,4 +416,4 @@ agent = CodeAgent(
340
  )
341
 
342
 
343
- GradioUI(agent).launch()
 
9
  from typing import List
10
  from huggingface_hub import login
11
  import os
12
+ from CustomGradioUI import CustomGradioUI
13
 
14
 
15
  from Gradio_UI import GradioUI
 
166
  return [f"Error fetching PR files: {response.json().get('message', 'Unknown error')}"]
167
 
168
  files = response.json()
169
+ files_changed = [file['filename'] for file in files]
170
+ print(files_changed)
171
+ return files_changed
172
 
173
  except Exception as e:
174
  return [f"Error retrieving files for PR #{pr_number}: {str(e)}"]
175
+
176
+ #Helper Function
177
+ def diff_to_code(diff: str) -> str:
178
+ """
179
+ Converts a unified diff string into a regular code string by extracting
180
+ added and context lines, while ignoring diff metadata and removed lines.
181
 
182
+ Args:
183
+ diff: A unified diff string representing code changes.
184
+
185
+ Returns:
186
+ A string containing the reconstructed code.
187
+ """
188
+ code_lines = []
189
+ for line in diff.splitlines():
190
+ # Skip diff metadata lines
191
+ if line.startswith("diff") or line.startswith("index") or line.startswith("---") or line.startswith("+++"):
192
+ continue
193
+ # Skip hunk headers (lines starting with @@)
194
+ if re.match(r'^@@', line):
195
+ continue
196
+ # Skip removal lines (lines starting with '-')
197
+ if line.startswith("-"):
198
+ continue
199
+ # For added lines, remove the '+' prefix
200
+ if line.startswith("+"):
201
+ code_lines.append(line[1:])
202
+ # For context lines (starting with a space), remove the leading space
203
+ elif line.startswith(" "):
204
+ code_lines.append(line[1:])
205
+ else:
206
+ code_lines.append(line)
207
+ return "\n".join(code_lines)
208
+ '''
209
  @tool
210
  def detect_code_smells(code: str) -> str:
211
  """Detects common code smells such as long functions and deeply nested loops.
 
234
  except Exception as e:
235
  return f"Error analyzing code: {str(e)}"
236
  '''
237
+
238
+ @tool
239
+ def detect_code_smells_diff(diff: str) -> str:
240
+ """Detects common code smells such as long functions and deeply nested loops from a code diff.
241
+
242
+ Args:
243
+ diff: A unified diff string representing changes in code to analyze for potential code smells.
244
+
245
+ Returns:
246
+ A string listing detected code smells based on the added and context code lines.
247
+ If no code smells are found, returns a message indicating the code is clean.
248
+ """
249
+ try:
250
+ # Use the helper function to convert the diff into a code string.
251
+ code = diff_to_code(diff)
252
+ tree = ast.parse(code)
253
+ issues = []
254
+
255
+ for node in ast.walk(tree):
256
+ # Detect long functions (more than 20 statements)
257
+ if isinstance(node, ast.FunctionDef) and len(node.body) > 20:
258
+ issues.append(f"Long function detected: {node.name} ({len(node.body)} lines)")
259
+ # Detect deeply nested loops by counting nested For/While nodes
260
+ if isinstance(node, (ast.For, ast.While)):
261
+ nested_loops = sum(isinstance(n, (ast.For, ast.While)) for n in ast.walk(node))
262
+ if nested_loops > 2:
263
+ issues.append(f"Deeply nested loop detected at line {node.lineno}")
264
+
265
+ return "\n".join(issues) if issues else "No code smells detected."
266
+
267
+ except Exception as e:
268
+ return f"Error analyzing code diff: {str(e)}"
269
+
270
+ '''
271
  @tool
272
  def get_file_content(github_url: str, file_path: str) -> str:
273
  """Fetches the content of a specific file from the GitHub repository.
 
290
  return f"Error: {str(e)}"
291
 
292
  '''
293
+
294
+
295
  @tool
296
+ def security_check_code_diff(diff: str) -> str:
297
+ """Analyzes the provided code diff for potential security vulnerabilities.
298
 
299
  Args:
300
+ diff: A unified diff string representing changes in code. The source code to be analyzed for common security issues (e.g., hardcoded secrets, unsafe functions).
301
 
302
  Returns:
303
  A string listing detected potential security vulnerabilities based on common patterns (e.g., hardcoded credentials,
 
305
  """
306
  import re
307
  issues = []
308
+ code = diff_to_code(diff)
309
 
310
  # Check for hardcoded credentials (case-insensitive search)
311
  secret_patterns = [
 
361
  return "No documentation updates detected. Consider reviewing the docs to ensure they reflect the new changes."
362
 
363
  @tool
364
+ def lint_code(diff: str) -> str:
365
  """Analyzes the provided code snippet for style and potential issues using a linter.
366
 
367
  Args:
368
+ diff: The source code to be analyzed.
369
 
370
  Returns:
371
  A string with linting warnings and suggestions for improvement, or a message indicating that no issues were found.
 
373
  # This is a placeholder; you could integrate pylint or flake8 via subprocess or an API.
374
  # For demonstration, we'll simulate a response.
375
  issues = []
376
+
377
+ code = diff_to_code(diff)
378
+
379
  if "print(" in code:
380
  issues.append("Consider removing debug print statements.")
381
  if not issues:
 
405
 
406
  agent = CodeAgent(
407
  model=model,
408
+ tools=[final_answer, get_open_pull_requests, find_todo_comments, get_pr_diff, get_pr_files_changed, detect_code_smells_diff, security_check_code, check_documentation_updates, lint_code, get_pr_diff_for_file ], ## add your tools here (don't remove final answer)
409
  max_steps=6,
410
  verbosity_level=1,
411
  grammar=None,
 
416
  )
417
 
418
 
419
+ CustomGradioUI(agent).launch()