Team Ai
Apppublic

Rob200/LegacyCodeDocumentAgent

sourceHugging Facemitupdated 8mo agoView on Hugging Face
0likes
app.py504 linesDownload Raw Back to root
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()