From fd1a4fb38cad01fa2cab67385bf7607d7ff05685 Mon Sep 17 00:00:00 2001 From: Aravind Raju <80222285+aravindraju007@users.noreply.github.com> Date: Sat, 25 Jul 2026 05:51:03 +0530 Subject: [PATCH 1/3] Add files via upload feat: add zero-click AI pipeline --- .github/workflows/pr_analyzer.yml | 48 +++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 .github/workflows/pr_analyzer.yml diff --git a/.github/workflows/pr_analyzer.yml b/.github/workflows/pr_analyzer.yml new file mode 100644 index 000000000..914a6d386 --- /dev/null +++ b/.github/workflows/pr_analyzer.yml @@ -0,0 +1,48 @@ +name: Automated V2 PR Analyzer + +on: + pull_request: + types: [opened, synchronize] + +jobs: + analyze_pr: + runs-on: ubuntu-latest + + steps: + - name: Checkout Repository + uses: actions/checkout@v4 + + - name: Set up Python Environment + uses: actions/setup-python@v5 + with: + python-version: '3.10' + + - name: Install System Dependencies + run: | + sudo apt-get update + sudo apt-get install -y libpango-1.0-0 libpangoft2-1.0-0 + + - name: Install Python Libraries + run: | + python -m pip install --upgrade pip + # Point pip to the new subfolder + pip install -r tools/ai_pr_analyzer/requirements.txt + + - name: Execute V2 PR Analyzer Pipeline + env: + GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }} + GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} + # Grab just the integer ID of the Pull Request + PR_NUMBER: ${{ github.event.pull_request.number }} + run: | + cd tools/ai_pr_analyzer + # Pass only the number to your script + python main.py $PR_NUMBER + + - name: Upload PDF Dashboard as Artifact + uses: actions/upload-artifact@v4 + with: + name: BioDynaMo-PR-Analysis-Report + # Pull the generated PDF from the new path + path: tools/ai_pr_analyzer/output/*.pdf + retention-days: 14 \ No newline at end of file From 21266cf5a070e4fca3ec4304cb26b4b227d5271d Mon Sep 17 00:00:00 2001 From: Aravind Raju <80222285+aravindraju007@users.noreply.github.com> Date: Sat, 25 Jul 2026 05:54:45 +0530 Subject: [PATCH 2/3] Add files via upload --- tools/ai_pr_analyzer/ai/llm_gemini.py | 153 ++++++++++++ .../analysis/static_analyzer.py | 50 ++++ tools/ai_pr_analyzer/api/github_client.py | 56 +++++ tools/ai_pr_analyzer/main.py | 42 ++++ .../reporting/pdf_02_generator.py | 94 ++++++++ tools/ai_pr_analyzer/requirements.txt | 217 ++++++++++++++++++ 6 files changed, 612 insertions(+) create mode 100644 tools/ai_pr_analyzer/ai/llm_gemini.py create mode 100644 tools/ai_pr_analyzer/analysis/static_analyzer.py create mode 100644 tools/ai_pr_analyzer/api/github_client.py create mode 100644 tools/ai_pr_analyzer/main.py create mode 100644 tools/ai_pr_analyzer/reporting/pdf_02_generator.py create mode 100644 tools/ai_pr_analyzer/requirements.txt diff --git a/tools/ai_pr_analyzer/ai/llm_gemini.py b/tools/ai_pr_analyzer/ai/llm_gemini.py new file mode 100644 index 000000000..b429cf517 --- /dev/null +++ b/tools/ai_pr_analyzer/ai/llm_gemini.py @@ -0,0 +1,153 @@ + +GEMINI_API_KEY = "" +import re +import os +from google import genai +from google.genai import types + +def generate_ai_insights(metrics, files_data, complexity_warnings="None"): + """Generates PR analysis text using Gemini 2.5 Flash.""" + file_list = "\n".join(list(files_data.keys())) + + + # THE ANALYST (Text Generation) + # ==================----------------===============---------------===========------------- + prompt_text01 = f""" + You are a Staff Software Engineer analyzing a Pull Request for BioDynaMo. + + Quantitative Metrics: + - Title: {metrics.get('title', 'N/A')} + - Lines Added: {metrics.get('additions', 0)} + - Lines Removed: {metrics.get('deletions', 0)} + + Files Changed: + {file_list} + + High Complexity Warnings: + {complexity_warnings} + + You are an expert C++ software engineer and open-source maintainer. + Analyze the following GitHub Pull Request and write a professional technical analysis report. + + You must format your response exactly matching this 6-point rubric: + PURPOSE: Purpose and Functionality + POSITIVES: Positive Aspects. + CONCERNS: Potential Concerns or Weaknesses and Indicators of potential architectural issues, code smells, or spaghetti code. + CODE_QUALITY: Code Quality and Maintainability Considerations. + + + """ + + print("Generating architectural analysis text with Gemini 2.5 Flash...") + + #try: + # Initialize the official Google GenAI client + # This automatically looks for the GEMINI_API_KEY environment variable + #client = genai.Client(api_key=GEMINI_API_KEY) + + + #response = client.models.generate_content( + #model='gemini-2.5-flash', + #contents=prompt_text01, + #config=types.GenerateContentConfig( + #temperature=0.2, # Slight creativity for writing + # ), + #) + #text_output01 = response.text.replace("**", "").replace("## ", "") + + #except Exception as e: + # print(f" Gemini API Error: {e}") + #return "Error", "Error", "Error", "Error", "Error", "Error" + + prompt_text02 = f""" + You are a Staff Software Engineer analyzing a Pull Request for BioDynaMo. + + Quantitative Metrics: + - Title: {metrics.get('title', 'N/A')} + - Lines Added: {metrics.get('additions', 0)} + - Lines Removed: {metrics.get('deletions', 0)} + + Files Changed: + {file_list} + + High Complexity Warnings: + {complexity_warnings} + + You are an expert C++ software engineer and open-source maintainer. + Analyze the following GitHub Pull Request and write a professional technical analysis report. + + Provide your response using EXACTLY this heading: + IMPACT: Architectural Impact Assessment, that is Assessment of the likely functional impact and breadth of the changes. + ACTION_PLAN : A prioritised list of specific recommendations for improvement. + SUMMARY : Comprehensive explanation of the contribution overall. + END: ending by + + """ + prompt_text03 = f""" + + You are a Staff Software Engineer analyzing a Pull Request for BioDynaMo. + Analyze the following GitHub Pull Request and based on the following GitHub Pull Request analysis, create a prioritized action plan for the developer. + + Quantitative Metrics: + - Title: {metrics.get('title', 'N/A')} + - Lines Added: {metrics.get('additions', 0)} + - Lines Removed: {metrics.get('deletions', 0)} + + Files Changed: + {file_list} + + High Complexity Warnings: + {complexity_warnings} + + Provide your response using EXACTLY this heading: + SUMMARY : Comprehensive explanation of the contribution overall. + """ + + def response_out(ptext): + client = genai.Client(api_key=GEMINI_API_KEY) + response_2 = client.models.generate_content( + model='gemini-2.5-flash', + contents=ptext,) + + return response_2 + + text_output01 = response_out(prompt_text01).text.replace("**", "").replace("## ", "") + text_output02 =response_out(prompt_text02).text.replace("**", "").replace("## ", "") + text_output03 =response_out(prompt_text03).text.replace("**", "").replace("## ", "") + + print(text_output01) + print(text_output02) + #print (text_output03) + + # Finally, combine them for your parser, or parse them separately! + #text_output = text_output01 + "\n" + action_plan_text + + + # PARSING ENGINE + # ===================------------===========---------===========--------- + + def extract_section(text, start_keyword, end_keywords): + end_pattern = "|".join([f"{k}:?" for k in end_keywords]) + "|$" + pattern = rf"{start_keyword}:?\s*(.*?)(?={end_pattern})" + match = re.search(pattern, text, re.IGNORECASE | re.DOTALL) + return match.group(1).strip() if match else f"Data not generated for {start_keyword}." + + try: + purpose = extract_section(text_output01, "PURPOSE: Purpose and Functionality", ["POSITIVES: Positive Aspects", "CONCERNS: Potential Concerns or Weaknesses and Indicators of potential architectural issues, code smells, or spaghetti code", "CODE_QUALITY: Code Quality and Maintainability Considerations", "IMPACT: Architectural Impact Assessment, that is Assessment of the likely functional impact and breadth of the changes"]) + positives = extract_section(text_output01, "POSITIVES: Positive Aspects", ["CONCERNS: Potential Concerns or Weaknesses and Indicators of potential architectural issues, code smells, or spaghetti code", "CODE_QUALITY: Code Quality and Maintainability Considerations", "IMPACT: Architectural Impact Assessment, that is Assessment of the likely functional impact and breadth of the changes"]) + concerns = extract_section(text_output01, "CONCERNS: Potential Concerns or Weaknesses and Indicators of potential architectural issues, code smells, or spaghetti code", ["CODE_QUALITY: Code Quality and Maintainability Considerations", "IMPACT: Architectural Impact Assessment, that is Assessment of the likely functional impact and breadth of the changes"]) + quality = extract_section(text_output01, "CODE_QUALITY: Code Quality and Maintainability Considerations", ["IMPACT: Architectural Impact Assessment, that is Assessment of the likely functional impact and breadth of the changes"]) + impact = extract_section(text_output02, "IMPACT: Architectural Impact Assessment, that is Assessment of the likely functional impact and breadth of the changes",["ACTION_PLAN : A prioritised list of specific recommendations for improvement","SUMMARY : Comprehensive explanation of the contribution overall","END: ending by"]) + action_plan = extract_section(text_output02, "ACTION_PLAN : A prioritised list of specific recommendations for improvement",["SUMMARY : Comprehensive explanation of the contribution overall","END: ending by"]) + summary = extract_section(text_output02, "SUMMARY : Comprehensive explanation of the contribution overall",["END: ending by"]) + print(summary) + + except Exception as e: + print(f">*< >*< >*< >*< >*< >*< >*< Parsing Error: {e}") + return "Error", "Error", "Error", "Error", "Error", "Error" + + + return purpose, positives, concerns, quality, impact, action_plan, summary + + + #return text_output01, ap_text_output \ No newline at end of file diff --git a/tools/ai_pr_analyzer/analysis/static_analyzer.py b/tools/ai_pr_analyzer/analysis/static_analyzer.py new file mode 100644 index 000000000..fbc48caed --- /dev/null +++ b/tools/ai_pr_analyzer/analysis/static_analyzer.py @@ -0,0 +1,50 @@ +import os +import base64 +import lizard +import matplotlib.pyplot as plt + + +def generate_complexity_chart(file_data): + #calculates cyclomatric complexity using lizard and output a Base64 PNG + + filenames, complexities = [], [] + for filename, code_string in file_data.items(): + #feeding raw c++ code into lizard, so it can scan and find the code complexity. + analysis = lizard.analyze_file.analyze_source_code(filename,code_string) + # we are complexity of the by taking avg code complexty as the sum of number of cyclomatic_complexty by number of functions. + avg_ccn = sum(f.cyclomatic_complexity for f in analysis.function_list) / len(analysis.function_list) if analysis.function_list else 0 + filenames.append(filename.split('/')[-1]) + complexities.append(avg_ccn) + + if not filenames: + return None + + #plotting graph for file name vs code complexity + + plt.figure(figsize=(8,4)) + colors = ['#e74c34' if c > 10 else '#f39c12' if c > 5 else '#27ae60' for c in complexities] + plt.bar(filenames, complexities, color = colors) + plt.axhline( y= 10, color='r', linestyle ='--', label='High Risk (CCN > 10)') + plt.title('Code Complexities Impact by File') + plt.ylabel('Avg Clyclomatric Complexity') + plt.xticks(rotation =45, ha= 'right') + plt.tight_layout() + + chart_path = 'temp_complexity.png' + plt.savefig(chart_path, dpi =150) + plt.close() + + # reopening sved plot as read binary and encodes it into standard base64 string , so that it can be weasyprint(it can read base64 sting in html) + #can save local memory + with open(chart_path, 'rb') as f: + chart_b64 = base64.b64encode(f.read()).decode('utf-8') + os.remove(chart_path) + return chart_b64 + + + + + + + + diff --git a/tools/ai_pr_analyzer/api/github_client.py b/tools/ai_pr_analyzer/api/github_client.py new file mode 100644 index 000000000..08e8cc8af --- /dev/null +++ b/tools/ai_pr_analyzer/api/github_client.py @@ -0,0 +1,56 @@ +import os +import requests + + +GEMINI_API_KEY ="" +GITHUB_TOKEN="" + +#def extract_pr_details(url): + +def get_pr_data(owner, repo, pr_number): + + #fetching quantiataive metrics and changed file contents from the PR + GITHUB_TOKEN= os.getenv("") + headers ={"Accept": "application/vnd.github.v3+json"} + + if GITHUB_TOKEN: + headers["Authorization"] = f"token {GITHUB_TOKEN}" + else: + print("Warning: No GitHub Token found. Proceeding with anonymous API limits.") + + # Get PR meta and metrics + + pr_url = f"https://api.github.com/repos/{owner}/{repo}/pulls/{pr_number}" + pr_res = requests.get(pr_url,headers=headers) + pr_res.raise_for_status() + pr_info = pr_res.json() + + # extrating informations + metrics ={ + "additions": pr_info.get("additions", 0), + "deletions": pr_info.get("deletions",0), + "changed_files": pr_info.get("changed_files"), + "total_lines": pr_info.get("additions",0) + pr_info.get("deletions",0), + "title": pr_info.get("title",""), + "description": pr_info.get("body","") + + } + + #get changed files for statistical analysis + + files_url = f"https://api.github.com/repos/{owner}/{repo}/pulls/{pr_number}/files" + files_res = requests.get(files_url,headers=headers) + files_res.raise_for_status() + + files_data = {} + for f in files_res.json(): + filename = f["filename"] + if filename.endswith(('.cpp','.cc','.h','.hpp'))and f['status'] != 'removed': + raw_url = f["raw_url"] + raw_res = requests.get(raw_url, headers=headers) + if raw_res.status_code == 200: + files_data[filename] =raw_res.text + + return metrics, files_data + + \ No newline at end of file diff --git a/tools/ai_pr_analyzer/main.py b/tools/ai_pr_analyzer/main.py new file mode 100644 index 000000000..eba92562a --- /dev/null +++ b/tools/ai_pr_analyzer/main.py @@ -0,0 +1,42 @@ +import sys +from dotenv import load_dotenv + +# Load environment variables before doing anything else +load_dotenv() + +# Import our custom modules +from api.github_client import get_pr_data +from analysis.static_analyzer import generate_complexity_chart +from ai.llm_gemini import generate_ai_insights +from reporting.pdf_02_generator import generate_pdf_report + +if __name__ == "__main__": + OWNER = "BioDynaMo" + REPO = "biodynamo" + PR_NUMBER = 471 + + if len(sys.argv) > 1: + # sys.argv[1] is the number we passed from the YAML file + PR_NUMBER = int(sys.argv[1]) + print(f"CI/CD Trigger Detected! Targeting PR #{PR_NUMBER}") + + print(f"Fetching data for {OWNER}/{REPO} PR #{PR_NUMBER}...") + metrics, files_data = get_pr_data(OWNER, REPO, PR_NUMBER) + + print("*-----> Running static C++ complexity analysis...") + chart_b64 = generate_complexity_chart(files_data) + + + # Phase 3: AI Brain + print("*---> Phase 3: Generating AI insights...") + purpose, positives, concerns, quality, impact, action_plan, summary = generate_ai_insights(metrics, files_data) + + # Phase 4: Flowchart Rendering + #print("🎨 Phase 4: Rendering architecture diagrams...") + #mermaid_svg = render_mermaid_to_svg(mermaid_code) + + + # Phase 5: PDF Assembly + print("*----> Phase 5: Compiling final PDF report...") + output_filename = f"PR_{PR_NUMBER}_Analysis.pdf" + generate_pdf_report(metrics, purpose, positives, concerns, quality, impact, action_plan, summary, PR_NUMBER, chart_b64, output_filename) \ No newline at end of file diff --git a/tools/ai_pr_analyzer/reporting/pdf_02_generator.py b/tools/ai_pr_analyzer/reporting/pdf_02_generator.py new file mode 100644 index 000000000..42624a23f --- /dev/null +++ b/tools/ai_pr_analyzer/reporting/pdf_02_generator.py @@ -0,0 +1,94 @@ +import zlib +import base64 +import markdown +import requests +from weasyprint import HTML + + + +def generate_pdf_report(metrics, purpose, positives, concerns, quality, impact, action_plan, summary, pr_number, chart_b64, output_filename): + """Compiles the HTML template and renders the final PDF.""" + + # 1. Convert Markdown to raw HTML + #raw_html01 = markdown.markdown(text_output01, extensions=['extra', 'codehilite']) + #raw_html02 =markdown.markdown(ap_text_output, extensions=['extra', 'codehilite']) + purpose_p = markdown.markdown(purpose, extensions=['extra', 'codehilite']) + positives_p = markdown.markdown(positives, extensions=['extra', 'codehilite']) + concerns_c = markdown.markdown(concerns, extensions=['extra', 'codehilite']) + quality_q = markdown.markdown(quality, extensions=['extra', 'codehilite']) + impact_i = markdown.markdown(impact, extensions=['extra', 'codehilite']) + action_plan_ap = markdown.markdown(action_plan, extensions=['extra', 'codehilite']) + summary_s = markdown.markdown(summary, extensions=['extra', 'codehilite']) + + + + + total_size = metrics.get('additions', 0) + metrics.get('deletions', 0) + files_changed = metrics.get('changed_files', len(metrics.get('file_names', []))) + + html_content = f""" + + + + + + +

BioDynaMo PR Diagnostics Report , PR {pr_number}

+

PR Title: {metrics.get('title', 'N/A')}

+ +
+
Files Changed{files_changed}
+
Lines Added+{metrics.get('additions', 0)}
+
Lines Removed-{metrics.get('deletions', 0)}
+
Total PR Size{total_size}
+
+ +

Purpose & Overall Explanation

+

{purpose_p}

+ +
+

2. Static Code Complexity Analysis

+

Files exceeding an average complexity of 10 should be reviewed for potential refactoring.

+ Complexity Chart +
+ +

Positives & Strengths

+

{positives_p}

+ +

Potential Concerns

+

{concerns_c}

+ +

Code Quality and Maintainability Considerations

+

{quality_q}

+ +

Architectural Impact

+

{impact_i}

+ +

Action plan

+

{action_plan_ap}

+ +

Summary

+

{summary_s}

+ + + + + + + + """ + + print(f"📄 Compiling Executive PDF Report...") + HTML(string=html_content).write_pdf(output_filename) + print(f"✅ Success! Report saved as: {output_filename}") \ No newline at end of file diff --git a/tools/ai_pr_analyzer/requirements.txt b/tools/ai_pr_analyzer/requirements.txt new file mode 100644 index 000000000..915f6d240 --- /dev/null +++ b/tools/ai_pr_analyzer/requirements.txt @@ -0,0 +1,217 @@ +altair==5.5.0 +annotated-doc==0.0.4 +annotated-types==0.7.0 +anyio==4.9.0 +appnope==0.1.3 +argon2-cffi==25.1.0 +argon2-cffi-bindings==21.2.0 +arrow==1.3.0 +asttokens==2.2.1 +async-lru==2.0.5 +attrs==25.3.0 +babel==2.17.0 +backcall==0.2.0 +backoff==2.2.1 +bcrypt==5.0.0 +beautifulsoup4==4.13.4 +bleach==6.2.0 +blinker==1.8.2 +build==1.3.0 +cachetools==5.5.2 +certifi==2025.6.15 +cffi==1.17.1 +charset-normalizer==3.4.0 +chromadb==1.3.6 +click==8.1.7 +coloredlogs==15.0.1 +comm==0.2.2 +contourpy==1.3.2 +cycler==0.12.1 +debugpy==1.6.5 +decorator==5.1.1 +defusedxml==0.7.1 +distro==1.9.0 +durationpy==0.10 +entrypoints==0.4 +exceptiongroup==1.3.0 +executing==1.2.0 +f==0.0.1 +fastapi==0.124.2 +fastjsonschema==2.21.1 +filelock==3.16.1 +Flask==3.0.3 +flatbuffers==25.9.23 +fonttools==4.58.0 +fqdn==1.5.1 +fsspec==2024.9.0 +gitdb==4.0.12 +GitPython==3.1.44 +google-auth==2.43.0 +googleapis-common-protos==1.72.0 +grpcio==1.76.0 +h11==0.16.0 +httpcore==1.0.9 +httptools==0.7.1 +httpx==0.28.1 +huggingface-hub==0.26.2 +humanfriendly==10.0 +idna==3.10 +importlib_metadata==8.7.0 +importlib_resources==6.5.2 +ipykernel==6.20.2 +ipython==8.8.0 +ipywidgets==8.1.7 +isoduration==20.11.0 +itsdangerous==2.2.0 +jedi==0.18.2 +Jinja2==3.1.4 +joblib==1.5.1 +json5==0.12.0 +jsonpointer==3.0.0 +jsonschema==4.24.0 +jsonschema-specifications==2025.4.1 +jupyter==1.1.1 +jupyter-console==6.6.3 +jupyter-events==0.12.0 +jupyter-lsp==2.2.5 +jupyter_client==7.4.9 +jupyter_core==5.1.3 +jupyter_server==2.16.0 +jupyter_server_terminals==0.5.3 +jupyterlab==4.4.3 +jupyterlab_pygments==0.3.0 +jupyterlab_server==2.27.3 +jupyterlab_widgets==3.0.15 +kiwisolver==1.4.8 +kubernetes==34.1.0 +markdown-it-py==4.0.0 +MarkupSafe==2.1.5 +matplotlib==3.10.3 +matplotlib-inline==0.1.6 +mdurl==0.1.2 +mistune==3.1.3 +mmh3==5.2.0 +mpmath==1.3.0 +narwhals==1.41.0 +nbclient==0.10.2 +nbconvert==7.16.6 +nbformat==5.10.4 +nest-asyncio==1.5.6 +nest-simulator==3.3 +networkx==3.3 +nltk==3.9.1 +notebook==7.4.3 +notebook_shim==0.2.4 +numpy==2.2.6 +oauthlib==3.3.1 +ollama==0.6.1 +onnxruntime==1.23.2 +opentelemetry-api==1.39.1 +opentelemetry-exporter-otlp-proto-common==1.39.1 +opentelemetry-exporter-otlp-proto-grpc==1.39.1 +opentelemetry-proto==1.39.1 +opentelemetry-sdk==1.39.1 +opentelemetry-semantic-conventions==0.60b1 +orjson==3.11.5 +outcome==1.3.0.post0 +overrides==7.7.0 +packaging==23.0 +pandas==2.3.1 +pandocfilters==1.5.1 +parso==0.8.3 +pexpect==4.8.0 +pickleshare==0.7.5 +pillow==11.2.1 +platformdirs==2.6.2 +posthog==5.4.0 +prometheus_client==0.22.1 +prompt-toolkit==3.0.36 +protobuf==6.31.1 +psutil==5.9.4 +ptyprocess==0.7.0 +pure-eval==0.2.2 +pyarrow==20.0.0 +pyasn1==0.6.1 +pyasn1_modules==0.4.2 +pybase64==1.4.3 +pycparser==2.22 +pydantic==2.12.5 +pydantic_core==2.41.5 +pydeck==0.9.1 +Pygments==2.14.0 +pyparsing==3.2.3 +pypdf==6.4.1 +PyPDF2==3.0.1 +PyPika==0.48.9 +pyproject_hooks==1.2.0 +PySocks==1.7.1 +pyTelegramBotAPI==4.26.0 +python-dateutil==2.8.2 +python-dotenv==1.2.1 +python-json-logger==3.3.0 +python-multipart==0.0.20 +pytz==2024.2 +PyYAML==6.0.2 +pyzmq==25.0.0 +referencing==0.36.2 +regex==2024.9.11 +requests==2.32.3 +requests-oauthlib==2.0.0 +rfc3339-validator==0.1.4 +rfc3986-validator==0.1.1 +rich==14.2.0 +rpds-py==0.25.1 +rsa==4.9.1 +safetensors==0.4.5 +scikit-learn==1.7.2 +scipy==1.14.1 +seaborn==0.13.2 +selenium==4.33.0 +Send2Trash==1.8.3 +sentence-transformers==5.2.0 +shellingham==1.5.4 +six==1.16.0 +smmap==5.0.2 +sniffio==1.3.1 +sortedcontainers==2.4.0 +soupsieve==2.7 +SQLAlchemy==2.0.41 +stack-data==0.6.2 +starlette==0.50.0 +streamlit==1.45.1 +sympy==1.13.3 +telebot==0.0.5 +tenacity==9.1.2 +terminado==0.18.1 +threadpoolctl==3.5.0 +tinycss2==1.4.0 +tokenizers==0.20.3 +toml==0.10.2 +tomli==2.2.1 +torch==2.4.1 +tornado==6.2 +tqdm==4.66.6 +traitlets==5.8.1 +transformers==4.46.2 +trio==0.30.0 +trio-websocket==0.12.2 +typer==0.20.0 +types-python-dateutil==2.9.0.20250516 +typing-inspection==0.4.2 +typing_extensions==4.15.0 +tzdata==2024.2 +uri-template==1.3.0 +urllib3==2.3.0 +uvicorn==0.38.0 +uvloop==0.22.1 +watchfiles==1.1.1 +wcwidth==0.2.6 +webcolors==24.11.1 +webencodings==0.5.1 +websocket-client==1.8.0 +websockets==15.0.1 +Werkzeug==3.0.6 +widgetsnbextension==4.0.14 +wsproto==1.2.0 +xgboost==3.0.2 +zipp==3.23.0 From 490bffb7a0d3adb8adf319e82096a727ffaac85f Mon Sep 17 00:00:00 2001 From: Aravind Raju <80222285+aravindraju007@users.noreply.github.com> Date: Sat, 25 Jul 2026 05:57:21 +0530 Subject: [PATCH 3/3] Update requirements.txt --- tools/ai_pr_analyzer/requirements.txt | 254 ++++++-------------------- 1 file changed, 52 insertions(+), 202 deletions(-) diff --git a/tools/ai_pr_analyzer/requirements.txt b/tools/ai_pr_analyzer/requirements.txt index 915f6d240..09af2a765 100644 --- a/tools/ai_pr_analyzer/requirements.txt +++ b/tools/ai_pr_analyzer/requirements.txt @@ -1,217 +1,67 @@ -altair==5.5.0 -annotated-doc==0.0.4 annotated-types==0.7.0 -anyio==4.9.0 -appnope==0.1.3 -argon2-cffi==25.1.0 -argon2-cffi-bindings==21.2.0 -arrow==1.3.0 -asttokens==2.2.1 -async-lru==2.0.5 -attrs==25.3.0 -babel==2.17.0 -backcall==0.2.0 -backoff==2.2.1 -bcrypt==5.0.0 -beautifulsoup4==4.13.4 -bleach==6.2.0 -blinker==1.8.2 -build==1.3.0 -cachetools==5.5.2 -certifi==2025.6.15 -cffi==1.17.1 -charset-normalizer==3.4.0 -chromadb==1.3.6 -click==8.1.7 -coloredlogs==15.0.1 -comm==0.2.2 -contourpy==1.3.2 +anyio==4.12.1 +brotli==1.2.0 +certifi==2026.5.20 +cffi==2.0.0 +charset-normalizer==3.4.7 +contourpy==1.3.0 +cryptography==49.0.0 +cssselect2==0.8.0 cycler==0.12.1 -debugpy==1.6.5 -decorator==5.1.1 -defusedxml==0.7.1 -distro==1.9.0 -durationpy==0.10 -entrypoints==0.4 -exceptiongroup==1.3.0 -executing==1.2.0 -f==0.0.1 -fastapi==0.124.2 -fastjsonschema==2.21.1 -filelock==3.16.1 -Flask==3.0.3 -flatbuffers==25.9.23 -fonttools==4.58.0 -fqdn==1.5.1 -fsspec==2024.9.0 -gitdb==4.0.12 -GitPython==3.1.44 -google-auth==2.43.0 -googleapis-common-protos==1.72.0 -grpcio==1.76.0 +dotenv==0.9.9 +exceptiongroup==1.3.1 +filelock==3.19.1 +fonttools==4.60.2 +fsspec==2025.10.0 +google-auth==2.50.0 +google-genai==1.47.0 h11==0.16.0 +hf-xet==1.5.1 httpcore==1.0.9 -httptools==0.7.1 httpx==0.28.1 -huggingface-hub==0.26.2 -humanfriendly==10.0 -idna==3.10 -importlib_metadata==8.7.0 +idna==3.18 +importlib_metadata==8.7.1 importlib_resources==6.5.2 -ipykernel==6.20.2 -ipython==8.8.0 -ipywidgets==8.1.7 -isoduration==20.11.0 -itsdangerous==2.2.0 -jedi==0.18.2 -Jinja2==3.1.4 -joblib==1.5.1 -json5==0.12.0 -jsonpointer==3.0.0 -jsonschema==4.24.0 -jsonschema-specifications==2025.4.1 -jupyter==1.1.1 -jupyter-console==6.6.3 -jupyter-events==0.12.0 -jupyter-lsp==2.2.5 -jupyter_client==7.4.9 -jupyter_core==5.1.3 -jupyter_server==2.16.0 -jupyter_server_terminals==0.5.3 -jupyterlab==4.4.3 -jupyterlab_pygments==0.3.0 -jupyterlab_server==2.27.3 -jupyterlab_widgets==3.0.15 -kiwisolver==1.4.8 -kubernetes==34.1.0 -markdown-it-py==4.0.0 -MarkupSafe==2.1.5 -matplotlib==3.10.3 -matplotlib-inline==0.1.6 -mdurl==0.1.2 -mistune==3.1.3 -mmh3==5.2.0 +Jinja2==3.1.6 +kiwisolver==1.4.7 +lizard==1.23.0 +Markdown==3.9 +MarkupSafe==3.0.3 +matplotlib==3.9.4 mpmath==1.3.0 -narwhals==1.41.0 -nbclient==0.10.2 -nbconvert==7.16.6 -nbformat==5.10.4 -nest-asyncio==1.5.6 -nest-simulator==3.3 -networkx==3.3 -nltk==3.9.1 -notebook==7.4.3 -notebook_shim==0.2.4 -numpy==2.2.6 -oauthlib==3.3.1 -ollama==0.6.1 -onnxruntime==1.23.2 -opentelemetry-api==1.39.1 -opentelemetry-exporter-otlp-proto-common==1.39.1 -opentelemetry-exporter-otlp-proto-grpc==1.39.1 -opentelemetry-proto==1.39.1 -opentelemetry-sdk==1.39.1 -opentelemetry-semantic-conventions==0.60b1 -orjson==3.11.5 -outcome==1.3.0.post0 -overrides==7.7.0 -packaging==23.0 -pandas==2.3.1 -pandocfilters==1.5.1 -parso==0.8.3 -pexpect==4.8.0 -pickleshare==0.7.5 -pillow==11.2.1 -platformdirs==2.6.2 -posthog==5.4.0 -prometheus_client==0.22.1 -prompt-toolkit==3.0.36 -protobuf==6.31.1 -psutil==5.9.4 -ptyprocess==0.7.0 -pure-eval==0.2.2 -pyarrow==20.0.0 -pyasn1==0.6.1 +networkx==3.2.1 +numpy==2.0.2 +packaging==26.2 +pathspec==1.1.1 +pillow==11.3.0 +psutil==7.2.2 +pyasn1==0.6.3 pyasn1_modules==0.4.2 -pybase64==1.4.3 -pycparser==2.22 -pydantic==2.12.5 -pydantic_core==2.41.5 -pydeck==0.9.1 -Pygments==2.14.0 -pyparsing==3.2.3 -pypdf==6.4.1 -PyPDF2==3.0.1 -PyPika==0.48.9 -pyproject_hooks==1.2.0 -PySocks==1.7.1 -pyTelegramBotAPI==4.26.0 -python-dateutil==2.8.2 +pycparser==2.23 +pydantic==2.13.4 +pydantic_core==2.46.4 +pydyf==0.11.0 +Pygments==2.20.0 +pyparsing==3.3.2 +pyphen==0.17.2 +python-dateutil==2.9.0.post0 python-dotenv==1.2.1 -python-json-logger==3.3.0 -python-multipart==0.0.20 -pytz==2024.2 -PyYAML==6.0.2 -pyzmq==25.0.0 -referencing==0.36.2 -regex==2024.9.11 -requests==2.32.3 -requests-oauthlib==2.0.0 -rfc3339-validator==0.1.4 -rfc3986-validator==0.1.1 -rich==14.2.0 -rpds-py==0.25.1 -rsa==4.9.1 -safetensors==0.4.5 -scikit-learn==1.7.2 -scipy==1.14.1 -seaborn==0.13.2 -selenium==4.33.0 -Send2Trash==1.8.3 -sentence-transformers==5.2.0 -shellingham==1.5.4 -six==1.16.0 -smmap==5.0.2 -sniffio==1.3.1 -sortedcontainers==2.4.0 -soupsieve==2.7 -SQLAlchemy==2.0.41 -stack-data==0.6.2 -starlette==0.50.0 -streamlit==1.45.1 -sympy==1.13.3 -telebot==0.0.5 +PyYAML==6.0.3 +regex==2026.1.15 +requests==2.32.5 +safetensors==0.7.0 +six==1.17.0 +sympy==1.14.0 tenacity==9.1.2 -terminado==0.18.1 -threadpoolctl==3.5.0 tinycss2==1.4.0 -tokenizers==0.20.3 -toml==0.10.2 -tomli==2.2.1 -torch==2.4.1 -tornado==6.2 -tqdm==4.66.6 -traitlets==5.8.1 -transformers==4.46.2 -trio==0.30.0 -trio-websocket==0.12.2 -typer==0.20.0 -types-python-dateutil==2.9.0.20250516 +tinyhtml5==2.0.0 +tokenizers==0.22.2 +tqdm==4.68.2 typing-inspection==0.4.2 typing_extensions==4.15.0 -tzdata==2024.2 -uri-template==1.3.0 -urllib3==2.3.0 -uvicorn==0.38.0 -uvloop==0.22.1 -watchfiles==1.1.1 -wcwidth==0.2.6 -webcolors==24.11.1 +urllib3==1.26.20 +weasyprint==66.0 webencodings==0.5.1 -websocket-client==1.8.0 websockets==15.0.1 -Werkzeug==3.0.6 -widgetsnbextension==4.0.14 -wsproto==1.2.0 -xgboost==3.0.2 -zipp==3.23.0 +zipp==3.23.1 +zopfli==0.2.3.post1