acecalisto3 commited on
Commit
c8af669
·
verified ·
1 Parent(s): 38ad742

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +148 -171
app.py CHANGED
@@ -1,5 +1,4 @@
1
  import os
2
- import sys
3
  import shutil
4
  import logging
5
  import time
@@ -7,10 +6,15 @@ import requests
7
  import gradio as gr
8
  import atexit
9
  import subprocess
 
10
  import webbrowser
11
  from datetime import datetime
12
  from typing import List, Dict, Any
13
- from urllib.parse import urlparse, quote
 
 
 
 
14
 
15
  # Constants
16
  INPUT_DIRECTORY = 'input'
@@ -40,197 +44,173 @@ def initialize_environment():
40
 
41
  # GitHub API handler
42
  class GitHubAPI:
43
- def __init__(self, token: str, logger: logging.Logger):
44
  self.token = token
45
- self.logger = logger
46
  self.headers = {
47
  'Authorization': f'token {token}',
48
  'Accept': 'application/vnd.github.v3+json'
49
  }
50
  self.base_url = "https://api.github.com"
51
 
52
- def _check_rate_limit(self) -> bool:
53
- try:
54
- response = requests.get(f"{self.base_url}/rate_limit", headers=self.headers)
55
- response.raise_for_status()
56
- limits = response.json()
57
- remaining = limits['resources']['core']['remaining']
58
- reset_time = limits['resources']['core']['reset']
59
-
60
- if remaining < 10:
61
- wait_time = max(0, reset_time - int(time.time()))
62
- if wait_time > 0:
63
- self.logger.warning(f"Rate limit nearly exceeded. Waiting {wait_time} seconds before retrying...")
64
- time.sleep(wait_time)
65
- return False
66
- return True
67
- except requests.exceptions.RequestException as e:
68
- self.logger.error(f"Error checking rate limit: {str(e)}. Retrying...")
69
- return True
70
-
71
  def get_repository(self, owner: str, repo: str) -> Dict:
72
  try:
73
  response = requests.get(f"{self.base_url}/repos/{owner}/{repo}", headers=self.headers)
74
  response.raise_for_status()
75
  return response.json()
76
  except requests.HTTPError as e:
77
- self.logger.error(f"HTTP error getting repository info for {owner}/{repo}: {str(e)}. Please check the repository details.")
78
  raise
79
  except Exception as e:
80
- self.logger.error(f"Error getting repository info: {str(e)}")
81
  raise
82
 
83
  def get_issues(self, owner: str, repo: str, state: str = 'open') -> List[Dict]:
84
- if not self._check_rate_limit():
85
- return []
86
-
87
  try:
88
  response = requests.get(f"{self.base_url}/repos/{owner}/{repo}/issues", headers=self.headers, params={'state': state})
89
  response.raise_for_status()
90
  issues = response.json()
91
  return [issue for issue in issues if 'pull_request' not in issue]
92
  except Exception as e:
93
- self.logger.error(f"Error fetching issues for repository {owner}/{repo}: {str(e)}. Please verify the repository and token.")
94
  return []
95
 
96
- # GitHub Bot
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
97
  class GitHubBot:
98
  def __init__(self, logger: logging.Logger):
99
  self.github_api = None
100
  self.logger = logger
 
 
 
 
 
 
 
 
 
 
101
 
102
- def initialize_api(self, token: str):
103
- self.github_api = GitHubAPI(token, self.logger)
 
104
 
105
- def fetch_issues(self, token: str, owner: str, repo: str) -> List[Dict]:
106
- try:
107
- self.initialize_api(token)
108
- return self.github_api.get_issues(owner, repo)
109
- except Exception as e:
110
- self.logger.error(f"Error fetching issues for repository {owner}/{repo}: {str(e)}")
111
- return []
 
112
 
113
  def resolve_issue(self, token: str, owner: str, repo: str, issue_number: int, resolution: str, forked_repo: str) -> str:
114
  try:
115
- self.initialize_api(token)
116
- self.github_api.get_repository(owner, repo)
117
 
118
- # Create resolution file
119
- timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
120
- resolution_file = f"{RESOLUTIONS_DIRECTORY}/resolution_{issue_number}_{timestamp}.md"
 
 
121
 
122
- with open(resolution_file, "w") as f:
123
- f.write(f"# Resolution for Issue #{issue_number}\n\n{resolution}")
124
 
125
- # Validate forked_repo before cloning
126
- if not forked_repo:
127
- raise ValueError("Forked repository URL cannot be empty.")
128
 
129
- # Clone the forked repo
130
- subprocess.run(['git', 'clone', forked_repo, '/tmp/' + forked_repo.split('/')[-1]], check=True)
 
131
 
132
- # Change to the cloned directory
133
- os.chdir('/tmp/' + forked_repo.split('/')[-1])
 
 
 
134
 
135
- # Assuming manual intervention now
136
- input("Apply the fix manually and stage the changes (press ENTER)? ")
137
 
138
- # Commit and push the modifications
139
- subprocess.run(['git', 'add', '.'], check=True)
140
- subprocess.run(['git', 'commit', '-m', f"Resolved issue #{issue_number} ({quote(resolution)})"], check=True)
141
- subprocess.run(['git', 'push', 'origin', 'HEAD'], check=True)
142
 
143
- # Open Pull Request page
144
- webbrowser.open(f'https://github.com/{forked_repo.split("/")[-1]}/compare/master...{owner}:{forked_repo.split("/")[-1]}_resolved_issue_{issue_number}')
145
 
146
- return f"Resolution saved: {resolution_file}"
147
 
148
  except Exception as e:
149
  error_msg = f"Error resolving issue #{issue_number} in repository {owner}/{repo}: {str(e)}"
150
  self.logger.error(error_msg)
151
  return error_msg
152
 
153
- def suggest_automated_fixes(self, issue_title: str) -> str:
154
- if "missing README" in issue_title.lower():
155
- return "Consider adding a README.md file to provide project documentation."
156
- return "No automated fix available for this issue."
157
-
158
- def handle_issue_selection(token, owner, repo, issue_number, resolution, forked_repo):
159
- bot = GitHubBot(logger)
160
- result = bot.resolve_issue(token, owner, repo, issue_number, resolution, forked_repo)
161
- return result
162
-
163
- def extract_info_from_url(url: str) -> Dict[str, Any]:
164
- info = {}
165
- try:
166
- response = requests.get(url)
167
- response.raise_for_status()
168
- info['status_code'] = response.status_code
169
- info['headers'] = dict(response.headers)
170
- info['content'] = response.text[:500] # Limit content to first 500 characters for brevity
171
-
172
- parsed_url = urlparse(url)
173
- if 'github.com' in parsed_url.netloc:
174
- parts = parsed_url.path.split('/')
175
- if len(parts) > 2:
176
- owner = parts[1]
177
- repo = parts[2]
178
- issues = bot.fetch_issues(github_token, owner, repo)
179
- info['issues'] = issues
180
- elif 'huggingface.co' in parsed_url.netloc:
181
- # Add Hugging Face specific handling if needed
182
- pass
183
-
184
- except requests.HTTPError as e:
185
- info['error'] = f"HTTP error: {str(e)}"
186
- except Exception as e:
187
- info['error'] = f"Error: {str(e)}"
188
- return info
189
-
190
- # Initialize GitHubBot globally
191
- logger = initialize_logger() # Initialize logger before creating the bot
192
- bot = GitHubBot(logger)
193
-
194
- # Define missing functions with validation
195
- def fetch_issues(token, repo_url):
196
- try:
197
- parts = repo_url.split('/')
198
- if len(parts) < 2:
199
- raise ValueError("Repository URL is not in the correct format. Expected format: 'owner/repo'.")
200
-
201
- owner, repo = parts[-2], parts[-1]
202
- issues = bot.fetch_issues(token, owner, repo)
203
- return issues
204
- except Exception as e:
205
- return str(e)
206
-
207
- def resolve_issue(token, repo_url, issue_number, resolution, forked_repo_url):
208
- try:
209
- parts = repo_url.split('/')
210
- if len(parts) < 2:
211
- raise ValueError("Repository URL is not in the correct format. Expected format: 'owner/repo'.")
212
-
213
- owner, repo = parts[-2], parts[-1]
214
- result = bot.resolve_issue(token, owner, repo, issue_number, resolution, forked_repo_url)
215
- return result
216
- except Exception as e:
217
- return str(e)
218
-
219
- def extract_info(url):
220
- try:
221
- info = extract_info_from_url(url)
222
- return info
223
- except Exception as e:
224
- return str(e)
225
-
226
  def create_gradio_interface():
227
  with gr.Blocks() as demo:
228
- gr.Markdown("# GitHub Issue Resolver")
229
- gr.Markdown("This application allows you to fetch and resolve GitHub issues efficiently.")
230
-
231
  with gr.Row():
232
  token_input = gr.Textbox(label="GitHub Token", placeholder="Enter your GitHub token")
233
- repo_url_input = gr.Textbox(label="Repository URL", placeholder=" Enter the repository URL (owner/repo)")
 
 
 
 
234
 
235
  with gr.Row():
236
  issue_number_input = gr.Number(label="Issue Number", info="Enter the issue number")
@@ -241,42 +221,39 @@ def create_gradio_interface():
241
  submit_button = gr.Button("Resolve Issue")
242
  result_output = gr.Textbox(label="Result", interactive=False)
243
 
244
- def on_submit(token, repo_url, issue_number, resolution, forked_repo):
245
- issues = fetch_issues(token, repo_url)
246
- if issues:
247
- automated_fix = bot.suggest_automated_fixes(issues[0]['title'])
248
- resolution += f"\n\n**Automated Suggestion:** {automated_fix}"
249
- result = resolve_issue(token, repo_url, issue_number, resolution, forked_repo)
 
 
 
 
 
 
250
  return result
251
- return "No issues found or an error occurred."
 
252
 
253
- submit_button.click(on_submit, inputs=[token_input, repo_url_input, issue_number_input, resolution_input, forked_repo_input], outputs=result_output)
 
 
 
 
 
254
 
255
  return demo
256
 
257
- # Cleanup function
258
- def cleanup():
259
- try:
260
- temp_dirs = [REPOS_DIRECTORY]
261
- for dir_name in temp_dirs:
262
- if os.path.exists(dir_name):
263
- shutil.rmtree(dir_name)
264
- logging.shutdown()
265
- except Exception as e:
266
- print(f"Error during cleanup: {str(e)}")
267
-
268
- def main():
269
- # Initialize environment and logger
270
- initialize_environment()
271
- global logger
272
- logger = initialize_logger()
273
-
274
- # Register cleanup handlers
275
- atexit.register(cleanup)
276
-
277
- # Create Gradio interface
278
- demo = create_gradio_interface()
279
- demo.launch()
280
 
 
281
  if __name__ == "__main__":
282
- main()
 
 
1
  import os
 
2
  import shutil
3
  import logging
4
  import time
 
6
  import gradio as gr
7
  import atexit
8
  import subprocess
9
+ from urllib.parse import urlparse, quote
10
  import webbrowser
11
  from datetime import datetime
12
  from typing import List, Dict, Any
13
+ from abc import ABC, abstractmethod
14
+ from enum import Enum
15
+ from dataclasses import dataclass
16
+ from concurrent.futures import ThreadPoolExecutor
17
+ import base64
18
 
19
  # Constants
20
  INPUT_DIRECTORY = 'input'
 
44
 
45
  # GitHub API handler
46
  class GitHubAPI:
47
+ def __init__(self, token: str):
48
  self.token = token
 
49
  self.headers = {
50
  'Authorization': f'token {token}',
51
  'Accept': 'application/vnd.github.v3+json'
52
  }
53
  self.base_url = "https://api.github.com"
54
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
55
  def get_repository(self, owner: str, repo: str) -> Dict:
56
  try:
57
  response = requests.get(f"{self.base_url}/repos/{owner}/{repo}", headers=self.headers)
58
  response.raise_for_status()
59
  return response.json()
60
  except requests.HTTPError as e:
61
+ logger.error(f"HTTP error getting repository info for {owner}/{repo}: {str(e)}")
62
  raise
63
  except Exception as e:
64
+ logger.error(f"Error getting repository info: {str(e)}")
65
  raise
66
 
67
  def get_issues(self, owner: str, repo: str, state: str = 'open') -> List[Dict]:
 
 
 
68
  try:
69
  response = requests.get(f"{self.base_url}/repos/{owner}/{repo}/issues", headers=self.headers, params={'state': state})
70
  response.raise_for_status()
71
  issues = response.json()
72
  return [issue for issue in issues if 'pull_request' not in issue]
73
  except Exception as e:
74
+ logger.error(f"Error fetching issues for repository {owner}/{repo}: {str(e)}")
75
  return []
76
 
77
+ # Enum for Issue Severity
78
+ class IssueSeverity(Enum):
79
+ CRITICAL = 5
80
+ HIGH = 4
81
+ MEDIUM = 3
82
+ LOW = 2
83
+ TRIVIAL = 1
84
+
85
+ @dataclass
86
+ class CodeContext:
87
+ file_path: str
88
+ content: str
89
+ language: str
90
+
91
+ class AIProvider(ABC):
92
+ @abstractmethod
93
+ def analyze_code(self, content: str) -> str:
94
+ pass
95
+
96
+ @abstractmethod
97
+ def generate_solution(self, context: str, issue: str) -> str:
98
+ pass
99
+
100
+ class CustomAIProvider(AIProvider):
101
+ def __init__(self, api_base: str, api_key: str, model: str = "gpt-3.5-turbo"):
102
+ self.api_base = api_base
103
+ self.api_key = api_key
104
+ self.model = model
105
+ self.headers = {
106
+ "Authorization": f"Bearer {api_key}",
107
+ "Content-Type": "application/json"
108
+ }
109
+
110
+ def analyze_code(self, content: str) -> str:
111
+ payload = {
112
+ "model": self.model,
113
+ "messages": [{"role": "user", "content": f"Analyze this code:\n{content}"}]
114
+ }
115
+ response = requests.post(f"{self.api_base}/v1/chat/completions",
116
+ headers = self.headers,
117
+ json=payload)
118
+ return response.json()["choices"][0]["message"]["content"]
119
+
120
+ def generate_solution(self, context: str, issue: str) -> str:
121
+ payload = {
122
+ "model": self.model,
123
+ "messages": [{"role": "user", "content": f"Context:\n{context}\nIssue:\n{issue}\nGenerate solution:"}]
124
+ }
125
+ response = requests.post(f"{self.api_base}/v1/chat/completions",
126
+ headers=self.headers,
127
+ json=payload)
128
+ return response.json()["choices"][0]["message"]["content"]
129
+
130
  class GitHubBot:
131
  def __init__(self, logger: logging.Logger):
132
  self.github_api = None
133
  self.logger = logger
134
+ self.ai_provider = None
135
+ self.file_types = {
136
+ '.py': {'language': 'Python', 'parser': 'python'},
137
+ '.js': {'language': 'JavaScript', 'parser': 'javascript'},
138
+ '.ts': {'language': 'TypeScript', 'parser': 'typescript'},
139
+ '.java': {'language': 'Java', 'parser': 'java'},
140
+ '.cpp': {'language': 'C++', 'parser': 'cpp'},
141
+ '.go': {'language': 'Go', 'parser': 'go'},
142
+ '.rs': {'language': 'Rust', 'parser': 'rust'}
143
+ }
144
 
145
+ def initialize_api(self, token: str, ai_provider: AIProvider = None):
146
+ self.github_api = GitHubAPI(token)
147
+ self.ai_provider = ai_provider
148
 
149
+ def crawl_issues(self, owner: str, repo: str) -> List[Dict]:
150
+ issues = self.github_api.get_issues(owner, repo)
151
+ return issues
152
+
153
+ def replicate_error(self, issue: Dict) -> str:
154
+ # Simulate the error in a controlled environment
155
+ # This is a placeholder for actual error replication logic
156
+ return f"Simulated error for issue: {issue['title']}"
157
 
158
  def resolve_issue(self, token: str, owner: str, repo: str, issue_number: int, resolution: str, forked_repo: str) -> str:
159
  try:
160
+ if not self.ai_provider:
161
+ raise ValueError("AI provider not initialized. Please initialize with an AI provider.")
162
 
163
+ # Get issue details
164
+ issue_url = f"{self.github_api.base_url}/repos/{owner}/{repo}/issues/{issue_number}"
165
+ response = requests.get(issue_url, headers=self.github_api.headers)
166
+ response.raise_for_status()
167
+ issue = response.json()
168
 
169
+ # Replicate the error
170
+ error_replication = self.replicate_error(issue)
171
 
172
+ # Generate AI solution
173
+ context = f"Repository: {owner}/{repo}\nIssue #{issue_number}: {issue['title']}\n\nError Replication:\n{error_replication}\n"
174
+ ai_solution = self.ai_provider.generate_solution(context, issue['body'])
175
 
176
+ # Create bash script for resolution
177
+ bash_script = f"""#!/bin/bash
178
+ # Bash script to resolve issue #{issue_number} in {owner}/{repo}
179
 
180
+ echo "Starting resolution for issue #{issue_number}..."
181
+ # Add commands to resolve the issue here
182
+ # Example: git clone {forked_repo}
183
+ # Example: cd {repo}
184
+ # Example: {ai_solution}
185
 
186
+ echo "Resolution complete."
187
+ """
188
 
189
+ script_file = f"{RESOLUTIONS_DIRECTORY}/resolve_issue_{issue_number}.sh"
190
+ with open(script_file, "w") as f:
191
+ f.write(bash_script)
 
192
 
193
+ # Make the script executable
194
+ os.chmod(script_file, 0o755)
195
 
196
+ return f"Bash script generated: {script_file}"
197
 
198
  except Exception as e:
199
  error_msg = f"Error resolving issue #{issue_number} in repository {owner}/{repo}: {str(e)}"
200
  self.logger.error(error_msg)
201
  return error_msg
202
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
203
  def create_gradio_interface():
204
  with gr.Blocks() as demo:
205
+ gr.Markdown("# GitHub Issue Resolver with AI Assistant")
206
+
 
207
  with gr.Row():
208
  token_input = gr.Textbox(label="GitHub Token", placeholder="Enter your GitHub token")
209
+ repo_url_input = gr.Textbox(label="Repository URL", placeholder="Enter the repository URL (owner/repo)")
210
+
211
+ with gr.Row():
212
+ ai_api_base = gr.Textbox(label="AI API Base URL", placeholder="Enter your AI API base URL")
213
+ ai_api_key = gr.Textbox(label="AI API Key", placeholder="Enter your AI API key")
214
 
215
  with gr.Row():
216
  issue_number_input = gr.Number(label="Issue Number", info="Enter the issue number")
 
221
  submit_button = gr.Button("Resolve Issue")
222
  result_output = gr.Textbox(label="Result", interactive=False)
223
 
224
+ def on_submit(token, repo_url, ai_api_base, ai_api_key, issue_number, resolution, forked_repo):
225
+ try:
226
+ parts = repo_url.split('/')
227
+ owner, repo = parts[-2], parts[-1]
228
+
229
+ # Initialize bot with AI provider
230
+ ai_provider = CustomAIProvider(ai_api_base, ai_api_key)
231
+ bot.initialize_api(token, ai_provider)
232
+
233
+ # Crawl issues and resolve
234
+ issues = bot.crawl_issues(owner, repo)
235
+ result = bot.resolve_issue(token, owner, repo, int(issue_number), resolution, forked_repo)
236
  return result
237
+ except Exception as e:
238
+ return f"Error: {str(e)}"
239
 
240
+ submit_button.click(
241
+ on_submit,
242
+ inputs=[token_input, repo_url_input, ai_api_base, ai_api_key,
243
+ issue_number_input, resolution_input, forked_repo_input],
244
+ outputs=result_output
245
+ )
246
 
247
  return demo
248
 
249
+ # Initialize the logger and environment
250
+ logger = initialize_logger()
251
+ initialize_environment()
252
+
253
+ # Create the GitHub bot instance
254
+ bot = GitHubBot(logger)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
255
 
256
+ # Launch the Gradio interface
257
  if __name__ == "__main__":
258
+ demo = create_gradio_interface()
259
+ demo.launch()