faizee07's picture
V2
21ab451 verified
import os
import shutil
import tempfile
from flask import Flask, request, jsonify, render_template_string
import git
import json
import google.generativeai as genai
import traceback
# --- Configure API Key ---
API_KEY = os.environ.get("GOOGLE_API_KEY")
if API_KEY:
try:
genai.configure(api_key=API_KEY)
print("βœ… Google API Key configured successfully.")
except Exception as e:
print(f"🚨 ERROR: Failed to configure Google API Key. Error: {e}")
else:
print("🚨 WARNING: GOOGLE_API_KEY secret not set in Hugging Face Spaces.")
app = Flask(__name__)
HTML_TEMPLATE = """
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>AI README Generator 🧠</title>
<style>
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700&display=swap');
body {
font-family: 'Inter', sans-serif;
background-color: #f0f2f5;
color: #1c1e21;
margin: 0;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
min-height: 100vh;
padding: 20px;
box-sizing: border-box;
}
.container {
background-color: #ffffff;
padding: 40px 50px;
border-radius: 12px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
width: 100%;
max-width: 700px;
text-align: center;
}
h1 { font-size: 2.2em; color: #1877f2; margin-bottom: 10px; }
p { color: #606770; font-size: 1.1em; margin-bottom: 30px; }
.input-group { display: flex; margin-bottom: 20px; }
#repo-url { flex-grow: 1; padding: 15px; border: 1px solid #dddfe2; border-radius: 6px 0 0 6px; font-size: 1em; outline: none; min-width: 0; }
#repo-url:focus { border-color: #1877f2; box-shadow: 0 0 0 2px rgba(24, 119, 242, 0.2); }
button { padding: 15px 25px; border: none; background-color: #1877f2; color: white; font-size: 1em; font-weight: 600; border-radius: 0 6px 6px 0; cursor: pointer; transition: background-color 0.3s; }
button:hover { background-color: #166fe5; }
.loader { border: 4px solid #f3f3f3; border-top: 4px solid #1877f2; border-radius: 50%; width: 40px; height: 40px; animation: spin 1s linear infinite; margin: 30px auto; display: none; }
@keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }
#result-container { display: none; margin-top: 20px; }
#result { margin-top: 10px; padding: 20px; background-color: #f7f7f7; border: 1px solid #dddfe2; border-radius: 6px; text-align: left; white-space: pre-wrap; font-family: 'Courier New', Courier, monospace; max-height: 400px; overflow-y: auto; word-wrap: break-word; }
.action-buttons { text-align: right; margin-top: 15px; }
.action-buttons button {
padding: 10px 20px;
border-radius: 6px;
background-color: #e4e6eb;
color: #1c1e21;
font-weight: 600;
margin-left: 10px;
font-size: 0.9em;
}
.action-buttons button:hover { background-color: #d8dbdf; }
footer { margin-top: 25px; text-align: center; font-size: 0.9em; color: #8a8d91; }
footer a { color: #1877f2; text-decoration: none; font-weight: 600; }
footer a:hover { text-decoration: underline; }
</style>
</head>
<body>
<div class="container">
<h1>AI README Generator 🧠</h1>
<p>Enter a public GitHub repository URL and let a true AI agent analyze the code and generate a README for you.</p>
<form id="repo-form"><div class="input-group"><input type="url" id="repo-url" placeholder="e.g., https://github.com/user/project" required><button type="submit">Generate</button></div></form>
<div class="loader" id="loader"></div>
<div id="result-container">
<h2>Generated README.md:</h2>
<pre id="result"></pre>
<div class="action-buttons">
<button id="copy-btn">Copy</button>
<button id="download-btn">Download .md</button>
</div>
</div>
</div>
<footer>
<p>Made with ❀️ by <a href="https://asadfaizee.is-a.dev/" target="_blank" rel="noopener noreferrer">Asad Faizee</a></p>
</footer>
<script>
const repoForm = document.getElementById('repo-form');
const loader = document.getElementById('loader');
const resultContainer = document.getElementById('result-container');
const resultDiv = document.getElementById('result');
const copyBtn = document.getElementById('copy-btn');
const downloadBtn = document.getElementById('download-btn');
repoForm.addEventListener('submit', async function(event) {
event.preventDefault();
const url = document.getElementById('repo-url').value;
loader.style.display = 'block';
resultContainer.style.display = 'none';
resultDiv.textContent = '';
try {
const response = await fetch('/generate', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ url: url }) });
const data = await response.json();
if (response.ok) {
resultDiv.textContent = data.readme;
} else {
resultDiv.textContent = 'Error: ' + data.error;
}
resultContainer.style.display = 'block';
} catch (error) {
resultDiv.textContent = 'An unexpected error occurred: ' + error.toString();
resultContainer.style.display = 'block';
} finally {
loader.style.display = 'none';
}
});
copyBtn.addEventListener('click', function() {
const resultText = resultDiv.textContent;
const textArea = document.createElement('textarea');
textArea.value = resultText;
document.body.appendChild(textArea);
textArea.select();
document.execCommand('copy');
document.body.removeChild(textArea);
this.textContent = 'Copied!';
setTimeout(() => { this.textContent = 'Copy'; }, 2000);
});
downloadBtn.addEventListener('click', function() {
const resultText = resultDiv.textContent;
const blob = new Blob([resultText], { type: 'text/markdown' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'README.md';
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
});
</script>
</body>
</html>
"""
def generate_readme_with_llm(repo_path):
print("Step 4: Starting to analyze repository files.")
file_structure = ""
file_contents = ""
for root, _, files in os.walk(repo_path):
if '.git' in root:
continue
level = root.replace(repo_path, '').count(os.sep)
indent = ' ' * 4 * level
file_structure += f"{indent}{os.path.basename(root)}/\n"
sub_indent = ' ' * 4 * (level + 1)
for f in files[:5]:
file_structure += f"{sub_indent}{f}\n"
try:
with open(os.path.join(root, f), 'r', errors='ignore') as file:
content = file.read(2000)
file_contents += f"\n--- Start of {f} ---\n{content}\n--- End of {f} ---\n"
except Exception:
continue
prompt = f"""
You are an expert technical writer. Analyze the repository context below and generate a professional README.md.
**File Structure:**
```
{file_structure}
```
**Key File Contents:**
```
{file_contents}
```
Generate a README.md with these sections: Project Title, About the Project, Getting Started, and Usage.
- Infer purpose, technologies, and setup commands.
- Output must be valid Markdown.
"""
try:
print("Step 5: Sending request to Gemini API...")
# Reverted model name back to user's preference.
model = genai.GenerativeModel('gemini-2.5-flash')
response = model.generate_content(prompt)
print("Step 6: Received response from Gemini API.")
readme_text = response.text.strip().replace("```markdown", "").replace("```", "")
return readme_text
except Exception as e:
print(f"🚨🚨🚨 CRITICAL ERROR during Gemini API call: {e}")
raise
@app.route('/')
def index():
return render_template_string(HTML_TEMPLATE)
@app.route('/generate', methods=['POST'])
def generate():
print("\n--- NEW REQUEST ---")
print("Step 1: Received request to /generate.")
data = request.get_json()
if not data or 'url' not in data:
return jsonify({"error": "Request body must be JSON with a 'url' key."}), 400
repo_url = data.get('url')
if not repo_url or "github.com" not in repo_url.lower():
print(f"Validation failed for URL: {repo_url}")
return jsonify({"error": "A valid public GitHub repository URL is required."}), 400
if not API_KEY:
return jsonify({"error": "Server is missing the GOOGLE_API_KEY. Cannot contact the LLM."}), 500
temp_dir = tempfile.mkdtemp()
try:
print(f"Step 2: Cloning repository: {repo_url} into {temp_dir}")
git.Repo.clone_from(repo_url, temp_dir)
print("Step 3: Cloning successful.")
readme_content = generate_readme_with_llm(temp_dir)
print("Step 7: Successfully generated README. Sending response.")
return jsonify({"readme": readme_content})
except Exception as e:
print(f"🚨🚨🚨 AN UNEXPECTED ERROR OCCURRED in /generate route 🚨🚨🚨")
traceback.print_exc()
return jsonify({"error": "An unexpected server error occurred. Please check the logs."}), 500
finally:
print(f"Step 8: Cleaning up temporary directory: {temp_dir}")
shutil.rmtree(temp_dir)
if __name__ == '__main__':
app.run(host='0.0.0.0', port=int(os.environ.get("PORT", 7860)))