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/') 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)