GitHubHelper / app.py
MingZ6's picture
Add Dockerfile, update agents and app for environment variable support, and enhance requirements
f57b851
from flask import Flask, render_template, request, jsonify, url_for, send_from_directory
import requests
from bs4 import BeautifulSoup
from agents import SummarizerAgent, InsightAgent, RecommenderAgent, QuestionGeneratorAgent, CLISetupAgent, ChatbotAgent, PRReviewAgent
from github_utils import get_repo_content, get_repo_structure, get_repo_metadata, is_github_url, get_pr_details, get_target_branch_code, verify_github_credentials
import os
app = Flask(__name__)
summarizer = SummarizerAgent()
insight_agent = InsightAgent()
recommender_agent = RecommenderAgent()
question_generator = QuestionGeneratorAgent()
cli_setup_agent = CLISetupAgent()
chatbot_agent = ChatbotAgent() # Initialize the new ChatbotAgent
pr_review_agent = PRReviewAgent() # Initialize the new PRReviewAgent
@app.route('/templates/<path:filename>')
def serve_template_file(filename):
"""Serve static files from the templates directory."""
return send_from_directory(os.path.join(app.root_path, 'templates'), filename)
@app.route("/", methods=["GET", "POST"])
def index():
repo_data = None
insights = None
if request.method == "POST":
repo_url = request.form.get("repo_url", "")
if repo_url and is_github_url(repo_url):
# Get repository content
repo_data = {
"url": repo_url,
"metadata": get_repo_metadata(repo_url),
"structure": get_repo_structure(repo_url),
"content": get_repo_content(repo_url)
}
return render_template(
"index.html", repo_data=repo_data, insights=insights
)
@app.route("/summarize", methods=["POST"])
def summarize():
repo_content = request.json.get("content", {})
if not repo_content:
return jsonify({"error": "No content provided"}), 400
try:
# Generate summaries for each file
summaries = {}
for filename, content in repo_content.items():
if isinstance(content, str) and len(content) > 0:
# Limit content to 1000 words to avoid token limits
words = content.split()
if len(words) > 1000:
content_for_summary = " ".join(words[:1000])
else:
content_for_summary = content
# Generate summary
summary = summarizer.process(content_for_summary)
summaries[filename] = summary
return jsonify({"summaries": summaries})
except Exception as e:
return jsonify({"error": f"Error generating summaries: {str(e)}"}), 500
@app.route("/analyze", methods=["POST"])
def analyze():
summaries = request.json.get("summaries", {})
if not summaries:
return jsonify({"error": "No summaries provided"}), 400
try:
# Generate insights from all summaries
summary_texts = list(summaries.values())
insights = insight_agent.process_text(summary_texts)
return jsonify({"insights": insights})
except Exception as e:
return jsonify({"error": f"Error generating insights: {str(e)}"}), 500
@app.route("/recommend", methods=["POST"])
def recommend():
data = request.json
insights = data.get("insights", "")
summaries = data.get("summaries", [])
user_goal = data.get("goal", "")
persona = data.get("persona", "")
if not insights or not summaries:
return jsonify({"error": "Missing required data"}), 400
try:
recommendations = recommender_agent.process(
insights, summaries, user_goal, persona
)
next_query = recommender_agent.suggest_next_query(
insights, summaries, user_goal, persona
)
return jsonify({"recommendations": recommendations, "next_query": next_query})
except Exception as e:
return jsonify({"error": f"Error generating recommendations: {str(e)}"}), 500
@app.route("/generate_questions", methods=["POST"])
def generate_questions():
data = request.json
content = data.get("content", "")
category = data.get("category", "repository")
source = data.get("source", "")
if not content or not source:
return jsonify({"error": "Missing required data"}), 400
try:
questions = question_generator.generate_questions(content, category, source)
return jsonify({"questions": questions})
except Exception as e:
return jsonify({"error": f"Error generating questions: {str(e)}"}), 500
@app.route("/workflow", methods=["POST"])
def workflow():
"""Complete workflow from repository URL to recommendations."""
repo_url = request.json.get("repo_url", "")
user_goal = request.json.get("goal", "Understand the codebase")
persona = request.json.get("persona", "Developer")
github_auth = request.json.get("github_auth", None)
if not repo_url or not is_github_url(repo_url):
return jsonify({"error": "Valid GitHub repository URL required"}), 400
try:
# Prepare authentication if provided
auth = None
if github_auth and 'username' in github_auth and 'token' in github_auth:
auth = (github_auth['username'], github_auth['token'])
# Step 1: Get repository content
repo_content = get_repo_content(repo_url, auth=auth)
if "error" in repo_content:
return jsonify({"error": repo_content["error"]}), 500
# Get repository metadata
repo_metadata = get_repo_metadata(repo_url, auth=auth)
# Ensure repo_metadata has the URL
if "url" not in repo_metadata:
repo_metadata["url"] = repo_url
# Step 2: Generate summaries
summaries = {}
for filename, content in repo_content.items():
words = content.split()
if len(words) > 1000:
content_for_summary = " ".join(words[:1000])
else:
content_for_summary = content
summary = summarizer.process(content_for_summary)
summaries[filename] = summary
# New Step: Generate CLI setup instructions
try:
cli_setup = cli_setup_agent.generate_setup_instructions(repo_content, repo_metadata)
if not cli_setup or len(cli_setup.strip()) < 10:
cli_setup = "Sorry, couldn't generate setup instructions for this repository."
except Exception as e:
print(f"Error in CLI setup generation: {str(e)}")
cli_setup = "Error generating setup instructions. Please check the repository and try again."
# Step 3: Generate insights
summary_texts = list(summaries.values())
insights = insight_agent.process_text(summary_texts)
# Step 4: Generate recommendations
recommendations = recommender_agent.process(
insights, summary_texts, user_goal, persona
)
# Step 5: Suggest next exploration area
next_area = recommender_agent.suggest_next_query(
insights, summary_texts, user_goal, persona
)
# Step 6: Generate questions
repo_name = repo_metadata.get("name", "GitHub Repository")
questions = question_generator.generate_questions(
repo_name, "repository", repo_url
)
return jsonify({
"summaries": summaries,
"cli_setup": cli_setup,
"insights": insights,
"recommendations": recommendations,
"next_area": next_area,
"questions": questions,
"repo_content": repo_content, # Add repository content for the chatbot
"repo_metadata": repo_metadata # Add repository metadata for the chatbot
})
except Exception as e:
return jsonify({"error": f"Error in workflow: {str(e)}"}), 500
@app.route("/chat", methods=["POST"])
def chat():
"""Handle chatbot questions about a repository."""
data = request.json
question = data.get("question", "")
repo_url = data.get("repo_url", "")
repo_content = data.get("repo_content", {})
repo_metadata = data.get("repo_metadata", {})
summaries = data.get("summaries", {})
insights = data.get("insights", "")
github_auth = data.get("github_auth", None)
if not question:
return jsonify({"error": "No question provided"}), 400
# Prepare authentication if provided
auth = None
if github_auth and 'username' in github_auth and 'token' in github_auth:
auth = (github_auth['username'], github_auth['token'])
if not repo_content and repo_url:
# If content isn't provided but URL is, fetch the repository content
if is_github_url(repo_url):
repo_content = get_repo_content(repo_url, auth=auth)
repo_metadata = get_repo_metadata(repo_url, auth=auth)
# Ensure repo_metadata has the URL
if "url" not in repo_metadata:
repo_metadata["url"] = repo_url
else:
return jsonify({"error": "Valid GitHub repository URL required"}), 400
if not repo_content:
return jsonify({"error": "No repository content provided"}), 400
try:
# Use the chatbot agent to answer the question
answer = chatbot_agent.answer_question(
question=question,
repo_content=repo_content,
repo_metadata=repo_metadata,
summaries=summaries,
insights=insights
)
return jsonify({
"answer": answer,
"question": question
})
except Exception as e:
return jsonify({"error": f"Error answering question: {str(e)}"}), 500
@app.route("/review_pr", methods=["POST"])
def review_pr():
"""Review a GitHub Pull Request and provide professional code suggestions."""
pr_url = request.json.get("pr_url", "")
max_files = request.json.get("max_files", 25) # Default to 25 files
file_types = request.json.get("file_types", None) # Default to all code files
github_auth = request.json.get("github_auth", None) # GitHub authentication
if not pr_url:
return jsonify({"error": "No PR URL provided"}), 400
try:
# Prepare authentication if provided
auth = None
if github_auth and 'username' in github_auth and 'token' in github_auth:
auth = (github_auth['username'], github_auth['token'])
# Step 1: Fetch PR details
pr_details = get_pr_details(pr_url, max_files=max_files, file_types=file_types, auth=auth)
if "error" in pr_details:
return jsonify({"error": pr_details["error"]}), 500
# Step 2: Fetch target branch code
target_branch_code = get_target_branch_code(pr_url, max_files=max_files, file_types=file_types, auth=auth)
if "error" in target_branch_code:
return jsonify({"error": target_branch_code["error"]}), 500
# Step 3: Generate PR review
review_result = pr_review_agent.review_pr(pr_details, target_branch_code)
# Step 4: Return the results
return jsonify({
"pr_title": pr_details.get("title", ""),
"pr_user": pr_details.get("user", ""),
"target_branch": pr_details.get("target_branch", ""),
"source_branch": pr_details.get("source_branch", ""),
"changed_files_count": len(pr_details.get("changed_files", [])),
"total_file_count": pr_details.get("total_file_count", 0),
"review": review_result.get("review", "Error generating review"),
"analyzed_files": [file["filename"] for file in pr_details.get("changed_files", [])]
})
except Exception as e:
return jsonify({"error": f"Error reviewing PR: {str(e)}"}), 500
@app.route("/verify_github_credentials", methods=["POST"])
def verify_credentials():
"""Verify GitHub credentials and return status."""
data = request.json
github_username = data.get("github_username", "")
github_token = data.get("github_token", "")
if not github_username or not github_token:
return jsonify({"valid": False, "error": "Missing username or token"}), 400
# Verify the credentials
is_valid = verify_github_credentials(github_username, github_token)
if is_valid:
return jsonify({"valid": True, "message": "Successfully authenticated with GitHub"})
else:
return jsonify({"valid": False, "error": "Invalid GitHub credentials"}), 401
if __name__ == "__main__":
# Use environment variables for port if available (needed for Hugging Face)
port = int(os.environ.get('PORT', 5001))
# Bind to 0.0.0.0 instead of 127.0.0.1 to be accessible from outside the container
app.run(debug=False, host='0.0.0.0', port=port)