acecalisto3 commited on
Commit
73ffbb8
·
verified ·
1 Parent(s): cc23ad6

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +196 -354
app.py CHANGED
@@ -10,52 +10,15 @@ import requests
10
  import gradio as gr
11
  import atexit
12
  import subprocess
13
- from urllib.parse import urlparse, quote
14
- import warnings
15
  import webbrowser
 
 
 
 
 
16
  import spaces
17
-
18
- device = "cuda"
19
-
20
- @spaces.GPU()
21
- def stream_chat(
22
- message: str,
23
- history: list,
24
- system_prompt: str,
25
- temperature: float = 0.8,
26
- max_new_tokens: int = 1024,
27
- top_p: float = 1.0,
28
- top_k: int = 20,
29
- penalty: float = 1.2,
30
- ):
31
- print(f'message: {message}')
32
- print(f'history: {history}')
33
-
34
- conversation = [
35
- {"role": "system", "content": system_prompt}
36
- ]
37
- for prompt, answer in history:
38
- conversation.extend([
39
- {"role": "user", "content": prompt},
40
- {"role": "assistant", "content": answer},
41
- ])
42
-
43
- conversation.append({"role": "user", "content": message})
44
-
45
- input_ids = tokenizer.apply_chat_template(conversation, add_generation_prompt=True, return_tensors="pt").to(model.device)
46
-
47
- streamer = TextIteratorStreamer(tokenizer, timeout=60.0, skip_prompt=True, skip_special_tokens=True)
48
-
49
- generate_kwargs = dict(
50
- input_ids=input_ids,
51
- max_new_tokens = max_new_tokens,
52
- do_sample = False if temperature == 0 else True,
53
- top_p = top_p,
54
- top_k = top_k,
55
- temperature = temperature,
56
- eos_token_id=[128001,128008,128009],
57
- streamer=streamer,
58
- )
59
 
60
  # Constants
61
  INPUT_DIRECTORY = 'input'
@@ -64,14 +27,8 @@ LOGS_DIRECTORY = 'logs'
64
  RESOLUTIONS_DIRECTORY = 'resolutions'
65
  REPOS_DIRECTORY = 'repos'
66
 
67
- # Set up environment
68
- def initialize_environment(input_file, output_directory):
69
- directories = [LOGS_DIRECTORY, RESOLUTIONS_DIRECTORY, REPOS_DIRECTORY, input_file, output_directory]
70
- for directory in directories:
71
- os.makedirs(directory, exist_ok=True)
72
-
73
  # Set up logging
74
- def initialize_logger():
75
  log_file = f"{LOGS_DIRECTORY}/github_bot_{datetime.now().strftime('%Y%m%d_%H%M%S')}.log"
76
  logging.basicConfig(
77
  level=logging.INFO,
@@ -83,9 +40,13 @@ def initialize_logger():
83
  )
84
  return logging.getLogger(__name__)
85
 
86
- # Initialize environment and logger
87
- initialize_environment(INPUT_DIRECTORY, OUTPUT_DIRECTORY)
88
- logger = initialize_logger() # Initialize logger globally
 
 
 
 
89
 
90
  # GitHub API handler
91
  class GitHubAPI:
@@ -112,165 +73,81 @@ class GitHubAPI:
112
  time.sleep(wait_time)
113
  return False
114
  return True
115
- except Exception as e:
116
  logger.error(f"Error checking rate limit: {str(e)}")
117
  return True
118
 
119
- def get_repository(self, owner: str, repo: str) -> Dict:
120
  try:
121
  response = requests.get(f"{self.base_url}/repos/{owner}/{repo}", headers=self.headers)
122
  response.raise_for_status()
123
  return response.json()
124
- except requests.HTTPError as e:
125
- logger.error(f"HTTP error getting repository info: {str(e)}")
126
- raise
127
- except Exception as e:
128
- logger.error(f"Error getting repository info: {str(e)}")
129
- raise
130
-
131
- def get_issues(self, owner: str, repo: str, state: str = 'open') -> List[Dict]:
132
- if not self._check_rate_limit():
133
- return []
134
 
 
135
  try:
136
- response = requests.get(f"{self.base_url}/repos/{owner}/{repo}/issues", headers=self.headers, params={'state': state})
137
  response.raise_for_status()
138
- issues = response.json()
139
- return [issue for issue in issues if 'pull_request' not in issue]
140
- except Exception as e:
141
- logger.error(f"Error fetching issues: {str(e)}")
142
  return []
143
 
144
- # GitHub Bot
145
- class GitHubBot:
146
- def __init__(self):
147
- self.github_api = None
148
-
149
- def initialize_api(self, token: str):
150
- self.github_api = GitHubAPI(token)
151
-
152
- def fetch_issues(self, token: str, owner: str, repo: str) -> List[Dict]:
153
  try:
154
- self.initialize_api(token)
155
- return self.github_api.get_issues(owner, repo)
156
- except Exception as e:
157
- logger.error(f"Error fetching issues: {str(e)}")
158
- return []
 
159
 
160
- def resolve_issue(self, token: str, owner: str, repo: str, issue_number: int, resolution: str, forked_repo: str) -> str:
161
  try:
162
- self.initialize_api(token)
163
- self.github_api.get_repository(owner, repo)
164
-
165
- # Create resolution file
166
- timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
167
- resolution_file = f"{RESOLUTIONS_DIRECTORY}/resolution_{issue_number}_{timestamp}.md"
168
-
169
- with open(resolution_file, "w") as f:
170
- f.write(f"# Resolution for Issue #{issue_number}\n\n{resolution}")
171
-
172
- # Clone the forked repo
173
- subprocess.run(['git', 'clone', forked_repo, '/tmp/' + forked_repo.split('/')[-1]], check=True)
174
-
175
- # Change to the cloned directory
176
- os.chdir('/tmp/' + forked_repo.split('/')[-1])
177
-
178
- # Assuming manual intervention now
179
- input(" Apply the fix manually and stage the changes (press ENTER)? ")
180
-
181
- # Commit and push the modifications
182
- subprocess.run(['git', 'add', '.'], check=True)
183
- subprocess.run(['git', 'commit', '-m', f"Resolved issue #{issue_number} ({quote(resolution)})"], check=True)
184
- subprocess.run(['git', 'push', 'origin', 'HEAD'], check=True)
185
-
186
- # Open Pull Request page
187
- webbrowser.open(f'https://github.com/{forked_repo.split("/")[-1]}/compare/master...{owner}:{forked_repo.split("/")[-1]}_resolved_issue_{issue_number}')
188
-
189
- return f"Resolution saved: {resolution_file}"
190
-
191
- except Exception as e:
192
- error_msg = f"Error resolving issue: {str(e)}"
193
- logger.error(error_msg)
194
- return error_msg
195
-
196
- def handle_issue_selection(token, owner, repo, issue_number, resolution, forked_repo):
197
- bot = GitHubBot()
198
- result = bot.resolve_issue(token, owner, repo, issue_number, resolution, forked_repo)
199
- return result
200
-
201
- def extract_info_from_url(url: str) -> Dict[str, Any]:
202
- info = {}
203
- try:
204
- response = requests.get(url)
205
- response.raise_for_status()
206
- info['status_code'] = response.status_code
207
- info['headers'] = dict(response.headers)
208
- info['content'] = response.text[:500] # Limit content to first 500 characters for brevity
209
-
210
- parsed_url = urlparse(url)
211
- if 'github.com' in parsed_url.netloc:
212
- parts = parsed_url.path.split('/')
213
- if len(parts) > 2:
214
- owner = parts[1]
215
- repo = parts[2]
216
- issues = bot.fetch_issues(github_token, owner, repo)
217
- info['issues'] = issues
218
- elif 'huggingface.co' in parsed_url.netloc:
219
- # Add Hugging Face specific handling if needed
220
- pass
221
-
222
- except requests.HTTPError as e:
223
- info['error'] = f"HTTP error: {str(e)}"
224
- except Exception as e:
225
- info['error'] = f"Error: {str(e)}"
226
- return info
227
-
228
- # Initialize GitHubBot globally
229
- bot = GitHubBot()
230
-
231
- # Define missing functions with validation
232
- def fetch_issues(token, repo_url):
233
- try:
234
- parts = repo_url.split('/')
235
- if len(parts) < 2:
236
- raise ValueError("Repository URL is not in the correct format. Expected format: 'owner/repo'.")
237
-
238
- owner, repo = parts[-2], parts[-1]
239
- issues = bot.fetch_issues(token, owner, repo)
240
- return issues
241
- except Exception as e:
242
- return str(e)
243
-
244
- def resolve_issue(token, repo_url, issue_number, resolution, forked_repo_url):
245
- try:
246
- parts = repo_url.split('/')
247
- if len(parts) < 2:
248
- raise ValueError("Repository URL is not in the correct format. Expected format: 'owner/repo'.")
249
-
250
- owner, repo = parts[-2], parts[-1]
251
- result = bot.resolve_issue(token, owner, repo, issue_number, resolution, forked_repo_url)
252
- return result
253
- except Exception as e:
254
- return str(e)
255
-
256
- def extract_info(url):
257
- try:
258
- info = extract_info_from_url(url)
259
- return info
260
- except Exception as e:
261
- return str(e)
262
-
263
- def submit_issue(token, repo_url, issue_number, resolution, forked_repo_url):
264
- try:
265
- parts = repo_url.split('/')
266
- if len(parts) < 2:
267
- raise ValueError("Repository URL is not in the correct format. Expected format: 'owner/repo'.")
268
-
269
- owner, repo = parts[-2], parts[-1]
270
- result = bot.resolve_issue(token, owner, repo, issue_number, resolution, forked_repo_url)
271
- return result
272
- except Exception as e:
273
- return str(e)
274
 
275
  # HTML and CSS integration
276
  custom_html = """
@@ -322,13 +199,16 @@ custom_html = """
322
  color: white;
323
  border: 2px solid #22c55e;
324
  }
 
 
 
325
  </style>
326
  </head>
327
  <body class="bg-gray-900">
328
- <div class="container mx-auto p-4" >
329
  <h1 class="text-4xl md:text-5xl font-extrabold text-center mb-8">GitHub Issue Manager</h1>
330
  <!-- GitHub Token & Repo URL -->
331
- <div class="card bg-gray-800 p-8">
332
  <div class="form-control">
333
  <label class="label">
334
  <span class="label-text">GitHub Token</span>
@@ -346,7 +226,7 @@ custom_html = """
346
  <div class="card bg-gray-800 p-8">
347
  <div class="flex justify-between gap-4">
348
  <button class="btn btn-primary" id="fetch-issues">Fetch Issues</button>
349
- <select class="select select-primary w-full max-w-xs" id="issue-dropdown" >
350
  <option disabled selected>Select Issue</option>
351
  </select>
352
  </div>
@@ -367,7 +247,7 @@ custom_html = """
367
  </div>
368
  </div>
369
  <!-- Output Area -->
370
- <div class="card bg-gray-800 p-8 mt-4" >
371
  <label class="label">
372
  <span class="label-text">Output</span>
373
  </label>
@@ -385,6 +265,7 @@ custom_html = """
385
  <button class="btn btn-primary" id="extract-info">Extract Info</button>
386
  </div>
387
  </div>
 
388
  </div>
389
  <script>
390
  const githubTokenInput = document.getElementById('github-token');
@@ -397,115 +278,137 @@ custom_html = """
397
  const outputTextarea = document.getElementById('output-textarea');
398
  const urlTextbox = document.getElementById('url-textbox');
399
  const extractInfoButton = document.getElementById('extract-info');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
400
 
401
  fetchIssuesButton.addEventListener('click', async () => {
402
- const token = githubTokenInput.value;
403
- const repoUrl = repoUrlInput.value;
404
- if (!token || !repoUrl) {
405
- outputTextarea.value = "Please provide both GitHub Token and Repository URL.";
406
- return;
407
- }
408
- try {
409
- const response = await fetch('/fetch_issues', {
410
- method: 'POST',
411
- headers: {
412
- 'Content-Type': 'application/json',
413
- },
414
- body: JSON.stringify({ token: token, repoUrl: repoUrl }),
415
- });
416
- if (!response.ok) {
417
- throw new Error(`HTTP error! status: ${response.status}`);
418
  }
419
- const data = await response.json();
420
- if (data.error) {
421
- outputTextarea.value = data.error;
422
- } else {
423
- issueDropdown.innerHTML = '';
424
- data.issues.forEach(issue => {
425
- const option = document.createElement('option');
426
- option.value = issue.number;
427
- option.text = `${issue.number}: ${issue.title}`;
428
- issueDropdown.add(option);
429
- });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
430
  }
431
- } catch (error) {
432
- outputTextarea.value = `Error fetching issues: ${error.message}`;
433
- }
434
  });
435
 
436
  resolveIssueButton.addEventListener('click', async () => {
437
- const token = githubTokenInput.value;
438
- const repoUrl = repoUrlInput.value;
439
- const issueNumber = issueDropdown.value;
440
- const resolution = resolutionTextarea.value;
441
- const forkedRepoUrl = forkedRepoUrlInput.value;
442
-
443
- if (!token || !repoUrl || !issueNumber || !resolution) {
444
- outputTextarea.value ="Please provide all required fields.";
445
- return;
446
- }
447
- try {
448
- const response = await fetch('/resolve_issue', {
449
- method: 'POST',
450
- headers: {
451
- 'Content-Type': 'application/json',
452
- },
453
- body: JSON.stringify({ token: token, repoUrl: repoUrl, issueNumber: issueNumber, resolution: resolution, forkedRepoUrl: forkedRepoUrl }),
454
- });
455
- if (!response.ok) {
456
- throw new Error(`HTTP error! status: ${response.status}`);
457
  }
458
- const data = await response.json();
459
- if (data.error) {
460
- outputTextarea.value = data.error;
461
- } else {
462
- outputTextarea.value = data.result;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
463
  }
464
- } catch (error) {
465
- outputTextarea.value = `Error resolving issue: ${error.message}`;
466
- }
467
  });
468
 
469
  extractInfoButton.addEventListener('click', async () => {
470
- const url = urlTextbox.value;
471
- if (!url) {
472
- outputTextarea.value = "Please provide a URL.";
473
- return;
474
- }
475
- try {
476
- const response = await fetch('/extract_info', {
477
- method: 'POST',
478
- headers: {
479
- 'Content-Type': 'application/json',
480
- },
481
- body: JSON.stringify({ url: url }),
482
- });
483
- if (!response.ok) {
484
- throw new Error(`HTTP error! status: ${response.status}`);
485
  }
486
- const data = await response.json();
487
- if (data.error) {
488
- outputTextarea.value = data.error;
489
- } else {
490
- outputTextarea.value = JSON.stringify(data, null, 2);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
491
  }
492
- } catch (error) {
493
- outputTextarea.value = `Error extracting info: ${error.message}`;
494
- }
495
  });
496
  </script>
497
  </body>
498
  </html>
499
  """
500
 
501
- def create_gradio_interface():
502
  with gr.Blocks(css=None, theme=None) as demo:
503
  gr.HTML(custom_html)
504
-
505
  return demo
506
 
507
  # Cleanup function
508
- def cleanup():
509
  try:
510
  temp_dirs = [REPOS_DIRECTORY]
511
  for dir_name in temp_dirs:
@@ -516,7 +419,7 @@ def cleanup():
516
  print(f"Error during cleanup: {str(e)}")
517
 
518
  # Signal handler
519
- def signal_handler(signum, frame):
520
  logger.info(f"Received signal {signum}")
521
  cleanup()
522
  sys.exit(0)
@@ -527,71 +430,10 @@ if __name__ == "__main__":
527
  signal.signal(signal.SIGINT, signal_handler)
528
  signal.signal(signal.SIGTERM, signal_handler)
529
 
530
- # Create Gradio interface
531
- with gr.Blocks() as demo:
532
- # Add HTML template
533
- gr.HTML(custom_html)
534
-
535
- # Create interface components that match the HTML IDs
536
- with gr.Row(visible=True):
537
- github_token_input = gr.Textbox(label="GitHub Token", elem_id="github-token")
538
- repo_url_input = gr.Textbox(label="Repository URL", elem_id="repo-url")
539
-
540
- with gr.Row(visible=True):
541
- issue_dropdown = gr.Dropdown(label="Select Issue", elem_id="issue-dropdown")
542
- resolution_textarea = gr.Textbox(label="Resolution", elem_id="resolution-textarea", lines=3)
543
- forked_repo_url_input = gr.Textbox(label="Forked Repository URL", elem_id="forked-repo-url")
544
-
545
- output_area = gr.Textbox(label="Output", elem_id="output-textarea", lines=5)
546
- url_textbox = gr.Textbox(label="URL to Extract Info", elem_id="url-textbox")
547
-
548
- # Define buttons with matching elem_ids
549
- fetch_button = gr.Button("Fetch Issues", elem_id="fetch-issues")
550
- resolve_button = gr.Button("Resolve Issue", elem_id="resolve-issue")
551
- extract_button = gr.Button("Extract Info", elem_id="extract-info")
552
- submit_button = gr.Button("Submit", elem_id="submit")
553
-
554
- # Connect Python functions to buttons
555
- fetch_button.click(
556
- fn=fetch_issues,
557
- inputs=[github_token_input, repo_url_input],
558
- outputs=output_area
559
- )
560
 
561
- resolve_button.click(
562
- fn=resolve_issue,
563
- inputs=[
564
- github_token_input,
565
- repo_url_input,
566
- issue_dropdown,
567
- resolution_textarea,
568
- forked_repo_url_input
569
- ],
570
- outputs=output_area
571
- )
572
-
573
- extract_button.click(
574
- fn=extract_info,
575
- inputs=[url_textbox],
576
- outputs=output_area
577
- )
578
-
579
- submit_button.click(
580
- fn=submit_issue,
581
- inputs=[
582
- github_token_input,
583
- repo_url_input,
584
- issue_dropdown,
585
- resolution_textarea,
586
- forked_repo_url_input
587
- ],
588
- outputs=output_area
589
- )
590
-
591
- # Launch the interface
592
- demo.launch(
593
- server_name="0.0.0.0",
594
- server_port=7860,
595
- share=True,
596
- debug=True
597
- )
 
10
  import gradio as gr
11
  import atexit
12
  import subprocess
 
 
13
  import webbrowser
14
+ import urllib.parse
15
+ import warnings
16
+ import torch
17
+ import flask
18
+ from flask import Flask, request, jsonify
19
  import spaces
20
+ from accelerate import Accelerator
21
+ from threading import Thread
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
22
 
23
  # Constants
24
  INPUT_DIRECTORY = 'input'
 
27
  RESOLUTIONS_DIRECTORY = 'resolutions'
28
  REPOS_DIRECTORY = 'repos'
29
 
 
 
 
 
 
 
30
  # Set up logging
31
+ def initialize_logger() -> logging.Logger:
32
  log_file = f"{LOGS_DIRECTORY}/github_bot_{datetime.now().strftime('%Y%m%d_%H%M%S')}.log"
33
  logging.basicConfig(
34
  level=logging.INFO,
 
40
  )
41
  return logging.getLogger(__name__)
42
 
43
+ # Set up environment
44
+ @spaces.GPU
45
+ def initialize_environment(input_file: str, output_directory: str) -> logging.Logger:
46
+ directories = [LOGS_DIRECTORY, RESOLUTIONS_DIRECTORY, REPOS_DIRECTORY, input_file, output_directory]
47
+ for directory in directories:
48
+ os.makedirs(directory, exist_ok=True)
49
+ return initialize_logger()
50
 
51
  # GitHub API handler
52
  class GitHubAPI:
 
73
  time.sleep(wait_time)
74
  return False
75
  return True
76
+ except requests.exceptions.RequestException as e:
77
  logger.error(f"Error checking rate limit: {str(e)}")
78
  return True
79
 
80
+ def get_repository(self, owner: str, repo: str) -> Dict[str, Any]:
81
  try:
82
  response = requests.get(f"{self.base_url}/repos/{owner}/{repo}", headers=self.headers)
83
  response.raise_for_status()
84
  return response.json()
85
+ except requests.exceptions.RequestException as e:
86
+ logger.error(f"Error getting repository: {str(e)}")
87
+ return {}
 
 
 
 
 
 
 
88
 
89
+ def get_issues(self, owner: str, repo: str) -> List[Dict[str, Any]]:
90
  try:
91
+ response = requests.get(f"{self.base_url}/repos/{owner}/{repo}/issues", headers=self.headers)
92
  response.raise_for_status()
93
+ return response.json()
94
+ except requests.exceptions.RequestException as e:
95
+ logger.error(f"Error getting issues: {str(e)}")
 
96
  return []
97
 
98
+ def create_issue(self, owner: str, repo: str, title: str, body: str) -> Dict[str, Any]:
 
 
 
 
 
 
 
 
99
  try:
100
+ response = requests.post(f"{self.base_url}/repos/{owner}/{repo}/issues", headers=self.headers, json={'title': title, 'body': body})
101
+ response.raise_for_status()
102
+ return response.json()
103
+ except requests.exceptions.RequestException as e:
104
+ logger.error(f"Error creating issue: {str(e)}")
105
+ return {}
106
 
107
+ def update_issue(self, owner: str, repo: str, issue_number: int, title: str, body: str) -> Dict[str, Any]:
108
  try:
109
+ response = requests.patch(f"{self.base_url}/repos/{owner}/{repo}/issues/{issue_number}", headers=self.headers, json={'title': title, 'body': body})
110
+ response.raise_for_status()
111
+ return response.json()
112
+ except requests.exceptions.RequestException as e:
113
+ logger.error(f"Error updating issue: {str(e)}")
114
+ return {}
115
+
116
+ app = Flask(__name__)
117
+
118
+ @app.route('/fetch-issues', methods=['POST'])
119
+ def fetch_issues() -> Dict[str, Any]:
120
+ data = request.get_json()
121
+ github_token = data['githubToken']
122
+ repo_url = data['repoUrl']
123
+ owner, repo = repo_url.split('/')[-2:]
124
+ github_api = GitHubAPI (github_token)
125
+ issues = github_api.get_issues(owner , repo)
126
+ return jsonify({'issues': issues})
127
+
128
+ @app.route('/resolve-issue', methods=['POST'])
129
+ def resolve_issue() -> Dict[str, Any]:
130
+ data = request.get_json()
131
+ github_token = data['githubToken']
132
+ repo_url = data['repoUrl']
133
+ issue = data['issue']
134
+ resolution = data['resolution']
135
+ forked_repo_url = data['forkedRepoUrl']
136
+ owner, repo = repo_url.split('/')[-2:]
137
+ github_api = GitHubAPI(github_token)
138
+ issue_number = issue['number']
139
+ github_api.update_issue(owner, repo, issue_number, issue['title'], resolution)
140
+ return jsonify({'output': f"Issue {issue_number} resolved"})
141
+
142
+ @app.route('/extract-info', methods=['POST'])
143
+ def extract_info() -> Dict[str, Any]:
144
+ data = request.get_json()
145
+ url = data['url']
146
+ # Extract info from URL
147
+ return jsonify({'output': f"Info extracted from {url}"})
148
+
149
+ def run_flask_app() -> None:
150
+ app.run(debug=True, use_reloader=False)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
151
 
152
  # HTML and CSS integration
153
  custom_html = """
 
199
  color: white;
200
  border: 2px solid #22c55e;
201
  }
202
+ .loading {
203
+ display: none;
204
+ }
205
  </style>
206
  </head>
207
  <body class="bg-gray-900">
208
+ <div class="container mx-auto p-4">
209
  <h1 class="text-4xl md:text-5xl font-extrabold text-center mb-8">GitHub Issue Manager</h1>
210
  <!-- GitHub Token & Repo URL -->
211
+ <div class="card bg-gray-800 p-8"> ```html
212
  <div class="form-control">
213
  <label class="label">
214
  <span class="label-text">GitHub Token</span>
 
226
  <div class="card bg-gray-800 p-8">
227
  <div class="flex justify-between gap-4">
228
  <button class="btn btn-primary" id="fetch-issues">Fetch Issues</button>
229
+ <select class="select select-primary w-full max-w-xs" id="issue-dropdown">
230
  <option disabled selected>Select Issue</option>
231
  </select>
232
  </div>
 
247
  </div>
248
  </div>
249
  <!-- Output Area -->
250
+ <div class="card bg-gray-800 p-8 mt-4">
251
  <label class="label">
252
  <span class="label-text">Output</span>
253
  </label>
 
265
  <button class="btn btn-primary" id="extract-info">Extract Info</button>
266
  </div>
267
  </div>
268
+ <div class="loading" id="loading-indicator">Loading...</div>
269
  </div>
270
  <script>
271
  const githubTokenInput = document.getElementById('github-token');
 
278
  const outputTextarea = document.getElementById('output-textarea');
279
  const urlTextbox = document.getElementById('url-textbox');
280
  const extractInfoButton = document.getElementById('extract-info');
281
+ const loadingIndicator = document.getElementById('loading-indicator');
282
+
283
+ const showLoading = () => {
284
+ loadingIndicator.style.display = 'block';
285
+ fetchIssuesButton.disabled = true;
286
+ resolveIssueButton.disabled = true;
287
+ extractInfoButton.disabled = true;
288
+ };
289
+
290
+ const hideLoading = () => {
291
+ loadingIndicator.style.display = 'none';
292
+ fetchIssuesButton.disabled = false;
293
+ resolveIssueButton.disabled = false;
294
+ extractInfoButton.disabled = false;
295
+ };
296
 
297
  fetchIssuesButton.addEventListener('click', async () => {
298
+ const token = githubTokenInput.value;
299
+ const repoUrl = repoUrlInput.value;
300
+ if (!token || !repoUrl) {
301
+ outputTextarea.value = "Please provide both GitHub Token and Repository URL.";
302
+ return;
 
 
 
 
 
 
 
 
 
 
 
303
  }
304
+ showLoading();
305
+ try {
306
+ const response = await fetch('/fetch-issues', {
307
+ method: 'POST',
308
+ headers: {
309
+ 'Content-Type': 'application/json',
310
+ },
311
+ body: JSON.stringify({ githubToken: token, repoUrl: repoUrl }),
312
+ });
313
+ if (!response.ok) {
314
+ throw new Error(`HTTP error! status: ${response.status}`);
315
+ }
316
+ const data = await response.json();
317
+ if (data.error) {
318
+ outputTextarea.value = data.error;
319
+ } else {
320
+ issueDropdown.innerHTML = '';
321
+ data.issues.forEach(issue => {
322
+ const option = document.createElement('option');
323
+ option.value = JSON.stringify(issue);
324
+ option.text = `${issue.number}: ${issue.title}`;
325
+ issueDropdown.add(option);
326
+ });
327
+ }
328
+ } catch (error) {
329
+ outputTextarea.value = `Error fetching issues: ${error.message}`;
330
+ } finally {
331
+ hideLoading();
332
  }
 
 
 
333
  });
334
 
335
  resolveIssueButton.addEventListener('click', async () => {
336
+ const token = githubTokenInput.value;
337
+ const repoUrl = repoUrlInput.value;
338
+ const issue = JSON.parse(issueDropdown.value);
339
+ const resolution = resolutionTextarea.value;
340
+ const forkedRepoUrl = forkedRepoUrlInput.value;
341
+ if (!token || !repoUrl || !issue || !resolution) {
342
+ outputTextarea.value = "Please provide all required fields.";
343
+ return;
 
 
 
 
 
 
 
 
 
 
 
 
344
  }
345
+ showLoading();
346
+ try {
347
+ const response = await fetch('/resolve-issue', {
348
+ method: 'POST',
349
+ headers: {
350
+ 'Content-Type': 'application/json',
351
+ },
352
+ body: JSON.stringify({ githubToken: token, repoUrl: repoUrl, issue: issue, resolution: resolution, forkedRepoUrl: forkedRepoUrl }),
353
+ });
354
+ if (!response.ok) {
355
+ throw new Error(`HTTP error! status: ${response.status}`);
356
+ }
357
+ const data = await response.json();
358
+ if (data.error) {
359
+ outputTextarea.value = data.error;
360
+ } else {
361
+ outputTextarea.value = data.output;
362
+ }
363
+ } catch (error) {
364
+ outputTextarea.value = `Error resolving issue: ${error.message}`;
365
+ } finally {
366
+ hideLoading();
367
  }
 
 
 
368
  });
369
 
370
  extractInfoButton.addEventListener('click', async () => {
371
+ const url = urlTextbox.value;
372
+ if (!url) {
373
+ outputTextarea.value = "Please provide a URL.";
374
+ return;
 
 
 
 
 
 
 
 
 
 
 
375
  }
376
+ showLoading();
377
+ try {
378
+ const response = await fetch('/extract-info', {
379
+ method: 'POST',
380
+ headers: {
381
+ 'Content-Type': 'application/json',
382
+ },
383
+ body: JSON.stringify({ url: url }),
384
+ });
385
+ if (!response.ok) {
386
+ throw new Error(`HTTP error! status: ${response.status}`);
387
+ }
388
+ const data = await response.json();
389
+ if (data.error) {
390
+ outputTextarea.value = data.error;
391
+ } else {
392
+ outputTextarea.value = data.output;
393
+ }
394
+ } catch (error) {
395
+ outputTextarea.value = `Error extracting info: ${error.message}`;
396
+ } finally {
397
+ hideLoading();
398
  }
 
 
 
399
  });
400
  </script>
401
  </body>
402
  </html>
403
  """
404
 
405
+ def create_gradio_interface() -> gr.Blocks:
406
  with gr.Blocks(css=None, theme=None) as demo:
407
  gr.HTML(custom_html)
 
408
  return demo
409
 
410
  # Cleanup function
411
+ def cleanup() -> None:
412
  try:
413
  temp_dirs = [REPOS_DIRECTORY]
414
  for dir_name in temp_dirs:
 
419
  print(f"Error during cleanup: {str(e)}")
420
 
421
  # Signal handler
422
+ def signal_handler(signum: int, frame) -> None:
423
  logger.info(f"Received signal {signum}")
424
  cleanup()
425
  sys.exit(0)
 
430
  signal.signal(signal.SIGINT, signal_handler)
431
  signal.signal(signal.SIGTERM, signal_handler)
432
 
433
+ # Run Flask app in a separate thread
434
+ flask_thread = Thread(target=run_flask_app)
435
+ flask_thread.start()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
436
 
437
+ # Create Gradio interface
438
+ demo = create_gradio_interface()
439
+ demo.launch()