Rob200/LegacyCodeDocumentAgent
0
1"""2Legacy Code Documentation Agent - Hugging Face Demo3A cost-controlled demo version for public showcase.4"""5 6import streamlit as st7import os8import time9from datetime import datetime, timedelta10from collections import defaultdict11 12# Import local modules13from file_handler import read_code_file, EXTENSION_MAP14from agent import generate_documentation15from pdf_exporter import markdown_to_pdf16from run_logger import log_run, get_summary17 18# =============================================================================19# DEMO CONFIGURATION20# =============================================================================21DEMO_MODE = True22MAX_GENERATIONS_PER_HOUR = 1023GITHUB_REPO = "https://github.com/Robby-Burns/LegacyCodeDocumentAgent"24 25# Sample files for demo (these will be created if they don't exist)26SAMPLE_FILES = {27 "sample_delinquent_loans.sql": '''CREATE PROCEDURE GetDelinquentLoans @DaysOverdue INT = 30,28 @LoanType VARCHAR(20) = NULL29AS30 BEGIN31 SET32 NOCOUNT ON;33 34 SELECT m.MemberID,35 m.FirstName + ' ' + m.LastName AS MemberName,36 l.LoanID,37 l.LoanType,38 l.OriginalAmount,39 l.CurrentBalance,40 l.PaymentDueDate,41 DATEDIFF(DAY, l.PaymentDueDate, GETDATE()) AS DaysDelinquent,42 l.InterestRate,43 la.AccountStatus44 FROM Members m45 INNER JOIN Loans l ON m.MemberID = l.MemberID46 INNER JOIN LoanAccounts la ON l.LoanID = la.LoanID47 WHERE l.PaymentDueDate < DATEADD(DAY, -@DaysOverdue, GETDATE())48 AND la.AccountStatus = 'Active'49 AND (@LoanType IS NULL OR l.LoanType = @LoanType)50 ORDER BY DaysDelinquent DESC;51 END''',52 53 "sample_interest_calc.py": '''def calculate_compound_interest(principal, rate, years, compounds_per_year=12):54 """55 Calculate compound interest for a loan or savings account.56 """57 if principal <= 0 or rate < 0 or years < 0:58 raise ValueError("Invalid input values")59 60 # Convert annual rate to decimal61 r = rate / 10062 63 # Compound interest formula: A = P(1 + r/n)^(nt)64 amount = principal * (1 + r / compounds_per_year) ** (compounds_per_year * years)65 66 interest_earned = amount - principal67 68 return {69 "principal": principal,70 "final_amount": round(amount, 2),71 "interest_earned": round(interest_earned, 2),72 "rate_percent": rate,73 "years": years74 }75 76 77def calculate_monthly_payment(loan_amount, annual_rate, loan_term_months):78 """79 Calculate monthly payment for a fixed-rate loan.80 Used for auto loans, personal loans, and mortgages.81 """82 monthly_rate = (annual_rate / 100) / 1283 84 if monthly_rate == 0:85 return loan_amount / loan_term_months86 87 payment = loan_amount * (monthly_rate * (1 + monthly_rate)**loan_term_months) / ((1 + monthly_rate)**loan_term_months - 1)88 89 return round(payment, 2)''',90 91 "sample_loan_measures.dax": '''Total Outstanding Loans = 92CALCULATE(93 SUM(Loans[CurrentBalance]),94 Loans[AccountStatus] = "Active",95 Loans[LoanType] IN {"Auto", "Personal", "Mortgage", "HELOC"}96)97 98Delinquent Loan Count = 99CALCULATE(100 COUNTROWS(Loans),101 Loans[DaysDelinquent] > 30,102 Loans[AccountStatus] = "Active"103)104 105Average Loan Amount by Type = 106AVERAGEX(107 SUMMARIZE(108 Loans,109 Loans[LoanType],110 "AvgAmount", AVERAGE(Loans[OriginalAmount])111 ),112 [AvgAmount]113)'''114}115 116# =============================================================================117# PAGE CONFIGURATION118# =============================================================================119st.set_page_config(120 page_title="Legacy Code Documentation Agent - Demo",121 page_icon="π",122 layout="wide"123)124 125# Custom CSS126st.markdown("""127<style>128 .main-header {129 font-size: 2.5rem;130 font-weight: bold;131 color: #5dade2;132 margin-bottom: 0.5rem;133 }134 .sub-header {135 font-size: 1.2rem;136 color: #aab7b8;137 margin-bottom: 2rem;138 }139 .demo-banner {140 background-color: #1a3a4a;141 border: 1px solid #5dade2;142 border-radius: 0.5rem;143 padding: 1rem;144 margin-bottom: 1rem;145 color: #ffffff;146 }147 .demo-banner a {148 color: #f9e79f;149 font-weight: bold;150 }151 .rate-limit-box {152 background-color: #1a3a4a;153 border: 1px solid #5dade2;154 border-radius: 0.5rem;155 padding: 0.5rem 1rem;156 margin: 1rem 0;157 color: #ffffff;158 }159</style>160""", unsafe_allow_html=True)161 162# =============================================================================163# RATE LIMITING164# =============================================================================165def init_rate_limiting():166 """Initialize rate limiting in session state."""167 if "generation_times" not in st.session_state:168 st.session_state.generation_times = []169 170 171def check_rate_limit() -> tuple[bool, int]:172 """173 Check if user is within rate limit.174 Returns (is_allowed, remaining_count)175 """176 init_rate_limiting()177 178 # Remove timestamps older than 1 hour179 one_hour_ago = datetime.now() - timedelta(hours=1)180 st.session_state.generation_times = [181 t for t in st.session_state.generation_times182 if t > one_hour_ago183 ]184 185 current_count = len(st.session_state.generation_times)186 remaining = MAX_GENERATIONS_PER_HOUR - current_count187 188 return remaining > 0, remaining189 190 191def record_generation():192 """Record a generation timestamp."""193 init_rate_limiting()194 st.session_state.generation_times.append(datetime.now())195 196 197# =============================================================================198# SAMPLE FILE MANAGEMENT199# =============================================================================200def ensure_sample_files():201 """Create sample files if they don't exist."""202 os.makedirs("sample_files", exist_ok=True)203 204 for filename, content in SAMPLE_FILES.items():205 filepath = os.path.join("sample_files", filename)206 if not os.path.exists(filepath):207 with open(filepath, "w", encoding="utf-8") as f:208 f.write(content)209 210 211def get_sample_files() -> list:212 """Get list of sample files."""213 ensure_sample_files()214 return list(SAMPLE_FILES.keys())215 216 217# =============================================================================218# HELPER FUNCTIONS219# =============================================================================220def init_session_state():221 """Initialize session state variables."""222 if "documentation" not in st.session_state:223 st.session_state.documentation = None224 if "doc_result" not in st.session_state:225 st.session_state.doc_result = None226 if "file_info" not in st.session_state:227 st.session_state.file_info = None228 229 230def generate_pdf_bytes(markdown_content: str) -> bytes:231 """Generate PDF and return as bytes for download."""232 import tempfile233 temp_dir = tempfile.mkdtemp()234 pdf_path = os.path.join(temp_dir, "documentation.pdf")235 236 result = markdown_to_pdf(markdown_content, pdf_path)237 238 if result["success"]:239 with open(pdf_path, "rb") as f:240 return f.read()241 return None242 243 244def display_metrics():245 """Display run history metrics in the sidebar."""246 summary = get_summary()247 248 st.sidebar.markdown("---")249 st.sidebar.markdown("### π Demo Statistics")250 251 col1, col2 = st.sidebar.columns(2)252 col1.metric("Total Runs", summary["total_runs"])253 254 success_rate = 0255 if summary["total_runs"] > 0:256 success_rate = (summary['successful_runs'] / summary['total_runs']) * 100257 258 col2.metric("Success Rate", f"{success_rate:.0f}%")259 260 261# =============================================================================262# MAIN APPLICATION263# =============================================================================264def main():265 """Main Streamlit application."""266 init_session_state()267 ensure_sample_files()268 269 # Header270 st.markdown('<p class="main-header">π Legacy Code Documentation Agent</p>',271 unsafe_allow_html=True)272 st.markdown('<p class="sub-header">AI-powered documentation for legacy code files</p>',273 unsafe_allow_html=True)274 275 # Demo Banner276 st.markdown(f"""277 <div class="demo-banner">278 <strong>π― Demo Mode</strong><br>279 This is a limited demo showcasing the agent's capabilities using sample files.<br>280 <a href="{GITHUB_REPO}" target="_blank">β Get the full version on GitHub</a> β 281 includes file uploads, batch processing, and more!282 </div>283 """, unsafe_allow_html=True)284 285 # Sidebar286 st.sidebar.image("https://img.icons8.com/color/96/000000/source-code.png", width=80)287 st.sidebar.markdown("### π About This Demo")288 st.sidebar.markdown("""289 This agent automatically generates professional documentation from legacy code.290 291 **Features:**292 - Multi-language (SQL, Python, C++, DAX)293 - Credit Union domain knowledge294 - 7-section professional reports295 - PDF export296 """)297 298 st.sidebar.markdown("---")299 st.sidebar.markdown("### π Supported Languages")300 for ext, lang in EXTENSION_MAP.items():301 st.sidebar.markdown(f"- `{ext}` β {lang}")302 303 display_metrics()304 305 st.sidebar.markdown("---")306 st.sidebar.markdown(f"[π¦ View on GitHub]({GITHUB_REPO})")307 308 # Main content309 tab1, tab2, tab3 = st.tabs(["π€ Generate Documentation", "π How It Works", "βΉοΈ About"])310 311 with tab1:312 # Rate limit check313 is_allowed, remaining = check_rate_limit()314 315 st.markdown(f"""316 <div class="rate-limit-box">317 β±οΈ <strong>Rate Limit:</strong> {remaining}/{MAX_GENERATIONS_PER_HOUR} generations remaining this hour318 </div>319 """, unsafe_allow_html=True)320 321 st.markdown("### Select a Sample File")322 323 sample_files = get_sample_files()324 selected_file = st.selectbox(325 "Choose a sample code file to document:",326 sample_files,327 format_func=lambda x: f"{x} ({EXTENSION_MAP.get(os.path.splitext(x)[1], 'Unknown')})"328 )329 330 if selected_file:331 filepath = os.path.join("sample_files", selected_file)332 file_result = read_code_file(filepath)333 334 if file_result["success"]:335 # Show code preview336 with st.expander("π View Source Code", expanded=False):337 st.code(file_result["content"], language=file_result["language"].lower())338 339 st.markdown("---")340 341 col1, col2, col3 = st.columns([1, 1, 2])342 343 with col1:344 generate_btn = st.button(345 "π Generate Documentation",346 type="primary",347 use_container_width=True,348 disabled=not is_allowed349 )350 351 with col2:352 include_pdf = st.checkbox("Include PDF", value=True)353 354 if not is_allowed:355 st.warning("β±οΈ Rate limit reached. Please try again in an hour.")356 357 if generate_btn and is_allowed:358 st.session_state.file_info = file_result359 360 with st.spinner("π€ AI is analyzing your code... (15-30 seconds)"):361 doc_result = generate_documentation(362 filename=file_result["filename"],363 language=file_result["language"],364 code_content=file_result["content"]365 )366 367 if not doc_result["success"]:368 st.error(f"β Error: {doc_result['error']}")369 else:370 record_generation() # Record for rate limiting371 372 st.session_state.doc_result = doc_result373 374 full_content = f"# Documentation: {file_result['filename']}\n\n"375 full_content += f"*Generated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}*\n\n"376 full_content += "---\n\n"377 full_content += doc_result["documentation"]378 379 st.session_state.documentation = full_content380 381 # Log the run382 log_run(383 filename=file_result["filename"],384 language=file_result["language"],385 model=doc_result["model_used"],386 input_tokens=doc_result["usage"]["input_tokens"],387 output_tokens=doc_result["usage"]["output_tokens"],388 cost=doc_result["usage"]["estimated_cost"],389 output_path="huggingface_demo",390 pdf_path="huggingface_demo" if include_pdf else None,391 success=True392 )393 394 st.success("β
Documentation generated successfully!")395 396 # Display results397 if st.session_state.documentation:398 st.markdown("---")399 st.markdown("### π Generated Documentation")400 401 if st.session_state.doc_result:402 usage = st.session_state.doc_result["usage"]403 col1, col2, col3, col4 = st.columns(4)404 col1.metric("Model", st.session_state.doc_result["model_used"])405 col2.metric("Input Tokens", f"{usage['input_tokens']:,}")406 col3.metric("Output Tokens", f"{usage['output_tokens']:,}")407 col4.metric("Cost", f"${usage['estimated_cost']:.4f}")408 409 st.markdown("#### Download Options:")410 d_col1, d_col2, d_col3 = st.columns([1, 1, 2])411 412 with d_col1:413 st.download_button(414 label="π₯ Download Markdown",415 data=st.session_state.documentation,416 file_name=f"{st.session_state.file_info['filename']}_documentation.md",417 mime="text/markdown",418 use_container_width=True419 )420 421 with d_col2:422 if include_pdf:423 with st.spinner("Generating PDF..."):424 pdf_bytes = generate_pdf_bytes(st.session_state.documentation)425 426 if pdf_bytes:427 st.download_button(428 label="π₯ Download PDF",429 data=pdf_bytes,430 file_name=f"{st.session_state.file_info['filename']}_documentation.pdf",431 mime="application/pdf",432 use_container_width=True433 )434 435 st.markdown("#### Preview:")436 st.markdown(st.session_state.documentation)437 438 with tab2:439 st.markdown("### π§ How It Works")440 441 st.markdown("""442```443 βββββββββββββββββββ βββββββββββββββββββ βββββββββββββββββββ444 β Your Code ββββββΆβ AI Analysis ββββββΆβ Documentation β445 β (SQL/Python/ β β (LLM API) β β (Markdown/PDF) β446 β C++/DAX) β β β β β447 βββββββββββββββββββ βββββββββββββββββββ βββββββββββββββββββ448```449 450 #### The 7-Section Report:451 452 1. **Overview** β Plain-English summary for managers453 2. **Business Logic** β Decision rules and conditions 454 3. **Inputs** β Parameters and data sources (table format)455 4. **Outputs** β Results and return values (table format)456 5. **Dependencies** β External tables, procedures, functions457 6. **Data Relationships** β JOIN explanations with business context458 7. **Best Practices Review** β Actionable improvement recommendations459 460 #### Key Features:461 462 - π¦ **Credit Union Domain Knowledge** β Understands Member, Loan, Share terminology463 - π **LLM Agnostic** β Works with OpenAI, Google Gemini, or Anthropic Claude464 - π **Batch Processing** β Document entire folders at once (full version)465 - π° **Cost Tracking** β Monitor API usage and costs466 """)467 468 with tab3:469 st.markdown("### βΉοΈ About This Project")470 471 st.markdown(f"""472 **Legacy Code Documentation Agent** is an AI-powered tool built for Credit Union IT teams473 to automatically generate professional documentation from legacy code.474 475 #### π― The Problem476 Legacy code often lacks documentation, making it hard to:477 - Onboard new team members478 - Understand business logic479 - Maintain compliance requirements480 481 #### π‘ The Solution482 This agent reads your code and generates comprehensive, professional documentation483 in seconds β for less than $0.001 per file.484 485 ---486 487 #### π Get the Full Version488 489 This demo is limited to sample files. The full version includes:490 - β
File uploads491 - β
Batch folder processing 492 - β
Excel run history493 - β
Command-line interface494 495 **[β View on GitHub]({GITHUB_REPO})**496 497 ---498 499 *Built by [Robby Burns](https://www.robbyburns.online/) β AI/ML Product Manager*500 """)501 502 503if __name__ == "__main__":504 main()