Spaces:
Runtime error
Runtime error
# app.py - Improved version | |
import os | |
import gradio as gr | |
from github_ai_agent import GitHubAIAgent | |
import markdown | |
from typing import Tuple, Dict, Any, List | |
import google.generativeai as genai | |
def create_web_interface(agent: GitHubAIAgent) -> gr.Blocks: | |
"""Create enhanced Gradio web interface for the GitHub AI Agent with improved UI""" | |
# Custom CSS for better styling | |
custom_css = """ | |
.gradio-container { | |
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; | |
} | |
/* Repository Card */ | |
.repository-card { | |
border-radius: 10px; | |
box-shadow: 0 4px 6px rgba(0,0,0,0.1); | |
padding: 20px; | |
margin-bottom: 20px; | |
background-color: #f9f9f9; | |
transition: transform 0.2s, box-shadow 0.2s; | |
} | |
.repository-card:hover { | |
transform: translateY(-2px); | |
box-shadow: 0 6px 10px rgba(0,0,0,0.1); | |
} | |
/* Authentication Badge */ | |
.auth-badge { | |
display: inline-block; | |
padding: 5px 10px; | |
border-radius: 15px; | |
font-size: 12px; | |
font-weight: bold; | |
margin-bottom: 10px; | |
} | |
.auth-authenticated { | |
background-color: #d4edda; | |
color: #155724; | |
} | |
.auth-unauthenticated { | |
background-color: #f8d7da; | |
color: #721c24; | |
} | |
/* Metric Cards */ | |
.metric-container { | |
display: flex; | |
flex-wrap: wrap; | |
gap: 15px; | |
margin-top: 15px; | |
} | |
.metric-card { | |
flex: 1 1 100px; | |
padding: 15px; | |
border-radius: 8px; | |
background-color: #ffffff; | |
box-shadow: 0 2px 5px rgba(0,0,0,0.05); | |
text-align: center; | |
} | |
.metric-value { | |
font-size: 24px; | |
font-weight: bold; | |
margin: 10px 0; | |
color: #2980b9; | |
} | |
.metric-label { | |
font-size: 12px; | |
color: #7f8c8d; | |
} | |
/* Progress Bar */ | |
.progress-container { | |
width: 100%; | |
background-color: #e0e0e0; | |
border-radius: 5px; | |
margin: 10px 0; | |
} | |
.progress-bar { | |
height: 5px; | |
border-radius: 5px; | |
background-color: #3498db; | |
} | |
/* Theme Toggle Switch */ | |
.theme-switch-container { | |
display: flex; | |
align-items: center; | |
margin-bottom: 20px; | |
} | |
.theme-switch { | |
position: relative; | |
display: inline-block; | |
width: 60px; | |
height: 30px; | |
margin-left: 10px; | |
} | |
.theme-switch input { | |
opacity: 0; | |
width: 0; | |
height: 0; | |
} | |
.slider { | |
position: absolute; | |
cursor: pointer; | |
top: 0; | |
left: 0; | |
right: 0; | |
bottom: 0; | |
background-color: #ccc; | |
transition: .4s; | |
border-radius: 34px; | |
} | |
.slider:before { | |
position: absolute; | |
content: ""; | |
height: 22px; | |
width: 22px; | |
left: 4px; | |
bottom: 4px; | |
background-color: white; | |
transition: .4s; | |
border-radius: 50%; | |
} | |
input:checked + .slider { | |
background-color: #2196F3; | |
} | |
input:checked + .slider:before { | |
transform: translateX(30px); | |
} | |
/* Dark Theme Styles */ | |
.dark-theme { | |
background-color: #2c3e50; | |
color: #ecf0f1; | |
} | |
.dark-theme .metric-card { | |
background-color: #34495e; | |
color: #ecf0f1; | |
} | |
.dark-theme .metric-value { | |
color: #3498db; | |
} | |
.dark-theme .repository-card { | |
background-color: #34495e; | |
color: #ecf0f1; | |
} | |
/* Loading Animation */ | |
@keyframes pulse { | |
0% { opacity: 0.6; } | |
50% { opacity: 1; } | |
100% { opacity: 0.6; } | |
} | |
.loading { | |
animation: pulse 1.5s infinite; | |
border-radius: 8px; | |
background-color: #f0f0f0; | |
padding: 20px; | |
text-align: center; | |
} | |
/* Tooltip */ | |
.tooltip { | |
position: relative; | |
display: inline-block; | |
cursor: help; | |
} | |
.tooltip .tooltiptext { | |
visibility: hidden; | |
width: 200px; | |
background-color: #555; | |
color: #fff; | |
text-align: center; | |
border-radius: 6px; | |
padding: 5px; | |
position: absolute; | |
z-index: 1; | |
bottom: 125%; | |
left: 50%; | |
margin-left: -100px; | |
opacity: 0; | |
transition: opacity 0.3s; | |
} | |
.tooltip:hover .tooltiptext { | |
visibility: visible; | |
opacity: 1; | |
} | |
/* Improved tabs */ | |
.tab-nav { | |
border-bottom: 2px solid #e0e0e0; | |
margin-bottom: 20px; | |
} | |
.tab-button { | |
padding: 10px 20px; | |
background: transparent; | |
border: none; | |
cursor: pointer; | |
font-weight: bold; | |
color: #7f8c8d; | |
position: relative; | |
} | |
.tab-button.active { | |
color: #3498db; | |
} | |
.tab-button.active:after { | |
content: ''; | |
position: absolute; | |
bottom: -2px; | |
left: 0; | |
width: 100%; | |
height: 2px; | |
background-color: #3498db; | |
} | |
""" | |
def load_repository(repo_url: str, gemini_key: str, use_token: bool = False, github_token: str = "") -> Tuple[str, str, float]: | |
"""Load a repository (Gradio wrapper) with progress tracking""" | |
try: | |
if use_token and not github_token: | |
return "Error: GitHub token is required for private repositories.", "", 0.0 | |
# Set API keys | |
actual_github_token = github_token if use_token else "" | |
agent.set_api_keys(gemini_key, actual_github_token) | |
if not agent.config.gemini_api_key: | |
return "Error: Gemini API key is required.", "", 0.0 | |
# Progress indicators (for UI feedback) | |
progress_value = 0.1 | |
progress_message = "Initializing repository..." | |
yield progress_message, "", progress_value | |
# Load repository | |
progress_value = 0.2 | |
progress_message = f"Loading repository: {repo_url}" | |
yield progress_message, "", progress_value | |
result = agent.load_repository(repo_url) | |
if not result['success']: | |
return f"Error: {result['message']}", "", 0.0 | |
# Indicate progress | |
progress_value = 0.6 | |
progress_message = "Repository loaded. Building knowledge base..." | |
yield progress_message, "", progress_value | |
# Get repository insights | |
insights = agent.get_repository_insights() | |
# Create HTML report | |
analysis_content = insights['analysis']['analysis'].replace('\n', '<br>') if insights['success'] else "No analysis available." | |
# Calculate badge text and class | |
badge_text = "Private Repository" if use_token else "Public Repository" | |
badge_class = "auth-authenticated" if use_token else "auth-unauthenticated" | |
# Format date properly | |
created_at = result['repo_data'].get('created_at', 'Unknown') | |
if hasattr(created_at, 'strftime'): | |
created_at = created_at.strftime('%Y-%m-%d') | |
updated_at = result['repo_data'].get('updated_at', 'Unknown') | |
if hasattr(updated_at, 'strftime'): | |
updated_at = updated_at.strftime('%Y-%m-%d') | |
# Build HTML with improved styling | |
html_report = f""" | |
<div class="repository-card"> | |
<div class="auth-badge {badge_class}">{badge_text}</div> | |
<h2>Repository: {result['repo_data']['full_name']}</h2> | |
<p><strong>Description:</strong> {result['repo_data']['description'] or "No description available"}</p> | |
<div class="metric-container"> | |
<div class="metric-card"> | |
<div class="metric-label">Stars</div> | |
<div class="metric-value">{result['repo_data']['stars']}</div> | |
</div> | |
<div class="metric-card"> | |
<div class="metric-label">Forks</div> | |
<div class="metric-value">{result['repo_data']['forks']}</div> | |
</div> | |
<div class="metric-card"> | |
<div class="metric-label">Language</div> | |
<div class="metric-value">{result['repo_data']['language'] or "N/A"}</div> | |
</div> | |
<div class="metric-card"> | |
<div class="metric-label">Files</div> | |
<div class="metric-value">{result['file_count']}</div> | |
</div> | |
<div class="metric-card"> | |
<div class="metric-label">Contributors</div> | |
<div class="metric-value">{result['contributor_count']}</div> | |
</div> | |
</div> | |
<p><strong>Created:</strong> {created_at} | <strong>Last Updated:</strong> {updated_at}</p> | |
<p><strong>License:</strong> {result['repo_data'].get('license', 'None')}</p> | |
<h3>Topics</h3> | |
<p>{', '.join(result['repo_data'].get('topics', ['None'])) if result['repo_data'].get('topics') else 'None'}</p> | |
</div> | |
<h3>Repository Analysis</h3> | |
<div class="repository-card"> | |
{analysis_content} | |
</div> | |
""" | |
# Complete progress | |
progress_value = 1.0 | |
progress_message = "Analysis complete!" | |
yield progress_message, markdown.markdown(html_report), progress_value | |
return progress_message, markdown.markdown(html_report), progress_value | |
except Exception as e: | |
return f"Error: {str(e)}", "", 0.0 | |
def ask_question(question: str) -> str: | |
"""Ask a question about the repository (Gradio wrapper)""" | |
if not question: | |
return "Please enter a question." | |
if not agent.repository_loaded: | |
return "Please load a repository first." | |
try: | |
result = agent.answer_query(question) | |
if result['success']: | |
# Format the answer with improved styling | |
answer_content = result['answer'].replace('\n', '<br>') | |
html_response = f""" | |
<div class="repository-card"> | |
<h3>Answer</h3> | |
<div>{answer_content}</div> | |
""" | |
if result['relevant_files']: | |
html_response += f""" | |
<h4>Relevant Files:</h4> | |
<ul> | |
""" | |
for file in result['relevant_files']: | |
html_response += f"<li><code>{file}</code></li>" | |
html_response += "</ul>" | |
html_response += "</div>" | |
return markdown.markdown(html_response) | |
else: | |
return f"Error: {result['message']}" | |
except Exception as e: | |
return f"Error: {str(e)}" | |
def analyze_code(file_path: str, code_snippet: str) -> str: | |
"""Analyze code (Gradio wrapper)""" | |
if not file_path and not code_snippet: | |
return "Please provide either a file path or a code snippet." | |
try: | |
result = agent.analyze_code(file_path, code_snippet) | |
if result['success']: | |
# Format the analysis with improved styling | |
analysis_content = result['analysis'].replace('\n', '<br>') | |
html_response = f""" | |
<div class="repository-card"> | |
<h3>Code Analysis</h3> | |
<p><strong>{"File" if file_path else "Code Snippet"}:</strong> {file_path if file_path else "Provided snippet"}</p> | |
<div>{analysis_content}</div> | |
</div> | |
""" | |
return markdown.markdown(html_response) | |
else: | |
return f"Error: {result['message']}" | |
except Exception as e: | |
return f"Error: {str(e)}" | |
def find_collaborators(requirements: str) -> str: | |
"""Find potential collaborators (Gradio wrapper)""" | |
if not requirements: | |
return "Please provide requirements for collaborators." | |
if not agent.repository_loaded: | |
return "Please load a repository first." | |
try: | |
result = agent.find_collaborators(requirements) | |
if result['success']: | |
html_response = "<h3>Potential Collaborators</h3>" | |
for collaborator in result['collaborators']: | |
confidence = collaborator.get('confidence', 0) | |
confidence_percent = f"{confidence:.1%}" if isinstance(confidence, float) else "N/A" | |
# Apply color based on confidence | |
if isinstance(confidence, float): | |
if confidence >= 0.8: | |
confidence_color = "#27ae60" # Green for high confidence | |
elif confidence >= 0.5: | |
confidence_color = "#f39c12" # Yellow for medium confidence | |
else: | |
confidence_color = "#e74c3c" # Red for low confidence | |
else: | |
confidence_color = "#7f8c8d" # Gray for N/A | |
html_response += f""" | |
<div class="repository-card"> | |
<h4>{collaborator['login']}</h4> | |
<p><strong>Confidence: </strong><span style="color: {confidence_color}; font-weight: bold;">{confidence_percent}</span></p> | |
<p><strong>Reasons:</strong></p> | |
<ul> | |
""" | |
for reason in collaborator.get('reasons', []): | |
html_response += f"<li>{reason}</li>" | |
html_response += """ | |
</ul> | |
</div> | |
""" | |
return markdown.markdown(html_response) | |
else: | |
return f"Error: {result['message']}" | |
except Exception as e: | |
return f"Error: {str(e)}" | |
def get_insights() -> str: | |
"""Get repository insights (Gradio wrapper)""" | |
if not agent.repository_loaded: | |
return "Please load a repository first." | |
try: | |
result = agent.get_repository_insights() | |
if result['success']: | |
insights = result['insights'] | |
html_response = "<h2>Repository Insights</h2>" | |
if 'basic_stats' in insights: | |
html_response += """ | |
<div class="repository-card"> | |
<h3>Basic Statistics</h3> | |
<ul> | |
""" | |
html_response += f"<li><strong>Repository:</strong> {insights['basic_stats'].get('name', 'N/A')}</li>" | |
html_response += f"<li><strong>Description:</strong> {insights['basic_stats'].get('description', 'N/A')}</li>" | |
html_response += f"<li><strong>Stars:</strong> {insights['basic_stats'].get('stars', 0)}</li>" | |
html_response += f"<li><strong>Forks:</strong> {insights['basic_stats'].get('forks', 0)}</li>" | |
html_response += f"<li><strong>Age:</strong> {insights['basic_stats'].get('age_days', 0)} days</li>" | |
html_response += f"<li><strong>Primary Language:</strong> {insights['basic_stats'].get('primary_language', 'N/A')}</li>" | |
if 'topics' in insights['basic_stats']: | |
html_response += f"<li><strong>Topics:</strong> {', '.join(insights['basic_stats']['topics'])}</li>" | |
html_response += """ | |
</ul> | |
</div> | |
""" | |
if 'activity' in insights: | |
html_response += """ | |
<div class="repository-card"> | |
<h3>Activity</h3> | |
<div class="metric-container"> | |
""" | |
# Add metrics | |
metrics = [ | |
("Total Commits", insights['activity'].get('total_commits', 0)), | |
("Days Span", insights['activity'].get('days_span', 0)), | |
("Commits Per Day", insights['activity'].get('commits_per_day', 0)) | |
] | |
for label, value in metrics: | |
html_response += f""" | |
<div class="metric-card"> | |
<div class="metric-label">{label}</div> | |
<div class="metric-value">{value}</div> | |
</div> | |
""" | |
html_response += """ | |
</div> | |
<ul> | |
""" | |
if 'first_commit' in insights['activity']: | |
first_commit = insights['activity']['first_commit'] | |
if hasattr(first_commit, 'strftime'): | |
first_commit = first_commit.strftime('%Y-%m-%d') | |
html_response += f"<li><strong>First Commit:</strong> {first_commit}</li>" | |
if 'last_commit' in insights['activity']: | |
last_commit = insights['activity']['last_commit'] | |
if hasattr(last_commit, 'strftime'): | |
last_commit = last_commit.strftime('%Y-%m-%d') | |
html_response += f"<li><strong>Last Commit:</strong> {last_commit}</li>" | |
if 'monthly_activity' in insights['activity']: | |
html_response += "<li><strong>Monthly Activity:</strong><ul>" | |
for month_data in insights['activity']['monthly_activity'][:5]: | |
html_response += f"<li>{month_data['month']}: {month_data['commits']} commits</li>" | |
html_response += "</ul></li>" | |
html_response += """ | |
</ul> | |
</div> | |
""" | |
if 'contributors' in insights: | |
html_response += """ | |
<div class="repository-card"> | |
<h3>Contributors</h3> | |
<div class="metric-container"> | |
""" | |
# Add metrics | |
metrics = [ | |
("Total Contributors", insights['contributors'].get('total_contributors', 0)), | |
("Bus Factor", insights['contributors'].get('bus_factor', 0)) | |
] | |
for label, value in metrics: | |
html_response += f""" | |
<div class="metric-card"> | |
<div class="metric-label">{label}</div> | |
<div class="metric-value">{value}</div> | |
</div> | |
""" | |
html_response += """ | |
</div> | |
""" | |
if 'top_contributors' in insights['contributors']: | |
html_response += "<h4>Top Contributors</h4><ul>" | |
for contributor in insights['contributors']['top_contributors'][:5]: | |
html_response += f"<li><strong>{contributor['login']}:</strong> {contributor['contributions']} contributions" | |
if contributor['top_files']: | |
html_response += f" (Top files: {', '.join(contributor['top_files'][:3])})" | |
html_response += "</li>" | |
html_response += "</ul>" | |
html_response += """ | |
</div> | |
""" | |
if 'code' in insights: | |
html_response += """ | |
<div class="repository-card"> | |
<h3>Code</h3> | |
""" | |
if 'central_files' in insights['code']: | |
html_response += "<h4>Central Files</h4><ul>" | |
for file in insights['code']['central_files'][:5]: | |
html_response += f"<li><strong>{file['filename']}:</strong> {file['connections']} connections</li>" | |
html_response += "</ul>" | |
if 'frequently_modified_files' in insights['code']: | |
html_response += "<h4>Frequently Modified Files</h4><ul>" | |
for file in insights['code']['frequently_modified_files'][:5]: | |
html_response += f"<li><strong>{file['filename']}:</strong> {file['modifications']} modifications</li>" | |
html_response += "</ul>" | |
if 'file_types' in insights['code']: | |
html_response += "<h4>File Types</h4><ul>" | |
for file_type in insights['code']['file_types'][:5]: | |
ext = file_type['extension'] or 'No extension' | |
html_response += f"<li><strong>{ext}:</strong> {file_type['count']} files</li>" | |
html_response += "</ul>" | |
html_response += """ | |
</div> | |
""" | |
if 'issues' in insights: | |
html_response += """ | |
<div class="repository-card"> | |
<h3>Issues</h3> | |
<div class="metric-container"> | |
""" | |
# Add metrics | |
metrics = [ | |
("Total Issues", insights['issues'].get('total_issues', 0)), | |
("Open Issues", insights['issues'].get('open_issues', 0)), | |
("Closed Issues", insights['issues'].get('closed_issues', 0)), | |
("Resolution Rate", f"{insights['issues'].get('resolution_rate', 0) * 100:.1f}%") | |
] | |
for label, value in metrics: | |
html_response += f""" | |
<div class="metric-card"> | |
<div class="metric-label">{label}</div> | |
<div class="metric-value">{value}</div> | |
</div> | |
""" | |
html_response += """ | |
</div> | |
""" | |
if 'avg_days_to_close' in insights['issues']: | |
html_response += f"<p><strong>Average Days to Close:</strong> {insights['issues']['avg_days_to_close']}</p>" | |
if 'top_labels' in insights['issues']: | |
html_response += "<h4>Top Labels</h4><ul>" | |
for label in insights['issues']['top_labels']: | |
html_response += f"<li><strong>{label['label']}:</strong> {label['count']} issues</li>" | |
html_response += "</ul>" | |
html_response += """ | |
</div> | |
""" | |
return markdown.markdown(html_response) | |
else: | |
return f"Error: {result['message']}" | |
except Exception as e: | |
return f"Error: {str(e)}" | |
def show_visualizations() -> Tuple[str, str, str, str]: | |
"""Show repository visualizations (Gradio wrapper)""" | |
if not agent.repository_loaded: | |
return "No repository loaded.", "No repository loaded.","No repository loaded.","No repository loaded." | |
try: | |
result = agent.get_visualizations() | |
if result['success']: | |
visualizations = result['visualizations'] | |
repo_graph_html = "<div class='repository-card'><p>No repository graph available.</p></div>" | |
activity_chart_html = "<div class='repository-card'><p>No activity chart available.</p></div>" | |
contributor_network_html = "<div class='repository-card'><p>No contributor network available.</p></div>" | |
dependency_graph_html = "<div class='repository-card'><p>No dependency graph available.</p></div>" | |
for viz_type, path in visualizations.items(): | |
if os.path.exists(path): | |
with open(path, 'r', encoding='utf-8') as f: | |
content = f.read() | |
if viz_type == "repository_graph": | |
repo_graph_html = content | |
elif viz_type == "activity_chart": | |
activity_chart_html = content | |
elif viz_type == "contributor_network": | |
contributor_network_html = content | |
elif viz_type == "dependency_graph": | |
dependency_graph_html = content | |
return repo_graph_html, activity_chart_html, contributor_network_html, dependency_graph_html | |
else: | |
error_msg = f"<div class='repository-card'><p>Error: {result['message']}</p></div>" | |
return error_msg, error_msg, error_msg, error_msg | |
except Exception as e: | |
error_msg = f"<div class='repository-card'><p>Error: {str(e)}</p></div>" | |
return error_msg, error_msg, error_msg, error_msg | |
# Helper function to display API key tooltip | |
def api_key_tooltip(): | |
return """ | |
<div class="tooltip"> | |
<i>ⓘ</i> | |
<span class="tooltiptext">Your API key is used securely and not stored permanently.</span> | |
</div> | |
""" | |
# Create the Gradio interface | |
with gr.Blocks(css=custom_css, title="GitHub Repository AI Agent") as interface: | |
gr.HTML(""" | |
<div style="text-align: center; margin-bottom: 10px;"> | |
<h1>GitHub Repository AI Agent</h1> | |
<p>Analyze repositories, understand code, identify collaborators, and extract insights with Gemini AI</p> | |
</div> | |
""") | |
# Theme toggle | |
with gr.Row(): | |
with gr.Column(scale=1): | |
pass # Empty column for spacing | |
with gr.Column(scale=10): | |
theme_switch = gr.Checkbox(label="Dark Mode", value=False) | |
with gr.Column(scale=1): | |
pass # Empty column for spacing | |
with gr.Tab("Repository"): | |
with gr.Row(): | |
with gr.Column(): | |
gr.HTML("<h3>Repository Configuration</h3>") | |
repo_url = gr.Textbox(label="Repository URL", placeholder="https://github.com/username/repository") | |
gemini_api_key = gr.Textbox(label="Gemini API Key", type="password") | |
# Private repository toggle | |
repo_type = gr.Radio( | |
["Public Repository", "Private Repository"], | |
label="Repository Type", | |
value="Public Repository" | |
) | |
# GitHub token input (initially hidden) | |
with gr.Group(visible=False) as private_repo_group: | |
github_token = gr.Textbox( | |
label="GitHub Personal Access Token", | |
type="password", | |
placeholder="Enter your GitHub token for private repository access" | |
) | |
gr.HTML("<div style='font-size: 12px; color: #777;'>⚠️ Your token is used securely and not stored permanently.</div>") | |
load_btn = gr.Button("Load Repository", variant="primary") | |
# Loading status | |
load_status = gr.Textbox(label="Status", visible=True) | |
progress = gr.Slider( | |
minimum=0, | |
maximum=1, | |
value=0, | |
label="Loading Progress", | |
visible=True | |
) | |
repo_info = gr.HTML(label="Repository Information") | |
# Show/hide GitHub token based on repository type selection | |
repo_type.change( | |
fn=lambda x: gr.Group(visible=(x == "Private Repository")), | |
inputs=repo_type, | |
outputs=private_repo_group | |
) | |
# Load repository with progress updates | |
load_btn.click( | |
load_repository, | |
inputs=[ | |
repo_url, | |
gemini_api_key, | |
# Pass whether to use token based on repo type | |
repo_type.change(lambda x: x == "Private Repository", repo_type), | |
github_token | |
], | |
outputs=[load_status, repo_info, progress] | |
) | |
with gr.Tab("Ask Questions"): | |
with gr.Row(): | |
question = gr.Textbox( | |
label="Question", | |
placeholder="Ask about the repository (e.g., What's the main purpose of this repository?)", | |
lines=3 | |
) | |
ask_btn = gr.Button("Ask", variant="primary") | |
answer = gr.HTML(label="Answer") | |
ask_btn.click( | |
ask_question, | |
inputs=question, | |
outputs=answer | |
) | |
with gr.Tab("Code Analysis"): | |
with gr.Row(): | |
with gr.Column(): | |
file_path = gr.Textbox(label="File Path", placeholder="path/to/file.py") | |
code_snippet = gr.Code(label="Or paste code snippet", language="python", lines=10) | |
analyze_btn = gr.Button("Analyze", variant="primary") | |
code_analysis = gr.HTML(label="Analysis") | |
analyze_btn.click( | |
analyze_code, | |
inputs=[file_path, code_snippet], | |
outputs=code_analysis | |
) | |
with gr.Tab("Find Collaborators"): | |
with gr.Row(): | |
requirements = gr.Textbox( | |
label="Requirements", | |
placeholder="Describe what you're looking for in a collaborator (e.g., experience with machine learning, React development, etc.)", | |
lines=5 | |
) | |
find_btn = gr.Button("Find Collaborators", variant="primary") | |
collaborators = gr.HTML(label="Potential Collaborators") | |
find_btn.click( | |
find_collaborators, | |
inputs=requirements, | |
outputs=collaborators | |
) | |
with gr.Tab("Insights"): | |
insights_btn = gr.Button("Get Repository Insights", variant="primary") | |
insights_html = gr.HTML(label="Repository Insights") | |
insights_btn.click( | |
get_insights, | |
inputs=[], | |
outputs=insights_html | |
) | |
with gr.Tab("Visualizations"): | |
viz_btn = gr.Button("Show Visualizations", variant="primary") | |
# Create tabs for visualizations | |
viz_tabs = gr.Tabs([ | |
gr.TabItem("Repository Graph", gr.HTML(label="Repository Graph")), | |
gr.TabItem("Activity Chart", gr.HTML(label="Activity Chart")), | |
gr.TabItem("Contributor Network", gr.HTML(label="Contributor Network")), | |
gr.TabItem("Dependency Graph", gr.HTML(label="Dependency Graph")) | |
]) | |
viz_btn.click( | |
show_visualizations, | |
inputs=[], | |
outputs=[ | |
viz_tabs.select(0), | |
viz_tabs.select(1), | |
viz_tabs.select(2), | |
viz_tabs.select(3) | |
] | |
) | |
# Add theme toggle functionality | |
theme_switch.change( | |
fn=lambda x: gr.Blocks(css=custom_css + (".gradio-container {background-color: #2c3e50; color: #ecf0f1;}" if x else "")), | |
inputs=theme_switch, | |
outputs=interface | |
) | |
return interface | |
def main(): | |
"""Main function for running the GitHub AI Agent""" | |
agent = GitHubAIAgent() | |
interface = create_web_interface(agent) | |
interface.launch(show_api=False, server_name="0.0.0.0", server_port=int(os.environ.get('PORT', 7860))) | |
if __name__ == "__main__": | |
main() |