Spaces:
Runtime error
Runtime error
import gradio as gr | |
import langchain | |
import huggingface_hub | |
import dotenv | |
import PyYaml | |
from typing import Optional, Union, List, Dict, Any, Tuple | |
import subprocess | |
from pathlib import Path | |
import json | |
import tempfile | |
from datetime import datetime, timezone | |
import re | |
# Add these utility classes based on git-bob's architecture | |
class TerminalCommand: | |
def execute(command: Union[str, List[str]], cwd: Optional[str] = None) -> Tuple[str, str, int]: | |
""" | |
Execute a terminal command and return stdout, stderr, and return code | |
""" | |
if isinstance(command, str): | |
command = command.split() | |
try: | |
process = subprocess.Popen( | |
command, | |
stdout=subprocess.PIPE, | |
stderr=subprocess.PIPE, | |
cwd=cwd, | |
text=True | |
) | |
stdout, stderr = process.communicate() | |
return stdout.strip(), stderr.strip(), process.returncode | |
except Exception as e: | |
return "", str(e), 1 | |
class GitUtilities: | |
def __init__(self, repo_path: str): | |
self.repo_path = Path(repo_path) | |
def clone(self, url: str, branch: str = "main") -> bool: | |
"""Clone a repository""" | |
_, stderr, code = TerminalCommand.execute( | |
f"git clone -b {branch} {url} {self.repo_path}" | |
) | |
return code == 0 | |
def commit(self, message: str) -> bool: | |
"""Create a commit with the given message""" | |
_, _, code = TerminalCommand.execute( | |
["git", "commit", "-m", message], | |
str(self.repo_path) | |
) | |
return code == 0 | |
def push(self, remote: str = "origin", branch: str = "main") -> bool: | |
"""Push changes to remote""" | |
_, _, code = TerminalCommand.execute( | |
["git", "push", remote, branch], | |
str(self.repo_path) | |
) | |
return code == 0 | |
def create_branch(self, branch_name: str) -> bool: | |
"""Create and checkout a new branch""" | |
_, _, code = TerminalCommand.execute( | |
["git", "checkout", "-b", branch_name], | |
str(self.repo_path) | |
) | |
return code == 0 | |
# Enhance the GitHubBot class with new features | |
class GitHubBot: | |
def __init__(self, logger: logging.Logger): | |
self.github_api = None | |
self.logger = logger | |
self.ai_provider = None | |
self.file_types = { | |
'.py': {'language': 'Python', 'parser': 'python'}, | |
'.js': {'language': 'JavaScript', 'parser': 'javascript'}, | |
'.ts': {'language': 'TypeScript', 'parser': 'typescript'}, | |
'.java': {'language': 'Java', 'parser': 'java'}, | |
'.cpp': {'language': 'C++', 'parser': 'cpp'}, | |
'.go': {'language': 'Go', 'parser': 'go'}, | |
'.rs': {'language': 'Rust', 'parser': 'rust'} | |
} | |
self.git = None | |
self.temp_dir = None | |
def initialize_api(self, token: str, ai_provider: AIProvider = None): | |
self.github_api = GitHubAPI(token, self.logger) | |
self.ai_provider = ai_provider | |
self.temp_dir = tempfile.mkdtemp() | |
self.git = GitUtilities(self.temp_dir) | |
def create_pull_request(self, owner: str, repo: str, title: str, body: str, head: str, base: str = "main") -> Dict: | |
"""Create a pull request""" | |
url = f"{self.github_api.base_url}/repos/{owner}/{repo}/pulls" | |
data = { | |
"title": title, | |
"body": body, | |
"head": head, | |
"base": base | |
} | |
response = requests.post(url, headers=self.github_api.headers, json=data) | |
response.raise_for_status() | |
return response.json() | |
def resolve_issue(self, token: str, owner: str, repo: str, issue_number: int, resolution: str, forked_repo: str) -> str: | |
try: | |
if not self.ai_provider: | |
raise ValueError("AI provider not initialized. Please initialize with an AI provider.") | |
# Create a unique branch name for this fix | |
branch_name = f"fix/issue-{issue_number}-{datetime.now().strftime('%Y%m%d-%H%M%S')}" | |
# Clone repository and create new branch | |
if not self.git.clone(forked_repo): | |
raise Exception("Failed to clone repository") | |
if not self.git.create_branch(branch_name): | |
raise Exception("Failed to create branch") | |
# Get repository content and analyze | |
code_contexts = self._get_main_branch_content(owner, repo) | |
# Get issue details | |
issue_url = f"{self.github_api.base_url}/repos/{owner}/{repo}/issues/{issue_number}" | |
response = requests.get(issue_url, headers=self.github_api.headers) | |
response.raise_for_status() | |
issue = response.json() | |
# Determine severity | |
severity = self._determine_severity(issue) | |
# Generate AI solution | |
context = f"Repository: {owner}/{repo}\nIssue #{issue_number}: {issue['title']}\n\nCode contexts:\n" | |
for ctx in code_contexts[:3]: | |
context += f"\nFile: {ctx.file_path}\n```{ctx.language}\n{ctx.content}\n```\n" | |
ai_solution = self.ai_provider.generate_solution(context, issue['body']) | |
# Create comprehensive resolution document | |
full_resolution = self._create_resolution_document( | |
issue_number, severity, resolution, ai_solution | |
) | |
# Save resolution | |
resolution_path = Path(RESOLUTIONS_DIRECTORY) / f"resolution_{issue_number}_{datetime.now().strftime('%Y%m%d_%H%M%S')}.md" | |
resolution_path.write_text(full_resolution) | |
# Prompt for manual intervention | |
print("\nAI-generated solution and original resolution have been saved.") | |
print(f"Please review and apply changes in: {self.temp_dir}") | |
input("Press Enter when changes are ready to commit...") | |
# Commit and push changes | |
if not self.git.commit(f"Fix #{issue_number}: {issue['title']}\n\n{resolution}"): | |
raise Exception("Failed to commit changes") | |
if not self.git.push("origin", branch_name): | |
raise Exception("Failed to push changes") | |
# Create pull request | |
pr = self.create_pull_request( | |
owner=owner, | |
repo=repo, | |
title=f"Fix #{issue_number}: {issue['title']}", | |
body=full_resolution, | |
head=branch_name | |
) | |
return f"Resolution implemented and PR created: {pr['html_url']}" | |
except Exception as e: | |
error_msg = f"Error resolving issue #{issue_number} in repository {owner}/{repo}: {str(e)}" | |
self.logger.error(error_msg) | |
return error_msg | |
finally: | |
# Cleanup | |
if self.temp_dir and os.path.exists(self.temp_dir): | |
shutil.rmtree(self.temp_dir) | |
def _create_resolution_document(self, issue_number: int, severity: IssueSeverity, | |
resolution: str, ai_solution: str) -> str: | |
"""Create a comprehensive resolution document""" | |
return f"""# Resolution for Issue #{issue_number} | |
## Severity: {severity.name} | |
## Original Resolution | |
{resolution} | |
## AI-Generated Solution | |
{ai_solution} | |
## Implementation Details | |
- Implementation Date: {datetime.now(timezone.utc).isoformat()} | |
- Severity Level: {severity.name} | |
- Resolution Type: {'AI-ASSISTED' if ai_solution else 'MANUAL'} | |
## Testing Notes | |
- [ ] Code changes have been tested locally | |
- [ ] All tests pass | |
- [ ] No new issues introduced | |
## Review Checklist | |
- [ ] Code follows project style guidelines | |
- [ ] Documentation has been updated | |
- [ ] Changes are properly tested | |
- [ ] No security vulnerabilities introduced | |
## Additional Notes | |
Please review the changes carefully before merging. | |
""" | |
# The following lines should not be part of the _create_resolution_document method | |
script_file = f"{RESOLUTIONS_DIRECTORY}/resolve_issue_{issue_number}.sh" | |
with open(script_file, "w") as f: | |
f.write(bash_script) | |
# Make the script executable | |
os.chmod(script_file, 0o755) | |
return f"Bash script generated: {script_file}" | |
except Exception as e: | |
error_msg = f"Error resolving issue #{issue_number} in repository {owner}/{repo}: {str(e)}" | |
self.logger.error(error_msg) | |
return error_msg | |
def create_gradio_interface(): | |
with gr.Blocks() as demo: | |
gr.Markdown("# Enhanced GitHub Issue Resolver") | |
gr.Markdown("AI-assisted issue resolution with integrated git operations") | |
with gr.Tab("Configuration"): | |
with gr.Row(): | |
token_input = gr.Textbox(label="GitHub Token", placeholder="Enter your GitHub token") | |
repo_url_input = gr.Textbox(label="Repository URL", placeholder="Enter the repository URL (owner/repo)") | |
with gr.Row(): | |
ai_api_base = gr.Textbox(label="AI API Base URL", placeholder="Enter your AI API base URL") | |
ai_api_key = gr.Textbox(label="AI API Key", placeholder="Enter your AI API key") | |
with gr.Tab("Issue Resolution"): | |
with gr.Row(): | |
issue_number_input = gr.Number(label="Issue Number", info="Enter the issue number") | |
severity_indicator = gr.Label(label="Issue Severity") | |
resolution_input = gr.Textbox(label="Resolution", placeholder="Describe the resolution for the issue", lines=4) | |
forked_repo_input = gr.Textbox(label="Forked Repository URL", placeholder="Enter the forked repository URL") | |
with gr.Row(): | |
analyze_button = gr.Button("Analyze Issue") | |
resolve_button = gr.Button("Resolve Issue") | |
result_output = gr.Textbox(label="Result", interactive=False) | |
def on_analyze(token, repo_url, issue_number): | |
try: | |
parts = repo_url.split('/') | |
owner, repo = parts[-2], parts[-1] | |
# Initialize bot | |
bot.initialize_api(token) | |
# Get issue details | |
issue_url = f"{bot.github_api.base_url}/repos/{owner}/{repo}/issues/{issue_number}" | |
response = requests.get(issue_url, headers=bot.github_api.headers) | |
response.raise_for_status() | |
issue = response.json() | |
# Determine severity | |
severity = bot._determine_severity(issue) | |
return f"Issue Severity: {severity.name}" | |
except Exception as e: | |
return f"Error: {str(e)}" | |
def on_resolve(token, repo_url, ai_api_base, ai_api_key, issue_number, resolution, forked_repo): | |
try: | |
parts = repo_url.split('/') | |
owner, repo = parts[-2], parts[-1] | |
# Initialize bot with AI provider | |
ai_provider = CustomAIProvider(ai_api_base, ai_api_key) | |
bot.initialize_api(token, ai_provider) | |
result = bot.resolve_issue(token, owner, repo, int(issue_number), resolution, forked_repo) | |
return result | |
except Exception as e: | |
return f"Error: {str(e)}" | |
analyze_button.click( | |
on_analyze, | |
inputs=[token_input, repo_url_input, issue_number_input], | |
outputs=[severity_indicator] | |
) | |
resolve_button.click( | |
on_resolve, | |
inputs=[token_input, repo_url_input, ai_api_base, ai_api_key, | |
issue_number_input, resolution_input, forked_repo_input], | |
outputs=[result_output] | |
) | |
return demo |