acecalisto3 commited on
Commit
56c4359
·
verified ·
1 Parent(s): dbf2fcd

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +465 -146
app.py CHANGED
@@ -1,161 +1,480 @@
1
- from flask import Flask, render_template, request, jsonify
2
- import requests
3
- import os
4
  import logging
 
 
5
  from datetime import datetime
6
- from transformers import pipeline
 
 
 
 
 
 
7
  import spaces
8
 
9
- device = "cuda" if torch.cuda.is_available() else "cpu"
10
-
11
- # Load the model and tokenizer
12
- model_name = "mixtral/instruct-v0.1"
13
- tokenizer = AutoTokenizer.from_pretrained(model_name)
14
- model = AutoModelForCausalLM.from_pretrained(model_name).to(device)
15
-
16
- def stream_chat(
17
- message: str,
18
- history: list,
19
- system_prompt: str,
20
- temperature: float = 0.8,
21
- max_new_tokens: int = 1024,
22
- top_p: float = 1.0,
23
- top_k: int = 20,
24
- penalty: float = 1.2,
25
- ):
26
- conversation = [
27
- {"role": "system", "content": system_prompt}
28
- ]
29
- for prompt, answer in history:
30
- conversation.extend([
31
- {"role": "user", "content": prompt},
32
- {"role": "assistant", "content": answer},
33
- ])
34
-
35
- conversation.append({"role": "user", "content": message})
36
-
37
- input_ids = tokenizer.apply_chat_template(conversation, add_generation_prompt=True, return_tensors="pt").to(device)
38
-
39
- streamer = TextStreamer(tokenizer, timeout=60.0, skip_prompt=True, skip_special_tokens=True)
40
-
41
- generate_kwargs = dict(
42
- input_ids=input_ids,
43
- max_new_tokens=max_new_tokens,
44
- do_sample=temperature != 0,
45
- top_p=top_p,
46
- top_k=top_k,
47
- temperature=temperature,
48
- eos_token_id=[128001, 128008, 128009],
49
- streamer=streamer,
50
  )
 
51
 
52
- output = model.generate(**generate_kwargs)
53
- return tokenizer.decode(output[0], skip_special_tokens=True)
 
 
 
54
 
55
- # Initialize the pipeline
56
- pipe = pipeline("text-generation", model="mistralai/Mixtral-8x7B-Instruct-v0.1")
57
 
58
- app = Flask(__name__)
 
 
 
 
 
 
 
 
59
 
60
- # Configure logging
61
- LOGS_DIRECTORY = 'logs'
62
- if not os.path.exists(LOGS_DIRECTORY):
63
- os.makedirs(LOGS_DIRECTORY)
64
-
65
- logging.basicConfig(
66
- level=logging.INFO,
67
- format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
68
- handlers=[
69
- logging.FileHandler(f"{LOGS_DIRECTORY}/github_bot_{datetime.now().strftime('%Y%m%d_%H%M%S')}.log"),
70
- logging.StreamHandler()
71
- ]
72
- )
73
- logger = logging.getLogger(__name__)
74
-
75
- def stream_chat(message: str, history: list, system_prompt: str):
76
- conversation = [{"role": "system", "content": system_prompt}]
77
- for prompt, answer in history:
78
- conversation.extend([
79
- {"role": "user", "content": prompt},
80
- {"role": "assistant", "content": answer},
81
- ])
82
- conversation.append({"role": "user", "content": message})
83
- return pipe(conversation)
84
-
85
- @app.route('/')
86
- def index():
87
- return render_template('index.html')
88
-
89
- @app.route('/fetch_issues', methods=['POST'])
90
- def fetch_issues():
91
- github_token = request.form.get('github_token')
92
- repo_url = request.form.get('repo_url')
93
-
94
- if not github_token or not repo_url:
95
- logger.error('GitHub Token and Repository URL are required')
96
- return jsonify({'error': 'GitHub Token and Repository URL are required'}), 400
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
97
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
98
  try:
99
- repo_owner, repo_name = repo_url.split('/')[-2:]
100
- headers = {'Authorization': f'token {github_token}'}
101
- response = requests.get(f'https://api.github.com/repos/{repo_owner}/{repo_name}/issues', headers=headers)
102
  response.raise_for_status()
103
- issues = response.json()
104
- logger.info(f'Successfully fetched issues for {repo_owner}/{repo_name}')
105
- return jsonify(issues)
106
- except requests.exceptions.RequestException as e:
107
- logger.error(f'Error fetching issues: {str(e)}')
108
- return jsonify({'error': str(e)}), 500
109
-
110
- @app.route('/resolve_issue', methods=['POST'])
111
- def resolve_issue():
112
- github_token = request.form.get('github_token')
113
- issue_number = request.form.get('issue_number')
114
- resolution = request.form.get('resolution')
115
- repo_url = request.form.get('repo_url')
116
- forked_repo_url = request.form.get('forked_repo_url')
117
-
118
- if not github_token or not issue_number or not resolution or not repo_url:
119
- logger.error('GitHub Token, Issue Number, Resolution, and Repository URL are required')
120
- return jsonify({'error': 'GitHub Token, Issue Number, Resolution, and Repository URL are required'}), 400
 
 
 
 
 
 
121
 
 
 
122
  try:
123
- repo_owner, repo_name = repo_url.split('/')[-2:]
124
- headers = {'Authorization': f'token {github_token}'}
125
-
126
- # Create a comment with the resolution in the issue
127
- comment_data = {"body": resolution}
128
- requests.post(f'https://api.github.com/repos/{repo_owner}/{repo_name}/issues/{issue_number}/comments', headers=headers, json=comment_data)
129
-
130
- if forked_repo_url:
131
- # Extract the forked repo's owner and name
132
- forked_repo_owner, forked_repo_name = forked_repo_url.split('/')[-2:]
133
-
134
- # Close the issue in your forked repository
135
- close_issue_data = {"state": "closed"}
136
- requests.patch(f'https://api.github.com/repos/{forked_repo_owner}/{forked_repo_name}/issues/{issue_number}', headers=headers, json=close_issue_data)
137
-
138
- logger.info(f'Issue #{issue_number} resolved successfully')
139
- return jsonify({'message': f'Issue #{issue_number} resolved successfully'})
140
- except requests.exceptions.RequestException as e:
141
- logger.error(f'Error resolving issue: {str(e)}')
142
- return jsonify({'error': str(e)}), 500
143
-
144
- @app.route('/extract_info', methods=['POST'])
145
- def extract_info():
146
- url = request.form.get('url')
147
- if not url:
148
- logger.error('URL is required')
149
- return jsonify({'error': 'URL is required'}), 400
150
  try:
151
- headers = {"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36"}
152
- response = requests.get(url, headers=headers)
153
- response.raise_for_status()
154
- logger.info(f'Successfully extracted info from {url}')
155
- return jsonify({"status": "success", "response": response.text})
156
- except requests.exceptions.RequestException as e:
157
- logger.error(f'Error extracting info: {str(e)}')
158
- return jsonify({'error': str(e)}), 500
159
-
160
- if __name__ == '__main__':
161
- app.run(debug=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import sys
2
+ import signal
3
+ import shutil
4
  import logging
5
+ import time
6
+ import os
7
  from datetime import datetime
8
+ from typing import List, Dict, Any
9
+ import requests
10
+ import gradio as gr
11
+ import atexit
12
+ import subprocess
13
+ from urllib.parse import urlparse, quote
14
+ import webbrowser
15
  import spaces
16
 
17
+ # Constants
18
+ INPUT_DIRECTORY = 'input'
19
+ OUTPUT_DIRECTORY = 'output'
20
+ LOGS_DIRECTORY = 'logs'
21
+ RESOLUTIONS_DIRECTORY = 'resolutions'
22
+ REPOS_DIRECTORY = 'repos'
23
+
24
+ # Set up logging
25
+ def initialize_logger() -> logging.Logger:
26
+ log_file = f"{LOGS_DIRECTORY}/github_bot_{datetime.now().strftime('%Y%m%d_%H%M%S')}.log"
27
+ logging.basicConfig(
28
+ level=logging.INFO,
29
+ format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
30
+ handlers=[
31
+ logging.FileHandler(log_file),
32
+ logging.StreamHandler()
33
+ ]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
34
  )
35
+ return logging.getLogger(__name__)
36
 
37
+ # Initialize environment and logger
38
+ def initialize_environment():
39
+ directories = [LOGS_DIRECTORY, RESOLUTIONS_DIRECTORY, REPOS_DIRECTORY, INPUT_DIRECTORY, OUTPUT_DIRECTORY]
40
+ for directory in directories:
41
+ os.makedirs(directory, exist_ok=True)
42
 
43
+ initialize_environment()
44
+ logger = initialize_logger() # Initialize logger globally
45
 
46
+ # GitHub API handler
47
+ class GitHubAPI:
48
+ def __init__(self, token: str):
49
+ self.token = token
50
+ self.headers = {
51
+ 'Authorization': f'token {token}',
52
+ 'Accept': 'application/vnd.github.v3+json'
53
+ }
54
+ self.base_url = "https://api.github.com"
55
 
56
+ def _check_rate_limit(self) -> bool:
57
+ try:
58
+ response = requests.get(f"{self.base_url}/rate_limit", headers=self.headers)
59
+ response.raise_for_status()
60
+ limits = response.json()
61
+ remaining = limits['resources']['core']['remaining']
62
+ reset_time = limits['resources']['core']['reset']
63
+
64
+ if remaining < 10:
65
+ wait_time = max(0, reset_time - int(time.time()))
66
+ if wait_time > 0:
67
+ logger.warning(f"Rate limit nearly exceeded. Waiting {wait_time} seconds before retrying...")
68
+ time.sleep(wait_time)
69
+ return False
70
+ return True
71
+ except requests.exceptions.RequestException as e:
72
+ logger.error(f"Error checking rate limit: {str(e)}. Retrying...")
73
+ return True
74
+
75
+ def get_repository(self, owner: str, repo: str) -> Dict:
76
+ try:
77
+ response = requests.get(f"{self.base_url}/repos/{owner}/{repo}", headers=self.headers)
78
+ response.raise_for_status()
79
+ return response.json()
80
+ except requests.HTTPError as e:
81
+ logger.error(f"HTTP error getting repository info for {owner}/{repo}: {str(e)}. Please check the repository details.")
82
+ raise
83
+ except Exception as e:
84
+ logger.error(f"Error getting repository info: {str(e)}")
85
+ raise
86
+
87
+ def get_issues(self, owner: str, repo: str, state: str = 'open') -> List[Dict]:
88
+ if not self._check_rate_limit():
89
+ return []
90
+
91
+ try:
92
+ response = requests.get(f"{self.base_url}/repos/{owner}/{repo}/issues", headers=self.headers, params={'state': state})
93
+ response.raise_for_status()
94
+ issues = response.json()
95
+ return [issue for issue in issues if 'pull_request' not in issue]
96
+ except Exception as e:
97
+ logger.error(f"Error fetching issues for repository {owner}/{repo}: {str(e)}. Please verify the repository and token.")
98
+ return []
99
+
100
+ # GitHub Bot
101
+ class GitHubBot:
102
+ def __init__(self):
103
+ self.github_api = None
104
+
105
+ def initialize_api(self, token: str):
106
+ self.github_api = GitHubAPI(token)
107
+
108
+ def fetch_issues(self, token: str, owner: str, repo: str) -> List[Dict]:
109
+ try:
110
+ self.initialize_api(token)
111
+ return self.github_api.get_issues(owner, repo)
112
+ except Exception as e:
113
+ logger.error(f"Error fetching issues for repository {owner}/{repo}: {str(e)}")
114
+ return []
115
+
116
+ def resolve_issue(self, token: str, owner: str, repo: str, issue_number: int, resolution: str, forked_repo: str) -> str:
117
+ try:
118
+ self.initialize_api(token)
119
+ self.github_api.get_repository(owner, repo)
120
+
121
+ # Create resolution file
122
+ timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
123
+ resolution_file = f"{RESOLUTIONS_DIRECTORY}/resolution_{issue_number}_{timestamp}.md"
124
+
125
+ with open(resolution_file, "w") as f:
126
+ f.write(f"# Resolution for Issue #{issue_number}\n\n{resolution}")
127
+
128
+ # Clone the forked repo
129
+ subprocess.run(['git', 'clone', forked_repo, '/tmp/' + forked_repo.split('/')[-1]], check=True)
130
+
131
+ # Change to the cloned directory
132
+ os.chdir('/tmp/' + forked_repo.split('/')[-1])
133
+
134
+ # Assuming manual intervention now
135
+ input("Apply the fix manually and stage the changes (press ENTER)? ")
136
+
137
+ # Commit and push the modifications
138
+ subprocess.run(['git', 'add', '.'], check=True)
139
+ subprocess.run(['git', 'commit', '-m', f"Resolved issue #{issue_number} ({quote(resolution)})"], check=True)
140
+ subprocess.run(['git', 'push', 'origin', 'HEAD'], check=True)
141
+
142
+ # Open Pull Request page
143
+ webbrowser.open(f'https://github.com/{forked_repo.split("/")[-1]}/compare/master...{owner}:{forked_repo.split("/")[-1]}_resolved_issue_{issue_number}')
144
 
145
+ return f"Resolution saved: {resolution_file}"
146
+
147
+ except Exception as e:
148
+ error_msg = f"Error resolving issue #{issue_number} in repository {owner}/{repo}: {str(e)}"
149
+ logger.error(error_msg)
150
+ return error_msg
151
+
152
+ def handle_issue_selection(token, owner, repo, issue_number, resolution, forked_repo):
153
+ bot = GitHubBot()
154
+ result = bot.resolve_issue(token, owner, repo, issue_number, resolution, forked_repo)
155
+ return result
156
+
157
+ def extract_info_from_url(url: str) -> Dict[str, Any]:
158
+ info = {}
159
  try:
160
+ response = requests.get(url)
 
 
161
  response.raise_for_status()
162
+ info['status_code'] = response.status_code
163
+ info['headers'] = dict(response.headers)
164
+ info['content'] = response.text[:500] # Limit content to first 500 characters for brevity
165
+
166
+ parsed_url = urlparse(url)
167
+ if 'github.com' in parsed_url.netloc:
168
+ parts = parsed_url.path.split('/')
169
+ if len(parts) > 2:
170
+ owner = parts[1]
171
+ repo = parts[2]
172
+ issues = bot.fetch_issues(github_token, owner, repo)
173
+ info['issues'] = issues
174
+ elif 'huggingface.co' in parsed_url.netloc:
175
+ # Add Hugging Face specific handling if needed
176
+ pass
177
+
178
+ except requests.HTTPError as e:
179
+ info['error'] = f"HTTP error: {str(e)}"
180
+ except Exception as e:
181
+ info['error'] = f"Error: {str(e)}"
182
+ return info
183
+
184
+ # Initialize GitHubBot globally
185
+ bot = GitHubBot()
186
 
187
+ # Define missing functions with validation
188
+ def fetch_issues(token, repo_url):
189
  try:
190
+ parts = repo_url.split('/')
191
+ if len(parts) < 2:
192
+ raise ValueError("Repository URL is not in the correct format. Expected format: 'owner/repo'.")
193
+
194
+ owner, repo = parts[-2], parts[-1]
195
+ issues = bot.fetch_issues(token, owner, repo)
196
+ return issues
197
+ except Exception as e:
198
+ return str(e)
199
+
200
+ def resolve_issue(token, repo_url, issue_number, resolution, forked_repo_url):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
201
  try:
202
+ parts = repo_url.split('/')
203
+ if len(parts) < 2:
204
+ raise ValueError("Repository URL is not in the correct format. Expected format: 'owner/repo'.")
205
+
206
+ owner, repo = parts[-2], parts[-1]
207
+ result = bot.resolve_issue(token, owner, repo, issue_number, resolution, forked_repo_url)
208
+ return result
209
+ except Exception as e:
210
+ return str(e)
211
+
212
+ def extract_info(url):
213
+ try:
214
+ info = extract_info_from_url(url)
215
+ return info
216
+ except Exception as e:
217
+ return str(e)
218
+
219
+ # HTML and CSS integration
220
+ custom_html = """
221
+ <!DOCTYPE html>
222
+ <html>
223
+ <head>
224
+ <title>GitHub Issue Manager</title>
225
+ <meta charset="UTF-8">
226
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
227
+ <link href="https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css" rel="stylesheet">
228
+ <link href="https://cdn.jsdelivr.net/npm/daisyui@3.1.0/dist/full.css" rel="stylesheet" type="text/css" />
229
+ <style>
230
+ body {
231
+ background: linear-gradient(to bottom right, #121212, #303030) !important;
232
+ color: #e0e0e0;
233
+ font-family: 'Roboto', sans-serif;
234
+ overflow-x: hidden; /* Prevent horizontal scrollbar */
235
+ }
236
+ .card {
237
+ border-radius: 1.5rem;
238
+ box-shadow: 0 15px 25px rgba (0, 0, 0, 0.2);
239
+ margin-bottom: 20px;
240
+ }
241
+ .input, .select, .textarea {
242
+ border: 2px solid #4a4a4a;
243
+ border-radius: 0.75rem;
244
+ padding-inline: 1.5rem;
245
+ padding-block: 0.75rem;
246
+ }
247
+ h1, h2, h3 {
248
+ color: #e0e0e0;
249
+ }
250
+ .output-area {
251
+ padding: 1.5rem;
252
+ border-radius: 0.75rem;
253
+ }
254
+ .btn {
255
+ border-radius: 0.75rem;
256
+ padding-block: 0.75rem;
257
+ padding-inline: 1.5rem;
258
+ }
259
+ .btn-primary {
260
+ background-color: #7928CA;
261
+ color: white;
262
+ border: 2px solid #7928CA;
263
+ }
264
+ .btn-primary:hover {
265
+ background-color: #5e22a1;
266
+ }
267
+ .btn-success {
268
+ background-color: #22c55e;
269
+ color: white;
270
+ border: 2px solid #22c55e;
271
+ }
272
+ .btn-success:hover {
273
+ background-color: #1a9a46;
274
+ }
275
+ </style>
276
+ </head>
277
+ <body class="bg-gray-900">
278
+ <div class="container mx-auto p-4">
279
+ <h1 class="text-4xl md:text-5xl font-extrabold text-center mb-8">GitHub Issue Manager</h1>
280
+ <!-- GitHub Token & Repo URL -->
281
+ <div class="card bg-gray-800 p-8">
282
+ <div class="form-control">
283
+ <label class="label">
284
+ <span class="label-text">GitHub Token</span>
285
+ </label>
286
+ <input type="password" placeholder="Enter your GitHub Personal Access Token" class="input input-bordered input-primary" id="github-token">
287
+ </div>
288
+ <div class="form-control">
289
+ <label class="label">
290
+ <span class="label-text">Repository URL</span>
291
+ </label>
292
+ <input type="text" placeholder="Enter the full GitHub repository URL" class="input input-bordered input-primary" id="repo-url">
293
+ </div>
294
+ </div>
295
+ <!-- Fetch & Resolve Section -->
296
+ <div class="card bg-gray-800 p-8">
297
+ <div class="flex justify-between gap-4">
298
+ <button class="btn btn-primary" id="fetch-issues">Fetch Issues</button>
299
+ <select class="select select-primary w-full max-w-xs" id="issue-dropdown">
300
+ <option disabled selected>Select Issue</option>
301
+ </select>
302
+ </div>
303
+ <div class="form-control mt-4">
304
+ <label class="label">
305
+ <span class="label-text">Resolution</span>
306
+ </label>
307
+ <textarea class="textarea textarea-primary" placeholder="Enter the resolution details" id="resolution-textarea"></textarea>
308
+ </div>
309
+ <div class="form-control mt-4">
310
+ <label class="label">
311
+ <span class="label-text">Forked Repository URL</span>
312
+ </label>
313
+ <input type="text" placeholder="URL to your forked repository (Optional)" class="input input-bordered input-primary" id="forked-repo-url">
314
+ </div>
315
+ <div class="form-control mt-4">
316
+ <button class="btn btn-success" id="resolve-issue">Resolve Issue</button>
317
+ </div>
318
+ </div>
319
+ <!-- Output Area -->
320
+ <div class="card bg-gray-800 p-8 mt-4">
321
+ <label class="label">
322
+ <span class="label-text">Output</span>
323
+ </label>
324
+ <textarea class="textarea textarea-primary h-48" id="output-textarea" readonly></textarea>
325
+ </div>
326
+ <!-- URL to Extract -->
327
+ <div class="card bg-gray-800 p-8 mt-4">
328
+ <div class="form-control">
329
+ <label class="label">
330
+ <span class="label-text">URL</span>
331
+ </label>
332
+ <input type="text" placeholder="Enter a URL to extract information" class="input input-bordered input-primary" id="url-textbox">
333
+ </div>
334
+ <div class="form-control">
335
+ <button class="btn btn-primary" id="extract-info">Extract Info</button>
336
+ </div>
337
+ </div>
338
+ </div>
339
+ <script>
340
+ const githubTokenInput = document.getElementById('github-token');
341
+ const repoUrlInput = document.getElementById('repo-url');
342
+ const fetchIssuesButton = document.getElementById('fetch-issues');
343
+ const issueDropdown = document.getElementById('issue-dropdown');
344
+ const resolutionTextarea = document.getElementById('resolution-textarea');
345
+ const forkedRepoUrlInput = document.getElementById('forked-repo-url');
346
+ const resolveIssueButton = document.getElementById('resolve-issue');
347
+ const outputTextarea = document.getElementById('output-textarea');
348
+ const urlTextbox = document.getElementById('url-textbox');
349
+ const extractInfoButton = document.getElementById('extract-info');
350
+
351
+ fetchIssuesButton.addEventListener('click', async () => {
352
+ const token = githubTokenInput.value;
353
+ const repoUrl = repoUrlInput.value;
354
+ if (!token || !repoUrl) {
355
+ outputTextarea.value = "Please provide both GitHub Token and Repository URL.";
356
+ return;
357
+ }
358
+ try {
359
+ const response = await fetch('/fetch-issues', {
360
+ method: 'POST',
361
+ headers: {
362
+ 'Content-Type': 'application/json',
363
+ },
364
+ body: JSON.stringify({ githubToken: token, repoUrl: repoUrl }),
365
+ });
366
+ if (!response.ok) {
367
+ throw new Error(`HTTP error! status: ${response.status}`);
368
+ }
369
+ const data = await response.json();
370
+ if (data.error) {
371
+ outputTextarea.value = data.error;
372
+ } else {
373
+ issueDropdown.innerHTML = '';
374
+ data.issues.forEach(issue => {
375
+ const option = document.createElement('option');
376
+ option.value = issue.number;
377
+ option.text = `${issue.number}: ${issue.title}`;
378
+ issueDropdown.add(option);
379
+ });
380
+ }
381
+ } catch (error) {
382
+ outputTextarea.value = `Error fetching issues: ${error.message}`;
383
+ }
384
+ });
385
+
386
+ resolveIssueButton.addEventListener('click', async () => {
387
+ const token = githubTokenInput.value;
388
+ const repoUrl = repoUrlInput.value;
389
+ const issueNumber = issueDropdown.value;
390
+ const resolution = resolutionTextarea.value;
391
+ const forkedRepoUrl = forkedRepoUrlInput.value;
392
+ if (!token || !repoUrl || !issueNumber || !resolution) {
393
+ outputTextarea.value = "Please provide all required fields.";
394
+ return;
395
+ }
396
+ try {
397
+ const response = await fetch('/resolve-issue', {
398
+ method: 'POST',
399
+ headers: {
400
+ 'Content-Type': 'application/json',
401
+ },
402
+ body: JSON.stringify({ githubToken: token, repoUrl: repoUrl, issueNumber: issueNumber, resolution: resolution, forkedRepoUrl: forkedRepoUrl }),
403
+ });
404
+ if (!response.ok) {
405
+ throw new Error(`HTTP error! status: ${response.status}`);
406
+ }
407
+ const data = await response.json();
408
+ if (data.error) {
409
+ outputTextarea.value = data.error;
410
+ } else {
411
+ outputTextarea.value = data.output;
412
+ }
413
+ } catch (error) {
414
+ outputTextarea.value = `Error resolving issue: ${error.message}`;
415
+ }
416
+ });
417
+
418
+ extractInfoButton.addEventListener('click', async () => {
419
+ const url = urlTextbox.value;
420
+ if (!url) {
421
+ outputTextarea.value = "Please provide a URL.";
422
+ return;
423
+ }
424
+ try {
425
+ const response = await fetch('/extract-info', {
426
+ method: 'POST',
427
+ headers: {
428
+ 'Content-Type': 'application/json',
429
+ },
430
+ body: JSON.stringify({ url: url }),
431
+ });
432
+ if (!response.ok) {
433
+ throw new Error(`HTTP error! status: ${response.status}`);
434
+ }
435
+ const data = await response.json();
436
+ if (data.error) {
437
+ outputTextarea.value = data.error;
438
+ } else {
439
+ outputTextarea.value = JSON.stringify(data, null, 2);
440
+ }
441
+ } catch (error) {
442
+ outputTextarea.value = `Error extracting info: ${error.message}`;
443
+ }
444
+ });
445
+ </script>
446
+ </body>
447
+ </html>
448
+ """
449
+
450
+ def create_gradio_interface():
451
+ with gr.Blocks(css=None, theme=None) as demo:
452
+ gr.HTML(custom_html)
453
+ return demo
454
+
455
+ # Cleanup function
456
+ def cleanup():
457
+ try:
458
+ temp_dirs = [REPOS_DIRECTORY]
459
+ for dir_name in temp_dirs:
460
+ if os.path.exists(dir_name):
461
+ shutil.rmtree(dir_name)
462
+ logging.shutdown()
463
+ except Exception as e:
464
+ print(f"Error during cleanup: {str(e)}")
465
+
466
+ # Signal handler
467
+ def signal_handler(signum, frame):
468
+ logger.info(f"Received signal {signum}")
469
+ cleanup()
470
+ sys.exit(0)
471
+
472
+ if __name__ == "__main__":
473
+ # Register cleanup handlers
474
+ atexit.register(cleanup)
475
+ signal.signal(signal.SIGINT, signal_handler)
476
+ signal.signal(signal.SIGTERM, signal_handler)
477
+
478
+ # Create Gradio interface
479
+ demo = create_gradio_interface()
480
+ demo.launch()