acecalisto3 commited on
Commit
a376c90
·
verified ·
1 Parent(s): b4162d5

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +120 -207
app.py CHANGED
@@ -1,6 +1,6 @@
1
- import gradio as gr
2
  import langchain
3
- import huggingface_hub
4
  import dotenv
5
  import PyYaml
6
  from typing import Optional, Union, List, Dict, Any, Tuple
@@ -10,8 +10,14 @@ import json
10
  import tempfile
11
  from datetime import datetime, timezone
12
  import re
 
 
 
 
 
 
 
13
 
14
- # Add these utility classes based on git-bob's architecture
15
  class TerminalCommand:
16
  @staticmethod
17
  def execute(command: Union[str, List[str]], cwd: Optional[str] = None) -> Tuple[str, str, int]:
@@ -20,7 +26,7 @@ class TerminalCommand:
20
  """
21
  if isinstance(command, str):
22
  command = command.split()
23
-
24
  try:
25
  process = subprocess.Popen(
26
  command,
@@ -32,6 +38,7 @@ class TerminalCommand:
32
  stdout, stderr = process.communicate()
33
  return stdout.strip(), stderr.strip(), process.returncode
34
  except Exception as e:
 
35
  return "", str(e), 1
36
 
37
  class GitUtilities:
@@ -40,263 +47,169 @@ class GitUtilities:
40
 
41
  def clone(self, url: str, branch: str = "main") -> bool:
42
  """Clone a repository"""
43
- _, stderr, code = TerminalCommand.execute(
44
  f"git clone -b {branch} {url} {self.repo_path}"
45
  )
 
 
46
  return code == 0
47
 
48
  def commit(self, message: str) -> bool:
49
  """Create a commit with the given message"""
50
- _, _, code = TerminalCommand.execute(
51
  ["git", "commit", "-m", message],
52
  str(self.repo_path)
53
  )
 
 
54
  return code == 0
55
 
56
  def push(self, remote: str = "origin", branch: str = "main") -> bool:
57
  """Push changes to remote"""
58
- _, _, code = TerminalCommand.execute(
59
  ["git", "push", remote, branch],
60
  str(self.repo_path)
61
  )
 
 
62
  return code == 0
63
 
64
  def create_branch(self, branch_name: str) -> bool:
65
  """Create and checkout a new branch"""
66
- _, _, code = TerminalCommand.execute(
67
  ["git", "checkout", "-b", branch_name],
68
  str(self.repo_path)
69
  )
 
 
70
  return code == 0
71
 
72
- # Enhance the GitHubBot class with new features
73
  class GitHubBot:
74
  def __init__(self, logger: logging.Logger):
75
  self.github_api = None
76
  self.logger = logger
77
  self.ai_provider = None
78
- self.file_types = {
79
- '.py': {'language': 'Python', 'parser': 'python'},
80
- '.js': {'language': 'JavaScript', 'parser': 'javascript'},
81
- '.ts': {'language': 'TypeScript', 'parser': 'typescript'},
82
- '.java': {'language': 'Java', 'parser': 'java'},
83
- '.cpp': {'language': 'C++', 'parser': 'cpp'},
84
- '.go': {'language': 'Go', 'parser': 'go'},
85
- '.rs': {'language': 'Rust', 'parser': 'rust'}
86
- }
87
  self.git = None
88
  self.temp_dir = None
 
89
 
90
- def initialize_api(self, token: str, ai_provider: AIProvider = None):
91
- self.github_api = GitHubAPI(token, self.logger)
92
- self.ai_provider = ai_provider
 
 
93
  self.temp_dir = tempfile.mkdtemp()
94
  self.git = GitUtilities(self.temp_dir)
95
 
96
  def create_pull_request(self, owner: str, repo: str, title: str, body: str, head: str, base: str = "main") -> Dict:
97
- """Create a pull request"""
98
- url = f"{self.github_api.base_url}/repos/{owner}/{repo}/pulls"
99
  data = {
100
  "title": title,
101
  "body": body,
102
  "head": head,
103
  "base": base
104
  }
105
- response = requests.post(url, headers=self.github_api.headers, json=data)
106
- response.raise_for_status()
107
- return response.json()
108
-
109
- def resolve_issue(self, token: str, owner: str, repo: str, issue_number: int, resolution: str, forked_repo: str) -> str:
110
- try:
111
- if not self.ai_provider:
112
- raise ValueError("AI provider not initialized. Please initialize with an AI provider.")
113
-
114
- # Create a unique branch name for this fix
115
- branch_name = f"fix/issue-{issue_number}-{datetime.now().strftime('%Y%m%d-%H%M%S')}"
116
-
117
- # Clone repository and create new branch
118
- if not self.git.clone(forked_repo):
119
- raise Exception("Failed to clone repository")
120
-
121
- if not self.git.create_branch(branch_name):
122
- raise Exception("Failed to create branch")
123
-
124
- # Get repository content and analyze
125
- code_contexts = self._get_main_branch_content(owner, repo)
126
-
127
- # Get issue details
128
- issue_url = f"{self.github_api.base_url}/repos/{owner}/{repo}/issues/{issue_number}"
129
- response = requests.get(issue_url, headers=self.github_api.headers)
130
- response.raise_for_status()
131
- issue = response.json()
132
-
133
- # Determine severity
134
- severity = self._determine_severity(issue)
135
-
136
- # Generate AI solution
137
- context = f"Repository: {owner}/{repo}\nIssue #{issue_number}: {issue['title']}\n\nCode contexts:\n"
138
- for ctx in code_contexts[:3]:
139
- context += f"\nFile: {ctx.file_path}\n```{ctx.language}\n{ctx.content}\n```\n"
140
-
141
- ai_solution = self.ai_provider.generate_solution(context, issue['body'])
142
-
143
- # Create comprehensive resolution document
144
- full_resolution = self._create_resolution_document(
145
- issue_number, severity, resolution, ai_solution
146
- )
147
-
148
- # Save resolution
149
- resolution_path = Path(RESOLUTIONS_DIRECTORY) / f"resolution_{issue_number}_{datetime.now().strftime('%Y%m%d_%H%M%S')}.md"
150
- resolution_path.write_text(full_resolution)
151
-
152
- # Prompt for manual intervention
153
- print("\nAI-generated solution and original resolution have been saved.")
154
- print(f"Please review and apply changes in: {self.temp_dir}")
155
- input("Press Enter when changes are ready to commit...")
156
-
157
- # Commit and push changes
158
- if not self.git.commit(f"Fix #{issue_number}: {issue['title']}\n\n{resolution}"):
159
- raise Exception("Failed to commit changes")
160
-
161
- if not self.git.push("origin", branch_name):
162
- raise Exception("Failed to push changes")
163
-
164
- # Create pull request
165
- pr = self.create_pull_request(
166
- owner=owner,
167
- repo=repo,
168
- title=f"Fix #{issue_number}: {issue['title']}",
169
- body=full_resolution,
170
- head=branch_name
171
- )
172
-
173
- return f"Resolution implemented and PR created: {pr['html_url']}"
174
-
175
- except Exception as e:
176
- error_msg = f"Error resolving issue #{issue_number} in repository {owner}/{repo}: {str(e)}"
177
- self.logger.error(error_msg)
178
- return error_msg
179
-
180
- finally:
181
- # Cleanup
182
- if self.temp_dir and os.path.exists(self.temp_dir):
183
- shutil.rmtree(self.temp_dir)
184
 
185
- def _create_resolution_document(self, issue_number: int, severity: IssueSeverity,
186
- resolution: str, ai_solution: str) -> str:
187
- """Create a comprehensive resolution document"""
188
- return f"""# Resolution for Issue #{issue_number}
189
- ## Severity: {severity.name}
190
- ## Original Resolution
 
 
 
 
 
 
191
  {resolution}
192
- ## AI-Generated Solution
193
- {ai_solution}
194
- ## Implementation Details
195
- - Implementation Date: {datetime.now(timezone.utc).isoformat()}
196
- - Severity Level: {severity.name}
197
- - Resolution Type: {'AI-ASSISTED' if ai_solution else 'MANUAL'}
198
- ## Testing Notes
199
- - [ ] Code changes have been tested locally
200
- - [ ] All tests pass
201
- - [ ] No new issues introduced
202
- ## Review Checklist
203
- - [ ] Code follows project style guidelines
204
- - [ ] Documentation has been updated
205
- - [ ] Changes are properly tested
206
- - [ ] No security vulnerabilities introduced
207
- ## Additional Notes
208
- Please review the changes carefully before merging.
209
- """
210
-
211
- # The following lines should not be part of the _create_resolution_document method
212
- script_file = f"{RESOLUTIONS_DIRECTORY}/resolve_issue_{issue_number}.sh"
213
- with open(script_file, "w") as f:
214
- f.write(bash_script)
215
-
216
- # Make the script executable
217
- os.chmod(script_file, 0o755)
218
 
219
- return f"Bash script generated: {script_file}"
 
 
220
 
221
- except Exception as e:
222
- error_msg = f"Error resolving issue #{issue_number} in repository {owner}/{repo}: {str(e)}"
223
- self.logger.error(error_msg)
224
- return error_msg
 
 
 
 
 
225
 
226
- def create_gradio_interface():
227
  with gr.Blocks() as demo:
228
- gr.Markdown("# Enhanced GitHub Issue Resolver")
229
- gr.Markdown("AI-assisted issue resolution with integrated git operations")
230
-
231
- with gr.Tab("Configuration"):
232
- with gr.Row():
233
- token_input = gr.Textbox(label="GitHub Token", placeholder="Enter your GitHub token")
234
- repo_url_input = gr.Textbox(label="Repository URL", placeholder="Enter the repository URL (owner/repo)")
235
-
236
- with gr.Row():
237
- ai_api_base = gr.Textbox(label="AI API Base URL", placeholder="Enter your AI API base URL")
238
- ai_api_key = gr.Textbox(label="AI API Key", placeholder="Enter your AI API key")
239
 
240
  with gr.Tab("Issue Resolution"):
241
  with gr.Row():
242
- issue_number_input = gr.Number(label="Issue Number", info="Enter the issue number")
243
- severity_indicator = gr.Label(label="Issue Severity")
244
-
245
- resolution_input = gr.Textbox(label="Resolution", placeholder="Describe the resolution for the issue", lines=4)
246
- forked_repo_input = gr.Textbox(label="Forked Repository URL", placeholder="Enter the forked repository URL")
247
-
248
- with gr.Row():
249
- analyze_button = gr.Button("Analyze Issue")
250
- resolve_button = gr.Button("Resolve Issue")
251
 
 
252
  result_output = gr.Textbox(label="Result", interactive=False)
253
 
254
- def on_analyze(token, repo_url, issue_number):
255
- try:
256
- parts = repo_url.split('/')
257
- owner, repo = parts[-2], parts[-1]
258
-
259
- # Initialize bot
260
- bot.initialize_api(token)
261
-
262
- # Get issue details
263
- issue_url = f"{bot.github_api.base_url}/repos/{owner}/{repo}/issues/{issue_number}"
264
- response = requests.get(issue_url, headers=bot.github_api.headers)
265
- response.raise_for_status()
266
- issue = response.json()
267
-
268
- # Determine severity
269
- severity = bot._determine_severity(issue)
270
-
271
- return f"Issue Severity: {severity.name}"
272
- except Exception as e:
273
- return f"Error: {str(e)}"
274
-
275
- def on_resolve(token, repo_url, ai_api_base, ai_api_key, issue_number, resolution, forked_repo):
276
- try:
277
- parts = repo_url.split('/')
278
- owner, repo = parts[-2], parts[-1]
279
-
280
- # Initialize bot with AI provider
281
- ai_provider = CustomAIProvider(ai_api_base, ai_api_key)
282
- bot.initialize_api(token, ai_provider)
283
-
284
- result = bot.resolve_issue(token, owner, repo, int(issue_number), resolution, forked_repo)
285
- return result
286
- except Exception as e:
287
- return f"Error: {str(e)}"
288
-
289
- analyze_button.click(
290
- on_analyze,
291
- inputs=[token_input, repo_url_input, issue_number_input],
292
- outputs=[severity_indicator]
293
- )
294
 
295
- resolve_button.click(
296
- on_resolve,
297
- inputs=[token_input, repo_url_input, ai_api_base, ai_api_key,
298
- issue_number_input, resolution_input, forked_repo_input],
299
- outputs=[result_output]
300
- )
301
 
302
- return demo
 
 
 
1
+ import gradio as gr
2
  import langchain
3
+ import huggingface_hub
4
  import dotenv
5
  import PyYaml
6
  from typing import Optional, Union, List, Dict, Any, Tuple
 
10
  import tempfile
11
  from datetime import datetime, timezone
12
  import re
13
+ import requests
14
+ import logging
15
+ import shutil
16
+
17
+ # Configure logging
18
+ logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s")
19
+ logger = logging.getLogger(__name__)
20
 
 
21
  class TerminalCommand:
22
  @staticmethod
23
  def execute(command: Union[str, List[str]], cwd: Optional[str] = None) -> Tuple[str, str, int]:
 
26
  """
27
  if isinstance(command, str):
28
  command = command.split()
29
+
30
  try:
31
  process = subprocess.Popen(
32
  command,
 
38
  stdout, stderr = process.communicate()
39
  return stdout.strip(), stderr.strip(), process.returncode
40
  except Exception as e:
41
+ logger.error(f"Error executing command {command}: {e}")
42
  return "", str(e), 1
43
 
44
  class GitUtilities:
 
47
 
48
  def clone(self, url: str, branch: str = "main") -> bool:
49
  """Clone a repository"""
50
+ stdout, stderr, code = TerminalCommand.execute(
51
  f"git clone -b {branch} {url} {self.repo_path}"
52
  )
53
+ if code != 0:
54
+ logger.error(f"Git clone failed: {stderr}")
55
  return code == 0
56
 
57
  def commit(self, message: str) -> bool:
58
  """Create a commit with the given message"""
59
+ stdout, stderr, code = TerminalCommand.execute(
60
  ["git", "commit", "-m", message],
61
  str(self.repo_path)
62
  )
63
+ if code != 0:
64
+ logger.error(f"Git commit failed: {stderr}")
65
  return code == 0
66
 
67
  def push(self, remote: str = "origin", branch: str = "main") -> bool:
68
  """Push changes to remote"""
69
+ stdout, stderr, code = TerminalCommand.execute(
70
  ["git", "push", remote, branch],
71
  str(self.repo_path)
72
  )
73
+ if code != 0:
74
+ logger.error(f"Git push failed: {stderr}")
75
  return code == 0
76
 
77
  def create_branch(self, branch_name: str) -> bool:
78
  """Create and checkout a new branch"""
79
+ stdout, stderr, code = TerminalCommand.execute(
80
  ["git", "checkout", "-b", branch_name],
81
  str(self.repo_path)
82
  )
83
+ if code != 0:
84
+ logger.error(f"Git branch creation failed: {stderr}")
85
  return code == 0
86
 
 
87
  class GitHubBot:
88
  def __init__(self, logger: logging.Logger):
89
  self.github_api = None
90
  self.logger = logger
91
  self.ai_provider = None
 
 
 
 
 
 
 
 
 
92
  self.git = None
93
  self.temp_dir = None
94
+ self.base_url = "https://api.github.com"
95
 
96
+ def initialize_api(self, token: str):
97
+ """Initialize the GitHub API with a token."""
98
+ if not token:
99
+ raise ValueError("GitHub token is required.")
100
+ self.github_api = {"Authorization": f"Bearer {token}"}
101
  self.temp_dir = tempfile.mkdtemp()
102
  self.git = GitUtilities(self.temp_dir)
103
 
104
  def create_pull_request(self, owner: str, repo: str, title: str, body: str, head: str, base: str = "main") -> Dict:
105
+ """Create a pull request."""
106
+ url = f"{self.base_url}/repos/{owner}/{repo}/pulls"
107
  data = {
108
  "title": title,
109
  "body": body,
110
  "head": head,
111
  "base": base
112
  }
113
+ try:
114
+ response = requests.post(url, headers=self.github_api, json=data)
115
+ response.raise_for_status()
116
+ return response.json()
117
+ except requests.RequestException as e:
118
+ logger.error(f"Error creating pull request: {e}")
119
+ raise
120
+
121
+ def resolve_issue(self, token: str, owner: str, repo: str, issue_number: int, resolution: str, forked_repo: str) -> str:
122
+ """Resolve a GitHub issue."""
123
+ try:
124
+ self.initialize_api(token)
125
+ branch_name = f"fix/issue-{issue_number}-{datetime.now().strftime('%Y%m%d-%H%M%S')}"
126
+
127
+ # Clone repository
128
+ if not self.git.clone(forked_repo):
129
+ raise Exception("Failed to clone repository")
130
+
131
+ # Create a new branch
132
+ if not self.git.create_branch(branch_name):
133
+ raise Exception("Failed to create branch")
134
+
135
+ # Generate resolution content
136
+ resolution_content = self._create_resolution_document(issue_number, resolution)
137
+
138
+ # Save resolution file
139
+ resolution_path = Path(self.temp_dir) / f"resolution_{issue_number}.md"
140
+ with open(resolution_path, "w") as f:
141
+ f.write(resolution_content)
142
+
143
+ # Commit and push changes
144
+ if not self.git.commit(f"Fix for issue #{issue_number}"):
145
+ raise Exception("Failed to commit changes")
146
+ if not self.git.push("origin", branch_name):
147
+ raise Exception("Failed to push changes")
148
+
149
+ # Create a pull request
150
+ pr = self.create_pull_request(
151
+ owner=owner,
152
+ repo=repo,
153
+ title=f"Fix for issue #{issue_number}",
154
+ body="This PR resolves the reported issue with the following resolution.",
155
+ head=branch_name
156
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
157
 
158
+ return f"Pull request created: {pr['html_url']}"
159
+ except Exception as e:
160
+ logger.error(f"Error resolving issue #{issue_number}: {e}")
161
+ return f"Error: {e}"
162
+ finally:
163
+ if self.temp_dir and os.path.exists(self.temp_dir):
164
+ shutil.rmtree(self.temp_dir)
165
+
166
+ def _create_resolution_document(self, issue_number: int, resolution: str) -> str:
167
+ """Create a resolution document."""
168
+ return f"""# Resolution for Issue #{issue_number}
169
+ ## Resolution Details
170
  {resolution}
171
+ ## Metadata
172
+ - Date: {datetime.now(timezone.utc).isoformat()}
173
+ - Resolved By: Automated System
174
+ """
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
175
 
176
+ def create_gradio_interface():
177
+ """Create the Gradio interface."""
178
+ bot = GitHubBot(logger)
179
 
180
+ def on_resolve(token, repo_url, issue_number, resolution, forked_repo):
181
+ try:
182
+ parts = repo_url.strip("/").split("/")
183
+ owner, repo = parts[-2], parts[-1]
184
+ result = bot.resolve_issue(token, owner, repo, int(issue_number), resolution, forked_repo)
185
+ return result
186
+ except Exception as e:
187
+ logger.error(f"Error in issue resolution: {e}")
188
+ return f"Error: {e}"
189
 
 
190
  with gr.Blocks() as demo:
191
+ gr.Markdown("# GitHub Issue Resolver")
192
+ gr.Markdown("Resolve GitHub issues with AI assistance and Git integration.")
 
 
 
 
 
 
 
 
 
193
 
194
  with gr.Tab("Issue Resolution"):
195
  with gr.Row():
196
+ token_input = gr.Textbox(label="GitHub Token", placeholder="Enter your GitHub token")
197
+ repo_url_input = gr.Textbox(label="Repository URL", placeholder="Enter the repository URL")
198
+ issue_number_input = gr.Number(label="Issue Number", precision=0)
199
+ resolution_input = gr.Textbox(label="Resolution", placeholder="Describe the resolution for the issue")
200
+ forked_repo_input = gr.Textbox(label="Forked Repo URL", placeholder="Enter the forked repository URL")
 
 
 
 
201
 
202
+ resolve_button = gr.Button("Resolve Issue")
203
  result_output = gr.Textbox(label="Result", interactive=False)
204
 
205
+ resolve_button.click(
206
+ fn=on_resolve,
207
+ inputs=[token_input, repo_url_input, issue_number_input, resolution_input, forked_repo_input],
208
+ outputs=[result_output]
209
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
210
 
211
+ return demo
 
 
 
 
 
212
 
213
+ if __name__ == "__main__":
214
+ demo = create_gradio_interface()
215
+ demo.launch(server_name="0.0.0.0", server_port=7860)